tortuga::validator_router
Module 0xc0ded0c0::validator_router
0xc0ded0c0::validator_router
module tortuga::validator_router
tortuga::validator_router
This module handles interaction between the liquid staking protocol and the validators. It is responsible for:
delegations and withdrawals from validators,
validator signup and removal.
The delegators to the protocol will interact usually with tortuga::stake_router
module.
This module uses oracle::validator_states
module, which keeps track of changes in the states of the validators and calculates their performance.
It provides an endpoint for validators to be able to charge commissions. Charging commission often will compound the commissions, and will also keep update the state of this contract.
It also manages the AptosCoinReserve
for the protocol, which holds the APT that the delegators have requested for withdrawal (but not claimed yet) or deposited to the protocol.
Validators will receive delegations from the protocol after they call validator_signup
. They also will be able to receive outside delegations.
This module only manages the stake that the protocol has with the validators.
use 0x1::account;
use 0x1::aptos_coin;
use 0x1::bcs;
use 0x1::coin;
use 0x1::error;
use 0x1::event;
use 0x1::option;
use 0x1::signer;
use 0x1::simple_map;
use 0x1::stake;
use 0x1::timestamp;
use 0xc0ded0c0::delegation_service;
use 0xc0ded0c0::delegation_state;
use 0xc0ded0c0::iterable_table_custom;
use 0xc0ded0c0::stake_pool_helpers;
use 0xc0ded0c0::validator_states;
use 0xc0ded0c1::permissions;
use 0xc0ded0c2::tortuga_governance;
Resource ValidatorContract
ValidatorContract
This struct is used to allow permissioned validator signup.
struct ValidatorContract has store, key
Resource Status
Status
This struct stores validator signup details.
struct Status has key
Resource WithdrawSignature
WithdrawSignature
Withdrawal to distribute claims should be from earliest validator to maximize the liquidity of the protocol. This struct helps with that mechanism as follows:
protocol sets a withdraw signature which is valid as long as
timestamp::now_seconds
() < unlocking_at
andmanaged_pool_address
is still a validator in our set.In case of a deficit, anyone can then request a withdraw for any validator as long as that validator's
unlocking_at
is not later than the one in withdraw signature.If the withdraw signature has expired (i.e. the
managed_pool_address
has either left the validator set of the liquid staking contract or has unlocked already), then a withdrawal can be requested from any validator.
This strikes a balance between keeping the protocol liquid and not allowing early withdraw their stake before they are supposed to.
struct WithdrawSignature has drop, key
Resource AptosCoinReserve
AptosCoinReserve
This reserve store coins which can be either delegated to validators, or withdrawn from them to clear Ticket
s in stake_router
module.
struct AptosCoinReserve has key
Resource DelegationAccounts
DelegationAccounts
These resource accounts delegate to the individual validators
struct DelegationAccounts has store, key
Struct DelegationAccount
DelegationAccount
Delegation account for a validator, stores the address and capabilities.
struct DelegationAccount has store
Struct ValidatorSignupEvent
ValidatorSignupEvent
Event generated when a validator successfully signs up.
struct ValidatorSignupEvent has drop, store
Struct ValidatorOffboardEvent
ValidatorOffboardEvent
Event generated when a validator is off-boarded.
struct ValidatorOffboardEvent has drop, store
Struct PayCommissionEvent
PayCommissionEvent
Event generated when a validator is paid commission.
struct PayCommissionEvent has drop, store
Struct WithdrawToReserveEvent
WithdrawToReserveEvent
Event generated when a validator withdraws to the reserve.
struct WithdrawToReserveEvent has drop, store
Struct WithdrawSignatureSetEvent
WithdrawSignatureSetEvent
Event generated when the contract sets a WithdrawSignature
.
struct WithdrawSignatureSetEvent has drop, store
Constants
Validator state active
.
const VALIDATOR_STATUS_ACTIVE: u64 = 2;
Validator state inactive
.
const VALIDATOR_STATUS_INACTIVE: u64 = 4;
The following constants must match those of stake
.
move
Validator state pending_active
.
const VALIDATOR_STATUS_PENDING_ACTIVE: u64 = 1;
Validator state pending_inactive
.
const VALIDATOR_STATUS_PENDING_INACTIVE: u64 = 3;
When an unauthorized action is performed.
const EPERMISSION_DENIED: u64 = 3;
The maximum minimum transaction amount
const DEFAULT_MAX_MAX_COMMISSION: u64 = 1000000;
When a validator is already associated with the protocol.
const EALREADY_AN_ASSOCIATED_VALIDATOR: u64 = 10;
When a validator tries to rejoin without claiming the owner capability first.
const ECANNOT_REJOIN_WITH_UNCLAIMED_OWNER_CAP: u64 = 13;
When a validator already has a ValidatorContract
.
const ECONTRACT_ALREADY_ISSUED: u64 = 4;
When a validator is not in ACTIVE
state.
const EINACTIVE_VALIDATOR: u64 = 7;
When the max_commission is too high.
const EMAX_COMMISSION_TOO_HIGH: u64 = 16;
When the protocol has a withdrawable amount with a validator.
const ENONZERO_WITHDRAWABLE_AMOUNT: u64 = 9;
When the module is not initialized.
const ENOT_INITIALIZED: u64 = 11;
When there is no OwnerCapability
to claim with the protocol.
const ENO_OWNER_CAP_TO_CLAIM: u64 = 12;
When self-signup is not allowed.
const ESELF_SIGNUP_NOT_ALLOWED: u64 = 15;
When more than max_number_of_validators
validators are already associated with the protocol.
const ETOO_MANY_VALIDATORS: u64 = 8;
When the validator is not associated with the protocol.
const EVALIDATOR_NOT_FOUND: u64 = 2;
When an unlock is requested from a validator but is not allowed by the current WithdrawSignature
.
const EVALIDATOR_UNLOCK_IS_TOO_FAR: u64 = 14;
Function initialize
initialize
Initializes the module with a given max_number_of_validators
. It is initialized in the module tortuga::stake_router
.
public(friend) fun initialize(tortuga_governance_deployer: &signer, max_number_of_validators: u64)
Function assert_no_withdrawable_amount
assert_no_withdrawable_amount
Ensures that the protocol does not have any currently withdrawable amount in the validator at managed_pool_address
.
Abort conditions
If
is_withdrawable_amount_nonzero()
returnstrue
.
public fun assert_no_withdrawable_amount(managed_pool_address: address)
Function get_total_balance
get_total_balance
Returns the total balance in APT staked with the validators and in the AptosCoinReserve
. This may will only be 100% accurate if the oracle was updated in the current epoch.
public fun get_total_balance(): u64
Function get_reserve_balance
get_reserve_balance
Returns the balance in the AptosCoinReserve
.
public fun get_reserve_balance(): u64
Function get_current_number_of_validators
get_current_number_of_validators
Returns the number of associated validators.
public fun get_current_number_of_validators(): u64
Function get_max_number_of_validators
get_max_number_of_validators
Returns the current maximum number of validators.
public fun get_max_number_of_validators(): u64
Function get_allow_self_signup
get_allow_self_signup
Returns true
if self-signup is allowed, false
otherwise.
public fun get_allow_self_signup(): bool
Function is_withdrawable_amount_positive
is_withdrawable_amount_positive
Return true
if there is any withdrawable balance in the validator at managed_pool_address
.
public fun is_withdrawable_amount_positive(managed_pool_address: address): bool
Function is_an_associated_validator
is_an_associated_validator
Returns if the validator at managed_pool_address
is associated with the protocol.
public fun is_an_associated_validator(managed_pool_address: address): bool
Function set_max_number_of_validators
set_max_number_of_validators
Set the maximum number of validators that can be associated with the protocol to value
.
Restrictions
Only the
tortuga_governance
can call this function.
public fun set_max_number_of_validators(tortuga_governance: &signer, value: u64)
Function set_max_max_commission
set_max_max_commission
Set the maximum max_commission for validators to value
This does not affect existing validators.
Restrictions
Only the
tortuga_governance
can call this function.
public fun set_max_max_commission(tortuga_governance: &signer, value: u64)
Function set_allow_self_signup
set_allow_self_signup
Set the allow_self_signup
flag.
Restrictions
Only the
tortuga_governance
can call this function.
public fun set_allow_self_signup(tortuga_governance: &signer, value: bool)
Function set_withdraw_signature
set_withdraw_signature
Set the WithdrawSigner
for the protocol to the validator managed_pool_address
such that it expires when managed_pool_address
unlocks or if managed_pool_address
is unassociated with the protocol.
It allows permissionless unstaking while still maintaining liquidity.
Restrictions
Only the
tortuga_governance
can call this function.
Abort conditions
If
managed_pool_address
is not associated with the protocol.
public fun set_withdraw_signature(tortuga_governance: &signer, managed_pool_address: address)
Function reset_validator_states_config
reset_validator_states_config
Update the configs of the oracle::validator_state
module.
Restrictions
Only the
tortuga_governance
can call this function.
public fun reset_validator_states_config(tortuga_governance: &signer, initial_time_averaged_effective_reward_rate: u128, min_span_between_observations_sec: u64, max_number_of_observations: u64, initial_delegation_target: u64, rate_normalizer: u128, time_normalizer: u128, max_time_averaged_effective_reward_rate: u128, allow_permissionless_scoring: bool, ramp_up_duration: u64)
Function set_validator_state_permissioned_score_for_validator
set_validator_state_permissioned_score_for_validator
Set the score for validator at managed_pool_address
to value
. It would not affect target_delegations
for the validator unless oracle::validator_states::StatsConfig.allow_permissionless_scoring
is set to false
.
See oracle::validator_states
module for details of the scoring mechanisms.
Restrictions
Only the
tortuga_governance
can call this function.
public fun set_validator_state_permissioned_score_for_validator(tortuga_governance: &signer, managed_pool_address: address, value: u128)
Function vet_a_validator
vet_a_validator
Vets a validator for becoming associated with the protocol.
If Status
.allow_self_signup
is set to false
, then a validator can only join after it has been vetted.
Restrictions
Only the
tortuga_governance
can call this function.
Abort conditions
If
managed_pool_address
already has aValidatorContract
.
public fun vet_a_validator(tortuga_governance: &signer, potential_managed_pool_owner: address)
Function validator_begin_leaving_force
validator_begin_leaving_force
Forcibly remove a validator from the protocol.
The protocol may have to remove non-owner delegators via delegation::delegation_service
module after calling this function, and before calling validator_offboard_force
.
The validator can then claim owner_cap
via claim_stake_pool_owner_cap
()
.
public fun validator_begin_leaving_force(tortuga_governance: &signer, managed_pool_address: address)
Function validator_offboard_force
validator_offboard_force
This function is called to forcible remove the validator at managed_pool_address
. This can only be called after the validator has begin leaving, i.e., the ManagedStakePool
is in self-destruct state.
This function call is permissionless.
Abort conditions
If the validator has non-zero balance.
public fun validator_offboard_force(managed_pool_address: address)
Function withdraw_to_reserve
withdraw_to_reserve
Request withdrawal of the amount in Payout
for the validator who is the owner of managed_pool_address
.
The function call is permissionless.
Abort conditions
If the validator is not in the protocol.
public fun withdraw_to_reserve(managed_pool_address: address)
Function pay_commission_to_the_validator
pay_commission_to_the_validator
Pays commission to the validator at managed_pool_address
.
Validator can call this function to make sure that their commission is compounded.
This function is permissionless.
Abort conditions
If the validator at address
managed_pool_address
is not an associated validator.
public fun pay_commission_to_the_validator(managed_pool_address: address)
Function validator_signup
validator_signup
A validator can sign up to become an associated validator. max_commission
is the commission that validator charges outside delegators. protocol_commission
is the commission that validator charges the protocol. commission_recipient_address
is the address where the APT generated by the validator are sent.
Both commissions are provided with 6 decimal precision, i.e., 1000000
is 100%
.
Abort conditions
If the validator is already in the protocol.
If the validator has not claimed their
owner_cap
after leaving the protocol previously.If the validator's state is not
ACTIVE
.If self-signup is not allowed.
If the maximum number of validators has already been reached.
public fun validator_signup(pool_owner: &signer, commission_recipient_address: address, max_commission: u64, protocol_commission: u64)
Function validator_begin_leaving
validator_begin_leaving
A validator calls this to begin off-boarding and get their stake_pool
back. This initiates self-destruct of the ManagedStakePool
. A validator may have to remove non-owner delegators via delegation::delegation_service
module after calling this function and before calling validator_offboard
()
.
public fun validator_begin_leaving(pool_owner: &signer)
Function claim_stake_pool_owner_cap
claim_stake_pool_owner_cap
Validators can claim their OwnerCapability
if they have been removed by the protocol (forced-offboarding).
Abort conditions
If the validator is not in the protocol.
public fun claim_stake_pool_owner_cap(old_pool_owner: &signer)
Function validator_offboard
validator_offboard
A validator can call this to retrieve owner_cap
. It must be called after the validator has called validator_begins_leaving()
, and after removing all the non-owner delegators from the ManagedStakePool
.
public fun validator_offboard(pool_owner: &signer)
Function get_delegation_account_address
get_delegation_account_address
Get the delegation account that protocol uses for an associated validator at managed_pool_address
.
public(friend) fun get_delegation_account_address(managed_pool_address: address): address
Function delegate
delegate
Delegates to a validator at managed_pool_address
with amount
APT. We allow delegations even if there is nonzero unlocking balance for simplicity.
Abort conditions
If there is any withdrawable amount.
If the validator is not in the protocol.
If the validator is in self-destruct state.
If the validator is not
ACTIVE
.
public(friend) fun delegate(managed_pool_address: address, amount: u64)
Function reserve_for_payout
reserve_for_payout
Reserve amount
to unlock from the validator at managed_pool_address
at its next unlock.
Abort conditions
If the validator is not in the protocol.
If the validator's unlock period is too far. See
WithdrawSignature
.
public(friend) fun reserve_for_payout(managed_pool_address: address, amount: u64)
Function extract_coins_from_reserve
extract_coins_from_reserve
Extract amount
APT from the AptosCoinReserve
of the protocol.
public(friend) fun extract_coins_from_reserve(amount: u64): coin::Coin<aptos_coin::AptosCoin>
Function deposit_coins_to_reserve
deposit_coins_to_reserve
Deposit amount
APT to the AptosCoinReserve
of the protocol.
public(friend) fun deposit_coins_to_reserve(coins_to_deposit: coin::Coin<aptos_coin::AptosCoin>)
Function assert_initialized
assert_initialized
Assert that the protocol has a DelegationAccounts
resource.
Abort conditions
If the protocol does not have a
DelegationAccounts
resource.
fun assert_initialized()
Function update_validator_states
update_validator_states
Update the oracle in module oracle::validator_state
for the validator at managed_pool_address
.
fun update_validator_states(managed_pool_address: address, amount_in: u64, amount_out: u64, amount_reserved: u64)
Function pay_commission_to_validator_internal
pay_commission_to_validator_internal
Pay commission to the owner of the validator at managed_pool_address
, and return the amount in APT.
fun pay_commission_to_validator_internal(managed_pool_address: address): u64
Function get_current_validator_balances
get_current_validator_balances
Returns the current balances in reserved and unreserved pool, in APT and number of shares for the protocol in the validator at managed_pool_address
.
Restrictions
There should be no pending commissions to be paid out by
managed_pool_address
.
fun get_current_validator_balances(managed_pool_address: address): (u64, u64)
Function verify_withdraw_signature_for_validator
verify_withdraw_signature_for_validator
Returns true
if the managed_pool_address
can be withdrawn from. If the WithdrawSignature
has not been set, the validator in it is no longer associated with the protocol, or it has expired, returns true
. This allows the withdrawals to go through even if the signatures are not updated on time. Otherwise, it returns true
if the lockup period for the validator at the address managed_pool_address
is earlier than WithdrawSignature
.unlock_at
.
Abort conditions
If the
managed_pool_address
is not associated with a validator.
fun verify_withdraw_signature_for_validator(managed_pool_address: address): bool
Function validator_begin_leaving_protocol_internal
validator_begin_leaving_protocol_internal
Starts the process of the validator leaving the protocol. This function will self-destruct the ManagedStakePool
which the validator had created before joining the protocol.
Abort conditions
If the validator is not associated with the protocol.
fun validator_begin_leaving_protocol_internal(managed_pool_address: address)
Function validator_offboard_internal
validator_offboard_internal
Function to offboard an associated validator at address managed_pool_address
from the protocol after the reserved and unreserved balance with the protocol is zero.
Returns the OwnerCapability
after a successful off-boarding.
Abort conditions
If the validator is not associated with the protocol.
If the validator has a non-zero balance of the protocol.
fun validator_offboard_internal(managed_pool_address: address): stake::OwnerCapability
Function create_delegation_account
create_delegation_account
Creates a new resource account for the pool_owner
, register it to receive AptosCoin
and returns the resource_signer
and the SignerCapability
for the resource_signer
.
It uses timestamp::now_seconds
()
as the seed
for the resource account.
fun create_delegation_account(pool_owner: &signer): (signer, account::SignerCapability)
Function get_delegation_account_signer
get_delegation_account_signer
Returns a signer
for the validator at managed_pool_address
.
fun get_delegation_account_signer(managed_pool_address: address): signer
Function delegation_account_exists
delegation_account_exists
Returns true
if the managed_pool_address
has a DelegationAccount
from the Tortuga protocol.
fun delegation_account_exists(managed_pool_address: address): bool
Function get_shift_amount
get_shift_amount
Get shift amount. At genesis this will be set to 0.
fun get_shift_amount(managed_pool_address: address): u64
Last updated