Skip to content

Latest commit

 

History

History
111 lines (86 loc) · 7 KB

1_Registration_Binding.md

File metadata and controls

111 lines (86 loc) · 7 KB

Registration Binding

When a new user registers to use the product, a SoulBound Token (SBT), which can be understood as a non-transferable NFT, can be sent to their on-chain address to bind the registration information.

Admin Privileges AdminCap

Compared to the permissionless mint function in the profile_clock example code from the first unit, a more common implementation is to use administrative privileges to register users after receiving registration information.

The sign_up example code changes the original permissionless registration method to one that requires administrative privileges.

First, an AdminCap administrative privilege Object is defined.

public struct AdminCap has key, store {
    id: UID,
}

The functionality to generate AdminCap is placed within the init function, which is a function that is automatically executed only once when the contract is deployed.

fun init(ctx: &mut TxContext) {
    let admin = AdminCap {
        id: object::new(ctx),
    };
    transfer::public_transfer(admin, ctx.sender());
}

This function generates AdminCap and sends it to the address of the user who deployed the contract.

In the original mint function, &AdminCap is added as an input parameter, which is not actually used, so the parameter name _admin is prefixed with an underscore. When calling the mint function, the Object ID of &AdminCap must be input; if it is not called from the address with AdminCap, an error will occur. This restricts the mint function to be callable only by administrators.

public fun mint(_admin: &AdminCap, handle: String, recipient: address, ctx: &mut TxContext) {
    let profile = new(handle, ctx);
    transfer::transfer(profile, recipient);
}

NFT vs SBT

In this new example code, a new question arises: why are transfer::transfer and transfer::public_transfer used sometimes to transfer assets, and what is the difference between them?

In Sui, Objects can be divided into Objects that can be freely transferred and traded, such as NFTs and Coins, and Objects that cannot be freely transferred and traded, which are suitable for SBTs.

In the example code, AdminCap includes both key and store capabilities when defined, belonging to Objects that can be freely transferred.

public struct AdminCap has key, store {
    id: UID,
}

While Profile only has the key capability, belonging to Objects that cannot be freely transferred.

public struct Profile has key {
    id: UID,
    handle: String,
    points: u64,
    last_time: u64,
}

For Objects that can be freely transferred, the public_transfer, public_freeze_object, public_share_object methods of the transfer module can be used in PTB or any other contract to handle the Object, which can be forwarded to other accounts, becoming shared or frozen.

However, for Objects that cannot be freely transferred, only the transfer, freeze_object, share_object methods of the transfer module can be used within the module where the Object is defined to handle it.

Table and Deduplication

When registering a user account, it is necessary to avoid duplicate registrations for the same account or address. This can be achieved using the Table data structure for deduplication. The example program defines an additional data structure specifically for recording registered account information on top of the original code.

public struct HandleRecord has key {
    id: UID,
    record: Table<String, bool>,
}

When registering a new account, the handle information is used as the key for registration.

public fun mint(
    _admin: &AdminCap, 
    handle_record: &mut HandleRecord, 
    handle: String, 
    recipient: address, 
    ctx: &mut TxContext
) {
    table::add<String, bool>(&mut handle_record.record, handle, true);
    let profile = new(handle, ctx);
    transfer::transfer(profile, recipient);
}

Here, table::add<String, bool>(&mut handle_record.record, handle, true); will fail to add and roll back the program execution if there is a duplicate handle; the true boolean value is just a placeholder and has no actual meaning.

Assignment

When checking for duplicate registrations, not only should handle be deduplicated, but also address. Based on the example code in this section, add an additional data structure for checking address duplicates. Prohibit registered handle and address from registering again.

Table, TableVec, VecMap, VecSet, vector

If it's just for deduplication, according to conventional programming habits, data structures like sets should be used, and Sui has defined similar data structures, such as VecMap, VecSet. So why did we choose the Table data structure for implementation?

On Sui, any Object has a data storage limit. Both VecMap and VecSet are built based on the vector data structure and belong to a single Object. When too much data is stored, it will no longer be callable.

However, Table and TableVec create new data as individual Objects when adding new data, and then bind the ownership of the newly added data to Table or TableVec using the dynamic_field dynamic attribute. This supports these two data structures to add data without limits.

Assignment

Read the source code of these data structures:

If our application has a referral registration feature and needs to record the referral registration relationship between users on the blockchain, try to choose the most suitable one from these data structures to build, and write the Sui Move contract code.