Ex 5a. IOTA Rebased Contract - FreeBeer

unofficial site

- return to IOTA Rebased Useful Links

FreeBeer Contract Development

The concept is for an onchain contract where you can hit the 'free beer' button and be awarded some IOTA. You incur some gas costs but the free iota exceed that. At the same time your address is written to the chain and everybody is limited from asking for more for about the next hour, or to have more than 3 free beers per address. There is also the ability for anybody to top up the supply of free beer.


[1] Basic steps in building an IOTA Rebased Contract

The contract itself is listed below. In outline though there is a freebeer.move file within a sources folder. At top level there is a Move.toml file like this:


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

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

    [addresses]
    freebeer = "0x0"
    

Typically the next step is as follows. This creates a build folder.


    % iota move build
    

Next this has to be published to the IOTA Rebased blockchain (Testnet). Here is the CLI call, using a gas CoinId from the live CLI account:


    iota client publish \
    --gas-budget 60000000 \
    --gas 0x034010c356652f1f28e41ef45831aa983f920cbdb799c974fa69ced276454922
    

[2] IOTA Rebased Testnet Locations.

Transaction Digest: HyNUbYZWoHK1e35dbWmCeBVtC3w3PHpNEo7qoe7bwmvS

PackageID: 0x80da07146a4060d7195f81bd25655816f55a465218f4ba7c5d355e10e8759556

Created by account: 0xc196e256a58a1ea07e2cb27887090c3cf6a1ad1ec189c8a40575e7cde6c3dc4a

Created at the time of contract publication:
UpgradeCap 0xf0153c6276ab99a59e0e7d25d19c04099b9dd3dd8641e3aba02147519b5321c2


[3] Initializing the 'freebeer' contract

This is the call used and the resulting Transaction:

Transaction Digest: 6KRyX9KyoqZFywRcc4DYvjwC3nuktAekmpWx1iapkin3

Object changed: GlobalConfig 0x9d8cc06a57a89e63afc48805dde64c8bdd7aee9211f002566e25325b3206b95c
(which now has values now: initialized: false, last_beer_time: 0, cooldown_period: 3600000)

Object changed: AdminCap 0x0d5dc60b506b47bf32fd78f5c6ea755ab1316309f92664cf4028d211ede365b1
(just has this unique Id given)


    % iota client call \
    --gas-budget 10000000 \
    --gas 0x034010c356652f1f28e41ef45831aa983f920cbdb799c974fa69ced276454922 \
    --function initialize \
    --module beer \
    --package 0x80da07146a4060d7195f81bd25655816f55a465218f4ba7c5d355e10e8759556
    

[4] Calling the SETUP function

This is the call used and the resulting Transaction. Note that the two arguments are the Object Ids of two objects given UIDs in the previous step. that is: --args "ADMIN_CAP_ID" "GLOBAL_CONFIG_ID"

Transaction Digest: BcNMCwGTdgSnNmtWRQaj6TBpvT7Go6EV78nSHdGXSoUm

Object created: Registry 0xfe8bae921d2abbb42773c91110bbacb2b353c2d41a1e3b8f9cafa7fcd3956ea9
(which has an empty table with fields 'id' and 'size')

Object created: Treasury 0xba1cb7687c673df83d6a0364ecd76e632b3a3fffedba6b5596d72cfdddb203da
(which has value: beer_funds => 0)

Object changed: GlobalConfig 0x9d8cc06a57a89e63afc48805dde64c8bdd7aee9211f002566e25325b3206b95c
(which now has values now: initialized: true, last_beer_time: 0, cooldown_period: 3600000)


    iota client call \
    --gas-budget 10000000 \
    --gas 0x034010c356652f1f28e41ef45831aa983f920cbdb799c974fa69ced276454922  \
    --function setup \
    --module beer \
    --package 0x80da07146a4060d7195f81bd25655816f55a465218f4ba7c5d355e10e8759556 \
    --args "0x0d5dc60b506b47bf32fd78f5c6ea755ab1316309f92664cf4028d211ede365b1" "0x9d8cc06a57a89e63afc48805dde64c8bdd7aee9211f002566e25325b3206b95c"
    

[5] Adding Beer to the Treasury

This is the call used to call the addbeer function using a coinId with 7.298 IOTA value, and the resulting Transaction.

Transaction Digest: 4SG6wEnq6EV9WWrs6bRACQQF8Y1iuf2KejDLX5nxs8yc

