diff --git a/aptos-move/framework/aptos-framework/doc/fungible_asset.md b/aptos-move/framework/aptos-framework/doc/fungible_asset.md index 34f0f217ba769..04d23734d5785 100644 --- a/aptos-move/framework/aptos-framework/doc/fungible_asset.md +++ b/aptos-move/framework/aptos-framework/doc/fungible_asset.md @@ -9,43 +9,57 @@ metadata object can be any object that equipped with 0x1::error; +use 0x1::event; use 0x1::object; use 0x1::option; use 0x1::signer; @@ -75,7 +89,7 @@ Define the metadata required of an metadata to be fungible. supply: u64
- The current supply. + The current supply of the fungible asset.
maximum: option::Option<u64> @@ -100,7 +114,7 @@ Define the metadata required of an metadata to be fungible. decimals: u8
- Number of decimals used to get its user representation. + Number of decimals used for display purposes. For example, if decimals equals 2, a balance of 505 coins should be displayed to a user as 5.05 (505 / 10 ** 2).
@@ -113,7 +127,7 @@ Define the metadata required of an metadata to be fungible. ## Resource `FungibleAssetWallet` -The resource of an object holding the properties of fungible assets. +The wallet object that holds fungible assets of a specific type associated with an account.
struct FungibleAssetWallet has key
@@ -142,14 +156,46 @@ The resource of an object holding the properties of fungible assets.
 allow_ungated_transfer: bool
 
 
- Fungible Assets transferring is a common operation, this allows for disabling and enabling - transfers bypassing the use of a TransferRef. + Fungible Assets transferring is a common operation, this allows for freezing/unfreezing accounts.
+ + + + + + + +## Resource `FungibleAssetWalletEvents` + + + +
struct FungibleAssetWalletEvents has key
+
+ + + +
+Fields + + +
-delete_ref: object::DeleteRef +deposit_events: event::EventHandle<fungible_asset::DepositEvent>
- The delete_ref of this object, used for cleanup. + +
+
+withdraw_events: event::EventHandle<fungible_asset::WithdrawEvent> +
+
+ +
+
+set_ungated_transfer_events: event::EventHandle<fungible_asset::SetUngatedTransferEvent> +
+
+
@@ -160,8 +206,8 @@ The resource of an object holding the properties of fungible assets. ## Struct `FungibleAsset` -The transferable version of fungible metadata. -Note: it does not have store ability, only used in hot potato pattern. +FungibleAsset can be passed into function for type safety and to guarantee a specific amount. +FungibleAsset cannot be stored directly and will have to be deposited back into a wallet.
struct FungibleAsset
@@ -195,7 +241,7 @@ Note: it does not have store ability, only used in hot potato patte
 
 ## Struct `MintRef`
 
-Ref to mint.
+MintRef can be used to mint the fungible asset into an account's wallet.
 
 
 
struct MintRef has drop, store
@@ -223,7 +269,8 @@ Ref to mint.
 
 ## Struct `TransferRef`
 
-Ref to control the transfer.
+TransferRef can be used to allow or disallow the owner of fungible assets from transferring the asset
+and allow the holder of TransferRef to transfer fungible assets from any account.
 
 
 
struct TransferRef has drop, store
@@ -251,7 +298,7 @@ Ref to control the transfer.
 
 ## Struct `BurnRef`
 
-Ref to burn..
+BurnRef can be used to burn fungible assets from a given holder account.
 
 
 
struct BurnRef has drop, store
@@ -273,6 +320,90 @@ Ref to burn..
 
 
 
+
+ + + +## Struct `DepositEvent` + +Emitted when fungible assets are deposited into a wallet. + + +
struct DepositEvent has drop, store
+
+ + + +
+Fields + + +
+
+amount: u64 +
+
+ +
+
+ + +
+ + + +## Struct `WithdrawEvent` + +Emitted when fungible assets are withdrawn from a wallet. + + +
struct WithdrawEvent has drop, store
+
+ + + +
+Fields + + +
+
+amount: u64 +
+
+ +
+
+ + +
+ + + +## Struct `SetUngatedTransferEvent` + +Emitted when a wallet's ungated (owner) transfer permission is updated. + + +
struct SetUngatedTransferEvent has drop, store
+
+ + + +
+Fields + + +
+
+transfer_allowed: bool +
+
+ +
+
+ +
@@ -282,10 +413,10 @@ Ref to burn.. -Insufficient amount. +Insufficient balance to withdraw or transfer. -
const EINSUFFICIENT_BALANCE: u64 = 5;
+
const EINSUFFICIENT_BALANCE: u64 = 4;
 
@@ -300,94 +431,115 @@ Amount cannot be zero. + + +Cannot destroy non-empty fungible assets. + + +
const EAMOUNT_IS_NOT_ZERO: u64 = 12;
+
+ + + -The burn ref and the the wallet do not match. +Burn ref and wallet do not match. -
const EBURN_REF_AND_WALLET_MISMATCH: u64 = 3;
+
const EBURN_REF_AND_WALLET_MISMATCH: u64 = 10;
 
- + -Current supply overflow +Fungible asset and wallet do not match. -
const ECURRENT_SUPPLY_OVERFLOW: u64 = 8;
+
const EFUNGIBLE_ASSET_AND_WALLET_MISMATCH: u64 = 11;
 
- + -Current supply underflow +The fungible asset's supply has exceeded maximum. -
const ECURRENT_SUPPLY_UNDERFLOW: u64 = 9;
+
const EMAX_SUPPLY_EXCEEDED: u64 = 5;
 
- + -FungibleAsset type and the wallet type mismatch. +The mint ref and the the wallet do not match. -
const EFUNGIBLE_ASSET_AND_WALLET_MISMATCH: u64 = 6;
+
const EMINT_REF_AND_WALLET_MISMATCH: u64 = 7;
 
- + -Not the owner, +Account is not the wallet's owner. -
const ENOT_OWNER: u64 = 10;
+
const ENOT_WALLET_OWNER: u64 = 8;
 
- + -The transfer ref and the wallet do not match. +More tokens than remaining supply are being burnt. -
const ETRANSFER_REF_AND_WALLET_MISMATCH: u64 = 2;
+
const ESUPPLY_UNDERFLOW: u64 = 6;
 
- + -The token account is still allow_ungated_transfer so cannot be deleted. +The transfer ref and the fungible asset do not match. -
const EUNGATED_TRANSFER_IS_NOT_ALLOWED: u64 = 4;
+
const ETRANSFER_REF_AND_FUNGIBLE_ASSET_MISMATCH: u64 = 2;
 
- + -Amount cannot be zero. +Transfer ref and wallet do not match. -
const EZERO_AMOUNT: u64 = 7;
+
const ETRANSFER_REF_AND_WALLET_MISMATCH: u64 = 9;
 
- + + +Account cannot transfer or receive fungible assets. + + +
const EUNGATED_TRANSFER_IS_NOT_ALLOWED: u64 = 3;
+
+ + -## Function `init_metadata` + -The initialization of an object with FungibleAssetMetadata. +## Function `add_fungibility` +Make an existing object fungible by adding the FungibleAssetMetadata resource. +This returns the capabilities to mint, burn, and transfer. -
public fun init_metadata(constructor_ref: &object::ConstructorRef, maximum_supply: u64, name: string::String, symbol: string::String, decimals: u8): (fungible_asset::MintRef, fungible_asset::TransferRef, fungible_asset::BurnRef)
+
+
public fun add_fungibility(constructor_ref: &object::ConstructorRef, maximum_supply: u64, name: string::String, symbol: string::String, decimals: u8): object::Object<fungible_asset::FungibleAssetMetadata>
 
