diff --git a/book/build-your-staking-dapp/avalanche/overview.md b/book/build-your-staking-dapp/avalanche/overview.md index 96ab340..bb13c4a 100644 --- a/book/build-your-staking-dapp/avalanche/overview.md +++ b/book/build-your-staking-dapp/avalanche/overview.md @@ -1,15 +1,23 @@ # Overview -Staking on the Avalanche network (AVAX) involves locking up tokens to support the network's security and operations. In return, stakers earn rewards. - {% hint style="info" %} The Avalanche blockchain, renowned for its rapid transaction processing, low fees, and eco-friendly architecture, utilizes a unique consensus protocol known as Avalanche Consensus. This protocol enables a high degree of decentralization and security, allowing validators to participate by staking AVAX, the network's native token. {% endhint %} +Staking on the Avalanche network (AVAX) involves locking up tokens to support the network's security and operations. In return, stakers earn rewards. + The **Chorus One SDK** simplifies this process, providing developers with the tools needed to build, sign, and broadcast staking transactions. This guide will walk you through the fundamentals of staking on Avalanche using the Chorus One SDK. +{% hint style="info" %} + +**Compatibility Notice** + +The methods provided in this documentation are compatible with popular Avalanche libraries such as `@avalabs/avalanchejs`. This compatibility ensures that you can seamlessly integrate these methods into your existing Avalanche projects. + +{% endhint %} + ## Setting Up the Staker To get started with staking on the Avalanche network using the Chorus One SDK, you will first need to initialize the SDK. @@ -139,10 +147,6 @@ const { status, receipt } = await staker.getTxStatus({ txId, chain: 'P' }) console.log(status) // 'success' ``` -{% hint style="info" %} -The signature of these methods is compatible with the methods provided by popular Avalanche libraries like `@avalabs/avalanchejs`. -{% endhint %} - --- ## Next Steps diff --git a/book/build-your-staking-dapp/cosmos/overview.md b/book/build-your-staking-dapp/cosmos/overview.md index 1caf7e7..cd74f25 100644 --- a/book/build-your-staking-dapp/cosmos/overview.md +++ b/book/build-your-staking-dapp/cosmos/overview.md @@ -10,6 +10,14 @@ The **Chorus One SDK** simplifies this process, providing developers with the to This guide will walk you through the fundamentals of staking on Cosmos using the Chorus One SDK. +{% hint style="info" %} + +**Compatibility Notice** + +The methods provided in this documentation are compatible with popular Cosmos libraries such as `@cosmjs/cosmwasm`. This compatibility ensures that you can seamlessly integrate these methods into your existing Cosmos projects. + +{% endhint %} + ## Setting Up the Staker To get started with staking on the Cosmos network using the Chorus One SDK, you will first need to initialize the SDK for Cosmos. @@ -107,10 +115,6 @@ const signedTx = TxRaw.encode(rawTx).finish() Additionally, you can use the Chorus One SDK to sign transactions using Fireblocks, mnemonic or other methods. -{% hint style="info" %} -The signature of these methods is compatible with the methods provided by popular Cosmos libraries like `@cosmjs/cosmwasm`. -{% endhint %} - For detailed information on setting up and configuring these options, refer to the [What is a Signer?](../../signers-explained/what-is-a-signer.md) section. {% tabs %} diff --git a/book/build-your-staking-dapp/ethereum/overview.md b/book/build-your-staking-dapp/ethereum/overview.md index a635e4f..1b24312 100644 --- a/book/build-your-staking-dapp/ethereum/overview.md +++ b/book/build-your-staking-dapp/ethereum/overview.md @@ -14,6 +14,14 @@ To enhance efficiency, the SDK leverages [Stakewise V3's](https://docs.stakewise This guide will walk you through the fundamentals of staking on Ethereum using the Chorus One SDK. +{% hint style="info" %} + +**Compatibility Notice** + +The methods provided in this documentation are compatible with popular Ethereum libraries such as `Ethers` and `viem`. This compatibility ensures that you can seamlessly integrate these methods into your existing Ethereum projects. + +{% endhint %} + ## Understanding Key Concepts
@@ -113,6 +121,23 @@ const { tx } = await staker.buildStakeTx({ }) ``` +### Ensuring Correct Amount Format for Staking + +The `amount` parameter must be a string representing the amount of ETH to deposit. For example, `'1'` represents 1 ETH. + +If you have the amount as a `bigint`, convert it to a string using the `formatEther` function from `viem`. Example: + +```typescript +import { formatEther } from 'viem' + +const amountBigInt = 10000000000000000n // 0.01 ETH +const amountToStake = formatEther(amountBigInt) + +console.log(amountToStake) // "0.01" +``` + +This ensures the `amountToStake` parameter is in the correct format for the staking transaction function. + --- ## Getting the Validator Address provided by Chorus One @@ -142,12 +167,63 @@ Once the transaction is built, you can sign that transaction using your own sign const signedTx = await yourWalletClient.signTransaction(tx) ``` +When using your own signer, you will need to calculate the transaction fees yourself before signing. + Additionally, you can use the Chorus One SDK to sign transactions using Fireblocks, mnemonic or other methods. - For detailed information on setting up and configuring these options, please refer to the [What is a Signer?](../../signers-explained/what-is-a-signer.md) section. {% tabs %} + +{% tab title="Using wagmi/Viem for Signing" %} +By integrating wagmi, you can take advantage of its lightweight developer-friendly wallet client capabilities to sign transactions on the Ethereum network: + +```javascript +import { useWalletClient } from 'wagmi' + +const { data: walletClient } = useWalletClient() + +// Prepare the transaction and estimate the gas fees +const request = await walletClient.prepareTransactionRequest(tx) + +// Sign and broadcast the transaction +const hash = await walletClient.sendTransaction(request) +``` + +For more information please refer to the [Viem Documentation](https://viem.sh/) + +{% endtab %} + +{% tab title="Using Ethers for Signing" %} + +By integrating Ethers, you can use its widely adopted and feature-rich library for signing transactions on the Ethereum network: + +```javascript +import { BrowserProvider } from 'ethers' + +const provider = new BrowserProvider(window.ethereum) +const signer = await provider.getSigner() + +// Estimate gas fees +const feeData = await provider.getFeeData() +const gasLimit = await provider.estimateGas(tx) + +// Sign and broadcast the transaction +const { hash } = await signer.sendTransaction({ + ...tx, + // Optional: Set the gas limit and fees + gasLimit: gasLimit, + maxFeePerGas: feeData.maxFeePerGas, + maxPriorityFeePerGas: feeData.maxPriorityFeePerGas +}) +``` + +For more information please refer to the [Ethers Documentation](https://docs.ethers.org/) + +{% endtab %} + {% tab title="Using Fireblocks for Signing" %} + By integrating Fireblocks, you can leverage its robust security features to sign transactions on the Ethereum network. To set up Fireblocks, you will need to provide the necessary API key, secret key, and vault ID: ```javascript @@ -164,15 +240,28 @@ const signer = new FireblocksSigner({ await signer.init() +// Just sign the transaction const { signedTx } = await staker.sign({ signer, signerAddress: '0x70aEe8a9099ebADB186C2D530F72CF5dC7FE6B30', - tx + tx, + baseFeeMultiplier: 2, // Optional: Multiplier for the base fee per gas + defaultPriorityFee: '2' // Optional: Override for the maxPriorityFeePerGas }) ``` +### Configuring Transaction Fees + +When signing transactions, you can optionally configure the fees to manage cost and priority. The `fees` parameter allows you to specify a `baseFeeMultiplier` and a `defaultPriorityFee`. + +- **`baseFeeMultiplier`**: (Optional) This multiplier helps manage fee fluctuations by adjusting the base fee per gas from the latest block. For example, if the `baseFeeMultiplier` is set to 2, the final `maxFeePerGas` will be 2 times the base fee. The default value is 1.2. + +- **`defaultPriorityFee`**: (Optional) This value allows you to override the `maxPriorityFeePerGas` estimated by the RPC. You can specify a fixed value to ensure that your transaction has a certain priority. By default, the `maxPriorityFeePerGas` is calculated by the RPC. + For more information please refer to the [Signing with Fireblocks](../../signers-explained/fireblocks.md) + {% endtab %} + {% endtabs %} --- @@ -193,10 +282,6 @@ const { status, receipt } = await staker.getTxStatus({ txHash }) console.log(status) // 'success' ``` -{% hint style="info" %} -The signature of these methods is compatible with the methods provided by popular Ethereum libraries like `viem`. -{% endhint %} - --- ## Next Steps diff --git a/book/build-your-staking-dapp/near/overview.md b/book/build-your-staking-dapp/near/overview.md index e68af78..9fca63b 100644 --- a/book/build-your-staking-dapp/near/overview.md +++ b/book/build-your-staking-dapp/near/overview.md @@ -10,6 +10,14 @@ The **Chorus One SDK** simplifies this process, providing developers with the to This guide will walk you through the fundamentals of staking on NEAR using the Chorus One SDK. +{% hint style="info" %} + +**Compatibility Notice** + +The methods provided in this documentation are compatible with popular NEAR libraries such as `near-api-js`. This compatibility ensures that you can seamlessly integrate these methods into your existing NEAR projects. + +{% endhint %} + ## Setting Up the Staker To get started with staking on the NEAR network using the Chorus One SDK, you will first need to initialize the SDK for NEAR. @@ -151,10 +159,6 @@ const { status, receipt } = await staker.getTxStatus({ console.log(status) // 'success' ``` -{% hint style="info" %} -The signature of these methods is compatible with the methods provided by popular NEAR libraries like `near-api-js`. -{% endhint %} - --- ## Next Steps diff --git a/book/build-your-staking-dapp/polkadot-substrate/overview.md b/book/build-your-staking-dapp/polkadot-substrate/overview.md index 59d9ad7..a916ee7 100644 --- a/book/build-your-staking-dapp/polkadot-substrate/overview.md +++ b/book/build-your-staking-dapp/polkadot-substrate/overview.md @@ -10,6 +10,14 @@ The **Chorus One SDK** simplifies this process, providing developers with the to This guide will walk you through the fundamentals of staking on Substrate using the Chorus One SDK. +{% hint style="info" %} + +**Compatibility Notice** + +The methods provided in this documentation are compatible with popular Substrate libraries such as `@polkadot/api`. This compatibility ensures that you can seamlessly integrate these methods into your existing Substrate projects. + +{% endhint %} + ## Setting Up the Staker To get started with staking on the Substrate network using the Chorus One SDK, you will first need to initialize the SDK for Substrate. @@ -145,10 +153,6 @@ const { status, receipt } = await staker.getTxStatus({ txHash }) console.log(status) // 'success' ``` -{% hint style="info" %} -The signature of these methods is compatible with the methods provided by popular Substrate libraries like `@polkadot/api`. -{% endhint %} - ## Closing the Connection After completing the staking operations, close the connection to the Substrate network: diff --git a/book/build-your-staking-dapp/solana/overview.md b/book/build-your-staking-dapp/solana/overview.md index 5834038..553d6ad 100644 --- a/book/build-your-staking-dapp/solana/overview.md +++ b/book/build-your-staking-dapp/solana/overview.md @@ -12,6 +12,14 @@ The **Chorus One SDK** simplifies the staking process on the Solana network, pro This guide will walk you through the fundamentals of staking on Solana using the Chorus One SDK. +{% hint style="info" %} + +**Compatibility Notice** + +The methods provided in this documentation are compatible with popular Solana libraries such as `@solana/web3.js`. This compatibility ensures that you can seamlessly integrate these methods into your existing Solana projects. + +{% endhint %} + ## Setting Up the Staker To get started with staking on the Solana network using the Chorus One SDK, you will first need to initialize the SDK. @@ -147,10 +155,6 @@ const { status, receipt } = await staker.getTxStatus({ txHash }) console.log(status) // 'success' ``` -{% hint style="info" %} -The signature of these methods is compatible with the methods provided by popular Solana libraries like `@solana/web3.js`. -{% endhint %} - --- ## Next Steps diff --git a/book/build-your-staking-dapp/ton/overview.md b/book/build-your-staking-dapp/ton/overview.md index ce8148b..17997de 100644 --- a/book/build-your-staking-dapp/ton/overview.md +++ b/book/build-your-staking-dapp/ton/overview.md @@ -15,6 +15,14 @@ It supports: This guide will walk you through the fundamentals of staking on TON using the Chorus One SDK. +{% hint style="info" %} + +**Compatibility Notice** + +The methods provided in this documentation are compatible with popular TON libraries such as `@ton/ton`. This compatibility ensures that you can seamlessly integrate these methods into your existing TON projects. + +{% endhint %} + ## Setting Up the Staker To get started with staking on TON using the Chorus One SDK, you will first need to initialize the SDK. @@ -175,10 +183,6 @@ const { status, receipt } = await staker.getTxStatus({ console.log(status) // 'success' ``` -{% hint style="info" %} -The signature of these methods is compatible with the methods provided by popular TON libraries like `@ton/ton`. -{% endhint %} - --- ## Next Steps diff --git a/book/docs/classes/ethereum_src.EthereumStaker.md b/book/docs/classes/ethereum_src.EthereumStaker.md index b30076b..b971269 100644 --- a/book/docs/classes/ethereum_src.EthereumStaker.md +++ b/book/docs/classes/ethereum_src.EthereumStaker.md @@ -107,7 +107,7 @@ Builds a staking transaction. | `params` | `Object` | Parameters for building the transaction | | `params.delegatorAddress` | \`0x$\{string}\` | The delegator (wallet) address to stake from | | `params.validatorAddress` | \`0x$\{string}\` | The validator (vault) address to stake with | -| `params.amount` | `string` | The amount to stake, specified in `ETH` | +| `params.amount` | `string` | The amount to stake, specified in `ETH`. E.g. "1" - 1 ETH | | `params.referrer?` | \`0x$\{string}\` | (Optional) The address of the referrer. This is used to track the origin of transactions, providing insights into which sources or campaigns are driving activity. This can be useful for analytics and optimizing user acquisition strategies | ### Returns @@ -136,7 +136,7 @@ method. | `params` | `Object` | Parameters for building the transaction | | `params.delegatorAddress` | \`0x$\{string}\` | The delegator (wallet) address that is unstaking | | `params.validatorAddress` | \`0x$\{string}\` | The validator (vault) address to unstake from | -| `params.amount` | `string` | The amount to unstake, specified in `ETH` | +| `params.amount` | `string` | The amount to unstake, specified in `ETH`. E.g. "1" - 1 ETH | ### Returns @@ -186,7 +186,7 @@ Builds a mint transaction. | `params` | `Object` | Parameters for building the transaction | | `params.delegatorAddress` | \`0x$\{string}\` | The delegator (wallet) address | | `params.validatorAddress` | \`0x$\{string}\` | The validator (vault) address to mint shares for | -| `params.amount` | `string` | The amount to mint, specified in `osETH` | +| `params.amount` | `string` | The amount to mint, specified in `osETH`. E.g. "1" - 1 osETH | | `params.referrer?` | \`0x$\{string}\` | (Optional) The address of the referrer. This is used to track the origin of transactions, providing insights into which sources or campaigns are driving activity. This can be useful for analytics and optimizing user acquisition strategies. | ### Returns @@ -210,7 +210,7 @@ Builds a burn transaction. | `params` | `Object` | Parameters for building the transaction | | `params.delegatorAddress` | \`0x$\{string}\` | The delegator (wallet) address | | `params.validatorAddress` | \`0x$\{string}\` | The validator (vault) address to burn shares from | -| `params.amount` | `string` | The amount to burn, specified in `osETH` | +| `params.amount` | `string` | The amount to burn, specified in `osETH`. E.g. "1" - 1 osETH | ### Returns @@ -415,6 +415,8 @@ Signs a transaction using the provided signer. | `params.signer` | `Signer` | A signer instance. | | `params.signerAddress` | \`0x$\{string}\` | The address of the signer | | `params.tx` | [`Transaction`](../interfaces/ethereum_src.Transaction.md) | The transaction to sign | +| `params.baseFeeMultiplier?` | `number` | (Optional) The multiplier for fees, which is used to manage fee fluctuations, is applied to the base fee per gas from the latest block to determine the final `maxFeePerGas`. The default value is 1.2. | +| `params.defaultPriorityFee?` | `string` | (Optional) This overrides the the `maxPriorityFeePerGas` estimated by the RPC. | ### Returns diff --git a/book/docs/interfaces/ethereum_src.Transaction.md b/book/docs/interfaces/ethereum_src.Transaction.md index f11afcd..fa72993 100644 --- a/book/docs/interfaces/ethereum_src.Transaction.md +++ b/book/docs/interfaces/ethereum_src.Transaction.md @@ -4,21 +4,12 @@ Represents an Ethereum transaction. ### Properties -- [account](ethereum_src.Transaction.md#account) - [to](ethereum_src.Transaction.md#to) - [value](ethereum_src.Transaction.md#value) - [data](ethereum_src.Transaction.md#data) ## Properties -### account - -• **account**: \`0x$\{string}\` - -The account (sender) address in hexadecimal format. - -___ - ### to • **to**: \`0x$\{string}\` diff --git a/book/ethereum-tutorial/3-staking.md b/book/ethereum-tutorial/3-staking.md index 5f6e694..57ef7fc 100644 --- a/book/ethereum-tutorial/3-staking.md +++ b/book/ethereum-tutorial/3-staking.md @@ -78,15 +78,14 @@ const stake = async ({ const validatorAddress = CHORUS_ONE_ETHEREUM_VALIDATORS.ethereum.mevMaxVault - const stakeTx = await staker.buildStakeTx({ + const { tx: stakeTx } = await staker.buildStakeTx({ delegatorAddress: userAddress, validatorAddress, - amount: amountToStake + amount: amountToStake // Passed as string, e.g. '1' - 1 ETH }) console.log(stakeTx) // { - // account: "0x...", // to: "0x...", // data: "0x...", // value: 10000000000000000n @@ -98,7 +97,31 @@ const stake = async ({ } ``` +The `Transaction` object returned by `buildStakeTransaction` includes the following parameters: + +- **`to` (Hex)**: The address of the contract to interact with(the vault address). +- **`data` (Hex)**: A contract hashed method call with encoded arguments (the transformation of the method call into this encoded and hashed form is handled by the `encodeFunctionData` method from the viem library). +- **`value` (bigint)**: The amount of ETH being used in the transaction. In this case, it's the amount being staked. + +### Ensuring Correct Amount Format for Staking + +The `amountToStake` parameter must be a string representing the amount of ETH to deposit. For example, `'1'` represents 1 ETH. + +If you have the amount as a `bigint`, convert it to a string using the `formatEther` function from `viem`. Example: + +```typescript +import { formatEther } from 'viem' + +const amountBigInt = 10000000000000000n // 0.01 ETH +const amountToStake = formatEther(amountBigInt) + +console.log(amountToStake) // "0.01" +``` + +This ensures the `amountToStake` parameter is in the correct format for the staking transaction function. + {% hint style="info" %} +**Configuring Fees** We utilize here the Ethereum Improvement Proposal 1559 (EIP-1559) transaction type. With EIP-1559, users specify two types of fees: @@ -107,14 +130,9 @@ We utilize here the Ethereum Improvement Proposal 1559 (EIP-1559) transaction ty The network determines the actual fee based on the current demand for block space and the transaction's priority. To estimate the gas required for the transaction, we use the `prepareTransactionRequest` method from the `viem`'s wallet client. -{% endhint %} +For detailed information on configuring fees, please refer to the [Viem Documentation](https://viem.sh/docs/chains/fees). -The `Transaction` object returned by `buildStakeTransaction` includes the following parameters: - -- **`account` (Hex)**: The user's Ethereum address. -- **`to` (Hex)**: The address of the contract to interact with(the vault address). -- **`data` (Hex)**: A contract hashed method call with encoded arguments (the transformation of the method call into this encoded and hashed form is handled by the `encodeFunctionData` method from the viem library). -- **`value` (bigint)**: The amount of ETH being used in the transaction. In this case, it's the amount being staked. +{% endhint %} ## Next Steps diff --git a/book/ethereum-tutorial/4-unstaking.md b/book/ethereum-tutorial/4-unstaking.md index e9cab70..aac5a38 100644 --- a/book/ethereum-tutorial/4-unstaking.md +++ b/book/ethereum-tutorial/4-unstaking.md @@ -50,10 +50,10 @@ Once you've determined the maximum unstakeable amount, you can proceed to build **The following example demonstrates how to use the `buildUnstakeTx` method:** ```typescript -const unstakeTx = await staker.buildUnstakeTx({ +const { tx: unstakeTx } = await staker.buildUnstakeTx({ delegatorAddress: userAddress, validatorAddress, - amount: amountToUnstake + amount: amountToUnstake // Passed as string, e.g. '1' - 1 ETH }) const request = await walletClient.prepareTransactionRequest(unstakeTx) @@ -103,7 +103,7 @@ To withdraw assets, you'll need to identify which queue items are ready to be wi **The following example demonstrates how to prepare the withdrawal transaction:** ```typescript -const withdrawTx = await staker.buildWithdrawTx({ +const { tx: withdrawTx } = await staker.buildWithdrawTx({ delegatorAddress: userAddress, validatorAddress, positionTickets: unstakeQueue.filter((item) => item.isWithdrawable).map((item) => item.positionTicket) diff --git a/book/ethereum-tutorial/5-minting-os-eth.md b/book/ethereum-tutorial/5-minting-os-eth.md index 7d5b234..331a988 100644 --- a/book/ethereum-tutorial/5-minting-os-eth.md +++ b/book/ethereum-tutorial/5-minting-os-eth.md @@ -88,10 +88,10 @@ If the minting limits and health factors are within acceptable ranges, you can p **To illustrate this, we use the `buildMintTx` method in the following example:** ```typescript -const mintTx = await staker.buildMintTx({ +const { tx: mintTx } = await staker.buildMintTx({ delegatorAddress: userAddress, validatorAddress, - amount: amountToMint + amount: amountToMint // Passed as string, e.g. '1' - 1 ETH }) const request = await walletClient.prepareTransactionRequest(unstakeTx) diff --git a/book/ethereum-tutorial/6-burning-os-eth.md b/book/ethereum-tutorial/6-burning-os-eth.md index 556120e..3420ef2 100644 --- a/book/ethereum-tutorial/6-burning-os-eth.md +++ b/book/ethereum-tutorial/6-burning-os-eth.md @@ -34,10 +34,10 @@ After determining the maximum amount of osETH that can be burned, proceed to bui **Here's how you can implement this with the `buildBurnTx` method:** ```typescript -const burnTx = await staker.buildBurnTx({ +const { tx: burnTx } = await staker.buildBurnTx({ delegatorAddress: userAddress, validatorAddress, - amount: amountToBurn + amount: amountToBurn // Passed as string, e.g. '1' - 1 ETH }) const request = await walletClient.prepareTransactionRequest(unstakeTx) diff --git a/packages/ethereum/src/lib/connector.ts b/packages/ethereum/src/lib/connector.ts index e580e12..9859e65 100644 --- a/packages/ethereum/src/lib/connector.ts +++ b/packages/ethereum/src/lib/connector.ts @@ -1,4 +1,4 @@ -import { createPublicClient, PublicClient, http, Hex, parseGwei } from 'viem' +import { createPublicClient, PublicClient, http, Hex, Chain } from 'viem' import { holesky, mainnet } from 'viem/chains' import { Networks } from './types/networks' @@ -13,6 +13,7 @@ export interface GraphQLRequest { // Connector abstraction, providing on-chain and GraphQL API primitives export class StakewiseConnector { + chain: Chain /** Base URL for Stakewise main graph API */ baseAPI: string /** Base URL for Stakewise subgraph */ @@ -27,18 +28,13 @@ export class StakewiseConnector { mintTokenConfig: Hex /** Stakewise mint token controller contract address */ mintTokenController: Hex - /** Gas max fee */ - maxFeePerGas: bigint - /** Gas max priority fee */ - maxPriorityFeePerGas: bigint constructor (network: Networks, rpcUrl?: string) { // These parameters might need to be changed for Gnosis and Mainnet - this.maxFeePerGas = parseGwei('1.5') - this.maxPriorityFeePerGas = parseGwei('1.5') const transport = rpcUrl ? http(rpcUrl) : http() switch (network) { case 'holesky': + this.chain = holesky this.eth = createPublicClient({ chain: holesky, transport @@ -53,6 +49,7 @@ export class StakewiseConnector { this.mintTokenController = '0x7BbC1733ee018f103A9a9052a18fA9273255Cf36' break case 'ethereum': + this.chain = mainnet this.eth = createPublicClient({ chain: mainnet, transport @@ -60,10 +57,6 @@ export class StakewiseConnector { this.baseAPI = 'https://mainnet-api.stakewise.io/graphql' this.baseGraph = 'https://mainnet-graph.stakewise.io/subgraphs/name/stakewise/stakewise' - this.eth = createPublicClient({ - chain: mainnet, - transport: transport - }) // Stakewise keeper contract this.keeper = '0x6B5815467da09DaA7DC83Db21c9239d98Bb487b5' this.priceOracle = '0x8023518b2192FB5384DAdc596765B3dD1cdFe471' diff --git a/packages/ethereum/src/lib/methods/buildBurnTx.ts b/packages/ethereum/src/lib/methods/buildBurnTx.ts index 902ddfc..2e0b164 100644 --- a/packages/ethereum/src/lib/methods/buildBurnTx.ts +++ b/packages/ethereum/src/lib/methods/buildBurnTx.ts @@ -9,7 +9,7 @@ export async function buildBurnTx (request: { vault: Hex amount: bigint }): Promise { - const { userAccount, vault, amount } = request + const { vault, amount } = request const tx = encodeFunctionData({ abi: VaultABI, @@ -18,7 +18,6 @@ export async function buildBurnTx (request: { }) return { - account: userAccount, to: vault, data: tx } diff --git a/packages/ethereum/src/lib/methods/buildMintTx.ts b/packages/ethereum/src/lib/methods/buildMintTx.ts index 37c1311..1845897 100644 --- a/packages/ethereum/src/lib/methods/buildMintTx.ts +++ b/packages/ethereum/src/lib/methods/buildMintTx.ts @@ -19,7 +19,6 @@ export const buildMintTx = async (request: { }) return { - account: userAccount, to: vault, data: tx } diff --git a/packages/ethereum/src/lib/methods/buildStakeTx.ts b/packages/ethereum/src/lib/methods/buildStakeTx.ts index 86b4031..d6093e9 100644 --- a/packages/ethereum/src/lib/methods/buildStakeTx.ts +++ b/packages/ethereum/src/lib/methods/buildStakeTx.ts @@ -48,7 +48,6 @@ export async function buildStakeTx (request: { } return { - account: userAccount, to: vault, data: tx, value: amount diff --git a/packages/ethereum/src/lib/methods/buildUnstakeTx.ts b/packages/ethereum/src/lib/methods/buildUnstakeTx.ts index 544924a..4e6ffa4 100644 --- a/packages/ethereum/src/lib/methods/buildUnstakeTx.ts +++ b/packages/ethereum/src/lib/methods/buildUnstakeTx.ts @@ -47,7 +47,6 @@ export async function buildUnstakeTx (request: { } return { - account: userAccount, to: vault, data: tx } diff --git a/packages/ethereum/src/lib/methods/buildWithdrawTx.ts b/packages/ethereum/src/lib/methods/buildWithdrawTx.ts index 9dc175f..0f931a7 100644 --- a/packages/ethereum/src/lib/methods/buildWithdrawTx.ts +++ b/packages/ethereum/src/lib/methods/buildWithdrawTx.ts @@ -19,7 +19,9 @@ export async function buildWithdrawTx (request: { const multicallArgs = queueItems .filter((item) => { - return !positionTickets || positionTickets.includes(item.positionTicket.toString()) + if (!positionTickets) return item.isWithdrawable + + return positionTickets.includes(item.positionTicket.toString()) }) .map((item) => { const timestamp = Math.floor(item.when.getTime() / 1000) @@ -27,7 +29,7 @@ export async function buildWithdrawTx (request: { throw new Error('Exit queue index is missing') } if (!item.isWithdrawable) { - throw new Error('Item is not withdrawable') + throw new Error(`Position #${item.positionTicket} is not withdrawable`) } return encodeFunctionData({ @@ -44,7 +46,6 @@ export async function buildWithdrawTx (request: { }) return { - account: userAccount, to: vault, data: tx } diff --git a/packages/ethereum/src/lib/methods/getTxHistory.ts b/packages/ethereum/src/lib/methods/getTxHistory.ts index c9593a6..6198336 100644 --- a/packages/ethereum/src/lib/methods/getTxHistory.ts +++ b/packages/ethereum/src/lib/methods/getTxHistory.ts @@ -2,13 +2,18 @@ import { Hex } from 'viem' import { StakewiseConnector } from '../connector' import { VaultActionType, VaultTransaction } from '../types/transactionHistory' -async function extractTransactionsHistory (connector: StakewiseConnector, vault: Hex): Promise { +async function extractTransactionsHistory ( + connector: StakewiseConnector, + vault: Hex, + userAccount: Hex +): Promise { const vars_getActions = { where: { vault_: { id: vault.toLowerCase() }, - actionType_in: Object.values(VaultActionType) + actionType_in: Object.values(VaultActionType), + address: userAccount.toLowerCase() }, first: 1000, skip: 0 @@ -63,10 +68,10 @@ async function extractTransactionsHistory (connector: StakewiseConnector, vault: export async function getTxHistory (params: { connector: StakewiseConnector vault: Hex - // userAccount: Hex + userAccount: Hex }): Promise> { - const { connector, vault } = params - const interactions = await extractTransactionsHistory(connector, vault) + const { connector, vault, userAccount } = params + const interactions = await extractTransactionsHistory(connector, vault, userAccount) return interactions } diff --git a/packages/ethereum/src/lib/types/transaction.ts b/packages/ethereum/src/lib/types/transaction.ts index 756c6e2..7b1317d 100644 --- a/packages/ethereum/src/lib/types/transaction.ts +++ b/packages/ethereum/src/lib/types/transaction.ts @@ -4,11 +4,6 @@ import { Hex } from 'viem' * Represents an Ethereum transaction. */ export interface Transaction { - /** - * The account (sender) address in hexadecimal format. - */ - account: Hex - /** * The recipient (receiver) address in hexadecimal format. */ diff --git a/packages/ethereum/src/staker.ts b/packages/ethereum/src/staker.ts index ebad24f..3b04de2 100644 --- a/packages/ethereum/src/staker.ts +++ b/packages/ethereum/src/staker.ts @@ -1,6 +1,6 @@ import type { Signer } from '@chorus-one/signer' import { publicKeyConvert } from 'secp256k1' -import { createWalletClient, formatEther, Hex, http, keccak256, parseEther, serializeTransaction } from 'viem' +import { Chain, createWalletClient, formatEther, Hex, http, keccak256, parseEther, serializeTransaction } from 'viem' import { StakewiseConnector } from './lib/connector' import { @@ -76,7 +76,7 @@ export class EthereumStaker { * @param params - Parameters for building the transaction * @param params.delegatorAddress - The delegator (wallet) address to stake from * @param params.validatorAddress - The validator (vault) address to stake with - * @param params.amount - The amount to stake, specified in `ETH` + * @param params.amount - The amount to stake, specified in `ETH`. E.g. "1" - 1 ETH * @param params.referrer - (Optional) The address of the referrer. This is used to track the origin of transactions, * providing insights into which sources or campaigns are driving activity. This can be useful for analytics and * optimizing user acquisition strategies @@ -86,14 +86,14 @@ export class EthereumStaker { async buildStakeTx (params: { delegatorAddress: Hex validatorAddress: Hex - amount: string + amount: string // ETH assets referrer?: Hex }): Promise<{ tx: Transaction }> { const tx = await buildStakeTx({ connector: this.connector, userAccount: params.delegatorAddress, vault: params.validatorAddress, - amount: parseEther(params.amount), + amount: this.parseEther(params.amount), referrer: params.referrer }) @@ -111,20 +111,20 @@ export class EthereumStaker { * @param params - Parameters for building the transaction * @param params.delegatorAddress - The delegator (wallet) address that is unstaking * @param params.validatorAddress - The validator (vault) address to unstake from - * @param params.amount - The amount to unstake, specified in `ETH` + * @param params.amount - The amount to unstake, specified in `ETH`. E.g. "1" - 1 ETH * * @returns Returns a promise that resolves to an Ethereum unstaking transaction. */ async buildUnstakeTx (params: { delegatorAddress: Hex validatorAddress: Hex - amount: string + amount: string // ETH assets }): Promise<{ tx: Transaction }> { const tx = await buildUnstakeTx({ connector: this.connector, userAccount: params.delegatorAddress, vault: params.validatorAddress, - amount: parseEther(params.amount) + amount: this.parseEther(params.amount) }) return { tx } @@ -167,7 +167,7 @@ export class EthereumStaker { * @param params - Parameters for building the transaction * @param params.delegatorAddress - The delegator (wallet) address * @param params.validatorAddress - The validator (vault) address to mint shares for - * @param params.amount - The amount to mint, specified in `osETH` + * @param params.amount - The amount to mint, specified in `osETH`. E.g. "1" - 1 osETH * @param params.referrer - (Optional) The address of the referrer. This is used to track the origin of * transactions, providing insights into which sources or campaigns are driving activity. This can be useful for * analytics and optimizing user acquisition strategies. @@ -177,14 +177,14 @@ export class EthereumStaker { async buildMintTx (params: { delegatorAddress: Hex validatorAddress: Hex - amount: string // shares + amount: string // osETH shares referrer?: Hex }): Promise<{ tx: Transaction }> { const tx = await buildMintTx({ connector: this.connector, userAccount: params.delegatorAddress, vault: params.validatorAddress, - amount: parseEther(params.amount), + amount: this.parseEther(params.amount), referrer: params.referrer }) @@ -197,20 +197,20 @@ export class EthereumStaker { * @param params - Parameters for building the transaction * @param params.delegatorAddress - The delegator (wallet) address * @param params.validatorAddress - The validator (vault) address to burn shares from - * @param params.amount - The amount to burn, specified in `osETH` + * @param params.amount - The amount to burn, specified in `osETH`. E.g. "1" - 1 osETH * * @returns Returns a promise that resolves to an Ethereum burn transaction. */ async buildBurnTx (params: { delegatorAddress: Hex validatorAddress: Hex - amount: string + amount: string // osETH shares }): Promise<{ tx: Transaction }> { const tx = await buildBurnTx({ connector: this.connector, userAccount: params.delegatorAddress, vault: params.validatorAddress, - amount: parseEther(params.amount) + amount: this.parseEther(params.amount) }) return { tx } @@ -301,9 +301,9 @@ export class EthereumStaker { * @returns Returns a promise that resolves to the transaction history for the specified delegator. */ async getTxHistory (params: { delegatorAddress: Hex; validatorAddress: Hex }) { - // TODO: add validatorAddress to the query const txHistory = await getTxHistory({ connector: this.connector, + userAccount: params.delegatorAddress, vault: params.validatorAddress }) @@ -404,8 +404,8 @@ export class EthereumStaker { async getMintHealth (params: { stakeAmount: string; mintAmount: string }) { const health = await getMintHealth({ connector: this.connector, - mintedShares: parseEther(params.mintAmount), - stakedAssets: parseEther(params.stakeAmount) + mintedShares: this.parseEther(params.mintAmount), + stakedAssets: this.parseEther(params.stakeAmount) }) return { health } @@ -418,13 +418,36 @@ export class EthereumStaker { * @param params.signer - A signer instance. * @param params.signerAddress - The address of the signer * @param params.tx - The transaction to sign + * @param params.baseFeeMultiplier - (Optional) The multiplier for fees, which is used to manage fee fluctuations, is applied to the base fee per gas from the latest block to determine the final `maxFeePerGas`. The default value is 1.2. + * @param params.defaultPriorityFee - (Optional) This overrides the the `maxPriorityFeePerGas` estimated by the RPC. * * @returns A promise that resolves to an object containing the signed transaction. */ - async sign (params: { signer: Signer; signerAddress: Hex; tx: Transaction }): Promise<{ signedTx: Hex }> { - const { signer, signerAddress, tx: tx } = params + async sign (params: { + signer: Signer + signerAddress: Hex + tx: Transaction + baseFeeMultiplier?: number + defaultPriorityFee?: string + }): Promise<{ signedTx: Hex }> { + const { signer, signerAddress, tx: tx, baseFeeMultiplier, defaultPriorityFee } = params + + const baseChain = this.connector.chain + + const baseFees = baseChain.fees ?? {} + const fees = { + ...baseFees, + baseFeeMultiplier: baseFeeMultiplier ?? baseFees.baseFeeMultiplier, + defaultPriorityFee: + defaultPriorityFee === undefined ? baseFees.defaultPriorityFee : this.parseEther(defaultPriorityFee) + } + + const chain: Chain = { + ...baseChain, + fees + } const client = createWalletClient({ - chain: this.connector.eth.chain, + chain, transport: http(), account: signerAddress }) @@ -496,4 +519,22 @@ export class EthereumStaker { } } } + + private parseEther (amount: string): bigint { + if (typeof amount === 'bigint') + throw new Error( + 'Amount must be a string, denominated in ETH. e.g. "1.5" - 1.5 ETH. You can use `formatEther` to convert a `bigint` to a string' + ) + if (typeof amount !== 'string') + throw new Error('Amount must be a string, denominated in ETH. e.g. "1.5" - 1.5 ETH.') + if (amount === '') throw new Error('Amount cannot be empty') + let result: bigint + try { + result = parseEther(amount) + } catch (e) { + throw new Error('Amount must be a valid number denominated in ETH. e.g. "1.5" - 1.5 ETH') + } + if (result <= 0n) throw new Error('Amount must be greater than 0') + return result + } }