Object changed: Treasury 0xba1cb7687c673df83d6a0364ecd76e632b3a3fffedba6b5596d72cfdddb203da
The beer_funds variable now contains 7.298 Iota, the Registry remains empty with no members.


    iota client call \
    --gas-budget 10000000 \
    --gas 0x034010c356652f1f28e41ef45831aa983f920cbdb799c974fa69ced276454922 \
    --function addbeer \
    --module beer \
    --package 0x80da07146a4060d7195f81bd25655816f55a465218f4ba7c5d355e10e8759556\
    --args 0x36a63f51e2e34f4dd3286b6297c63e7a914fbc20e61a88f4ad671dce5f3d13b7 "0xba1cb7687c673df83d6a0364ecd76e632b3a3fffedba6b5596d72cfdddb203da"
    

[6] Checking the 'free_beer_status'

This was the call used, with parameters GlobalConfig and Treasury.
Transaction Digest: 4notAgjEkY7yZtBQpPvU5ra3MuucvZw5uTwbGBLnqaCa


    iota client call \
    --gas-budget 10000000 \
    --gas 0x034010c356652f1f28e41ef45831aa983f920cbdb799c974fa69ced276454922 \
    --function get_beer_status \
    --module beer \
    --package 0x80da07146a4060d7195f81bd25655816f55a465218f4ba7c5d355e10e8759556 \
    --args 0xba1cb7687c673df83d6a0364ecd76e632b3a3fffedba6b5596d72cfdddb203da 0x9d8cc06a57a89e63afc48805dde64c8bdd7aee9211f002566e25325b3206b95c
    

A parsed JSON was returned.


            next_available  │ 1735067784714 │   
    │  │   ├─────────────────┼───────────────┤    
    │  │   │ status          │ 0             │     
    │  │   ├─────────────────┼───────────────┤  
    │  │   │ treasury_amount │ 7298019600   
    

[7] Getting your free beer from the Treasury

Need to swop CLI address to test the contract. The following may be useful (with your own addresses substitutued):


    % iota client addresses
    % iota client switch --address 0xc196e256a58a1ea07e2cb27887090c3cf6a1ad1ec189c8a40575e7cde6c3dc4a
    or % iota client switch --address 0xda5f052f5d98d7d6bdbeaff2df40acd1ff7e7e48368de591870d542e2e8eaf48
    iota client balance --with-coins
    

The call below is used to 'get beer' using account 0xda5..
Transaction Digest: DZyA8NAYRCyBPbLJKVc4Xr6Lj6fTCxgTKevqbcDa89Xx


    iota client call \
    --gas-budget 10000000 \
    --gas 0x3050ec46526a091e2cd2c4c6488ecb212863744e0b796b5ea155b11695196fde \
    --function getbeer \
    --module beer \
    --package 0x80da07146a4060d7195f81bd25655816f55a465218f4ba7c5d355e10e8759556 \
    --args 0xba1cb7687c673df83d6a0364ecd76e632b3a3fffedba6b5596d72cfdddb203da 0xfe8bae921d2abbb42773c91110bbacb2b353c2d41a1e3b8f9cafa7fcd3956ea9 0x9d8cc06a57a89e63afc48805dde64c8bdd7aee9211f002566e25325b3206b95c
    

Did it work. Well, first gas was paid using 0x3050... Then the transfers showed:

- a coin value 7000000 nano was created owned by the account 0xda5.. (the free beer)
CoinId: 0x17e5ba0530e2019d63d296ec5890c6031559a26bffa31c8bd4c3c11bd4744249

The GlobalConfig was updated.
The Treasury value has decreased by 7000000 nano

A field with the address 0xda5... and value 1 has been created, owned by 0xe452... (see below)
FieldID: 0x706b3cdf6777759317d2ee5ea0aae1e624d85d0f363bffcf52303587ba3b7d87

The Registry has an entry for 0xe4526...(see Field above)

[8] Summary

The 'free beer' contract seems to be working, so time to build a Dapp to make interface generally available and easier to use. This is just draft 1 of course as issues remain including improving security.


