diff --git a/aptos-move/framework/aptos-framework/doc/managed_coin.md b/aptos-move/framework/aptos-framework/doc/managed_coin.md index 8ae37e71a1f9c..50c2383fd111d 100644 --- a/aptos-move/framework/aptos-framework/doc/managed_coin.md +++ b/aptos-move/framework/aptos-framework/doc/managed_coin.md @@ -14,6 +14,8 @@ By utilizing this current module, a developer can create his own coin and care l - [Function `initialize`](#0x1_managed_coin_initialize) - [Function `mint`](#0x1_managed_coin_mint) - [Function `register`](#0x1_managed_coin_register) +- [Function `destroy_caps`](#0x1_managed_coin_destroy_caps) +- [Function `remove_caps`](#0x1_managed_coin_remove_caps) - [Specification](#@Specification_1) - [High-level Requirements](#high-level-req) - [Module-level Specification](#module-level-spec) @@ -21,6 +23,8 @@ By utilizing this current module, a developer can create his own coin and care l - [Function `initialize`](#@Specification_1_initialize) - [Function `mint`](#@Specification_1_mint) - [Function `register`](#@Specification_1_register) + - [Function `destroy_caps`](#@Specification_1_destroy_caps) + - [Function `remove_caps`](#@Specification_1_remove_caps)
use 0x1::coin;
@@ -231,6 +235,72 @@ Required if user wants to start accepting deposits of CoinType in h
 
 
 
+
+
+
+
+## Function `destroy_caps`
+
+Destroys capabilities from the account, so that the user no longer has access to mint or burn.
+
+
+
public entry fun destroy_caps<CoinType>(account: &signer)
+
+ + + +
+Implementation + + +
public entry fun destroy_caps<CoinType>(account: &signer) acquires Capabilities {
+    let (burn_cap, freeze_cap, mint_cap) = remove_caps<CoinType>(account);
+    destroy_burn_cap(burn_cap);
+    destroy_freeze_cap(freeze_cap);
+    destroy_mint_cap(mint_cap);
+}
+
+ + + +
+ + + +## Function `remove_caps` + +Removes capabilities from the account to be stored or destroyed elsewhere + + +
public fun remove_caps<CoinType>(account: &signer): (coin::BurnCapability<CoinType>, coin::FreezeCapability<CoinType>, coin::MintCapability<CoinType>)
+
+ + + +
+Implementation + + +
public fun remove_caps<CoinType>(
+    account: &signer
+): (BurnCapability<CoinType>, FreezeCapability<CoinType>, MintCapability<CoinType>) acquires Capabilities {
+    let account_addr = signer::address_of(account);
+    assert!(
+        exists<Capabilities<CoinType>>(account_addr),
+        error::not_found(ENO_CAPABILITIES),
+    );
+
+    let Capabilities<CoinType> {
+        burn_cap,
+        freeze_cap,
+        mint_cap,
+    } = move_from<Capabilities<CoinType>>(account_addr);
+    (burn_cap, freeze_cap, mint_cap)
+}
+
+ + +
@@ -423,4 +493,40 @@ Updating Account.guid_creation_num will not overflow.
+ + + +### Function `destroy_caps` + + +
public entry fun destroy_caps<CoinType>(account: &signer)
+
+ + + + +
let account_addr = signer::address_of(account);
+aborts_if !exists<Capabilities<CoinType>>(account_addr);
+ensures !exists<Capabilities<CoinType>>(account_addr);
+
+ + + + + +### Function `remove_caps` + + +
public fun remove_caps<CoinType>(account: &signer): (coin::BurnCapability<CoinType>, coin::FreezeCapability<CoinType>, coin::MintCapability<CoinType>)
+
+ + + + +
let account_addr = signer::address_of(account);
+aborts_if !exists<Capabilities<CoinType>>(account_addr);
+ensures !exists<Capabilities<CoinType>>(account_addr);
+
+ + [move-book]: https://aptos.dev/move/book/SUMMARY diff --git a/aptos-move/framework/aptos-framework/sources/managed_coin.move b/aptos-move/framework/aptos-framework/sources/managed_coin.move index d2932ddb4edd7..a5da4b24ad5c9 100644 --- a/aptos-move/framework/aptos-framework/sources/managed_coin.move +++ b/aptos-move/framework/aptos-framework/sources/managed_coin.move @@ -6,7 +6,9 @@ module aptos_framework::managed_coin { use std::error; use std::signer; - use aptos_framework::coin::{Self, BurnCapability, FreezeCapability, MintCapability}; + use aptos_framework::coin::{Self, BurnCapability, FreezeCapability, MintCapability, destroy_burn_cap, + destroy_freeze_cap, destroy_mint_cap + }; // // Errors @@ -97,6 +99,32 @@ module aptos_framework::managed_coin { coin::register(account); } + /// Destroys capabilities from the account, so that the user no longer has access to mint or burn. + public entry fun destroy_caps(account: &signer) acquires Capabilities { + let (burn_cap, freeze_cap, mint_cap) = remove_caps(account); + destroy_burn_cap(burn_cap); + destroy_freeze_cap(freeze_cap); + destroy_mint_cap(mint_cap); + } + + /// Removes capabilities from the account to be stored or destroyed elsewhere + public fun remove_caps( + account: &signer + ): (BurnCapability, FreezeCapability, MintCapability) acquires Capabilities { + let account_addr = signer::address_of(account); + assert!( + exists>(account_addr), + error::not_found(ENO_CAPABILITIES), + ); + + let Capabilities { + burn_cap, + freeze_cap, + mint_cap, + } = move_from>(account_addr); + (burn_cap, freeze_cap, mint_cap) + } + // // Tests // @@ -156,6 +184,40 @@ module aptos_framework::managed_coin { let new_supply = coin::supply(); assert!(option::extract(&mut new_supply) == 20, 2); + + // Destroy mint capabilities + destroy_caps(&mod_account); + assert!(!exists>(signer::address_of(&mod_account)), 3); + } + + #[test(source = @0xa11ce, destination = @0xb0b, mod_account = @0x1)] + public entry fun test_end_to_end_caps_removal( + source: signer, + destination: signer, + mod_account: signer + ) acquires Capabilities { + let source_addr = signer::address_of(&source); + let destination_addr = signer::address_of(&destination); + aptos_framework::account::create_account_for_test(source_addr); + aptos_framework::account::create_account_for_test(destination_addr); + aptos_framework::account::create_account_for_test(signer::address_of(&mod_account)); + aggregator_factory::initialize_aggregator_factory_for_test(&mod_account); + + initialize( + &mod_account, + b"Fake Money", + b"FMD", + 10, + true + ); + assert!(coin::is_coin_initialized(), 0); + + // Remove capabilities + let (burn_cap, freeze_cap, mint_cap) = remove_caps(&mod_account); + assert!(!exists>(signer::address_of(&mod_account)), 3); + coin::destroy_mint_cap(mint_cap); + coin::destroy_freeze_cap(freeze_cap); + coin::destroy_burn_cap(burn_cap); } #[test(source = @0xa11ce, destination = @0xb0b, mod_account = @0x1)] diff --git a/aptos-move/framework/aptos-framework/sources/managed_coin.spec.move b/aptos-move/framework/aptos-framework/sources/managed_coin.spec.move index e6eafd0904c11..344c9744f7c97 100644 --- a/aptos-move/framework/aptos-framework/sources/managed_coin.spec.move +++ b/aptos-move/framework/aptos-framework/sources/managed_coin.spec.move @@ -147,4 +147,16 @@ spec aptos_framework::managed_coin { aborts_if !exists>(account_addr) && !type_info::spec_is_struct(); ensures exists>(account_addr); } + + spec remove_caps(account: &signer): (BurnCapability, FreezeCapability, MintCapability) { + let account_addr = signer::address_of(account); + aborts_if !exists>(account_addr); + ensures !exists>(account_addr); + } + + spec destroy_caps (account: &signer) { + let account_addr = signer::address_of(account); + aborts_if !exists>(account_addr); + ensures !exists>(account_addr); + } } diff --git a/aptos-move/framework/cached-packages/src/aptos_framework_sdk_builder.rs b/aptos-move/framework/cached-packages/src/aptos_framework_sdk_builder.rs index aa41aa6ea1fd3..3fa25198f4e99 100644 --- a/aptos-move/framework/cached-packages/src/aptos_framework_sdk_builder.rs +++ b/aptos-move/framework/cached-packages/src/aptos_framework_sdk_builder.rs @@ -510,6 +510,11 @@ pub enum EntryFunctionCall { amount: u64, }, + /// Destroys capabilities from the account, so that the user no longer has access to mint or burn. + ManagedCoinDestroyCaps { + coin_type: TypeTag, + }, + /// Initialize new coin `CoinType` in Aptos Blockchain. /// Mint and Burn Capabilities will be stored under `account` in `Capabilities` resource. ManagedCoinInitialize { @@ -1341,6 +1346,7 @@ impl EntryFunctionCall { n_vec, } => jwks_update_federated_jwk_set(iss, kid_vec, alg_vec, e_vec, n_vec), ManagedCoinBurn { coin_type, amount } => managed_coin_burn(coin_type, amount), + ManagedCoinDestroyCaps { coin_type } => managed_coin_destroy_caps(coin_type), ManagedCoinInitialize { coin_type, name, @@ -2983,6 +2989,22 @@ pub fn managed_coin_burn(coin_type: TypeTag, amount: u64) -> TransactionPayload )) } +/// Destroys capabilities from the account, so that the user no longer has access to mint or burn. +pub fn managed_coin_destroy_caps(coin_type: TypeTag) -> TransactionPayload { + TransactionPayload::EntryFunction(EntryFunction::new( + ModuleId::new( + AccountAddress::new([ + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 1, + ]), + ident_str!("managed_coin").to_owned(), + ), + ident_str!("destroy_caps").to_owned(), + vec![coin_type], + vec![], + )) +} + /// Initialize new coin `CoinType` in Aptos Blockchain. /// Mint and Burn Capabilities will be stored under `account` in `Capabilities` resource. pub fn managed_coin_initialize( @@ -5498,6 +5520,16 @@ mod decoder { } } + pub fn managed_coin_destroy_caps(payload: &TransactionPayload) -> Option { + if let TransactionPayload::EntryFunction(script) = payload { + Some(EntryFunctionCall::ManagedCoinDestroyCaps { + coin_type: script.ty_args().get(0)?.clone(), + }) + } else { + None + } + } + pub fn managed_coin_initialize(payload: &TransactionPayload) -> Option { if let TransactionPayload::EntryFunction(script) = payload { Some(EntryFunctionCall::ManagedCoinInitialize { @@ -6801,6 +6833,10 @@ static SCRIPT_FUNCTION_DECODER_MAP: once_cell::sync::Lazy