IOTA Rebased

unofficial site

- 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

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 <--amounts ...|--count >


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 --module --function


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

Example Transaction

Successful payment of 2 IOTA using:

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:

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.