staking_proxy - [mainnet]
use 0x1::error;use 0x1::permissioned_signer;use 0x1::signer;use 0x1::stake;use 0x1::staking_contract;use 0x1::vesting;
Constants
Signer does not have permission to perform stake proxy logic.
const ENO_STAKE_PERMISSION: u64 = 28;
Structs
StakeProxyPermission
struct StakeProxyPermission has copy, drop, store
Fields
-
dummy_field: bool
Functions
check_stake_proxy_permission
Permissions
fun check_stake_proxy_permission(s: &signer)
Implementation
inline fun check_stake_proxy_permission(s: &signer) { assert!( permissioned_signer::check_permission_exists(s, StakeProxyPermission {}), error::permission_denied(ENO_STAKE_PERMISSION), );}
grant_permission
Grant permission to mutate staking on behalf of the master signer.
public fun grant_permission(master: &signer, permissioned_signer: &signer)
Implementation
public fun grant_permission(master: &signer, permissioned_signer: &signer) { permissioned_signer::authorize_unlimited(master, permissioned_signer, StakeProxyPermission {})}
set_operator
public entry fun set_operator(owner: &signer, old_operator: address, new_operator: address)
Implementation
public entry fun set_operator(owner: &signer, old_operator: address, new_operator: address) { set_vesting_contract_operator(owner, old_operator, new_operator); set_staking_contract_operator(owner, old_operator, new_operator); set_stake_pool_operator(owner, new_operator);}
set_voter
public entry fun set_voter(owner: &signer, operator: address, new_voter: address)
Implementation
public entry fun set_voter(owner: &signer, operator: address, new_voter: address) { set_vesting_contract_voter(owner, operator, new_voter); set_staking_contract_voter(owner, operator, new_voter); set_stake_pool_voter(owner, new_voter);}
set_vesting_contract_operator
public entry fun set_vesting_contract_operator(owner: &signer, old_operator: address, new_operator: address)
Implementation
public entry fun set_vesting_contract_operator(owner: &signer, old_operator: address, new_operator: address) { check_stake_proxy_permission(owner); let owner_address = signer::address_of(owner); let vesting_contracts = &vesting::vesting_contracts(owner_address); vector::for_each_ref(vesting_contracts, |vesting_contract| { let vesting_contract = *vesting_contract; if (vesting::operator(vesting_contract) == old_operator) { let current_commission_percentage = vesting::operator_commission_percentage(vesting_contract); vesting::update_operator(owner, vesting_contract, new_operator, current_commission_percentage); }; });}
set_staking_contract_operator
public entry fun set_staking_contract_operator(owner: &signer, old_operator: address, new_operator: address)
Implementation
public entry fun set_staking_contract_operator(owner: &signer, old_operator: address, new_operator: address) { check_stake_proxy_permission(owner); let owner_address = signer::address_of(owner); if (staking_contract::staking_contract_exists(owner_address, old_operator)) { let current_commission_percentage = staking_contract::commission_percentage(owner_address, old_operator); staking_contract::switch_operator(owner, old_operator, new_operator, current_commission_percentage); };}
set_stake_pool_operator
public entry fun set_stake_pool_operator(owner: &signer, new_operator: address)
Implementation
public entry fun set_stake_pool_operator(owner: &signer, new_operator: address) { check_stake_proxy_permission(owner); let owner_address = signer::address_of(owner); if (stake::stake_pool_exists(owner_address)) { stake::set_operator(owner, new_operator); };}
set_vesting_contract_voter
public entry fun set_vesting_contract_voter(owner: &signer, operator: address, new_voter: address)
Implementation
public entry fun set_vesting_contract_voter(owner: &signer, operator: address, new_voter: address) { check_stake_proxy_permission(owner); let owner_address = signer::address_of(owner); let vesting_contracts = &vesting::vesting_contracts(owner_address); vector::for_each_ref(vesting_contracts, |vesting_contract| { let vesting_contract = *vesting_contract; if (vesting::operator(vesting_contract) == operator) { vesting::update_voter(owner, vesting_contract, new_voter); }; });}
set_staking_contract_voter
public entry fun set_staking_contract_voter(owner: &signer, operator: address, new_voter: address)
Implementation
public entry fun set_staking_contract_voter(owner: &signer, operator: address, new_voter: address) { check_stake_proxy_permission(owner); let owner_address = signer::address_of(owner); if (staking_contract::staking_contract_exists(owner_address, operator)) { staking_contract::update_voter(owner, operator, new_voter); };}
set_stake_pool_voter
public entry fun set_stake_pool_voter(owner: &signer, new_voter: address)
Implementation
public entry fun set_stake_pool_voter(owner: &signer, new_voter: address) { check_stake_proxy_permission(owner); if (stake::stake_pool_exists(signer::address_of(owner))) { stake::set_delegated_voter(owner, new_voter); };}
Specification
High-level Requirements
No. | Requirement | Criticality | Implementation | Enforcement |
---|---|---|---|---|
1 | When updating the Vesting operator, it should be updated throughout all depending units. | Medium | The VestingContract contains a StakingInfo object that has an operator field, and this operator is mapped to a StakingContract object that in turn encompasses a StakePool object where the operator matches. | Audited that it ensures the two operator fields hold the new value after the update. |
2 | When updating the Vesting voter, it should be updated throughout all depending units. | Medium | The VestingContract contains a StakingInfo object that has an operator field, and this operator is mapped to a StakingContract object that in turn encompasses a StakePool object where the operator matches. | Audited that it ensures the two operator fields hold the new value after the update. |
3 | The operator and voter of a Vesting Contract should only be updated by the owner of the contract. | High | The owner-operator-voter model, as defined in the documentation, grants distinct abilities to each role. Therefore, it's crucial to ensure that only the owner has the authority to modify the operator or voter, to prevent the compromise of the StakePool. | Audited that it ensures the signer owns the AdminStore resource and that the operator or voter intended for the update actually exists. |
4 | The operator and voter of a Staking Contract should only be updated by the owner of the contract. | High | The owner-operator-voter model, as defined in the documentation, grants distinct abilities to each role. Therefore, it's crucial to ensure that only the owner has the authority to modify the operator or voter, to prevent the compromise of the StakePool. | Audited the patterns of updating operators and voters in the staking contract. |
5 | Staking Contract's operators should be unique inside a store. | Medium | Duplicates among operators could result in incorrectly updating the operator or voter associated with the incorrect StakingContract. | Enforced via SimpleMap. |
Module-level Specification
pragma verify = true;pragma aborts_if_is_partial;
grant_permission
public fun grant_permission(master: &signer, permissioned_signer: &signer)
pragma aborts_if_is_partial;aborts_if !permissioned_signer::spec_is_permissioned_signer(permissioned_signer);aborts_if permissioned_signer::spec_is_permissioned_signer(master);aborts_if signer::address_of(master) != signer::address_of(permissioned_signer);
set_operator
public entry fun set_operator(owner: &signer, old_operator: address, new_operator: address)
Aborts if conditions of SetStakePoolOperator are not met
pragma verify = false;pragma aborts_if_is_partial;include SetStakePoolOperator;include SetStakingContractOperator;
set_voter
public entry fun set_voter(owner: &signer, operator: address, new_voter: address)
Aborts if conditions of SetStackingContractVoter and SetStackPoolVoterAbortsIf are not met
pragma aborts_if_is_partial;pragma verify_duration_estimate = 120;include SetStakingContractVoter;include SetStakePoolVoterAbortsIf;
set_vesting_contract_operator
public entry fun set_vesting_contract_operator(owner: &signer, old_operator: address, new_operator: address)
pragma verify = false;
set_staking_contract_operator
public entry fun set_staking_contract_operator(owner: &signer, old_operator: address, new_operator: address)
pragma aborts_if_is_partial;pragma verify = false;include SetStakingContractOperator;
schema SetStakingContractOperator { owner: signer; old_operator: address; new_operator: address; let owner_address = signer::address_of(owner); let store = global<Store>(owner_address); let staking_contract_exists = exists<Store>(owner_address) && simple_map::spec_contains_key(store.staking_contracts, old_operator); aborts_if staking_contract_exists && simple_map::spec_contains_key(store.staking_contracts, new_operator); let post post_store = global<Store>(owner_address); ensures staking_contract_exists ==> !simple_map::spec_contains_key(post_store.staking_contracts, old_operator); let staking_contract = simple_map::spec_get(store.staking_contracts, old_operator); let stake_pool = global<stake::StakePool>(staking_contract.pool_address); let active = coin::value(stake_pool.active); let pending_active = coin::value(stake_pool.pending_active); let total_active_stake = active + pending_active; let accumulated_rewards = total_active_stake - staking_contract.principal; let commission_amount = accumulated_rewards * staking_contract.commission_percentage / 100; aborts_if staking_contract_exists && !exists<stake::StakePool>(staking_contract.pool_address); ensures staking_contract_exists ==> simple_map::spec_get(post_store.staking_contracts, new_operator).principal == total_active_stake - commission_amount; let pool_address = staking_contract.owner_cap.pool_address; let current_commission_percentage = staking_contract.commission_percentage; aborts_if staking_contract_exists && commission_amount != 0 && !exists<stake::StakePool>(pool_address); ensures staking_contract_exists && commission_amount != 0 ==> global<stake::StakePool>(pool_address).operator_address == new_operator && simple_map::spec_get(post_store.staking_contracts, new_operator).commission_percentage == current_commission_percentage; ensures staking_contract_exists ==> simple_map::spec_contains_key(post_store.staking_contracts, new_operator);}
set_stake_pool_operator
public entry fun set_stake_pool_operator(owner: &signer, new_operator: address)
Aborts if stake_pool is exists and when OwnerCapability or stake_pool_exists One of them are not exists
include SetStakePoolOperator;include AbortsIfSignerPermissionStakeProxy { s: owner};include exists<stake::StakePool>(signer::address_of(owner)) ==> stake::AbortsIfSignerPermissionStake { s:owner};
schema SetStakePoolOperator { owner: &signer; new_operator: address; include AbortsIfSignerPermissionStakeProxy { s: owner }; let owner_address = signer::address_of(owner); let ownership_cap = borrow_global<stake::OwnerCapability>(owner_address); let pool_address = ownership_cap.pool_address; aborts_if stake::stake_pool_exists(owner_address) && !(exists<stake::OwnerCapability>(owner_address) && stake::stake_pool_exists(pool_address)); ensures stake::stake_pool_exists(owner_address) ==> global<stake::StakePool>(pool_address).operator_address == new_operator;}
set_vesting_contract_voter
public entry fun set_vesting_contract_voter(owner: &signer, operator: address, new_voter: address)
pragma verify = false;
set_staking_contract_voter
public entry fun set_staking_contract_voter(owner: &signer, operator: address, new_voter: address)
include SetStakingContractVoter;include AbortsIfSignerPermissionStakeProxy { s: owner};
Make sure staking_contract_exists first Then abort if the resource is not exist
schema SetStakingContractVoter { owner: &signer; operator: address; new_voter: address; let owner_address = signer::address_of(owner); let staker = owner_address; let store = global<Store>(staker); let staking_contract_exists = exists<Store>(staker) && simple_map::spec_contains_key(store.staking_contracts, operator); let staker_address = owner_address; let staking_contract = simple_map::spec_get(store.staking_contracts, operator); let pool_address = staking_contract.pool_address; let pool_address1 = staking_contract.owner_cap.pool_address; aborts_if staking_contract_exists && !exists<stake::StakePool>(pool_address); aborts_if staking_contract_exists && !exists<stake::StakePool>(staking_contract.owner_cap.pool_address); ensures staking_contract_exists ==> global<stake::StakePool>(pool_address1).delegated_voter == new_voter;}
set_stake_pool_voter
public entry fun set_stake_pool_voter(owner: &signer, new_voter: address)
include SetStakePoolVoterAbortsIf;include AbortsIfSignerPermissionStakeProxy { s: owner};include exists<stake::StakePool>(signer::address_of(owner)) ==> stake::AbortsIfSignerPermissionStake { s:owner};
schema SetStakePoolVoterAbortsIf { owner: &signer; new_voter: address; include AbortsIfSignerPermissionStakeProxy { s: owner }; let owner_address = signer::address_of(owner); let ownership_cap = global<stake::OwnerCapability>(owner_address); let pool_address = ownership_cap.pool_address; aborts_if stake::stake_pool_exists(owner_address) && !(exists<stake::OwnerCapability>(owner_address) && stake::stake_pool_exists(pool_address)); ensures stake::stake_pool_exists(owner_address) ==> global<stake::StakePool>(pool_address).delegated_voter == new_voter;}
schema AbortsIfSignerPermissionStakeProxy { s: signer; let perm = StakeProxyPermission {}; aborts_if !permissioned_signer::spec_check_permission_exists(s, perm);}