delegation::delegation_state
Module 0xc0ded0c0::delegation_state
0xc0ded0c0::delegation_state
module delegation::delegation_state
delegation::delegation_state
We first give a brief overview of model of staking Aptos uses and then describe how this module implements a managed stake pool to enable delegation.
In Aptos ecosystem, funds stored in a stake pool (validator) could be distributed among:
active
,inactive
,pending_active
,pending_inactive
.
Similarly, a validator's state could also be
active
,inactive
,pending_active
,pending_inactive
.
New APT is only generated when the validator's state is active
or pending_inactive
, and funds in it are in state active
or pending_inactive
.
There are two kinds of shares represented in the state:
reserved shares which are stored in
reserved_pool
, and,unreserved shares which are stored in
unreserved_pool
.
Reserved shares in are the ones that the owner or a delegator has requested to unstake and withdraw. Funds corresponding to reserved shares are either ALL in pending_inactive
(if lockup has not expired) or they can be withdrawn and dispersed. The dispersal must happen before anyone else can reserve any more shares for withdrawal. So in a managed pool, funds can never be in both inactive
and pending_inactive
. Also, reserved shares and unreserved shares can be unequal in value (per unit share). Their values are calculated independently in this module.
If the funds corresponding to the reserved shares have been in inactive state for more than an epoch, then the value of reserved shares starts to depreciate compared to the value of unreserved shares, if the validator state is active
or pending_inactive
. Hence, before any delegation, any inactive funds are disbursed so that the depreciated shares do not affect the share calculation for the new delegator.
Also, before any new delegations, owner's commission must be paid by everyone so that the new delegator is not subject to any pending commissions.
Payment to the owner is a three step process:
New unreserved shares are minted without adding any new funds to the pool. The resulting depreciation of unreserved shares is equal to the commission due on funds associated with the unreserved shares.
New reserved shares are minted without adding any new funds to the pool at first. The resulting depreciation of reserved shares makes owner own a part of the fund that is currently inactive or
pending_inactive
.commission_exempt
is adjusted to current total worth of the pool to reflect that there are no remaining owner commission at this moment.
Hence, paying commission to owner just manipulates shares, and does not move any coins.
use 0x1::account;
use 0x1::aptos_coin;
use 0x1::coin;
use 0x1::error;
use 0x1::event;
use 0x1::signer;
use 0x1::stake;
use 0x1::timestamp;
use 0xc0ded0c0::math;
use 0xc0ded0c0::pool;
use 0xc0ded0c0::stake_pool_helpers;
Resource SharesData
SharesData
Created when a stake pool owner (validator) signs up via validator_router
. It is stored with the pool owner, and destroyed when they leave the protocol. The stake pool can accept delegations from both the liquid-staking protocol as well as from other delegators.
struct SharesData has key
Resource PendingCommissions
PendingCommissions
Created when the pool owner requests an update to commissions. Destroyed when the new commission takes effect.
struct PendingCommissions has key
Resource Payout
Payout
This is stored with a delegator's account and aggregates across all delegations made by them, potentially across multiple managed_stake_pool
s.
struct Payout has key
Struct WithdrawPayoutEvent
WithdrawPayoutEvent
Event which is generated when a Payout takes place.
struct WithdrawPayoutEvent has drop, store
Constants
Normalizer for the commission.
const COMMISSION_NORMALIZER: u64 = 1000000;
If the value of remaining unreserved shares is less than this when a delegator requests reservation of some of their shares, then all the shares of the delegator are reserved in order to not leave small amount of dust in the protocol. This mitigates a potential grieifing attack on the protocol.
const DUST_TOLERANCE: u64 = 1000000000;
When the total balance does not match the sum of reserved and unreserved balance.
const EBALANCE_ERROR: u64 = 13;
When the stake pool's funds are not in the active
state at initialization.
const ECAN_ONLY_INIT_WITH_ACTIVE_STAKE: u64 = 11;
When the pool has no share holders or no shares to burn
const ECOULD_NOT_BURN_PAYOUT_SHARES: u64 = 2;
When there are funds in both active as well as inactive state at the same time.
const EDELAYED_PAYOUTS: u64 = 7;
When the commission being set is larger than 100%.
const EINVALID_COMMISSION: u64 = 6;
When the delegator tries to withdraw more unreserved shares than they have
const ENOT_ENOUGH_UNRESERVED_SHARES: u64 = 0;
When there are no reserved shares to withdraw.
const ENO_RESERVED_SHARE_SUPPLY: u64 = 10;
When there are no unreserved shares to unstake and withdraw.
const ENO_UNRESERVED_SHARE_SUPPLY: u64 = 9;
When the stake pool's funds do not meet the min_stake
requirement.
const EOWNER_STAKE_TOO_LOW: u64 = 14;
When the funds from the inactive
state have not been disbursed before doing an action which changes the delegation_state
.
const EPAYOUTS_NOT_COMPLETE: u64 = 4;
When the pool has shareholders other than the owner during destruction.
const EPOOL_HAS_SHAREHOLDERS: u64 = 20;
When there are pending commissions to the owner which have not been paid.
const EUNPAID_COMMISSION_TO_OWNER: u64 = 5;
When there is a shareholder with zero shares during dispersal.
const EZERO_ENTRY: u64 = 21;
Initially 100 shares equals 1 OCTA (i.e. 10^-8 APT)
const INITIAL_SHARES_FACTOR: u64 = 100;
The precision for the commission change multiplier.
const MULTIPLIER_PRECISION: u64 = 10000;
Function is_owner_alone_in_the_pool
is_owner_alone_in_the_pool
Returns whether the only shareholder of managed_pool_address
is the owner of the pool.
public fun is_owner_alone_in_the_pool(managed_pool_address: address): bool
Function is_an_active_delegator
is_an_active_delegator
Returns whether the delegator_address
a shareholder in the managed_pool_address
.
public fun is_an_active_delegator(managed_pool_address: address, delegator_address: address): bool
Function assert_no_pending_commissions
assert_no_pending_commissions
Assert that there is no pending commission to the owner.
Abort conditions
If there are unpaid commissions to the owner.
public fun assert_no_pending_commissions(managed_pool_address: address)
Function get_current_commission
get_current_commission
Returns the current_commission
from SharesData
, which is the current commission rate that non-protocol delegators will have to pay.
public fun get_current_commission(managed_pool_address: address): u64
Function get_stake_pool_address
get_stake_pool_address
Returns the stake pool address for the managed pool at managed_pool_address
.
public fun get_stake_pool_address(managed_pool_address: address): address
Function get_total_length_of_share_maps
get_total_length_of_share_maps
Returns the number of shareholders in the managed pool at managed_pool_address
.
public fun get_total_length_of_share_maps(managed_pool_address: address): u64
Function get_share_supply
get_share_supply
Returns the number of shares in the managed pool at managed_pool_address
.
public fun get_share_supply(managed_pool_address: address): u64
Function get_commission_exempt
get_commission_exempt
Returns the commission_exempt
amount in the pool, i.e., the total amount (in APT) on which commission has been paid. It is equal to total worth of the pool, if and only if there is no commission due for any delegator.
This gets reset to current total worth whenever commission is paid to the owner.
public fun get_commission_exempt(managed_pool_address: address): u64
Function get_commission_exempt_reserved
get_commission_exempt_reserved
Returns the commission_exempt
amount in the reserved pool.
public fun get_commission_exempt_reserved(managed_pool_address: address): u64
Function get_commission_exempt_unreserved
get_commission_exempt_unreserved
Returns the commission_exempt
amount in the unreserved pool.
public fun get_commission_exempt_unreserved(managed_pool_address: address): u64
Function get_balance_reserved
get_balance_reserved
Returns the current value of the shares in the reserved pool of managed pool at managed_pool_address
for delegator
.
public fun get_balance_reserved(managed_pool_address: address, delegator_address: address): u64
Function get_balance_unreserved
get_balance_unreserved
Returns the current value of the shares in the unreserved pool of managed pool at managed_pool_address
for delegator
.
public fun get_balance_unreserved(managed_pool_address: address, delegator: address): u64
Function get_reserved_total_balance
get_reserved_total_balance
Returns the total value of the shares in the reserved pool of managed pool at managed_pool_address
.
public fun get_reserved_total_balance(managed_pool_address: address): u64
Function get_reserved_share_supply
get_reserved_share_supply
Returns the total number of shares in the reserved pool of managed pool at managed_pool_address
.
fun get_reserved_share_supply(managed_pool_address: address): u64
Function get_unreserved_share_supply
get_unreserved_share_supply
Returns the total number of shares in the unreserved pool of managed pool at managed_pool_address
.
fun get_unreserved_share_supply(managed_pool_address: address): u64
Function get_value_to_shares_unreserved
get_value_to_shares_unreserved
Converts a given APT amount
to the number of shares in the unreserved pool of the managed pool at managed_pool_address
.
public fun get_value_to_shares_unreserved(managed_pool_address: address, amount: u64): u64
Function get_shares_to_value_reserved
get_shares_to_value_reserved
Converts a given num_shares
shares in the reserved pool of the managed pool at managed_pool_address
to their value in APT.
Abort condition
If there are no shares in the reserved pool.
fun get_shares_to_value_reserved(managed_pool_address: address, num_shares: u64): u64
Function get_shares_to_value_unreserved
get_shares_to_value_unreserved
Converts a given num_shares
shares in the unreserved pool of the managed pool at managed_pool_address
to their value in APT.
Abort condition
If there are no shares in the unreserved pool.
public fun get_shares_to_value_unreserved(managed_pool_address: address, num_shares: u64): u64
Function get_unreserved_shares_with_delegator
get_unreserved_shares_with_delegator
Returns shares owned by a delegator delegator
in the unreserved pool of the ManagesStakingPool
at managed_pool_address
.
public fun get_unreserved_shares_with_delegator(managed_pool_address: address, delegator: address): u64
Function get_reserved_shares_with_delegator
get_reserved_shares_with_delegator
Returns shares owned by a delegator delegator
in the reserved pool of the managed pool at managed_pool_address
.
public fun get_reserved_shares_with_delegator(managed_pool_address: address, delegator: address): u64
Function get_protocol_delegator_address
get_protocol_delegator_address
Returns the address of the protocol. This is the address that protocol uses to delegate to an associated validator.
public fun get_protocol_delegator_address(managed_pool_address: address): address
Function get_protocol_commission
get_protocol_commission
Returns the protocol_commission
of the managed pool at managed_pool_address
.
public fun get_protocol_commission(managed_pool_address: address): u64
Function get_protocol_commission_change_multiplier
get_protocol_commission_change_multiplier
Returns the factor by which the protocol commission will be increased by the validator in future. If the protocol is not expecting any change, or the protocol commission is being lowered, this will return MULTIPLIER_PRECISION
. Otherwise it will return a number less than MULTIPLIER_PRECISION
, according to MULTIPLIER_PRECISION
* (100 - new_commssion_pct) / (100 - old_commission_pct)
.
public fun get_protocol_commission_change_multiplier(managed_pool_address: address): u64
Function get_protocol_payout_value
get_protocol_payout_value
Returns the total amount of protocol's inactive and withdrawable funds with an associated validator.
public fun get_protocol_payout_value(managed_pool_address: address): u64
Function get_num_shares_to_reserve_rounded_up
get_num_shares_to_reserve_rounded_up
Returns the shares which the delegator_address
is allowed to withdraw from the unreserved pool of the managed pool at managed_pool_address
, given that the delegator wants to withdraw amount
APT. This ensures that the amount of APT withdrawn does not leave less than DUST_TOLERANCE
APT in the unreserved pool for the delegator.
public fun get_num_shares_to_reserve_rounded_up(managed_pool_address: address, delegator_address: address, amount: u64): u64
Function payout_value
payout_value
Returns the withdrawable amount of a delegator delegator_address
.
public fun payout_value(delegator_address: address): u64
Function extract_payout
extract_payout
Extracts the payout which is currently available to the delegator
.
public fun extract_payout(delegator: &signer): coin::Coin<aptos_coin::AptosCoin>
Function safe_create_payout_store
safe_create_payout_store
Creates and saves an empty payout struct for the user signaling they want to receive payouts
public fun safe_create_payout_store(creator: &signer)
Function withdraw_payout
withdraw_payout
The delegator
can request their unlocked stake that have accrued since their last withdrawal.
public fun withdraw_payout(delegator: &signer)
Function initialize_internal
initialize_internal
Initializes a managed pool for the managed_pool_owner
using the stake pool at stake_pool_address
. The protocol's address is set to protocol_delegator_address
.
Requirements
Stake pool at
stake_pool_address
must only haveactive
stake.There should not be any
pending_active
stake: wait an epoch for pending active stake to become active.There should not be any
inactive
stake: either withdraw or restake inactive funds before initialization.Stake pool must have more than
min_stake
in the pool as specified byaptos_framework
Restrictions
Only the
friend
moduledelegation_service
can call this function.
Abort conditions
If the owner's stake is too low.
If the owner's stake is not active.
public(friend) fun initialize_internal(managed_pool_owner: &signer, protocol_delegator_address: address, commission_recipient_address: address, stake_pool_address: address)
Function change_commission_internal
change_commission_internal
Changes the commission of the managed pool. new_default_commission
is the commission charged to non-protocol delegators while new_protocol_commission
is the commission charged to the protocol delegator. If previous commission rates can be activated. Do so.
Assumes
new_protocol_commission
is smaller than thenew_default_commission
.
Restrictions
Only the
friend
moduledelegation_service
can call this function.
Aborts conditions
If the
new_default_commission
is more thanCOMMISSION_NORMALIZER
.If the
new_protocol_commission
is more thanCOMMISSION_NORMALIZER
.
public(friend) fun change_commission_internal(pool_owner: &signer, new_default_commission: u64, new_protocol_commission: u64)
Function change_commission_recipient_internal
change_commission_recipient_internal
Changes the commission recipient of the managed pool.
Restrictions
Only the
friend
moduledelegation_service
can call this function.
public(friend) fun change_commission_recipient_internal(managed_pool_address: address, new_commission_recipient_address: address)
Function disperse_all_payouts
disperse_all_payouts
This function extracts withdrawable balance and disperses all shares in reserved pool of the managed_pool_address
.
Restrictions
Only the
friend
moduledelegation_service
can call this function.Needs the
OwnerCapability
of the stake pool owner.Must pay pending commissions before calling this.
public(friend) fun disperse_all_payouts(managed_pool_address: address, stake_pool_owner_cap: &stake::OwnerCapability)
Function pay_commission_to_owner
pay_commission_to_owner
This mints new shares for the owner (and for the protocol as a commission rebate), so that owners new worth increases exactly by total commission due, and everyone else's worth decreases according to their due commission. Returns the total value of shares minted. If pending commission rates can be activated at the end. Do so.
This function does not move any coins, and can be called at any time.
Restrictions
Only the
friend
moduledelegation_service
can call this function.
public(friend) fun pay_commission_to_owner(managed_pool_address: address): u64
Function delegate_internal
delegate_internal
This function delegates coins_to_delegate
on behalf of the delegator
to the managed stake pool at managed_pool_address
.
Restrictions
Only the
friend
moduledelegation_service
can call this function.The
OwnerCapability
for the underlying stake pool is required.
Assumes
max number of delegations is not violated
min delegation condition is not violated
commission has been paid and payouts dispersed
must pay pending commissions before calling this.
must disperse all payouts before calling this.
public(friend) fun delegate_internal(delegator: &signer, coins_to_delegate: coin::Coin<aptos_coin::AptosCoin>, managed_pool_address: address, stake_pool_owner_cap: &stake::OwnerCapability)
Function reserve_shares_for_withdraw_internal
reserve_shares_for_withdraw_internal
Reserves at least shares num_shares
to be withdrawn for delegator_address
from the managed pool at managed_pool_address
.
Restrictions
Only the
friend
moduledelegation_service
can call this function.The
OwnerCapability
for the underlying stake pool is required.Must pay pending commissions before calling this.
Must disperse all payouts before calling this.
Aborts if
num_shares
is greater than the number of shares owned by the delegator in the unreserved pool.
public(friend) fun reserve_shares_for_withdraw_internal(delegator_address: address, num_shares: u64, managed_pool_address: address, stake_pool_owner_cap: &stake::OwnerCapability)
Function self_destruct_internal
self_destruct_internal
Sets the managed pool at managed_pool_address
to be destroyed.
Restrictions
Only the
friend
moduledelegation_service
can call this function.
Assumes
The reserved and unreserved pools are empty.
Abort conditions
Owner is not the only delegator in either reserved or unreserved pool.
public(friend) fun self_destruct_internal(managed_pool_address: address)
Function create_or_update_pending_commissions
create_or_update_pending_commissions
Create or update an existing PendingCommissions
for the pool_owner
Note: the update timestamp will reset for existing requests, thus restarting the clock for lockup duration commission update.
fun create_or_update_pending_commissions(pool_owner: &signer, new_default_commission: u64, new_protocol_commission: u64)
Function commission_rate_bookkeeping
commission_rate_bookkeeping
If a StakingConfig lockup duration has passed since a requested commission update. Then we activate the pending commission rates.
fun commission_rate_bookkeeping(managed_pool_address: address, shares_data: &mut delegation_state::SharesData)
Function pay_commission_to_owner_reserved
pay_commission_to_owner_reserved
Mints new reserved shares to the owner based on how much commission was earned on reserved shares.
fun pay_commission_to_owner_reserved(managed_pool_address: address): u64
Function pay_commission_to_owner_unreserved
pay_commission_to_owner_unreserved
Mints new reserved shares to the commission_recipient based on how much commission was earned on reserved shares. A new delegation is also subject stake generated on the epoch in which the delegation happens. See get_num_new_shares_to_mint
()
for more details.
Returns the total worth of the newly minted shares.
fun pay_commission_to_owner_unreserved(managed_pool_address: address): u64
Function get_num_new_shares_to_mint
get_num_new_shares_to_mint
Returns the number of shares the pool should mint given the current state of the reserved and unreserved pool for value_being_added
.
Assumes
there is no restakable balance
the owner has been paid any pending commissions
the reserved shared have been dispersed
The last assumption is a simplifying one as shares corresponding to inactive balance depreciate in value. Making that assumption simplifies shares calculation.
fun get_num_new_shares_to_mint(managed_pool_address: address, value_being_added: u64): u64
Last updated