Airdropper Contract in Aptos | Move dApp Extreme Beginners

Source code address:

https://github.com/NonceGeek/Airdropper-Based-On-Github

0x01 One sentence in brief

Airdropper contracts are Aptos-based airdrops - you can airdrop coins against a list of Aptos addresses.

It's also an extension of MoveDID - MoveDID binds Aptos, Ethereum, and other formats to a Github Account, allowing you to bulk airdrop all the Contributors in a Repo or Organization. Contributors under a Repo or Organization.

0x02 Basic Contract Structure

module my_addr::airdropper {
    use aptos_framework::coin;
    use aptos_framework::timestamp;
    use aptos_framework::account; use aptos_framework::account; use aptos_framework::account
    use aptos_framework::account::SignerCapability; use aptos_framework::account::SignerCapability; use aptos_framework::account
    use aptos_framework::guid; use aptos_framework::account::SignerCapability

    use std::error;; use std::signer;:SignerCapability
    use std::signer;; use std::vector;; use std::vector
    use std::vector; use std::option
    use std::option::Option; use std::string; use std::{String}
    use std::vector; use std::option::Option; use std::string::{String};

    use aptos_std::event::{Self, EventHandle}; use aptos_std::event::{Self, EventHandle}
    use aptos_std::table::{Self, Table}; use aptos_std::table::{Self, Table}; use aptos_std::table::{Self, Table}

    use aptos_token::token::{Token, TokenId}; use aptos_std::table::{Self, Table}; use aptos_std::table::{Self, Table}

    
    const EINVALID_OWNER: u64 = 2;
    const EOWNER_NOT_HAVING_ENOUGH_COIN: u64 = 3;
    
    const EAIRDROPER_NOT_RECEIVER: u64 = 5;
    const ERROR_NOT_ENOUGH_LENGTH: u64 = 6;


    struct AirdropCap has key {
   cap: SignerCapability, struct AirdropCap has key {
  }

    struct AirdropCoinEvent has drop, store {
        sender_address: address, description: String, }
        description: String, coin_amount: u64, }
        coin_amount: u64, timestamp: u64, String
        
        receiver_address: address, }
    }

    struct AirdropTokenEvent has drop, store {
        airdrop_id: u64, sender_address: address, } struct AirdropTokenEvent has drop, store {
        sender_address: address, token_id: TokenId, }
        
        amount: u64,
        timestamp: u64,
        receiver_address: address, }
    }

    struct ClaimTokenEvent has drop, store {
        airdrop_id: u64, sender_address: address, } struct ClaimTokenEvent has drop, store {
        sender_address: address, token_id: TokenId, }
        token_id: TokenId, amount: u64,
        amount: u64,
        claimer_address: address, token_id: TokenId, amount: u64, timestamp: u64,
        claimer_address: address, }
    }

    struct AirdropTokenItem has store {
        airdrop_id: u64, locked_token: Option, } struct AirdropTokenItem has store {
        locked_token: Option, timestamp: u64,
        locked_token: Option, timestamp: u64,
        sender_address: address, receiver_address: address, receiver_tokens
        receiver_address: address, }
    }

    struct AirdropItemsData has key {
        airdrop_items: Table,
        airdrop_coin_events: EventHandle, }
        airdrop_token_events: EventHandle,
        claim_token_events: EventHandle,
    }

    // get signer unique id for airdrop
    fun get_unique_airdrop_id() : u64 acquires AirdropCap {
    ......
        airdrop_id
    }

    fun get_airdrop_signer_address() : address acquires AirdropCap {
    ......
        airdrop_signer_address
    }

    // call after you deploy
    public entry fun initialize_script(sender: &signer) {
        ......
    }

    fun airdrop_coin(
        sender: &signer, description: String, }
        sender: &signer, description: String, receiver.
        description: String, receiver: address, amount: u64, }
        amount: u64.
    ) acquires AirdropCap, AirdropItemsData {
        ......
    }

    public entry fun airdrop_coins_average_script(
        sender: &signer, description: String, Airdrop_coins_average_script(
        description: String,
        receivers: vector
, uint_amount: u64, uint_count: u64, uint_amount: u64, uint_count: u64 uint_amount: u64, ) acquires AirdropCap ) acquires AirdropCap, AirdropItemsData { l...... } public entry fun airdrop_coins_not_average_script( sender: &signer, description: String, Airdrop_coins_not_average_script description: String, amounts: vector, . ) acquires AirdropCap, AirdropItemsData { ...... } # tests }

