Skip to content

Commit

Permalink
feat(example): basic implementation of ERC20 (#228)
Browse files Browse the repository at this point in the history
* chore(erc20): basic skeleton of erc20

* feat(erc20): test initial storage

* chore(erc20): adapt to new constructor

* chore(erc20): use auto-generated getters

* feat(examples): add example of const functions

* chore(erc20): test decimals

* feat(zink): introduce u256

* fix(storage): keys in ls bytes

* feat(zink): complete functions of erc20

* feat(erc20): add _mint and _burn

* feat(erc20): complete host calls

* fix(codegen): ls bytes conversion

* feat(abi): introduce u256

* chore(zink): ignore tests of erc20 atm
  • Loading branch information
clearloop authored Nov 6, 2024
1 parent 3191069 commit a217af6
Show file tree
Hide file tree
Showing 26 changed files with 517 additions and 24 deletions.
4 changes: 4 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -110,3 +110,7 @@ opcodes = { workspace = true, features = ["data"] }
tracing.workspace = true
zint.workspace = true
hex.workspace = true

# [features]
# default = [ "evm" ]
# evm = [ ]
4 changes: 2 additions & 2 deletions codegen/src/asm.rs
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ impl Assembler {
return Ok(());
}

tracing::debug!(
tracing::trace!(
"increment stack pointer {}.add({items}) -> {}",
self.sp,
self.sp + items
Expand All @@ -67,7 +67,7 @@ impl Assembler {
return Ok(());
}

tracing::debug!(
tracing::trace!(
"decrement stack pointer {}.sub({items}) -> {}",
self.sp,
self.sp - items
Expand Down
6 changes: 6 additions & 0 deletions codegen/src/codegen/constructor.rs
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ pub struct Constructor {
impl Constructor {
/// preset storage for the contract
pub fn storage(&mut self, mapping: InitStorage) -> Result<()> {
tracing::debug!("Building storage in constructor ...");
for (key, value) in mapping.into_iter() {
self.masm.push(&value)?;
self.masm.push(&key)?;
Expand All @@ -38,6 +39,11 @@ impl Constructor {
let runtime_bytecode_offset =
Self::runtime_bytcode_offset(init_code_len, runtime_bytecode_size.len());

tracing::trace!("length of bytecode: {:?}", runtime_bytecode_len);
tracing::trace!(
"length of bytecode in hex: {:?}",
hex::encode(&runtime_bytecode_size)
);
let mut masm = self.masm.clone();

// 1. copy runtime bytecode to memory
Expand Down
2 changes: 1 addition & 1 deletion codegen/src/codegen/dispatcher.rs
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,7 @@ impl Dispatcher {
self.abi.push(abi.clone());

let selector_bytes = abi.selector();
tracing::trace!(
tracing::debug!(
"Emitting selector {:?} for function: {}",
selector_bytes,
abi.signature(),
Expand Down
10 changes: 5 additions & 5 deletions codegen/src/local.rs
Original file line number Diff line number Diff line change
Expand Up @@ -87,11 +87,11 @@ impl Locals {
let offset = if local.ty() == &LocalSlotType::Parameter {
self.inner[..index].iter().fold(0, |acc, x| acc + x.align())
} else {
panic!("This should never be reached");
// self.inner[..index]
// .iter()
// .filter(|x| x.ty() == &LocalSlotType::Variable)
// .fold(0, |acc, x| acc + x.align())
// panic!("This should never be reached");
self.inner[..index]
.iter()
.filter(|x| x.ty() == &LocalSlotType::Variable)
.fold(0, |acc, x| acc + x.align())
}
.to_ls_bytes()
.to_vec()
Expand Down
2 changes: 1 addition & 1 deletion codegen/src/masm/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -86,7 +86,7 @@ impl MacroAssembler {

/// Place n bytes on stack.
pub fn push(&mut self, bytes: &[u8]) -> Result<()> {
tracing::trace!("push bytes: 0x{:x?}", bytes);
tracing::trace!("push bytes: 0x{}", hex::encode(bytes));

// TODO: support PUSH0 #247
//
Expand Down
1 change: 1 addition & 0 deletions codegen/src/visitor/call.rs
Original file line number Diff line number Diff line change
Expand Up @@ -111,6 +111,7 @@ impl Function {
HostFunc::Evm(OpCode::LOG3) => self.log(3),
HostFunc::Evm(OpCode::LOG4) => self.log(4),
HostFunc::Evm(op) => self.masm.emit_op(op),
HostFunc::U256MAX => self.masm.push(&[255; 32]),
HostFunc::Revert(count) => self.revert(count),
HostFunc::NoOp | HostFunc::Label(_) => Ok(()),
_ => {
Expand Down
3 changes: 2 additions & 1 deletion codegen/src/visitor/control.rs
Original file line number Diff line number Diff line change
Expand Up @@ -104,7 +104,8 @@ impl Function {
///
/// Performs an unconditional branch.
pub fn _br(&mut self, _depth: u32) -> Result<()> {
todo!()
// TODO: do sth here?
Ok(())
}

/// Performs a conditional branch if i32 is non-zero.
Expand Down
5 changes: 4 additions & 1 deletion codegen/src/visitor/log.rs
Original file line number Diff line number Diff line change
Expand Up @@ -33,9 +33,11 @@ impl Function {
}

let offset_len = (data[0] - 0x5f) as usize;
tracing::trace!("offset len: {offset_len}");
let offset = {
let mut bytes = [0; 4];
bytes[..offset_len].copy_from_slice(&data[1..(1 + offset_len)]);
bytes[(4 - offset_len)..].copy_from_slice(&data[1..(offset_len + 1)]);
bytes.reverse();
i32::from_le_bytes(bytes)
};
tracing::debug!("log offset: {:?}", offset);
Expand All @@ -45,6 +47,7 @@ impl Function {
return Err(Error::InvalidDataOffset(data[offset_len + 1].into()));
}
let size = {
// TODO: from ls bytes as offset
let mut bytes = [0; 4];
let size_bytes = &data[(offset_len + 2)..];
bytes[..size_bytes.len()].copy_from_slice(size_bytes);
Expand Down
9 changes: 6 additions & 3 deletions codegen/src/wasm/abi.rs
Original file line number Diff line number Diff line change
Expand Up @@ -78,9 +78,6 @@ macro_rules! offset {
.rev()
.skip_while(|b| *b == 0)
.collect::<Vec<_>>()
.into_iter()
.rev()
.collect::<Vec<_>>()
.into()
}
}
Expand Down Expand Up @@ -131,3 +128,9 @@ impl ToLSBytes for &[ValType] {
.to_ls_bytes()
}
}

#[test]
fn test_usize_to_ls_bytes() {
assert_eq!(363usize.to_ls_bytes().to_vec(), vec![0x01, 0x6b]);
assert_eq!(255usize.to_ls_bytes().to_vec(), vec![0xff]);
}
6 changes: 6 additions & 0 deletions codegen/src/wasm/host.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,8 @@ pub enum HostFunc {
EmitABI,
/// check equal of two addresses
AddressEq,
/// Push u256 max to stack
U256MAX,
/// Revert messages with length of slots
Revert(usize),
/// Compiler labels
Expand Down Expand Up @@ -66,6 +68,10 @@ impl TryFrom<(&str, &str)> for HostFunc {
})?)),
("zinkc", "emit_abi") => Ok(Self::EmitABI),
("zinkc", "address_eq") => Ok(Self::Evm(OpCode::EQ)),
("zinkc", "u256_add") => Ok(Self::Evm(OpCode::ADD)),
("zinkc", "u256_sub") => Ok(Self::Evm(OpCode::SUB)),
("zinkc", "u256_lt") => Ok(Self::Evm(OpCode::LT)),
("zinkc", "u256_max") => Ok(Self::U256MAX),
("zinkc", "label_reserve_mem_32") => Ok(Self::Label(CompilerLabel::ReserveMemory32)),
("zinkc", "label_reserve_mem_64") => Ok(Self::Label(CompilerLabel::ReserveMemory64)),
_ => {
Expand Down
8 changes: 7 additions & 1 deletion evm/abi/src/arg.rs
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,10 @@ pub enum Param {
UInt32,
/// A 64-bit unsigned integer.
UInt64,
/// A 256-bit unsigned integer.
UInt256,
// /// A 256-bit unsigned integer.
// UInt256,
/// A boolean type.
Bool,
/// An EVM address.
Expand All @@ -60,10 +64,11 @@ impl From<&str> for Param {
"u16" | "uint16" => Param::UInt16,
"u32" | "uint32" => Param::UInt32,
"u64" | "uint64" => Param::UInt64,
"U256" | "u256" | "uint256" => Param::UInt256,
"bool" => Param::Bool,
"address" | "Address" => Param::Address,
"Bytes" | "Vec<u8>" => Param::Bytes,
"String" => Param::String,
"String" | "String32" => Param::String,
_ => Param::Unknown(s.to_string()),
}
}
Expand All @@ -88,6 +93,7 @@ impl AsRef<str> for Param {
Param::UInt16 => "uint16",
Param::UInt32 => "uint32",
Param::UInt64 => "uint64",
Param::UInt256 => "uint256",
Param::Address => "address",
Param::Bool => "boolean",
Param::Bytes => "bytes",
Expand Down
26 changes: 26 additions & 0 deletions examples/constfn.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
//! Storage example.
#![cfg_attr(target_arch = "wasm32", no_std)]
#![cfg_attr(target_arch = "wasm32", no_main)]

extern crate zink;

/// set value to the storage.
#[zink::external]
pub fn decimals() -> i32 {
8
}

#[cfg(not(target_arch = "wasm32"))]
fn main() {}

#[test]
fn value() -> anyhow::Result<()> {
use zint::{Bytes32, Contract};

let mut contract = Contract::search("constfn")?.compile()?;

let decimals = 8.to_bytes32().to_vec();
let info = contract.execute(&[b"decimals()".to_vec(), decimals.clone()])?;
assert_eq!(info.ret, decimals);
Ok(())
}
Loading

0 comments on commit a217af6

Please sign in to comment.