The ART20 module implements a sophisticated transfer mechanism that goes beyond simple ownership changes. It manages user balances, collection integrity, deny lists, and batch operations while ensuring proper state updates across the protocol.
Transfer Functions
1. Standard Transfer
public entry fun transfer_art20(
tokens: vector<NFT>,
recipients: vector<address>,
collection_cap: &CollectionCap,
mut sender_balances: vector<UserBalance>,
ctx: &mut TxContext
)
Purpose
Enables secure transfer of NFTs while maintaining protocol state, balances, and collection integrity.
Parameters
tokens: Vector of NFTs to transfer
recipients: Vector of recipient addresses
collection_cap: Collection capability for validation
sender_balances: Vector of sender's balance objects
ctx: Transaction context
Process Flow
Initial Validation
let sender = tx_context::sender(ctx);
let token_count = vector::length(&tokens);
let recipient_count = vector::length(&recipients);
assert!(token_count == recipient_count, E_INVALID_LENGTH);
Balance Verification
let mut total_available = 0u64;
let mut i = 0;
while (i < vector::length(&sender_balances)) {
let balance = vector::borrow(&sender_balances, i);
total_available = safe_add(total_available, balance.balance);
i = i + 1;
};
assert!(total_available >= token_count, E_INSUFFICIENT_BALANCE);
Deny List Check
check_deny_list_restrictions(collection_cap, sender);
for (recipient in recipients) {
check_deny_list_restrictions(collection_cap, recipient);
}
Transfer Execution
let mut i = 0;
while (i < token_count) {
let recipient = *vector::borrow(&recipients, i);
let token = vector::pop_back(&mut tokens);
// Update balances
update_balances(&mut sender_balances, token_count);
// Create recipient balance if needed
let recipient_balance = create_user_balance(
collection_id,
1,
ctx
);
// Transfer token and balance
transfer::public_transfer(token, recipient);
transfer::transfer(recipient_balance, recipient);
i = i + 1;
};
// Transfer single NFT
public fun transfer_single(
nft: NFT,
recipient: address,
collection_cap: &CollectionCap,
user_balance: &mut UserBalance,
ctx: &mut TxContext
) {
let tokens = vector::singleton(nft);
let recipients = vector::singleton(recipient);
let balances = vector::singleton(user_balance);
transfer_art20(tokens, recipients, collection_cap, balances, ctx);
}
2. Batch Transfer Example
// Transfer multiple NFTs to single recipient
public fun transfer_batch(
nfts: vector<NFT>,
recipient: address,
quantity: u64,
collection_cap: &CollectionCap,
user_balance: &mut UserBalance,
ctx: &mut TxContext
) {
let balances = vector::singleton(user_balance);
batch_transfer_art20(nfts, recipient, quantity, collection_cap, balances, ctx);
}
Error Handling
Common errors and their meanings:
Error Code
Description
Solution
E_INSUFFICIENT_BALANCE
Sender lacks required balance
Verify balance before transfer
E_COLLECTION_MISMATCH
NFT doesn't belong to collection
Check collection ID
E_ADDRESS_DENIED
Address in deny list
Verify recipient status
E_INVALID_LENGTH
Mismatched vectors
Ensure equal tokens and recipients
Events
TransferEvent
Emitted for individual transfers
Tracks from/to addresses, token ID, amount
BatchTransferEvent
Emitted for batch transfers
Includes all recipients and token IDs
Records timestamp and collection ID
Best Practices
Balance Management
Always verify balances before transfer
Handle balance updates atomically
Clean up empty balances
Security
Implement deny list checks
Verify collection capability
Validate all addresses
Gas Optimization
Use batch transfers when possible
Clean up unused resources
Minimize storage operations
Common Integration Patterns
Marketplace Integration
public fun list_for_sale(
nft: NFT,
price: u64,
collection_cap: &CollectionCap,
user_balance: &mut UserBalance,
ctx: &mut TxContext
) {
// Verify ownership and balance
// Transfer to marketplace contract
transfer_art20(
vector::singleton(nft),
vector::singleton(get_marketplace_address()),
collection_cap,
vector::singleton(user_balance),
ctx
);
}
Game Integration
public fun transfer_to_game(
game_nfts: vector<NFT>,
player: address,
collection_cap: &CollectionCap,
user_balance: &mut UserBalance,
ctx: &mut TxContext
) {
// Update game state
// Transfer NFTs to player
batch_transfer_art20(
game_nfts,
player,
vector::length(&game_nfts),
collection_cap,
vector::singleton(user_balance),
ctx
);
}