Ex 3. IOTA Rebased Contract ClubMem

unofficial site

- return to IOTA Rebased Useful Links

ClubMem Dapp

ClubMem Smart Contract

This is a first draft of the ClubMem smart contract which implements a simple membership system on the IOTA Rebased chain with three tiers of membership: Gold (5 IOTA), Silver (3 IOTA), and Bronze (2 IOTA). The contract processes membership payments and includes an admin fee mechanism for invalid payment amounts.

Contract Implementation

The contract is implemented in two files. The Move.toml file is at top level and the clubmem.move file is in a 'sources' folder. Lower down the page the commands to build and publish the contract are shown, as well as how to call it. There is a separate page about the Dapp for accessing it using a standard webpage and wallet.

Move.toml Configuration

[package]
name = "clubmem"
edition = "2024.beta"

[dependencies]
Iota = { git = "https://github.com/iotaledger/iota.git", subdir = "crates/iota-framework/packages/iota-framework", rev = "develop" }

[addresses]
clubmem = "0x0"

Contract Source (clubmem.move)

module clubmem::membership {
    use iota::coin::{Self, Coin};
    use iota::tx_context::{Self, TxContext};
    use iota::event;
    use iota::transfer;

    #[allow(duplicate_alias)]
    public struct MembershipAccepted has copy, drop {
        from: address,
        amount: u64,
        tier: u8
    }

    public struct MembershipRejected has copy, drop {
        from: address,
        original_amount: u64,
        refunded_amount: u64,
        admin_fee: u64,
        reason: vector
    }

    const ADMIN_FEE: u64 = 5000;    // 5000 nanos admin fee
    const TREASURY: address = @0xc196e256a58a1ea07e2cb27887090c3cf6a1ad1ec189c8a40575e7cde6c3dc4a;
    
    // Valid payment amounts in nanos (1 IOTA = 1,000,000,000 nanos)
    const GOLD_AMOUNT: u64 = 5000000000;     // 5 IOTA
    const SILVER_AMOUNT: u64 = 3000000000;   // 3 IOTA
    const BRONZE_AMOUNT: u64 = 2000000000;   // 2 IOTA

    public entry fun join(
        mut coin: Coin,
        ctx: &mut TxContext
    ) {
        let amount = coin::value(&coin);
        let sender = tx_context::sender(ctx);

        if (amount == GOLD_AMOUNT || amount == SILVER_AMOUNT || amount == BRONZE_AMOUNT) {
            let tier = if (amount == GOLD_AMOUNT) { 3 }
                      else if (amount == SILVER_AMOUNT) { 2 }
                      else { 1 };
                      
            transfer::public_transfer(coin, TREASURY);
            event::emit(MembershipAccepted {
                from: sender,
                amount,
                tier
            });
        } else {
            if (amount > ADMIN_FEE) {
                // Split the coin into admin fee and refund
                let admin_coin = coin::split(&mut coin, ADMIN_FEE, ctx);
                
                // Transfer admin fee to treasury
                transfer::public_transfer(admin_coin, TREASURY);
                
                // Refund the remainder
                let refund_amount = coin::value(&coin);
                transfer::public_transfer(coin, sender);
                
                event::emit(MembershipRejected {
                    from: sender,
                    original_amount: amount,
                    refunded_amount: refund_amount,
                    admin_fee: ADMIN_FEE,
                    reason: b"Incorrect payment amount. Admin fee of 5000 nanos deducted."
                });
            } else {
                // Amount too small, return everything
                transfer::public_transfer(coin, sender);
                event::emit(MembershipRejected {
                    from: sender,
                    original_amount: amount,
                    refunded_amount: amount,
                    admin_fee: 0,
                    reason: b"Amount too small to cover admin fee. Full amount returned."
                });
            }
        }
    }
}

Contract Features

Deployment Process

The contract was deployed using the following steps:

1. Build the Contract

iota move build

2. Publish the Contract

iota client publish \
--gas-budget 20000000 \
--gas 0x284a318dbc43ed71b098cb77f54ea1759a2e18f0fe98db006a751b2f8dc27901

This resulted in the contract being published with Package ID:
0x36e7624d6afeead61cd6971193d5d1544a7f5efe04db530a43f61f67d1d40f69

