-
Notifications
You must be signed in to change notification settings - Fork 17
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
feat: storage layout + read/write slots + mut keyword #30
Changes from all commits
0da7443
bcc19ba
5f732fa
bfb03db
f008813
d7d7a7a
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -3,22 +3,22 @@ | |
|
||
use core::default::Default; | ||
|
||
use contract_derive::{contract, payable, Event}; | ||
use eth_riscv_runtime::types::Mapping; | ||
use contract_derive::{contract, payable, storage, Event}; | ||
use eth_riscv_runtime::types::{Mapping, Slot, StorageLayout}; | ||
|
||
use alloy_core::primitives::{address, Address, U256}; | ||
|
||
extern crate alloc; | ||
use alloc::string::String; | ||
|
||
#[derive(Default)] | ||
#[storage] | ||
pub struct ERC20 { | ||
balances: Mapping<Address, u64>, | ||
allowances: Mapping<Address, Mapping<Address, u64>>, | ||
total_supply: U256, | ||
name: String, | ||
symbol: String, | ||
decimals: u8, | ||
total_supply: Slot<U256>, | ||
balances: Mapping<Address, U256>, | ||
allowances: Mapping<Address, Mapping<Address, U256>>, | ||
// name: String, | ||
// symbol: String, | ||
// decimals: u8, | ||
} | ||
|
||
#[derive(Event)] | ||
|
@@ -27,7 +27,7 @@ pub struct Transfer { | |
pub from: Address, | ||
#[indexed] | ||
pub to: Address, | ||
pub value: u64, | ||
pub value: U256, | ||
} | ||
|
||
#[derive(Event)] | ||
|
@@ -36,16 +36,39 @@ pub struct Mint { | |
pub from: Address, | ||
#[indexed] | ||
pub to: Address, | ||
pub value: u64, | ||
pub value: U256, | ||
} | ||
|
||
#[contract] | ||
impl ERC20 { | ||
pub fn balance_of(&self, owner: Address) -> u64 { | ||
self.balances.read(owner) | ||
// -- STATE MODIFYING FUNCTIONS ------------------------------------------- | ||
#[payable] | ||
pub fn mint(&mut self, to: Address, value: U256) -> bool { | ||
// TODO: implement constructors and store contract owner | ||
let _owner = msg_sender(); | ||
|
||
// increase user balance | ||
let to_balance = self.balances.read(to); | ||
self.balances.write(to, to_balance + value); | ||
log::emit(Transfer::new( | ||
address!("0000000000000000000000000000000000000000"), | ||
to, | ||
value, | ||
)); | ||
|
||
// increase total supply | ||
self.total_supply += value; | ||
|
||
true | ||
} | ||
|
||
pub fn transfer(&self, to: Address, value: u64) -> bool { | ||
pub fn approve(&mut self, spender: Address, value: U256) -> bool { | ||
let mut spender_allowances = self.allowances.read(msg_sender()); | ||
spender_allowances.write(spender, value); | ||
true | ||
} | ||
|
||
pub fn transfer(&mut self, to: Address, value: U256) -> bool { | ||
let from = msg_sender(); | ||
let from_balance = self.balances.read(from); | ||
let to_balance = self.balances.read(to); | ||
|
@@ -61,13 +84,7 @@ impl ERC20 { | |
true | ||
} | ||
|
||
pub fn approve(&self, spender: Address, value: u64) -> bool { | ||
let spender_allowances = self.allowances.read(msg_sender()); | ||
spender_allowances.write(spender, value); | ||
true | ||
} | ||
|
||
pub fn transfer_from(&self, sender: Address, recipient: Address, amount: u64) -> bool { | ||
pub fn transfer_from(&mut self, sender: Address, recipient: Address, amount: U256) -> bool { | ||
let allowance = self.allowances.read(sender).read(msg_sender()); | ||
let sender_balance = self.balances.read(sender); | ||
let recipient_balance = self.balances.read(recipient); | ||
|
@@ -81,25 +98,17 @@ impl ERC20 { | |
true | ||
} | ||
|
||
// -- GETTER FUNCTIONS ---------------------------------------------------- | ||
|
||
pub fn total_supply(&self) -> U256 { | ||
self.total_supply | ||
self.total_supply.read() | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think you wrote this somewhere, but |
||
} | ||
|
||
pub fn allowance(&self, owner: Address, spender: Address) -> u64 { | ||
self.allowances.read(owner).read(spender) | ||
pub fn balance_of(&self, owner: Address) -> U256 { | ||
self.balances.read(owner) | ||
} | ||
|
||
#[payable] | ||
pub fn mint(&self, to: Address, value: u64) -> bool { | ||
let owner = msg_sender(); | ||
|
||
let to_balance = self.balances.read(to); | ||
self.balances.write(to, to_balance + value); | ||
log::emit(Transfer::new( | ||
address!("0000000000000000000000000000000000000000"), | ||
to, | ||
value, | ||
)); | ||
true | ||
pub fn allowance(&self, owner: Address, spender: Address) -> U256 { | ||
self.allowances.read(owner).read(spender) | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -10,8 +10,8 @@ pub use riscv_rt::entry; | |
|
||
mod alloc; | ||
pub mod block; | ||
pub mod tx; | ||
pub mod types; | ||
pub mod tx; | ||
|
||
pub mod log; | ||
pub use log::{emit_log, Event}; | ||
|
@@ -22,8 +22,8 @@ pub use call::call_contract; | |
const CALLDATA_ADDRESS: usize = 0x8000_0000; | ||
|
||
pub trait Contract { | ||
fn call(&self); | ||
fn call_with_data(&self, calldata: &[u8]); | ||
fn call(&mut self); | ||
fn call_with_data(&mut self, calldata: &[u8]); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It could also make sense later on to have immutable versions of these, not sure how that would work but it'd be cool. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. agreed. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. not right now |
||
} | ||
|
||
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) -> ! { | |
unreachable!() | ||
} | ||
|
||
pub fn sload(key: u64) -> U256 { | ||
let first: u64; | ||
let second: u64; | ||
let third: u64; | ||
let fourth: u64; | ||
pub fn sload(key: U256) -> U256 { | ||
let key = key.as_limbs(); | ||
let (val0, val1, val2, val3): (u64, u64, u64, u64); | ||
unsafe { | ||
asm!("ecall", lateout("a0") first, lateout("a1") second, lateout("a2") third, lateout("a3") fourth, in("a0") key, in("t0") u8::from(Syscall::SLoad)); | ||
asm!( | ||
"ecall", | ||
lateout("a0") val0, lateout("a1") val1, lateout("a2") val2, lateout("a3") val3, | ||
in("a0") key[0], in("a1") key[1], in("a2") key[2], in("a3") key[3], | ||
in("t0") u8::from(Syscall::SLoad)); | ||
} | ||
U256::from_limbs([first, second, third, fourth]) | ||
U256::from_limbs([val0, val1, val2, val3]) | ||
} | ||
|
||
pub fn sstore(key: u64, value: U256) { | ||
let limbs = value.as_limbs(); | ||
pub fn sstore(key: U256, value: U256) { | ||
let key = key.as_limbs(); | ||
let value = value.as_limbs(); | ||
|
||
unsafe { | ||
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)); | ||
asm!( | ||
"ecall", | ||
in("a0") key[0], in("a1") key[1], in("a2") key[2], in("a3") key[3], | ||
in("a4") value[0], in("a5") value[1], in("a6") value[2], in("a7") value[3], | ||
in("t0") u8::from(Syscall::SStore) | ||
); | ||
} | ||
} | ||
|
||
|
@@ -107,12 +116,8 @@ pub fn revert() -> ! { | |
unreachable!() | ||
} | ||
|
||
pub fn keccak256(offset: u64, size: u64) -> B256 { | ||
let first: u64; | ||
let second: u64; | ||
let third: u64; | ||
let fourth: u64; | ||
|
||
pub fn keccak256(offset: u64, size: u64) -> U256 { | ||
let (first, second, third, fourth): (u64, u64, u64, u64); | ||
unsafe { | ||
asm!( | ||
"ecall", | ||
|
@@ -125,21 +130,11 @@ pub fn keccak256(offset: u64, size: u64) -> B256 { | |
in("t0") u8::from(Syscall::Keccak256) | ||
); | ||
} | ||
|
||
let mut bytes = [0u8; 32]; | ||
|
||
bytes[0..8].copy_from_slice(&first.to_be_bytes()); | ||
bytes[8..16].copy_from_slice(&second.to_be_bytes()); | ||
bytes[16..24].copy_from_slice(&third.to_be_bytes()); | ||
bytes[24..32].copy_from_slice(&fourth.to_be_bytes()); | ||
|
||
B256::from_slice(&bytes) | ||
U256::from_limbs([first, second, third, fourth]) | ||
} | ||
|
||
pub fn msg_sender() -> Address { | ||
let first: u64; | ||
let second: u64; | ||
let third: u64; | ||
let (first, second, third): (u64, u64, u64); | ||
unsafe { | ||
asm!("ecall", lateout("a0") first, lateout("a1") second, lateout("a2") third, in("t0") u8::from(Syscall::Caller)); | ||
} | ||
|
@@ -151,10 +146,7 @@ pub fn msg_sender() -> Address { | |
} | ||
|
||
pub fn msg_value() -> U256 { | ||
let first: u64; | ||
let second: u64; | ||
let third: u64; | ||
let fourth: u64; | ||
let (first, second, third, fourth): (u64, u64, u64, u64); | ||
unsafe { | ||
asm!("ecall", lateout("a0") first, lateout("a1") second, lateout("a2") third, lateout("a3") fourth, in("t0") u8::from(Syscall::CallValue)); | ||
} | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We chatted about some of this offline, I think it's a bit odd to have
Slot
here but not in Mapping, but good for a first version and can be improved in the future.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
So if I understand correctly, every type used in a struct that derives
storage
has to implementStorageLayout
, right?There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
yes, my bad. I totally forgot... I'll amend the PR to incorporate that design pattern.
yes, that's correct they need to impl
StorageLayout
so that we can ensure that we can allocate a slot in the layout for them.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think this is fine as is and we can iterate later on