# Volo Liquid Staking API Reference

## Volo Staking API Reference

**Source Code**: <https://github.com/Sui-Volo/volo-smart-contracts/tree/main/liquid_staking>

**Contract Address**: `0x68d22cf8bdbcd11ecba1e094922873e4080d4d11133e2443fddda0bfd11dae20`

> **Note:** In this documentation, `CERT` refers to **vSUI** (Volo Staked SUI). They are the same token - `CERT` is the internal module name used in the smart contract.

***

### Table of Contents

1. Data Structures
2. User Functions
3. View Functions
4. Admin Functions
5. Operator Functions
6. Events
7. Constants & Error Codes

***

### 1. Data Structures

#### StakePool

```move
public struct StakePool has key, store {
    id: UID,
    fee_config: FeeConfig,
    fees: Balance<SUI>,
    boosted_balance: Balance<SUI>,
    boosted_reward_amount: u64,
    accrued_reward_fees: u64,
    validator_pool: ValidatorPool,
    manage: Manage,
    extra_fields: Bag
}
```

| Field                   | Type            | Description                                      |
| ----------------------- | --------------- | ------------------------------------------------ |
| `fee_config`            | `FeeConfig`     | Fee configuration                                |
| `fees`                  | `Balance<SUI>`  | Accumulated protocol fees (stake + unstake fees) |
| `boosted_balance`       | `Balance<SUI>`  | Deposited boosted reward balance                 |
| `boosted_reward_amount` | `u64`           | Amount to distribute per epoch                   |
| `accrued_reward_fees`   | `u64`           | Accumulated reward fees (not yet collected)      |
| `validator_pool`        | `ValidatorPool` | Validator management                             |
| `manage`                | `Manage`        | Version and pause state                          |

***

#### ValidatorPool

```move
public struct ValidatorPool has store {
    sui_pool: Balance<SUI>,
    validator_infos: vector<ValidatorInfo>,
    total_sui_supply: u64,
    last_refresh_epoch: u64,
    total_weight: u64,
    manage: Manage,
    extra_fields: Bag
}
```

| Field                | Type                    | Description                                |
| -------------------- | ----------------------- | ------------------------------------------ |
| `sui_pool`           | `Balance<SUI>`          | Buffer for stake/unstake operations        |
| `validator_infos`    | `vector<ValidatorInfo>` | All validator information                  |
| `total_sui_supply`   | `u64`                   | Total SUI managed (sui\_pool + all stakes) |
| `last_refresh_epoch` | `u64`                   | Last epoch when pool was refreshed         |
| `total_weight`       | `u64`                   | Sum of all validator weights               |

***

#### ValidatorInfo

```move
public struct ValidatorInfo has store {
    staking_pool_id: ID,
    validator_address: address,
    active_stake: Option<FungibleStakedSui>,
    inactive_stake: Option<StakedSui>,
    exchange_rate: PoolTokenExchangeRate,
    total_sui_amount: u64,
    assigned_weight: u64,
    last_refresh_epoch: u64,
    extra_fields: Bag
}
```

| Field               | Type                        | Description                        |
| ------------------- | --------------------------- | ---------------------------------- |
| `staking_pool_id`   | `ID`                        | Validator's staking pool ID        |
| `validator_address` | `address`                   | Validator address                  |
| `active_stake`      | `Option<FungibleStakedSui>` | Active stake (earning rewards)     |
| `inactive_stake`    | `Option<StakedSui>`         | Inactive stake (first epoch)       |
| `exchange_rate`     | `PoolTokenExchangeRate`     | Current exchange rate              |
| `total_sui_amount`  | `u64`                       | Total SUI staked to this validator |
| `assigned_weight`   | `u64`                       | Weight for allocation              |

***

#### FeeConfig

```move
public struct FeeConfig has store {
    stake_fee_bps: u64,
    unstake_fee_bps: u64,
    reward_fee_bps: u64,
    unstake_fee_redistribution_bps: u64,
    extra_fields: Bag
}
```

