diff --git a/aptos-framework/doc/delegation_pool.md b/aptos-framework/doc/delegation_pool.md index ca973d3dc..9cb98422b 100644 --- a/aptos-framework/doc/delegation_pool.md +++ b/aptos-framework/doc/delegation_pool.md @@ -145,6 +145,7 @@ transferred to A - [Function `calculate_and_update_voter_total_voting_power`](#0x1_delegation_pool_calculate_and_update_voter_total_voting_power) - [Function `calculate_and_update_remaining_voting_power`](#0x1_delegation_pool_calculate_and_update_remaining_voting_power) - [Function `calculate_and_update_delegator_voter`](#0x1_delegation_pool_calculate_and_update_delegator_voter) +- [Function `get_expected_stake_pool_address`](#0x1_delegation_pool_get_expected_stake_pool_address) - [Function `initialize_delegation_pool`](#0x1_delegation_pool_initialize_delegation_pool) - [Function `enable_partial_governance_voting`](#0x1_delegation_pool_enable_partial_governance_voting) - [Function `vote`](#0x1_delegation_pool_vote) @@ -161,6 +162,7 @@ transferred to A - [Function `get_delegator_active_shares`](#0x1_delegation_pool_get_delegator_active_shares) - [Function `get_delegator_pending_inactive_shares`](#0x1_delegation_pool_get_delegator_pending_inactive_shares) - [Function `get_used_voting_power`](#0x1_delegation_pool_get_used_voting_power) +- [Function `create_resource_account_seed`](#0x1_delegation_pool_create_resource_account_seed) - [Function `borrow_mut_used_voting_power`](#0x1_delegation_pool_borrow_mut_used_voting_power) - [Function `update_and_borrow_mut_delegator_vote_delegation`](#0x1_delegation_pool_update_and_borrow_mut_delegator_vote_delegation) - [Function `update_and_borrow_mut_delegated_votes`](#0x1_delegation_pool_update_and_borrow_mut_delegated_votes) @@ -1659,6 +1661,34 @@ latest state. + + + + +## Function `get_expected_stake_pool_address` + +Return the address of the stake pool to be created with the provided owner, and seed. + + +
#[view]
+public fun get_expected_stake_pool_address(owner: address, delegation_pool_creation_seed: vector<u8>): address
+
+ + + +
+Implementation + + +
public fun get_expected_stake_pool_address(owner: address, delegation_pool_creation_seed: vector<u8>
+): address {
+    let seed = create_resource_account_seed(delegation_pool_creation_seed);
+    account::create_resource_address(&owner, seed)
+}
+
+ + +
@@ -1691,11 +1721,7 @@ Ownership over setting the operator/voter is granted to owner who h assert!(operator_commission_percentage <= MAX_FEE, error::invalid_argument(EINVALID_COMMISSION_PERCENTAGE)); // generate a seed to be used to create the resource account hosting the delegation pool - let seed = vector::empty<u8>(); - // include module salt (before any subseeds) to avoid conflicts with other modules creating resource accounts - vector::append(&mut seed, MODULE_SALT); - // include an additional salt in case the same resource account has already been created - vector::append(&mut seed, delegation_pool_creation_seed); + let seed = create_resource_account_seed(delegation_pool_creation_seed); let (stake_pool_signer, stake_pool_signer_cap) = account::create_resource_account(owner, seed); coin::register<AptosCoin>(&stake_pool_signer); @@ -2244,6 +2270,38 @@ Get the used voting power of a voter on a proposal. + + + + +## Function `create_resource_account_seed` + +Create the seed to derive the resource account address. + + +
fun create_resource_account_seed(delegation_pool_creation_seed: vector<u8>): vector<u8>
+
+ + + +
+Implementation + + +
fun create_resource_account_seed(
+    delegation_pool_creation_seed: vector<u8>,
+): vector<u8> {
+    let seed = vector::empty<u8>();
+    // include module salt (before any subseeds) to avoid conflicts with other modules creating resource accounts
+    vector::append(&mut seed, MODULE_SALT);
+    // include an additional salt in case the same resource account has already been created
+    vector::append(&mut seed, delegation_pool_creation_seed);
+    seed
+}
+
+ + +
diff --git a/aptos-framework/doc/staking_contract.md b/aptos-framework/doc/staking_contract.md index 6b096895e..f8436012f 100644 --- a/aptos-framework/doc/staking_contract.md +++ b/aptos-framework/doc/staking_contract.md @@ -52,6 +52,7 @@ pool. - [Function `staking_contract_amounts`](#0x1_staking_contract_staking_contract_amounts) - [Function `pending_distribution_counts`](#0x1_staking_contract_pending_distribution_counts) - [Function `staking_contract_exists`](#0x1_staking_contract_staking_contract_exists) +- [Function `get_expected_stake_pool_address`](#0x1_staking_contract_get_expected_stake_pool_address) - [Function `create_staking_contract`](#0x1_staking_contract_create_staking_contract) - [Function `create_staking_contract_with_coins`](#0x1_staking_contract_create_staking_contract_with_coins) - [Function `add_stake`](#0x1_staking_contract_add_stake) @@ -71,6 +72,7 @@ pool. - [Function `get_staking_contract_amounts_internal`](#0x1_staking_contract_get_staking_contract_amounts_internal) - [Function `create_stake_pool`](#0x1_staking_contract_create_stake_pool) - [Function `update_distribution_pool`](#0x1_staking_contract_update_distribution_pool) +- [Function `create_resource_account_seed`](#0x1_staking_contract_create_resource_account_seed) - [Function `new_staking_contracts_holder`](#0x1_staking_contract_new_staking_contracts_holder) - [Specification](#@Specification_1) - [Function `stake_pool_address`](#@Specification_1_stake_pool_address) @@ -1027,6 +1029,37 @@ Return true if the staking contract between the provided staker and operator exi + + + + +## Function `get_expected_stake_pool_address` + +Return the address of the stake pool to be created with the provided staker, operator and seed. + + +
#[view]
+public fun get_expected_stake_pool_address(staker: address, operator: address, contract_creation_seed: vector<u8>): address
+
+ + + +
+Implementation + + +
public fun get_expected_stake_pool_address(
+    staker: address,
+    operator: address,
+    contract_creation_seed: vector<u8>,
+): address {
+    let seed = create_resource_account_seed(staker, operator, contract_creation_seed);
+    account::create_resource_address(&staker, seed)
+}
+
+ + +
@@ -1821,13 +1854,8 @@ Calculate accumulated rewards and commissions since last update. contract_creation_seed: vector<u8>, ): (signer, SignerCapability, OwnerCapability) { // Generate a seed that will be used to create the resource account that hosts the staking contract. - let seed = bcs::to_bytes(&signer::address_of(staker)); - vector::append(&mut seed, bcs::to_bytes(&operator)); - // Include a salt to avoid conflicts with any other modules out there that might also generate - // deterministic resource accounts for the same staker + operator addresses. - vector::append(&mut seed, SALT); - // Add an extra salt given by the staker in case an account with the same address has already been created. - vector::append(&mut seed, contract_creation_seed); + let seed = create_resource_account_seed( + signer::address_of(staker), operator, contract_creation_seed); let (stake_pool_signer, stake_pool_signer_cap) = account::create_resource_account(staker, seed); stake::initialize_stake_owner(&stake_pool_signer, 0, operator, voter); @@ -1896,6 +1924,42 @@ Calculate accumulated rewards and commissions since last update. + + + + +## Function `create_resource_account_seed` + +Create the seed to derive the resource account address. + + +
fun create_resource_account_seed(staker: address, operator: address, contract_creation_seed: vector<u8>): vector<u8>
+
+ + + +
+Implementation + + +
fun create_resource_account_seed(
+    staker: address,
+    operator: address,
+    contract_creation_seed: vector<u8>,
+): vector<u8> {
+    let seed = bcs::to_bytes(&staker);
+    vector::append(&mut seed, bcs::to_bytes(&operator));
+    // Include a salt to avoid conflicts with any other modules out there that might also generate
+    // deterministic resource accounts for the same staker + operator addresses.
+    vector::append(&mut seed, SALT);
+    // Add an extra salt given by the staker in case an account with the same address has already been created.
+    vector::append(&mut seed, contract_creation_seed);
+    seed
+}
+
+ + +
diff --git a/aptos-framework/sources/delegation_pool.move b/aptos-framework/sources/delegation_pool.move index a593a6842..536e5dda3 100644 --- a/aptos-framework/sources/delegation_pool.move +++ b/aptos-framework/sources/delegation_pool.move @@ -570,6 +570,14 @@ module aptos_framework::delegation_pool { ) } + #[view] + /// Return the address of the stake pool to be created with the provided owner, and seed. + public fun get_expected_stake_pool_address(owner: address, delegation_pool_creation_seed: vector + ): address { + let seed = create_resource_account_seed(delegation_pool_creation_seed); + account::create_resource_address(&owner, seed) + } + /// Initialize a delegation pool of custom fixed `operator_commission_percentage`. /// A resource account is created from `owner` signer and its supplied `delegation_pool_creation_seed` /// to host the delegation pool resource and own the underlying stake pool. @@ -585,11 +593,7 @@ module aptos_framework::delegation_pool { assert!(operator_commission_percentage <= MAX_FEE, error::invalid_argument(EINVALID_COMMISSION_PERCENTAGE)); // generate a seed to be used to create the resource account hosting the delegation pool - let seed = vector::empty(); - // include module salt (before any subseeds) to avoid conflicts with other modules creating resource accounts - vector::append(&mut seed, MODULE_SALT); - // include an additional salt in case the same resource account has already been created - vector::append(&mut seed, delegation_pool_creation_seed); + let seed = create_resource_account_seed(delegation_pool_creation_seed); let (stake_pool_signer, stake_pool_signer_cap) = account::create_resource_account(owner, seed); coin::register(&stake_pool_signer); @@ -835,6 +839,18 @@ module aptos_framework::delegation_pool { *smart_table::borrow_with_default(votes, key, &0) } + /// Create the seed to derive the resource account address. + fun create_resource_account_seed( + delegation_pool_creation_seed: vector, + ): vector { + let seed = vector::empty(); + // include module salt (before any subseeds) to avoid conflicts with other modules creating resource accounts + vector::append(&mut seed, MODULE_SALT); + // include an additional salt in case the same resource account has already been created + vector::append(&mut seed, delegation_pool_creation_seed); + seed + } + /// Borrow the mutable used voting power of a voter on a proposal. inline fun borrow_mut_used_voting_power(governance_records: &mut GovernanceRecords, voter: address, proposal_id: u64): &mut u64 { let votes = &mut governance_records.votes; @@ -3891,6 +3907,12 @@ module aptos_framework::delegation_pool { delegate_voting_power(delegator1, pool_address, signer::address_of(voter1)); } + #[test(staker = @0xe256f4f4e2986cada739e339895cf5585082ff247464cab8ec56eea726bd2263)] + public entry fun test_get_expected_stake_pool_address(staker: address) { + let pool_address = get_expected_stake_pool_address(staker, vector[0x42, 0x42]); + assert!(pool_address == @0xe9fc2fbb82b7e1cb7af3daef8c7a24e66780f9122d15e4f1d486ee7c7c36c48d, 0); + } + #[test_only] public fun assert_delegation( delegator_address: address, diff --git a/aptos-framework/sources/staking_contract.move b/aptos-framework/sources/staking_contract.move index 9aee05336..1291d6321 100644 --- a/aptos-framework/sources/staking_contract.move +++ b/aptos-framework/sources/staking_contract.move @@ -233,6 +233,17 @@ module aptos_framework::staking_contract { simple_map::contains_key(&store.staking_contracts, &operator) } + #[view] + /// Return the address of the stake pool to be created with the provided staker, operator and seed. + public fun get_expected_stake_pool_address( + staker: address, + operator: address, + contract_creation_seed: vector, + ): address { + let seed = create_resource_account_seed(staker, operator, contract_creation_seed); + account::create_resource_address(&staker, seed) + } + /// Staker can call this function to create a simple staking contract with a specified operator. public entry fun create_staking_contract( staker: &signer, @@ -670,13 +681,8 @@ module aptos_framework::staking_contract { contract_creation_seed: vector, ): (signer, SignerCapability, OwnerCapability) { // Generate a seed that will be used to create the resource account that hosts the staking contract. - let seed = bcs::to_bytes(&signer::address_of(staker)); - vector::append(&mut seed, bcs::to_bytes(&operator)); - // Include a salt to avoid conflicts with any other modules out there that might also generate - // deterministic resource accounts for the same staker + operator addresses. - vector::append(&mut seed, SALT); - // Add an extra salt given by the staker in case an account with the same address has already been created. - vector::append(&mut seed, contract_creation_seed); + let seed = create_resource_account_seed( + signer::address_of(staker), operator, contract_creation_seed); let (stake_pool_signer, stake_pool_signer_cap) = account::create_resource_account(staker, seed); stake::initialize_stake_owner(&stake_pool_signer, 0, operator, voter); @@ -722,6 +728,22 @@ module aptos_framework::staking_contract { pool_u64::update_total_coins(distribution_pool, updated_total_coins); } + /// Create the seed to derive the resource account address. + fun create_resource_account_seed( + staker: address, + operator: address, + contract_creation_seed: vector, + ): vector { + let seed = bcs::to_bytes(&staker); + vector::append(&mut seed, bcs::to_bytes(&operator)); + // Include a salt to avoid conflicts with any other modules out there that might also generate + // deterministic resource accounts for the same staker + operator addresses. + vector::append(&mut seed, SALT); + // Add an extra salt given by the staker in case an account with the same address has already been created. + vector::append(&mut seed, contract_creation_seed); + seed + } + /// Create a new staking_contracts resource. fun new_staking_contracts_holder(staker: &signer): Store { Store { @@ -1259,6 +1281,12 @@ module aptos_framework::staking_contract { stake::assert_stake_pool(pool_address, balance_2epoch, 0, 0, with_rewards(unpaid_commission)); } + #[test(staker = @0xe256f4f4e2986cada739e339895cf5585082ff247464cab8ec56eea726bd2263, operator= @0x9f0a211d218b082987408f1e393afe1ba0c202c6d280f081399788d3360c7f09)] + public entry fun test_get_expected_stake_pool_address(staker: address, operator: address) { + let pool_address = get_expected_stake_pool_address(staker, operator, vector[0x42, 0x42]); + assert!(pool_address == @0x9d9648031ada367c26f7878eb0b0406ae6a969b1a43090269e5cdfabe1b48f0f, 0); + } + #[test_only] public fun assert_staking_contract( staker: address, operator: address, principal: u64, commission_percentage: u64) acquires Store {