/// This module provides the foundation for transferring of Tokens module aptos_token::token_transfers { use std::signer; use std::string::String; use aptos_std::table_with_length::{Self, TableWithLength}; use aptos_token::token::{Self, Token, TokenId}; struct TokenTransfers has key { pending_claims: TableWithLength>, } fun initialize_token_transfers(account: &signer) { move_to( account, TokenTransfers { pending_claims: table_with_length::new>(), } ) } public entry fun offer_script( sender: signer, receiver: address, creator: address, collection: String, name: String, property_version: u64, amount: u64, ) acquires TokenTransfers { let token_id = token::create_token_id_raw(creator, collection, name, property_version); offer(&sender, receiver, token_id, amount); } // Make an entry into pending transfers and extract from gallery public fun offer( sender: &signer, receiver: address, token_id: TokenId, amount: u64, ) acquires TokenTransfers { let sender_addr = signer::address_of(sender); if (!exists(sender_addr)) { initialize_token_transfers(sender) }; let pending_claims = &mut borrow_global_mut(sender_addr).pending_claims; if (!table_with_length::contains(pending_claims, receiver)) { table_with_length::add(pending_claims, receiver, table_with_length::new()) }; let addr_pending_claims = table_with_length::borrow_mut(pending_claims, receiver); let token = token::withdraw_token(sender, token_id, amount); let token_id = *token::token_id(&token); if (table_with_length::contains(addr_pending_claims, token_id)) { let dst_token = table_with_length::borrow_mut(addr_pending_claims, token_id); token::merge(dst_token, token) } else { table_with_length::add(addr_pending_claims, token_id, token) } } public entry fun claim_script( receiver: signer, sender: address, creator: address, collection: String, name: String, property_version: u64, ) acquires TokenTransfers { let token_id = token::create_token_id_raw(creator, collection, name, property_version); claim(&receiver, sender, token_id); } // Pull from someone else's pending transfers and insert into our gallery public fun claim( receiver: &signer, sender: address, token_id: TokenId, ) acquires TokenTransfers { let receiver_addr = signer::address_of(receiver); let pending_claims = &mut borrow_global_mut(sender).pending_claims; let pending_tokens = table_with_length::borrow_mut(pending_claims, receiver_addr); let token = table_with_length::remove(pending_tokens, token_id); if (table_with_length::length(pending_tokens) == 0) { let real_pending_claims = table_with_length::remove(pending_claims, receiver_addr); table_with_length::destroy_empty(real_pending_claims) }; token::deposit_token(receiver, token) } public entry fun cancel_offer_script( sender: signer, receiver: address, creator: address, collection: String, name: String, property_version: u64, ) acquires TokenTransfers { let token_id = token::create_token_id_raw(creator, collection, name, property_version); cancel_offer(&sender, receiver, token_id); } // Extra from our pending_claims and return to gallery public fun cancel_offer( sender: &signer, receiver: address, token_id: TokenId, ) acquires TokenTransfers { let sender_addr = signer::address_of(sender); let pending_claims = &mut borrow_global_mut(sender_addr).pending_claims; let pending_tokens = table_with_length::borrow_mut(pending_claims, receiver); let token = table_with_length::remove(pending_tokens, token_id); if (table_with_length::length(pending_tokens) == 0) { let real_pending_claims = table_with_length::remove(pending_claims, receiver); table_with_length::destroy_empty(real_pending_claims) }; token::deposit_token(sender, token) } #[test(creator = @0x1, owner = @0x2)] public fun test_nft(creator: signer, owner: signer) acquires TokenTransfers { let token_id = create_token(&creator, 1); let creator_addr = signer::address_of(&creator); let owner_addr = signer::address_of(&owner); offer(&creator, owner_addr, token_id, 1); claim(&owner, creator_addr, token_id); offer(&owner, creator_addr, token_id, 1); cancel_offer(&owner, creator_addr, token_id); } #[test(creator = @0x1, owner0 = @0x2, owner1 = @0x3)] public fun test_editions( creator: signer, owner0: signer, owner1: signer, ) acquires TokenTransfers { let token_id = create_token(&creator, 2); let creator_addr = signer::address_of(&creator); let owner0_addr = signer::address_of(&owner0); let owner1_addr = signer::address_of(&owner1); offer(&creator, owner0_addr, token_id, 1); assert!(table_with_length::length(&borrow_global(creator_addr).pending_claims) == 1, 1); offer(&creator, owner1_addr, token_id, 1); assert!(table_with_length::length(&borrow_global(creator_addr).pending_claims) == 2, 2); claim(&owner0, creator_addr, token_id); assert!(table_with_length::length(&borrow_global(creator_addr).pending_claims) == 1, 3); claim(&owner1, creator_addr, token_id); assert!(table_with_length::length(&borrow_global(creator_addr).pending_claims) == 0, 4); offer(&owner0, owner1_addr, token_id, 1); claim(&owner1, owner0_addr, token_id); offer(&owner1, creator_addr, token_id, 1); offer(&owner1, creator_addr, token_id, 1); claim(&creator, owner1_addr, token_id); } #[test_only] public entry fun create_token(creator: &signer, amount: u64): TokenId { use std::string::{Self, String}; let collection_name = string::utf8(b"Hello, World"); let collection_mutation_setting = vector[false, false, false]; token::create_collection( creator, *&collection_name, string::utf8(b"Collection: Hello, World"), string::utf8(b"https://aptos.dev"), 1, collection_mutation_setting, ); let token_mutation_setting = vector[false, false, false, false, true]; let default_keys = vector[string::utf8(b"attack"), string::utf8(b"num_of_use")]; let default_vals = vector>[b"10", b"5"]; let default_types = vector[string::utf8(b"integer"), string::utf8(b"integer")]; token::create_token_script( creator, *&collection_name, string::utf8(b"Token: Hello, Token"), string::utf8(b"Hello, Token"), amount, amount, string::utf8(b"https://aptos.dev"), signer::address_of(creator), 100, 0, token_mutation_setting, default_keys, default_vals, default_types, ); token::create_token_id_raw( signer::address_of(creator), *&collection_name, string::utf8(b"Token: Hello, Token"), 0 ) } }