Example Contracts

Real-world contract patterns from the Symvasi validation suite. Progressively more complex.

Hello Storage

Beginner

The simplest possible contract. Stores a greeting and exposes a reader.

hello_storage.sym
1/// The simplest Symvasi contract.
2contract HelloStorage {
3
4 pub storage greeting: String
5
6 @init
7 fn deploy(initial: String) {
8 self.greeting = initial
9 }
10
11 @call
12 pub fn set_greeting(new_greeting: String) {
13 require(caller() == origin(), "Only EOA can set greeting")
14 self.greeting = new_greeting
15 }
16
17 @view
18 pub fn get() -> String {
19 self.greeting
20 }
21}

Counter

Beginner

Incrementing counter with owner control and events.

counter.sym
1/// A simple incrementing counter with owner control.
2contract Counter {
3
4 pub storage count: u64
5 storage owner: Address
6
7 event Incremented { by: Address, new_value: u64 }
8 event Reset { by: Address }
9
10 @init
11 fn deploy() {
12 self.owner = caller()
13 self.count = 0
14 }
15
16 @call
17 pub fn increment() {
18 self.count += 1
19 emit Incremented { by: caller(), new_value: self.count }
20 }
21
22 @call
23 pub fn increment_by(amount: u64) {
24 require(amount > 0, "Amount must be positive")
25 self.count += amount
26 emit Incremented { by: caller(), new_value: self.count }
27 }
28
29 @call
30 pub fn reset() {
31 require(caller() == self.owner, "Only owner can reset")
32 self.count = 0
33 emit Reset { by: caller() }
34 }
35
36 @view
37 pub fn get() -> u64 { self.count }
38}

Owned Contract

Intermediate

Two-step ownership transfer pattern with interface implementation.

owned_contract.sym
1pub interface Ownable {
2 fn owner(self) -> Address
3 fn transfer_ownership(mut self, new_owner: Address)
4 fn renounce_ownership(mut self)
5}
6
7contract OwnedContract implements Ownable {
8
9 pub storage owner: Address
10 storage pending_owner: Option<Address>
11
12 event OwnershipTransferred { from: Address, to: Address }
13
14 @init
15 fn deploy() {
16 self.owner = caller()
17 self.pending_owner = None
18 }
19
20 @view
21 pub fn owner(self) -> Address { self.owner }
22
23 $ two-step transfer is safer than single-step
24 @call
25 pub fn transfer_ownership(mut self, new_owner: Address) {
26 require(caller() == self.owner, "Only owner")
27 self.pending_owner = Some(new_owner)
28 }
29
30 @call
31 pub fn accept_ownership() {
32 match self.pending_owner {
33 Some(pending) => {
34 require(caller() == pending, "Not pending owner")
35 let old = self.owner
36 self.owner = pending
37 self.pending_owner = None
38 emit OwnershipTransferred { from: old, to: pending }
39 },
40 None => require(false, "No pending transfer"),
41 }
42 }
43
44 @call
45 pub fn renounce_ownership(mut self) {
46 require(caller() == self.owner, "Only owner")
47 self.owner = self_addr()
48 }
49}

Fungible Token (SYM-20)

Intermediate

Full ERC-20 equivalent with mint, burn, transfer, and allowances.

fungible_token_sym_20_.sym
1/// SYM-20 fungible token standard.
2contract FungibleToken {
3
4 pub storage name: String
5 pub storage symbol: String
6 pub storage decimals: u8
7 pub storage total_supply: u128
8 storage balances: Map<Address, u128>
9 storage owner: Address
10
11 event Transfer { from: Address, to: Address, amount: u128 }
12 event Mint { to: Address, amount: u128 }
13 event Burn { from: Address, amount: u128 }
14
15 @init
16 fn deploy(name: String, symbol: String, decimals: u8, initial_supply: u128) {
17 self.name = name
18 self.symbol = symbol
19 self.decimals = decimals
20 self.owner = caller()
21 self.total_supply = initial_supply
22 self.balances[caller()] = initial_supply
23 emit Mint { to: caller(), amount: initial_supply }
24 }
25
26 @view
27 pub fn balance_of(addr: Address) -> u128 { self.balances[addr] }
28
29 @call
30 pub fn transfer(to: Address, amount: u128) -> Result<(), Error> {
31 _transfer(caller(), to, amount)
32 }
33
34 @call
35 pub fn mint(to: Address, amount: u128) -> Result<(), Error> {
36 require(caller() == self.owner, "Only owner can mint")
37 self.total_supply += amount
38 self.balances[to] += amount
39 emit Mint { to: to, amount: amount }
40 Ok(())
41 }
42
43 @call
44 pub fn burn(amount: u128) -> Result<(), Error> {
45 require(self.balances[caller()] >= amount, "Insufficient balance")
46 self.balances[caller()] -= amount
47 self.total_supply -= amount
48 emit Burn { from: caller(), amount: amount }
49 Ok(())
50 }
51
52 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] -= amount
56 self.balances[to] += amount
57 emit Transfer { from: from, to: to, amount: amount }
58 Ok(())
59 }
60}

Multi-Sig Wallet

Advanced

N-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 {
3
4 storage owners: Vec<Address>
5 storage required: u64
6 storage tx_count: u64
7
8 pub struct Transaction {
9 pub to: Address,
10 pub value: Balance,
11 pub executed: bool,
12 pub approvals: u64,
13 }
14
15 storage transactions: Map<u64, Transaction>
16 storage approved: Map<u64, Map<Address, bool>>
17
18 event TxSubmitted { tx_id: u64, by: Address }
19 event TxApproved { tx_id: u64, by: Address }
20 event TxExecuted { tx_id: u64, by: Address }
21
22 @init
23 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 = owners
28 self.required = required
29 self.tx_count = 0
30 }
31
32 @payable
33 pub fn submit(to: Address, value: Balance) -> u64 {
34 require(_is_owner(caller()), "Not an owner")
35 let id = self.tx_count
36 self.transactions[id] = Transaction {
37 to: to, value: value, executed: false, approvals: 0,
38 }
39 self.tx_count += 1
40 emit TxSubmitted { tx_id: id, by: caller() }
41 id
42 }
43
44 @call
45 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()] = true
49 self.transactions[tx_id].approvals += 1
50 emit TxApproved { tx_id: tx_id, by: caller() }
51 }
52
53 @call
54 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 = true
59 send(tx.to, tx.value)?
60 emit TxExecuted { tx_id: tx_id, by: caller() }
61 Ok(())
62 }
63
64 priv fn _is_owner(addr: Address) -> bool {
65 for owner in self.owners {
66 if owner == addr { return true }
67 }
68 false
69 }
70}