- 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
- Three membership tiers with specific IOTA amounts
- Admin fee mechanism for invalid payments
- Event emission for accepted and rejected memberships
- Automatic refund handling for invalid amounts
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:
- Member tracking functionality
- Membership renewal system
- Member benefits management
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