elko/examples/
erc20.rs
1use crate::examples::Example;
2pub 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};