| Field                            | Type  | Max Value    | Description                             |
| -------------------------------- | ----- | ------------ | --------------------------------------- |
| `stake_fee_bps`                  | `u64` | 500 (5%)     | Fee on staking                          |
| `unstake_fee_bps`                | `u64` | 500 (5%)     | Fee on unstaking                        |
| `reward_fee_bps`                 | `u64` | 10000 (100%) | Performance fee on rewards              |
| `unstake_fee_redistribution_bps` | `u64` | 10000 (100%) | Portion of unstake fee returned to pool |

***

#### Metadata\<CERT>

```move
public struct Metadata<phantom T> has key, store {
    id: UID,
    version: u64,
    total_supply: Supply<T>,
}
```

Shared object storing the vSUI minting authority.

***

#### Capability Structs

```move
public struct AdminCap has key, store { id: UID }
public struct OperatorCap has key, store { id: UID }
```

***

### 2. User Functions

#### stake\_entry

Stake SUI and receive vSUI.

```move
public entry fun stake_entry(
    self: &mut StakePool,
    metadata: &mut Metadata<CERT>,
    system_state: &mut SuiSystemState,
    sui: Coin<SUI>,
    ctx: &mut TxContext
)
```

**Parameters:**

| Name           | Type                  | Description                   |
| -------------- | --------------------- | ----------------------------- |
| `self`         | `&mut StakePool`      | The stake pool object         |
| `metadata`     | `&mut Metadata<CERT>` | vSUI metadata (shared object) |
| `system_state` | `&mut SuiSystemState` | Sui system state (`0x5`)      |
| `sui`          | `Coin<SUI>`           | SUI coin to stake             |

**Requirements:**

* Pool must not be paused
* SUI amount >= 0.1 SUI (100,000,000 MIST)

**Emits:** `StakeEventExt`

**TypeScript Example:**

```typescript
import { Transaction } from '@mysten/sui/transactions';

const PACKAGE_ID = '0x68d22cf8bdbcd11ecba1e094922873e4080d4d11133e2443fddda0bfd11dae20';
const STAKE_POOL = '<STAKE_POOL_OBJECT_ID>';
const CERT_METADATA = '<CERT_METADATA_OBJECT_ID>';
const SUI_SYSTEM_STATE = '0x5';

async function stake(suiAmount: bigint) {
    const tx = new Transaction();
    const [coin] = tx.splitCoins(tx.gas, [suiAmount]);

    tx.moveCall({
        target: `${PACKAGE_ID}::stake_pool::stake_entry`,
        arguments: [
            tx.object(STAKE_POOL),
            tx.object(CERT_METADATA),
            tx.object(SUI_SYSTEM_STATE),
            coin,
        ],
    });

    return tx;
}
```

***

#### stake

Non-entry version that returns the minted vSUI.

```move
public fun stake(
    self: &mut StakePool,
    metadata: &mut Metadata<CERT>,
    system_state: &mut SuiSystemState,
    sui: Coin<SUI>,
    ctx: &mut TxContext
): Coin<CERT>
```

**Returns:** `Coin<CERT>` - The minted vSUI coin

***

#### unstake\_entry

Redeem vSUI for SUI.

```move
public entry fun unstake_entry(
    self: &mut StakePool,
    metadata: &mut Metadata<CERT>,
    system_state: &mut SuiSystemState,
    cert: Coin<CERT>,
    ctx: &mut TxContext
)
```

**Parameters:**

| Name   | Type         | Description         |
| ------ | ------------ | ------------------- |
| `cert` | `Coin<CERT>` | vSUI coin to redeem |

**Requirements:**

* Pool must not be paused
* vSUI amount >= 0.1 SUI equivalent

**Emits:** `UnstakeEventExt`

**TypeScript Example:**

```typescript
async function unstake(vsuiObjectId: string) {
    const tx = new Transaction();

    tx.moveCall({
        target: `${PACKAGE_ID}::stake_pool::unstake_entry`,
        arguments: [
            tx.object(STAKE_POOL),
            tx.object(CERT_METADATA),
            tx.object(SUI_SYSTEM_STATE),
            tx.object(vsuiObjectId),
        ],
    });

    return tx;
}
```

***

#### unstake

Non-entry version that returns the redeemed SUI.

```move
public fun unstake(
    self: &mut StakePool,
    metadata: &mut Metadata<CERT>,
    system_state: &mut SuiSystemState,
    lst: Coin<CERT>,
    ctx: &mut TxContext
): Coin<SUI>
```