[9] Complete contract code


    module freebeer::beer {
    use iota::object::{Self, UID};
    use iota::coin::{Self, Coin};
    use iota::tx_context::{Self, TxContext};
    use iota::event;
    use iota::transfer;
    use iota::balance::{Self, Balance};
    use iota::table::{Self, Table};  // Add Table import

    // ======== Capability and Config ========
    public struct AdminCap has key, store {
        id: UID
    }

    public struct GlobalConfig has key {
        id: UID,
        initialized: bool,
        last_beer_time: u64,    
        cooldown_period: u64     
    }

    // ======== Constants ========
    const BEER_AMOUNT: u64 = 7000000;         // 7,000,000 nanos
    const LOW_FUNDS_THRESHOLD: u64 = 21000000; // 3 beers worth
    const BASE_COOLDOWN: u64 = 3600000;       // 1 hour in milliseconds
    const COOLDOWN_VARIANCE: u64 = 300000;    // 5 minutes variance
    const MAX_BEERS_PER_ADDRESS: u64 = 3;     // Maximum beers per address
    
    // ======== Status Constants ========
    const STATUS_AVAILABLE: u8 = 0;
    const STATUS_COOLING: u8 = 1;
    const STATUS_OUT_OF_BEER: u8 = 2;

    // ======== Error Codes ========
    const ALREADY_INITIALIZED: u64 = 1;
    const MAX_BEERS_RECEIVED: u64 = 2;
    const INSUFFICIENT_FUNDS: u64 = 3;
    const COOLING_DOWN: u64 = 4;

    // ======== Events ========
    public struct InitializeEvent has copy, drop {
        admin: address,
        timestamp: u64
    }

    public struct BeerAdded has copy, drop {
        amount: u64,
        from: address,
        new_total: u64
    }

    public struct TreasuryLow has copy, drop {
        remaining: u64,
        timestamp: u64
    }

    public struct BeerReceived has copy, drop {
        recipient: address,
        amount: u64,
        beers_remaining: u64,    // Added field for beers remaining for this address
        timestamp: u64,
        remaining_treasury: u64,
        next_available: u64
    }

    public struct BeerAttemptRejected has copy, drop {
        recipient: address,
        reason: vector,
        timestamp: u64,
        next_available: u64
    }

    public struct BeerStatus has copy, drop {
        status: u8,
        next_available: u64,
        treasury_amount: u64
    }

    // ======== Storage Structures ========
    public struct Registry has key, store {
        id: UID,
        member_counts: Table  // Changed to track count per address
    }

    public struct Treasury has key, store {
        id: UID,
        beer_funds: Balance
    }

    // ======== Initialize System ========
    public entry fun initialize(ctx: &mut TxContext) {
        transfer::share_object(
            GlobalConfig {
                id: object::new(ctx),
                initialized: false,
                last_beer_time: 0,
                cooldown_period: BASE_COOLDOWN
            }
        );

        transfer::public_transfer(
            AdminCap {
                id: object::new(ctx)
            },
            tx_context::sender(ctx)
        );
    }

    // ======== Setup Beer System ========
    public entry fun setup(
        _admin_cap: &AdminCap,
        config: &mut GlobalConfig,
        ctx: &mut TxContext
    ) {
        assert!(!config.initialized, ALREADY_INITIALIZED);
        
        let registry = Registry {
            id: object::new(ctx),
            member_counts: table::new(ctx)  // Initialize empty table
        };

        let treasury = Treasury {
            id: object::new(ctx),
            beer_funds: balance::zero()
        };

        config.initialized = true;

        transfer::share_object(registry);
        transfer::share_object(treasury);

        event::emit(InitializeEvent {
            admin: tx_context::sender(ctx),
            timestamp: tx_context::epoch_timestamp_ms(ctx)
        });
    }

    // ======== Add Beer Funds ========
    public entry fun addbeer(
        coin: Coin, 
        treasury: &mut Treasury, 
        ctx: &mut TxContext
    ) {
        let amount = coin::value(&coin);
        balance::join(&mut treasury.beer_funds, coin::into_balance(coin));
        
        let new_total = balance::value(&treasury.beer_funds);
        
        event::emit(BeerAdded { 
            amount,
            from: tx_context::sender(ctx),
            new_total
        });

        if (new_total <= LOW_FUNDS_THRESHOLD) {
            event::emit(TreasuryLow {
                remaining: new_total,
                timestamp: tx_context::epoch_timestamp_ms(ctx)
            });
        };
    }

    // ======== Check Beer Status ========
    public fun check_status(
        treasury: &Treasury,
        config: &GlobalConfig,
        ctx: &TxContext
    ): u8 {
        let current_time = tx_context::epoch_timestamp_ms(ctx);
        let treasury_amount = balance::value(&treasury.beer_funds);

        if (treasury_amount < BEER_AMOUNT) {
            STATUS_OUT_OF_BEER
        } else if (current_time - config.last_beer_time < config.cooldown_period) {
            STATUS_COOLING
        } else {
            STATUS_AVAILABLE
        }
    }

    // ======== Get Beer ========
    public entry fun getbeer(
        treasury: &mut Treasury,
        registry: &mut Registry,
        config: &mut GlobalConfig,
        ctx: &mut TxContext
    ) {
        let recipient = tx_context::sender(ctx);
        let current_time = tx_context::epoch_timestamp_ms(ctx);
        
        // Check beer count for recipient
        let count = if (table::contains(®istry.member_counts, recipient)) {
            *table::borrow(®istry.member_counts, recipient)
        } else {
            0
        };

        // Check if recipient has reached limit
        if (count >= MAX_BEERS_PER_ADDRESS) {
            event::emit(BeerAttemptRejected {
                recipient,
                reason: b"Maximum number of beers already received",
                timestamp: current_time,
                next_available: 0
            });
            abort MAX_BEERS_RECEIVED
        };

        // Check if cooling down
        if (current_time - config.last_beer_time < config.cooldown_period) {
            let next_available = config.last_beer_time + config.cooldown_period;
            event::emit(BeerAttemptRejected {
                recipient,
                reason: b"Beer not available yet - cooling down",
                timestamp: current_time,
                next_available
            });
            abort COOLING_DOWN
        };

        // Check if we have enough funds
        let treasury_balance = balance::value(&treasury.beer_funds);
        if (treasury_balance < BEER_AMOUNT) {
            event::emit(BeerAttemptRejected {
                recipient,
                reason: b"Insufficient funds in treasury",
                timestamp: current_time,
                next_available: 0
            });
            abort INSUFFICIENT_FUNDS
        };

        // Send beer amount
        let beer_payment = coin::from_balance(
            balance::split(&mut treasury.beer_funds, BEER_AMOUNT),
            ctx
        );
        transfer::public_transfer(beer_payment, recipient);

        // Update recipient's beer count
        if (table::contains(&mut registry.member_counts, recipient)) {
            let count = table::borrow_mut(&mut registry.member_counts, recipient);
            *count = *count + 1;
        } else {
            table::add(&mut registry.member_counts, recipient, 1);
        };

        // Update cooldown with slight randomization
        let random_variance = (current_time & 0xFF) * COOLDOWN_VARIANCE / 0xFF;
        config.cooldown_period = BASE_COOLDOWN + random_variance;
        config.last_beer_time = current_time;

        // Calculate next available time
        let next_available = current_time + config.cooldown_period;

        // Get remaining beers for this address
        let beers_remaining = MAX_BEERS_PER_ADDRESS - (count + 1);

        // Emit success event
        event::emit(BeerReceived {
            recipient,
            amount: BEER_AMOUNT,
            beers_remaining,
            timestamp: current_time,
            remaining_treasury: balance::value(&treasury.beer_funds),
            next_available
        });

        // Emit status for dApp
        event::emit(BeerStatus {
            status: STATUS_COOLING,
            next_available,
            treasury_amount: balance::value(&treasury.beer_funds)
        });

        // Check if funds are low after serving
        if (balance::value(&treasury.beer_funds) <= LOW_FUNDS_THRESHOLD) {
            event::emit(TreasuryLow {
                remaining: balance::value(&treasury.beer_funds),
                timestamp: current_time
            });
        };
    }

    // ======== Public View Function for dApp ========
    public entry fun get_beer_status(
        treasury: &Treasury,
        config: &GlobalConfig,
        ctx: &mut TxContext
    ) {
        let current_time = tx_context::epoch_timestamp_ms(ctx);
        let next_available = if (current_time < config.last_beer_time + config.cooldown_period) {
            config.last_beer_time + config.cooldown_period
        } else {
            current_time
        };

        event::emit(BeerStatus {
            status: check_status(treasury, config, ctx),
            next_available,
            treasury_amount: balance::value(&treasury.beer_funds)
        });
    }
}
    

