From 4efdb8191c12e63e2fda74e732ef462f1710d671 Mon Sep 17 00:00:00 2001 From: Bo Wu <bo@aptoslabs.com> Date: Thu, 29 Sep 2022 12:10:11 -0700 Subject: [PATCH] [Token] reorgnize code --- .../framework/aptos-token/sources/token.move | 807 +++++++++--------- 1 file changed, 406 insertions(+), 401 deletions(-) diff --git a/aptos-move/framework/aptos-token/sources/token.move b/aptos-move/framework/aptos-token/sources/token.move index 8e0d781e159bf..a9b567267b48f 100644 --- a/aptos-move/framework/aptos-token/sources/token.move +++ b/aptos-move/framework/aptos-token/sources/token.move @@ -344,6 +344,48 @@ module aptos_token::token { ); } + /// create token with raw inputs + public entry fun create_token_script( + account: &signer, + collection: String, + name: String, + description: String, + balance: u64, + maximum: u64, + uri: String, + royalty_payee_address: address, + royalty_points_denominator: u64, + royalty_points_numerator: u64, + mutate_setting: vector<bool>, + property_keys: vector<String>, + property_values: vector<vector<u8>>, + property_types: vector<String> + ) acquires Collections, TokenStore { + let token_mut_config = create_token_mutability_config(&mutate_setting); + + let tokendata_id = create_tokendata( + account, + collection, + name, + description, + maximum, + uri, + royalty_payee_address, + royalty_points_denominator, + royalty_points_numerator, + token_mut_config, + property_keys, + property_values, + property_types + ); + + mint_token( + account, + tokendata_id, + balance, + ); + } + /// Mint more token from an existing token_data. Mint only adds more token to property_version 0 public entry fun mint_script( account: &signer, @@ -366,6 +408,36 @@ module aptos_token::token { ); } + /// mutate the token property and save the new property in TokenStore + /// if the token property_version is 0, we will create a new property_version per token to generate a new token_id per token + /// if the token property_version is not 0, we will just update the propertyMap and use the existing token_id (property_version) + public entry fun mutate_token_properties( + account: &signer, + token_owner: address, + creator: address, + collection_name: String, + token_name: String, + token_property_version: u64, + amount: u64, + keys: vector<String>, + values: vector<vector<u8>>, + types: vector<String>, + ) acquires Collections, TokenStore { + assert!(signer::address_of(account) == creator, error::not_found(ENO_MUTATE_CAPABILITY)); + let i = 0; + let token_id = create_token_id_raw( + creator, + collection_name, + token_name, + token_property_version, + ); + // give a new property_version for each token + while (i < amount) { + mutate_one_token(account, token_owner, token_id, keys, values, types); + i = i + 1; + }; + } + // // Transaction Entry functions // @@ -383,11 +455,6 @@ module aptos_token::token { direct_transfer(sender, receiver, token_id, amount); } - /// Deprecated function call - public entry fun initialize_token_script(_account: &signer) { - abort 0 - } - public entry fun opt_in_direct_transfer(account: &signer, opt_in: bool) acquires TokenStore { let addr = signer::address_of(account); initialize_token_store(account); @@ -395,6 +462,146 @@ module aptos_token::token { *opt_in_flag = opt_in; } + /// Burn a token by creator when the token's BURNABLE_BY_CREATOR is true + /// The token is owned at address owner + public entry fun burn_by_creator( + creator: &signer, + owner: address, + collection: String, + name: String, + property_version: u64, + amount: u64, + ) acquires Collections, TokenStore { + let creator_address = signer::address_of(creator); + assert!(amount > 0, error::invalid_argument(ENO_BURN_TOKEN_WITH_ZERO_AMOUNT)); + let token_id = create_token_id_raw(creator_address, collection, name, property_version); + let creator_addr = token_id.token_data_id.creator; + assert!( + exists<Collections>(creator_addr), + error::not_found(ECOLLECTIONS_NOT_PUBLISHED), + ); + + let collections = borrow_global_mut<Collections>(creator_address); + assert!( + table::contains(&collections.token_data, token_id.token_data_id), + error::not_found(ETOKEN_DATA_NOT_PUBLISHED), + ); + + let token_data = table::borrow_mut( + &mut collections.token_data, + token_id.token_data_id, + ); + + // The property should be explicitly set in the property_map for creator to burn the token + assert!( + property_map::contains_key(&token_data.default_properties, &string::utf8(BURNABLE_BY_CREATOR)), + error::permission_denied(ECREATOR_CANNOT_BURN_TOKEN) + ); + + let burn_by_creator_flag = property_map::read_bool(&token_data.default_properties, &string::utf8(BURNABLE_BY_CREATOR)); + assert!(burn_by_creator_flag, error::permission_denied(ECREATOR_CANNOT_BURN_TOKEN)); + + // Burn the tokens. + let Token { id: _, amount: burned_amount, token_properties: _ } = withdraw_with_event_internal(owner, token_id, amount); + let token_store = borrow_global_mut<TokenStore>(owner); + event::emit_event<BurnTokenEvent>( + &mut token_store.burn_events, + BurnTokenEvent { id: token_id, amount: burned_amount }, + ); + + if (token_data.maximum > 0) { + token_data.supply = token_data.supply - burned_amount; + + // Delete the token_data if supply drops to 0. + if (token_data.supply == 0) { + destroy_token_data(table::remove(&mut collections.token_data, token_id.token_data_id)); + + // update the collection supply + let collection_data = table::borrow_mut( + &mut collections.collection_data, + token_id.token_data_id.collection + ); + collection_data.supply = collection_data.supply - 1; + // delete the collection data if the collection supply equals 0 + if (collection_data.supply == 0) { + destroy_collection_data(table::remove(&mut collections.collection_data, collection_data.name)); + }; + }; + }; + } + + /// Burn a token by the token owner + public entry fun burn( + owner: &signer, + creators_address: address, + collection: String, + name: String, + property_version: u64, + amount: u64 + ) acquires Collections, TokenStore { + assert!(amount > 0, error::invalid_argument(ENO_BURN_TOKEN_WITH_ZERO_AMOUNT)); + let token_id = create_token_id_raw(creators_address, collection, name, property_version); + let creator_addr = token_id.token_data_id.creator; + assert!( + exists<Collections>(creator_addr), + error::not_found(ECOLLECTIONS_NOT_PUBLISHED), + ); + + let collections = borrow_global_mut<Collections>(creator_addr); + assert!( + table::contains(&collections.token_data, token_id.token_data_id), + error::not_found(ETOKEN_DATA_NOT_PUBLISHED), + ); + + let token_data = table::borrow_mut( + &mut collections.token_data, + token_id.token_data_id, + ); + + assert!( + property_map::contains_key(&token_data.default_properties, &string::utf8(BURNABLE_BY_OWNER)), + error::permission_denied(EOWNER_CANNOT_BURN_TOKEN) + ); + let burn_by_owner_flag = property_map::read_bool(&token_data.default_properties, &string::utf8(BURNABLE_BY_OWNER)); + assert!(burn_by_owner_flag, error::permission_denied(EOWNER_CANNOT_BURN_TOKEN)); + + // Burn the tokens. + let Token { id: _, amount: burned_amount, token_properties: _ } = withdraw_token(owner, token_id, amount); + let token_store = borrow_global_mut<TokenStore>(signer::address_of(owner)); + event::emit_event<BurnTokenEvent>( + &mut token_store.burn_events, + BurnTokenEvent { id: token_id, amount: burned_amount }, + ); + + // Decrease the supply correspondingly by the amount of tokens burned. + let token_data = table::borrow_mut( + &mut collections.token_data, + token_id.token_data_id, + ); + + // only update the supply if we tracking the supply and maximal + // maximal == 0 is reserved for unlimited token and collection with no tracking info. + if (token_data.maximum > 0) { + token_data.supply = token_data.supply - burned_amount; + + // Delete the token_data if supply drops to 0. + if (token_data.supply == 0) { + destroy_token_data(table::remove(&mut collections.token_data, token_id.token_data_id)); + + // update the collection supply + let collection_data = table::borrow_mut( + &mut collections.collection_data, + token_id.token_data_id.collection + ); + collection_data.supply = collection_data.supply - 1; + // delete the collection data if the collection supply equals 0 + if (collection_data.supply == 0) { + destroy_collection_data(table::remove(&mut collections.collection_data, collection_data.name)); + }; + }; + }; + } + /// Allow creator to mutate the default properties in TokenData public fun mutate_tokendata_property( creator: &signer, @@ -513,51 +720,6 @@ module aptos_token::token { token_data.uri = uri; } - /// mutate the token property and save the new property in TokenStore - /// if the token property_version is 0, we will create a new property_version per token to generate a new token_id per token - /// if the token property_version is not 0, we will just update the propertyMap and use the existing token_id (property_version) - public entry fun mutate_token_properties( - account: &signer, - token_owner: address, - creator: address, - collection_name: String, - token_name: String, - token_property_version: u64, - amount: u64, - keys: vector<String>, - values: vector<vector<u8>>, - types: vector<String>, - ) acquires Collections, TokenStore { - assert!(signer::address_of(account) == creator, error::not_found(ENO_MUTATE_CAPABILITY)); - let i = 0; - let token_id = create_token_id_raw( - creator, - collection_name, - token_name, - token_property_version, - ); - // give a new property_version for each token - while (i < amount) { - mutate_one_token(account, token_owner, token_id, keys, values, types); - i = i + 1; - }; - } - - fun update_token_property_internal( - token_owner: address, - token_id: TokenId, - keys: vector<String>, - values: vector<vector<u8>>, - types: vector<String>, - ) acquires TokenStore { - let tokens = &mut borrow_global_mut<TokenStore>(token_owner).tokens; - assert!(table::contains(tokens, token_id), error::not_found(ENO_TOKEN_IN_TOKEN_STORE)); - - let value = &mut table::borrow_mut(tokens, token_id).token_properties; - - property_map::update_property_map(value, keys, values, types); - } - /// Deposit the token balance into the owner's account and emit an event. public fun deposit_token(account: &signer, token: Token) acquires TokenStore { let account_addr = signer::address_of(account); @@ -572,42 +734,14 @@ module aptos_token::token { direct_deposit(account_addr, token); } - /// Deposit the token balance into the recipients account and emit an event. - fun direct_deposit(account_addr: address, token: Token) acquires TokenStore { - assert!(token.amount > 0, error::invalid_argument(ETOKEN_CANNOT_HAVE_ZERO_AMOUNT)); - let token_store = borrow_global_mut<TokenStore>(account_addr); - - event::emit_event<DepositEvent>( - &mut token_store.deposit_events, - DepositEvent { id: token.id, amount: token.amount }, - ); - - assert!( - exists<TokenStore>(account_addr), - error::not_found(ETOKEN_STORE_NOT_PUBLISHED), - ); - - if (!table::contains(&token_store.tokens, token.id)) { - table::add(&mut token_store.tokens, token.id, token); - } else { - let recipient_token = table::borrow_mut(&mut token_store.tokens, token.id); - merge(recipient_token, token); - }; - } - public fun direct_transfer( sender: &signer, receiver: &signer, token_id: TokenId, amount: u64, - ) acquires TokenStore { - let token = withdraw_token(sender, token_id, amount); - deposit_token(receiver, token); - } - - /// Deprecated function call - public fun initialize_token(_account: &signer, _token_id: TokenId) { - abort 0 + ) acquires TokenStore { + let token = withdraw_token(sender, token_id, amount); + deposit_token(receiver, token); } public fun initialize_token_store(account: &signer) { @@ -700,41 +834,6 @@ module aptos_token::token { withdraw_with_event_internal(account_addr, id, amount) } - fun withdraw_with_event_internal( - account_addr: address, - id: TokenId, - amount: u64, - ): Token acquires TokenStore { - // It does not make sense to withdraw 0 tokens. - assert!(amount > 0, error::invalid_argument(EWITHDRAW_ZERO)); - // Make sure the account has sufficient tokens to withdraw. - assert!(balance_of(account_addr, id) >= amount, error::invalid_argument(EINSUFFICIENT_BALANCE)); - - assert!( - exists<TokenStore>(account_addr), - error::not_found(ETOKEN_STORE_NOT_PUBLISHED), - ); - - let token_store = borrow_global_mut<TokenStore>(account_addr); - event::emit_event<WithdrawEvent>( - &mut token_store.withdraw_events, - WithdrawEvent { id, amount }, - ); - let tokens = &mut borrow_global_mut<TokenStore>(account_addr).tokens; - assert!( - table::contains(tokens, id), - error::not_found(ENO_TOKEN_IN_TOKEN_STORE), - ); - // balance > amount and amount > 0 indirectly asserted that balance > 0. - let balance = &mut table::borrow_mut(tokens, id).amount; - if (*balance > amount) { - *balance = *balance - amount; - Token { id, amount, token_properties: property_map::empty() } - } else { - table::remove(tokens, id) - } - } - // // Public functions for creating and maintaining tokens // @@ -946,303 +1045,96 @@ module aptos_token::token { public fun create_token_mutability_config(mutate_setting: &vector<bool>): TokenMutabilityConfig { TokenMutabilityConfig { maximum: *vector::borrow(mutate_setting, TOKEN_MAX_MUTABLE_IND), - uri: *vector::borrow(mutate_setting, TOKEN_URI_MUTABLE_IND), - royalty: *vector::borrow(mutate_setting, TOKEN_ROYALTY_MUTABLE_IND), - description: *vector::borrow(mutate_setting, TOKEN_DESCRIPTION_MUTABLE_IND), - properties: *vector::borrow(mutate_setting, TOKEN_PROPERTY_MUTABLE_IND), - } - } - - public fun create_collection_mutability_config(mutate_setting: &vector<bool>): CollectionMutabilityConfig { - CollectionMutabilityConfig { - description: *vector::borrow(mutate_setting, COLLECTION_DESCRIPTION_MUTABLE_IND), - uri: *vector::borrow(mutate_setting, COLLECTION_URI_MUTABLE_IND), - maximum: *vector::borrow(mutate_setting, COLLECTION_MAX_MUTABLE_IND), - } - } - - /// create token with raw inputs - public entry fun create_token_script( - account: &signer, - collection: String, - name: String, - description: String, - balance: u64, - maximum: u64, - uri: String, - royalty_payee_address: address, - royalty_points_denominator: u64, - royalty_points_numerator: u64, - mutate_setting: vector<bool>, - property_keys: vector<String>, - property_values: vector<vector<u8>>, - property_types: vector<String> - ) acquires Collections, TokenStore { - let token_mut_config = create_token_mutability_config(&mutate_setting); - - let tokendata_id = create_tokendata( - account, - collection, - name, - description, - maximum, - uri, - royalty_payee_address, - royalty_points_denominator, - royalty_points_numerator, - token_mut_config, - property_keys, - property_values, - property_types - ); - - mint_token( - account, - tokendata_id, - balance, - ); - } - - public fun mint_token( - account: &signer, - token_data_id: TokenDataId, - amount: u64, - ): TokenId acquires Collections, TokenStore { - assert!(token_data_id.creator == signer::address_of(account), error::permission_denied(ENO_MINT_CAPABILITY)); - let creator_addr = token_data_id.creator; - let all_token_data = &mut borrow_global_mut<Collections>(creator_addr).token_data; - assert!(table::contains(all_token_data, token_data_id), error::not_found(ETOKEN_DATA_NOT_PUBLISHED)); - let token_data = table::borrow_mut(all_token_data, token_data_id); - - if (token_data.maximum > 0) { - assert!(token_data.supply + amount <= token_data.maximum, error::invalid_argument(EMINT_WOULD_EXCEED_TOKEN_MAXIMUM)); - token_data.supply = token_data.supply + amount; - }; - - // we add more tokens with property_version 0 - let token_id = create_token_id(token_data_id, 0); - deposit_token(account, - Token { - id: token_id, - amount, - token_properties: property_map::empty(), // same as default properties no need to store - } - ); - event::emit_event<MintTokenEvent>( - &mut borrow_global_mut<Collections>(creator_addr).mint_token_events, - MintTokenEvent { - id: token_data_id, - amount, - } - ); - - token_id - } - - /// create tokens and directly deposite to receiver's address. The receiver should opt-in direct transfer - public fun mint_token_to( - account: &signer, - receiver: address, - token_data_id: TokenDataId, - amount: u64, - ) acquires Collections, TokenStore { - assert!(exists<TokenStore>(receiver), error::not_found(ETOKEN_STORE_NOT_PUBLISHED)); - let opt_in_transfer = borrow_global<TokenStore>(receiver).direct_transfer; - assert!(opt_in_transfer, error::permission_denied(EUSER_NOT_OPT_IN_DIRECT_TRANSFER)); - - assert!(token_data_id.creator == signer::address_of(account), error::permission_denied(ENO_MINT_CAPABILITY)); - let creator_addr = token_data_id.creator; - let all_token_data = &mut borrow_global_mut<Collections>(creator_addr).token_data; - assert!(table::contains(all_token_data, token_data_id), error::not_found(ETOKEN_DATA_NOT_PUBLISHED)); - let token_data = table::borrow_mut(all_token_data, token_data_id); - - if (token_data.maximum > 0) { - assert!(token_data.supply + amount <= token_data.maximum, error::invalid_argument(EMINT_WOULD_EXCEED_TOKEN_MAXIMUM)); - token_data.supply = token_data.supply + amount; - }; - - // we add more tokens with property_version 0 - let token_id = create_token_id(token_data_id, 0); - direct_deposit(receiver, - Token { - id: token_id, - amount, - token_properties: property_map::empty(), // same as default properties no need to store - } - ); - - event::emit_event<MintTokenEvent>( - &mut borrow_global_mut<Collections>(creator_addr).mint_token_events, - MintTokenEvent { - id: token_data_id, - amount, - } - ); - } - - /// Burn a token by creator when the token's BURNABLE_BY_CREATOR is true - /// The token is owned at address owner - public entry fun burn_by_creator( - creator: &signer, - owner: address, - collection: String, - name: String, - property_version: u64, - amount: u64, - ) acquires Collections, TokenStore { - let creator_address = signer::address_of(creator); - assert!(amount > 0, error::invalid_argument(ENO_BURN_TOKEN_WITH_ZERO_AMOUNT)); - let token_id = create_token_id_raw(creator_address, collection, name, property_version); - let creator_addr = token_id.token_data_id.creator; - assert!( - exists<Collections>(creator_addr), - error::not_found(ECOLLECTIONS_NOT_PUBLISHED), - ); - - let collections = borrow_global_mut<Collections>(creator_address); - assert!( - table::contains(&collections.token_data, token_id.token_data_id), - error::not_found(ETOKEN_DATA_NOT_PUBLISHED), - ); - - let token_data = table::borrow_mut( - &mut collections.token_data, - token_id.token_data_id, - ); - - // The property should be explicitly set in the property_map for creator to burn the token - assert!( - property_map::contains_key(&token_data.default_properties, &string::utf8(BURNABLE_BY_CREATOR)), - error::permission_denied(ECREATOR_CANNOT_BURN_TOKEN) - ); - - let burn_by_creator_flag = property_map::read_bool(&token_data.default_properties, &string::utf8(BURNABLE_BY_CREATOR)); - assert!(burn_by_creator_flag, error::permission_denied(ECREATOR_CANNOT_BURN_TOKEN)); - - // Burn the tokens. - let Token { id: _, amount: burned_amount, token_properties: _ } = withdraw_with_event_internal(owner, token_id, amount); - let token_store = borrow_global_mut<TokenStore>(owner); - event::emit_event<BurnTokenEvent>( - &mut token_store.burn_events, - BurnTokenEvent { id: token_id, amount: burned_amount }, - ); - - if (token_data.maximum > 0) { - token_data.supply = token_data.supply - burned_amount; - - // Delete the token_data if supply drops to 0. - if (token_data.supply == 0) { - destroy_token_data(table::remove(&mut collections.token_data, token_id.token_data_id)); - - // update the collection supply - let collection_data = table::borrow_mut( - &mut collections.collection_data, - token_id.token_data_id.collection - ); - collection_data.supply = collection_data.supply - 1; - // delete the collection data if the collection supply equals 0 - if (collection_data.supply == 0) { - destroy_collection_data(table::remove(&mut collections.collection_data, collection_data.name)); - }; - }; - }; - } - - /// Burn a token by the token owner - public entry fun burn( - owner: &signer, - creators_address: address, - collection: String, - name: String, - property_version: u64, - amount: u64 - ) acquires Collections, TokenStore { - assert!(amount > 0, error::invalid_argument(ENO_BURN_TOKEN_WITH_ZERO_AMOUNT)); - let token_id = create_token_id_raw(creators_address, collection, name, property_version); - let creator_addr = token_id.token_data_id.creator; - assert!( - exists<Collections>(creator_addr), - error::not_found(ECOLLECTIONS_NOT_PUBLISHED), - ); + uri: *vector::borrow(mutate_setting, TOKEN_URI_MUTABLE_IND), + royalty: *vector::borrow(mutate_setting, TOKEN_ROYALTY_MUTABLE_IND), + description: *vector::borrow(mutate_setting, TOKEN_DESCRIPTION_MUTABLE_IND), + properties: *vector::borrow(mutate_setting, TOKEN_PROPERTY_MUTABLE_IND), + } + } - let collections = borrow_global_mut<Collections>(creator_addr); - assert!( - table::contains(&collections.token_data, token_id.token_data_id), - error::not_found(ETOKEN_DATA_NOT_PUBLISHED), - ); + public fun create_collection_mutability_config(mutate_setting: &vector<bool>): CollectionMutabilityConfig { + CollectionMutabilityConfig { + description: *vector::borrow(mutate_setting, COLLECTION_DESCRIPTION_MUTABLE_IND), + uri: *vector::borrow(mutate_setting, COLLECTION_URI_MUTABLE_IND), + maximum: *vector::borrow(mutate_setting, COLLECTION_MAX_MUTABLE_IND), + } + } - let token_data = table::borrow_mut( - &mut collections.token_data, - token_id.token_data_id, - ); + public fun mint_token( + account: &signer, + token_data_id: TokenDataId, + amount: u64, + ): TokenId acquires Collections, TokenStore { + assert!(token_data_id.creator == signer::address_of(account), error::permission_denied(ENO_MINT_CAPABILITY)); + let creator_addr = token_data_id.creator; + let all_token_data = &mut borrow_global_mut<Collections>(creator_addr).token_data; + assert!(table::contains(all_token_data, token_data_id), error::not_found(ETOKEN_DATA_NOT_PUBLISHED)); + let token_data = table::borrow_mut(all_token_data, token_data_id); - assert!( - property_map::contains_key(&token_data.default_properties, &string::utf8(BURNABLE_BY_OWNER)), - error::permission_denied(EOWNER_CANNOT_BURN_TOKEN) - ); - let burn_by_owner_flag = property_map::read_bool(&token_data.default_properties, &string::utf8(BURNABLE_BY_OWNER)); - assert!(burn_by_owner_flag, error::permission_denied(EOWNER_CANNOT_BURN_TOKEN)); + if (token_data.maximum > 0) { + assert!(token_data.supply + amount <= token_data.maximum, error::invalid_argument(EMINT_WOULD_EXCEED_TOKEN_MAXIMUM)); + token_data.supply = token_data.supply + amount; + }; - // Burn the tokens. - let Token { id: _, amount: burned_amount, token_properties: _ } = withdraw_token(owner, token_id, amount); - let token_store = borrow_global_mut<TokenStore>(signer::address_of(owner)); - event::emit_event<BurnTokenEvent>( - &mut token_store.burn_events, - BurnTokenEvent { id: token_id, amount: burned_amount }, + // we add more tokens with property_version 0 + let token_id = create_token_id(token_data_id, 0); + deposit_token(account, + Token { + id: token_id, + amount, + token_properties: property_map::empty(), // same as default properties no need to store + } ); - - // Decrease the supply correspondingly by the amount of tokens burned. - let token_data = table::borrow_mut( - &mut collections.token_data, - token_id.token_data_id, + event::emit_event<MintTokenEvent>( + &mut borrow_global_mut<Collections>(creator_addr).mint_token_events, + MintTokenEvent { + id: token_data_id, + amount, + } ); - // only update the supply if we tracking the supply and maximal - // maximal == 0 is reserved for unlimited token and collection with no tracking info. - if (token_data.maximum > 0) { - token_data.supply = token_data.supply - burned_amount; + token_id + } - // Delete the token_data if supply drops to 0. - if (token_data.supply == 0) { - destroy_token_data(table::remove(&mut collections.token_data, token_id.token_data_id)); + /// create tokens and directly deposite to receiver's address. The receiver should opt-in direct transfer + public fun mint_token_to( + account: &signer, + receiver: address, + token_data_id: TokenDataId, + amount: u64, + ) acquires Collections, TokenStore { + assert!(exists<TokenStore>(receiver), error::not_found(ETOKEN_STORE_NOT_PUBLISHED)); + let opt_in_transfer = borrow_global<TokenStore>(receiver).direct_transfer; + assert!(opt_in_transfer, error::permission_denied(EUSER_NOT_OPT_IN_DIRECT_TRANSFER)); - // update the collection supply - let collection_data = table::borrow_mut( - &mut collections.collection_data, - token_id.token_data_id.collection - ); - collection_data.supply = collection_data.supply - 1; - // delete the collection data if the collection supply equals 0 - if (collection_data.supply == 0) { - destroy_collection_data(table::remove(&mut collections.collection_data, collection_data.name)); - }; - }; + assert!(token_data_id.creator == signer::address_of(account), error::permission_denied(ENO_MINT_CAPABILITY)); + let creator_addr = token_data_id.creator; + let all_token_data = &mut borrow_global_mut<Collections>(creator_addr).token_data; + assert!(table::contains(all_token_data, token_data_id), error::not_found(ETOKEN_DATA_NOT_PUBLISHED)); + let token_data = table::borrow_mut(all_token_data, token_data_id); + + if (token_data.maximum > 0) { + assert!(token_data.supply + amount <= token_data.maximum, error::invalid_argument(EMINT_WOULD_EXCEED_TOKEN_MAXIMUM)); + token_data.supply = token_data.supply + amount; }; - } - fun destroy_token_data(token_data: TokenData) { - let TokenData { - maximum: _, - largest_property_version: _, - supply: _, - uri: _, - royalty: _, - name: _, - description: _, - default_properties: _, - mutability_config: _, - } = token_data; - } + // we add more tokens with property_version 0 + let token_id = create_token_id(token_data_id, 0); + direct_deposit(receiver, + Token { + id: token_id, + amount, + token_properties: property_map::empty(), // same as default properties no need to store + } + ); - fun destroy_collection_data(collection_data: CollectionData) { - let CollectionData { - description: _, - name: _, - uri: _, - supply: _, - maximum: _, - mutability_config: _, - } = collection_data; + event::emit_event<MintTokenEvent>( + &mut borrow_global_mut<Collections>(creator_addr).mint_token_events, + MintTokenEvent { + id: token_data_id, + amount, + } + ); } public fun create_token_id(token_data_id: TokenDataId, property_version: u64): TokenId { @@ -1362,6 +1254,107 @@ module aptos_token::token { token_data.uri } + // + // Private functions + // + fun destroy_token_data(token_data: TokenData) { + let TokenData { + maximum: _, + largest_property_version: _, + supply: _, + uri: _, + royalty: _, + name: _, + description: _, + default_properties: _, + mutability_config: _, + } = token_data; + } + + fun destroy_collection_data(collection_data: CollectionData) { + let CollectionData { + description: _, + name: _, + uri: _, + supply: _, + maximum: _, + mutability_config: _, + } = collection_data; + } + + fun withdraw_with_event_internal( + account_addr: address, + id: TokenId, + amount: u64, + ): Token acquires TokenStore { + // It does not make sense to withdraw 0 tokens. + assert!(amount > 0, error::invalid_argument(EWITHDRAW_ZERO)); + // Make sure the account has sufficient tokens to withdraw. + assert!(balance_of(account_addr, id) >= amount, error::invalid_argument(EINSUFFICIENT_BALANCE)); + + assert!( + exists<TokenStore>(account_addr), + error::not_found(ETOKEN_STORE_NOT_PUBLISHED), + ); + + let token_store = borrow_global_mut<TokenStore>(account_addr); + event::emit_event<WithdrawEvent>( + &mut token_store.withdraw_events, + WithdrawEvent { id, amount }, + ); + let tokens = &mut borrow_global_mut<TokenStore>(account_addr).tokens; + assert!( + table::contains(tokens, id), + error::not_found(ENO_TOKEN_IN_TOKEN_STORE), + ); + // balance > amount and amount > 0 indirectly asserted that balance > 0. + let balance = &mut table::borrow_mut(tokens, id).amount; + if (*balance > amount) { + *balance = *balance - amount; + Token { id, amount, token_properties: property_map::empty() } + } else { + table::remove(tokens, id) + } + } + + fun update_token_property_internal( + token_owner: address, + token_id: TokenId, + keys: vector<String>, + values: vector<vector<u8>>, + types: vector<String>, + ) acquires TokenStore { + let tokens = &mut borrow_global_mut<TokenStore>(token_owner).tokens; + assert!(table::contains(tokens, token_id), error::not_found(ENO_TOKEN_IN_TOKEN_STORE)); + + let value = &mut table::borrow_mut(tokens, token_id).token_properties; + + property_map::update_property_map(value, keys, values, types); + } + + /// Deposit the token balance into the recipients account and emit an event. + fun direct_deposit(account_addr: address, token: Token) acquires TokenStore { + assert!(token.amount > 0, error::invalid_argument(ETOKEN_CANNOT_HAVE_ZERO_AMOUNT)); + let token_store = borrow_global_mut<TokenStore>(account_addr); + + event::emit_event<DepositEvent>( + &mut token_store.deposit_events, + DepositEvent { id: token.id, amount: token.amount }, + ); + + assert!( + exists<TokenStore>(account_addr), + error::not_found(ETOKEN_STORE_NOT_PUBLISHED), + ); + + if (!table::contains(&token_store.tokens, token.id)) { + table::add(&mut token_store.tokens, token.id, token); + } else { + let recipient_token = table::borrow_mut(&mut token_store.tokens, token.id); + merge(recipient_token, token); + }; + } + // ****************** TEST-ONLY FUNCTIONS ************** #[test(creator = @0x1, owner = @0x2)] @@ -2106,4 +2099,16 @@ module aptos_token::token { token_properties: _, } = token; } + + // + // Deprecated functions + // + + public entry fun initialize_token_script(_account: &signer) { + abort 0 + } + + public fun initialize_token(_account: &signer, _token_id: TokenId) { + abort 0 + } }