**Returns:** `Coin<SUI>` - The redeemed SUI coin

***

#### delegate\_stake\_entry

Stake SUI with a preferred validator.

```move
public entry fun delegate_stake_entry(
    self: &mut StakePool,
    metadata: &mut Metadata<CERT>,
    system_state: &mut SuiSystemState,
    sui: Coin<SUI>,
    v_address: address,
    ctx: &mut TxContext
)
```

**Parameters:**

| Name        | Type      | Description                 |
| ----------- | --------- | --------------------------- |
| `v_address` | `address` | Preferred validator address |

**Emits:** `DelegateStakeEvent`

**Note:** This function behaves identically to `stake_entry` but additionally emits a `DelegateStakeEvent` with the validator address. The backend uses this event to adjust validator weights.

***

#### rebalance

Rebalance stakes across validators according to weights. This is a permissionless function.

```move
public fun rebalance(
    self: &mut StakePool,
    metadata: &mut Metadata<CERT>,
    system_state: &mut SuiSystemState,
    ctx: &mut TxContext
)
```

**Emits:** `RebalanceEvent`

**TypeScript Example:**

```typescript
async function rebalance() {
    const tx = new Transaction();

    tx.moveCall({
        target: `${PACKAGE_ID}::stake_pool::rebalance`,
        arguments: [
            tx.object(STAKE_POOL),
            tx.object(CERT_METADATA),
            tx.object(SUI_SYSTEM_STATE),
        ],
    });

    return tx;
}
```

***

#### refresh

Update pool state if epoch has changed. Called automatically by stake/unstake.

```move
public fun refresh(
    self: &mut StakePool,
    metadata: &Metadata<CERT>,
    system_state: &mut SuiSystemState,
    ctx: &mut TxContext
): bool
```

**Returns:** `bool` - `true` if epoch rolled over and state was updated

**Emits:** `EpochChangedEvent` (if epoch changed)

***

### 3. View Functions

#### get\_ratio

Returns how many vSUI you get per 1 SUI (in 1e9 precision).

```move
public fun get_ratio(self: &StakePool, metadata: &Metadata<CERT>): u64
```

**Returns:** Ratio in 1e9 precision

**Example:** If returns `950_000_000`, then 1 SUI = 0.95 vSUI

***

#### get\_ratio\_reverse

Returns how many SUI you get per 1 vSUI (in 1e9 precision).

```move
public fun get_ratio_reverse(self: &StakePool, metadata: &Metadata<CERT>): u64
```

**Returns:** Ratio in 1e9 precision

**Example:** If returns `1_052_631_579`, then 1 vSUI ≈ 1.053 SUI

***

#### publish\_ratio

Emit ratio in V1 format (1e18 precision) for compatibility.

```move
public fun publish_ratio(self: &StakePool, metadata: &Metadata<CERT>)
```

***

#### get\_amount\_out

Calculate output amount after fees.

```move
public fun get_amount_out(
    self: &StakePool,
    metadata: &Metadata<CERT>,
    amount_in: u64,
    sui2lst: bool
): u64
```

**Parameters:**

| Name        | Type   | Description                               |
| ----------- | ------ | ----------------------------------------- |
| `amount_in` | `u64`  | Input amount in MIST                      |
| `sui2lst`   | `bool` | `true` for SUI→vSUI, `false` for vSUI→SUI |

**Returns:** Output amount after fees

***

#### sui\_amount\_to\_lst\_amount

Convert SUI amount to vSUI amount (no fees).

```move
public fun sui_amount_to_lst_amount(
    self: &StakePool,
    metadata: &Metadata<CERT>,
    sui_amount: u64
): u64
```

***

#### lst\_amount\_to\_sui\_amount

Convert vSUI amount to SUI amount (no fees).

```move
public fun lst_amount_to_sui_amount(
    self: &StakePool,
    metadata: &Metadata<CERT>,
    lst_amount: u64
): u64
```

***

#### total\_sui\_supply

Returns total SUI managed by the pool.

```move
public fun total_sui_supply(self: &StakePool): u64
```

***