@@ -396,20 +548,20 @@ The initialization of an object with init_metadata( +
public fun add_fungibility(
     constructor_ref: &ConstructorRef,
     maximum_supply: u64,
     name: String,
     symbol: String,
     decimals: u8,
-): (MintRef, TransferRef, BurnRef) {
-    let metadata_object_signer = object::generate_signer(constructor_ref);
+): Object<FungibleAssetMetadata> {
+    let metadata_object_signer = &object::generate_signer(constructor_ref);
     let converted_maximum = if (maximum_supply == 0) {
         option::none()
     } else {
         option::some(maximum_supply)
     };
-    move_to(&metadata_object_signer,
+    move_to(metadata_object_signer,
         FungibleAssetMetadata {
             supply: 0,
             maximum: converted_maximum,
@@ -418,8 +570,89 @@ The initialization of an object with object::object_from_constructor_ref<FungibleAssetMetadata>(constructor_ref)
+}
+
+ + + + + + + +## Function `generate_mint_ref` + +Creates a mint ref that can be used to mint fungible assets from the given fungible object's constructor ref. +This can only be called at object creation time as constructor_ref is only available then. + + +
public fun generate_mint_ref(constructor_ref: &object::ConstructorRef): fungible_asset::MintRef
+
+ + + +
+Implementation + + +
public fun generate_mint_ref(constructor_ref: &ConstructorRef): MintRef {
+    let metadata = object::object_from_constructor_ref<FungibleAssetMetadata>(constructor_ref);
+    MintRef { metadata }
+}
+
+ + + +
+ + + +## Function `generate_burn_ref` + +Creates a burn ref that can be used to burn fungible assets from the given fungible object's constructor ref. +This can only be called at object creation time as constructor_ref is only available then. + + +
public fun generate_burn_ref(constructor_ref: &object::ConstructorRef): fungible_asset::BurnRef
+
+ + + +
+Implementation + + +
public fun generate_burn_ref(constructor_ref: &ConstructorRef): BurnRef {
+    let metadata = object::object_from_constructor_ref<FungibleAssetMetadata>(constructor_ref);
+    BurnRef { metadata }
+}
+
+ + + +
+ + + +## Function `generate_transfer_ref` + +Creates a transfer ref that can be used to freeze/unfreeze/transfer fungible assets from the given fungible +object's constructor ref. +This can only be called at object creation time as constructor_ref is only available then. + + +
public fun generate_transfer_ref(constructor_ref: &object::ConstructorRef): fungible_asset::TransferRef
+
+ + + +
+Implementation + + +
public fun generate_transfer_ref(constructor_ref: &ConstructorRef): TransferRef {
     let metadata = object::object_from_constructor_ref<FungibleAssetMetadata>(constructor_ref);
-    (MintRef { metadata }, TransferRef { metadata }, BurnRef { metadata })
+    TransferRef { metadata }
 }
 
@@ -434,7 +667,7 @@ The initialization of an object with supply<T: key>(metadata: &object::Object<T>): u64 +
public fun supply<T: key>(metadata: object::Object<T>): u64
 
@@ -443,8 +676,8 @@ Get the current supply from metadata. Implementation -
public fun supply<T: key>(metadata: &Object<T>): u64 acquires FungibleAssetMetadata {
-    borrow_fungible_metadata(metadata).supply
+
public fun supply<T: key>(metadata: Object<T>): u64 acquires FungibleAssetMetadata {
+    borrow_fungible_metadata(&metadata).supply
 }
 
@@ -459,7 +692,7 @@ Get the current supply from metadata. Get the maximum supply from metadata. -
public fun maximum<T: key>(metadata: &object::Object<T>): option::Option<u64>
+
public fun maximum<T: key>(metadata: object::Object<T>): option::Option<u64>
 
@@ -468,8 +701,8 @@ Get the maximum supply from metadata. Implementation -
public fun maximum<T: key>(metadata: &Object<T>): Option<u64> acquires FungibleAssetMetadata {
-    borrow_fungible_metadata(metadata).maximum
+
public fun maximum<T: key>(metadata: Object<T>): Option<u64> acquires FungibleAssetMetadata {
+    borrow_fungible_metadata(&metadata).maximum
 }
 
@@ -484,7 +717,7 @@ Get the maximum supply from metadata. Get the name of the fungible asset from metadata. -
public fun name<T: key>(metadata: &object::Object<T>): string::String
+
public fun name<T: key>(metadata: object::Object<T>): string::String
 
@@ -493,8 +726,8 @@ Get the name of the fungible asset from metadata. Implementation -
public fun name<T: key>(metadata: &Object<T>): String acquires FungibleAssetMetadata {
-    borrow_fungible_metadata(metadata).name
+
public fun name<T: key>(metadata: Object<T>): String acquires FungibleAssetMetadata {
+    borrow_fungible_metadata(&metadata).name
 }
 
@@ -509,7 +742,7 @@ Get the name of the fungible asset from metadata. Get the symbol of the fungible asset from metadata. -
public fun symbol<T: key>(metadata: &object::Object<T>): string::String
+
public fun symbol<T: key>(metadata: object::Object<T>): string::String
 
@@ -518,8 +751,8 @@ Get the symbol of the fungible asset from metadata. Implementation -
public fun symbol<T: key>(metadata: &Object<T>): String acquires FungibleAssetMetadata {
-    borrow_fungible_metadata(metadata).symbol
+
public fun symbol<T: key>(metadata: Object<T>): String acquires FungibleAssetMetadata {
+    borrow_fungible_metadata(&metadata).symbol
 }
 
@@ -534,7 +767,7 @@ Get the symbol of the fungible asset from metadata. Get the decimals from metadata. -
public fun decimals<T: key>(metadata: &object::Object<T>): u8
+
public fun decimals<T: key>(metadata: object::Object<T>): u8
 
@@ -543,8 +776,8 @@ Get the decimals from metadata. Implementation -
public fun decimals<T: key>(metadata: &Object<T>): u8 acquires FungibleAssetMetadata {
-    borrow_fungible_metadata(metadata).decimals
+
public fun decimals<T: key>(metadata: Object<T>): u8 acquires FungibleAssetMetadata {
+    borrow_fungible_metadata(&metadata).decimals
 }
 
@@ -552,14 +785,14 @@ Get the decimals from metadata.
- + -## Function `verify` +## Function `wallet_exists` -Verify any object is equipped with FungibleAssetMetadata and return the object. +Return whether the provided address has a wallet initialized. -
public fun verify<T: key>(metadata: &object::Object<T>): object::Object<fungible_asset::FungibleAssetMetadata>
+
public fun wallet_exists(wallet: address): bool
 
@@ -568,9 +801,8 @@ Verify any object is equipped with verify<T: key>(metadata: &Object<T>): Object<FungibleAssetMetadata> { - let addr = object::object_address(metadata); - object::address_to_object<FungibleAssetMetadata>(addr) +
public fun wallet_exists(wallet: address): bool {
+    exists<FungibleAssetWallet>(wallet)
 }
 
@@ -578,14 +810,14 @@ Verify any object is equipped with + -## Function `new_fungible_asset_wallet_object` +## Function `metadata_from_asset` -Create a new wallet object to hold fungible asset. +Return the underlying metadata object -
public(friend) fun new_fungible_asset_wallet_object<T: key>(creator_ref: &object::ConstructorRef, metadata: &object::Object<T>): object::Object<fungible_asset::FungibleAssetWallet>
+
public fun metadata_from_asset(fa: &fungible_asset::FungibleAsset): object::Object<fungible_asset::FungibleAssetMetadata>
 
@@ -594,20 +826,33 @@ Create a new wallet object to hold fungible asset. Implementation -
public(friend) fun new_fungible_asset_wallet_object<T: key>(
-    creator_ref: &ConstructorRef,
-    metadata: &Object<T>,
-): Object<FungibleAssetWallet> {
-    let wallet_signer = object::generate_signer(creator_ref);
-    let metadata = verify(metadata);
+
public fun metadata_from_asset(fa: &FungibleAsset): Object<FungibleAssetMetadata> {
+    fa.metadata
+}
+
- move_to(&wallet_signer, FungibleAssetWallet { - metadata, - balance: 0, - allow_ungated_transfer: true, - delete_ref: object::generate_delete_ref(creator_ref) - }); - object::object_from_constructor_ref<FungibleAssetWallet>(creator_ref) + + + + + + +## Function `wallet_metadata` + +Return the underlying metadata object. + + +
public fun wallet_metadata<T: key>(wallet: object::Object<T>): object::Object<fungible_asset::FungibleAssetMetadata>
+
+ + + +
+Implementation + + +
public fun wallet_metadata<T: key>(wallet: Object<T>): Object<FungibleAssetMetadata> acquires FungibleAssetWallet {
+    borrow_wallet_resource(&wallet).metadata
 }
 
@@ -615,14 +860,14 @@ Create a new wallet object to hold fungible asset.
- + -## Function `metadata_from_asset` +## Function `amount` -Return the underlying metadata object +Return amount of a given fungible asset. -
public fun metadata_from_asset(fa: &fungible_asset::FungibleAsset): object::Object<fungible_asset::FungibleAssetMetadata>
+
public fun amount(fa: &fungible_asset::FungibleAsset): u64
 
@@ -631,8 +876,8 @@ Return the underlying metadata object Implementation -
public fun metadata_from_asset(fa: &FungibleAsset): Object<FungibleAssetMetadata> {
-    fa.metadata
+
public fun amount(fa: &FungibleAsset): u64 {
+    fa.amount
 }
 
@@ -640,14 +885,14 @@ Return the underlying metadata object - + -## Function `metadata_from_wallet` +## Function `balance` -Return the underlying metadata object. +Get the balance of a given wallet. -
public fun metadata_from_wallet(wallet: &object::Object<fungible_asset::FungibleAssetWallet>): object::Object<fungible_asset::FungibleAssetMetadata>
+
public fun balance<T: key>(wallet: object::Object<T>): u64
 
@@ -656,10 +901,12 @@ Return the underlying metadata object. Implementation -
public fun metadata_from_wallet(
-    wallet: &Object<FungibleAssetWallet>
-): Object<FungibleAssetMetadata> acquires FungibleAssetWallet {
-    borrow_fungible_asset(wallet).metadata
+
public fun balance<T: key>(wallet: Object<T>): u64 acquires FungibleAssetWallet {
+    if (wallet_exists(object::object_address(&wallet))) {
+        borrow_wallet_resource(&wallet).balance
+    } else {
+        0
+    }
 }
 
@@ -667,14 +914,15 @@ Return the underlying metadata object. - + -## Function `amount` +## Function `ungated_transfer_allowed` -Return amount inside. +Return whether a wallet can freely send or receive fungible assets. +If the wallet has not been created, we default to returning true as deposits can be sent to it. -
public fun amount(fa: &fungible_asset::FungibleAsset): u64
+
public fun ungated_transfer_allowed<T: key>(wallet: object::Object<T>): bool
 
@@ -683,8 +931,9 @@ Return amount inside. Implementation -
public fun amount(fa: &FungibleAsset): u64 {
-    fa.amount
+
public fun ungated_transfer_allowed<T: key>(wallet: Object<T>): bool acquires FungibleAssetWallet {
+    !wallet_exists(object::object_address(&wallet)) ||
+        borrow_wallet_resource(&wallet).allow_ungated_transfer
 }
 
@@ -692,14 +941,13 @@ Return amount inside. - + -## Function `destory_fungible_asset_wallet` +## Function `asset_metadata` -Destroy wallet object. -
public(friend) fun destory_fungible_asset_wallet(wallet: object::Object<fungible_asset::FungibleAssetWallet>)
+
public fun asset_metadata(fa: &fungible_asset::FungibleAsset): object::Object<fungible_asset::FungibleAssetMetadata>
 
@@ -708,16 +956,8 @@ Destroy wallet object. Implementation -
public(friend) fun destory_fungible_asset_wallet(
-    wallet: Object<FungibleAssetWallet>
-) acquires FungibleAssetWallet {
-    let FungibleAssetWallet {
-        metadata: _,
-        balance: _,
-        allow_ungated_transfer: _,
-        delete_ref
-    } = move_from<FungibleAssetWallet>(object::object_address(&wallet));
-    object::delete(delete_ref);
+
public fun asset_metadata(fa: &FungibleAsset): Object<FungibleAssetMetadata> {
+    fa.metadata
 }
 
@@ -725,14 +965,14 @@ Destroy wallet object. - + -## Function `balance` +## Function `mint_ref_metadata` -Get the balance of wallet. +Get the underlying metadata object from MintRef. -
public fun balance(wallet: &object::Object<fungible_asset::FungibleAssetWallet>): u64
+
public fun mint_ref_metadata(ref: &fungible_asset::MintRef): object::Object<fungible_asset::FungibleAssetMetadata>
 
@@ -741,8 +981,8 @@ Get the balance of wallet. Implementation -
public fun balance(wallet: &Object<FungibleAssetWallet>): u64 acquires FungibleAssetWallet {
-    borrow_fungible_asset(wallet).balance
+
public fun mint_ref_metadata(ref: &MintRef): Object<FungibleAssetMetadata> {
+    ref.metadata
 }
 
@@ -750,14 +990,14 @@ Get the balance of wallet. - + -## Function `ungated_transfer_allowed` +## Function `transfer_ref_metadata` -Return allowed_ungated_transfer. +Get the underlying metadata object from TransferRef. -
public fun ungated_transfer_allowed(wallet: &object::Object<fungible_asset::FungibleAssetWallet>): bool
+
public fun transfer_ref_metadata(ref: &fungible_asset::TransferRef): object::Object<fungible_asset::FungibleAssetMetadata>
 
@@ -766,8 +1006,8 @@ Return allowed_ungated_transfer. Implementation -
public fun ungated_transfer_allowed(wallet: &Object<FungibleAssetWallet>): bool acquires FungibleAssetWallet {
-    borrow_fungible_asset(wallet).allow_ungated_transfer
+
public fun transfer_ref_metadata(ref: &TransferRef): Object<FungibleAssetMetadata> {
+    ref.metadata
 }
 
@@ -775,14 +1015,14 @@ Return allowed_ungated_transfer. - + -## Function `mint` +## Function `burn_ref_metadata` -Mint the amount of fungible asset. +Get the underlying metadata object from BurnRef. -
public fun mint(ref: &fungible_asset::MintRef, amount: u64): fungible_asset::FungibleAsset
+
public fun burn_ref_metadata(ref: &fungible_asset::BurnRef): object::Object<fungible_asset::FungibleAssetMetadata>
 
@@ -791,17 +1031,8 @@ Mint the amount of fungible asset. Implementation -
public fun mint(
-    ref: &MintRef,
-    amount: u64,
-): FungibleAsset acquires FungibleAssetMetadata {
-    assert!(amount != 0, error::invalid_argument(EAMOUNT_CANNOT_BE_ZERO));
-    let metadata = verify(&ref.metadata);
-    increase_supply(&ref.metadata, amount);
-    FungibleAsset {
-        metadata,
-        amount
-    }
+
public fun burn_ref_metadata(ref: &BurnRef): Object<FungibleAssetMetadata> {
+    ref.metadata
 }
 
@@ -809,14 +1040,15 @@ Mint the amount of fungible asset. - + -## Function `set_ungated_transfer` +## Function `transfer` -Enable/disable the direct transfer of fungible asset. +Transfer amount of fungible asset from from_wallet, which should be owned by sender, to receiver. +Note: it does not move the underlying object. -
public fun set_ungated_transfer(ref: &fungible_asset::TransferRef, wallet: &object::Object<fungible_asset::FungibleAssetWallet>, allow: bool)
+
public entry fun transfer<T: key>(sender: &signer, from: object::Object<T>, to: object::Object<T>, amount: u64)
 
@@ -825,16 +1057,14 @@ Enable/disable the direct transfer of fungible asset. Implementation -
public fun set_ungated_transfer(
-    ref: &TransferRef,
-    wallet: &Object<FungibleAssetWallet>,
-    allow: bool,
-) acquires FungibleAssetWallet {
-    assert!(
-        &ref.metadata == &metadata_from_wallet(wallet),
-        error::invalid_argument(ETRANSFER_REF_AND_WALLET_MISMATCH)
-    );
-    borrow_fungible_asset_mut(wallet).allow_ungated_transfer = allow;
+
public entry fun transfer<T: key>(
+    sender: &signer,
+    from: Object<T>,
+    to: Object<T>,
+    amount: u64,
+) acquires FungibleAssetWallet, FungibleAssetWalletEvents {
+    let fa = withdraw(sender, from, amount);
+    deposit(to, fa);
 }
 
@@ -842,14 +1072,15 @@ Enable/disable the direct transfer of fungible asset. - + -## Function `burn` +## Function `create_wallet` -Burn the amount of fungible metadata from account. +Allow an object to hold a wallet for fungible assets. +Applications can use this to create multiple wallets for isolating fungible assets for different purposes. -
public fun burn(ref: &fungible_asset::BurnRef, wallet: &object::Object<fungible_asset::FungibleAssetWallet>, amount: u64)
+
public fun create_wallet<T: key>(constructor_ref: &object::ConstructorRef, metadata: object::Object<T>): object::Object<fungible_asset::FungibleAssetWallet>
 
@@ -858,20 +1089,26 @@ Burn the amount of fungible metadata from burn( - ref: &BurnRef, - wallet: &Object<FungibleAssetWallet>, - amount: u64 -) acquires FungibleAssetWallet, FungibleAssetMetadata { - assert!( - &ref.metadata == &metadata_from_wallet(wallet), - error::invalid_argument(EBURN_REF_AND_WALLET_MISMATCH) - ); - let FungibleAsset { +
public fun create_wallet<T: key>(
+    constructor_ref: &ConstructorRef,
+    metadata: Object<T>,
+): Object<FungibleAssetWallet> {
+    let wallet_obj = &object::generate_signer(constructor_ref);
+    let metadata = object::convert<T, FungibleAssetMetadata>(metadata);
+    move_to(wallet_obj, FungibleAssetWallet {
         metadata,
-        amount,
-    } = extract(wallet, amount);
-    decrease_supply(&metadata, amount);
+        balance: 0,
+        allow_ungated_transfer: true,
+    });
+    move_to(wallet_obj,
+        FungibleAssetWalletEvents {
+            deposit_events: object::new_event_handle<DepositEvent>(wallet_obj),
+            withdraw_events: object::new_event_handle<WithdrawEvent>(wallet_obj),
+            set_ungated_transfer_events: object::new_event_handle<SetUngatedTransferEvent>(wallet_obj),
+        }
+    );
+
+    object::object_from_constructor_ref<FungibleAssetWallet>(constructor_ref)
 }
 
@@ -883,10 +1120,10 @@ Burn the amount of fungible metadata from withdraw(account: &signer, wallet: &object::Object<fungible_asset::FungibleAssetWallet>, amount: u64): fungible_asset::FungibleAsset +
public fun withdraw<T: key>(owner: &signer, wallet: object::Object<T>, amount: u64): fungible_asset::FungibleAsset
 
@@ -895,13 +1132,14 @@ Withdarw amount of fungible asset from wallet by the o Implementation -
public fun withdraw(
-    account: &signer,
-    wallet: &Object<FungibleAssetWallet>,
-    amount: u64
-): FungibleAsset acquires FungibleAssetWallet {
-    assert_owner(account, wallet);
-    extract(wallet, amount)
+
public fun withdraw<T: key>(
+    owner: &signer,
+    wallet: Object<T>,
+    amount: u64,
+): FungibleAsset acquires FungibleAssetWallet, FungibleAssetWalletEvents {
+    assert!(object::owns(wallet, signer::address_of(owner)), error::permission_denied(ENOT_WALLET_OWNER));
+    assert!(ungated_transfer_allowed(wallet), error::invalid_argument(EUNGATED_TRANSFER_IS_NOT_ALLOWED));
+    withdraw_internal(object::object_address(&wallet), amount)
 }
 
@@ -916,7 +1154,7 @@ Withdarw amount of fungible asset from wallet by the o Deposit amount of fungible asset to wallet. -
public fun deposit(wallet: &object::Object<fungible_asset::FungibleAssetWallet>, fa: fungible_asset::FungibleAsset)
+
public fun deposit<T: key>(wallet: object::Object<T>, fa: fungible_asset::FungibleAsset)
 
@@ -925,16 +1163,12 @@ Deposit amount of fungible asset to wallet. Implementation -
public fun deposit(
-    wallet: &Object<FungibleAssetWallet>,
+
public fun deposit<T: key>(
+    wallet: Object<T>,
     fa: FungibleAsset,
-) acquires FungibleAssetWallet {
-    let FungibleAsset { metadata, amount } = fa;
-    // ensure merging the same coin
-    let wallet = borrow_fungible_asset_mut(wallet);
-    assert!(wallet.allow_ungated_transfer, error::invalid_argument(EUNGATED_TRANSFER_IS_NOT_ALLOWED));
-    assert!(wallet.metadata == metadata, error::invalid_argument(EFUNGIBLE_ASSET_AND_WALLET_MISMATCH));
-    wallet.balance = wallet.balance + amount;
+) acquires FungibleAssetWallet, FungibleAssetWalletEvents {
+    assert!(ungated_transfer_allowed(wallet), error::invalid_argument(EUNGATED_TRANSFER_IS_NOT_ALLOWED));
+    deposit_internal(wallet, fa);
 }
 
@@ -942,15 +1176,14 @@ Deposit amount of fungible asset to wallet. - + -## Function `transfer` +## Function `mint` -Transfer amount of fungible metadata of metadata to receiver. -Note: it does not move the underlying object. +Mint the specified amount of fungible asset. -
public fun transfer(account: &signer, amount: u64, from: &object::Object<fungible_asset::FungibleAssetWallet>, to: &object::Object<fungible_asset::FungibleAssetWallet>)
+
public fun mint(ref: &fungible_asset::MintRef, amount: u64): fungible_asset::FungibleAsset
 
@@ -959,14 +1192,120 @@ Note: it does not move the underlying object. Implementation -
public fun transfer(
-    account: &signer,
+
public fun mint(
+    ref: &MintRef,
     amount: u64,
-    from: &Object<FungibleAssetWallet>,
-    to: &Object<FungibleAssetWallet>,
-) acquires FungibleAssetWallet {
-    let fa = withdraw(account, from, amount);
-    deposit(to, fa);
+): FungibleAsset acquires FungibleAssetMetadata {
+    assert!(amount > 0, error::invalid_argument(EAMOUNT_CANNOT_BE_ZERO));
+    let metadata = ref.metadata;
+    increase_supply(&metadata, amount);
+
+    FungibleAsset {
+        metadata,
+        amount
+    }
+}
+
+ + + + + + + +## Function `mint_to` + +Mint the specified amount of fungible asset to a destination wallet. + + +
public fun mint_to<T: key>(ref: &fungible_asset::MintRef, wallet: object::Object<T>, amount: u64)
+
+ + + +
+Implementation + + +
public fun mint_to<T: key>(
+    ref: &MintRef,
+    wallet: Object<T>,
+    amount: u64,
+) acquires FungibleAssetMetadata, FungibleAssetWallet, FungibleAssetWalletEvents {
+    deposit(wallet, mint(ref, amount));
+}
+
+ + + +
+ + + +## Function `set_ungated_transfer` + +Enable/disable a wallet's ability to do direct transfers of fungible asset. + + +
public fun set_ungated_transfer<T: key>(ref: &fungible_asset::TransferRef, wallet: object::Object<T>, allow: bool)
+
+ + + +
+Implementation + + +
public fun set_ungated_transfer<T: key>(
+    ref: &TransferRef,
+    wallet: Object<T>,
+    allow: bool,
+) acquires FungibleAssetWallet, FungibleAssetWalletEvents {
+    assert!(
+        ref.metadata == wallet_metadata(wallet),
+        error::invalid_argument(ETRANSFER_REF_AND_WALLET_MISMATCH),
+    );
+    let wallet_addr = object::object_address(&wallet);
+    borrow_global_mut<FungibleAssetWallet>(wallet_addr).allow_ungated_transfer = allow;
+
+    let events = borrow_global_mut<FungibleAssetWalletEvents>(wallet_addr);
+    event::emit_event(&mut events.set_ungated_transfer_events, SetUngatedTransferEvent { transfer_allowed: allow });
+}
+
+ + + +
+ + + +## Function `burn` + +Burn the amount of fungible metadata from the given wallet. + + +
public fun burn<T: key>(ref: &fungible_asset::BurnRef, wallet: object::Object<T>, amount: u64)
+
+ + + +
+Implementation + + +
public fun burn<T: key>(
+    ref: &BurnRef,
+    wallet: Object<T>,
+    amount: u64
+) acquires FungibleAssetMetadata, FungibleAssetWallet, FungibleAssetWalletEvents {
+    let metadata = ref.metadata;
+    assert!(metadata == wallet_metadata(wallet), error::invalid_argument(EBURN_REF_AND_WALLET_MISMATCH));
+    let wallet_addr = object::object_address(&wallet);
+    let FungibleAsset {
+        metadata,
+        amount,
+    } = withdraw_internal(wallet_addr, amount);
+    decrease_supply(&metadata, amount);
 }
 
@@ -978,10 +1317,10 @@ Note: it does not move the underlying object. ## Function `withdraw_with_ref` -Withdarw amount of fungible metadata from account ignoring allow_ungated_transfer. +Withdraw amount of fungible metadata from wallet ignoring allow_ungated_transfer. -
public fun withdraw_with_ref(ref: &fungible_asset::TransferRef, wallet: &object::Object<fungible_asset::FungibleAssetWallet>, amount: u64): fungible_asset::FungibleAsset
+
public fun withdraw_with_ref<T: key>(ref: &fungible_asset::TransferRef, wallet: object::Object<T>, amount: u64): fungible_asset::FungibleAsset
 
@@ -990,24 +1329,16 @@ Withdarw amount of fungible metadata from withdraw_with_ref( +
public fun withdraw_with_ref<T: key>(
     ref: &TransferRef,
-    wallet: &Object<FungibleAssetWallet>,
+    wallet: Object<T>,
     amount: u64
-): FungibleAsset acquires FungibleAssetWallet {
+): FungibleAsset acquires FungibleAssetWallet, FungibleAssetWalletEvents {
     assert!(
-        &ref.metadata == &metadata_from_wallet(wallet),
-        error::invalid_argument(ETRANSFER_REF_AND_WALLET_MISMATCH)
+        ref.metadata == wallet_metadata(wallet),
+        error::invalid_argument(ETRANSFER_REF_AND_WALLET_MISMATCH),
     );
-    let ungated_transfer_allowed = ungated_transfer_allowed(wallet);
-    if (!ungated_transfer_allowed) {
-        set_ungated_transfer(ref, wallet, true);
-    };
-    let fa = extract(wallet, amount);
-    if (!ungated_transfer_allowed) {
-        set_ungated_transfer(ref, wallet, false);
-    };
-    fa
+    withdraw_internal(object::object_address(&wallet), amount)
 }
 
@@ -1019,10 +1350,10 @@ Withdarw amount of fungible metadata from account ignoring allow_ungated_transfer. +Deposit fungible asset into wallet ignoring allow_ungated_transfer. -
public fun deposit_with_ref(ref: &fungible_asset::TransferRef, wallet: &object::Object<fungible_asset::FungibleAssetWallet>, fa: fungible_asset::FungibleAsset)
+
public fun deposit_with_ref<T: key>(ref: &fungible_asset::TransferRef, wallet: object::Object<T>, fa: fungible_asset::FungibleAsset)
 
@@ -1031,23 +1362,16 @@ Deposit fungible asset into accountImplementation -
public fun deposit_with_ref(
+
public fun deposit_with_ref<T: key>(
     ref: &TransferRef,
-    wallet: &Object<FungibleAssetWallet>,
+    wallet: Object<T>,
     fa: FungibleAsset
-) acquires FungibleAssetWallet {
+) acquires FungibleAssetWallet, FungibleAssetWalletEvents {
     assert!(
-        &ref.metadata == &metadata_from_wallet(wallet),
-        error::invalid_argument(ETRANSFER_REF_AND_WALLET_MISMATCH)
+        ref.metadata == fa.metadata,
+        error::invalid_argument(ETRANSFER_REF_AND_FUNGIBLE_ASSET_MISMATCH)
     );
-    let ungated_transfer_allowed = ungated_transfer_allowed(wallet);
-    if (!ungated_transfer_allowed) {
-        set_ungated_transfer(ref, wallet, true);
-    };
-    deposit(wallet, fa);
-    if (!ungated_transfer_allowed) {
-        set_ungated_transfer(ref, wallet, false);
-    };
+    deposit_internal(wallet, fa);
 }
 
@@ -1062,7 +1386,7 @@ Deposit fungible asset into accountammount of fungible metadata with TransferRef even ungated transfer is disabled. -
public fun transfer_with_ref(transfer_ref: &fungible_asset::TransferRef, from: &object::Object<fungible_asset::FungibleAssetWallet>, to: &object::Object<fungible_asset::FungibleAssetWallet>, amount: u64)
+
public fun transfer_with_ref<T: key>(transfer_ref: &fungible_asset::TransferRef, from: object::Object<T>, to: object::Object<T>, amount: u64)
 
@@ -1071,12 +1395,12 @@ Transfer ammount of fungible metadata with transfer_with_ref( +
public fun transfer_with_ref<T: key>(
     transfer_ref: &TransferRef,
-    from: &Object<FungibleAssetWallet>,
-    to: &Object<FungibleAssetWallet>,
+    from: Object<T>,
+    to: Object<T>,
     amount: u64,
-) acquires FungibleAssetWallet {
+) acquires FungibleAssetWallet, FungibleAssetWalletEvents {
     let fa = withdraw_with_ref(transfer_ref, from, amount);
     deposit_with_ref(transfer_ref, to, fa);
 }
@@ -1086,14 +1410,14 @@ Transfer ammount of  fungible metadata with 
+
 
-## Function `mint_ref_metadata`
+## Function `extract`
 
-Get the underlying metadata object from MintRef.
+Extract a given amount from the given fungible asset and return a new one.
 
 
-
public fun mint_ref_metadata(ref: &fungible_asset::MintRef): object::Object<fungible_asset::FungibleAssetMetadata>
+
public fun extract(fungible_asset: &mut fungible_asset::FungibleAsset, amount: u64): fungible_asset::FungibleAsset
 
@@ -1102,8 +1426,13 @@ Get the underlying metadata object from mint_ref_metadata(ref: &MintRef): Object<FungibleAssetMetadata> { - ref.metadata +
public fun extract(fungible_asset: &mut FungibleAsset, amount: u64): FungibleAsset {
+    assert!(fungible_asset.amount >= amount, error::invalid_argument(EINSUFFICIENT_BALANCE));
+    fungible_asset.amount = fungible_asset.amount - amount;
+    FungibleAsset {
+        metadata: fungible_asset.metadata,
+        amount,
+    }
 }
 
@@ -1111,14 +1440,15 @@ Get the underlying metadata object from + -## Function `transfer_ref_metadata` +## Function `merge` -Get the underlying metadata object from TransferRef. +"Merges" the two given fungible assets. The coin passed in as dst_fungible_asset will have a value equal +to the sum of the two (dst_fungible_asset and src_fungible_asset). -
public fun transfer_ref_metadata(ref: &fungible_asset::TransferRef): object::Object<fungible_asset::FungibleAssetMetadata>
+
public fun merge(dst_fungible_asset: &mut fungible_asset::FungibleAsset, src_fungible_asset: fungible_asset::FungibleAsset)
 
@@ -1127,8 +1457,9 @@ Get the underlying metadata object from transfer_ref_metadata(ref: &TransferRef): Object<FungibleAssetMetadata> { - ref.metadata +
public fun merge(dst_fungible_asset: &mut FungibleAsset, src_fungible_asset: FungibleAsset) {
+    let FungibleAsset { metadata: _, amount } = src_fungible_asset;
+    dst_fungible_asset.amount = dst_fungible_asset.amount + amount;
 }
 
@@ -1136,14 +1467,14 @@ Get the underlying metadata object from + -## Function `burn_ref_metadata` +## Function `destroy_zero` -Get the underlying metadata object from BurnRef. +Destroy an empty fungible asset. -
public fun burn_ref_metadata(ref: &fungible_asset::BurnRef): object::Object<fungible_asset::FungibleAssetMetadata>
+
public fun destroy_zero(fungible_asset: fungible_asset::FungibleAsset)
 
@@ -1152,8 +1483,9 @@ Get the underlying metadata object from burn_ref_metadata(ref: &BurnRef): Object<FungibleAssetMetadata> { - ref.metadata +
public fun destroy_zero(fungible_asset: FungibleAsset) {
+    let FungibleAsset { amount, metadata: _ } = fungible_asset;
+    assert!(amount == 0, error::invalid_argument(EAMOUNT_IS_NOT_ZERO));
 }
 
@@ -1161,14 +1493,49 @@ Get the underlying metadata object from + -## Function `extract` +## Function `deposit_internal` + + + +
fun deposit_internal<T: key>(wallet: object::Object<T>, fa: fungible_asset::FungibleAsset)
+
+ + + +
+Implementation + + +
fun deposit_internal<T: key>(
+    wallet: Object<T>,
+    fa: FungibleAsset,
+) acquires FungibleAssetWallet, FungibleAssetWalletEvents {
+    let FungibleAsset { metadata, amount } = fa;
+    let wallet_metadata = wallet_metadata(wallet);
+    assert!(metadata == wallet_metadata, error::invalid_argument(EFUNGIBLE_ASSET_AND_WALLET_MISMATCH));
+    let wallet_addr = object::object_address(&wallet);
+    let wallet = borrow_global_mut<FungibleAssetWallet>(wallet_addr);
+    wallet.balance = wallet.balance + amount;
+
+    let events = borrow_global_mut<FungibleAssetWalletEvents>(wallet_addr);
+    event::emit_event(&mut events.deposit_events, DepositEvent { amount });
+}
+
+ + + +
+ + + +## Function `withdraw_internal` Extract amount of fungible asset from wallet. -
fun extract(wallet: &object::Object<fungible_asset::FungibleAssetWallet>, amount: u64): fungible_asset::FungibleAsset
+
fun withdraw_internal(wallet_addr: address, amount: u64): fungible_asset::FungibleAsset
 
@@ -1177,19 +1544,20 @@ Extract amount of fungible asset from wallet. Implementation -
fun extract(
-    wallet: &Object<FungibleAssetWallet>,
+
fun withdraw_internal(
+    wallet_addr: address,
     amount: u64,
-): FungibleAsset acquires FungibleAssetWallet {
+): FungibleAsset acquires FungibleAssetWallet, FungibleAssetWalletEvents {
     assert!(amount != 0, error::invalid_argument(EAMOUNT_CANNOT_BE_ZERO));
-    let wallet = borrow_fungible_asset_mut(wallet);
-    assert!(wallet.allow_ungated_transfer, error::invalid_argument(EUNGATED_TRANSFER_IS_NOT_ALLOWED));
+    let wallet = borrow_global_mut<FungibleAssetWallet>(wallet_addr);
     assert!(wallet.balance >= amount, error::invalid_argument(EINSUFFICIENT_BALANCE));
     wallet.balance = wallet.balance - amount;
-    FungibleAsset {
-        metadata: wallet.metadata,
-        amount
-    }
+
+    let events = borrow_global_mut<FungibleAssetWalletEvents>(wallet_addr);
+    let metadata = wallet.metadata;
+    event::emit_event(&mut events.withdraw_events, WithdrawEvent { amount });
+
+    FungibleAsset { metadata, amount }
 }
 
@@ -1214,11 +1582,11 @@ Increase the supply of a fungible metadata by minting.
fun increase_supply<T: key>(metadata: &Object<T>, amount: u64) acquires FungibleAssetMetadata {
-    assert!(amount != 0, error::invalid_argument(EZERO_AMOUNT));
+    assert!(amount != 0, error::invalid_argument(EAMOUNT_CANNOT_BE_ZERO));
     let fungible_metadata = borrow_fungible_metadata_mut(metadata);
     if (option::is_some(&fungible_metadata.maximum)) {
         let max = *option::borrow(&fungible_metadata.maximum);
-        assert!(max - fungible_metadata.supply >= amount, error::invalid_argument(ECURRENT_SUPPLY_OVERFLOW))
+        assert!(max - fungible_metadata.supply >= amount, error::invalid_argument(EMAX_SUPPLY_EXCEEDED))
     };
     fungible_metadata.supply = fungible_metadata.supply + amount;
 }
@@ -1245,9 +1613,9 @@ Decrease the supply of a fungible metadata by burning.
 
 
 
fun decrease_supply<T: key>(metadata: &Object<T>, amount: u64) acquires FungibleAssetMetadata {
-    assert!(amount != 0, error::invalid_argument(EZERO_AMOUNT));
+    assert!(amount != 0, error::invalid_argument(EAMOUNT_CANNOT_BE_ZERO));
     let fungible_metadata = borrow_fungible_metadata_mut(metadata);
-    assert!(fungible_metadata.supply >= amount, error::invalid_argument(ECURRENT_SUPPLY_UNDERFLOW));
+    assert!(fungible_metadata.supply >= amount, error::invalid_argument(ESUPPLY_UNDERFLOW));
     fungible_metadata.supply = fungible_metadata.supply - amount;
 }
 
@@ -1256,5 +1624,14 @@ Decrease the supply of a fungible metadata by burning.
+ + +## Specification + + + +
pragma verify=false;
+
+ [move-book]: https://aptos.dev/guides/move-guides/book/SUMMARY diff --git a/aptos-move/framework/aptos-framework/doc/fungible_store.md b/aptos-move/framework/aptos-framework/doc/fungible_store.md deleted file mode 100644 index 365aee7b2ab66..0000000000000 --- a/aptos-move/framework/aptos-framework/doc/fungible_store.md +++ /dev/null @@ -1,520 +0,0 @@ - - - -# Module `0x1::fungible_store` - -This defines a store of FungibleAssetWallet under each account. - - -- [Resource `FungibleAssetStore`](#0x1_fungible_store_FungibleAssetStore) -- [Constants](#@Constants_0) -- [Function `balance`](#0x1_fungible_store_balance) -- [Function `ungated_transfer_allowed`](#0x1_fungible_store_ungated_transfer_allowed) -- [Function `set_ungated_transfer`](#0x1_fungible_store_set_ungated_transfer) -- [Function `withdraw`](#0x1_fungible_store_withdraw) -- [Function `deposit`](#0x1_fungible_store_deposit) -- [Function `transfer`](#0x1_fungible_store_transfer) -- [Function `transfer_with_ref`](#0x1_fungible_store_transfer_with_ref) -- [Function `withdraw_with_ref`](#0x1_fungible_store_withdraw_with_ref) -- [Function `deposit_with_ref`](#0x1_fungible_store_deposit_with_ref) -- [Function `burn`](#0x1_fungible_store_burn) -- [Function `get_account_fungible_asset_object`](#0x1_fungible_store_get_account_fungible_asset_object) -- [Function `create_account_fungible_asset_object`](#0x1_fungible_store_create_account_fungible_asset_object) -- [Specification](#@Specification_1) - - -
use 0x1::create_signer;
-use 0x1::error;
-use 0x1::fungible_asset;
-use 0x1::object;
-use 0x1::option;
-use 0x1::signer;
-use 0x1::smart_table;
-
- - - - - -## Resource `FungibleAssetStore` - -Represents all the fungible asset wallet objects of an onwer keyed by the base metadata objects. - - -
struct FungibleAssetStore has key
-
- - - -
-Fields - - -
-
-index: smart_table::SmartTable<object::Object<fungible_asset::FungibleAssetMetadata>, object::Object<fungible_asset::FungibleAssetWallet>> -
-
- -
-
- - -
- - - -## Constants - - - - -The fungible asset wallet object existence error. - - -
const EFUNGIBLE_ASSET_WALLET_OBJECT: u64 = 1;
-
- - - - - -## Function `balance` - -Check the balance of an account. - - -
public fun balance<T: key>(account: address, metadata: &object::Object<T>): u64
-
- - - -
-Implementation - - -
public fun balance<T: key>(
-    account: address,
-    metadata: &Object<T>
-): u64 acquires FungibleAssetStore {
-    let metadata = fungible_asset::verify(metadata);
-    let afa_opt = get_account_fungible_asset_object(
-        account,
-        &metadata,
-        false
-    );
-    if (option::is_none(&afa_opt)) {
-        return 0
-    };
-    let wallet = option::destroy_some(afa_opt);
-    fungible_asset::balance(&wallet)
-}
-
- - - -
- - - -## Function `ungated_transfer_allowed` - -Return true if account allows ungated transfer. - - -
public fun ungated_transfer_allowed<T: key>(account: address, metadata: &object::Object<T>): bool
-
- - - -
-Implementation - - -
public fun ungated_transfer_allowed<T: key>(
-    account: address,
-    metadata: &Object<T>
-): bool acquires FungibleAssetStore {
-    let metadata = fungible_asset::verify(metadata);
-    let afa_opt = get_account_fungible_asset_object(
-        account,
-        &metadata,
-        false
-    );
-    if (option::is_none(&afa_opt)) {
-        return true
-    };
-    let wallet = option::destroy_some(afa_opt);
-    fungible_asset::ungated_transfer_allowed(&wallet)
-}
-
- - - -
- - - -## Function `set_ungated_transfer` - -Enable/disable the direct transfer. - - -
public fun set_ungated_transfer(ref: &fungible_asset::TransferRef, account: address, allow: bool)
-
- - - -
-Implementation - - -
public fun set_ungated_transfer(
-    ref: &TransferRef,
-    account: address,
-    allow: bool
-) acquires FungibleAssetStore {
-    let metadata = fungible_asset::verify(&fungible_asset::transfer_ref_metadata(ref));
-    let afa_opt = get_account_fungible_asset_object(account, &metadata, !allow);
-    if (option::is_none(&afa_opt)) {
-        return
-    };
-    let wallet = option::destroy_some(afa_opt);
-    fungible_asset::set_ungated_transfer(ref, &wallet, allow);
-    maybe_delete(wallet);
-}
-
- - - -
- - - -## Function `withdraw` - -Withdraw amount of fungible asset from account. - - -
public fun withdraw<T: key>(account: &signer, metadata: &object::Object<T>, amount: u64): fungible_asset::FungibleAsset
-
- - - -
-Implementation - - -
public fun withdraw<T: key>(
-    account: &signer,
-    metadata: &Object<T>,
-    amount: u64
-): FungibleAsset acquires FungibleAssetStore {
-    let metadata = fungible_asset::verify(metadata);
-    let account_address = signer::address_of(account);
-    let wallet = ensure_fungible_asset_wallet(
-        account_address,
-        &metadata,
-        false
-    );
-
-    let fa = fungible_asset::withdraw(account, &wallet, amount);
-    maybe_delete(wallet);
-    fa
-}
-
- - - -
- - - -## Function `deposit` - -Deposit fungible asset to account. - - -
public fun deposit(fa: fungible_asset::FungibleAsset, to: address)
-
- - - -
-Implementation - - -
public fun deposit(
-    fa: FungibleAsset,
-    to: address
-) acquires FungibleAssetStore {
-    let metadata = fungible_asset::metadata_from_asset(&fa);
-    let wallet = ensure_fungible_asset_wallet(
-        to,
-        &metadata,
-        true
-    );
-    fungible_asset::deposit(&wallet, fa);
-}
-
- - - -
- - - -## Function `transfer` - -Transfer amount of fungible asset as the owner. - - -
public fun transfer<T: key>(from: &signer, metadata: &object::Object<T>, amount: u64, to: address)
-
- - - -
-Implementation - - -
public fun transfer<T: key>(
-    from: &signer,
-    metadata: &Object<T>,
-    amount: u64,
-    to: address
-) acquires FungibleAssetStore {
-    let fa = withdraw(from, metadata, amount);
-    deposit(fa, to);
-}
-
- - - -
- - - -## Function `transfer_with_ref` - -Transfer ammount of fungible asset ignoring allow_ungated_transfer with TransferRef. - - -
public fun transfer_with_ref(ref: &fungible_asset::TransferRef, from: address, to: address, amount: u64)
-
- - - -
-Implementation - - -
public fun transfer_with_ref(
-    ref: &TransferRef,
-    from: address,
-    to: address,
-    amount: u64,
-) acquires FungibleAssetStore {
-    let sender_wallet = ensure_fungible_asset_wallet(
-        from,
-        &fungible_asset::transfer_ref_metadata(ref),
-        false
-    );
-    let receiver_wallet = ensure_fungible_asset_wallet(
-        to,
-        &fungible_asset::transfer_ref_metadata(ref),
-        true
-    );
-    fungible_asset::transfer_with_ref(ref, &sender_wallet, &receiver_wallet, amount);
-}
-
- - - -
- - - -## Function `withdraw_with_ref` - -Withdraw ammount of fungible asset ignoring allow_ungated_transfer with TransferRef. - - -
public fun withdraw_with_ref(ref: &fungible_asset::TransferRef, account: address, amount: u64): fungible_asset::FungibleAsset
-
- - - -
-Implementation - - -
public fun withdraw_with_ref(
-    ref: &TransferRef,
-    account: address,
-    amount: u64
-): FungibleAsset acquires FungibleAssetStore {
-    let wallet = ensure_fungible_asset_wallet(
-        account,
-        &fungible_asset::transfer_ref_metadata(ref),
-        false
-    );
-    fungible_asset::withdraw_with_ref(ref, &wallet, amount)
-}
-
- - - -
- - - -## Function `deposit_with_ref` - -Deposit ammount of fungible asset ignoring allow_ungated_transfer with TransferRef. - - -
public fun deposit_with_ref(ref: &fungible_asset::TransferRef, account: address, fa: fungible_asset::FungibleAsset)
-
- - - -
-Implementation - - -
public fun deposit_with_ref(ref: &TransferRef, account: address, fa: FungibleAsset) acquires FungibleAssetStore {
-    let wallet = ensure_fungible_asset_wallet(
-        account,
-        &fungible_asset::transfer_ref_metadata(ref),
-        true
-    );
-    fungible_asset::deposit_with_ref(ref, &wallet, fa);
-}
-
- - - -
- - - -## Function `burn` - -Burn the amount of fungible asset from account with BurnRef. - - -
public fun burn(ref: &fungible_asset::BurnRef, account: address, amount: u64)
-
- - - -
-Implementation - - -
public fun burn(ref: &BurnRef, account: address, amount: u64) acquires FungibleAssetStore {
-    let wallet = ensure_fungible_asset_wallet(
-        account,
-        &fungible_asset::burn_ref_metadata(ref),
-        false
-    );
-    fungible_asset::burn(ref, &wallet, amount);
-    maybe_delete(wallet);
-}
-
- - - -
- - - -## Function `get_account_fungible_asset_object` - -Get the FungibleAssetWallet object of metadata belonging to account. -if create_on_demand is true, an default FungibleAssetWallet will be created if not exist; otherwise abort. - - -
fun get_account_fungible_asset_object(account: address, metadata: &object::Object<fungible_asset::FungibleAssetMetadata>, create_on_demand: bool): option::Option<object::Object<fungible_asset::FungibleAssetWallet>>
-
- - - -
-Implementation - - -
fun get_account_fungible_asset_object(
-    account: address,
-    metadata: &Object<FungibleAssetMetadata>,
-    create_on_demand: bool
-): Option<Object<FungibleAssetWallet>> acquires FungibleAssetStore {
-    ensure_fungible_asset_store(account);
-    let metadata = fungible_asset::verify(metadata);
-    let index_table = &mut borrow_global_mut<FungibleAssetStore>(account).index;
-    if (!smart_table::contains(index_table, copy metadata)) {
-        if (create_on_demand) {
-            let afa_obj = create_account_fungible_asset_object(account, &metadata);
-            smart_table::add(index_table, copy metadata, afa_obj);
-        } else {
-            return option::none()
-        }
-    };
-    let wallet = *smart_table::borrow(index_table, metadata);
-    option::some(wallet)
-}
-
- - - -
- - - -## Function `create_account_fungible_asset_object` - -Create a default FungibleAssetWallet object with zero balance of metadata. - - -
fun create_account_fungible_asset_object(account: address, metadata: &object::Object<fungible_asset::FungibleAssetMetadata>): object::Object<fungible_asset::FungibleAssetWallet>
-
- - - -
-Implementation - - -
fun create_account_fungible_asset_object(
-    account: address,
-    metadata: &Object<FungibleAssetMetadata>
-): Object<FungibleAssetWallet> {
-    // Must review carefully here.
-    let asset_signer = create_signer::create_signer(object::object_address(metadata));
-    let creator_ref = object::create_object_from_object(&asset_signer);
-    let wallet = fungible_asset::new_fungible_asset_wallet_object(&creator_ref, metadata);
-    // Transfer the owner to `account`.
-    object::transfer(&asset_signer, wallet, account);
-    // Disable transfer of coin object so the object itself never gets transfered.
-    let transfer_ref = object::generate_transfer_ref(&creator_ref);
-    object::disable_ungated_transfer(&transfer_ref);
-    wallet
-}
-
- - - -
- - - -## Specification - - - -
pragma verify = false;
-
- - -[move-book]: https://aptos.dev/guides/move-guides/book/SUMMARY diff --git a/aptos-move/framework/aptos-framework/doc/managed_fungible_metadata.md b/aptos-move/framework/aptos-framework/doc/managed_fungible_metadata.md deleted file mode 100644 index f4f9de9289862..0000000000000 --- a/aptos-move/framework/aptos-framework/doc/managed_fungible_metadata.md +++ /dev/null @@ -1,549 +0,0 @@ - - - -# Module `0x1::managed_fungible_metadata` - -This module provides an addtional ready-to-use solution on top of FungibleAssetMetadata that manages the refs of -mint, burn and transfer for the creator in a straightforward scheme. It offers creators to destory any refs in an -on-demand manner too. - - -- [Resource `ManagingRefs`](#0x1_managed_fungible_metadata_ManagingRefs) -- [Constants](#@Constants_0) -- [Function `init_managing_refs`](#0x1_managed_fungible_metadata_init_managing_refs) -- [Function `mint`](#0x1_managed_fungible_metadata_mint) -- [Function `withdraw`](#0x1_managed_fungible_metadata_withdraw) -- [Function `deposit`](#0x1_managed_fungible_metadata_deposit) -- [Function `transfer`](#0x1_managed_fungible_metadata_transfer) -- [Function `burn`](#0x1_managed_fungible_metadata_burn) -- [Function `set_ungated_transfer`](#0x1_managed_fungible_metadata_set_ungated_transfer) -- [Function `can_mint`](#0x1_managed_fungible_metadata_can_mint) -- [Function `can_transfer`](#0x1_managed_fungible_metadata_can_transfer) -- [Function `can_burn`](#0x1_managed_fungible_metadata_can_burn) -- [Function `waive_mint`](#0x1_managed_fungible_metadata_waive_mint) -- [Function `waive_transfer`](#0x1_managed_fungible_metadata_waive_transfer) -- [Function `waive_burn`](#0x1_managed_fungible_metadata_waive_burn) -- [Specification](#@Specification_1) - - -
use 0x1::error;
-use 0x1::fungible_asset;
-use 0x1::fungible_store;
-use 0x1::object;
-use 0x1::option;
-use 0x1::signer;
-use 0x1::string;
-
- - - - - -## Resource `ManagingRefs` - -Hold refs to control the minting, transfer and burning of fungible assets. - - -
struct ManagingRefs has key
-
- - - -
-Fields - - -
-
-mint: option::Option<fungible_asset::MintRef> -
-
- -
-
-transfer: option::Option<fungible_asset::TransferRef> -
-
- -
-
-burn: option::Option<fungible_asset::BurnRef> -
-
- -
-
- - -
- - - -## Constants - - - - -Not the owner. - - -
const ENOT_OWNER: u64 = 4;
-
- - - - - -BurnRef existence error. - - -
const EBURN_REF: u64 = 3;
-
- - - - - -Refs existence errors. - - -
const EMANAGED_FUNGIBLE_ASSET_REFS: u64 = 5;
-
- - - - - -MintRef existence error. - - -
const EMINT_REF: u64 = 1;
-
- - - - - -TransferRef existence error. - - -
const ETRANSFER_REF: u64 = 2;
-
- - - - - -## Function `init_managing_refs` - -Initialize metadata object and store the refs. - - -
public fun init_managing_refs(constructor_ref: &object::ConstructorRef, maximum_supply: u64, name: string::String, symbol: string::String, decimals: u8)
-
- - - -
-Implementation - - -
public fun init_managing_refs(
-    constructor_ref: &ConstructorRef,
-    maximum_supply: u64,
-    name: String,
-    symbol: String,
-    decimals: u8
-) {
-    let (mint_ref, transfer_ref, burn_ref) = fungible_asset::init_metadata(
-        constructor_ref,
-        maximum_supply,
-        name,
-        symbol,
-        decimals
-    );
-    let metadata_object_signer = object::generate_signer(constructor_ref);
-    move_to(
-        &metadata_object_signer,
-        ManagingRefs {
-            mint: option::some(mint_ref), transfer: option::some(transfer_ref), burn: option::some(burn_ref)
-        }
-    )
-}
-
- - - -
- - - -## Function `mint` - -Mint as the owner of metadata object. - - -
public fun mint<T: key>(metadata_owner: &signer, metadata: &object::Object<T>, amount: u64, to: address)
-
- - - -
-Implementation - - -
public fun mint<T: key>(
-    metadata_owner: &signer,
-    metadata: &Object<T>,
-    amount: u64,
-    to: address
-) acquires ManagingRefs {
-    assert_owner(metadata_owner, metadata);
-    let mint_ref = borrow_mint_from_refs(metadata);
-    let fa = fungible_asset::mint(mint_ref, amount);
-    fungible_store::deposit(fa, to);
-}
-
- - - -
- - - -## Function `withdraw` - -Withdraw as the owner of metadata object ignoring allow_ungated_transfer field. - - -
public fun withdraw<T: key>(metadata_owner: &signer, metadata: &object::Object<T>, amount: u64, from: address): fungible_asset::FungibleAsset
-
- - - -
-Implementation - - -
public fun withdraw<T: key>(
-    metadata_owner: &signer,
-    metadata: &Object<T>,
-    amount: u64,
-    from: address,
-): FungibleAsset acquires ManagingRefs {
-    assert_owner(metadata_owner, metadata);
-    let transfer_ref = borrow_transfer_from_refs(metadata);
-    fungible_store::withdraw_with_ref(transfer_ref, from, amount)
-}
-
- - - -
- - - -## Function `deposit` - -Deposit as the owner of metadata object ignoring allow_ungated_transfer field. - - -
public fun deposit<T: key>(metadata_owner: &signer, metadata: &object::Object<T>, to: address, fa: fungible_asset::FungibleAsset)
-
- - - -
-Implementation - - -
public fun deposit<T: key>(
-    metadata_owner: &signer,
-    metadata: &Object<T>,
-    to: address,
-    fa: FungibleAsset
-) acquires ManagingRefs {
-    assert_owner(metadata_owner, metadata);
-    let transfer_ref = borrow_transfer_from_refs(metadata);
-    fungible_store::deposit_with_ref(transfer_ref, to, fa);
-}
-
- - - -
- - - -## Function `transfer` - -Transfer as the owner of metadata object ignoring allow_ungated_transfer field. - - -
public fun transfer<T: key>(metadata_owner: &signer, metadata: &object::Object<T>, from: address, to: address, amount: u64)
-
- - - -
-Implementation - - -
public fun transfer<T: key>(
-    metadata_owner: &signer,
-    metadata: &Object<T>,
-    from: address,
-    to: address,
-    amount: u64,
-) acquires ManagingRefs {
-    assert_owner(metadata_owner, metadata);
-    let transfer_ref = borrow_transfer_from_refs(metadata);
-    fungible_store::transfer_with_ref(transfer_ref, from, to, amount);
-}
-
- - - -
- - - -## Function `burn` - -Burn fungible assets as the owner of metadata object. - - -
public fun burn<T: key>(metadata_owner: &signer, metadata: &object::Object<T>, from: address, amount: u64)
-
- - - -
-Implementation - - -
public fun burn<T: key>(
-    metadata_owner: &signer,
-    metadata: &Object<T>,
-    from: address,
-    amount: u64
-) acquires ManagingRefs {
-    assert_owner(metadata_owner, metadata);
-    let burn_ref = borrow_burn_from_refs(metadata);
-    fungible_store::burn(burn_ref, from, amount);
-}
-
- - - -
- - - -## Function `set_ungated_transfer` - -Set the allow_ungated_transfer field in AccountFungibleAsset associated with metadata of account as the -owner of metadata object. - - -
public fun set_ungated_transfer<T: key>(metadata_owner: &signer, metadata: &object::Object<T>, account: address, allow: bool)
-
- - - -
-Implementation - - -
public fun set_ungated_transfer<T: key>(
-    metadata_owner: &signer,
-    metadata: &Object<T>,
-    account: address,
-    allow: bool
-) acquires ManagingRefs {
-    assert_owner(metadata_owner, metadata);
-    let transfer_ref = borrow_transfer_from_refs(metadata);
-    fungible_store::set_ungated_transfer(transfer_ref, account, allow);
-}
-
- - - -
- - - -## Function `can_mint` - -Return if the owner of metadata has access to MintRef. - - -
public fun can_mint<T: key>(metadata: &object::Object<T>): bool
-
- - - -
-Implementation - - -
public fun can_mint<T: key>(metadata: &Object<T>): bool acquires ManagingRefs {
-    option::is_some(&borrow_refs(metadata).mint)
-}
-
- - - -
- - - -## Function `can_transfer` - -Return if the owner of metadata has access to TransferRef. - - -
public fun can_transfer<T: key>(metadata: &object::Object<T>): bool
-
- - - -
-Implementation - - -
public fun can_transfer<T: key>(metadata: &Object<T>): bool acquires ManagingRefs {
-    option::is_some(&borrow_refs(metadata).transfer)
-}
-
- - - -
- - - -## Function `can_burn` - -Return if the owner of metadata has access to BurnRef. - - -
public fun can_burn<T: key>(metadata: &object::Object<T>): bool
-
- - - -
-Implementation - - -
public fun can_burn<T: key>(metadata: &Object<T>): bool acquires ManagingRefs {
-    option::is_some(&borrow_refs(metadata).burn)
-}
-
- - - -
- - - -## Function `waive_mint` - -Let metadata owner to explicitly waive the mint capability. - - -
public fun waive_mint<T: key>(metadata_owner: &signer, metadata: &object::Object<T>)
-
- - - -
-Implementation - - -
public fun waive_mint<T: key>(
-    metadata_owner: &signer,
-    metadata: &Object<T>
-) acquires ManagingRefs {
-    let mint_ref = &mut borrow_refs_mut(metadata_owner, metadata).mint;
-    assert!(option::is_some(mint_ref), error::not_found(EMINT_REF));
-    option::extract(mint_ref);
-}
-
- - - -
- - - -## Function `waive_transfer` - -Let metadata owner to explicitly waive the transfer capability. - - -
public fun waive_transfer<T: key>(metadata_owner: &signer, metadata: &object::Object<T>)
-
- - - -
-Implementation - - -
public fun waive_transfer<T: key>(
-    metadata_owner: &signer,
-    metadata: &Object<T>
-) acquires ManagingRefs {
-    let transfer_ref = &mut borrow_refs_mut(metadata_owner, metadata).transfer;
-    assert!(option::is_some(transfer_ref), error::not_found(ETRANSFER_REF));
-    option::extract(transfer_ref);
-}
-
- - - -
- - - -## Function `waive_burn` - -Let metadata owner to explicitly waive the burn capability. - - -
public fun waive_burn<T: key>(metadata_owner: &signer, metadata: &object::Object<T>)
-
- - - -
-Implementation - - -
public fun waive_burn<T: key>(
-    metadata_owner: &signer,
-    metadata: &Object<T>
-) acquires ManagingRefs {
-    let burn_ref = &mut borrow_refs_mut(metadata_owner, metadata).burn;
-    assert!(option::is_some(burn_ref), error::not_found(ETRANSFER_REF));
-    option::extract(burn_ref);
-}
-
- - - -
- - - -## Specification - - - -
pragma verify = false;
-
- - -[move-book]: https://aptos.dev/guides/move-guides/book/SUMMARY diff --git a/aptos-move/framework/aptos-framework/doc/multisig_account.md b/aptos-move/framework/aptos-framework/doc/multisig_account.md index 3d262fb195d06..f418306e79a1b 100644 --- a/aptos-move/framework/aptos-framework/doc/multisig_account.md +++ b/aptos-move/framework/aptos-framework/doc/multisig_account.md @@ -731,16 +731,6 @@ Event emitted when a transaction's metadata is updated. ## Constants - - -Account executing this operation is not an owner of the multisig account. - - -
const ENOT_OWNER: u64 = 2003;
-
- - - The salt used to create a resource account during multisig account creation. @@ -853,6 +843,16 @@ Transaction has not received enough rejections to be officially rejected. + + +Account executing this operation is not an owner of the multisig account. + + +
const ENOT_OWNER: u64 = 2003;
+
+ + + The number of metadata keys and values don't match. diff --git a/aptos-move/framework/aptos-framework/doc/object.md b/aptos-move/framework/aptos-framework/doc/object.md index bdeb28fdbb39b..47b86220ba7ae 100644 --- a/aptos-move/framework/aptos-framework/doc/object.md +++ b/aptos-move/framework/aptos-framework/doc/object.md @@ -29,13 +29,17 @@ make it so that a reference to a global object can be returned from a function. - [Struct `ExtendRef`](#0x1_object_ExtendRef) - [Struct `TransferRef`](#0x1_object_TransferRef) - [Struct `LinearTransferRef`](#0x1_object_LinearTransferRef) +- [Struct `DeriveRef`](#0x1_object_DeriveRef) - [Struct `TransferEvent`](#0x1_object_TransferEvent) - [Constants](#@Constants_0) - [Function `address_to_object`](#0x1_object_address_to_object) - [Function `create_object_address`](#0x1_object_create_object_address) +- [Function `create_derived_object_address`](#0x1_object_create_derived_object_address) - [Function `exists_at`](#0x1_object_exists_at) - [Function `object_address`](#0x1_object_object_address) +- [Function `convert`](#0x1_object_convert) - [Function `create_named_object`](#0x1_object_create_named_object) +- [Function `create_derived_object`](#0x1_object_create_derived_object) - [Function `create_object_from_account`](#0x1_object_create_object_from_account) - [Function `create_object_from_object`](#0x1_object_create_object_from_object) - [Function `create_object_from_guid`](#0x1_object_create_object_from_guid) @@ -43,6 +47,7 @@ make it so that a reference to a global object can be returned from a function. - [Function `generate_delete_ref`](#0x1_object_generate_delete_ref) - [Function `generate_extend_ref`](#0x1_object_generate_extend_ref) - [Function `generate_transfer_ref`](#0x1_object_generate_transfer_ref) +- [Function `generate_derive_ref`](#0x1_object_generate_derive_ref) - [Function `generate_signer`](#0x1_object_generate_signer) - [Function `address_from_constructor_ref`](#0x1_object_address_from_constructor_ref) - [Function `object_from_constructor_ref`](#0x1_object_object_from_constructor_ref) @@ -342,6 +347,34 @@ the current owner. + + + + +## Struct `DeriveRef` + +Used to create derived objects from a given objects. + + +
struct DeriveRef has drop, store
+
+ + + +
+Fields + + +
+
+self: address +
+
+ +
+
+ +
@@ -480,6 +513,23 @@ nesting, but any checks such as transfer will only be evaluated this deep. + + +Scheme identifier used to generate an object's address obj_addr as derived from another object. +The object's address is generated as: +``` +obj_addr = sha3_256(account addr | derived from object's address | 0xFC) +``` + +This 0xFC constant serves as a domain separation tag to prevent existing authentication key and resource account +derivation to produce an object address. + + +
const OBJECT_DERIVED_SCHEME: u8 = 252;
+
+ + + Scheme identifier used to generate an object's address obj_addr via a fresh GUID generated by the creator at @@ -565,6 +615,34 @@ Derives an object address from source material: sha3_256([creator address | seed + + + + +## Function `create_derived_object_address` + +Derives an object address from the source address and an object: sha3_256([source | object addr | 0xFC]). + + +
public fun create_derived_object_address(source: address, derive_from: address): address
+
+ + + +
+Implementation + + +
public fun create_derived_object_address(source: address, derive_from: address): address {
+    let bytes = bcs::to_bytes(&source);
+    vector::append(&mut bytes, bcs::to_bytes(&derive_from));
+    vector::push_back(&mut bytes, OBJECT_DERIVED_SCHEME);
+    from_bcs::to_address(hash::sha3_256(bytes))
+}
+
+ + +
@@ -612,6 +690,31 @@ Returns the address of within an ObjectId. + + + + +## Function `convert` + +Convert Object to Object. + + +
public fun convert<X: key, Y: key>(object: object::Object<X>): object::Object<Y>
+
+ + + +
+Implementation + + +
public fun convert<X: key, Y: key>(object: Object<X>): Object<Y> {
+    address_to_object<Y>(object.inner)
+}
+
+ + +
@@ -640,6 +743,34 @@ by knowing the user generated seed used to create them. Named objects cannot be + + + + +## Function `create_derived_object` + +Create a new object whose address is derived based on the creator account address and another object. +Derivde objects, similar to named objects, cannot be deleted. + + +
public fun create_derived_object(creator: &signer, derive_ref: &object::DeriveRef): object::ConstructorRef
+
+ + + +
+Implementation + + +
public fun create_derived_object(creator: &signer, derive_ref: &DeriveRef): ConstructorRef {
+    let creator_address = signer::address_of(creator);
+    let obj_addr = create_derived_object_address(creator_address, derive_ref.self);
+    create_object_internal(creator_address, obj_addr, false)
+}
+
+ + +
@@ -838,6 +969,31 @@ Generates the TransferRef, which can be used to manage object transfers. + + + + +## Function `generate_derive_ref` + +Generates the DeriveRef, which can be used to create determnistic derived objects from the current object. + + +
public fun generate_derive_ref(ref: &object::ConstructorRef): object::DeriveRef
+
+ + + +
+Implementation + + +
public fun generate_derive_ref(ref: &ConstructorRef): DeriveRef {
+    DeriveRef { self: ref.self }
+}
+
+ + +
diff --git a/aptos-move/framework/aptos-framework/doc/overview.md b/aptos-move/framework/aptos-framework/doc/overview.md index df92f4ce727ac..d3a35aee771e2 100644 --- a/aptos-move/framework/aptos-framework/doc/overview.md +++ b/aptos-move/framework/aptos-framework/doc/overview.md @@ -28,16 +28,15 @@ This is the reference documentation of the Aptos framework. - [`0x1::delegation_pool`](delegation_pool.md#0x1_delegation_pool) - [`0x1::event`](event.md#0x1_event) - [`0x1::fungible_asset`](fungible_asset.md#0x1_fungible_asset) -- [`0x1::fungible_store`](fungible_store.md#0x1_fungible_store) - [`0x1::gas_schedule`](gas_schedule.md#0x1_gas_schedule) - [`0x1::genesis`](genesis.md#0x1_genesis) - [`0x1::governance_proposal`](governance_proposal.md#0x1_governance_proposal) - [`0x1::guid`](guid.md#0x1_guid) - [`0x1::managed_coin`](managed_coin.md#0x1_managed_coin) -- [`0x1::managed_fungible_metadata`](managed_fungible_metadata.md#0x1_managed_fungible_metadata) - [`0x1::multisig_account`](multisig_account.md#0x1_multisig_account) - [`0x1::object`](object.md#0x1_object) - [`0x1::optional_aggregator`](optional_aggregator.md#0x1_optional_aggregator) +- [`0x1::primary_wallet`](primary_wallet.md#0x1_primary_wallet) - [`0x1::reconfiguration`](reconfiguration.md#0x1_reconfiguration) - [`0x1::resource_account`](resource_account.md#0x1_resource_account) - [`0x1::stake`](stake.md#0x1_stake) diff --git a/aptos-move/framework/aptos-framework/doc/primary_wallet.md b/aptos-move/framework/aptos-framework/doc/primary_wallet.md new file mode 100644 index 0000000000000..a11c3f5266800 --- /dev/null +++ b/aptos-move/framework/aptos-framework/doc/primary_wallet.md @@ -0,0 +1,381 @@ + + + +# Module `0x1::primary_wallet` + +This defines the module for interacting with primary wallets of accounts/objects, which have deterministic addresses + + +- [Resource `PrimaryWalletSupport`](#0x1_primary_wallet_PrimaryWalletSupport) +- [Function `enable_primary_wallet`](#0x1_primary_wallet_enable_primary_wallet) +- [Function `ensure_primary_wallet_exists`](#0x1_primary_wallet_ensure_primary_wallet_exists) +- [Function `create_primary_wallet`](#0x1_primary_wallet_create_primary_wallet) +- [Function `primary_wallet_address`](#0x1_primary_wallet_primary_wallet_address) +- [Function `primary_wallet`](#0x1_primary_wallet_primary_wallet) +- [Function `primary_wallet_exists`](#0x1_primary_wallet_primary_wallet_exists) +- [Function `balance`](#0x1_primary_wallet_balance) +- [Function `ungated_transfer_allowed`](#0x1_primary_wallet_ungated_transfer_allowed) +- [Function `withdraw`](#0x1_primary_wallet_withdraw) +- [Function `deposit`](#0x1_primary_wallet_deposit) +- [Function `transfer`](#0x1_primary_wallet_transfer) +- [Specification](#@Specification_0) + + +
use 0x1::create_signer;
+use 0x1::fungible_asset;
+use 0x1::object;
+use 0x1::signer;
+
+ + + + + +## Resource `PrimaryWalletSupport` + +Resource stored on the fungible asset metadata object to allow creating primary wallets for it. + + +
struct PrimaryWalletSupport has key
+
+ + + +
+Fields + + +
+
+metadata_derive_ref: object::DeriveRef +
+
+ +
+
+ + +
+ + + +## Function `enable_primary_wallet` + +Creators of fungible assets can call this to enable support for creating primary (deterministic) wallets for +their users. + + +
public fun enable_primary_wallet(metadata_constructor_ref: &object::ConstructorRef)
+
+ + + +
+Implementation + + +
public fun enable_primary_wallet(metadata_constructor_ref: &ConstructorRef) {
+    // Ensure that this is a fungible asset metadata object.
+    object::object_from_constructor_ref<FungibleAssetMetadata>(metadata_constructor_ref);
+    let metadata_obj = &object::generate_signer(metadata_constructor_ref);
+    move_to(metadata_obj, PrimaryWalletSupport {
+        metadata_derive_ref: object::generate_derive_ref(metadata_constructor_ref),
+    });
+}
+
+ + + +
+ + + +## Function `ensure_primary_wallet_exists` + + + +
public fun ensure_primary_wallet_exists<T: key>(owner: address, metadata: object::Object<T>): object::Object<fungible_asset::FungibleAssetWallet>
+
+ + + +
+Implementation + + +
public fun ensure_primary_wallet_exists<T: key>(
+    owner: address,
+    metadata: Object<T>,
+): Object<FungibleAssetWallet> acquires PrimaryWalletSupport {
+    if (!primary_wallet_exists(owner, metadata)) {
+        create_primary_wallet(owner, metadata);
+    };
+    primary_wallet(owner, metadata)
+}
+
+ + + +
+ + + +## Function `create_primary_wallet` + +Create a primary wallet object to hold fungible asset for the given address. + + +
public fun create_primary_wallet<T: key>(owner_addr: address, metadata: object::Object<T>): object::Object<fungible_asset::FungibleAssetWallet>
+
+ + + +
+Implementation + + +
public fun create_primary_wallet<T: key>(
+    owner_addr: address,
+    metadata: Object<T>,
+): Object<FungibleAssetWallet> acquires PrimaryWalletSupport {
+    let owner = &create_signer::create_signer(owner_addr);
+    let metadata_addr = object::object_address(&metadata);
+    let derive_ref = &borrow_global<PrimaryWalletSupport>(metadata_addr).metadata_derive_ref;
+    let constructor_ref = &object::create_derived_object(owner, derive_ref);
+
+    // Disable ungated transfer as deterministic wallets shouldn't be transferrable.
+    let transfer_ref = &object::generate_transfer_ref(constructor_ref);
+    object::disable_ungated_transfer(transfer_ref);
+
+    fungible_asset::create_wallet(constructor_ref, metadata)
+}
+
+ + + +
+ + + +## Function `primary_wallet_address` + + + +
public fun primary_wallet_address<T: key>(owner: address, metadata: object::Object<T>): address
+
+ + + +
+Implementation + + +
public fun primary_wallet_address<T: key>(owner: address, metadata: Object<T>): address {
+    let metadata_addr = object::object_address(&metadata);
+    object::create_derived_object_address(owner, metadata_addr)
+}
+
+ + + +
+ + + +## Function `primary_wallet` + + + +
public fun primary_wallet<T: key>(owner: address, metadata: object::Object<T>): object::Object<fungible_asset::FungibleAssetWallet>
+
+ + + +
+Implementation + + +
public fun primary_wallet<T: key>(owner: address, metadata: Object<T>): Object<FungibleAssetWallet> {
+    let wallet = primary_wallet_address(owner, metadata);
+    object::address_to_object<FungibleAssetWallet>(wallet)
+}
+
+ + + +
+ + + +## Function `primary_wallet_exists` + + + +
public fun primary_wallet_exists<T: key>(account: address, metadata: object::Object<T>): bool
+
+ + + +
+Implementation + + +
public fun primary_wallet_exists<T: key>(account: address, metadata: Object<T>): bool {
+    fungible_asset::wallet_exists(primary_wallet_address(account, metadata))
+}
+
+ + + +
+ + + +## Function `balance` + +Get the balance of account's primary wallet. + + +
public fun balance<T: key>(account: address, metadata: object::Object<T>): u64
+
+ + + +
+Implementation + + +
public fun balance<T: key>(account: address, metadata: Object<T>): u64 {
+    if (primary_wallet_exists(account, metadata)) {
+        fungible_asset::balance(primary_wallet(account, metadata))
+    } else {
+        0
+    }
+}
+
+ + + +
+ + + +## Function `ungated_transfer_allowed` + +Return whether the given account's primary wallet can do direct transfers. + + +
public fun ungated_transfer_allowed<T: key>(account: address, metadata: object::Object<T>): bool
+
+ + + +
+Implementation + + +
public fun ungated_transfer_allowed<T: key>(account: address, metadata: Object<T>): bool {
+    fungible_asset::ungated_transfer_allowed(primary_wallet(account, metadata))
+}
+
+ + + +
+ + + +## Function `withdraw` + +Withdraw amount of fungible asset from wallet by the owner. + + +
public fun withdraw<T: key>(owner: &signer, metadata: object::Object<T>, amount: u64): fungible_asset::FungibleAsset
+
+ + + +
+Implementation + + +
public fun withdraw<T: key>(owner: &signer, metadata: Object<T>, amount: u64): FungibleAsset {
+    let wallet = primary_wallet(signer::address_of(owner), metadata);
+    fungible_asset::withdraw(owner, wallet, amount)
+}
+
+ + + +
+ + + +## Function `deposit` + +Deposit amount of fungible asset to the given account's primary wallet. + + +
public fun deposit(owner: address, fa: fungible_asset::FungibleAsset)
+
+ + + +
+Implementation + + +
public fun deposit(owner: address, fa: FungibleAsset) acquires PrimaryWalletSupport {
+    let metadata = fungible_asset::asset_metadata(&fa);
+    let wallet = ensure_primary_wallet_exists(owner, metadata);
+    fungible_asset::deposit(wallet, fa);
+}
+
+ + + +
+ + + +## Function `transfer` + +Transfer amount of fungible asset from sender's primary wallet to receiver's primary wallet. + + +
public entry fun transfer<T: key>(sender: &signer, metadata: object::Object<T>, recipient: address, amount: u64)
+
+ + + +
+Implementation + + +
public entry fun transfer<T: key>(
+    sender: &signer,
+    metadata: Object<T>,
+    recipient: address,
+    amount: u64,
+) acquires PrimaryWalletSupport {
+    let sender_wallet = ensure_primary_wallet_exists(signer::address_of(sender), metadata);
+    let recipient_wallet = ensure_primary_wallet_exists(recipient, metadata);
+    fungible_asset::transfer(sender, sender_wallet, recipient_wallet, amount);
+}
+
+ + + +
+ + + +## Specification + + + +
pragma verify=false;
+
+ + +[move-book]: https://aptos.dev/guides/move-guides/book/SUMMARY diff --git a/aptos-move/framework/aptos-framework/doc/storage_gas.md b/aptos-move/framework/aptos-framework/doc/storage_gas.md index f58b85130ea37..b4df40c4471d9 100644 --- a/aptos-move/framework/aptos-framework/doc/storage_gas.md +++ b/aptos-move/framework/aptos-framework/doc/storage_gas.md @@ -1442,6 +1442,7 @@ A non decreasing curve must ensure that next is greater than cur.
pragma aborts_if_is_strict = false;
+pragma verify = false;
 pragma opaque;
 include ValidatePointsAbortsIf;
 
diff --git a/aptos-move/framework/aptos-framework/sources/create_signer.move b/aptos-move/framework/aptos-framework/sources/create_signer.move index 2428b9a44638b..3ae2ea6c99e75 100644 --- a/aptos-move/framework/aptos-framework/sources/create_signer.move +++ b/aptos-move/framework/aptos-framework/sources/create_signer.move @@ -11,10 +11,10 @@ module aptos_framework::create_signer { friend aptos_framework::account; friend aptos_framework::aptos_account; - friend aptos_framework::fungible_store; friend aptos_framework::genesis; friend aptos_framework::multisig_account; friend aptos_framework::object; + friend aptos_framework::primary_wallet; public(friend) native fun create_signer(addr: address): signer; } diff --git a/aptos-move/framework/aptos-framework/sources/fungible_asset.move b/aptos-move/framework/aptos-framework/sources/fungible_asset.move index 2c4619516caa5..9e96669b6f1e2 100644 --- a/aptos-move/framework/aptos-framework/sources/fungible_asset.move +++ b/aptos-move/framework/aptos-framework/sources/fungible_asset.move @@ -1,39 +1,43 @@ /// This defines the fungible asset module that can issue fungible asset of any `FungibleAssetMetadata` object. The /// metadata object can be any object that equipped with `FungibleAssetMetadata` resource. module aptos_framework::fungible_asset { - use aptos_framework::object::{Self, Object, DeleteRef, ConstructorRef}; + use aptos_framework::event; + use aptos_framework::object::{Self, Object, ConstructorRef}; + use std::error; use std::option::{Self, Option}; - use std::string::String; use std::signer; - - friend aptos_framework::fungible_store; + use std::string::String; /// Amount cannot be zero. const EAMOUNT_CANNOT_BE_ZERO: u64 = 1; - /// The transfer ref and the wallet do not match. - const ETRANSFER_REF_AND_WALLET_MISMATCH: u64 = 2; - /// The burn ref and the the wallet do not match. - const EBURN_REF_AND_WALLET_MISMATCH: u64 = 3; - /// The token account is still allow_ungated_transfer so cannot be deleted. - const EUNGATED_TRANSFER_IS_NOT_ALLOWED: u64 = 4; - /// Insufficient amount. - const EINSUFFICIENT_BALANCE: u64 = 5; - /// FungibleAsset type and the wallet type mismatch. - const EFUNGIBLE_ASSET_AND_WALLET_MISMATCH: u64 = 6; - /// Amount cannot be zero. - const EZERO_AMOUNT: u64 = 7; - /// Current supply overflow - const ECURRENT_SUPPLY_OVERFLOW: u64 = 8; - /// Current supply underflow - const ECURRENT_SUPPLY_UNDERFLOW: u64 = 9; - /// Not the owner, - const ENOT_OWNER: u64 = 10; + /// The transfer ref and the fungible asset do not match. + const ETRANSFER_REF_AND_FUNGIBLE_ASSET_MISMATCH: u64 = 2; + /// Account cannot transfer or receive fungible assets. + const EUNGATED_TRANSFER_IS_NOT_ALLOWED: u64 = 3; + /// Insufficient balance to withdraw or transfer. + const EINSUFFICIENT_BALANCE: u64 = 4; + /// The fungible asset's supply has exceeded maximum. + const EMAX_SUPPLY_EXCEEDED: u64 = 5; + /// More tokens than remaining supply are being burnt. + const ESUPPLY_UNDERFLOW: u64 = 6; + /// The mint ref and the the wallet do not match. + const EMINT_REF_AND_WALLET_MISMATCH: u64 = 7; + /// Account is not the wallet's owner. + const ENOT_WALLET_OWNER: u64 = 8; + /// Transfer ref and wallet do not match. + const ETRANSFER_REF_AND_WALLET_MISMATCH: u64 = 9; + /// Burn ref and wallet do not match. + const EBURN_REF_AND_WALLET_MISMATCH: u64 = 10; + /// Fungible asset and wallet do not match. + const EFUNGIBLE_ASSET_AND_WALLET_MISMATCH: u64 = 11; + /// Cannot destroy non-empty fungible assets. + const EAMOUNT_IS_NOT_ZERO: u64 = 12; #[resource_group_member(group = aptos_framework::object::ObjectGroup)] /// Define the metadata required of an metadata to be fungible. struct FungibleAssetMetadata has key { - /// The current supply. + /// The current supply of the fungible asset. supply: u64, /// The maximum supply limit where `option::none()` means no limit. maximum: Option, @@ -42,63 +46,84 @@ module aptos_framework::fungible_asset { /// Symbol of the fungible metadata, usually a shorter version of the name. /// For example, Singapore Dollar is SGD. symbol: String, - /// Number of decimals used to get its user representation. + /// Number of decimals used for display purposes. /// For example, if `decimals` equals `2`, a balance of `505` coins should /// be displayed to a user as `5.05` (`505 / 10 ** 2`). decimals: u8, } #[resource_group_member(group = aptos_framework::object::ObjectGroup)] - /// The resource of an object holding the properties of fungible assets. + /// The wallet object that holds fungible assets of a specific type associated with an account. struct FungibleAssetWallet has key { /// The address of the base metadata object. metadata: Object, /// The balance of the fungible metadata. balance: u64, - /// Fungible Assets transferring is a common operation, this allows for disabling and enabling - /// transfers bypassing the use of a TransferRef. + /// Fungible Assets transferring is a common operation, this allows for freezing/unfreezing accounts. allow_ungated_transfer: bool, - /// The delete_ref of this object, used for cleanup. - delete_ref: DeleteRef } - /// The transferable version of fungible metadata. - /// Note: it does not have `store` ability, only used in hot potato pattern. + #[resource_group_member(group = aptos_framework::object::ObjectGroup)] + struct FungibleAssetWalletEvents has key { + deposit_events: event::EventHandle, + withdraw_events: event::EventHandle, + set_ungated_transfer_events: event::EventHandle, + } + + /// FungibleAsset can be passed into function for type safety and to guarantee a specific amount. + /// FungibleAsset cannot be stored directly and will have to be deposited back into a wallet. struct FungibleAsset { metadata: Object, amount: u64, } - /// Ref to mint. + /// MintRef can be used to mint the fungible asset into an account's wallet. struct MintRef has drop, store { metadata: Object } - /// Ref to control the transfer. + /// TransferRef can be used to allow or disallow the owner of fungible assets from transferring the asset + /// and allow the holder of TransferRef to transfer fungible assets from any account. struct TransferRef has drop, store { metadata: Object } - /// Ref to burn.. + /// BurnRef can be used to burn fungible assets from a given holder account. struct BurnRef has drop, store { metadata: Object } - /// The initialization of an object with `FungibleAssetMetadata`. - public fun init_metadata( + /// Emitted when fungible assets are deposited into a wallet. + struct DepositEvent has drop, store { + amount: u64, + } + + /// Emitted when fungible assets are withdrawn from a wallet. + struct WithdrawEvent has drop, store { + amount: u64, + } + + /// Emitted when a wallet's ungated (owner) transfer permission is updated. + struct SetUngatedTransferEvent has drop, store { + transfer_allowed: bool, + } + + /// Make an existing object fungible by adding the FungibleAssetMetadata resource. + /// This returns the capabilities to mint, burn, and transfer. + public fun add_fungibility( constructor_ref: &ConstructorRef, maximum_supply: u64, name: String, symbol: String, decimals: u8, - ): (MintRef, TransferRef, BurnRef) { - let metadata_object_signer = object::generate_signer(constructor_ref); + ): Object { + let metadata_object_signer = &object::generate_signer(constructor_ref); let converted_maximum = if (maximum_supply == 0) { option::none() } else { option::some(maximum_supply) }; - move_to(&metadata_object_signer, + move_to(metadata_object_signer, FungibleAssetMetadata { supply: 0, maximum: converted_maximum, @@ -107,57 +132,65 @@ module aptos_framework::fungible_asset { decimals, } ); + object::object_from_constructor_ref(constructor_ref) + } + + /// Creates a mint ref that can be used to mint fungible assets from the given fungible object's constructor ref. + /// This can only be called at object creation time as constructor_ref is only available then. + public fun generate_mint_ref(constructor_ref: &ConstructorRef): MintRef { let metadata = object::object_from_constructor_ref(constructor_ref); - (MintRef { metadata }, TransferRef { metadata }, BurnRef { metadata }) + MintRef { metadata } } + /// Creates a burn ref that can be used to burn fungible assets from the given fungible object's constructor ref. + /// This can only be called at object creation time as constructor_ref is only available then. + public fun generate_burn_ref(constructor_ref: &ConstructorRef): BurnRef { + let metadata = object::object_from_constructor_ref(constructor_ref); + BurnRef { metadata } + } + + /// Creates a transfer ref that can be used to freeze/unfreeze/transfer fungible assets from the given fungible + /// object's constructor ref. + /// This can only be called at object creation time as constructor_ref is only available then. + public fun generate_transfer_ref(constructor_ref: &ConstructorRef): TransferRef { + let metadata = object::object_from_constructor_ref(constructor_ref); + TransferRef { metadata } + } + + #[view] /// Get the current supply from `metadata`. - public fun supply(metadata: &Object): u64 acquires FungibleAssetMetadata { - borrow_fungible_metadata(metadata).supply + public fun supply(metadata: Object): u64 acquires FungibleAssetMetadata { + borrow_fungible_metadata(&metadata).supply } + #[view] /// Get the maximum supply from `metadata`. - public fun maximum(metadata: &Object): Option acquires FungibleAssetMetadata { - borrow_fungible_metadata(metadata).maximum + public fun maximum(metadata: Object): Option acquires FungibleAssetMetadata { + borrow_fungible_metadata(&metadata).maximum } + #[view] /// Get the name of the fungible asset from `metadata`. - public fun name(metadata: &Object): String acquires FungibleAssetMetadata { - borrow_fungible_metadata(metadata).name + public fun name(metadata: Object): String acquires FungibleAssetMetadata { + borrow_fungible_metadata(&metadata).name } + #[view] /// Get the symbol of the fungible asset from `metadata`. - public fun symbol(metadata: &Object): String acquires FungibleAssetMetadata { - borrow_fungible_metadata(metadata).symbol + public fun symbol(metadata: Object): String acquires FungibleAssetMetadata { + borrow_fungible_metadata(&metadata).symbol } + #[view] /// Get the decimals from `metadata`. - public fun decimals(metadata: &Object): u8 acquires FungibleAssetMetadata { - borrow_fungible_metadata(metadata).decimals + public fun decimals(metadata: Object): u8 acquires FungibleAssetMetadata { + borrow_fungible_metadata(&metadata).decimals } - - /// Verify any object is equipped with `FungibleAssetMetadata` and return the object. - public fun verify(metadata: &Object): Object { - let addr = object::object_address(metadata); - object::address_to_object(addr) - } - - /// Create a new wallet object to hold fungible asset. - public(friend) fun new_fungible_asset_wallet_object( - creator_ref: &ConstructorRef, - metadata: &Object, - ): Object { - let wallet_signer = object::generate_signer(creator_ref); - let metadata = verify(metadata); - - move_to(&wallet_signer, FungibleAssetWallet { - metadata, - balance: 0, - allow_ungated_transfer: true, - delete_ref: object::generate_delete_ref(creator_ref) - }); - object::object_from_constructor_ref(creator_ref) + #[view] + /// Return whether the provided address has a wallet initialized. + public fun wallet_exists(wallet: address): bool { + exists(wallet) } /// Return the underlying metadata object @@ -165,260 +198,300 @@ module aptos_framework::fungible_asset { fa.metadata } + #[view] /// Return the underlying metadata object. - public fun metadata_from_wallet( - wallet: &Object - ): Object acquires FungibleAssetWallet { - borrow_fungible_asset(wallet).metadata + public fun wallet_metadata(wallet: Object): Object acquires FungibleAssetWallet { + borrow_wallet_resource(&wallet).metadata } - /// Return `amount` inside. + /// Return `amount` of a given fungible asset. public fun amount(fa: &FungibleAsset): u64 { fa.amount } - /// Destroy `wallet` object. - public(friend) fun destory_fungible_asset_wallet( - wallet: Object - ) acquires FungibleAssetWallet { - let FungibleAssetWallet { - metadata: _, - balance: _, - allow_ungated_transfer: _, - delete_ref - } = move_from(object::object_address(&wallet)); - object::delete(delete_ref); + #[view] + /// Get the balance of a given wallet. + public fun balance(wallet: Object): u64 acquires FungibleAssetWallet { + if (wallet_exists(object::object_address(&wallet))) { + borrow_wallet_resource(&wallet).balance + } else { + 0 + } + } + + #[view] + /// Return whether a wallet can freely send or receive fungible assets. + /// If the wallet has not been created, we default to returning true as deposits can be sent to it. + public fun ungated_transfer_allowed(wallet: Object): bool acquires FungibleAssetWallet { + !wallet_exists(object::object_address(&wallet)) || + borrow_wallet_resource(&wallet).allow_ungated_transfer + } + + public fun asset_metadata(fa: &FungibleAsset): Object { + fa.metadata + } + + /// Get the underlying metadata object from `MintRef`. + public fun mint_ref_metadata(ref: &MintRef): Object { + ref.metadata + } + + /// Get the underlying metadata object from `TransferRef`. + public fun transfer_ref_metadata(ref: &TransferRef): Object { + ref.metadata } - /// Get the balance of `wallet`. - public fun balance(wallet: &Object): u64 acquires FungibleAssetWallet { - borrow_fungible_asset(wallet).balance + /// Get the underlying metadata object from `BurnRef`. + public fun burn_ref_metadata(ref: &BurnRef): Object { + ref.metadata } + /// Transfer `amount` of fungible asset from `from_wallet`, which should be owned by `sender`, to `receiver`. + /// Note: it does not move the underlying object. + public entry fun transfer( + sender: &signer, + from: Object, + to: Object, + amount: u64, + ) acquires FungibleAssetWallet, FungibleAssetWalletEvents { + let fa = withdraw(sender, from, amount); + deposit(to, fa); + } - /// Return `allowed_ungated_transfer`. - public fun ungated_transfer_allowed(wallet: &Object): bool acquires FungibleAssetWallet { - borrow_fungible_asset(wallet).allow_ungated_transfer + /// Allow an object to hold a wallet for fungible assets. + /// Applications can use this to create multiple wallets for isolating fungible assets for different purposes. + public fun create_wallet( + constructor_ref: &ConstructorRef, + metadata: Object, + ): Object { + let wallet_obj = &object::generate_signer(constructor_ref); + let metadata = object::convert(metadata); + move_to(wallet_obj, FungibleAssetWallet { + metadata, + balance: 0, + allow_ungated_transfer: true, + }); + move_to(wallet_obj, + FungibleAssetWalletEvents { + deposit_events: object::new_event_handle(wallet_obj), + withdraw_events: object::new_event_handle(wallet_obj), + set_ungated_transfer_events: object::new_event_handle(wallet_obj), + } + ); + + object::object_from_constructor_ref(constructor_ref) } - /// Mint the `amount` of fungible asset. + /// Withdraw `amount` of fungible asset from `wallet` by the owner. + public fun withdraw( + owner: &signer, + wallet: Object, + amount: u64, + ): FungibleAsset acquires FungibleAssetWallet, FungibleAssetWalletEvents { + assert!(object::owns(wallet, signer::address_of(owner)), error::permission_denied(ENOT_WALLET_OWNER)); + assert!(ungated_transfer_allowed(wallet), error::invalid_argument(EUNGATED_TRANSFER_IS_NOT_ALLOWED)); + withdraw_internal(object::object_address(&wallet), amount) + } + + /// Deposit `amount` of fungible asset to `wallet`. + public fun deposit( + wallet: Object, + fa: FungibleAsset, + ) acquires FungibleAssetWallet, FungibleAssetWalletEvents { + assert!(ungated_transfer_allowed(wallet), error::invalid_argument(EUNGATED_TRANSFER_IS_NOT_ALLOWED)); + deposit_internal(wallet, fa); + } + + /// Mint the specified `amount` of fungible asset. public fun mint( ref: &MintRef, amount: u64, ): FungibleAsset acquires FungibleAssetMetadata { - assert!(amount != 0, error::invalid_argument(EAMOUNT_CANNOT_BE_ZERO)); - let metadata = verify(&ref.metadata); - increase_supply(&ref.metadata, amount); + assert!(amount > 0, error::invalid_argument(EAMOUNT_CANNOT_BE_ZERO)); + let metadata = ref.metadata; + increase_supply(&metadata, amount); + FungibleAsset { metadata, amount } } - /// Enable/disable the direct transfer of fungible asset. - public fun set_ungated_transfer( + /// Mint the specified `amount` of fungible asset to a destination wallet. + public fun mint_to( + ref: &MintRef, + wallet: Object, + amount: u64, + ) acquires FungibleAssetMetadata, FungibleAssetWallet, FungibleAssetWalletEvents { + deposit(wallet, mint(ref, amount)); + } + + /// Enable/disable a wallet's ability to do direct transfers of fungible asset. + public fun set_ungated_transfer( ref: &TransferRef, - wallet: &Object, + wallet: Object, allow: bool, - ) acquires FungibleAssetWallet { + ) acquires FungibleAssetWallet, FungibleAssetWalletEvents { assert!( - &ref.metadata == &metadata_from_wallet(wallet), - error::invalid_argument(ETRANSFER_REF_AND_WALLET_MISMATCH) + ref.metadata == wallet_metadata(wallet), + error::invalid_argument(ETRANSFER_REF_AND_WALLET_MISMATCH), ); - borrow_fungible_asset_mut(wallet).allow_ungated_transfer = allow; + let wallet_addr = object::object_address(&wallet); + borrow_global_mut(wallet_addr).allow_ungated_transfer = allow; + + let events = borrow_global_mut(wallet_addr); + event::emit_event(&mut events.set_ungated_transfer_events, SetUngatedTransferEvent { transfer_allowed: allow }); } - /// Burn the `amount` of fungible metadata from `account`. - public fun burn( + /// Burn the `amount` of fungible metadata from the given wallet. + public fun burn( ref: &BurnRef, - wallet: &Object, + wallet: Object, amount: u64 - ) acquires FungibleAssetWallet, FungibleAssetMetadata { - assert!( - &ref.metadata == &metadata_from_wallet(wallet), - error::invalid_argument(EBURN_REF_AND_WALLET_MISMATCH) - ); + ) acquires FungibleAssetMetadata, FungibleAssetWallet, FungibleAssetWalletEvents { + let metadata = ref.metadata; + assert!(metadata == wallet_metadata(wallet), error::invalid_argument(EBURN_REF_AND_WALLET_MISMATCH)); + let wallet_addr = object::object_address(&wallet); let FungibleAsset { metadata, amount, - } = extract(wallet, amount); + } = withdraw_internal(wallet_addr, amount); decrease_supply(&metadata, amount); } - /// Withdarw `amount` of fungible asset from `wallet` by the owner. - public fun withdraw( - account: &signer, - wallet: &Object, - amount: u64 - ): FungibleAsset acquires FungibleAssetWallet { - assert_owner(account, wallet); - extract(wallet, amount) - } - - /// Deposit `amount` of fungible asset to `wallet`. - public fun deposit( - wallet: &Object, - fa: FungibleAsset, - ) acquires FungibleAssetWallet { - let FungibleAsset { metadata, amount } = fa; - // ensure merging the same coin - let wallet = borrow_fungible_asset_mut(wallet); - assert!(wallet.allow_ungated_transfer, error::invalid_argument(EUNGATED_TRANSFER_IS_NOT_ALLOWED)); - assert!(wallet.metadata == metadata, error::invalid_argument(EFUNGIBLE_ASSET_AND_WALLET_MISMATCH)); - wallet.balance = wallet.balance + amount; - } - - /// Transfer `amount` of fungible metadata of `metadata` to `receiver`. - /// Note: it does not move the underlying object. - public fun transfer( - account: &signer, - amount: u64, - from: &Object, - to: &Object, - ) acquires FungibleAssetWallet { - let fa = withdraw(account, from, amount); - deposit(to, fa); - } - - /// Withdarw `amount` of fungible metadata from `account` ignoring `allow_ungated_transfer`. - public fun withdraw_with_ref( + /// Withdraw `amount` of fungible metadata from `wallet` ignoring `allow_ungated_transfer`. + public fun withdraw_with_ref( ref: &TransferRef, - wallet: &Object, + wallet: Object, amount: u64 - ): FungibleAsset acquires FungibleAssetWallet { + ): FungibleAsset acquires FungibleAssetWallet, FungibleAssetWalletEvents { assert!( - &ref.metadata == &metadata_from_wallet(wallet), - error::invalid_argument(ETRANSFER_REF_AND_WALLET_MISMATCH) + ref.metadata == wallet_metadata(wallet), + error::invalid_argument(ETRANSFER_REF_AND_WALLET_MISMATCH), ); - let ungated_transfer_allowed = ungated_transfer_allowed(wallet); - if (!ungated_transfer_allowed) { - set_ungated_transfer(ref, wallet, true); - }; - let fa = extract(wallet, amount); - if (!ungated_transfer_allowed) { - set_ungated_transfer(ref, wallet, false); - }; - fa + withdraw_internal(object::object_address(&wallet), amount) } - /// Deposit fungible asset into `account` ignoring `allow_ungated_transfer`. - public fun deposit_with_ref( + /// Deposit fungible asset into `wallet` ignoring `allow_ungated_transfer`. + public fun deposit_with_ref( ref: &TransferRef, - wallet: &Object, + wallet: Object, fa: FungibleAsset - ) acquires FungibleAssetWallet { + ) acquires FungibleAssetWallet, FungibleAssetWalletEvents { assert!( - &ref.metadata == &metadata_from_wallet(wallet), - error::invalid_argument(ETRANSFER_REF_AND_WALLET_MISMATCH) + ref.metadata == fa.metadata, + error::invalid_argument(ETRANSFER_REF_AND_FUNGIBLE_ASSET_MISMATCH) ); - let ungated_transfer_allowed = ungated_transfer_allowed(wallet); - if (!ungated_transfer_allowed) { - set_ungated_transfer(ref, wallet, true); - }; - deposit(wallet, fa); - if (!ungated_transfer_allowed) { - set_ungated_transfer(ref, wallet, false); - }; + deposit_internal(wallet, fa); } /// Transfer `ammount` of fungible metadata with `TransferRef` even ungated transfer is disabled. - public fun transfer_with_ref( + public fun transfer_with_ref( transfer_ref: &TransferRef, - from: &Object, - to: &Object, + from: Object, + to: Object, amount: u64, - ) acquires FungibleAssetWallet { + ) acquires FungibleAssetWallet, FungibleAssetWalletEvents { let fa = withdraw_with_ref(transfer_ref, from, amount); deposit_with_ref(transfer_ref, to, fa); } - /// Get the underlying metadata object from `MintRef`. - public fun mint_ref_metadata(ref: &MintRef): Object { - ref.metadata + /// Extract a given amount from the given fungible asset and return a new one. + public fun extract(fungible_asset: &mut FungibleAsset, amount: u64): FungibleAsset { + assert!(fungible_asset.amount >= amount, error::invalid_argument(EINSUFFICIENT_BALANCE)); + fungible_asset.amount = fungible_asset.amount - amount; + FungibleAsset { + metadata: fungible_asset.metadata, + amount, + } } - /// Get the underlying metadata object from `TransferRef`. - public fun transfer_ref_metadata(ref: &TransferRef): Object { - ref.metadata + /// "Merges" the two given fungible assets. The coin passed in as `dst_fungible_asset` will have a value equal + /// to the sum of the two (`dst_fungible_asset` and `src_fungible_asset`). + public fun merge(dst_fungible_asset: &mut FungibleAsset, src_fungible_asset: FungibleAsset) { + let FungibleAsset { metadata: _, amount } = src_fungible_asset; + dst_fungible_asset.amount = dst_fungible_asset.amount + amount; } - /// Get the underlying metadata object from `BurnRef`. - public fun burn_ref_metadata(ref: &BurnRef): Object { - ref.metadata + /// Destroy an empty fungible asset. + public fun destroy_zero(fungible_asset: FungibleAsset) { + let FungibleAsset { amount, metadata: _ } = fungible_asset; + assert!(amount == 0, error::invalid_argument(EAMOUNT_IS_NOT_ZERO)); } - /// Assert the owner of `metadata`. - inline fun assert_owner(owner: &signer, metadata: &Object) { - assert!(object::is_owner(*metadata, signer::address_of(owner)), error::permission_denied(ENOT_OWNER)); + fun deposit_internal( + wallet: Object, + fa: FungibleAsset, + ) acquires FungibleAssetWallet, FungibleAssetWalletEvents { + let FungibleAsset { metadata, amount } = fa; + let wallet_metadata = wallet_metadata(wallet); + assert!(metadata == wallet_metadata, error::invalid_argument(EFUNGIBLE_ASSET_AND_WALLET_MISMATCH)); + let wallet_addr = object::object_address(&wallet); + let wallet = borrow_global_mut(wallet_addr); + wallet.balance = wallet.balance + amount; + + let events = borrow_global_mut(wallet_addr); + event::emit_event(&mut events.deposit_events, DepositEvent { amount }); } /// Extract `amount` of fungible asset from `wallet`. - fun extract( - wallet: &Object, + fun withdraw_internal( + wallet_addr: address, amount: u64, - ): FungibleAsset acquires FungibleAssetWallet { + ): FungibleAsset acquires FungibleAssetWallet, FungibleAssetWalletEvents { assert!(amount != 0, error::invalid_argument(EAMOUNT_CANNOT_BE_ZERO)); - let wallet = borrow_fungible_asset_mut(wallet); - assert!(wallet.allow_ungated_transfer, error::invalid_argument(EUNGATED_TRANSFER_IS_NOT_ALLOWED)); + let wallet = borrow_global_mut(wallet_addr); assert!(wallet.balance >= amount, error::invalid_argument(EINSUFFICIENT_BALANCE)); wallet.balance = wallet.balance - amount; - FungibleAsset { - metadata: wallet.metadata, - amount - } + + let events = borrow_global_mut(wallet_addr); + let metadata = wallet.metadata; + event::emit_event(&mut events.withdraw_events, WithdrawEvent { amount }); + + FungibleAsset { metadata, amount } } /// Increase the supply of a fungible metadata by minting. fun increase_supply(metadata: &Object, amount: u64) acquires FungibleAssetMetadata { - assert!(amount != 0, error::invalid_argument(EZERO_AMOUNT)); + assert!(amount != 0, error::invalid_argument(EAMOUNT_CANNOT_BE_ZERO)); let fungible_metadata = borrow_fungible_metadata_mut(metadata); if (option::is_some(&fungible_metadata.maximum)) { let max = *option::borrow(&fungible_metadata.maximum); - assert!(max - fungible_metadata.supply >= amount, error::invalid_argument(ECURRENT_SUPPLY_OVERFLOW)) + assert!(max - fungible_metadata.supply >= amount, error::invalid_argument(EMAX_SUPPLY_EXCEEDED)) }; fungible_metadata.supply = fungible_metadata.supply + amount; } /// Decrease the supply of a fungible metadata by burning. fun decrease_supply(metadata: &Object, amount: u64) acquires FungibleAssetMetadata { - assert!(amount != 0, error::invalid_argument(EZERO_AMOUNT)); + assert!(amount != 0, error::invalid_argument(EAMOUNT_CANNOT_BE_ZERO)); let fungible_metadata = borrow_fungible_metadata_mut(metadata); - assert!(fungible_metadata.supply >= amount, error::invalid_argument(ECURRENT_SUPPLY_UNDERFLOW)); + assert!(fungible_metadata.supply >= amount, error::invalid_argument(ESUPPLY_UNDERFLOW)); fungible_metadata.supply = fungible_metadata.supply - amount; } inline fun borrow_fungible_metadata( metadata: &Object ): &FungibleAssetMetadata acquires FungibleAssetMetadata { - let addr = object::object_address(&verify(metadata)); + let addr = object::object_address(metadata); borrow_global(addr) } inline fun borrow_fungible_metadata_mut( metadata: &Object ): &mut FungibleAssetMetadata acquires FungibleAssetMetadata { - let addr = object::object_address(&verify(metadata)); + let addr = object::object_address(metadata); borrow_global_mut(addr) } - inline fun borrow_fungible_asset( - wallet: &Object, - ): &FungibleAssetWallet acquires FungibleAssetWallet { + inline fun borrow_wallet_resource(wallet: &Object): &FungibleAssetWallet acquires FungibleAssetWallet { borrow_global(object::object_address(wallet)) } - inline fun borrow_fungible_asset_mut( - wallet: &Object, - ): &mut FungibleAssetWallet acquires FungibleAssetWallet { - borrow_global_mut(object::object_address(wallet)) - } - #[test_only] use std::string; #[test_only] use aptos_framework::account; - #[test_only] - use aptos_framework::object::object_address; #[test_only] #[resource_group_member(group = aptos_framework::object::ObjectGroup)] @@ -436,18 +509,22 @@ module aptos_framework::fungible_asset { } #[test_only] - public fun init_test_metadata(creator_ref: &ConstructorRef): (MintRef, TransferRef, BurnRef) { - init_metadata( - creator_ref, + public fun init_test_metadata(constructor_ref: &ConstructorRef): (MintRef, TransferRef, BurnRef) { + add_fungibility( + constructor_ref, 100 /* max supply */, string::utf8(b"USDA"), string::utf8(b"$$$"), 0 - ) + ); + let mint_ref = generate_mint_ref(constructor_ref); + let burn_ref = generate_burn_ref(constructor_ref); + let transfer_ref = generate_transfer_ref(constructor_ref); + (mint_ref, transfer_ref, burn_ref) } #[test_only] - public fun generate_refs( + public fun create_fungible_asset( creator: &signer ): (MintRef, TransferRef, BurnRef, Object) { let (creator_ref, metadata) = create_test_token(creator); @@ -456,35 +533,32 @@ module aptos_framework::fungible_asset { } #[test_only] - fun generate_wallet( - creator: &signer, - metadata: &Object, - ): Object { - if (!account::exists_at(signer::address_of(creator))) { - account::create_account_for_test(signer::address_of(creator)); + public fun create_test_wallet(owner: &signer, metadata: Object): Object { + let owner_addr = signer::address_of(owner); + if (!account::exists_at(owner_addr)) { + account::create_account_for_test(owner_addr); }; - let wallet_creator_ref = object::create_object_from_account(creator); - new_fungible_asset_wallet_object(&wallet_creator_ref, metadata) + create_wallet(&object::create_object_from_account(owner), metadata) } #[test(creator = @0xcafe)] fun test_metadata_basic_flow(creator: &signer) acquires FungibleAssetMetadata { let (creator_ref, asset) = create_test_token(creator); init_test_metadata(&creator_ref); - assert!(supply(&asset) == 0, 1); - assert!(maximum(&asset) == option::some(100), 2); - assert!(name(&asset) == string::utf8(b"USDA"), 3); - assert!(symbol(&asset) == string::utf8(b"$$$"), 4); - assert!(decimals(&asset) == 0, 5); + assert!(supply(asset) == 0, 1); + assert!(maximum(asset) == option::some(100), 2); + assert!(name(asset) == string::utf8(b"USDA"), 3); + assert!(symbol(asset) == string::utf8(b"$$$"), 4); + assert!(decimals(asset) == 0, 5); increase_supply(&asset, 50); - assert!(supply(&asset) == 50, 6); + assert!(supply(asset) == 50, 6); decrease_supply(&asset, 30); - assert!(supply(&asset) == 20, 7); + assert!(supply(asset) == 20, 7); } #[test(creator = @0xcafe)] - #[expected_failure(abort_code = 0x10008, location = Self)] + #[expected_failure(abort_code = 0x10005, location = Self)] fun test_supply_overflow(creator: &signer) acquires FungibleAssetMetadata { let (creator_ref, asset) = create_test_token(creator); init_test_metadata(&creator_ref); @@ -492,7 +566,7 @@ module aptos_framework::fungible_asset { } #[test(creator = @0xcafe)] - #[expected_failure(abort_code = 0x10009, location = Self)] + #[expected_failure(abort_code = 0x10006, location = Self)] fun test_supply_underflow(creator: &signer) acquires FungibleAssetMetadata { let (creator_ref, asset) = create_test_token(creator); init_test_metadata(&creator_ref); @@ -503,64 +577,65 @@ module aptos_framework::fungible_asset { fun test_e2e_basic_flow( creator: &signer, aaron: &signer, - ) acquires FungibleAssetWallet, FungibleAssetMetadata { - let (mint_ref, transfer_ref, burn_ref, metadata) = generate_refs(creator); - let metadata = verify(&metadata); - let creator_wallet = generate_wallet(creator, &metadata); - let aaron_wallet = generate_wallet(aaron, &metadata); + ) acquires FungibleAssetMetadata, FungibleAssetWallet, FungibleAssetWalletEvents { + let (mint_ref, transfer_ref, burn_ref, test_token) = create_fungible_asset(creator); + let metadata = mint_ref.metadata; + let creator_wallet = create_test_wallet(creator, metadata); + let aaron_wallet = create_test_wallet(aaron, metadata); - assert!(supply(&metadata) == 0, 1); + assert!(supply(test_token) == 0, 1); // Mint let fa = mint(&mint_ref, 100); - assert!(supply(&metadata) == 100, 2); + assert!(supply(test_token) == 100, 2); // Deposit - deposit(&creator_wallet, fa); + deposit(creator_wallet, fa); // Withdraw - let fa = withdraw(creator, &creator_wallet, 80); - assert!(supply(&metadata) == 100, 3); - deposit(&aaron_wallet, fa); + let fa = withdraw(creator, creator_wallet, 80); + assert!(supply(test_token) == 100, 3); + deposit(aaron_wallet, fa); // Burn - burn(&burn_ref, &aaron_wallet, 30); - assert!(supply(&metadata) == 70, 4); + burn(&burn_ref, aaron_wallet, 30); + assert!(supply(test_token) == 70, 4); // Transfer - transfer(creator, 10, &creator_wallet, &aaron_wallet); - assert!(balance(&creator_wallet) == 10, 5); - assert!(balance(&aaron_wallet) == 60, 6); + transfer(creator, creator_wallet, aaron_wallet, 10); + assert!(balance(creator_wallet) == 10, 5); + assert!(balance(aaron_wallet) == 60, 6); - set_ungated_transfer(&transfer_ref, &aaron_wallet, false); - assert!(!ungated_transfer_allowed(&aaron_wallet), 7); - - destory_fungible_asset_wallet(aaron_wallet); - assert!(!exists(object_address(&aaron_wallet)), 8); + set_ungated_transfer(&transfer_ref, aaron_wallet, false); + assert!(!ungated_transfer_allowed(aaron_wallet), 7); } #[test(creator = @0xcafe)] - #[expected_failure(abort_code = 0x10004, location = Self)] - fun test_ungated_transfer(creator: &signer) acquires FungibleAssetMetadata, FungibleAssetWallet { - let (mint_ref, transfer_ref, _burn_ref, metadata) = generate_refs(creator); - let metadata = verify(&metadata); - let creator_wallet = generate_wallet(creator, &metadata); + #[expected_failure(abort_code = 0x10003, location = Self)] + fun test_ungated_transfer( + creator: &signer + ) acquires FungibleAssetMetadata, FungibleAssetWallet, FungibleAssetWalletEvents { + let (mint_ref, transfer_ref, _burn_ref, _) = create_fungible_asset(creator); + let creator_wallet = create_test_wallet(creator, mint_ref.metadata); let fa = mint(&mint_ref, 100); - set_ungated_transfer(&transfer_ref, &creator_wallet, false); - deposit(&creator_wallet, fa); + set_ungated_transfer(&transfer_ref, creator_wallet, false); + deposit(creator_wallet, fa); } #[test(creator = @0xcafe, aaron = @0xface)] - fun test_transfer_with_ref(creator: &signer, aaron: &signer) acquires FungibleAssetMetadata, FungibleAssetWallet { - let (mint_ref, transfer_ref, _burn_ref, metadata) = generate_refs(creator); - let metadata = verify(&metadata); - let creator_wallet = generate_wallet(creator, &metadata); - let aaron_wallet = generate_wallet(aaron, &metadata); + fun test_transfer_with_ref( + creator: &signer, + aaron: &signer, + ) acquires FungibleAssetMetadata, FungibleAssetWallet, FungibleAssetWalletEvents { + let (mint_ref, transfer_ref, _burn_ref, _) = create_fungible_asset(creator); + let metadata = mint_ref.metadata; + let creator_wallet = create_test_wallet(creator, metadata); + let aaron_wallet = create_test_wallet(aaron, metadata); let fa = mint(&mint_ref, 100); - set_ungated_transfer(&transfer_ref, &creator_wallet, false); - set_ungated_transfer(&transfer_ref, &aaron_wallet, false); - deposit_with_ref(&transfer_ref, &creator_wallet, fa); - transfer_with_ref(&transfer_ref, &creator_wallet, &aaron_wallet, 80); - assert!(balance(&creator_wallet) == 20, 1); - assert!(balance(&aaron_wallet) == 80, 2); - assert!(!ungated_transfer_allowed(&creator_wallet), 3); - assert!(!ungated_transfer_allowed(&aaron_wallet), 4); + set_ungated_transfer(&transfer_ref, creator_wallet, false); + set_ungated_transfer(&transfer_ref, aaron_wallet, false); + deposit_with_ref(&transfer_ref, creator_wallet, fa); + transfer_with_ref(&transfer_ref, creator_wallet, aaron_wallet, 80); + assert!(balance(creator_wallet) == 20, 1); + assert!(balance(aaron_wallet) == 80, 2); + assert!(!ungated_transfer_allowed(creator_wallet), 3); + assert!(!ungated_transfer_allowed(aaron_wallet), 4); } } diff --git a/aptos-move/framework/aptos-framework/sources/fungible_asset.spec.move b/aptos-move/framework/aptos-framework/sources/fungible_asset.spec.move new file mode 100644 index 0000000000000..db2a3965b694f --- /dev/null +++ b/aptos-move/framework/aptos-framework/sources/fungible_asset.spec.move @@ -0,0 +1,6 @@ +spec aptos_framework::fungible_asset { + spec module { + // TODO: verification disabled until this module is specified. + pragma verify=false; + } +} diff --git a/aptos-move/framework/aptos-framework/sources/fungible_store.move b/aptos-move/framework/aptos-framework/sources/fungible_store.move deleted file mode 100644 index 6cdc6f5aa67fc..0000000000000 --- a/aptos-move/framework/aptos-framework/sources/fungible_store.move +++ /dev/null @@ -1,314 +0,0 @@ -/// This defines a store of `FungibleAssetWallet` under each account. -module aptos_framework::fungible_store { - use aptos_framework::create_signer; - use aptos_framework::fungible_asset::{Self, FungibleAssetWallet, FungibleAsset, FungibleAssetMetadata, TransferRef, metadata_from_wallet, BurnRef}; - use aptos_framework::object::{Self, Object}; - use aptos_std::smart_table::{Self, SmartTable}; - use std::error; - use std::option::{Self, Option}; - use std::signer; - #[test_only] - use aptos_framework::fungible_asset::verify; - - /// The fungible asset wallet object existence error. - const EFUNGIBLE_ASSET_WALLET_OBJECT: u64 = 1; - - /// Represents all the fungible asset wallet objects of an onwer keyed by the base metadata objects. - struct FungibleAssetStore has key { - index: SmartTable, Object> - } - - /// Check the balance of an account. - public fun balance( - account: address, - metadata: &Object - ): u64 acquires FungibleAssetStore { - let metadata = fungible_asset::verify(metadata); - let afa_opt = get_account_fungible_asset_object( - account, - &metadata, - false - ); - if (option::is_none(&afa_opt)) { - return 0 - }; - let wallet = option::destroy_some(afa_opt); - fungible_asset::balance(&wallet) - } - - /// Return true if `account` allows ungated transfer. - public fun ungated_transfer_allowed( - account: address, - metadata: &Object - ): bool acquires FungibleAssetStore { - let metadata = fungible_asset::verify(metadata); - let afa_opt = get_account_fungible_asset_object( - account, - &metadata, - false - ); - if (option::is_none(&afa_opt)) { - return true - }; - let wallet = option::destroy_some(afa_opt); - fungible_asset::ungated_transfer_allowed(&wallet) - } - - /// Enable/disable the direct transfer. - public fun set_ungated_transfer( - ref: &TransferRef, - account: address, - allow: bool - ) acquires FungibleAssetStore { - let metadata = fungible_asset::verify(&fungible_asset::transfer_ref_metadata(ref)); - let afa_opt = get_account_fungible_asset_object(account, &metadata, !allow); - if (option::is_none(&afa_opt)) { - return - }; - let wallet = option::destroy_some(afa_opt); - fungible_asset::set_ungated_transfer(ref, &wallet, allow); - maybe_delete(wallet); - } - - /// Withdraw `amount` of fungible asset from `account`. - public fun withdraw( - account: &signer, - metadata: &Object, - amount: u64 - ): FungibleAsset acquires FungibleAssetStore { - let metadata = fungible_asset::verify(metadata); - let account_address = signer::address_of(account); - let wallet = ensure_fungible_asset_wallet( - account_address, - &metadata, - false - ); - - let fa = fungible_asset::withdraw(account, &wallet, amount); - maybe_delete(wallet); - fa - } - - /// Deposit fungible asset to `account`. - public fun deposit( - fa: FungibleAsset, - to: address - ) acquires FungibleAssetStore { - let metadata = fungible_asset::metadata_from_asset(&fa); - let wallet = ensure_fungible_asset_wallet( - to, - &metadata, - true - ); - fungible_asset::deposit(&wallet, fa); - } - - /// Transfer `amount` of fungible asset as the owner. - public fun transfer( - from: &signer, - metadata: &Object, - amount: u64, - to: address - ) acquires FungibleAssetStore { - let fa = withdraw(from, metadata, amount); - deposit(fa, to); - } - - /// Transfer `ammount` of fungible asset ignoring `allow_ungated_transfer` with `TransferRef`. - public fun transfer_with_ref( - ref: &TransferRef, - from: address, - to: address, - amount: u64, - ) acquires FungibleAssetStore { - let sender_wallet = ensure_fungible_asset_wallet( - from, - &fungible_asset::transfer_ref_metadata(ref), - false - ); - let receiver_wallet = ensure_fungible_asset_wallet( - to, - &fungible_asset::transfer_ref_metadata(ref), - true - ); - fungible_asset::transfer_with_ref(ref, &sender_wallet, &receiver_wallet, amount); - } - - /// Withdraw `ammount` of fungible asset ignoring `allow_ungated_transfer` with `TransferRef`. - public fun withdraw_with_ref( - ref: &TransferRef, - account: address, - amount: u64 - ): FungibleAsset acquires FungibleAssetStore { - let wallet = ensure_fungible_asset_wallet( - account, - &fungible_asset::transfer_ref_metadata(ref), - false - ); - fungible_asset::withdraw_with_ref(ref, &wallet, amount) - } - - /// Deposit `ammount` of fungible asset ignoring `allow_ungated_transfer` with `TransferRef`. - public fun deposit_with_ref(ref: &TransferRef, account: address, fa: FungibleAsset) acquires FungibleAssetStore { - let wallet = ensure_fungible_asset_wallet( - account, - &fungible_asset::transfer_ref_metadata(ref), - true - ); - fungible_asset::deposit_with_ref(ref, &wallet, fa); - } - - /// Burn the `amount` of fungible asset from `account` with `BurnRef`. - public fun burn(ref: &BurnRef, account: address, amount: u64) acquires FungibleAssetStore { - let wallet = ensure_fungible_asset_wallet( - account, - &fungible_asset::burn_ref_metadata(ref), - false - ); - fungible_asset::burn(ref, &wallet, amount); - maybe_delete(wallet); - } - - /// Ensure fungible metadata store exists. If not, create it. - inline fun ensure_fungible_asset_store(account_address: address) { - if (!exists(account_address)) { - let account_signer = create_signer::create_signer(account_address); - move_to(&account_signer, FungibleAssetStore { - index: smart_table::new() - }) - } - } - - /// Get the `FungibleAssetWallet` object of `metadata` belonging to `account`. - /// if `create_on_demand` is true, an default `FungibleAssetWallet` will be created if not exist; otherwise abort. - fun get_account_fungible_asset_object( - account: address, - metadata: &Object, - create_on_demand: bool - ): Option> acquires FungibleAssetStore { - ensure_fungible_asset_store(account); - let metadata = fungible_asset::verify(metadata); - let index_table = &mut borrow_global_mut(account).index; - if (!smart_table::contains(index_table, copy metadata)) { - if (create_on_demand) { - let afa_obj = create_account_fungible_asset_object(account, &metadata); - smart_table::add(index_table, copy metadata, afa_obj); - } else { - return option::none() - } - }; - let wallet = *smart_table::borrow(index_table, metadata); - option::some(wallet) - } - - /// Ensure the existence and return the `FungibleAssetWallet`. - inline fun ensure_fungible_asset_wallet( - account: address, - metadata: &Object, - create_on_demand: bool - ): Object acquires FungibleAssetStore { - let afa_opt = get_account_fungible_asset_object(account, metadata, create_on_demand); - assert!(option::is_some(&afa_opt), error::not_found(EFUNGIBLE_ASSET_WALLET_OBJECT)); - option::destroy_some(afa_opt) - } - - /// Create a default `FungibleAssetWallet` object with zero balance of `metadata`. - fun create_account_fungible_asset_object( - account: address, - metadata: &Object - ): Object { - // Must review carefully here. - let asset_signer = create_signer::create_signer(object::object_address(metadata)); - let creator_ref = object::create_object_from_object(&asset_signer); - let wallet = fungible_asset::new_fungible_asset_wallet_object(&creator_ref, metadata); - // Transfer the owner to `account`. - object::transfer(&asset_signer, wallet, account); - // Disable transfer of coin object so the object itself never gets transfered. - let transfer_ref = object::generate_transfer_ref(&creator_ref); - object::disable_ungated_transfer(&transfer_ref); - wallet - } - - /// Remove the `FungibleAssetWallet` object of `metadata` from `account` if balance drops to 0 and - /// `allowed_ungated_transfer` is allowed. - inline fun maybe_delete(wallet: Object) acquires FungibleAssetStore { - if (fungible_asset::balance(&wallet) == 0 && fungible_asset::ungated_transfer_allowed(&wallet)) { - let owner = object::owner(wallet); - let index_table = &mut borrow_global_mut(owner).index; - smart_table::remove(index_table, metadata_from_wallet(&wallet)); - fungible_asset::destory_fungible_asset_wallet(wallet); - }; - } - - #[test(creator = @0xcafe, aaron = @0xface)] - fun test_basic_flow( - creator: &signer, - aaron: &signer - ) acquires FungibleAssetStore { - let (mint_ref, transfer_ref, burn_ref, metadata) = fungible_asset::generate_refs(creator); - - let creator_address = signer::address_of(creator); - let aaron_address = signer::address_of(aaron); - - // Mint - let fa = fungible_asset::mint(&mint_ref, 100); - deposit(fa, creator_address); - assert!(balance(creator_address, &metadata) == 100, 1); - - // Transfer - let fa = withdraw(creator, &metadata, 80); - deposit(fa, aaron_address); - assert!(balance(aaron_address, &metadata) == 80, 2); - - assert!(ungated_transfer_allowed(aaron_address, &metadata), 3); - set_ungated_transfer(&transfer_ref, aaron_address, false); - assert!(!ungated_transfer_allowed(aaron_address, &metadata), 4); - let fa = withdraw_with_ref(&transfer_ref, aaron_address, 20); - deposit_with_ref(&transfer_ref, aaron_address, fa); - transfer_with_ref(&transfer_ref, aaron_address, creator_address, 20); - assert!(balance(creator_address, &metadata) == 40, 5); - - // burn - burn(&burn_ref, creator_address, 30); - assert!(fungible_asset::supply(&metadata) == 70, 6); - } - - #[test(creator = @0xcafe)] - fun test_empty_account_default_behavior_and_creation_on_demand( - creator: &signer, - ) acquires FungibleAssetStore { - let (_mint_ref, transfer_ref, _burn_ref, metadata) = fungible_asset::generate_refs(creator); - let metadata = verify((&metadata)); - let creator_address = signer::address_of(creator); - - assert!(balance(creator_address, &metadata) == 0, 1); - assert!(ungated_transfer_allowed(creator_address, &metadata), 2); - assert!(option::is_none(&get_account_fungible_asset_object(creator_address, &metadata, false)), 3); - set_ungated_transfer(&transfer_ref, creator_address, false); - assert!(option::is_some(&get_account_fungible_asset_object(creator_address, &metadata, false)), 4); - } - - #[test(creator = @0xcafe)] - fun test_auto_deletion( - creator: &signer, - ) acquires FungibleAssetStore { - let (mint_ref, transfer_ref, burn_ref, metadata) = fungible_asset::generate_refs(creator); - let metadata = verify((&metadata)); - let creator_address = signer::address_of(creator); - - // Mint - let fa = fungible_asset::mint(&mint_ref, 100); - deposit(fa, creator_address); - assert!(balance(creator_address, &metadata) == 100, 1); - // exist - assert!(option::is_some(&get_account_fungible_asset_object(creator_address, &metadata, false)), 2); - - burn(&burn_ref, creator_address, 100); - assert!(balance(creator_address, &metadata) == 0, 3); - assert!(option::is_none(&get_account_fungible_asset_object(creator_address, &metadata, false)), 4); - set_ungated_transfer(&transfer_ref, creator_address, false); - assert!(option::is_some(&get_account_fungible_asset_object(creator_address, &metadata, false)), 5); - set_ungated_transfer(&transfer_ref, creator_address, true); - assert!(option::is_none(&get_account_fungible_asset_object(creator_address, &metadata, false)), 6); - } -} diff --git a/aptos-move/framework/aptos-framework/sources/fungible_store.spec.move b/aptos-move/framework/aptos-framework/sources/fungible_store.spec.move deleted file mode 100644 index 79083bb2abbe9..0000000000000 --- a/aptos-move/framework/aptos-framework/sources/fungible_store.spec.move +++ /dev/null @@ -1,6 +0,0 @@ -spec aptos_framework::fungible_store { - // TODO: temporarily mocked up. - spec module { - pragma verify = false; - } -} diff --git a/aptos-move/framework/aptos-framework/sources/managed_fungible_metadata.move b/aptos-move/framework/aptos-framework/sources/managed_fungible_metadata.move deleted file mode 100644 index 22d5d79cd5391..0000000000000 --- a/aptos-move/framework/aptos-framework/sources/managed_fungible_metadata.move +++ /dev/null @@ -1,294 +0,0 @@ -/// This module provides an addtional ready-to-use solution on top of `FungibleAssetMetadata` that manages the refs of -/// mint, burn and transfer for the creator in a straightforward scheme. It offers creators to destory any refs in an -/// on-demand manner too. -module aptos_framework::managed_fungible_metadata { - use aptos_framework::fungible_asset::{Self, MintRef, TransferRef, BurnRef, FungibleAsset}; - use aptos_framework::fungible_store; - use aptos_framework::object::{Self, Object, ConstructorRef}; - use std::error; - use std::option::{Self, Option}; - use std::signer; - use std::string::String; - - /// MintRef existence error. - const EMINT_REF: u64 = 1; - /// TransferRef existence error. - const ETRANSFER_REF: u64 = 2; - /// BurnRef existence error. - const EBURN_REF: u64 = 3; - /// Not the owner. - const ENOT_OWNER: u64 = 4; - /// Refs existence errors. - const EMANAGED_FUNGIBLE_ASSET_REFS: u64 = 5; - - #[resource_group_member(group = aptos_framework::object::ObjectGroup)] - /// Hold refs to control the minting, transfer and burning of fungible assets. - struct ManagingRefs has key { - mint: Option, - transfer: Option, - burn: Option, - } - - /// Initialize metadata object and store the refs. - public fun init_managing_refs( - constructor_ref: &ConstructorRef, - maximum_supply: u64, - name: String, - symbol: String, - decimals: u8 - ) { - let (mint_ref, transfer_ref, burn_ref) = fungible_asset::init_metadata( - constructor_ref, - maximum_supply, - name, - symbol, - decimals - ); - let metadata_object_signer = object::generate_signer(constructor_ref); - move_to( - &metadata_object_signer, - ManagingRefs { - mint: option::some(mint_ref), transfer: option::some(transfer_ref), burn: option::some(burn_ref) - } - ) - } - - /// Mint as the owner of metadata object. - public fun mint( - metadata_owner: &signer, - metadata: &Object, - amount: u64, - to: address - ) acquires ManagingRefs { - assert_owner(metadata_owner, metadata); - let mint_ref = borrow_mint_from_refs(metadata); - let fa = fungible_asset::mint(mint_ref, amount); - fungible_store::deposit(fa, to); - } - - /// Withdraw as the owner of metadata object ignoring `allow_ungated_transfer` field. - public fun withdraw( - metadata_owner: &signer, - metadata: &Object, - amount: u64, - from: address, - ): FungibleAsset acquires ManagingRefs { - assert_owner(metadata_owner, metadata); - let transfer_ref = borrow_transfer_from_refs(metadata); - fungible_store::withdraw_with_ref(transfer_ref, from, amount) - } - - /// Deposit as the owner of metadata object ignoring `allow_ungated_transfer` field. - public fun deposit( - metadata_owner: &signer, - metadata: &Object, - to: address, - fa: FungibleAsset - ) acquires ManagingRefs { - assert_owner(metadata_owner, metadata); - let transfer_ref = borrow_transfer_from_refs(metadata); - fungible_store::deposit_with_ref(transfer_ref, to, fa); - } - - /// Transfer as the owner of metadata object ignoring `allow_ungated_transfer` field. - public fun transfer( - metadata_owner: &signer, - metadata: &Object, - from: address, - to: address, - amount: u64, - ) acquires ManagingRefs { - assert_owner(metadata_owner, metadata); - let transfer_ref = borrow_transfer_from_refs(metadata); - fungible_store::transfer_with_ref(transfer_ref, from, to, amount); - } - - /// Burn fungible assets as the owner of metadata object. - public fun burn( - metadata_owner: &signer, - metadata: &Object, - from: address, - amount: u64 - ) acquires ManagingRefs { - assert_owner(metadata_owner, metadata); - let burn_ref = borrow_burn_from_refs(metadata); - fungible_store::burn(burn_ref, from, amount); - } - - /// Set the `allow_ungated_transfer` field in `AccountFungibleAsset` associated with `metadata` of `account` as the - /// owner of metadata object. - public fun set_ungated_transfer( - metadata_owner: &signer, - metadata: &Object, - account: address, - allow: bool - ) acquires ManagingRefs { - assert_owner(metadata_owner, metadata); - let transfer_ref = borrow_transfer_from_refs(metadata); - fungible_store::set_ungated_transfer(transfer_ref, account, allow); - } - - /// Return if the owner of `metadata` has access to `MintRef`. - public fun can_mint(metadata: &Object): bool acquires ManagingRefs { - option::is_some(&borrow_refs(metadata).mint) - } - - /// Return if the owner of `metadata` has access to `TransferRef`. - public fun can_transfer(metadata: &Object): bool acquires ManagingRefs { - option::is_some(&borrow_refs(metadata).transfer) - } - - /// Return if the owner of `metadata` has access to `BurnRef`. - public fun can_burn(metadata: &Object): bool acquires ManagingRefs { - option::is_some(&borrow_refs(metadata).burn) - } - - /// Let metadata owner to explicitly waive the mint capability. - public fun waive_mint( - metadata_owner: &signer, - metadata: &Object - ) acquires ManagingRefs { - let mint_ref = &mut borrow_refs_mut(metadata_owner, metadata).mint; - assert!(option::is_some(mint_ref), error::not_found(EMINT_REF)); - option::extract(mint_ref); - } - - /// Let metadata owner to explicitly waive the transfer capability. - public fun waive_transfer( - metadata_owner: &signer, - metadata: &Object - ) acquires ManagingRefs { - let transfer_ref = &mut borrow_refs_mut(metadata_owner, metadata).transfer; - assert!(option::is_some(transfer_ref), error::not_found(ETRANSFER_REF)); - option::extract(transfer_ref); - } - - /// Let metadata owner to explicitly waive the burn capability. - public fun waive_burn( - metadata_owner: &signer, - metadata: &Object - ) acquires ManagingRefs { - let burn_ref = &mut borrow_refs_mut(metadata_owner, metadata).burn; - assert!(option::is_some(burn_ref), error::not_found(ETRANSFER_REF)); - option::extract(burn_ref); - } - - /// Borrow the immutable reference of the `MintRef` of `metadata`. - inline fun borrow_mint_from_refs( - metadata: &Object, - ): &MintRef acquires ManagingRefs { - let mint_ref = &borrow_refs(metadata).mint; - assert!(option::is_some(mint_ref), error::not_found(EMINT_REF)); - option::borrow(mint_ref) - } - - /// Borrow the immutable reference of the `TransferRef` of `metadata`. - inline fun borrow_transfer_from_refs( - metadata: &Object, - ): &TransferRef acquires ManagingRefs { - let transfer_ref = &borrow_refs(metadata).transfer; - assert!(option::is_some(transfer_ref), error::not_found(ETRANSFER_REF)); - option::borrow(transfer_ref) - } - - /// Borrow the immutable reference of the `BurnRef` of `metadata`. - inline fun borrow_burn_from_refs( - metadata: &Object, - ): &BurnRef acquires ManagingRefs { - let burn_ref = &borrow_refs(metadata).burn; - assert!(option::is_some(burn_ref), error::not_found(EBURN_REF)); - option::borrow(burn_ref) - } - - /// Borrow the immutable reference of the refs of `metadata`. - inline fun borrow_refs( - metadata: &Object, - ): &ManagingRefs acquires ManagingRefs { - borrow_global_mut(verify(metadata)) - } - - /// Borrow the mutable reference of the refs of `metadata`. - inline fun borrow_refs_mut( - owner: &signer, - metadata: &Object, - ): &mut ManagingRefs acquires ManagingRefs { - assert_owner(owner, metadata); - borrow_global_mut(verify(metadata)) - } - - /// Verify `metadata` indeed has `ManagingRefs` resource associated. - inline fun verify(metadata: &Object): address { - let metadata_addr = object::object_address(metadata); - object::address_to_object(metadata_addr); - metadata_addr - } - - /// Assert the owner of `metadata`. - inline fun assert_owner(owner: &signer, metadata: &Object) { - assert!(object::is_owner(*metadata, signer::address_of(owner)), error::permission_denied(ENOT_OWNER)); - } - - #[test_only] - use aptos_framework::fungible_asset::TestToken; - #[test_only] - use aptos_framework::fungible_store::{balance, ungated_transfer_allowed}; - #[test_only] - use std::string; - - #[test_only] - public fun init_test_managing_refs(creator: &signer): Object { - let (creator_ref, metadata) = fungible_asset::create_test_token(creator); - init_managing_refs( - &creator_ref, - 100 /* max supply */, - string::utf8(b"USDA"), - string::utf8(b"$$$"), - 0 - ); - metadata - } - - #[test(creator = @0xcafe)] - fun test_basic_flow( - creator: &signer, - ) acquires ManagingRefs { - let metadata = init_test_managing_refs(creator); - let creator_address = signer::address_of(creator); - let aaron_address = @0xface; - - assert!(can_mint(&metadata), 1); - assert!(can_transfer(&metadata), 2); - assert!(can_burn(&metadata), 3); - - mint(creator, &metadata, 100, creator_address); - assert!(balance(creator_address, &metadata) == 100, 4); - set_ungated_transfer(creator, &metadata, creator_address, false); - assert!(!ungated_transfer_allowed(creator_address, &metadata), 5); - transfer(creator, &metadata, creator_address, aaron_address, 10); - assert!(balance(aaron_address, &metadata) == 10, 6); - - set_ungated_transfer(creator, &metadata, creator_address, true); - assert!(ungated_transfer_allowed(creator_address, &metadata), 7); - burn(creator, &metadata, creator_address, 90); - - waive_mint(creator, &metadata); - waive_transfer(creator, &metadata); - waive_burn(creator, &metadata); - - assert!(!can_mint(&metadata), 8); - assert!(!can_transfer(&metadata), 9); - assert!(!can_burn(&metadata), 10); - } - - #[test(creator = @0xcafe, aaron = @0xface)] - #[expected_failure(abort_code = 0x50004, location = Self)] - fun test_permission_denied( - creator: &signer, - aaron: &signer - ) acquires ManagingRefs { - let metadata = init_test_managing_refs(creator); - let creator_address = signer::address_of(creator); - assert!(can_mint(&metadata), 1); - mint(aaron, &metadata, 100, creator_address); - } -} diff --git a/aptos-move/framework/aptos-framework/sources/managed_fungible_metadata.spec.move b/aptos-move/framework/aptos-framework/sources/managed_fungible_metadata.spec.move deleted file mode 100644 index d6cd1b3a44ae6..0000000000000 --- a/aptos-move/framework/aptos-framework/sources/managed_fungible_metadata.spec.move +++ /dev/null @@ -1,6 +0,0 @@ -spec aptos_framework::managed_fungible_metadata { - // TODO: temporarily mocked up. - spec module { - pragma verify = false; - } -} diff --git a/aptos-move/framework/aptos-framework/sources/object.move b/aptos-move/framework/aptos-framework/sources/object.move index 1d5bcf9c53237..f9facbfd3bba3 100644 --- a/aptos-move/framework/aptos-framework/sources/object.move +++ b/aptos-move/framework/aptos-framework/sources/object.move @@ -49,6 +49,16 @@ module aptos_framework::object { /// nesting, but any checks such as transfer will only be evaluated this deep. const MAXIMUM_OBJECT_NESTING: u8 = 8; + /// Scheme identifier used to generate an object's address `obj_addr` as derived from another object. + /// The object's address is generated as: + /// ``` + /// obj_addr = sha3_256(account addr | derived from object's address | 0xFC) + /// ``` + /// + /// This 0xFC constant serves as a domain separation tag to prevent existing authentication key and resource account + /// derivation to produce an object address. + const OBJECT_DERIVED_SCHEME: u8 = 0xFC; + /// Scheme identifier used to generate an object's address `obj_addr` via a fresh GUID generated by the creator at /// `source_addr`. The object's address is generated as: /// ``` @@ -123,6 +133,11 @@ module aptos_framework::object { owner: address, } + /// Used to create derived objects from a given objects. + struct DeriveRef has drop, store { + self: address, + } + /// Emitted whenever the object's owner field is changed. struct TransferEvent has drop, store { object: address, @@ -145,6 +160,14 @@ module aptos_framework::object { from_bcs::to_address(hash::sha3_256(bytes)) } + /// Derives an object address from the source address and an object: sha3_256([source | object addr | 0xFC]). + public fun create_derived_object_address(source: address, derive_from: address): address { + let bytes = bcs::to_bytes(&source); + vector::append(&mut bytes, bcs::to_bytes(&derive_from)); + vector::push_back(&mut bytes, OBJECT_DERIVED_SCHEME); + from_bcs::to_address(hash::sha3_256(bytes)) + } + native fun exists_at(object: address): bool; /// Returns the address of within an ObjectId. @@ -152,6 +175,11 @@ module aptos_framework::object { object.inner } + /// Convert Object to Object. + public fun convert(object: Object): Object { + address_to_object(object.inner) + } + /// Create a new named object and return the ConstructorRef. Named objects can be queried globally /// by knowing the user generated seed used to create them. Named objects cannot be deleted. public fun create_named_object(creator: &signer, seed: vector): ConstructorRef { @@ -160,6 +188,14 @@ module aptos_framework::object { create_object_internal(creator_address, obj_addr, false) } + /// Create a new object whose address is derived based on the creator account address and another object. + /// Derivde objects, similar to named objects, cannot be deleted. + public fun create_derived_object(creator: &signer, derive_ref: &DeriveRef): ConstructorRef { + let creator_address = signer::address_of(creator); + let obj_addr = create_derived_object_address(creator_address, derive_ref.self); + create_object_internal(creator_address, obj_addr, false) + } + /// Create a new object from a GUID generated by an account. public fun create_object_from_account(creator: &signer): ConstructorRef { let guid = account::create_guid(creator); @@ -220,6 +256,11 @@ module aptos_framework::object { TransferRef { self: ref.self } } + /// Generates the DeriveRef, which can be used to create determnistic derived objects from the current object. + public fun generate_derive_ref(ref: &ConstructorRef): DeriveRef { + DeriveRef { self: ref.self } + } + /// Create a signer for the ConstructorRef public fun generate_signer(ref: &ConstructorRef): signer { create_signer(ref.self) diff --git a/aptos-move/framework/aptos-framework/sources/primary_wallet.move b/aptos-move/framework/aptos-framework/sources/primary_wallet.move new file mode 100644 index 0000000000000..432459eedafb1 --- /dev/null +++ b/aptos-move/framework/aptos-framework/sources/primary_wallet.move @@ -0,0 +1,110 @@ +/// This defines the module for interacting with primary wallets of accounts/objects, which have deterministic addresses +module aptos_framework::primary_wallet { + use aptos_framework::create_signer; + use aptos_framework::fungible_asset::{Self, FungibleAsset, FungibleAssetMetadata, FungibleAssetWallet}; + use aptos_framework::object::{Self, Object, ConstructorRef, DeriveRef}; + + use std::signer; + + #[resource_group_member(group = aptos_framework::object::ObjectGroup)] + /// Resource stored on the fungible asset metadata object to allow creating primary wallets for it. + struct PrimaryWalletSupport has key { + metadata_derive_ref: DeriveRef, + } + + /// Creators of fungible assets can call this to enable support for creating primary (deterministic) wallets for + /// their users. + public fun enable_primary_wallet(metadata_constructor_ref: &ConstructorRef) { + // Ensure that this is a fungible asset metadata object. + object::object_from_constructor_ref(metadata_constructor_ref); + let metadata_obj = &object::generate_signer(metadata_constructor_ref); + move_to(metadata_obj, PrimaryWalletSupport { + metadata_derive_ref: object::generate_derive_ref(metadata_constructor_ref), + }); + } + + public fun ensure_primary_wallet_exists( + owner: address, + metadata: Object, + ): Object acquires PrimaryWalletSupport { + if (!primary_wallet_exists(owner, metadata)) { + create_primary_wallet(owner, metadata); + }; + primary_wallet(owner, metadata) + } + + /// Create a primary wallet object to hold fungible asset for the given address. + public fun create_primary_wallet( + owner_addr: address, + metadata: Object, + ): Object acquires PrimaryWalletSupport { + let owner = &create_signer::create_signer(owner_addr); + let metadata_addr = object::object_address(&metadata); + let derive_ref = &borrow_global(metadata_addr).metadata_derive_ref; + let constructor_ref = &object::create_derived_object(owner, derive_ref); + + // Disable ungated transfer as deterministic wallets shouldn't be transferrable. + let transfer_ref = &object::generate_transfer_ref(constructor_ref); + object::disable_ungated_transfer(transfer_ref); + + fungible_asset::create_wallet(constructor_ref, metadata) + } + + #[view] + public fun primary_wallet_address(owner: address, metadata: Object): address { + let metadata_addr = object::object_address(&metadata); + object::create_derived_object_address(owner, metadata_addr) + } + + #[view] + public fun primary_wallet(owner: address, metadata: Object): Object { + let wallet = primary_wallet_address(owner, metadata); + object::address_to_object(wallet) + } + + #[view] + public fun primary_wallet_exists(account: address, metadata: Object): bool { + fungible_asset::wallet_exists(primary_wallet_address(account, metadata)) + } + + #[view] + /// Get the balance of `account`'s primary wallet. + public fun balance(account: address, metadata: Object): u64 { + if (primary_wallet_exists(account, metadata)) { + fungible_asset::balance(primary_wallet(account, metadata)) + } else { + 0 + } + } + + #[view] + /// Return whether the given account's primary wallet can do direct transfers. + public fun ungated_transfer_allowed(account: address, metadata: Object): bool { + fungible_asset::ungated_transfer_allowed(primary_wallet(account, metadata)) + } + + /// Withdraw `amount` of fungible asset from `wallet` by the owner. + public fun withdraw(owner: &signer, metadata: Object, amount: u64): FungibleAsset { + let wallet = primary_wallet(signer::address_of(owner), metadata); + fungible_asset::withdraw(owner, wallet, amount) + } + + /// Deposit `amount` of fungible asset to the given account's primary wallet. + public fun deposit(owner: address, fa: FungibleAsset) acquires PrimaryWalletSupport { + let metadata = fungible_asset::asset_metadata(&fa); + let wallet = ensure_primary_wallet_exists(owner, metadata); + fungible_asset::deposit(wallet, fa); + } + + /// Transfer `amount` of fungible asset from sender's primary wallet to receiver's primary wallet. + public entry fun transfer( + sender: &signer, + metadata: Object, + recipient: address, + amount: u64, + ) acquires PrimaryWalletSupport { + let sender_wallet = ensure_primary_wallet_exists(signer::address_of(sender), metadata); + let recipient_wallet = ensure_primary_wallet_exists(recipient, metadata); + fungible_asset::transfer(sender, sender_wallet, recipient_wallet, amount); + } +} diff --git a/aptos-move/framework/aptos-framework/sources/primary_wallet.spec.move b/aptos-move/framework/aptos-framework/sources/primary_wallet.spec.move new file mode 100644 index 0000000000000..6943786a06c1a --- /dev/null +++ b/aptos-move/framework/aptos-framework/sources/primary_wallet.spec.move @@ -0,0 +1,6 @@ +spec aptos_framework::primary_wallet { + spec module { + // TODO: verification disabled until this module is specified. + pragma verify=false; + } +} diff --git a/aptos-move/framework/aptos-framework/sources/storage_gas.spec.move b/aptos-move/framework/aptos-framework/sources/storage_gas.spec.move index a6a8eab2fc4d0..a319b53f62f2f 100644 --- a/aptos-move/framework/aptos-framework/sources/storage_gas.spec.move +++ b/aptos-move/framework/aptos-framework/sources/storage_gas.spec.move @@ -90,6 +90,7 @@ spec aptos_framework::storage_gas { /// A non decreasing curve must ensure that next is greater than cur. spec validate_points(points: &vector) { pragma aborts_if_is_strict = false; + pragma verify = false; // TODO: Disabled. Investigate why this fails. pragma opaque; include ValidatePointsAbortsIf; } diff --git a/aptos-move/move-examples/fungible_asset/Move.toml b/aptos-move/move-examples/fungible_asset/Move.toml index 6c23d31eb48e6..50f15538150b9 100644 --- a/aptos-move/move-examples/fungible_asset/Move.toml +++ b/aptos-move/move-examples/fungible_asset/Move.toml @@ -4,7 +4,7 @@ version = "0.0.0" [addresses] aptos_framework = "0x1" -fungible_asset = "_" +fungible_asset = "0xcafe" [dependencies] AptosFramework = { local = "../../framework/aptos-framework" } diff --git a/aptos-move/move-examples/fungible_asset/sources/coin.move b/aptos-move/move-examples/fungible_asset/sources/coin.move deleted file mode 100644 index 758ff8358ddea..0000000000000 --- a/aptos-move/move-examples/fungible_asset/sources/coin.move +++ /dev/null @@ -1,48 +0,0 @@ -/// This is an example showing how to create a fungible asset and how to use it. -module fungible_asset::coin { - use aptos_framework::managed_fungible_metadata; - use aptos_framework::object; - use std::string::{Self, String}; - - /// Create an coin object with built-in managing capabilities. - public entry fun create_coin( - creator: &signer, - name: String, - symbol: String, - max_supply: u64, - decimals: u8 - ) { - // TODO(lightmark): create_named_object vs create_object_from_account, which one to choose here. - let creator_ref = object::create_named_object(creator, *string::bytes(&name)); - managed_fungible_metadata::init_managing_refs(&creator_ref, max_supply, name, symbol, decimals); - } - - #[test_only] - use std::signer; - #[test_only] - use aptos_framework::account; - #[test_only] - use aptos_framework::fungible_store; - #[test_only] - use aptos_framework::fungible_asset::FungibleAssetMetadata; - - #[test(creator = @0xcafe, aaron = @0xface)] - entry fun e2e_test(creator: &signer, aaron: &signer) { - let usda = string::utf8(b"USDA"); - let creator_address = signer::address_of(creator); - account::create_account_for_test(creator_address); - let aaron_address = signer::address_of(aaron); - - create_coin(creator, usda, string::utf8(b"$"), 0, 0); - let coin_addr = object::create_object_address(&creator_address, *string::bytes(&usda)); - let coin = object::address_to_object(coin_addr); - - managed_fungible_metadata::mint(creator, &coin, 100, aaron_address); - fungible_store::transfer(aaron, &coin, 70, creator_address); - managed_fungible_metadata::set_ungated_transfer(creator, &coin, aaron_address, false); - managed_fungible_metadata::transfer(creator, &coin, aaron_address, creator_address, 10); - managed_fungible_metadata::burn(creator, &coin, creator_address, 20); - assert!(fungible_store::balance(creator_address, &coin) == 60, 1); - assert!(fungible_store::balance(aaron_address, &coin) == 20, 2); - } -} diff --git a/aptos-move/move-examples/fungible_asset/sources/managed_fungible_asset.move b/aptos-move/move-examples/fungible_asset/sources/managed_fungible_asset.move new file mode 100644 index 0000000000000..f38a6b0aecb9d --- /dev/null +++ b/aptos-move/move-examples/fungible_asset/sources/managed_fungible_asset.move @@ -0,0 +1,156 @@ +/// By deploying this module, the deployer will be creating a new managed fungible asset with the hardcoded +/// maximum supply, name, symbol, and decimals. The address of the asset can be obtained via get_asset(). +/// The deployer will also become the initial admin and can mint/burn/freeze/unfreeze accounts. +/// The admin can transfer the asset via object::transfer() at any point to set a new admin. +module fungible_asset::managed_fungible_asset { + use aptos_framework::fungible_asset::{Self, MintRef, TransferRef, BurnRef, FungibleAsset, FungibleAssetMetadata}; + use aptos_framework::object::{Self, Object}; + use aptos_framework::primary_wallet; + use std::error; + use std::signer; + use std::string::utf8; + + /// Only fungible asset metadata owner can make changes. + const ENOT_OWNER: u64 = 1; + + const ASSET_SYMBOL: vector = b"APT"; + + #[resource_group_member(group = aptos_framework::object::ObjectGroup)] + /// Hold refs to control the minting, transfer and burning of fungible assets. + struct ManagedFungibleAsset has key { + mint_ref: MintRef, + transfer_ref: TransferRef, + burn_ref: BurnRef, + } + + /// Initialize metadata object and store the refs. + fun init_module(admin: &signer) { + let constructor_ref = &object::create_named_object(admin, ASSET_SYMBOL); + fungible_asset::add_fungibility( + constructor_ref, + 0, /* maximum_supply. 0 means no maximum */ + utf8(b"Aptos Token"), /* name */ + utf8(ASSET_SYMBOL), /* symbol */ + 8, /* decimals */ + ); + // Add primary wallet support. + primary_wallet::enable_primary_wallet(constructor_ref); + + // Create mint/burn/transfer refs to allow creator to manage the fungible asset. + let mint_ref = fungible_asset::generate_mint_ref(constructor_ref); + let burn_ref = fungible_asset::generate_burn_ref(constructor_ref); + let transfer_ref = fungible_asset::generate_transfer_ref(constructor_ref); + let metadata_object_signer = object::generate_signer(constructor_ref); + move_to( + &metadata_object_signer, + ManagedFungibleAsset { mint_ref, transfer_ref, burn_ref } + ) + } + + #[view] + /// Return the address of the managed fungible asset that's created when this module is deployed. + public fun get_asset(): Object { + let asset_address = object::create_object_address(&@fungible_asset, ASSET_SYMBOL); + object::address_to_object(asset_address) + } + + /// Mint as the owner of metadata object. + public entry fun mint(admin: &signer, amount: u64, to: address) acquires ManagedFungibleAsset { + let asset = get_asset(); + let managed_fungible_asset = authorized_borrow_refs(admin, asset); + let to_wallet = primary_wallet::ensure_primary_wallet_exists(to, asset); + let fa = fungible_asset::mint(&managed_fungible_asset.mint_ref, amount); + fungible_asset::deposit_with_ref(&managed_fungible_asset.transfer_ref, to_wallet, fa); + } + + /// Transfer as the owner of metadata object ignoring `allow_ungated_transfer` field. + public entry fun transfer(admin: &signer, from: address, to: address, amount: u64) acquires ManagedFungibleAsset { + let asset = get_asset(); + let transfer_ref = &authorized_borrow_refs(admin, asset).transfer_ref; + let from_wallet = primary_wallet::ensure_primary_wallet_exists(from, asset); + let to_wallet = primary_wallet::ensure_primary_wallet_exists(to, asset); + fungible_asset::transfer_with_ref(transfer_ref, from_wallet, to_wallet, amount); + } + + /// Burn fungible assets as the owner of metadata object. + public entry fun burn(admin: &signer, from: address, amount: u64) acquires ManagedFungibleAsset { + let asset = get_asset(); + let burn_ref = &authorized_borrow_refs(admin, asset).burn_ref; + let from_wallet = primary_wallet::ensure_primary_wallet_exists(from, asset); + fungible_asset::burn(burn_ref, from_wallet, amount); + } + + /// Freeze an account so it cannot transfer or receive fungible assets. + public entry fun freeze_account(admin: &signer, account: address) acquires ManagedFungibleAsset { + let asset = get_asset(); + let transfer_ref = &authorized_borrow_refs(admin, asset).transfer_ref; + let wallet = primary_wallet::ensure_primary_wallet_exists(account, asset); + fungible_asset::set_ungated_transfer(transfer_ref, wallet, false); + } + + /// Unfreeze an account so it can transfer or receive fungible assets. + public entry fun unfreeze_account(admin: &signer, account: address) acquires ManagedFungibleAsset { + let asset = get_asset(); + let transfer_ref = &authorized_borrow_refs(admin, asset).transfer_ref; + let wallet = primary_wallet::ensure_primary_wallet_exists(account, asset); + fungible_asset::set_ungated_transfer(transfer_ref, wallet, true); + } + + /// Withdraw as the owner of metadata object ignoring `allow_ungated_transfer` field. + public fun withdraw(admin: &signer, amount: u64, from: address): FungibleAsset acquires ManagedFungibleAsset { + let asset = get_asset(); + let transfer_ref = &authorized_borrow_refs(admin, asset).transfer_ref; + let from_wallet = primary_wallet::ensure_primary_wallet_exists(from, asset); + fungible_asset::withdraw_with_ref(transfer_ref, from_wallet, amount) + } + + /// Deposit as the owner of metadata object ignoring `allow_ungated_transfer` field. + public fun deposit(admin: &signer, to: address, fa: FungibleAsset) acquires ManagedFungibleAsset { + let asset = get_asset(); + let transfer_ref = &authorized_borrow_refs(admin, asset).transfer_ref; + let to_wallet = primary_wallet::ensure_primary_wallet_exists(to, asset); + fungible_asset::deposit_with_ref(transfer_ref, to_wallet, fa); + } + + /// Borrow the immutable reference of the refs of `metadata`. + /// This validates that the signer is the metadata object's owner. + inline fun authorized_borrow_refs( + owner: &signer, + asset: Object, + ): &ManagedFungibleAsset acquires ManagedFungibleAsset { + assert!(object::is_owner(asset, signer::address_of(owner)), error::permission_denied(ENOT_OWNER)); + borrow_global(object::object_address(&asset)) + } + + #[test(creator = @0xcafe)] + fun test_basic_flow( + creator: &signer, + ) acquires ManagedFungibleAsset { + init_module(creator); + let creator_address = signer::address_of(creator); + let aaron_address = @0xface; + + mint(creator, 100, creator_address); + let asset = get_asset(); + assert!(primary_wallet::balance(creator_address, asset) == 100, 4); + freeze_account(creator, creator_address); + assert!(!primary_wallet::ungated_transfer_allowed(creator_address, asset), 5); + transfer(creator, creator_address, aaron_address, 10); + assert!(primary_wallet::balance(aaron_address, asset) == 10, 6); + + unfreeze_account(creator, creator_address); + assert!(primary_wallet::ungated_transfer_allowed(creator_address, asset), 7); + burn(creator, creator_address, 90); + } + + #[test(creator = @0xcafe, aaron = @0xface)] + #[expected_failure(abort_code = 0x50001, location = Self)] + fun test_permission_denied( + creator: &signer, + aaron: &signer + ) acquires ManagedFungibleAsset { + init_module(creator); + let creator_address = signer::address_of(creator); + mint(aaron, 100, creator_address); + } +}