diff --git a/aptos-move/framework/aptos-framework/doc/aptos_account.md b/aptos-move/framework/aptos-framework/doc/aptos_account.md index 33cd0ff87e5a13..6b4c97226eece0 100644 --- a/aptos-move/framework/aptos-framework/doc/aptos_account.md +++ b/aptos-move/framework/aptos-framework/doc/aptos_account.md @@ -605,6 +605,7 @@ to transfer APT) - if we want to allow APT PFS without account itself // as APT cannot be frozen or have dispatch, and PFS cannot be transfered // (PFS could potentially be burned. regular transfer would permanently unburn the store. // Ignoring the check here has the equivalent of unburning, transfers, and then burning again) + fungible_asset::withdraw_permission_check_by_address(source, @aptos_fungible_asset, amount); fungible_asset::deposit_internal(recipient_store, fungible_asset::withdraw_internal(sender_store, amount)); } diff --git a/aptos-move/framework/aptos-framework/doc/coin.md b/aptos-move/framework/aptos-framework/doc/coin.md index 07e97171f99b19..93c6b94cdc8f5f 100644 --- a/aptos-move/framework/aptos-framework/doc/coin.md +++ b/aptos-move/framework/aptos-framework/doc/coin.md @@ -158,6 +158,7 @@ This module provides the foundation for typesafe Coins. use 0x1::object; use 0x1::option; use 0x1::optional_aggregator; +use 0x1::permissioned_signer; use 0x1::primary_fungible_store; use 0x1::signer; use 0x1::string; @@ -3423,6 +3424,13 @@ Withdraw specified amount of coin CoinType from the si ): Coin<CoinType> acquires CoinStore, CoinConversionMap, CoinInfo, PairedCoinType { let account_addr = signer::address_of(account); + let metadata = paired_metadata<CoinType>(); + if(option::is_some(&metadata)) { + fungible_asset::withdraw_permission_check_by_address(account, object::object_address(&option::destroy_some(metadata)), amount); + } else { + permissioned_signer::assert_master_signer(account); + }; + let (coin_amount_to_withdraw, fa_amount_to_withdraw) = calculate_amount_to_withdraw<CoinType>( account_addr, amount @@ -3814,64 +3822,6 @@ initialize, initialize_internal, initialize_with_parallelizable_supply; - - - - -
fun spec_is_account_registered<CoinType>(account_addr: address): bool {
-   let paired_metadata_opt = spec_paired_metadata<CoinType>();
-   exists<CoinStore<CoinType>>(account_addr) || (option::spec_is_some(
-       paired_metadata_opt
-   ) && primary_fungible_store::spec_primary_store_exists(account_addr, option::spec_borrow(paired_metadata_opt)))
-}
-
- - - - - - - -
schema CoinSubAbortsIf<CoinType> {
-    amount: u64;
-    let addr = type_info::type_of<CoinType>().account_address;
-    let maybe_supply = global<CoinInfo<CoinType>>(addr).supply;
-    include (option::is_some(
-        maybe_supply
-    )) ==> optional_aggregator::SubAbortsIf { optional_aggregator: option::borrow(maybe_supply), value: amount };
-}
-
- - - - - - - -
schema CoinAddAbortsIf<CoinType> {
-    amount: u64;
-    let addr = type_info::type_of<CoinType>().account_address;
-    let maybe_supply = global<CoinInfo<CoinType>>(addr).supply;
-    include (option::is_some(
-        maybe_supply
-    )) ==> optional_aggregator::AddAbortsIf { optional_aggregator: option::borrow(maybe_supply), value: amount };
-}
-
- - - - - - - -
schema AbortsIfNotExistCoinInfo<CoinType> {
-    let addr = type_info::type_of<CoinType>().account_address;
-    aborts_if !exists<CoinInfo<CoinType>>(addr);
-}
-
- - - ### Struct `AggregatableCoin` @@ -4215,6 +4165,64 @@ Get address by reflection. + + + + +
fun spec_is_account_registered<CoinType>(account_addr: address): bool {
+   let paired_metadata_opt = spec_paired_metadata<CoinType>();
+   exists<CoinStore<CoinType>>(account_addr) || (option::spec_is_some(
+       paired_metadata_opt
+   ) && primary_fungible_store::spec_primary_store_exists(account_addr, option::spec_borrow(paired_metadata_opt)))
+}
+
+ + + + + + + +
schema CoinSubAbortsIf<CoinType> {
+    amount: u64;
+    let addr = type_info::type_of<CoinType>().account_address;
+    let maybe_supply = global<CoinInfo<CoinType>>(addr).supply;
+    include (option::is_some(
+        maybe_supply
+    )) ==> optional_aggregator::SubAbortsIf { optional_aggregator: option::borrow(maybe_supply), value: amount };
+}
+
+ + + + + + + +
schema CoinAddAbortsIf<CoinType> {
+    amount: u64;
+    let addr = type_info::type_of<CoinType>().account_address;
+    let maybe_supply = global<CoinInfo<CoinType>>(addr).supply;
+    include (option::is_some(
+        maybe_supply
+    )) ==> optional_aggregator::AddAbortsIf { optional_aggregator: option::borrow(maybe_supply), value: amount };
+}
+
+ + + + + + + +
schema AbortsIfNotExistCoinInfo<CoinType> {
+    let addr = type_info::type_of<CoinType>().account_address;
+    aborts_if !exists<CoinInfo<CoinType>>(addr);
+}
+
+ + + ### Function `name` @@ -4595,27 +4603,6 @@ The creator of CoinType must be @aptos_framework. -Make sure name and symbol are legal length. -Only the creator of CoinType can initialize. - - - - - -
schema InitializeInternalSchema<CoinType> {
-    account: signer;
-    name: vector<u8>;
-    symbol: vector<u8>;
-    let account_addr = signer::address_of(account);
-    let coin_address = type_info::type_of<CoinType>().account_address;
-    aborts_if coin_address != account_addr;
-    aborts_if exists<CoinInfo<CoinType>>(account_addr);
-    aborts_if len(name) > MAX_COIN_NAME_LENGTH;
-    aborts_if len(symbol) > MAX_COIN_SYMBOL_LENGTH;
-}
-
- - diff --git a/aptos-move/framework/aptos-framework/doc/dispatchable_fungible_asset.md b/aptos-move/framework/aptos-framework/doc/dispatchable_fungible_asset.md index b3b76f108128ca..da71484cc0c008 100644 --- a/aptos-move/framework/aptos-framework/doc/dispatchable_fungible_asset.md +++ b/aptos-move/framework/aptos-framework/doc/dispatchable_fungible_asset.md @@ -220,6 +220,7 @@ The semantics of deposit will be governed by the function specified in DispatchF amount: u64, ): FungibleAsset acquires TransferRefStore { fungible_asset::withdraw_sanity_check(owner, store, false); + fungible_asset::withdraw_permission_check(owner, store, amount); let func_opt = fungible_asset::withdraw_dispatch_function(store); if (option::is_some(&func_opt)) { assert!( diff --git a/aptos-move/framework/aptos-framework/doc/fungible_asset.md b/aptos-move/framework/aptos-framework/doc/fungible_asset.md index f12ee14942b4f4..b3a42cd2297ba0 100644 --- a/aptos-move/framework/aptos-framework/doc/fungible_asset.md +++ b/aptos-move/framework/aptos-framework/doc/fungible_asset.md @@ -20,6 +20,7 @@ metadata object can be any object that equipped with use 0x1::function_info; use 0x1::object; use 0x1::option; +use 0x1::permissioned_signer; use 0x1::signer; use 0x1::string; @@ -557,6 +563,33 @@ MutateMetadataRef can be used to directly modify the fungible asset's Metadata. + + + + +## Struct `WithdrawPermission` + + + +
struct WithdrawPermission has copy, drop, store
+
+ + + +
+Fields + + +
+
+metadata_address: address +
+
+ +
+
+ +
@@ -1135,6 +1168,16 @@ Provided withdraw function type doesn't meet the signature requirement. + + +signer don't have the permission to perform withdraw operation + + +
const EWITHDRAW_PERMISSION_DENIED: u64 = 34;
+
+ + + @@ -2675,12 +2718,75 @@ Withdraw amount of the fungible asset from store by th amount: u64, ): FungibleAsset acquires FungibleStore, DispatchFunctionStore, ConcurrentFungibleBalance { withdraw_sanity_check(owner, store, true); + withdraw_permission_check(owner, store, amount); withdraw_internal(object::object_address(&store), amount) } + + + + +## Function `withdraw_permission_check` + +Check the permission for withdraw operation. + + +
public(friend) fun withdraw_permission_check<T: key>(owner: &signer, store: object::Object<T>, amount: u64)
+
+ + + +
+Implementation + + +
public(friend) fun withdraw_permission_check<T: key>(
+    owner: &signer,
+    store: Object<T>,
+    amount: u64,
+) acquires FungibleStore {
+    assert!(permissioned_signer::check_permission(owner, amount as u256, WithdrawPermission {
+        metadata_address: object::object_address(&borrow_store_resource(&store).metadata)
+    }), error::permission_denied(EWITHDRAW_PERMISSION_DENIED));
+}
+
+ + + +
+ + + +## Function `withdraw_permission_check_by_address` + +Check the permission for withdraw operation. + + +
public(friend) fun withdraw_permission_check_by_address(owner: &signer, metadata_address: address, amount: u64)
+
+ + + +
+Implementation + + +
public(friend) fun withdraw_permission_check_by_address(
+    owner: &signer,
+    metadata_address: address,
+    amount: u64,
+) {
+    assert!(permissioned_signer::check_permission(owner, amount as u256, WithdrawPermission {
+        metadata_address,
+    }), error::permission_denied(EWITHDRAW_PERMISSION_DENIED));
+}
+
+ + +
@@ -3672,6 +3778,72 @@ Ensure a known + +## Function `grant_permission` + +Permission management + +Master signer grant permissioned signer ability to withdraw a given amount of fungible asset. + + +
public fun grant_permission(master: &signer, permissioned: &signer, token_type: object::Object<fungible_asset::Metadata>, amount: u64)
+
+ + + +
+Implementation + + +
public fun grant_permission(
+    master: &signer,
+    permissioned: &signer,
+    token_type: Object<Metadata>,
+    amount: u64
+) {
+    permissioned_signer::authorize(
+        master,
+        permissioned,
+        amount as u256,
+        WithdrawPermission {
+            metadata_address: object::object_address(&token_type),
+        }
+    )
+}
+
+ + + +
+ + + +## Function `revoke_permission` + +Removing permissions from permissioned signer. + + +
public fun revoke_permission(permissioned: &signer, token_type: object::Object<fungible_asset::Metadata>)
+
+ + + +
+Implementation + + +
public fun revoke_permission(permissioned: &signer, token_type: Object<Metadata>) {
+    permissioned_signer::revoke_permission(permissioned, WithdrawPermission {
+        metadata_address: object::object_address(&token_type),
+    })
+}
+
+ + +
diff --git a/aptos-move/framework/aptos-framework/sources/aptos_account.move b/aptos-move/framework/aptos-framework/sources/aptos_account.move index 34addca77cf286..e13a84be4772f7 100644 --- a/aptos-move/framework/aptos-framework/sources/aptos_account.move +++ b/aptos-move/framework/aptos-framework/sources/aptos_account.move @@ -210,6 +210,7 @@ module aptos_framework::aptos_account { // as APT cannot be frozen or have dispatch, and PFS cannot be transfered // (PFS could potentially be burned. regular transfer would permanently unburn the store. // Ignoring the check here has the equivalent of unburning, transfers, and then burning again) + fungible_asset::withdraw_permission_check_by_address(source, @aptos_fungible_asset, amount); fungible_asset::deposit_internal(recipient_store, fungible_asset::withdraw_internal(sender_store, amount)); } diff --git a/aptos-move/framework/aptos-framework/sources/coin.move b/aptos-move/framework/aptos-framework/sources/coin.move index 91a54edb7fdddb..43c450a5caede7 100644 --- a/aptos-move/framework/aptos-framework/sources/coin.move +++ b/aptos-move/framework/aptos-framework/sources/coin.move @@ -13,6 +13,7 @@ module aptos_framework::coin { use aptos_framework::event::{Self, EventHandle}; use aptos_framework::guid; use aptos_framework::optional_aggregator::{Self, OptionalAggregator}; + use aptos_framework::permissioned_signer; use aptos_framework::system_addresses; use aptos_framework::fungible_asset::{Self, FungibleAsset, Metadata, MintRef, TransferRef, BurnRef}; @@ -1168,6 +1169,13 @@ module aptos_framework::coin { ): Coin acquires CoinStore, CoinConversionMap, CoinInfo, PairedCoinType { let account_addr = signer::address_of(account); + let metadata = paired_metadata(); + if(option::is_some(&metadata)) { + fungible_asset::withdraw_permission_check_by_address(account, object::object_address(&option::destroy_some(metadata)), amount); + } else { + permissioned_signer::assert_master_signer(account); + }; + let (coin_amount_to_withdraw, fa_amount_to_withdraw) = calculate_amount_to_withdraw( account_addr, amount diff --git a/aptos-move/framework/aptos-framework/sources/dispatchable_fungible_asset.move b/aptos-move/framework/aptos-framework/sources/dispatchable_fungible_asset.move index 5a70aff95d2c11..37c16214fd8795 100644 --- a/aptos-move/framework/aptos-framework/sources/dispatchable_fungible_asset.move +++ b/aptos-move/framework/aptos-framework/sources/dispatchable_fungible_asset.move @@ -77,6 +77,7 @@ module aptos_framework::dispatchable_fungible_asset { amount: u64, ): FungibleAsset acquires TransferRefStore { fungible_asset::withdraw_sanity_check(owner, store, false); + fungible_asset::withdraw_permission_check(owner, store, amount); let func_opt = fungible_asset::withdraw_dispatch_function(store); if (option::is_some(&func_opt)) { assert!(