Your First Module

6.1 Sui: a counter object

module counter::counter; is the Move 2024 module label form (no braces needed).

UID, TxContext, object, transfer, and tx_context are implicitly imported in the 2024 edition.

public struct — struct declarations must state their visibility in Move 2024.

Build, publish, and call:

// sources/counter.move
module counter::counter;

/// A shared counter anyone can increment.
public struct Counter has key {
    id: UID,
    owner: address,
    value: u64,
}

/// Create a new counter and share it.
public fun create(ctx: &mut TxContext) {
    let counter = Counter {
        id: object::new(ctx),
        owner: ctx.sender(),
        value: 0,
    };
    transfer::share_object(counter);
}

public fun increment(counter: &mut Counter) {
    counter.value = counter.value + 1;
}

public fun value(counter: &Counter): u64 {
    counter.value
}

/// Only the owner may reset.
public fun reset(counter: &mut Counter, ctx: &TxContext) {
    assert!(counter.owner == ctx.sender(), 0);
    counter.value = 0;
}
sui move build
sui client publish
# copy the package ID from the output, then:
sui client call --package 0xPKG --module counter --function create
sui client call --package 0xPKG --module counter --function increment --args 0xCOUNTER

6.2 Aptos: a counter resource

// sources/counter.move
module my_addr::counter {
    use std::signer;

    struct Counter has key {
        value: u64,
    }

    const E_ALREADY_EXISTS: u64 = 1;
    const E_NOT_FOUND: u64 = 2;

    public entry fun create(account: &signer) {
        let addr = signer::address_of(account);
        assert!(!exists<Counter>(addr), E_ALREADY_EXISTS);
        move_to(account, Counter { value: 0 });
    }

    public entry fun increment(account: &signer) acquires Counter {
        let addr = signer::address_of(account);
        assert!(exists<Counter>(addr), E_NOT_FOUND);
        let counter = borrow_global_mut<Counter>(addr);
        counter.value = counter.value + 1;
    }

    #[view]
    public fun value(addr: address): u64 acquires Counter {
        borrow_global<Counter>(addr).value
    }
}
aptos move compile --named-addresses my_addr=default
aptos move publish --named-addresses my_addr=default
aptos move run --function-id default::counter::create
aptos move run --function-id default::counter::increment
aptos move view --function-id default::counter::value --args address:default

6.3 What is different?

| | Sui | Aptos | |-|-----|-------| | Where data lives | Counter is a standalone shared object | Counter lives under the creator's account | | Who can increment | Anyone with the object ID (it is shared) | Only the account (needs its &signer) | | Identity | id: UID field required | Identified by (address, type) | | Read-only queries | Read object via RPC/SDK, or devInspect | #[view] functions | | Module syntax | Module label module a::b; | Block module a::b { ... } |