Example Contracts
Three complete contracts, matching the implemented semantics of the compiler and VM. All three come from the compiler's examples/ directory and compile with sclc today.
A token contract
Demonstrates field protection via @writeable_by, caller gating with SENDER_ADDRESS(), overflow and supply-cap checks, and persistent balances in a map.
contract Token {
@writeable_by(init, transfer_ownership)
owner: Str;
@writeable_by(init, mint, burn)
total_supply: Int;
@writeable_by(init, pause, unpause)
paused: Int;
@writeable_by(init)
max_supply: Int;
@writeable_by(init)
initialised: Int;
fn init(owner_addr: Str) {
if (initialised == 1) {
return 0;
}
owner = owner_addr;
total_supply = 0;
paused = 0;
max_supply = 50000000000000000;
initialised = 1;
MAP_SET("balances", owner_addr, 0);
return 1;
}
fn mint(to: Str, amount: Int) {
let sender = SENDER_ADDRESS();
if (paused == 1) {
return 0;
}
if (owner != sender) {
return 0;
}
let current = MAP_GET("balances", to);
let sum = current + amount;
if (sum < current) {
return 0; // overflow check
}
let temp = total_supply + amount;
if (temp > max_supply) {
return 0;
}
MAP_SET("balances", to, sum);
total_supply = total_supply + amount;
return 1;
}
fn transfer(to: Str, amount: Int) {
if (paused == 1) {
return 0;
}
let sender = SENDER_ADDRESS();
let sender_balance = MAP_GET("balances", sender);
let recipient_balance = MAP_GET("balances", to);
if (sender_balance < amount) {
return 0;
}
MAP_SET("balances", sender, sender_balance - amount);
MAP_SET("balances", to, recipient_balance + amount);
return 1;
}
fn pause() {
let sender = SENDER_ADDRESS();
if (owner != sender) {
return 0;
}
paused = 1;
return 1;
}
fn balance_of(addr: Str) {
return MAP_GET("balances", addr);
}
fn get_total_supply() {
return total_supply;
}
}
Note the patterns: functions return 1 for success and 0 for failure; the initialised flag makes init one-shot; and every state-changing function checks paused and the caller before touching a balance.
A name registry with events
A minimal registry showing event emission for off-chain indexing. Clients subscribe to NameRegistered and NameTransferred via the node's events API instead of polling state.
contract BNS {
event NameRegistered(name: Str, owner: Str);
event NameTransferred(name: Str, old_owner: Str, new_owner: Str);
fn register(name: Str, owner: Str) {
MAP_SET("names", name, owner);
emit NameRegistered(name, owner);
return 1;
}
fn transfer(name: Str, new_owner: Str) {
let old_owner = MAP_GET("names", name);
MAP_SET("names", name, new_owner);
emit NameTransferred(name, old_owner, new_owner);
return 1;
}
fn lookup(name: Str) {
return MAP_GET("names", name);
}
}
A staking contract with cross-contract calls
Demonstrates CALL (moving tokens in as the user) versus CALL_AS_CONTRACT (paying tokens out as the contract), block-height time-locks, and storage cleanup.
contract Staking {
@writeable_by(init)
token_contract_id: Str;
fn init(token_contract_id_param: Str) {
token_contract_id = token_contract_id_param;
return 0;
}
fn stake(sender: Str, amount: Int, lock_until: Int, btc_txid: Str) {
MAP_SET("stakes", sender, amount);
MAP_SET("locks", sender, lock_until);
MAP_SET("txids", sender, btc_txid);
return CALL(token_contract_id, "transfer", ["Staking", amount]);
}
fn claim(sender: Str, btc_txid: Str, current_block: Int) {
let lock_until = MAP_GET("locks", sender);
let staked_amount = MAP_GET("stakes", sender);
if (current_block < lock_until) {
return 0;
}
MAP_SET("stakes", sender, 0);
MAP_SET("locks", sender, 0);
MAP_SET("txids", sender, "");
return CALL_AS_CONTRACT(token_contract_id, "transfer", [sender, staked_amount]);
}
}
The stake path uses CALL, so the token contract sees the original caller and debits their balance. The claim path uses CALL_AS_CONTRACT, so the token contract sees the staking contract as the sender and pays out from the stake pool it holds.
Caveat
Only use built-ins listed in the Built-ins section. Do not rely on helpers that are not present in the compiler registry yet.
More examples (vesting with multiple beneficiaries, signature-gated emitters, and more) live in the compiler repository's examples/ directory.