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
+    }
 }