Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
74 changes: 74 additions & 0 deletions docs/sdk/API/01-vault/01-transactions/addSubVault.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
---
id: addSubVault
slug: /sdk/api/vault/transactions/addsubvault
description: Use the StakeWise SDK addSubVault method to add a sub-vault to a StakeWise V3 meta vault registry on Mainnet, Hoodi, or Gnosis. Called by the meta vault curator to extend the registry of underlying vaults receiving routed deposits.
---

#### Description:

Adds a new sub-vault to a meta vault's registry. Called by the meta vault admin (curator).

#### Prerequisites before calling addSubVault

The contract reverts if the sub-vault is unreachable for the meta vault. Pre-check on the frontend so the user gets an informative error instead of a generic revert:

- If the sub-vault is **private** (`isPrivate: true` from `sdk.vault.getVault`), the meta vault address must be in the sub-vault's whitelist. Read the whitelist with `sdk.vault.getWhitelist({ vaultAddress: subVaultAddress })` and skip or warn the user if the meta vault is not present.
- If the sub-vault has a **blocklist** (`isBlocklist: true`), the meta vault address must not be on the blocklist. Read it with `sdk.vault.getBlocklist({ vaultAddress: subVaultAddress })`.
- The sub-vault itself must not be a meta vault (`isMetaVault: false`). Nesting meta vaults is not supported.
- The sub-vault should not already be in the meta vault's registry. Check the current list with `sdk.vault.getSubVaults({ vaultAddress })`.

```ts
const subVault = await sdk.vault.getVault({ vaultAddress: subVaultAddress })

if (subVault.isMetaVault) {
throw new Error('Cannot add a meta vault as a sub-vault')
}

if (subVault.isPrivate) {
const whitelist = await sdk.vault.getWhitelist({ vaultAddress: subVaultAddress, limit: 1000, skip: 0 })
const isWhitelisted = whitelist.some(({ address }) => address.toLowerCase() === metaVaultAddress.toLowerCase())

if (!isWhitelisted) {
throw new Error('Meta vault must be on the sub-vault whitelist before it can be added')
}
}

if (subVault.isBlocklist) {
const blocklist = await sdk.vault.getBlocklist({ vaultAddress: subVaultAddress, limit: 1000, skip: 0 })
const isBlocked = blocklist.some(({ address }) => address.toLowerCase() === metaVaultAddress.toLowerCase())

if (isBlocked) {
throw new Error('Meta vault is on the sub-vault blocklist and cannot be added')
}
}
```

#### Arguments:

| Name | Type | Required | Description |
|----------------|----------|----------|---------------------------|
| subVaultAddress | `string` | **Yes** | New sub-vault address |
| userAddress | `string` | **Yes** | The user address |
| vaultAddress | `string` | **Yes** | The address of the vault |

#### Example:

