pragma language_version >= 0.17.0;
import CompactStandardLibrary;
// Initialization state
export ledger _isInitialized: Boolean;
// Token metadata
export sealed ledger _name: Opaque<"string">;
export sealed ledger _symbol: Opaque<"string">;
export sealed ledger _decimals: Uint<8>;
// Token balances and allowances
export ledger _balances: Map<Either<ZswapCoinPublicKey, ContractAddress>, Uint<128>>;
export ledger _allowances: Map<Either<ZswapCoinPublicKey, ContractAddress>, Map<Either<ZswapCoinPublicKey, ContractAddress>, Uint<128>>>;
export ledger _totalSupply: Uint<128>;
// Initialize the token (can only be called once)
export circuit initialize(
name_: Opaque<"string">,
symbol_: Opaque<"string">,
decimals_: Uint<8>
): [] {
assert(!_isInitialized, "Already initialized");
_isInitialized = disclose(true);
_name = disclose(name_);
_symbol = disclose(symbol_);
_decimals = disclose(decimals_);
}
// Get token name
export circuit name(): Opaque<"string"> {
assert(_isInitialized, "Not initialized");
return _name;
}
// Get token symbol
export circuit symbol(): Opaque<"string"> {
assert(_isInitialized, "Not initialized");
return _symbol;
}
// Get decimals
export circuit decimals(): Uint<8> {
assert(_isInitialized, "Not initialized");
return _decimals;
}
// Get total supply
export circuit totalSupply(): Uint<128> {
assert(_isInitialized, "Not initialized");
return _totalSupply;
}
// Get balance of an account
export circuit balanceOf(account: Either<ZswapCoinPublicKey, ContractAddress>): Uint<128> {
assert(_isInitialized, "Not initialized");
if (!_balances.member(disclose(account))) {
return 0;
}
return _balances.lookup(disclose(account));
}
// Transfer tokens
export circuit transfer(to: Either<ZswapCoinPublicKey, ContractAddress>, value: Uint<128>): Boolean {
assert(_isInitialized, "Not initialized");
const owner = left<ZswapCoinPublicKey, ContractAddress>(ownPublicKey());
_transfer(owner, to, value);
return true;
}
// Approve spender
export circuit approve(spender: Either<ZswapCoinPublicKey, ContractAddress>, value: Uint<128>): Boolean {
assert(_isInitialized, "Not initialized");
const owner = left<ZswapCoinPublicKey, ContractAddress>(ownPublicKey());
_approve(owner, spender, value);
return true;
}
// Get allowance
export circuit allowance(
owner: Either<ZswapCoinPublicKey, ContractAddress>,
spender: Either<ZswapCoinPublicKey, ContractAddress>
): Uint<128> {
assert(_isInitialized, "Not initialized");
if (!_allowances.member(disclose(owner)) || !_allowances.lookup(owner).member(disclose(spender))) {
return 0;
}
return _allowances.lookup(owner).lookup(disclose(spender));
}
// Transfer from approved account
export circuit transferFrom(
from: Either<ZswapCoinPublicKey, ContractAddress>,
to: Either<ZswapCoinPublicKey, ContractAddress>,
value: Uint<128>
): Boolean {
assert(_isInitialized, "Not initialized");
const spender = left<ZswapCoinPublicKey, ContractAddress>(ownPublicKey());
_spendAllowance(from, spender, value);
_transfer(from, to, value);
return true;
}
// Internal transfer function
circuit _transfer(
from: Either<ZswapCoinPublicKey, ContractAddress>,
to: Either<ZswapCoinPublicKey, ContractAddress>,
value: Uint<128>
): [] {
assert(!isKeyOrAddressZero(from), "Invalid sender");
assert(!isKeyOrAddressZero(to), "Invalid receiver");
const fromBal = balanceOf(from);
assert(fromBal >= value, "Insufficient balance");
_balances.insert(disclose(from), disclose(fromBal - value as Uint<128>));
const toBal = balanceOf(to);
_balances.insert(disclose(to), disclose(toBal + value as Uint<128>));
}
// Internal approve function
circuit _approve(
owner: Either<ZswapCoinPublicKey, ContractAddress>,
spender: Either<ZswapCoinPublicKey, ContractAddress>,
value: Uint<128>
): [] {
assert(!isKeyOrAddressZero(owner), "Invalid owner");
assert(!isKeyOrAddressZero(spender), "Invalid spender");
if (!_allowances.member(disclose(owner))) {
_allowances.insert(disclose(owner), default<Map<Either<ZswapCoinPublicKey, ContractAddress>, Uint<128>>>);
}
_allowances.lookup(owner).insert(disclose(spender), disclose(value));
}
// Spend allowance
circuit _spendAllowance(
owner: Either<ZswapCoinPublicKey, ContractAddress>,
spender: Either<ZswapCoinPublicKey, ContractAddress>,
value: Uint<128>
): [] {
const currentAllowance = allowance(owner, spender);
const MAX_UINT128 = 340282366920938463463374607431768211455;
if (currentAllowance < MAX_UINT128) {
assert(currentAllowance >= value, "Insufficient allowance");
_approve(owner, spender, currentAllowance - value as Uint<128>);
}
}
// Mint new tokens
export circuit _mint(
account: Either<ZswapCoinPublicKey, ContractAddress>,
value: Uint<128>
): [] {
assert(_isInitialized, "Not initialized");
assert(!isKeyOrAddressZero(account), "Invalid receiver");
const MAX_UINT128 = 340282366920938463463374607431768211455;
assert(MAX_UINT128 - _totalSupply >= value, "Mint overflow");
_totalSupply = disclose(_totalSupply + value as Uint<128>);
const balance = balanceOf(account);
_balances.insert(disclose(account), disclose(balance + value as Uint<128>));
}
// Burn tokens
export circuit _burn(
account: Either<ZswapCoinPublicKey, ContractAddress>,
value: Uint<128>
): [] {
assert(_isInitialized, "Not initialized");
assert(!isKeyOrAddressZero(account), "Invalid sender");
const balance = balanceOf(account);
assert(balance >= value, "Insufficient balance");
_balances.insert(disclose(account), disclose(balance - value as Uint<128>));
_totalSupply = disclose(_totalSupply - value as Uint<128>);
}
// Helper Functions
// These would typically be imported from OpenZeppelin's Utils module
circuit isKeyOrAddressZero(keyOrAddress: Either<ZswapCoinPublicKey, ContractAddress>): Boolean {
return isContractAddress(keyOrAddress)
? default<ContractAddress> == keyOrAddress.right
: default<ZswapCoinPublicKey> == keyOrAddress.left;
}
circuit isContractAddress(keyOrAddress: Either<ZswapCoinPublicKey, ContractAddress>): Boolean {
return !keyOrAddress.is_left;
}
circuit burnAddress(): Either<ZswapCoinPublicKey, ContractAddress> {
return left<ZswapCoinPublicKey, ContractAddress>(default<ZswapCoinPublicKey>);
}