Custom Transfers
Overview
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; };
- Event Emission - event::emit(BatchTransferEvent { from: sender, recipients, token_ids, amounts, collection_id, timestamp: tx_context::epoch(ctx) });
2. Batch Transfer
public entry fun batch_transfer_art20(
    tokens: vector<NFT>,
    recipient: address,
    quantity: u64,
    collection_cap: &CollectionCap,
    sender_balances: vector<UserBalance>,
    ctx: &mut TxContext
)Special Features
- Balance Management - Tracks user token balances 
- Automatically creates recipient balances 
- Handles balance updates atomically 
 
- Security Checks - // Collection verification assert!(token.collection_id == collection_cap_id, E_COLLECTION_MISMATCH); // Balance verification assert!(current_balance >= quantity, E_INSUFFICIENT_BALANCE); // Deny list check assert!(!is_denied(collection_cap, recipient), E_ADDRESS_DENIED);
- Batch Processing - Efficient handling of multiple transfers 
- Atomic updates across all operations 
- Event emission for batch transfers 
 
Integration Guide
1. Basic Transfer
// 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:
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
    );
}Last updated