#### total\_lst\_supply

Returns total vSUI supply.

```move
public fun total_lst_supply(metadata: &Metadata<CERT>): u64
```

***

#### validator\_pool

Get reference to validator pool.

```move
public fun validator_pool(self: &StakePool): &ValidatorPool
```

***

#### fee\_config

Get reference to fee configuration.

```move
public fun fee_config(self: &StakePool): &FeeConfig
```

***

#### total\_fees

Returns total accumulated fees (stake fees + unstake fees + reward fees).

```move
public fun total_fees(self: &StakePool): u64
```

***

#### accrued\_reward\_fees

Returns accumulated reward fees not yet collected.

```move
public fun accrued_reward_fees(self: &StakePool): u64
```

***

#### boosted\_balance

Returns remaining boosted reward balance.

```move
public fun boosted_balance(self: &StakePool): u64
```

***

#### ValidatorPool View Functions

```move
public fun sui_pool(self: &ValidatorPool): &Balance<SUI>
public fun validators(self: &ValidatorPool): &vector<ValidatorInfo>
public fun total_weight(self: &ValidatorPool): u64
public fun validator_weights(self: &ValidatorPool): VecMap<address, u64>
public fun validator_stake_amounts(self: &ValidatorPool): (VecMap<address, u64>, VecMap<address, u64>)
public fun total_sui_supply(self: &ValidatorPool): u64
public fun last_refresh_epoch(self: &ValidatorPool): u64
public fun find_validator_index_by_address(self: &ValidatorPool, addr: address): Option<u64>
```

***

#### FeeConfig View Functions

```move
public fun stake_fee_bps(fees: &FeeConfig): u64
public fun unstake_fee_bps(fees: &FeeConfig): u64
public fun reward_fee_bps(fees: &FeeConfig): u64
public fun unstake_fee_redistribution_bps(fees: &FeeConfig): u64
```

***

### 4. Admin Functions

All admin functions require `AdminCap`.

#### set\_paused

Pause or unpause the pool.

```move
public fun set_paused(self: &mut StakePool, _: &AdminCap, paused: bool)
```

**Emits:** `SetPausedEvent`

***

#### migrate\_version

Migrate pool to current version.

```move
public fun migrate_version(self: &mut StakePool, _: &AdminCap)
```

***

#### update\_stake\_fee

Update stake fee.

```move
public fun update_stake_fee(self: &mut StakePool, _: &AdminCap, fee: u64)
```

**Parameters:**

* `fee`: Fee in BPS (max 500)

**Emits:** `FeeUpdateEvent`

***

#### update\_unstake\_fee

Update unstake fee.

```move
public fun update_unstake_fee(self: &mut StakePool, _: &AdminCap, fee: u64)
```

**Parameters:**

* `fee`: Fee in BPS (max 500)

**Emits:** `FeeUpdateEvent`

***

#### update\_reward\_fee

Update reward fee (performance fee).

```move
public fun update_reward_fee(self: &mut StakePool, _: &AdminCap, fee: u64)
```

**Parameters:**

* `fee`: Fee in BPS (max 10000)

**Emits:** `FeeUpdateEvent`

***

#### update\_unstake\_fee\_redistribution

Update unstake fee redistribution percentage.

```move
public fun update_unstake_fee_redistribution(self: &mut StakePool, _: &AdminCap, fee: u64)
```

**Parameters:**

* `fee`: Percentage in BPS (max 10000)

**Emits:** `FeeUpdateEvent`

***

#### update\_boosted\_reward\_amount

Set the amount of boosted rewards to distribute per epoch.

```move
public fun update_boosted_reward_amount(self: &mut StakePool, _: &AdminCap, amount: u64)
```

**Emits:** `BoostedRewardAmountUpdateEvent`

***

#### collect\_fees

Collect accumulated protocol fees.

```move
public fun collect_fees(
    self: &mut StakePool,
    metadata: &mut Metadata<CERT>,
    system_state: &mut SuiSystemState,
    _: &AdminCap,
    ctx: &mut TxContext
): Coin<SUI>
```

**Returns:** `Coin<SUI>` - All accumulated fees

**Emits:** `CollectFeesEvent`

***

#### mint\_operator\_cap