Interacting with the Contract

Although there is also a Dapp web interface, below is about calling the contract using the IOTA client CLI. Here's an example of processing a Gold membership (5 IOTA). The address in the arguments has to have exactly 5 Iota, otherwise a small 'administration charge' is made and the balance returned.

iota client call \
--gas-budget 10000000 \
--gas 0x284a318dbc43ed71b098cb77f54ea1759a2e18f0fe98db006a751b2f8dc27901 \
--function join \
--module membership \
--package 0x36e7624d6afeead61cd6971193d5d1544a7f5efe04db530a43f61f67d1d40f69 \
--type-args 0x2::iota::IOTA \
--args 0x4ef27dacdd974f9aecd3209d6bc3591286c285a5e5f1574db5bf7f9daed16087

Example Success Response

EventType: 0x36e7624d6afeead61cd6971193d5d1544a7f5efe04db530a43f61f67d1d40f69::membership::MembershipAccepted
ParsedJSON:
  amount: 5000000000
  from: 0xc196e256a58a1ea07e2cb27887090c3cf6a1ad1ec189c8a40575e7cde6c3dc4a
  tier: 3

Treasury and Administration

The contract uses the following treasury address for collecting membership fees and admin fees:
0xc196e256a58a1ea07e2cb27887090c3cf6a1ad1ec189c8a40575e7cde6c3dc4a

Next Steps

Future enhancements planned for the contract include:

Misc useful CLI commands

[1] Switch between accounts in CLI

The following lets you switch to a different address created by the IOTA Rebased CLI


    % iota client switch --address goofy-amber
    

and get some free coins for a Testnet account


    % iota client faucet
     

[2] Check Account balance and get Gas coins at the same time


    % iota client balance --with-coins
    

and SEND IOTA to another address if required (transfers ALL of the value in the specified gas coin - indeed, actually transfers the complete object ...)


     % iota client transfer \
    --to 0x598fc799d1c455bb73a1b09693bf7096fefe0f2ac03af9dfbf328754063c358b \
    --object-id 0x4ef27dacdd974f9aecd3209d6bc3591286c285a5e5f1574db5bf7f9daed16087 \
    --gas-budget 10000000
    

[3] Split a Gas coin into two of stated amount in CLI

Here 3 IOTA is split from coin-id 0xc4b9... Note that the coin used for gas cannot be the same coin as is split.


     % iota client split-coin \
    --gas-budget 10000000 \
    --gas 0xf2492ec9f2c2ce71be3e576845ea6abb92b3bb2dd73380d3cd91cb3bcc7f3420 \
    --coin-id 0xc4b9ca78ac29d9a0175b211d3704a14f8fa15dd5caf1f0ba740f0f8eb7692958 \
    --amounts 3000000000
    

and the similar action to MERGE COINS


    % iota client merge-coin \
    --primary-coin 0x4ef27dacdd974f9aecd3209d6bc3591286c285a5e5f1574db5bf7f9daed16087 \
    --coin-to-merge 0x2f6d95488e0ba8a0f4b1bc4da0df64640ddc3e68218edb8c54efeca12d9b9aea \
    --gas-budget 10000000
    

[4] Another example of calling the contract

The effect of the call below is that it was a successful transaction to secure a Silver Tier 2 membership:
[a] 0xc196e... (the Treasury) gained 3 IOTA
[b] 0xda5f... (the sender) paid a total of 3 IOTA + 1000000 nanos (gas)

Note that 0x2f6d... was a gas coin belonging to the sender with balance 3 IOTA.
Transaction Digest: 14TziExFVMDazftJPVxM6w739RV8wpetBGn4RWjcWE6x


     % iota client call \
    --gas-budget 10000000 \
    --gas 0xf2492ec9f2c2ce71be3e576845ea6abb92b3bb2dd73380d3cd91cb3bcc7f3420 \
    --function join \
    --module membership \
    --package 0x36e7624d6afeead61cd6971193d5d1544a7f5efe04db530a43f61f67d1d40f69 \
    --type-args 0x2::iota::IOTA \
    --args 0x2f6d95488e0ba8a0f4b1bc4da0df64640ddc3e68218edb8c54efeca12d9b9aea