- return to IOTA Rebased Useful Links
ClubMem Dapp
JustSoMuch Smart Contract
A smart contract for IOTA Rebased that only allows specific payment amounts: 2, 3, or 7 IOTA.
Contract Source Code
module just_so_much::payment_validator {
use iota::coin::{Self, Coin};
use iota::tx_context::TxContext;
use iota::event;
use iota::transfer;
#[allow(duplicate_alias)]
public struct PaymentAccepted has copy, drop {
from: address,
amount: u64,
recipient: address
}
public struct PaymentRejected has copy, drop {
from: address,
amount: u64,
reason: vector
}
// Error constants
const INVALID_AMOUNT: u64 = 0;
// Valid payment amounts in nanos (1 IOTA = 1,000,000,000 nanos)
const AMOUNT_TWO: u64 = 2000000000; // 2 IOTA
const AMOUNT_THREE: u64 = 3000000000; // 3 IOTA
const AMOUNT_SEVEN: u64 = 7000000000; // 7 IOTA
public entry fun make_payment(
coin: Coin,
recipient: address,
ctx: &mut TxContext
) {
let amount = coin::value(&coin);
let sender = tx_context::sender(ctx);
if (amount == AMOUNT_TWO || amount == AMOUNT_THREE || amount == AMOUNT_SEVEN) {
transfer::public_transfer(coin, recipient);
event::emit(PaymentAccepted {
from: sender,
amount,
recipient
});
} else {
transfer::public_transfer(coin, sender);
event::emit(PaymentRejected {
from: sender,
amount,
reason: b"Invalid payment amount. Only 2, 3, or 7 IOTA allowed."
});
abort INVALID_AMOUNT
}
}
}
Contract Details
- Package ID: 0x042a845f2862645369d80659bc52523f1a021497aa3c38bbd1715ade6a1f4c2b
- Network: IOTA Rebased Testnet
- Valid Amounts: 2 IOTA, 3 IOTA, 7 IOTA
Usage Instructions
For more information use: iota client --help
or more detailed request like: iota client faucet --help
1. Deploy Contract
Usage: iota client publish [OPTIONS] [package_path]
iota client publish \
--gas-budget 20000000 \
--gas YOUR_GAS_COIN_ID
2. Split Coin for Exact Amount (if needed)
Usage: iota client split-coin [OPTIONS] --coin-id
iota client split-coin \
--coin-id SOURCE_COIN_ID \
--amounts 2000000000 \
--gas-budget 10000000 \
--gas GAS_COIN_ID
3. Obtain all gas objects owned by the address
iota client gas [OPTIONS] [owner_address]
Provides gasCoinIds and nanosBalance. Also: iota client balance
4. Make Payment
Usage: iota client call [OPTIONS] --package
iota client call \
--gas-budget 10000000 \
--gas GAS_COIN_ID \
--function make_payment \
--module payment_validator \
--package 0x042a845f2862645369d80659bc52523f1a021497aa3c38bbd1715ade6a1f4c2b \
--type-args 0x2::iota::IOTA \
--args PAYMENT_COIN_ID RECIPIENT_ADDRESS
5. Sending funds using IOTA CLI
Useful reminder of how to simply send funds
iota client transfer-iota \
--to RECIPIENTS ADDRESS \
--iota-coin-object-id SENDERS_GASCOIN_ID \
--amount 8000000000 \
--gas-budget 10000000
Important Notes
- The concept of the contract is that it only accepts exact amounts of 2, 3, or 7 IOTA
- Amounts must be in nanos (1 IOTA = 1,000,000,000 nanos)
- Invalid amounts are returned to sender
- Separate gas coin required for transactions
- Events are emitted for both successful and rejected payments
Example Transaction
Successful payment of 2 IOTA using:
- Gas Coin: 0x284a318dbc43ed71b098cb77f54ea1759a2e18f0fe98db006a751b2f8dc27901
- Payment Coin: 0x35eb0cde640a124c2e2c202d2c63d2b7459a18a4e9efd5a026287a50ad231b77
- Recipient: 0x598fc799d1c455bb73a1b09693bf7096fefe0f2ac03af9dfbf328754063c358b
Bytecode Analysis
Once a contract is published on the IOTA blockchain it can be accessed in Explorer using its PackageId. That has a column for Modules. Under that are Functions and then their Move Bytecode which is at the moment v6.
The bytecode implementation can be broken down into several key functional sections that demonstrate how Move's bytecode handles payment validation and processing:
Payment Validation Flow
1. Value Check (Lines 25-27)
0: ImmBorrowLoc[0](Arg0: Coin<Ty0>) 1: Call coin::value<Ty0>(&Coin<Ty0>): u64 2: StLoc[5](loc2: u64)
This sequence retrieves the coin's value and stores it in 'Standard Local variable slot 5' called 'loc2' for later comparison. The ImmBorrowLoc instruction creates an immutable reference to the coin argument.
2. Sender Retrieval (Lines 28-31)
3: MoveLoc[2](Arg2: &mut TxContext) 4: FreezeRef 5: Call tx_context::sender(&TxContext): address 6: StLoc[6](loc3: address)
These instructions obtain the transaction sender's address from the context. The FreezeRef instruction converts the mutable reference to an immutable one before the call.
3. Amount Validation (Lines 33-57)
In the next area is CopyLoc[5] which copies the value of the payment coin, then LdConst which loads a Constant from the stored values that can be seen in Explorer. There is then Eq which compares the values. Finally there is a BrFalse statement which is a Branch if False with a line number.
In summary, the bytecode implements a series of equality checks against the valid amounts:
- First checks for 2 IOTA (2000000000)
- Then checks for 3 IOTA (3000000000)
- Finally checks for 7 IOTA (7000000000)
4. Payment Processing
Success Path (Lines 74-82):
40: MoveLoc[0](Arg0: Coin<Ty0>) 41: CopyLoc[1](Arg1: address) 42: Call transfer::public_transfer<Coin<Ty0>>(Coin<Ty0>, address)
When validation succeeds, these instructions transfer the coin to the recipient and emit a PaymentAccepted event.
Failure Path (Lines 63-72):
30: MoveLoc[0](Arg0: Coin<Ty0>) 31: CopyLoc[6](loc3: address) 32: Call transfer::public_transfer<Coin<Ty0>>(Coin<Ty0>, address)
When validation fails, these instructions return the coin to the sender, emit a PaymentRejected event, and abort the transaction.