Mint a new OperatorCap.

```move
public fun mint_operator_cap(
    self: &mut StakePool,
    _: &AdminCap,
    recipient: address,
    ctx: &mut TxContext
)
```

**Emits:** `MintOperatorCapEvent`

***

### 5. Operator Functions

All operator functions require `OperatorCap`.

#### set\_validator\_weights

Set validator weight allocation.

```move
public fun set_validator_weights(
    self: &mut StakePool,
    metadata: &mut Metadata<CERT>,
    system_state: &mut SuiSystemState,
    _: &OperatorCap,
    validator_weights: VecMap<address, u64>,
    ctx: &mut TxContext
)
```

**Parameters:**

| Name                | Type                   | Description                        |
| ------------------- | ---------------------- | ---------------------------------- |
| `validator_weights` | `VecMap<address, u64>` | Map of validator address to weight |

**Constraints:**

* Maximum 50 validators
* Total weight <= 10,000

**Emits:** `ValidatorWeightsUpdateEvent`

**TypeScript Example:**

```typescript
import { bcs } from '@mysten/sui/bcs';

async function setValidatorWeights(weights: [string, number][]) {
    const tx = new Transaction();

    // Serialize VecMap as vector of (address, u64) tuples
    const serialized = bcs.vector(
        bcs.tuple([bcs.Address, bcs.u64()])
    ).serialize(weights);

    tx.moveCall({
        target: `${PACKAGE_ID}::stake_pool::set_validator_weights`,
        arguments: [
            tx.object(STAKE_POOL),
            tx.object(CERT_METADATA),
            tx.object(SUI_SYSTEM_STATE),
            tx.object(OPERATOR_CAP),
            tx.pure(serialized),
        ],
    });

    return tx;
}

// Usage
const weights: [string, number][] = [
    ['0xvalidator1...', 5000],
    ['0xvalidator2...', 3000],
    ['0xvalidator3...', 2000],
];
await setValidatorWeights(weights);
```

***

#### deposit\_boosted\_balance

Deposit SUI as boosted rewards.

```move
public fun deposit_boosted_balance(
    self: &mut StakePool,
    _: &OperatorCap,
    coin: &mut Coin<SUI>,
    amount: u64,
    ctx: &mut TxContext
)
```

**Parameters:**

| Name     | Type             | Description              |
| -------- | ---------------- | ------------------------ |
| `coin`   | `&mut Coin<SUI>` | SUI coin to deposit from |
| `amount` | `u64`            | Amount to deposit        |

**Emits:** `DepositBoostedBalanceEvent`

***

### 6. Events

#### StakeEventExt

```move
public struct StakeEventExt has copy, drop {
    sui_amount_in: u64,      // SUI staked
    lst_amount_out: u64,     // vSUI received
    fee_amount: u64          // Stake fee charged
}
```

#### UnstakeEventExt

```move
public struct UnstakeEventExt has copy, drop {
    lst_amount_in: u64,          // vSUI burned
    sui_amount_out: u64,         // SUI received
    fee_amount: u64,             // Unstake fee (to protocol)
    redistribution_amount: u64   // Unstake fee (returned to pool)
}
```

#### EpochChangedEvent

```move
public struct EpochChangedEvent has copy, drop {
    old_sui_supply: u64,         // Previous total SUI
    new_sui_supply: u64,         // New total SUI (after rewards)
    boosted_reward_amount: u64,  // Boosted rewards distributed
    lst_supply: u64,             // Current vSUI supply
    reward_fee: u64              // Reward fee charged
}
```

#### DelegateStakeEvent

```move
public struct DelegateStakeEvent has copy, drop {
    v_address: address,      // Preferred validator
    sui_amount_in: u64,      // SUI staked
    lst_amount_out: u64      // vSUI received
}
```

#### ValidatorWeightsUpdateEvent

```move
public struct ValidatorWeightsUpdateEvent has copy, drop {
    validator_weights: VecMap<address, u64>
}
```

#### RebalanceEvent

```move
public struct RebalanceEvent has copy, drop {
    is_epoch_rolled_over: bool,  // Whether epoch changed
    sender: address              // Who triggered rebalance
}
```

#### FeeUpdateEvent

