Write a simple counter smart contract

In the backend/src/contract.pnt directory you'll have a simple token smart contract with burn, mint functionality

use std::lib::PredicateAddress;
use std::lib::@delta;
use std::lib::@safe_increment;
use std::lib::@init_once;
use std::lib::@init_delta;
use std::lib::@mut_keys;
use std::auth::@verify_key;
use std::lib::Secp256k1Signature;

storage {
    balances: (b256 => int),
    nonce: (b256 => int),
    token_name: b256,
    token_symbol: b256,
    decimals: int,
}

interface BurnAccount {
    predicate Owner(
        key: b256,
        amount: int,
        token_address: PredicateAddress,
    );
}

union BurnAuth = Signed(Secp256k1Signature) | Predicate(PredicateAddress);

predicate Burn(key: b256, amount: int, auth: BurnAuth) {
    let balance = mut storage::balances[key];
    let nonce = mut storage::nonce[key];

    constraint amount > 0;
    constraint @delta(balance) == 0 - amount;
    constraint balance' >= 0;
    constraint @safe_increment(nonce);
    
    constraint match auth {
        BurnAuth::Signed(sig) => @verify_key({key, amount, nonce'}; sig; key),
        BurnAuth::Predicate(addr) => @check_if_predicate_is_owner(BurnAccount; Owner; addr; key; amount),
    };
}

macro @check_if_predicate_is_owner($c, $p, $address, $arg0) {
    $c@[$address.contract]::$p@[$address.addr]($arg0, { contract: __this_contract_address(), addr: __this_address() })
}

macro @check_if_predicate_is_owner($c, $p, $address, $arg0, $arg1) {
    $c@[$address.contract]::$p@[$address.addr]($arg0, $arg1, { contract: __this_contract_address(), addr: __this_address() })
}

macro @check_if_predicate_is_owner($c, $p, $address, $arg0, $arg1, $arg2) {
    $c@[$address.contract]::$p@[$address.addr]($arg0, $arg1, $arg2, { contract: __this_contract_address(), addr: __this_address() })
}

union MintAuth = Signed(Secp256k1Signature) | Predicate(PredicateAddress);

interface MintAccount {
    predicate Owner(
        key: b256,
        amount: int,
        decimals: int,
        token_address: PredicateAddress,
    );
}

predicate Mint(key: b256, amount: int, decimals: int, auth: MintAuth) {
    let balance = mut storage::balances[key];
    let nonce = mut storage::nonce[key];
    let token_name = mut storage::token_name;
    let token_symbol = mut storage::token_symbol;
    let token_decimals = mut storage::decimals;

    constraint key == config::MINT_KEY;
    
    constraint @init_once(balance; amount);
    constraint @init_once(token_name; config::NAME);
    constraint @init_once(token_symbol; config::SYMBOL);
    constraint @init_once(token_decimals; decimals);
    constraint @init_once(nonce; 1);

    constraint match auth {
        MintAuth::Signed(sig) => @verify_key({key, amount, decimals, nonce'}; sig; key),
        MintAuth::Predicate(addr) => @check_if_predicate_is_owner(MintAccount; Owner; addr; key; decimals; amount),
    };
}

union TransferSignedMode = All | Key | KeyTo | KeyAmount;

type TransferSignedAuth = { sig: Secp256k1Signature, mode: TransferSignedMode };

union TransferAuthMode = Signed(TransferSignedAuth) | Predicate(PredicateAddress);

type Extra = { addr: PredicateAddress };

union ExtraConstraints = Extra(Extra) | None;

type TransferAuth = { mode: TransferAuthMode, extra: ExtraConstraints };

interface TransferAccount {
    predicate Owner(
        key: b256,
        to: b256,
        amount: int,
        token_address: PredicateAddress,
    );
}

interface ExtraConstraintsI {
    predicate Check(
        token_address: PredicateAddress,
    );
}

predicate Transfer(key: b256, to: b256, amount: int, auth: TransferAuth) {
    let sender_balance = mut storage::balances[key];
    let receiver_balance = mut storage::balances[to];
    let nonce = mut storage::nonce[key];

    constraint amount > 0;
    constraint sender_balance' >= 0;
    constraint @delta(sender_balance) == 0 - amount;
    constraint @init_delta(receiver_balance; amount);
    constraint @safe_increment(nonce);
    
    constraint match auth.mode {
        TransferAuthMode::Signed(auth) => match auth.mode {
            TransferSignedMode::All => @verify_key({key, to, amount, nonce'}; auth.sig; key),
            TransferSignedMode::Key => @verify_key({key, nonce'}; auth.sig; key),
            TransferSignedMode::KeyTo => @verify_key({key, to, nonce'}; auth.sig; key),
            TransferSignedMode::KeyAmount => @verify_key({key, amount, nonce'}; auth.sig; key),
        },
        TransferAuthMode::Predicate(addr) => @check_if_predicate_is_owner(TransferAccount; Owner; addr; key; to; amount),
    };
    constraint match auth.extra {
        ExtraConstraints::Extra(extra) => ExtraConstraintsI@[extra.addr.contract]::Check@[extra.addr.addr]({ contract: __this_contract_address(), addr: __this_address() }),
        ExtraConstraints::None => true,
    };
}

interface CancelAccount {
    predicate Owner(
        key: b256,
        token_address: PredicateAddress,
    );
}

union CancelAuth = Signed(Secp256k1Signature) | Predicate(PredicateAddress);

predicate Cancel(key: b256, auth: CancelAuth) {
    let nonce = mut storage::nonce[key];

    constraint @safe_increment(nonce);

    constraint match auth {
        CancelAuth::Signed(sig) => @verify_key({key, nonce'}; sig; key),
        CancelAuth::Predicate(addr) => @check_if_predicate_is_owner(CancelAccount; Owner; addr; key),
    };
}

We'll now compile the contract and deploy it locally. To compile :- Head over

Last updated