The Contract
pragma language_version >= 0.17.0;
import CompactStandardLibrary;
// Account balances
export ledger balances: Map<Either<ZswapCoinPublicKey, ContractAddress>, Uint<128>>;
// Total token supply
export ledger totalSupply: Uint<128>;
// Get total circulating supply
export circuit getTotalSupply(): Uint<128> {
return totalSupply;
}
// Mint new tokens
export circuit mint(
account: Either<ZswapCoinPublicKey, ContractAddress>,
amount: Uint<128>
): [] {
_mint(account, amount);
}
// Internal: Mint tokens
circuit _mint(
account: Either<ZswapCoinPublicKey, ContractAddress>,
amount: Uint<128>
): [] {
// Check total supply won't overflow
const MAX_UINT128 = 340282366920938463463374607431768211455 as Uint<128>;
assert(totalSupply <= MAX_UINT128 - amount, "Total supply overflow");
// Update total supply
totalSupply = disclose(totalSupply + amount);
// Get current balance
const currentBalance = getBalance(account);
// Check account balance won't overflow
assert(currentBalance <= MAX_UINT128 - amount, "Balance overflow");
// Increase account balance
balances.insert(disclose(account), disclose(currentBalance + amount));
}
// Helper: Get balance
circuit getBalance(account: Either<ZswapCoinPublicKey, ContractAddress>): Uint<128> {
if (!balances.member(disclose(account))) {
return 0;
}
return balances.lookup(disclose(account));
}
How It Works
Total Supply Tracking
export ledger totalSupply: Uint<128>;
export circuit getTotalSupply(): Uint<128> {
return totalSupply;
}
Total supply represents all tokens in circulation:
- Mint: Increases total supply
- Burn: Decreases total supply (see Burning tutorial)
- Transfer: Doesn’t change total supply (moves between accounts)
Invariant: sum(all balances) == totalSupply should always be true.
Minting and burning are the only operations that change this.
Minting Process
circuit _mint(
account: Either<ZswapCoinPublicKey, ContractAddress>,
amount: Uint<128>
): [] {
// Check total supply won't overflow
const MAX_UINT128 = 340282366920938463463374607431768211455 as Uint<128>;
assert(totalSupply <= MAX_UINT128 - amount, "Total supply overflow");
// Update total supply
totalSupply = disclose(totalSupply + amount);
// Get current balance
const currentBalance = getBalance(account);
// Check account balance won't overflow
assert(currentBalance <= MAX_UINT128 - amount, "Balance overflow");
// Increase account balance
balances.insert(disclose(account), disclose(currentBalance + amount));
}
Minting flow:
- Check total supply overflow: Prevent exceeding Uint<128> maximum
- Increase total supply: Add new tokens to circulation
- Check account overflow: Prevent recipient balance overflow
- Credit account: Add tokens to recipient’s balance
See Overflow Protection for detailed arithmetic safety patterns.
Access Control
// Public: Anyone can call (should add access control!)
export circuit mint(
account: Either<ZswapCoinPublicKey, ContractAddress>,
amount: Uint<128>
): [] {
_mint(account, amount);
}
The example mint() function has NO access control - anyone can mint unlimited tokens!
See Access Control for owner-only and role-based patterns.
What’s Next