Sitelet https://github.com/r55-eth/r55/commit/fdc04c90fb36c9e2fff6f9759fb6ecc12e671086
Skip to content

Commit fdc04c9

Browse files
authored
Basic ERC20 impl (#18)
1 parent fa8bfc8 commit fdc04c9

5 files changed

Lines changed: 149 additions & 52 deletions

File tree

‎erc20/src/lib.rs‎

Lines changed: 47 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -8,37 +8,69 @@ use eth_riscv_runtime::types::Mapping;
88

99
use alloy_core::primitives::{Address, address, U256};
1010

11+
extern crate alloc;
12+
use alloc::string::String;
13+
1114
#[derive(Default)]
1215
pub struct ERC20 {
13-
balance: Mapping<Address, u64>,
16+
balances: Mapping<Address, u64>,
17+
allowances: Mapping<Address, Mapping<Address, u64>>,
18+
total_supply: U256,
19+
name: String,
20+
symbol: String,
21+
decimals: u8,
1422
}
1523

1624
#[contract]
1725
impl ERC20 {
1826
pub fn balance_of(&self, owner: Address) -> u64 {
19-
self.balance.read(owner)
27+
self.balances.read(owner)
2028
}
2129

22-
pub fn transfer(&self, from: Address, to: Address, value: u64) {
23-
let from_balance = self.balance.read(from);
24-
let to_balance = self.balance.read(to);
25-
30+
pub fn transfer(&self, to: Address, value: u64) -> bool {
31+
let from = msg_sender();
32+
let from_balance = self.balances.read(from);
33+
let to_balance = self.balances.read(to);
2634
if from == to || from_balance < value {
2735
revert();
2836
}
2937

30-
self.balance.write(from, from_balance - value);
31-
self.balance.write(to, to_balance + value);
38+
self.balances.write(from, from_balance - value);
39+
self.balances.write(to, to_balance + value);
40+
41+
true
42+
}
43+
44+
pub fn approve(&self, spender: Address, value: u64) -> bool {
45+
let spender_allowances = self.allowances.read(msg_sender());
46+
spender_allowances.write(spender, value);
47+
true
48+
}
49+
50+
pub fn transfer_from(&self, sender: Address, recipient: Address, amount: u64) -> bool {
51+
let allowance = self.allowances.read(sender).read(msg_sender());
52+
let sender_balance = self.balances.read(sender);
53+
let recipient_balance = self.balances.read(recipient);
54+
55+
self.allowances.read(sender).write(msg_sender(), allowance - amount);
56+
self.balances.write(sender, sender_balance - amount);
57+
self.balances.write(recipient, recipient_balance + amount);
58+
59+
true
60+
}
61+
62+
pub fn total_supply(&self) -> U256 {
63+
self.total_supply
64+
}
65+
66+
pub fn allowance(&self, owner: Address, spender: Address) -> u64 {
67+
self.allowances.read(owner).read(spender)
3268
}
3369

34-
#[payable]
3570
pub fn mint(&self, to: Address, value: u64) {
3671
let owner = msg_sender();
37-
if owner != address!("0000000000000000000000000000000000000007") {
38-
revert();
39-
}
40-
41-
let to_balance = self.balance.read(to);
42-
self.balance.write(to, to_balance + value);
72+
let to_balance = self.balances.read(to);
73+
self.balances.write(to, to_balance + value);
4374
}
75+
4476
}

‎eth-riscv-runtime/src/lib.rs‎

Lines changed: 11 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -50,17 +50,21 @@ pub fn return_riscv(addr: u64, offset: u64) -> ! {
5050
unreachable!()
5151
}
5252

53-
pub fn sload(key: u64) -> u64 {
54-
let value: u64;
53+
pub fn sload(key: u64) -> U256 {
54+
let first: u64;
55+
let second: u64;
56+
let third: u64;
57+
let fourth: u64;
5558
unsafe {
56-
asm!("ecall", lateout("a0") value, in("a0") key, in("t0") u8::from(Syscall::SLoad));
59+
asm!("ecall", lateout("a0") first, lateout("a1") second, lateout("a2") third, lateout("a3") fourth, in("a0") key, in("t0") u8::from(Syscall::SLoad));
5760
}
58-
value
61+
U256::from_limbs([first, second, third, fourth])
5962
}
6063

61-
pub fn sstore(key: u64, value: u64) {
62-
unsafe {
63-
asm!("ecall", in("a0") key, in("a1") value, in("t0") u8::from(Syscall::SStore));
64+
pub fn sstore(key: u64, value: U256) {
65+
let limbs = value.as_limbs();
66+
unsafe {
67+
asm!("ecall", in("a0") key, in("a1") limbs[0], in("a2") limbs[1], in("a3") limbs[2], in("a4") limbs[3], in("t0") u8::from(Syscall::SStore));
6468
}
6569
}
6670

‎eth-riscv-runtime/src/types.rs‎

Lines changed: 45 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,9 @@
1-
use core::marker::PhantomData;
21
use core::default::Default;
2+
use core::marker::PhantomData;
33

44
use crate::*;
55

6-
use alloy_core::primitives::Address;
6+
use alloy_sol_types::{SolType, SolValue};
77

88
extern crate alloc;
99
use alloc::vec::Vec;
@@ -12,12 +12,50 @@ use alloc::vec::Vec;
1212
#[derive(Default)]
1313
pub struct Mapping<K, V> {
1414
id: u64,
15-
pd: PhantomData<(K, V)>
15+
pd: PhantomData<(K, V)>,
16+
}
17+
18+
/// A trait for types that can be read from and written to storage slots
19+
pub trait StorageStorable {
20+
fn read(key: u64) -> Self;
21+
fn write(&self, key: u64);
22+
}
23+
24+
impl<V> StorageStorable for V
25+
where
26+
V: SolValue + core::convert::From<<<V as SolValue>::SolType as SolType>::RustType>,
27+
{
28+
fn read(encoded_key: u64) -> Self {
29+
let bytes: [u8; 32] = sload(encoded_key).to_be_bytes();
30+
Self::abi_decode(&bytes, false).unwrap_or_else(|_| revert())
31+
}
32+
33+
fn write(&self, key: u64) {
34+
let bytes = self.abi_encode();
35+
let mut padded = [0u8; 32];
36+
padded[..bytes.len()].copy_from_slice(&bytes);
37+
sstore(key, U256::from_be_bytes(padded));
38+
}
1639
}
1740

18-
impl<K: ToBytes, V: Into<u64> + From<u64>> Mapping<K, V> {
41+
impl<K: SolValue, V: StorageStorable> StorageStorable for Mapping<K, V> {
42+
fn read(encoded_key: u64) -> Self {
43+
Self {
44+
id: encoded_key,
45+
pd: PhantomData,
46+
}
47+
}
48+
49+
fn write(&self, _key: u64) {
50+
// Mapping types can not directly be written to a storage slot
51+
// Instead the elements they contain need to be individually written to their own slots
52+
revert();
53+
}
54+
}
55+
56+
impl<K: SolValue, V: StorageStorable> Mapping<K, V> {
1957
pub fn encode_key(&self, key: K) -> u64 {
20-
let key_bytes = key.to_bytes();
58+
let key_bytes = key.abi_encode();
2159
let id_bytes = self.id.to_le_bytes();
2260

2361
// Concatenate the key bytes and id bytes
@@ -36,20 +74,10 @@ impl<K: ToBytes, V: Into<u64> + From<u64>> Mapping<K, V> {
3674
}
3775

3876
pub fn read(&self, key: K) -> V {
39-
sload(self.encode_key(key)).into()
77+
V::read(self.encode_key(key))
4078
}
4179

4280
pub fn write(&self, key: K, value: V) {
43-
sstore(self.encode_key(key), value.into());
44-
}
45-
}
46-
47-
pub trait ToBytes {
48-
fn to_bytes(&self) -> Vec<u8>;
49-
}
50-
51-
impl ToBytes for Address {
52-
fn to_bytes(&self) -> Vec<u8> {
53-
self.0.to_vec()
81+
value.write(self.encode_key(key));
5482
}
5583
}

‎r55/src/exec.rs‎

Lines changed: 11 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,7 @@ pub fn run_tx(db: &mut InMemoryDB, addr: &Address, calldata: Vec<u8>) -> Result<
4747
let mut evm = Evm::builder()
4848
.with_db(db)
4949
.modify_tx_env(|tx| {
50-
tx.caller = address!("0000000000000000000000000000000000000007");
50+
tx.caller = address!("0000000000000000000000000000000000000001");
5151
tx.transact_to = TransactTo::Call(*addr);
5252
tx.data = calldata.into();
5353
tx.value = U256::from(0);
@@ -192,7 +192,11 @@ fn execute_riscv(
192192
let key: u64 = emu.cpu.xregs.read(10);
193193
match host.sload(interpreter.contract.target_address, U256::from(key)) {
194194
Some((value, _is_cold)) => {
195-
emu.cpu.xregs.write(10, value.as_limbs()[0]);
195+
let limbs = value.as_limbs();
196+
emu.cpu.xregs.write(10, limbs[0]);
197+
emu.cpu.xregs.write(11, limbs[1]);
198+
emu.cpu.xregs.write(12, limbs[2]);
199+
emu.cpu.xregs.write(13, limbs[3]);
196200
}
197201
_ => {
198202
return return_revert(interpreter);
@@ -201,11 +205,14 @@ fn execute_riscv(
201205
}
202206
Syscall::SStore => {
203207
let key: u64 = emu.cpu.xregs.read(10);
204-
let value: u64 = emu.cpu.xregs.read(11);
208+
let first: u64 = emu.cpu.xregs.read(11);
209+
let second: u64 = emu.cpu.xregs.read(12);
210+
let third: u64 = emu.cpu.xregs.read(13);
211+
let fourth: u64 = emu.cpu.xregs.read(14);
205212
host.sstore(
206213
interpreter.contract.target_address,
207214
U256::from(key),
208-
U256::from(value),
215+
U256::from_limbs([first, second, third, fourth]),
209216
);
210217
}
211218
Syscall::Call => {

‎r55/src/main.rs‎

Lines changed: 35 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -132,24 +132,50 @@ fn test_runtime_from_binary() -> eyre::Result<()> {
132132

133133
let selector_balance = &keccak256("balance_of")[0..4];
134134
let selector_mint = &keccak256("mint")[0..4];
135-
let to: Address = address!("0000000000000000000000000000000000000007");
135+
let selector_transfer = &keccak256("transfer")[0..4];
136+
let selector_approve = &keccak256("approve")[0..4];
137+
let selector_allowance = &keccak256("allowance")[0..4];
138+
let alice: Address = address!("0000000000000000000000000000000000000001");
139+
let bob: Address = address!("0000000000000000000000000000000000000002");
140+
let carol: Address = address!("0000000000000000000000000000000000000003");
136141
let value_mint: u64 = 42;
137-
let mut calldata_balance = to.abi_encode();
138-
let mut calldata_mint = (to, value_mint).abi_encode();
142+
let value_transfer: u64 = 21;
143+
let mut calldata_balance = alice.abi_encode();
144+
let mut calldata_mint = (alice, value_mint).abi_encode();
145+
let mut calldata_transfer = (bob, value_transfer).abi_encode();
146+
let mut calldata_approve = (carol, value_transfer).abi_encode();
147+
let mut calldata_allowance = (alice, carol).abi_encode();
139148

140-
add_balance_to_db(&mut db, to, 1e18 as u64);
149+
add_balance_to_db(&mut db, alice, 1e18 as u64);
141150

142151
let mut complete_calldata_balance = selector_balance.to_vec();
143152
complete_calldata_balance.append(&mut calldata_balance);
144153

145154
let mut complete_calldata_mint = selector_mint.to_vec();
146155
complete_calldata_mint.append(&mut calldata_mint);
156+
let mut complete_calldata_transfer = selector_transfer.to_vec();
157+
complete_calldata_transfer.append(&mut calldata_transfer);
147158

159+
let mut complete_calldata_approve = selector_approve.to_vec();
160+
complete_calldata_approve.append(&mut calldata_approve);
161+
162+
let mut complete_calldata_allowance = selector_allowance.to_vec();
163+
complete_calldata_allowance.append(&mut calldata_allowance);
164+
165+
// Mint 42 tokens to Alice
148166
run_tx(&mut db, &CONTRACT_ADDR, complete_calldata_mint.clone())?;
167+
// Check Alice's balance
149168
run_tx(&mut db, &CONTRACT_ADDR, complete_calldata_balance.clone())?;
169+
// Transfer 21 tokens from Alice to Bob
170+
run_tx(&mut db, &CONTRACT_ADDR, complete_calldata_transfer.clone())?;
171+
// Check Alice's balance
172+
run_tx(&mut db, &CONTRACT_ADDR, complete_calldata_balance.clone())?;
173+
// Approve Carol to spend 21 token from Alice
174+
run_tx(&mut db, &CONTRACT_ADDR, complete_calldata_approve.clone())?;
175+
// Check Carol's allowance
176+
run_tx(&mut db, &CONTRACT_ADDR, complete_calldata_allowance.clone())?;
150177

151178
Ok(())
152-
153179
/*
154180
let account_db = &evm.db().accounts[&CONTRACT_ADDR];
155181
println!("Account storage: {:?}", account_db.storage);
@@ -161,12 +187,12 @@ fn test_runtime_from_binary() -> eyre::Result<()> {
161187
fn test_runtime(addr: &Address, db: &mut InMemoryDB) -> Result<()> {
162188
let selector_balance = &keccak256("balance_of")[0..4];
163189
let selector_mint = &keccak256("mint")[0..4];
164-
let to: Address = address!("0000000000000000000000000000000000000007");
190+
let alice: Address = address!("0000000000000000000000000000000000000001");
165191
let value_mint: u64 = 42;
166-
let mut calldata_balance = to.abi_encode();
167-
let mut calldata_mint = (to, value_mint).abi_encode();
192+
let mut calldata_balance = alice.abi_encode();
193+
let mut calldata_mint = (alice, value_mint).abi_encode();
168194

169-
add_balance_to_db(db, to, 1e18 as u64);
195+
add_balance_to_db(db, alice, 1e18 as u64);
170196

171197
let mut complete_calldata_balance = selector_balance.to_vec();
172198
complete_calldata_balance.append(&mut calldata_balance);

0 commit comments

Comments
 (0)