Example Contracts
Real-world contract patterns from the Symvasi validation suite. Progressively more complex.
Hello Storage
BeginnerThe simplest possible contract. Stores a greeting and exposes a reader.
hello_storage.sym
1/// The simplest Symvasi contract.2contract HelloStorage {34 pub storage greeting: String56 @init7 fn deploy(initial: String) {8 self.greeting = initial9 }1011 @call12 pub fn set_greeting(new_greeting: String) {13 require(caller() == origin(), "Only EOA can set greeting")14 self.greeting = new_greeting15 }1617 @view18 pub fn get() -> String {19 self.greeting20 }21}Counter
BeginnerIncrementing counter with owner control and events.
counter.sym
1/// A simple incrementing counter with owner control.2contract Counter {34 pub storage count: u645 storage owner: Address67 event Incremented { by: Address, new_value: u64 }8 event Reset { by: Address }910 @init11 fn deploy() {12 self.owner = caller()13 self.count = 014 }1516 @call17 pub fn increment() {18 self.count += 119 emit Incremented { by: caller(), new_value: self.count }20 }2122 @call23 pub fn increment_by(amount: u64) {24 require(amount > 0, "Amount must be positive")25 self.count += amount26 emit Incremented { by: caller(), new_value: self.count }27 }2829 @call30 pub fn reset() {31 require(caller() == self.owner, "Only owner can reset")32 self.count = 033 emit Reset { by: caller() }34 }3536 @view37 pub fn get() -> u64 { self.count }38}Owned Contract
IntermediateTwo-step ownership transfer pattern with interface implementation.
owned_contract.sym
1pub interface Ownable {2 fn owner(self) -> Address3 fn transfer_ownership(mut self, new_owner: Address)4 fn renounce_ownership(mut self)5}67contract OwnedContract implements Ownable {89 pub storage owner: Address10 storage pending_owner: Option<Address>1112 event OwnershipTransferred { from: Address, to: Address }1314 @init15 fn deploy() {16 self.owner = caller()17 self.pending_owner = None18 }1920 @view21 pub fn owner(self) -> Address { self.owner }2223 $ two-step transfer is safer than single-step24 @call25 pub fn transfer_ownership(mut self, new_owner: Address) {26 require(caller() == self.owner, "Only owner")27 self.pending_owner = Some(new_owner)28 }2930 @call31 pub fn accept_ownership() {32 match self.pending_owner {33 Some(pending) => {34 require(caller() == pending, "Not pending owner")35 let old = self.owner36 self.owner = pending37 self.pending_owner = None38 emit OwnershipTransferred { from: old, to: pending }39 },40 None => require(false, "No pending transfer"),41 }42 }4344 @call45 pub fn renounce_ownership(mut self) {46 require(caller() == self.owner, "Only owner")47 self.owner = self_addr()48 }49}Fungible Token (SYM-20)
IntermediateFull ERC-20 equivalent with mint, burn, transfer, and allowances.
fungible_token_sym_20_.sym
1/// SYM-20 fungible token standard.2contract FungibleToken {34 pub storage name: String5 pub storage symbol: String6 pub storage decimals: u87 pub storage total_supply: u1288 storage balances: Map<Address, u128>9 storage owner: Address1011 event Transfer { from: Address, to: Address, amount: u128 }12 event Mint { to: Address, amount: u128 }13 event Burn { from: Address, amount: u128 }1415 @init16 fn deploy(name: String, symbol: String, decimals: u8, initial_supply: u128) {17 self.name = name18 self.symbol = symbol19 self.decimals = decimals20 self.owner = caller()21 self.total_supply = initial_supply22 self.balances[caller()] = initial_supply23 emit Mint { to: caller(), amount: initial_supply }24 }2526 @view27 pub fn balance_of(addr: Address) -> u128 { self.balances[addr] }2829 @call30 pub fn transfer(to: Address, amount: u128) -> Result<(), Error> {31 _transfer(caller(), to, amount)32 }3334 @call35 pub fn mint(to: Address, amount: u128) -> Result<(), Error> {36 require(caller() == self.owner, "Only owner can mint")37 self.total_supply += amount38 self.balances[to] += amount39 emit Mint { to: to, amount: amount }40 Ok(())41 }4243 @call44 pub fn burn(amount: u128) -> Result<(), Error> {45 require(self.balances[caller()] >= amount, "Insufficient balance")46 self.balances[caller()] -= amount47 self.total_supply -= amount48 emit Burn { from: caller(), amount: amount }49 Ok(())50 }5152 priv fn _transfer(from: Address, to: Address, amount: u128) -> Result<(), Error> {53 require(amount > 0, "Zero transfer")54 require(self.balances[from] >= amount, "Insufficient balance")55 self.balances[from] -= amount56 self.balances[to] += amount57 emit Transfer { from: from, to: to, amount: amount }58 Ok(())59 }60}Multi-Sig Wallet
AdvancedN-of-M multi-signature wallet with submit, approve, execute, and revoke.
multi_sig_wallet.sym
1/// Multi-signature wallet requiring N-of-M approval.2contract MultiSig {34 storage owners: Vec<Address>5 storage required: u646 storage tx_count: u6478 pub struct Transaction {9 pub to: Address,10 pub value: Balance,11 pub executed: bool,12 pub approvals: u64,13 }1415 storage transactions: Map<u64, Transaction>16 storage approved: Map<u64, Map<Address, bool>>1718 event TxSubmitted { tx_id: u64, by: Address }19 event TxApproved { tx_id: u64, by: Address }20 event TxExecuted { tx_id: u64, by: Address }2122 @init23 fn deploy(owners: Vec<Address>, required: u64) {24 require(owners.len() > 0, "No owners")25 require(required > 0, "Required must be > 0")26 require(required <= owners.len() as u64, "Required exceeds owners")27 self.owners = owners28 self.required = required29 self.tx_count = 030 }3132 @payable33 pub fn submit(to: Address, value: Balance) -> u64 {34 require(_is_owner(caller()), "Not an owner")35 let id = self.tx_count36 self.transactions[id] = Transaction {37 to: to, value: value, executed: false, approvals: 0,38 }39 self.tx_count += 140 emit TxSubmitted { tx_id: id, by: caller() }41 id42 }4344 @call45 pub fn approve(tx_id: u64) {46 require(_is_owner(caller()), "Not an owner")47 require(!self.approved[tx_id][caller()], "Already approved")48 self.approved[tx_id][caller()] = true49 self.transactions[tx_id].approvals += 150 emit TxApproved { tx_id: tx_id, by: caller() }51 }5253 @call54 pub fn execute(tx_id: u64) -> Result<(), Error> {55 let tx = self.transactions[tx_id]56 require(!tx.executed, "Already executed")57 require(tx.approvals >= self.required, "Not enough approvals")58 self.transactions[tx_id].executed = true59 send(tx.to, tx.value)?60 emit TxExecuted { tx_id: tx_id, by: caller() }61 Ok(())62 }6364 priv fn _is_owner(addr: Address) -> bool {65 for owner in self.owners {66 if owner == addr { return true }67 }68 false69 }70}