[10] Complete contract MOVE Bytecode



// Move bytecode v6
module 80da07146a4060d7195f81bd25655816f55a465218f4ba7c5d355e10e8759556.beer {
use 0000000000000000000000000000000000000000000000000000000000000002::balance;
use 0000000000000000000000000000000000000000000000000000000000000002::coin;
use 0000000000000000000000000000000000000000000000000000000000000002::event;
use 0000000000000000000000000000000000000000000000000000000000000002::iota;
use 0000000000000000000000000000000000000000000000000000000000000002::object;
use 0000000000000000000000000000000000000000000000000000000000000002::table;
use 0000000000000000000000000000000000000000000000000000000000000002::transfer;
use 0000000000000000000000000000000000000000000000000000000000000002::tx_context;


struct AdminCap has store, key {
    id: UID
}
struct GlobalConfig has key {
    id: UID,
    initialized: bool,
    last_beer_time: u64,
    cooldown_period: u64
}
struct InitializeEvent has copy, drop {
    admin: address,
    timestamp: u64
}
struct BeerAdded has copy, drop {
    amount: u64,
    from: address,
    new_total: u64
}
struct TreasuryLow has copy, drop {
    remaining: u64,
    timestamp: u64
}
struct BeerReceived has copy, drop {
    recipient: address,
    amount: u64,
    beers_remaining: u64,
    timestamp: u64,
    remaining_treasury: u64,
    next_available: u64
}
struct BeerAttemptRejected has copy, drop {
    recipient: address,
    reason: vector,
    timestamp: u64,
    next_available: u64
}
struct BeerStatus has copy, drop {
    status: u8,
    next_available: u64,
    treasury_amount: u64
}
struct Registry has store, key {
    id: UID,
    member_counts: Table
}
struct Treasury has store, key {
    id: UID,
    beer_funds: Balance
}



entry public initialize(Arg0: &mut TxContext) {
B0:
    0: CopyLoc[0](Arg0: &mut TxContext)
    1: Call object::new(&mut TxContext): UID
    2: LdFalse
    3: LdU64(0)
    4: LdConst[2](u64: 3600..)
    5: Pack[1](GlobalConfig)
    6: Call transfer::share_object(GlobalConfig)
    7: CopyLoc[0](Arg0: &mut TxContext)
    8: Call object::new(&mut TxContext): UID
    9: Pack[0](AdminCap)
    10: MoveLoc[0](Arg0: &mut TxContext)
    11: FreezeRef
    12: Call tx_context::sender(&TxContext): address
    13: Call transfer::public_transfer(AdminCap, address)
    14: Ret

}
entry public setup(Arg0: &AdminCap, Arg1: &mut GlobalConfig, Arg2: &mut TxContext) {
B0:
    0: CopyLoc[1](Arg1: &mut GlobalConfig)
    1: ImmBorrowField[0](GlobalConfig.initialized: bool)
    2: ReadRef
    3: Not
    4: BrFalse(6)
B1:
    5: Branch(12)
B2:
    6: MoveLoc[2](Arg2: &mut TxContext)
    7: Pop
    8: MoveLoc[1](Arg1: &mut GlobalConfig)
    9: Pop
    10: LdConst[8](u64: 1)
    11: Abort
B3:
    12: CopyLoc[2](Arg2: &mut TxContext)
    13: Call object::new(&mut TxContext): UID
    14: CopyLoc[2](Arg2: &mut TxContext)
    15: Call table::new(&mut TxContext): Table
    16: Pack[8](Registry)
    17: StLoc[3](loc0: Registry)
    18: CopyLoc[2](Arg2: &mut TxContext)
    19: Call object::new(&mut TxContext): UID
    20: Call balance::zero(): Balance
    21: Pack[9](Treasury)
    22: StLoc[4](loc1: Treasury)
    23: LdTrue
    24: MoveLoc[1](Arg1: &mut GlobalConfig)
    25: MutBorrowField[0](GlobalConfig.initialized: bool)
    26: WriteRef
    27: MoveLoc[3](loc0: Registry)
    28: Call transfer::share_object(Registry)
    29: MoveLoc[4](loc1: Treasury)
    30: Call transfer::share_object(Treasury)
    31: CopyLoc[2](Arg2: &mut TxContext)
    32: FreezeRef
    33: Call tx_context::sender(&TxContext): address
    34: MoveLoc[2](Arg2: &mut TxContext)
    35: FreezeRef
    36: Call tx_context::epoch_timestamp_ms(&TxContext): u64
    37: Pack[2](InitializeEvent)
    38: Call event::emit(InitializeEvent)
    39: Ret

}
entry public addbeer(Arg0: Coin, Arg1: &mut Treasury, Arg2: &mut TxContext) {
B0:
    0: ImmBorrowLoc[0](Arg0: Coin)
    1: Call coin::value(&Coin): u64
    2: StLoc[3](loc0: u64)
    3: CopyLoc[1](Arg1: &mut Treasury)
    4: MutBorrowField[1](Treasury.beer_funds: Balance)
    5: MoveLoc[0](Arg0: Coin)
    6: Call coin::into_balance(Coin): Balance
    7: Call balance::join(&mut Balance, Balance): u64
    8: Pop
    9: MoveLoc[1](Arg1: &mut Treasury)
    10: ImmBorrowField[1](Treasury.beer_funds: Balance)
    11: Call balance::value(&Balance): u64
    12: StLoc[4](loc1: u64)
    13: MoveLoc[3](loc0: u64)
    14: CopyLoc[2](Arg2: &mut TxContext)
    15: FreezeRef
    16: Call tx_context::sender(&TxContext): address
    17: CopyLoc[4](loc1: u64)
    18: Pack[3](BeerAdded)
    19: Call event::emit(BeerAdded)
    20: CopyLoc[4](loc1: u64)
    21: LdConst[1](u64: 2100..)
    22: Le
    23: BrFalse(31)
B1:
    24: MoveLoc[4](loc1: u64)
    25: MoveLoc[2](Arg2: &mut TxContext)
    26: FreezeRef
    27: Call tx_context::epoch_timestamp_ms(&TxContext): u64
    28: Pack[4](TreasuryLow)
    29: Call event::emit(TreasuryLow)
    30: Branch(33)
B2:
    31: MoveLoc[2](Arg2: &mut TxContext)
    32: Pop
B3:
    33: Ret

}
public check_status(Arg0: &Treasury, Arg1: &GlobalConfig, Arg2: &TxContext): u8 {
B0:
    0: MoveLoc[2](Arg2: &TxContext)
    1: Call tx_context::epoch_timestamp_ms(&TxContext): u64
    2: StLoc[5](loc2: u64)
    3: MoveLoc[0](Arg0: &Treasury)
    4: ImmBorrowField[1](Treasury.beer_funds: Balance)
    5: Call balance::value(&Balance): u64
    6: LdConst[0](u64: 7000..)
    7: Lt
    8: BrFalse(14)
B1:
    9: MoveLoc[1](Arg1: &GlobalConfig)
    10: Pop
    11: LdConst[7](u8: 2)
    12: StLoc[4](loc1: u8)
    13: Branch(31)
B2:
    14: MoveLoc[5](loc2: u64)
    15: CopyLoc[1](Arg1: &GlobalConfig)
    16: ImmBorrowField[2](GlobalConfig.last_beer_time: u64)
    17: ReadRef
    18: Sub
    19: MoveLoc[1](Arg1: &GlobalConfig)
    20: ImmBorrowField[3](GlobalConfig.cooldown_period: u64)
    21: ReadRef
    22: Lt
    23: BrFalse(27)
B3:
    24: LdConst[6](u8: 1)
    25: StLoc[3](loc0: u8)
    26: Branch(29)
B4:
    27: LdConst[5](u8: 0)
    28: StLoc[3](loc0: u8)
B5:
    29: MoveLoc[3](loc0: u8)
    30: StLoc[4](loc1: u8)
B6:
    31: MoveLoc[4](loc1: u8)
    32: Ret

}
entry public getbeer(Arg0: &mut Treasury, Arg1: &mut Registry, Arg2: &mut GlobalConfig, Arg3: &mut TxContext) {
L0: loc4: u64
L1: loc5: u64
L2: loc6: u64
L3: loc7: u64
L4: loc8: address
B0:
    0: CopyLoc[3](Arg3: &mut TxContext)
    1: FreezeRef
    2: Call tx_context::sender(&TxContext): address
    3: StLoc[12](loc8: address)
    4: CopyLoc[3](Arg3: &mut TxContext)
    5: FreezeRef
    6: Call tx_context::epoch_timestamp_ms(&TxContext): u64
    7: StLoc[8](loc4: u64)
    8: CopyLoc[1](Arg1: &mut Registry)
    9: ImmBorrowField[4](Registry.member_counts: Table)
    10: CopyLoc[12](loc8: address)
    11: Call table::contains(&Table, address): bool
    12: BrFalse(20)
B1:
    13: CopyLoc[1](Arg1: &mut Registry)
    14: ImmBorrowField[4](Registry.member_counts: Table)
    15: CopyLoc[12](loc8: address)
    16: Call table::borrow(&Table, address): &u64
    17: ReadRef
    18: StLoc[4](loc0: u64)
    19: Branch(22)
B2:
    20: LdU64(0)
    21: StLoc[4](loc0: u64)
B3:
    22: MoveLoc[4](loc0: u64)
    23: StLoc[6](loc2: u64)
    24: CopyLoc[6](loc2: u64)
    25: LdConst[4](u64: 3)
    26: Ge
    27: BrFalse(44)
B4:
    28: MoveLoc[0](Arg0: &mut Treasury)
    29: Pop
    30: MoveLoc[1](Arg1: &mut Registry)
    31: Pop
    32: MoveLoc[3](Arg3: &mut TxContext)
    33: Pop
    34: MoveLoc[2](Arg2: &mut GlobalConfig)
    35: Pop
    36: MoveLoc[12](loc8: address)
    37: LdConst[11](vector: "Max..)
    38: MoveLoc[8](loc4: u64)
    39: LdU64(0)
    40: Pack[6](BeerAttemptRejected)
    41: Call event::emit(BeerAttemptRejected)
    42: LdConst[9](u64: 2)
    43: Abort
B5:
    44: CopyLoc[8](loc4: u64)
    45: CopyLoc[2](Arg2: &mut GlobalConfig)
    46: ImmBorrowField[2](GlobalConfig.last_beer_time: u64)
    47: ReadRef
    48: Sub
    49: CopyLoc[2](Arg2: &mut GlobalConfig)
    50: ImmBorrowField[3](GlobalConfig.cooldown_period: u64)
    51: ReadRef
    52: Lt
    53: BrFalse(76)
B6:
    54: MoveLoc[0](Arg0: &mut Treasury)
    55: Pop
    56: MoveLoc[1](Arg1: &mut Registry)
    57: Pop
    58: MoveLoc[3](Arg3: &mut TxContext)
    59: Pop
    60: CopyLoc[2](Arg2: &mut GlobalConfig)
    61: ImmBorrowField[2](GlobalConfig.last_beer_time: u64)
    62: ReadRef
    63: MoveLoc[2](Arg2: &mut GlobalConfig)
    64: ImmBorrowField[3](GlobalConfig.cooldown_period: u64)
    65: ReadRef
    66: Add
    67: StLoc[9](loc5: u64)
    68: MoveLoc[12](loc8: address)
    69: LdConst[12](vector: "Bee..)
    70: MoveLoc[8](loc4: u64)
    71: MoveLoc[9](loc5: u64)
    72: Pack[6](BeerAttemptRejected)
    73: Call event::emit(BeerAttemptRejected)
    74: LdConst[10](u64: 4)
    75: Abort
B7:
    76: CopyLoc[0](Arg0: &mut Treasury)
    77: ImmBorrowField[1](Treasury.beer_funds: Balance)
    78: Call balance::value(&Balance): u64
    79: LdConst[0](u64: 7000..)
    80: Lt
    81: BrFalse(98)
B8:
    82: MoveLoc[0](Arg0: &mut Treasury)
    83: Pop
    84: MoveLoc[1](Arg1: &mut Registry)
    85: Pop
    86: MoveLoc[3](Arg3: &mut TxContext)
    87: Pop
    88: MoveLoc[2](Arg2: &mut GlobalConfig)
    89: Pop
    90: MoveLoc[12](loc8: address)
    91: LdConst[13](vector: "Ins..)
    92: MoveLoc[8](loc4: u64)
    93: LdU64(0)
    94: Pack[6](BeerAttemptRejected)
    95: Call event::emit(BeerAttemptRejected)
    96: LdConst[4](u64: 3)
    97: Abort
B9:
    98: CopyLoc[0](Arg0: &mut Treasury)
    99: MutBorrowField[1](Treasury.beer_funds: Balance)
    100: LdConst[0](u64: 7000..)
    101: Call balance::split(&mut Balance, u64): Balance
    102: MoveLoc[3](Arg3: &mut TxContext)
    103: Call coin::from_balance(Balance, &mut TxContext): Coin
    104: CopyLoc[12](loc8: address)
    105: Call transfer::public_transfer>(Coin, address)
    106: CopyLoc[1](Arg1: &mut Registry)
    107: MutBorrowField[4](Registry.member_counts: Table)
    108: FreezeRef
    109: CopyLoc[12](loc8: address)
    110: Call table::contains(&Table, address): bool
    111: BrFalse(124)
B10:
    112: MoveLoc[1](Arg1: &mut Registry)
    113: MutBorrowField[4](Registry.member_counts: Table)
    114: CopyLoc[12](loc8: address)
    115: Call table::borrow_mut(&mut Table, address): &mut u64
    116: StLoc[7](loc3: &mut u64)
    117: CopyLoc[7](loc3: &mut u64)
    118: ReadRef
    119: LdU64(1)
    120: Add
    121: MoveLoc[7](loc3: &mut u64)
    122: WriteRef
    123: Branch(129)
B11:
    124: MoveLoc[1](Arg1: &mut Registry)
    125: MutBorrowField[4](Registry.member_counts: Table)
    126: CopyLoc[12](loc8: address)
    127: LdU64(1)
    128: Call table::add(&mut Table, address, u64)
B12:
    129: CopyLoc[8](loc4: u64)
    130: LdU64(255)
    131: BitAnd
    132: LdConst[3](u64: 300000)
    133: Mul
    134: LdU64(255)
    135: Div
    136: StLoc[11](loc7: u64)
    137: LdConst[2](u64: 3600..)
    138: MoveLoc[11](loc7: u64)
    139: Add
    140: CopyLoc[2](Arg2: &mut GlobalConfig)
    141: MutBorrowField[3](GlobalConfig.cooldown_period: u64)
    142: WriteRef
    143: CopyLoc[8](loc4: u64)
    144: CopyLoc[2](Arg2: &mut GlobalConfig)
    145: MutBorrowField[2](GlobalConfig.last_beer_time: u64)
    146: WriteRef
    147: CopyLoc[8](loc4: u64)
    148: MoveLoc[2](Arg2: &mut GlobalConfig)
    149: ImmBorrowField[3](GlobalConfig.cooldown_period: u64)
    150: ReadRef
    151: Add
    152: StLoc[10](loc6: u64)
    153: LdConst[4](u64: 3)
    154: MoveLoc[6](loc2: u64)
    155: LdU64(1)
    156: Add
    157: Sub
    158: StLoc[5](loc1: u64)
    159: MoveLoc[12](loc8: address)
    160: LdConst[0](u64: 7000..)
    161: MoveLoc[5](loc1: u64)
    162: CopyLoc[8](loc4: u64)
    163: CopyLoc[0](Arg0: &mut Treasury)
    164: ImmBorrowField[1](Treasury.beer_funds: Balance)
    165: Call balance::value(&Balance): u64
    166: CopyLoc[10](loc6: u64)
    167: Pack[5](BeerReceived)
    168: Call event::emit(BeerReceived)
    169: LdConst[6](u8: 1)
    170: MoveLoc[10](loc6: u64)
    171: CopyLoc[0](Arg0: &mut Treasury)
    172: ImmBorrowField[1](Treasury.beer_funds: Balance)
    173: Call balance::value(&Balance): u64
    174: Pack[7](BeerStatus)
    175: Call event::emit(BeerStatus)
    176: CopyLoc[0](Arg0: &mut Treasury)
    177: ImmBorrowField[1](Treasury.beer_funds: Balance)
    178: Call balance::value(&Balance): u64
    179: LdConst[1](u64: 2100..)
    180: Le
    181: BrFalse(189)
B13:
    182: MoveLoc[0](Arg0: &mut Treasury)
    183: ImmBorrowField[1](Treasury.beer_funds: Balance)
    184: Call balance::value(&Balance): u64
    185: MoveLoc[8](loc4: u64)
    186: Pack[4](TreasuryLow)
    187: Call event::emit(TreasuryLow)
    188: Branch(191)
B14:
    189: MoveLoc[0](Arg0: &mut Treasury)
    190: Pop
B15:
    191: Ret

}
entry public get_beer_status(Arg0: &Treasury, Arg1: &GlobalConfig, Arg2: &mut TxContext) {
B0:
    0: CopyLoc[2](Arg2: &mut TxContext)
    1: FreezeRef
    2: Call tx_context::epoch_timestamp_ms(&TxContext): u64
    3: StLoc[4](loc1: u64)
    4: CopyLoc[4](loc1: u64)
    5: CopyLoc[1](Arg1: &GlobalConfig)
    6: ImmBorrowField[2](GlobalConfig.last_beer_time: u64)
    7: ReadRef
    8: CopyLoc[1](Arg1: &GlobalConfig)
    9: ImmBorrowField[3](GlobalConfig.cooldown_period: u64)
    10: ReadRef
    11: Add
    12: Lt
    13: BrFalse(23)
B1:
    14: CopyLoc[1](Arg1: &GlobalConfig)
    15: ImmBorrowField[2](GlobalConfig.last_beer_time: u64)
    16: ReadRef
    17: CopyLoc[1](Arg1: &GlobalConfig)
    18: ImmBorrowField[3](GlobalConfig.cooldown_period: u64)
    19: ReadRef
    20: Add
    21: StLoc[3](loc0: u64)
    22: Branch(25)
B2:
    23: MoveLoc[4](loc1: u64)
    24: StLoc[3](loc0: u64)
B3:
    25: MoveLoc[3](loc0: u64)
    26: StLoc[5](loc2: u64)
    27: CopyLoc[0](Arg0: &Treasury)
    28: MoveLoc[1](Arg1: &GlobalConfig)
    29: MoveLoc[2](Arg2: &mut TxContext)
    30: FreezeRef
    31: Call check_status(&Treasury, &GlobalConfig, &TxContext): u8
    32: MoveLoc[5](loc2: u64)
    33: MoveLoc[0](Arg0: &Treasury)
    34: ImmBorrowField[1](Treasury.beer_funds: Balance)
    35: Call balance::value(&Balance): u64
    36: Pack[7](BeerStatus)
    37: Call event::emit(BeerStatus)
    38: Ret

}

Constants [
    0 => u64: 7000000
    1 => u64: 21000000
    2 => u64: 3600000
    3 => u64: 300000
    4 => u64: 3
    5 => u8: 0
    6 => u8: 1
    7 => u8: 2
    8 => u64: 1
    9 => u64: 2
    10 => u64: 4
    11 => vector: "Maximum number of beers already received" // interpreted as UTF8 string
    12 => vector: "Beer not available yet - cooling down" // interpreted as UTF8 string
    13 => vector: "Insufficient funds in treasury" // interpreted as UTF8 string
]
}