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

Commit fc09ee6

Browse files
authored
feat: storage layout + read/write slots + mut keyword (#30)
1 parent 7cff9ae commit fc09ee6

13 files changed

Lines changed: 547 additions & 230 deletions

File tree

‎contract-derive/src/lib.rs‎

Lines changed: 54 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
extern crate proc_macro;
2-
use alloy_core::primitives::keccak256;
2+
use alloy_core::primitives::{keccak256, U256};
3+
use alloy_sol_types::SolValue;
34
use proc_macro::TokenStream;
45
use quote::{format_ident, quote};
56
use syn::{
@@ -257,11 +258,11 @@ pub fn contract(_attr: TokenStream, item: TokenStream) -> TokenStream {
257258
#emit_helper
258259

259260
impl Contract for #struct_name {
260-
fn call(&self) {
261+
fn call(&mut self) {
261262
self.call_with_data(&msg_data());
262263
}
263264

264-
fn call_with_data(&self, calldata: &[u8]) {
265+
fn call_with_data(&mut self, calldata: &[u8]) {
265266
let selector = u32::from_be_bytes([calldata[0], calldata[1], calldata[2], calldata[3]]);
266267
let calldata = &calldata[4..];
267268

@@ -276,7 +277,7 @@ pub fn contract(_attr: TokenStream, item: TokenStream) -> TokenStream {
276277

277278
#[eth_riscv_runtime::entry]
278279
fn main() -> ! {
279-
let contract = #struct_name::default();
280+
let mut contract = #struct_name::default();
280281
contract.call();
281282
eth_riscv_runtime::return_riscv(0, 0)
282283
}
@@ -342,3 +343,52 @@ pub fn interface(_attr: TokenStream, item: TokenStream) -> TokenStream {
342343

343344
TokenStream::from(output)
344345
}
346+
347+
#[proc_macro_attribute]
348+
pub fn storage(_attr: TokenStream, input: TokenStream) -> TokenStream {
349+
let input = parse_macro_input!(input as DeriveInput);
350+
let name = &input.ident;
351+
let vis = &input.vis;
352+
353+
let fields = match &input.data {
354+
Data::Struct(data) => match &data.fields {
355+
Fields::Named(fields) => &fields.named,
356+
_ => {
357+
let output = quote! {
358+
#vis struct #name;
359+
impl #name { pub fn new() -> Self { Self {} } }
360+
};
361+
return TokenStream::from(output);
362+
}
363+
},
364+
_ => panic!("Storage derive only works on structs"),
365+
};
366+
367+
// Generate the struct definition with the same fields
368+
let struct_fields = fields.iter().map(|f| {
369+
let name = &f.ident;
370+
let ty = &f.ty;
371+
quote! { pub #name: #ty }
372+
});
373+
374+
// Generate initialization code for each field
375+
// TODO: PoC uses a naive strategy. Enhance to support complex types like tuples or custom structs.
376+
let init_fields = fields.iter().enumerate().map(|(i, f)| {
377+
let name = &f.ident;
378+
let slot = U256::from(i);
379+
let [limb0, limb1, limb2, limb3] = slot.as_limbs();
380+
quote! { #name: StorageLayout::allocate(#limb0, #limb1, #limb2, #limb3) }
381+
});
382+
383+
let expanded = quote! {
384+
#vis struct #name { #(#struct_fields,)* }
385+
386+
impl #name {
387+
pub fn default() -> Self {
388+
Self { #(#init_fields,)* }
389+
}
390+
}
391+
};
392+
393+
TokenStream::from(expanded)
394+
}

‎erc20/src/lib.rs‎

Lines changed: 45 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -3,22 +3,22 @@
33

44
use core::default::Default;
55

6-
use contract_derive::{contract, payable, Event};
7-
use eth_riscv_runtime::types::Mapping;
6+
use contract_derive::{contract, payable, storage, Event};
7+
use eth_riscv_runtime::types::{Mapping, Slot, StorageLayout};
88

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

1111
extern crate alloc;
1212
use alloc::string::String;
1313

14-
#[derive(Default)]
14+
#[storage]
1515
pub struct ERC20 {
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,
16+
total_supply: Slot<U256>,
17+
balances: Mapping<Address, U256>,
18+
allowances: Mapping<Address, Mapping<Address, U256>>,
19+
// name: String,
20+
// symbol: String,
21+
// decimals: u8,
2222
}
2323

2424
#[derive(Event)]
@@ -27,7 +27,7 @@ pub struct Transfer {
2727
pub from: Address,
2828
#[indexed]
2929
pub to: Address,
30-
pub value: u64,
30+
pub value: U256,
3131
}
3232

3333
#[derive(Event)]
@@ -36,16 +36,39 @@ pub struct Mint {
3636
pub from: Address,
3737
#[indexed]
3838
pub to: Address,
39-
pub value: u64,
39+
pub value: U256,
4040
}
4141

4242
#[contract]
4343
impl ERC20 {
44-
pub fn balance_of(&self, owner: Address) -> u64 {
45-
self.balances.read(owner)
44+
// -- STATE MODIFYING FUNCTIONS -------------------------------------------
45+
#[payable]
46+
pub fn mint(&mut self, to: Address, value: U256) -> bool {
47+
// TODO: implement constructors and store contract owner
48+
let _owner = msg_sender();
49+
50+
// increase user balance
51+
let to_balance = self.balances.read(to);
52+
self.balances.write(to, to_balance + value);
53+
log::emit(Transfer::new(
54+
address!("0000000000000000000000000000000000000000"),
55+
to,
56+
value,
57+
));
58+
59+
// increase total supply
60+
self.total_supply += value;
61+
62+
true
4663
}
4764

48-
pub fn transfer(&self, to: Address, value: u64) -> bool {
65+
pub fn approve(&mut self, spender: Address, value: U256) -> bool {
66+
let mut spender_allowances = self.allowances.read(msg_sender());
67+
spender_allowances.write(spender, value);
68+
true
69+
}
70+
71+
pub fn transfer(&mut self, to: Address, value: U256) -> bool {
4972
let from = msg_sender();
5073
let from_balance = self.balances.read(from);
5174
let to_balance = self.balances.read(to);
@@ -61,13 +84,7 @@ impl ERC20 {
6184
true
6285
}
6386

64-
pub fn approve(&self, spender: Address, value: u64) -> bool {
65-
let spender_allowances = self.allowances.read(msg_sender());
66-
spender_allowances.write(spender, value);
67-
true
68-
}
69-
70-
pub fn transfer_from(&self, sender: Address, recipient: Address, amount: u64) -> bool {
87+
pub fn transfer_from(&mut self, sender: Address, recipient: Address, amount: U256) -> bool {
7188
let allowance = self.allowances.read(sender).read(msg_sender());
7289
let sender_balance = self.balances.read(sender);
7390
let recipient_balance = self.balances.read(recipient);
@@ -81,25 +98,17 @@ impl ERC20 {
8198
true
8299
}
83100

101+
// -- GETTER FUNCTIONS ----------------------------------------------------
102+
84103
pub fn total_supply(&self) -> U256 {
85-
self.total_supply
104+
self.total_supply.read()
86105
}
87106

88-
pub fn allowance(&self, owner: Address, spender: Address) -> u64 {
89-
self.allowances.read(owner).read(spender)
107+
pub fn balance_of(&self, owner: Address) -> U256 {
108+
self.balances.read(owner)
90109
}
91110

92-
#[payable]
93-
pub fn mint(&self, to: Address, value: u64) -> bool {
94-
let owner = msg_sender();
95-
96-
let to_balance = self.balances.read(to);
97-
self.balances.write(to, to_balance + value);
98-
log::emit(Transfer::new(
99-
address!("0000000000000000000000000000000000000000"),
100-
to,
101-
value,
102-
));
103-
true
111+
pub fn allowance(&self, owner: Address, spender: Address) -> U256 {
112+
self.allowances.read(owner).read(spender)
104113
}
105114
}

‎erc20x/src/lib.rs‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ pub struct ERC20x;
1515

1616
#[contract]
1717
impl ERC20x {
18-
pub fn x_balance_of(&self, owner: Address, target: Address) -> u64 {
18+
pub fn x_balance_of(&self, owner: Address, target: Address) -> U256 {
1919
let token = IERC20::new(target);
2020
match token.balance_of(owner) {
2121
Some(balance) => balance,

‎erc20x_standalone/src/lib.rs‎

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,12 +14,12 @@ pub struct ERC20x;
1414

1515
#[interface]
1616
trait IERC20 {
17-
fn balance_of(&self, owner: Address) -> u64;
17+
fn balance_of(&self, owner: Address) -> U256;
1818
}
1919

2020
#[contract]
2121
impl ERC20x {
22-
pub fn x_balance_of(&self, owner: Address, target: Address) -> u64 {
22+
pub fn x_balance_of(&self, owner: Address, target: Address) -> U256 {
2323
let token = IERC20::new(target);
2424
match token.balance_of(owner) {
2525
Some(balance) => balance,

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

Lines changed: 27 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -10,8 +10,8 @@ pub use riscv_rt::entry;
1010

1111
mod alloc;
1212
pub mod block;
13-
pub mod tx;
1413
pub mod types;
14+
pub mod tx;
1515

1616
pub mod log;
1717
pub use log::{emit_log, Event};
@@ -22,8 +22,8 @@ pub use call::call_contract;
2222
const CALLDATA_ADDRESS: usize = 0x8000_0000;
2323

2424
pub trait Contract {
25-
fn call(&self);
26-
fn call_with_data(&self, calldata: &[u8]);
25+
fn call(&mut self);
26+
fn call_with_data(&mut self, calldata: &[u8]);
2727
}
2828

2929
pub unsafe fn slice_from_raw_parts(address: usize, length: usize) -> &'static [u8] {
@@ -56,21 +56,30 @@ pub fn return_riscv(addr: u64, offset: u64) -> ! {
5656
unreachable!()
5757
}
5858

59-
pub fn sload(key: u64) -> U256 {
60-
let first: u64;
61-
let second: u64;
62-
let third: u64;
63-
let fourth: u64;
59+
pub fn sload(key: U256) -> U256 {
60+
let key = key.as_limbs();
61+
let (val0, val1, val2, val3): (u64, u64, u64, u64);
6462
unsafe {
65-
asm!("ecall", lateout("a0") first, lateout("a1") second, lateout("a2") third, lateout("a3") fourth, in("a0") key, in("t0") u8::from(Syscall::SLoad));
63+
asm!(
64+
"ecall",
65+
lateout("a0") val0, lateout("a1") val1, lateout("a2") val2, lateout("a3") val3,
66+
in("a0") key[0], in("a1") key[1], in("a2") key[2], in("a3") key[3],
67+
in("t0") u8::from(Syscall::SLoad));
6668
}
67-
U256::from_limbs([first, second, third, fourth])
69+
U256::from_limbs([val0, val1, val2, val3])
6870
}
6971

70-
pub fn sstore(key: u64, value: U256) {
71-
let limbs = value.as_limbs();
72+
pub fn sstore(key: U256, value: U256) {
73+
let key = key.as_limbs();
74+
let value = value.as_limbs();
75+
7276
unsafe {
73-
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));
77+
asm!(
78+
"ecall",
79+
in("a0") key[0], in("a1") key[1], in("a2") key[2], in("a3") key[3],
80+
in("a4") value[0], in("a5") value[1], in("a6") value[2], in("a7") value[3],
81+
in("t0") u8::from(Syscall::SStore)
82+
);
7483
}
7584
}
7685

@@ -107,12 +116,8 @@ pub fn revert() -> ! {
107116
unreachable!()
108117
}
109118

110-
pub fn keccak256(offset: u64, size: u64) -> B256 {
111-
let first: u64;
112-
let second: u64;
113-
let third: u64;
114-
let fourth: u64;
115-
119+
pub fn keccak256(offset: u64, size: u64) -> U256 {
120+
let (first, second, third, fourth): (u64, u64, u64, u64);
116121
unsafe {
117122
asm!(
118123
"ecall",
@@ -125,21 +130,11 @@ pub fn keccak256(offset: u64, size: u64) -> B256 {
125130
in("t0") u8::from(Syscall::Keccak256)
126131
);
127132
}
128-
129-
let mut bytes = [0u8; 32];
130-
131-
bytes[0..8].copy_from_slice(&first.to_be_bytes());
132-
bytes[8..16].copy_from_slice(&second.to_be_bytes());
133-
bytes[16..24].copy_from_slice(&third.to_be_bytes());
134-
bytes[24..32].copy_from_slice(&fourth.to_be_bytes());
135-
136-
B256::from_slice(&bytes)
133+
U256::from_limbs([first, second, third, fourth])
137134
}
138135

139136
pub fn msg_sender() -> Address {
140-
let first: u64;
141-
let second: u64;
142-
let third: u64;
137+
let (first, second, third): (u64, u64, u64);
143138
unsafe {
144139
asm!("ecall", lateout("a0") first, lateout("a1") second, lateout("a2") third, in("t0") u8::from(Syscall::Caller));
145140
}
@@ -151,10 +146,7 @@ pub fn msg_sender() -> Address {
151146
}
152147

153148
pub fn msg_value() -> U256 {
154-
let first: u64;
155-
let second: u64;
156-
let third: u64;
157-
let fourth: u64;
149+
let (first, second, third, fourth): (u64, u64, u64, u64);
158150
unsafe {
159151
asm!("ecall", lateout("a0") first, lateout("a1") second, lateout("a2") third, lateout("a3") fourth, in("t0") u8::from(Syscall::CallValue));
160152
}

0 commit comments

Comments
 (0)