0x03 Key Functions

3.1 Entry function:public entry fun

List of entry functions:

  • public entry fun initialize_script(sender: &signer) : Initialization AirdropCapThe
  • public entry fun airdrop_coins_average_script(......)acquires AirdropCap, AirdropItemsData: Equalization of airdrops
  • public entry fun airdrop_coins_not_average_script(......)acquires AirdropCap, AirdropItemsData : unequal airdrops

3.1.1 Knowledge Points for Function Modifiers - It is important to check the security of function modifiers.

https://aptos.dev/guides/move-guides/move-on-aptos/#visibility

  • By default, functions are private, which means they can only be called by other functions in the same file. You can make the function available outside the file by using the visibility modifier [public, public(entry), etc.]. For example:
  • entry - isolates the calling function by making it an actual entry function, preventing re-entry (leading to compiler errors)
  • public - allows anyone to call the function from anywhere
  • public(entry) - allows only the method defined in the relevant transaction to call the function
  • public(friend) - Used to declare which modules the current module trusts.
  • public(script) - Allows submission, compilation and execution of arbitrary Move code on the Aptos network.

3.1.2 Knowledge Points Acquires

https://aptos.dev/guides/move-guides/move-structure/#acquires

Any time you need to use any global resource, such as a structure, you should acquire it first. For example, depositing and withdrawing an NFT both acquire the TokenStore.If you have a function in a different module that calls a function inside the module to acquire the resource, you don't have to mark the first function as acquires().

This makes ownership clear because the resources are stored within the account. The account can decide if the resource can be created there. The module that defines the resource has the right to read and modify the structure. Therefore code within that module needs to explicitly fetch the structure.

Nonetheless, anywhere you borrow or move in Move, you automatically acquire the resource. For clarity, use acquire to explicitly include. Again, the exists() function does not require the acquires() function.

Note: You can borrow global in a module from any account in a structure defined in your own module. You cannot borrow global outside the module.

3.1.3 public entry fun initialize_script(sender: &signer)

 public entry fun initialize_script(sender: &signer) {
       // 获取 sender 地址
        let sender_addr = signer::address_of(sender);
    // 需要 sender 和部署人一致
        assert!(sender_addr == @my_addr, error::invalid_argument(EINVALID_OWNER));
    // 创建资源账户: 
       //https://aptos.dev/guides/move-guides/mint-nft-cli/#2-use-resource-account-for-automation
       // https://aptos.dev/guides/resource-accounts/#:~:text=The%20easiest%20way%20to%20set,under%20the%20resource%20account's%20adddress.
        let (airdrop_signer, airdrop_cap) = account::create_resource_account(sender, x"01");
    // 获取资源账户地址
       let airdrop_signer_address = signer::address_of(&airdrop_signer);
  // 移动 AirdropCap 到 sender
  if(!exists<AirdropCap>(@my_addr)){
            move_to(sender, AirdropCap {
                cap: airdrop_cap
            })
  };
  // 移动 AirdropItemsData 到 airdrop_signer, AirdropItemsData 用于存储所有 Events
        if (!exists<AirdropItemsData>(airdrop_signer_address)) {
            move_to(&airdrop_signer, AirdropItemsData {
                airdrop_items: table::new(),
                airdrop_coin_events: account::new_event_handle<AirdropCoinEvent>(&airdrop_signer),
                airdrop_token_events: account::new_event_handle<AirdropTokenEvent>(&airdrop_signer),
                claim_token_events: account::new_event_handle<ClaimTokenEvent>(&airdrop_signer),
            });
  };
    }

💡 Resource Account

A resource account is a developer feature for managing resources independent of user-managed accounts, in particular for publishing modules and auto-signing transactions. For example, a developer can use a resource account to manage accounts used for module publishing, such as managing contracts. The contracts themselves do not require a signer after initialization. Resource accounts provide you with the means for modules to provide signers to other modules and to sign transactions on behalf of modules.

Typically, resource accounts are used for two main purposes:

Storage and segregation of resources: A module creates a resource account just to host a specific resource.Publish the module as a separate (resource) account:This is a building block in a decentralized design where no private key can control a resource account. Ownership (SignerCap) can be kept in another building block, such as governance.