```move
public struct FeeUpdateEvent has copy, drop {
    field: String,       // Fee field name
    old_value: u64,      // Previous value
    new_value: u64       // New value
}
```

#### CollectFeesEvent

```move
public struct CollectFeesEvent has copy, drop {
    amount: u64          // Total fees collected
}
```

#### SetPausedEvent

```move
public struct SetPausedEvent has copy, drop {
    paused: bool
}
```

#### MintOperatorCapEvent

```move
public struct MintOperatorCapEvent has copy, drop {
    recipient: address
}
```

#### DepositBoostedBalanceEvent

```move
public struct DepositBoostedBalanceEvent has copy, drop {
    before_balance: u64,
    after_balance: u64
}
```

#### BoostedRewardAmountUpdateEvent

```move
public struct BoostedRewardAmountUpdateEvent has copy, drop {
    old_value: u64,
    new_value: u64
}
```

***

### 7. Constants & Error Codes

#### Constants

| Constant                | Value         | Description                         |
| ----------------------- | ------------- | ----------------------------------- |
| `SUI_MIST`              | 1,000,000,000 | 1 SUI in MIST                       |
| `MIN_STAKE_AMOUNT`      | 100,000,000   | Minimum stake/unstake (0.1 SUI)     |
| `MIN_STAKE_THRESHOLD`   | 1,000,000,000 | Minimum stake per validator (1 SUI) |
| `MAX_VALIDATORS`        | 50            | Maximum validators                  |
| `MAX_TOTAL_WEIGHT`      | 10,000        | Maximum sum of weights              |
| `DEFAULT_WEIGHT`        | 100           | Default weight for new validators   |
| `MAX_STAKE_FEE_BPS`     | 500           | Maximum stake fee (5%)              |
| `MAX_UNSTAKE_FEE_BPS`   | 500           | Maximum unstake fee (5%)            |
| `MAX_BPS`               | 10,000        | Maximum reward fee (100%)           |
| `BPS_MULTIPLIER`        | 10,000        | Basis points multiplier             |
| `VERSION`               | 2             | Current contract version            |
| `ACCEPTABLE_MIST_ERROR` | 10            | Allowed rounding error              |

#### Error Codes

| Module           | Code  | Name                         | Description                 |
| ---------------- | ----- | ---------------------------- | --------------------------- |
| `fee_config`     | 20001 | `EInvalidFee`                | Fee exceeds maximum         |
| `stake_pool`     | 30000 | `EZeroMintAmount`            | Cannot mint zero vSUI       |
| `stake_pool`     | 30001 | `ERatio`                     | Ratio invariant violated    |
| `stake_pool`     | 30002 | `EZeroSupply`                | Zero supply error           |
| `stake_pool`     | 30003 | `EUnderMinAmount`            | Below minimum amount        |
| `validator_pool` | 40000 | `ENotEnoughSuiInSuiPool`     | Insufficient SUI            |
| `validator_pool` | 40001 | `ENotActiveValidator`        | Validator not active        |
| `validator_pool` | 40002 | `ETooManyValidators`         | Exceeds max validators      |
| `validator_pool` | 40003 | `EValidatorAlreadyExists`    | Validator exists            |
| `validator_pool` | 40004 | `EValidatorNotFound`         | Validator not found         |
| `validator_pool` | 40005 | `EInvalidValidatorWeight`    | Invalid weight              |
| `validator_pool` | 40006 | `EInvalidValidatorWeightSum` | Weight sum mismatch         |
| `validator_pool` | 40007 | `EInvalidValidatorSize`      | Invalid validator size      |
| `validator_pool` | 40008 | `EMaxTotalWeight`            | Total weight exceeds max    |
| `validator_pool` | 40009 | `ETotalSuiSupplyChanged`     | Supply changed unexpectedly |
| `manage`         | 50001 | `EIncompatibleVersion`       | Version mismatch            |
| `manage`         | 50002 | `EIncompatiblePaused`        | Pool is paused              |
| `cert`           | 1     | `E_INCOMPATIBLE_VERSION`     | Metadata version mismatch   |


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://volosui.gitbook.io/volo/liquid-staking/volo-liquid-staking-api-reference.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