```ts
const params = {
subVaultAddress: '0x...',
vaultAddress: '0x...',
userAddress: '0x...',
}

// Send transaction
const hash = await sdk.vault.addSubVault(params)

// Wait for the transaction to be confirmed and indexed
await sdk.provider.waitForTransaction(hash)
await sdk.utils.waitForSubgraph({ hash })

// When you sign transactions on the backend (for custodians)
const { data, to } = await sdk.vault.addSubVault.encode(params)
// Get an approximate gas per transaction
const gas = await sdk.vault.addSubVault.estimateGas(params)
```
5 changes: 5 additions & 0 deletions docs/sdk/API/01-vault/01-transactions/claimExitQueue.md
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,11 @@ const params = {

// Send transaction
const hash = await sdk.vault.claimExitQueue(params)

// Wait for the transaction to be confirmed and indexed
await sdk.provider.waitForTransaction(hash)
await sdk.utils.waitForSubgraph({ hash })

// When you sign transactions on the backend (for custodians)
const { data, to } = await sdk.vault.claimExitQueue.encode(params)
// Get an approximate gas per transaction
Expand Down
165 changes: 145 additions & 20 deletions docs/sdk/API/01-vault/01-transactions/createVault.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,29 +6,38 @@ description: Use the StakeWise SDK createVault method to deploy a new staking va

#### Description:

Create a vault. When the transaction is executed, one gwei of the deposit token must be stored in the vault to avoid [inflation attack](https://blog.openzeppelin.com/a-novel-defense-against-erc4626-inflation-attacks).
Pay attention to chains where the deposit token is not a native token such as Gnosis.
On these chains before creating the vault, ensure that you call the `approve` function on the deposit token contract,
allowing the vault factory address to spend one gwei.
You can retrieve the vault factory contract using the helper function: `sdk.getVaultFactory({ vaultType: params.type, isErc20: params.isErc20 })`.
How to deploy a StakeWise V3 vault: call `sdk.vault.create({ userAddress, type, ... })` on a write-capable SDK (browser wallet or backend signer). The simplest case is `VaultType.Default` with no `vaultToken`, no `capacity`, no `keysManagerFee` - the SDK applies sensible defaults and the returned hash points at a fully working open vault.

The optional `vaultToken: { name, symbol }` argument toggles between an **ERC20 vault** (vault mints a transferable share token) and a **non-ERC20 vault** (shares tracked internally). The `type` argument selects `Default`, `Private`, `Blocklist`, `MetaVault`, or `PrivateMetaVault` access. Regular vault types combine with the ERC20 toggle into six factories; meta vault types add their own factories. The SDK picks the right factory automatically.

When the transaction is executed, one gwei of the deposit token must be stored in the vault to avoid [inflation attack](https://blog.openzeppelin.com/a-novel-defense-against-erc4626-inflation-attacks).
On Mainnet and Hoodi the deposit token is the native asset (ETH) and the SDK attaches the 1 gwei automatically.
On Gnosis the deposit token is GNO, so before calling `sdk.vault.create` you must call `approve` on GNO to allow the vault factory to spend 1 gwei. Retrieve the factory address with `sdk.vault.getVaultFactory({ vaultType: params.type, isErc20: Boolean(params.vaultToken) })`.

**Important**: When creating a meta vault on Gnosis, only `VaultType.MetaVault` is supported (open access). `VaultType.PrivateMetaVault` is Mainnet and Hoodi only. ERC20 share tokens (`vaultToken`) are not available for meta vaults on Gnosis. All meta vaults reject the `isOwnMevEscrow` parameter on every chain.



#### Arguments:

| Name | Type | Required | Description |
|----------------|------------------------------------|----------|------------------------------------------------------------------------------------------------------------------------------------------------------------|
| userAddress | `string` | **Yes** | The address of the user initiating the action. This address will become the vault admin |
| type | `VaultType` | **No** | Allowed vault types: Default, Private and Blocklist. Available vault types can be found in the `enum VaultType` which you can be imported from the library |
| vaultToken | `{ name: string, symbol: string }` | **No** | If provided, the vault will be created with its own ERC20 token |
| capacity | `bigint` | **No** | If provided, should be defined in gwei. By default, capacity is `MaxUint256`; the minimum allowed capacity is `parseEther('32')` |
| keysManagerFee | `number` | **No** | If provided, should be between `0` and `100`, inclusive with a maximum of two decimal digits allowed (e.g., `15.35`). By default, the fee is `0` |
| isOwnMevEscrow | `boolean` | **No** | Defines whether to send block rewards to the Smoothing Pool (`false`) or keep them only to your Vault (`true`). By default, this value is `false` |
| image | `string` | **No** | The vault image in base64 string format (will be uploaded to IPFS; maximum size is 1 MB) |
| displayName | `string` | **No** | The vault display name (will be uploaded to IPFS; maximum size is 30 characters) |
| description | `string` | **No** | The vault description (will be uploaded to IPFS; maximum size is 1000 characters) |

#### Example:
| userAddress | `string` | **Yes** | The address of the user initiating the action. This address will become the vault admin |
| type | `VaultType` | **No** | One of: `Default`, `Private`, `Blocklist`, `MetaVault`, `PrivateMetaVault`. Imported from the library: `import { VaultType } from '@stakewise/v3-sdk'`. Use `MetaVault` / `PrivateMetaVault` to deploy a meta vault that holds a registry of sub-vaults; `PrivateMetaVault` is Mainnet/Hoodi only. |
| vaultToken | `{ name: string, symbol: string }` | **No** | If provided, the vault will be created with its own ERC20 token |
| capacity | `bigint` | **No** | If provided, should be defined in gwei. By default, capacity is `MaxUint256`; the minimum allowed capacity is `parseEther('32')`|
| keysManagerFee | `number` | **No** | If provided, should be between `0` and `100`, inclusive with a maximum of two decimal digits allowed (e.g., `15.35`). By default, the fee is `0`|
| isOwnMevEscrow | `boolean` | **No** | Defines whether to send block rewards to the Smoothing Pool (`false`) or keep them only to your Vault (`true`). By default, this value is `false`. **Note**: This parameter is not supported for metavaults|
| image | `string` | **No** | The vault image in base64 string format (will be uploaded to IPFS; maximum size is 1 MB)|
| displayName | `string` | **No** | The vault display name (will be uploaded to IPFS; maximum size is 30 characters)|
| description | `string` | **No** | The vault description (will be uploaded to IPFS; maximum size is 1000 characters)|

#### Example: ERC20 vault on Mainnet

```ts
import { MaxUint256 } from 'ethers'
import { VaultType } from '@stakewise/v3-sdk'

const params = {
userAddress: '0x...',
type: VaultType.Default,
Expand All @@ -44,11 +53,127 @@ const params = {
description: 'Example description',
}

// Transaction example
// Send transaction to create a vault
// Send transaction
const hash = await sdk.vault.create(params)

// Wait for the transaction to be confirmed and indexed
await sdk.provider.waitForTransaction(hash)
await sdk.utils.waitForSubgraph({ hash })

// When you sign transactions on the backend (for custodians)
const { data, to, value } = await sdk.vault.deposit.encode(params)
const { data, to, value } = await sdk.vault.create.encode(params)
// Get an approximate gas per transaction
const gas = await sdk.vault.deposit.estimateGas(params)
const gas = await sdk.vault.create.estimateGas(params)
```

#### Example: non-ERC20 Private vault

Omit `vaultToken` to deploy a non-ERC20 vault and switch `type` to restrict access:

```ts
import { VaultType } from '@stakewise/v3-sdk'

const hash = await sdk.vault.create({
userAddress: '0x...',
type: VaultType.Private,
isOwnMevEscrow: false,
keysManagerFee: 5,
displayName: 'Private vault',
})

// Wait for the transaction to be confirmed and indexed
await sdk.provider.waitForTransaction(hash)
await sdk.utils.waitForSubgraph({ hash })
```

#### Example: ERC20 vault on Gnosis (with GNO approve)

On Gnosis the 1 gwei security deposit is GNO, not native xDAI. Approve the vault factory before `sdk.vault.create`:

```ts
import { MaxUint256 } from 'ethers'
import { VaultType } from '@stakewise/v3-sdk'

const userAddress = '0x...'
const isErc20 = true

const factoryAddress = await sdk.vault.getVaultFactory({
vaultType: VaultType.Default,
isErc20,
})

const depositTokenAddress = sdk.config.addresses.tokens.depositToken
const gno = sdk.contracts.helpers.createErc20(depositTokenAddress)
const signer = await sdk.provider.getSigner(userAddress)

const approveTx = await gno.connect(signer).approve(factoryAddress, MaxUint256)
await approveTx.wait()

const hash = await sdk.vault.create({
userAddress,
type: VaultType.Default,
vaultToken: { name: 'Gnosis Vault Share', symbol: 'GVS' },
isOwnMevEscrow: false,
keysManagerFee: 5,
displayName: 'Gnosis ERC20 vault',
})

// Wait for the transaction to be confirmed and indexed
await sdk.provider.waitForTransaction(hash)
await sdk.utils.waitForSubgraph({ hash })
```

#### Example: meta vault on Gnosis

On Gnosis a meta vault uses `VaultType.MetaVault` (open) - `PrivateMetaVault` is not supported. Meta vaults do not accept `vaultToken` (no ERC20 share token on Gnosis meta vaults) and never accept `isOwnMevEscrow`. The sub-vaults underneath the meta vault keep their own MEV configuration.

```ts
import { MaxUint256 } from 'ethers'
import { VaultType } from '@stakewise/v3-sdk'

const userAddress = '0x...'

const factoryAddress = await sdk.vault.getVaultFactory({
vaultType: VaultType.MetaVault,
isErc20: false,
})

const depositTokenAddress = sdk.config.addresses.tokens.depositToken
const gno = sdk.contracts.helpers.createErc20(depositTokenAddress)
const signer = await sdk.provider.getSigner(userAddress)

const approveTx = await gno.connect(signer).approve(factoryAddress, MaxUint256)
await approveTx.wait()

const hash = await sdk.vault.create({
userAddress,
type: VaultType.MetaVault,
keysManagerFee: 5,
displayName: 'Gnosis Meta Vault',
})

// Wait for the transaction to be confirmed and indexed
await sdk.provider.waitForTransaction(hash)
await sdk.utils.waitForSubgraph({ hash })
```

After deploying the meta vault, the curator (the `userAddress` above) populates its registry by calling `sdk.vault.addSubVault({ vaultAddress: metaVaultAddress, subVaultAddress, userAddress })` for each sub-vault.

#### Example: private meta vault on Mainnet

On Mainnet and Hoodi, `VaultType.PrivateMetaVault` is supported. Mainnet does not require an approve step (the 1 gwei security deposit is native ETH and the SDK attaches it automatically):

```ts
import { VaultType } from '@stakewise/v3-sdk'

const hash = await sdk.vault.create({
userAddress: '0x...',
type: VaultType.PrivateMetaVault,
keysManagerFee: 5,
displayName: 'Private Meta Vault',
})

// Wait for the transaction to be confirmed and indexed
await sdk.provider.waitForTransaction(hash)
await sdk.utils.waitForSubgraph({ hash })
```
5 changes: 5 additions & 0 deletions docs/sdk/API/01-vault/01-transactions/deposit.md
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,11 @@ const params = {

// Send transaction
const hash = await sdk.vault.deposit(params)

// Wait for the transaction to be confirmed and indexed
await sdk.provider.waitForTransaction(hash)
await sdk.utils.waitForSubgraph({ hash })

// When you sign transactions on the backend (for custodians)
const { data, to, value } = await sdk.vault.deposit.encode(params)
// Get an approximate gas per transaction
Expand Down
39 changes: 39 additions & 0 deletions docs/sdk/API/01-vault/01-transactions/ejectSubVault.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
---
id: ejectSubVault
slug: /sdk/api/vault/transactions/ejectsubvault
description: Use the StakeWise SDK ejectSubVault method to remove an active sub-vault from a StakeWise V3 meta vault registry. Called by the meta vault curator to drop an in-use sub-vault from the registry.
---

#### Description:

Ejecting a sub-vault from the vault registry.

#### Arguments:

| Name | Type | Required | Description |
|----------------|----------|----------|---------------------------|
| subVaultAddress | `string` | **Yes** | The sub-vault address to eject |
| userAddress | `string` | **Yes** | The user address |
| vaultAddress | `string` | **Yes** | The address of the vault |

#### Example:

```ts
const params = {
subVaultAddress: '0x...',
vaultAddress: '0x...',
userAddress: '0x...',
}

// Send transaction
const hash = await sdk.vault.ejectSubVault(params)

// Wait for the transaction to be confirmed and indexed
await sdk.provider.waitForTransaction(hash)
await sdk.utils.waitForSubgraph({ hash })

// When you sign transactions on the backend (for custodians)
const { data, to } = await sdk.vault.ejectSubVault.encode(params)
// Get an approximate gas per transaction
const gas = await sdk.vault.ejectSubVault.estimateGas(params)
```
12 changes: 11 additions & 1 deletion docs/sdk/API/01-vault/01-transactions/operate.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,12 @@ description: Use the StakeWise SDK operate method to update vault settings such

Updates the vault by authorized personnel such as the vault admin, whitelistManager, blocklist manager, validators manager.

#### Constraints

- **One access mode per call.** The vault is either Private (whitelist) or Blocklist, never both. Passing `whitelist` / `whitelistManager` together with `blocklist` / `blocklistManager` in the same call throws an error before sending the transaction. Update one mode at a time.
- **700-address chunk limit.** `whitelist` and `blocklist` arrays are capped at 700 entries per call. Larger lists must be split across multiple sequential transactions; the SDK throws before sending if the limit is exceeded.
- **No vault-type pre-check.** The SDK does not verify that the target vault matches the access mode you are updating. Calling `whitelist` updates on a Default vault reverts at execution. Pre-check the vault type with `sdk.vault.getVault({ vaultAddress })` (`isPrivate`, `isBlocklist`) before calling `operate`.

#### Arguments:

| Name | Type | Required | Access | Description |
Expand All @@ -25,7 +31,6 @@ Updates the vault by authorized personnel such as the vault admin, whitelistMana
| vaultAddress | `string` | **Yes** | - | The address of the vault |
| admin | `string` | **No** | - | Changing the vault administrator |
| feePercent | `number` | **No** | Admin | Changing fee percent charged by the vault |

#### Example:

```ts
Expand Down Expand Up @@ -88,6 +93,11 @@ const blocklistParams = {

// Send transaction
const hash = await sdk.vault.operate(params)

// Wait for the transaction to be confirmed and indexed
await sdk.provider.waitForTransaction(hash)
await sdk.utils.waitForSubgraph({ hash })

// When you sign transactions on the backend (for custodians)
const { data, to } = await sdk.vault.operate.encode(params)
// Get an approximate gas per transaction
Expand Down
39 changes: 39 additions & 0 deletions docs/sdk/API/01-vault/01-transactions/rejectSubVault.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
---
id: rejectSubVault
slug: /sdk/api/vault/transactions/rejectsubvault
description: Use the StakeWise SDK rejectSubVault method to reject a proposed sub-vault from a StakeWise V3 meta vault registry before it becomes active. Called by the meta vault curator to discard a pending sub-vault proposal.
---

#### Description:

Rejecting a sub-vault from the vault registry.

#### Arguments:

| Name | Type | Required | Description |
|----------------|----------|----------|---------------------------|
| subVaultAddress | `string` | **Yes** | The sub-vault address to reject |
| userAddress | `string` | **Yes** | The user address |
| vaultAddress | `string` | **Yes** | The address of the vault |

#### Example:

```ts
const params = {
subVaultAddress: '0x...',
vaultAddress: '0x...',
userAddress: '0x...',
}

// Send transaction
const hash = await sdk.vault.rejectSubVault(params)

// Wait for the transaction to be confirmed and indexed
await sdk.provider.waitForTransaction(hash)
await sdk.utils.waitForSubgraph({ hash })

// When you sign transactions on the backend (for custodians)
const { data, to } = await sdk.vault.rejectSubVault.encode(params)
// Get an approximate gas per transaction
const gas = await sdk.vault.rejectSubVault.estimateGas(params)
```
Loading
Loading