3.1.4 public entry fun airdrop_coins_average_script

 public entry fun airdrop_coins_average_script(
        sender: &signer, description.
        
        receivers: vector
, uint_amount: u64, uint_count: u64, uint_amount: u64, uint_count: u64 uint_amount: u64, ) acquires AirdropCap ) acquires AirdropCap, AirdropItemsData { let sender_addr = signer::address_of(sender); let length_receiver = vector::length(&receivers); // Make sure the address amount is sufficient assert!(coin::balance(sender_addr) >= length_receiver * uint_amount, error::invalid_argument(EOWNER_NOT_HAVING_ENOUGH_COIN)); ; let i = length_receiver; // Loop through the drop while (i > 0) { let receiver_address = vector::pop_back(&mut receivers); airdrop_coin(sender, description, receiver_address, uint_amount); // Call the subfunction i = i - 1; } } }

3.1.5 public entry fun airdrop_coins_not_average_script

public entry fun airdrop_coins_not_average_script(
        sender: &signer, description.
        
        
        amounts: vector, .
    ) acquires AirdropCap, AirdropItemsData {
        let sender_addr = signer::address_of(sender);
        let length_receiver = vector::length(&receivers);
        let length_amounts = vector::length(&amounts);

        assert!(length_receiver == length_amounts, ERROR_NOT_ENOUGH_LENGTH).

        let y = length_amounts;

        // Get the total amount
        let total_amounts = 0;
        let calculation_amounts = amounts.

        while (y > 0) {
            let amount = vector::pop_back(&mut calculation_amounts);
            total_amount = total_amount + amount; let calculation_amounts = total_amount
            y = y - 1; }
        };

        // Determine if the amount is sufficient
        assert!(coin::balance(sender_addr) >= total_amount, error::invalid_argument(EOWNER_NOT_HAVING_ENOUGH_COIN));; // determine that the amount is sufficient.

        let i = length_receiver;
    // Loop through the drop
        while (i > 0) {
            let receiver_address = vector::pop_back(&mut receivers);
            let amount = vector::pop_back(&mut amounts);
            airdrop_coin(sender, description, receiver_address, amount);

            i = i - 1; }
        }
    }

4.2 Secondary functions:fun

4.2.1 Getting the Signer's Address - Resource Accounts

fun get_airdrop_signer_address() : address acquires AirdropCap {
        let airdrop_cap = &borrow_global(@my_addr).cap;
        let airdrop_signer = &account::create_signer_with_capability(airdrop_cap);
        let airdrop_signer_address = signer::address_of(airdrop_signer);

        airdrop_signer_address
    }

https://aptos.dev/guides/move-guides/move-on-aptos/#resource-accounts

💡 Signer acquisition method for resource accounts

Because Move models often need to know the signer of a transaction, Aptos provides resource accounts for assigning signer capabilities. Creating a resource account provides access to signer capabilities for automatic use. Signer capabilities can be retrieved by the signer of the resource account in conjunction with the address of the source account that created the resource account, or placed in module local storage. See the resource_signer_cap reference in create_nft_with_resource_account.move.

When you create a resource account, you also grant the accountSignatory capacity. The only field in the signer capability is the address of the signer. To see how we can create a signer from the signer capability, see the let resource_signer function in create_nft_with_resource_account.move.

The function of this resource account (resource account) is no different from a normal account, it's exactly the same. But he has no public or private key. And has the concept of signCap. (Who has the Cap, who can control the account), generally this is used in specialized data storage (such as NFT Market, each store has a corresponding resource account) and permission identification allocation (distinguish between vip and ordinary users).

-- Maintainer ff

https://github.com/aptos-labs/aptos-core/blob/916d8b40232040ce1eeefbb0278411c5007a26e8/aptos-move/move-examples/mint_nft/2-Using-Resource-Account/sources/create_nft_with_resource_account.move#L156

let resource_signer = account::create_signer_with_capability(&module_data.signer_cap);
let token_id = token::mint_token(&resource_signer, module_data.token_data_id, 1);
token::direct_transfer(&resource_signer, receiver, token_id, 1);

4.2.2 fun airdrop_coin

  fun airdrop_coin(
        sender: &signer, description.
        sender: &signer, description: String, receiver.
        receiver: address, amount: u64, coinType
        amount: u64.
    ) acquires AirdropCap, AirdropItemsData {
        let sender_addr = signer::address_of(sender);
        let airdrop_signer_address = get_airdrop_signer_address();
        let airdrop_items_data = borrow_global_mut(airdrop_signer_address);

        // Transfer operations
        coin::transfer(sender, receiver, amount);

       // Write to Event
        event::emit_event(
            &mut airdrop_items_data.airdrop_coin_events,
            AirdropCoinEvent {
                receiver_address: receiver,
                description: description,
                sender_address: sender_addr,
                coin_amount: amount,
                timestamp: timestamp::now_seconds(),
            }
        );
    }

