Created
March 12, 2025 17:32
-
-
Save Turupawn/99362e6318b777e96d36ac1d23f89749 to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// TODO: how include from different files? | |
extern { | |
// TODO: is it possible to use the same name for different functions? | |
fn usize_as_u256(_ value: usize) -> u256 | |
fn u8_as_u256(_ value: u8) -> u256 | |
fn u256_as_usize(_ value: u256) -> usize | |
fn todo() -> ! | |
} | |
use ingot::evm | |
use ingot::Foo | |
trait IntUpcast<To> { | |
fn upcast(self) -> To | |
} | |
impl IntUpcast<u256> for u8 { | |
fn upcast(self) -> u256 { | |
todo() | |
} | |
} | |
trait IntDowncast<To> { | |
fn downcast_truncate(self) -> To | |
fn downcast_saturate(self) -> To | |
// fn downcast(self) -> Option<To> // Option is in core lib, which isn't visible in lsp (see grant's PR) | |
} | |
impl IntDowncast<u8> for u256 { | |
fn downcast_truncate(self) -> u8 { | |
todo() | |
} | |
fn downcast_saturate(self) -> u8 { | |
todo() | |
} | |
} | |
fn example() { | |
let x: u8 = 10 | |
let y: u256 = x.upcast() | |
let z: u8 = y.downcast_truncate() | |
let w: u8 = y.downcast_saturate() | |
} | |
trait Buffer { | |
fn write_word(mut self, offset: u256, _ w: u256) | |
fn read_word(mut self, offset: u256) -> u256 | |
} | |
pub struct MemBuffer { ptr: Ptr, len: u256 } | |
impl Buffer for MemBuffer { | |
fn write_word(mut self, offset: u256, _ w: u256) { | |
let f = Foo { x: 10 } | |
__mstore(self.ptr + offset, w) | |
} | |
} | |
pub struct StorageBuffer {} | |
pub struct Cursor<B: Buffer> { | |
inner: B, | |
pos: u256 | |
} | |
impl<B: Buffer> Write for Cursor<B> { | |
fn write_word(mut self, _ w: u256) { | |
// TODO: check for out-of-bounds | |
self.inner.write_word(offset: self.pos, w) | |
self.pos += 32 | |
} | |
} | |
pub trait Decode { | |
fn decode(words: evm::StorageWords) -> Self | |
fn decode_size() -> usize | |
} | |
pub trait Encode { | |
fn encode<W: Write>(self, mut _ w: W) | |
fn encode_size() -> usize | |
} | |
impl Decode for u256 { | |
fn decode(words: evm::StorageWords) -> u256 { | |
words.values[0] | |
} | |
fn decode_size() -> usize { | |
32 | |
} | |
} | |
impl Encode for u256 { | |
fn encode<W: Write>(self, mut _ w: W) { | |
w.write_word(self) | |
} | |
fn encode_size() -> usize { | |
32 | |
} | |
} | |
impl<A: Decode, B: Decode, R: Read> Decode for (A, B) { | |
fn decode(_ r: Read) -> (A, B) { | |
(A::decode(r), B::decode(r)) | |
} | |
fn decode_size() -> usize { | |
A::decode_size() + B::decode_size() | |
} | |
} | |
impl<A: Decode, B: Decode, C: Decode, R: Read> Decode for (A, B, C) { | |
fn decode(_ r: Read) -> (A, B, C) { | |
(A::decode(r), B::decode(r), C::decode(r)) | |
} | |
fn decode_size() -> usize { | |
A::decode_size() + B::decode_size() + C::decode_size() | |
} | |
} | |
struct AccountInfo { | |
addr: Address, | |
balance: u256, | |
z: Foo, | |
} | |
impl Decode for AccountInfo { | |
fn decode<R: Read>(_ r: R) -> Self { | |
Self { | |
x: Address::decode(r), | |
y: u256::decode(r), | |
z: Foo::decode(r), | |
} | |
} | |
} | |
trait Write { | |
fn write_word(mut self, _ w: u256) | |
} | |
impl Write for MemBuf {} | |
impl Write for StorageBuf {} | |
impl Encode for (u256, u256) { | |
fn encode<W: Write>(self, mut _ w: W) { | |
w.write_word(self.0) | |
w.write_word(self.1) | |
} | |
fn encode_size() -> usize { | |
64 | |
} | |
} | |
impl<T, U> Map<T, U> | |
where T: Encode, U: Decode | |
{ | |
pub fn get(self, key: T) -> U { | |
// allocate memory for the key and the slot and encode the key | |
let allocated_buffer = std::evm::alloc(len: T::encode_size() + std::evm::SLOT_SIZE) | |
let key_encoded : std::evm::StorageWords = T::encode() | |
let mut key_cursor = Cursor::new(allocated_buffer) | |
slot.encode(key_cursor) | |
key.encode(key_cursor) | |
// store the key and the slot in the allocated memory | |
std::evm::__mstore(location: usize_as_u256(allocated_buffer.offset.location), storage_words: key_encoded) | |
std::evm::__mstore( | |
location: usize_as_u256(allocated_buffer.offset.location), | |
storage_words: std::evm::StorageWords { | |
values: [u8_as_u256(self.slot),0,0,0,0,0,0,0,0,0], | |
length: 1 | |
} | |
) | |
// compute the location of the value by keccak256ing the key and the slot | |
let value_location : u256 = std::evm::__keccak256( | |
args: std::evm::Buf { | |
offset: std::evm::Ptr { | |
location: allocated_buffer.offset.location | |
}, | |
len: (key_encoded.length + 1) * std::evm::SLOT_SIZE | |
} | |
) | |
// load the value from the location | |
let storage_words : std::evm::StorageWords = std::evm::__sload( | |
std::evm::WordSlice { | |
offset: u256_as_usize(value_location), | |
length: U::decode_size() | |
} | |
) | |
let return_value : U = U::decode(words: storage_words) | |
return_value | |
} | |
pub fn set(self, _ key: T, _ value: U) { | |
} | |
} | |
struct Map<T, U> { | |
// TODO: should slot be u256? | |
slot: u8, | |
} | |
contract ERC20 { | |
pub balance: Map<std::evm::Address, u256>, | |
pub allowance: Map<(std::evm::Address, std::evm::Address), std::evm::Address>, | |
pub TOTAL_SUPPLY: u256, | |
pub NAME: String<20>, | |
pub SYMBOL: String<5>, | |
pub DECIMALS: u8 | |
} | |
impl ERC20 { | |
pub fn constructor(mut self) { | |
// TODO: use native constructor | |
self.TOTAL_SUPPLY = 21_000_000 * 1**18 | |
self.balance.set(std::evm::__caller(), self.TOTAL_SUPPLY) | |
} | |
pub fn name(self) -> String<20> { | |
self.NAME | |
} | |
pub fn symbol(self) -> String<5> { | |
self.SYMBOL | |
} | |
pub fn decimals(self) -> u8 { | |
self.DECIMALS | |
} | |
pub fn totalSupply(self) -> u256 { | |
self.TOTAL_SUPPLY | |
} | |
pub fn balanceOf(self, account : std::evm::Address) -> u256 { | |
self.balance.get(key: account) | |
} | |
pub fn transfer(mut self, to : std::evm::Address, value : u256) -> bool { | |
if self.balance.get(key: std::evm::__caller()) < value { | |
std::evm::__revert() | |
} | |
self.balance.set(key: std::evm::__caller(), self.balance.get(key: std::evm::__caller()) - value) | |
self.balance.set(key: to, self.balance.get(key: to) + value) | |
true | |
} | |
pub fn allowance(self, owner : std::evm::Address, spender : std::evm::Address) -> u256 { | |
self.allowance.get(key: (owner, spender)) | |
} | |
pub fn approve(mut self, spender : std::evm::Address, value : u256) -> bool { | |
self.allowance.set((std::evm::__caller(), spender), value) | |
true | |
} | |
pub fn transferFrom(mut self, from : std::evm::Address, to : std::evm::Address, value : u256) -> bool { | |
if (self.allowance.get(key: (from, std::evm::__caller())) < value || self.balance.get(key: from) < value) { | |
std::evm::__revert() | |
} | |
self.allowance.set((from, std::evm::__caller()), self.allowance.get(key: (from, std::evm::__caller())) - value) | |
self.balance.set(from, self.balance.get(key: from) - value) | |
self.balance.set(to, self.balance.get(key: to) + value) | |
true | |
} | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
const HASH_SCRATCH_OFFSET: u256 = 0x0 | |
const SLOT_SIZE: usize = 32 | |
pub type Address = u256 | |
pub struct Wei { value: usize } | |
pub struct Ptr { pub location: usize } | |
pub struct Buf { pub offset: Ptr, pub len: usize } | |
impl Buf { | |
pub fn write_word(mut self, _ w: u256) { | |
__mstore(self.offset, w) | |
} | |
} | |
// TODO: how to declare dynamic array? | |
pub struct StorageWords { pub values: [u256; 10], pub length: usize } | |
pub struct WordSlice { pub offset: usize, pub length: usize } | |
extern { | |
pub fn todo() -> ! | |
pub fn __call(gas: Wei, address: Address, value: Wei, args: Buf, ret: Buf) | |
pub fn __mload(_ storage_slice: WordSlice) -> StorageWords | |
pub fn __sload(_ storage_slice: WordSlice) -> StorageWords | |
pub fn __mstore(_ loc: u256, storage_words: StorageWords) // TODO: use StorageWords | |
pub fn __sstore(_ loc: u256, _ data: u256) // TODO: use StorageWords | |
pub fn __keccak256(args: Buf) -> u256 // TODO: Should this use WordSlice? | |
pub fn __caller() -> Address | |
pub fn __revert() | |
//pub fn __sstore2(_ loc: StoragePtr, _ data: &[u8]) | |
} | |
pub fn alloc(len: usize) -> Buf { | |
// TODO: initialize the free memory pointer | |
let free_memory_pointer : StorageWords = __mload(WordSlice { offset: 0x40, length: 1 }) | |
// TODO: can I use extern functions here? | |
let free_memory_pointer_location : usize //= u256_as_usize(free_memory_pointer.values[0]) | |
Buf { offset: Ptr { location: free_memory_pointer_location }, len: 1} | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
[ingot] | |
name = "Erc20" | |
version = "0.0.1" |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
pub struct Foo { x: usize } |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment