Skip to main content

Documentation Index

Fetch the complete documentation index at: https://compact-by-example.org/llms.txt

Use this file to discover all available pages before exploring further.

The Contract

pragma language_version >= 0.17.0;

import CompactStandardLibrary;

// Simple balance tracker
export ledger balances: Map<Either<ZswapCoinPublicKey, ContractAddress>, Uint<128>>;

// Get balance for an account
export circuit getBalance(account: Either<ZswapCoinPublicKey, ContractAddress>): Uint<128> {
    // Check if account exists in map
    if (!balances.member(disclose(account))) {
        return 0; // Return 0 for accounts with no balance
    }
    return balances.lookup(disclose(account));
}

// Set balance for an account
export circuit setBalance(
    account: Either<ZswapCoinPublicKey, ContractAddress>,
    amount: Uint<128>
): [] {
    balances.insert(disclose(account), disclose(amount));
}

// Add to existing balance
export circuit addBalance(
    account: Either<ZswapCoinPublicKey, ContractAddress>,
    amount: Uint<128>
): [] {
    const currentBalance = getBalance(account);

    // Check for overflow
    const MAX_UINT128 = 340282366920938463463374607431768211455 as Uint<128>;
    assert(currentBalance <= MAX_UINT128 - amount, "Balance overflow");

    const newBalance = currentBalance + amount;
    balances.insert(disclose(account), disclose(newBalance));
}

// Subtract from balance
export circuit subtractBalance(
    account: Either<ZswapCoinPublicKey, ContractAddress>,
    amount: Uint<128>
): [] {
    const currentBalance = getBalance(account);

    // Check sufficient balance
    assert(currentBalance >= amount, "Insufficient balance");

    const newBalance = currentBalance - amount;
    balances.insert(disclose(account), disclose(newBalance));
}

How It Works

Map Declaration

export ledger balances: Map<Either<ZswapCoinPublicKey, ContractAddress>, Uint<128>>;
Maps have key and value type parameters. This one maps accounts to balance amounts.

Checking Membership

if (!balances.member(disclose(account))) {
    return 0;
}
Use member(key) to check if a key exists before lookup.

Looking Up Values

return balances.lookup(disclose(account));
Use lookup(key) to get the value for a key.

Inserting or Updating

balances.insert(disclose(account), disclose(amount));
Use insert(key, value) to add or update an entry.

Safe Arithmetic

const currentBalance = getBalance(account);

// Check for overflow before adding
const MAX_UINT128 = 340282366920938463463374607431768211455 as Uint<128>;
assert(currentBalance <= MAX_UINT128 - amount, "Balance overflow");

const newBalance = currentBalance + amount;
balances.insert(disclose(account), disclose(newBalance));
Always validate before modifying balances to prevent overflow and underflow.

Common Patterns

Initialize with Default

export circuit ensureAccount(account: Either<ZswapCoinPublicKey, ContractAddress>): [] {
    if (!balances.member(disclose(account))) {
        balances.insert(disclose(account), 0);
    }
}
Create an entry if it doesn’t exist.

Nested Maps

// Allowances: owner -> (spender -> amount)
export ledger allowances: Map<
    Either<ZswapCoinPublicKey, ContractAddress>,
    Map<Either<ZswapCoinPublicKey, ContractAddress>, Uint<128>>
>;

export circuit getAllowance(
    owner: Either<ZswapCoinPublicKey, ContractAddress>,
    spender: Either<ZswapCoinPublicKey, ContractAddress>
): Uint<128> {
    if (!allowances.member(disclose(owner))) {
        return 0;
    }

    const spenderAllowances = allowances.lookup(disclose(owner));
    if (!spenderAllowances.member(disclose(spender))) {
        return 0;
    }

    return spenderAllowances.lookup(disclose(spender));
}
Maps can contain other Maps for complex relationships (used in approval systems).

Iterating Over Maps

Compact doesn’t support iterating over Map keys directly. Track keys separately if needed.

What’s Next

Transfer Function

Build a token transfer system

ERC20 Token

See Maps in a complete token