0x04 Test Code

Test function usage  # [test] modifier.testin the program, such as in the following example:

#[test(aptos_framework = @0x1, airdrop = @my_addr, sender = @0xAE)]
public fun test_initialize_script(airdrop: &signer) {
  account::create_account_for_test(signer::address_of(airdrop));
  initialize_script(airdrop);
}

4.1 test_airdrop_coins_average_script

#[test(aptos_framework = @0x1, airdrop = @my_addr, sender = @0xAE, receiver_1 = @0xAF, receiver_2 = @0xB0)]
    public fun test_airdrop_coins_average_script(aptos_framework: &signer, airdrop: &signer, sender: &signer, receiver_1: &signer, receiver_2: & signer) acquires AirdropCap, AirdropItemsData {
        // Set the timestamp
        timestamp::set_time_has_started_for_testing(aptos_framework);

        // Create the test account
        account::create_account_for_test(signer::address_of(aptos_framework)).
        account::create_account_for_test(signer::address_of(airdrop)).
        account::create_account_for_test(signer::address_of(sender)); account::create_account_for_test(signer::address_of(sender));
        account::create_account_for_test(signer::address_of(receiver_1));
        account::create_account_for_test(signer::address_of(receiver_2)).

       // Create Fake Money
        coin::create_fake_money(aptos_framework, sender, 300);

       // Transfer and register tokens
        coin::transfer(aptos_framework, signer::address_of(sender), 300);

        coin::register(receiver_1);
        coin::register(receiver_2);

       // Initialize the script
        initialize_script(airdrop);

       // Execute the script
        airdrop_coins_average_script(
            sender, b "test", // Execute the script.
            b "test",
            vector
[signer::address_of(receiver_1), signer::address_of(receiver_2)], 150, ). assert!(coin::balance(signer::address_of(sender)) == 0, 1); assert! assert!(coin::balance(signer::address_of(receiver_1)) == 150, 1); assert! assert!(coin::balance(signer::address_of(receiver_2)) == 150, 1); assert! }

4.2 test_airdrop_coins_not_average_script

 #[test(aptos_framework = @0x1, airdrop = @my_addr, sender = @0xAE, receiver_1 = @0xAF, receiver_2 = @0xB0)]
    public fun test_airdrop_coins_not_average_script(aptos_framework: &signer, airdrop: &signer, sender: &signer, receiver_1: &signer, receiver_ 2: &signer) acquires AirdropCap, AirdropItemsData {
        // set timestamp
        timestamp::set_time_has_started_for_testing(aptos_framework);

        // create account
        account::create_account_for_test(signer::address_of(aptos_framework)); // create account.
        account::create_account_for_test(signer::address_of(airdrop));
        account::create_account_for_test(signer::address_of(sender)); account::create_account_for_test(signer::address_of(sender));
        account::create_account_for_test(signer::address_of(receiver_1));
        account::create_account_for_test(signer::address_of(receiver_2)).

        coin::create_fake_money(aptos_framework, sender, 300);
        coin::transfer(aptos_framework, signer::address_of(sender), 300);

        coin::register(receiver_1);
        coin::register(receiver_2);

        initialize_script(airdrop).

        airdrop_coins_not_average_script(
            sender,
            b "test",
            vector
[signer::address_of(receiver_1), signer::address_of(receiver_2)], vector[100, 200], . ). assert!(coin::balance(signer::address_of(sender)) == 0, 1); assert! assert!(coin::balance(signer::address_of(receiver_1)) == 100, 1); assert! assert!(coin::balance(signer::address_of(receiver_2)) == 200, 1); assert! }

All of the above content is reproduced from the Internet, does not represent the position of AptosNews, is not investment advice, investment risk, the market need to be cautious, such as infringement, please contact the administrator to delete.

Like (0)
Donate WeChat Sweep WeChat Sweep Alipay Sweep Alipay Sweep
Previous July 21, 2023 at 12:31 pm
Next July 21, 2023 at 12:49 pm

Related posts

Leave a Reply

Please Login to Comment
WeChat Sweep
Baidu Sweep

Subscribe to AptosNews

Subscribe to AptosNews to stay on top of Aptos.


This will close in 0 seconds

This site has no investment advice, investment risk, the market needs to be cautious.