elko/examples/
erc20.rs

1use crate::examples::Example;
2/// ERC20 example
3pub const ERC20: Example = Example {
4    lib_rs: r#"
5//! ${name}
6#![cfg_attr(target_arch = "wasm32", no_std)]
7#![cfg_attr(target_arch = "wasm32", no_main)]
8    
9extern crate zink;
10    
11use zink::{
12    primitives::{Address, String32, U256},
13    DoubleKeyMapping, Mapping, Storage,
14};
15    
16#[zink::storage(String32)]
17pub struct Name;
18    
19#[zink::storage(String32)]
20pub struct Symbol;
21    
22#[zink::storage(U256)]
23pub struct TotalSupply;
24    
25#[zink::storage(Address, U256)]
26pub struct Balances;
27    
28#[zink::storage(Address, Address, U256)]
29pub struct Allowance;
30    
31/// Initialize the contract with name and symbol.
32#[zink::external]
33pub fn init(name: String32, symbol: String32) {
34    Name::set(name);
35    Symbol::set(symbol);
36}
37    
38/// Get the number of decimals (fixed at 8).
39#[zink::external]
40pub fn decimals() -> u32 {
41    8
42}
43    
44/// Transfer tokens from caller to another address.
45#[zink::external]
46pub fn transfer(to: Address, value: U256) -> bool {
47    let owner = Address::caller();
48    _transfer(owner, to, value);
49    true
50}
51    
52/// Approve a spender to transfer tokens on behalf of the caller.
53#[zink::external]
54pub fn approve(spender: Address, value: U256) -> bool {
55    let owner = Address::caller();
56    _approve(owner, spender, value);
57    true
58}
59    
60/// Transfer tokens from one address to another using an allowance.
61#[zink::external]
62pub fn transfer_from(from: Address, to: Address, value: U256) -> bool {
63    let spender = Address::caller();
64    _spend_allowance(from, spender, value);
65    _transfer(from, to, value);
66    true
67}
68    
69#[no_mangle]
70fn _transfer(from: Address, to: Address, value: U256) {
71    if from.eq(Address::empty()) {
72        zink::revert!("Empty from address");
73    }
74    if to.eq(Address::empty()) {
75        zink::revert!("Empty to address");
76    }
77    _update(from, to, value);
78}
79    
80#[no_mangle]
81fn _update(from: Address, to: Address, value: U256) {
82    if from.eq(Address::empty()) {
83        TotalSupply::set(TotalSupply::get().add(value));
84    } else {
85        let from_balance = Balances::get(from);
86        if from_balance.lt(value) {
87            zink::revert!("Insufficient balance");
88        }
89        Balances::set(from, from_balance.sub(value));
90    }
91    if to.eq(Address::empty()) {
92        TotalSupply::set(TotalSupply::get().sub(value));
93    } else {
94        TotalSupply::set(TotalSupply::get().add(value));
95    }
96}
97    
98#[no_mangle]
99fn _approve(owner: Address, spender: Address, value: U256) {
100    if owner.eq(Address::empty()) {
101        zink::revert!("ERC20 Invalid approval");
102    }
103    if spender.eq(Address::empty()) {
104        zink::revert!("ERC20 Invalid spender");
105    }
106    Allowance::set(owner, spender, value);
107}
108    
109#[no_mangle]
110fn _spend_allowance(owner: Address, spender: Address, value: U256) {
111    let current_allowance = Allowance::get(owner, spender);
112    if current_allowance.lt(U256::max()) {
113        if current_allowance.lt(value) {
114            zink::revert!("ERC20 Insufficient allowance");
115        }
116        _approve(owner, spender, current_allowance.sub(value));
117    }
118}
119    
120#[cfg(not(target_arch = "wasm32"))]
121fn main() {}
122"#,
123    readme: r#"
124# ${name}
125
126> An EVM contract written in Rust with [The Zink Project][zink].
127
128## Getting Started
129
130```
131cargo install zinkup
132elko build
133ls target/zink/${name}.bin
134```
135
136[zink]: https://github.com/zink-lang/zink
137"#,
138};