Skip to main content

Staking and Delegation

The Rust SDK covers both sides of staking: operating a pool and delegating to one. The same workflow exists for the command line (concepts: pledge, cost per block, margin ratio, decommission key), Go, and JavaScript (delegations only).

The reward split: the staker receives cost_per_block + margin × (block_reward − cost_per_block); delegators share the remainder proportionally to their stake.

Creating a pool​

use mintlayer_sdk::wallet::{self, Amount, CreatePoolParams, TxOptions};

let c = wallet::Client::new("http://127.0.0.1:3034");

let result = c.create_stake_pool(CreatePoolParams {
account: 0,
amount: Amount::from_atoms(40_000_000_000_000), // pledge
cost_per_block: Amount::from_atoms(100_000_000), // flat fee per block
margin_ratio_per_thousand: "100".into(), // 10% staker cut
decommission_address: "mtc1q...".into(),
staker_address: None, // None = wallet-derived
vrf_public_key: None, // None = wallet-derived
options: TxOptions::default(),
}).await?;
println!("pool tx id: {}", result.tx_id);

margin_ratio_per_thousand is the staker's cut in thousandths as a decimal string: "100" = 10%, "50" = 5%, "1000" = 100%.

Operating the pool​

c.start_staking(0).await?;
println!("{:?}", c.staking_status(0).await?); // Staking | NotStaking
c.stop_staking(0).await?; // stops production; the pool stays alive

for p in c.list_owned_pools(0).await? {
println!("pool {} pledge={}", p.pool_id, p.pledge.atoms().unwrap_or_default());
}

// Shut the pool down; the pledge returns to output_address after maturity.
c.decommission_stake_pool(wallet::DecommissionParams {
account: 0,
pool_id: "mpool1...".into(),
output_address: "mtc1q_return...".into(),
options: TxOptions::default(),
}).await?;

stop_staking pauses block production without touching delegations or staked funds. Delegators must withdraw separately.

Delegating​

Create the delegation record first, then fund it (waiting for the creation transaction to confirm in between):

use mintlayer_sdk::wallet::{self, Amount, CreateDelegationParams, DelegateParams, TxOptions};

let created = c.create_delegation(CreateDelegationParams {
account: 0,
address: "mtc1q_owner...".into(), // address that can withdraw
pool_id: "mpool1...".into(),
options: TxOptions::default(),
}).await?;
println!("delegation id: {}", created.delegation_id);

c.delegate_staking(DelegateParams {
account: 0,
amount: Amount::from_atoms(10_000_000_000_000), // 100 ML
delegation_id: created.delegation_id,
options: TxOptions::default(),
}).await?;

Multiple delegate_staking calls to the same delegation increase the stake. Withdrawals arrive after the consensus lock period:

use mintlayer_sdk::wallet::{Amount, TxOptions, WithdrawParams};

c.withdraw_from_delegation(WithdrawParams {
account: 0,
address: "mtc1q_recipient...".into(),
amount: Amount::from_atoms(5_000_000_000_000), // 50 ML
delegation_id: "mdelg1...".into(),
options: TxOptions::default(),
}).await?;

for d in c.list_delegations(0).await? {
println!("{} pool={} balance={}", d.delegation_id, d.pool_id, d.balance.atoms().unwrap_or_default());
}

Reading state from the indexer​

let infos = idx.delegations("mtc1q_owner...").await?; // by owner address
let delegation = idx.delegation("mdelg1...").await?; // by delegation id
println!("next nonce: {}", delegation.next_nonce); // string-encoded u64

To walk all pools (deep listing, not just the first page), use the creation-height cursor pager; the by_pledge sort only exists on the offset-based list_pools:

let mut pools = idx.pools_pager(100);
while let Some(page) = pools.next_page().await? {
for pool in page.items {
println!("pool {} pledge={}", pool.pool_id, pool.staker_balance.decimal);
}
}

Cursor pages are stable only once the indexer's scanner is fully caught up; during catch-up or a reorg a walk may skip or repeat a pool.

Manual transaction building​

The crypto module encodes pool and delegation outputs/inputs for full-custody flows: encode_output_create_stake_pool with encode_stake_pool_data, encode_output_delegate_staking, encode_input_for_withdraw_from_delegation (nonce from the indexer), and get_pool_id / get_delegation_id to predict ids from the inputs. See Staking in the SDK reference for the full encoders.