diff --git a/Cargo.toml b/Cargo.toml index 88bef034e..3415610a8 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -110,3 +110,7 @@ opcodes = { workspace = true, features = ["data"] } tracing.workspace = true zint.workspace = true hex.workspace = true + +# [features] +# default = [ "evm" ] +# evm = [ ] diff --git a/codegen/src/asm.rs b/codegen/src/asm.rs index 329f66407..0103011b1 100644 --- a/codegen/src/asm.rs +++ b/codegen/src/asm.rs @@ -46,7 +46,7 @@ impl Assembler { return Ok(()); } - tracing::debug!( + tracing::trace!( "increment stack pointer {}.add({items}) -> {}", self.sp, self.sp + items @@ -67,7 +67,7 @@ impl Assembler { return Ok(()); } - tracing::debug!( + tracing::trace!( "decrement stack pointer {}.sub({items}) -> {}", self.sp, self.sp - items diff --git a/codegen/src/codegen/constructor.rs b/codegen/src/codegen/constructor.rs index a2428022c..a98d368e4 100644 --- a/codegen/src/codegen/constructor.rs +++ b/codegen/src/codegen/constructor.rs @@ -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)?; @@ -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 diff --git a/codegen/src/codegen/dispatcher.rs b/codegen/src/codegen/dispatcher.rs index d103d84e2..ceae36c57 100644 --- a/codegen/src/codegen/dispatcher.rs +++ b/codegen/src/codegen/dispatcher.rs @@ -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(), diff --git a/codegen/src/local.rs b/codegen/src/local.rs index d2a1df71d..32e0989e5 100644 --- a/codegen/src/local.rs +++ b/codegen/src/local.rs @@ -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() diff --git a/codegen/src/masm/mod.rs b/codegen/src/masm/mod.rs index 6a7cf60cd..b8a1577e8 100644 --- a/codegen/src/masm/mod.rs +++ b/codegen/src/masm/mod.rs @@ -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 // diff --git a/codegen/src/visitor/call.rs b/codegen/src/visitor/call.rs index 72e94f9e2..b50b5f9e4 100644 --- a/codegen/src/visitor/call.rs +++ b/codegen/src/visitor/call.rs @@ -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(()), _ => { diff --git a/codegen/src/visitor/control.rs b/codegen/src/visitor/control.rs index f8a50987d..f151f1533 100644 --- a/codegen/src/visitor/control.rs +++ b/codegen/src/visitor/control.rs @@ -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. diff --git a/codegen/src/visitor/log.rs b/codegen/src/visitor/log.rs index 801c95bd1..9bbb9e69f 100644 --- a/codegen/src/visitor/log.rs +++ b/codegen/src/visitor/log.rs @@ -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); @@ -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); diff --git a/codegen/src/wasm/abi.rs b/codegen/src/wasm/abi.rs index aa39b7822..8b1b93802 100644 --- a/codegen/src/wasm/abi.rs +++ b/codegen/src/wasm/abi.rs @@ -78,9 +78,6 @@ macro_rules! offset { .rev() .skip_while(|b| *b == 0) .collect::>() - .into_iter() - .rev() - .collect::>() .into() } } @@ -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]); +} diff --git a/codegen/src/wasm/host.rs b/codegen/src/wasm/host.rs index 9e9ec5dab..2fce8a2d4 100644 --- a/codegen/src/wasm/host.rs +++ b/codegen/src/wasm/host.rs @@ -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 @@ -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)), _ => { diff --git a/evm/abi/src/arg.rs b/evm/abi/src/arg.rs index 609dba96d..d7700166e 100644 --- a/evm/abi/src/arg.rs +++ b/evm/abi/src/arg.rs @@ -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. @@ -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" => Param::Bytes, - "String" => Param::String, + "String" | "String32" => Param::String, _ => Param::Unknown(s.to_string()), } } @@ -88,6 +93,7 @@ impl AsRef for Param { Param::UInt16 => "uint16", Param::UInt32 => "uint32", Param::UInt64 => "uint64", + Param::UInt256 => "uint256", Param::Address => "address", Param::Bool => "boolean", Param::Bytes => "bytes", diff --git a/examples/constfn.rs b/examples/constfn.rs new file mode 100644 index 000000000..87c369df1 --- /dev/null +++ b/examples/constfn.rs @@ -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(()) +} diff --git a/examples/erc20.rs b/examples/erc20.rs new file mode 100644 index 000000000..81edb0b1c --- /dev/null +++ b/examples/erc20.rs @@ -0,0 +1,212 @@ +//! Constructor example. +#![cfg_attr(target_arch = "wasm32", no_std)] +#![cfg_attr(target_arch = "wasm32", no_main)] + +extern crate zink; + +use zink::{ + primitives::{Address, String32, U256}, + DoubleKeyMapping, Mapping, Storage, +}; + +#[zink::storage(String32)] +pub struct Name; + +#[zink::storage(String32)] +pub struct Symbol; + +#[zink::storage(U256)] +pub struct TotalSupply; + +#[zink::storage(Address, U256)] +pub struct Balances; + +#[zink::storage(Address, Address, U256)] +pub struct Allowance; + +/// Get value from the storage. +#[zink::external] +pub fn init(name: String32, symbol: String32) { + Name::set(name); + Symbol::set(symbol); +} + +/// Get value from the storage. +#[zink::external] +pub fn decimals() -> u32 { + 8 +} + +#[zink::external] +pub fn transfer(to: Address, value: U256) -> bool { + let owner = unsafe { zink::ffi::evm::caller() }; + _transfer(owner, to, value); + true +} + +#[zink::external] +pub fn approve(spender: Address, value: U256) -> bool { + // TODO: wrap this in env + let owner = unsafe { zink::ffi::evm::caller() }; + _approve(owner, spender, value, false); + true +} + +#[zink::external] +pub fn transfer_from(from: Address, to: Address, value: U256) -> bool { + let spender = unsafe { zink::ffi::evm::caller() }; + _spend_allowance(from, spender, value); + _transfer(from, to, value); + true +} + +fn _transfer(from: Address, to: Address, value: U256) { + if from.eq(Address::empty()) { + zink::revert!("Empty from address"); + } + + if to.eq(Address::empty()) { + zink::revert!("Empty to address"); + } + + _update(from, to, value) +} + +fn _update(from: Address, to: Address, value: U256) { + if from.eq(Address::empty()) { + TotalSupply::set(TotalSupply::get().add(value)); + } else { + let from_balance = Balances::get(from); + if from_balance.lt(value) { + zink::revert!("Insufficient balance"); + } + + Balances::set(from, from_balance.sub(value)); + } + + if to.eq(Address::empty()) { + TotalSupply::set(TotalSupply::get().sub(value)); + } else { + TotalSupply::set(TotalSupply::get().add(value)); + } +} + +fn _mint(account: Address, value: U256) { + if account.eq(Address::empty()) { + zink::revert!("ERC20 invalid receiver"); + } + + _update(Address::empty(), account, value) +} + +fn _burn(account: Address, value: U256) { + if account.eq(Address::empty()) { + zink::revert!("ERC20 invalid sender"); + } + + _update(account, Address::empty(), value) +} + +fn _approve(owner: Address, spender: Address, value: U256, _emit_event: bool) { + if owner.eq(Address::empty()) { + zink::revert!("ERC20 Invalid approval"); + } + + if spender.eq(Address::empty()) { + zink::revert!("ERC20 Invalid spender"); + } + + Allowance::set(owner, spender, value); +} + +fn _spend_allowance(owner: Address, spender: Address, value: U256) { + let current_allowance = Allowance::get(owner, spender); + if current_allowance.lt(U256::max()) { + if current_allowance.lt(value) { + zink::revert!("ERC20 Insufficient allowance"); + } + + _approve(owner, spender, current_allowance.sub(value), false) + } +} + +#[cfg(not(target_arch = "wasm32"))] +fn main() {} + +// TODO: +// +// 1. nested function allocations +// 2. memory slots for local variables +#[ignore] +#[test] +fn deploy() -> anyhow::Result<()> { + use zint::{Bytes32, Contract, EVM}; + + let mut contract = Contract::search("erc20")?.compile()?; + + let mut evm = EVM::default(); + let name = "The Zink Language"; + let symbol = "zink"; + + // 1. deploy + let info = evm.deploy( + &contract + .construct( + [ + (Name::STORAGE_KEY.to_bytes32().into(), name.to_vec().into()), + ( + Symbol::STORAGE_KEY.to_bytes32().into(), + symbol.to_vec().into(), + ), + ( + TotalSupply::STORAGE_KEY.to_bytes32().into(), + vec![42].try_into()?, + ), + ] + .into_iter() + .collect(), + )? + .bytecode()?, + )?; + let address = info.address; + + // 2. get name + let info = evm + .calldata(&contract.encode(&[b"name()".to_vec()])?) + .call(address)?; + assert_eq!(info.ret, name.to_bytes32()); + + // 3. get symbol + let info = evm + .calldata(&contract.encode(&[b"symbol()".to_vec()])?) + .call(address)?; + assert_eq!(info.ret, symbol.to_bytes32()); + + // 3. get total supply + let info = evm + .calldata(&contract.encode(&[b"total_supply()".to_vec()])?) + .call(address)?; + assert_eq!(info.ret, 42u64.to_bytes32()); + + // 4. check decimals + let info = evm + .calldata(&contract.encode(&[b"decimals()".to_vec()])?) + .call(address)?; + assert_eq!(info.ret, 8u64.to_bytes32()); + + // 5. check approve + let info = evm + .calldata(&contract.encode(&[ + b"approve(address,uint256)".to_vec(), + [1; 20].to_vec(), + 42.to_bytes32().to_vec(), + ])?) + .call(address)?; + println!("{info:?}"); + println!("{:?}", String::from_utf8_lossy(&info.ret)); + let storage_key = Allowance::storage_key(Address::empty(), Address([1; 20])); + let storage = evm.storage(contract.address, storage_key)?; + println!("{storage:?}"); + + Ok(()) +} diff --git a/examples/storage.rs b/examples/storage.rs index eb5974266..131129340 100644 --- a/examples/storage.rs +++ b/examples/storage.rs @@ -24,9 +24,9 @@ fn value() -> anyhow::Result<()> { use zint::{Bytes32, Contract, U256}; let mut contract = Contract::search("storage")?.compile()?; + let value: i32 = 42; { - let value: i32 = 42; let info = contract.execute(&[b"set(int32)".to_vec(), value.to_bytes32().to_vec()])?; assert!(info.ret.is_empty()); assert_eq!( diff --git a/zink/codegen/src/lib.rs b/zink/codegen/src/lib.rs index 9974113bf..38b8e877d 100644 --- a/zink/codegen/src/lib.rs +++ b/zink/codegen/src/lib.rs @@ -10,6 +10,7 @@ mod event; mod revert; mod selector; mod storage; +mod utils; /// Revert with the input message /// diff --git a/zink/codegen/src/storage.rs b/zink/codegen/src/storage.rs index aa2723612..58b33a1ac 100644 --- a/zink/codegen/src/storage.rs +++ b/zink/codegen/src/storage.rs @@ -1,3 +1,4 @@ +use crate::utils::Bytes32; use heck::AsSnakeCase; use proc_macro::TokenStream; use proc_macro2::{Literal, Span, TokenTree}; @@ -45,8 +46,7 @@ impl Storage { let is = &self.target; let name = self.target.ident.clone(); let slot = storage_slot(name.to_string()); - let mut key = [0; 32]; - key[28..].copy_from_slice(&slot.to_le_bytes()); + let key = slot.to_bytes32(); let keyl = Literal::byte_string(&key); let mut expanded = quote! { @@ -96,7 +96,7 @@ impl Storage { let mut seed = [0; 64]; seed[..32].copy_from_slice(&key.bytes32()); - seed[60..].copy_from_slice(&Self::STORAGE_SLOT.to_le_bytes()); + seed[32..].copy_from_slice(&Self::STORAGE_SLOT.bytes32()); zink::keccak256(&seed) } } @@ -138,7 +138,7 @@ impl Storage { let mut seed = [0; 64]; seed[..32].copy_from_slice(&key1.bytes32()); - seed[60..].copy_from_slice(&Self::STORAGE_SLOT.to_le_bytes()); + seed[32..].copy_from_slice(&Self::STORAGE_SLOT.bytes32()); let skey1 = zink::keccak256(&seed); seed[..32].copy_from_slice(&skey1); seed[32..].copy_from_slice(&key2.bytes32()); diff --git a/zink/codegen/src/utils.rs b/zink/codegen/src/utils.rs new file mode 100644 index 000000000..7ab578804 --- /dev/null +++ b/zink/codegen/src/utils.rs @@ -0,0 +1,121 @@ +//! Utils for bytes conversion. +//! +//! TODO: move this util to other library + +/// Trait for converting type to bytes32. +pub trait Bytes32: Sized { + /// Convert type to the lowest significant bytes 32. + fn to_bytes32(&self) -> [u8; 32]; + + /// Convert type to vec of bytes. + fn to_vec(&self) -> Vec { + self.to_bytes32().to_vec() + } +} + +/// Implement Bytes32 for types. +macro_rules! impl_bytes32 { + ($($ty:ident),+) => { + $( + impl Bytes32 for $ty { + fn to_bytes32(&self) -> [u8; 32] { + let mut bytes = [0u8; 32]; + let ls_bytes = { + self.to_le_bytes() + .into_iter() + .rev() + .skip_while(|b| *b == 0) + .collect::>() + .into_iter() + .rev() + .collect::>() + }; + + bytes[(32 - ls_bytes.len())..].copy_from_slice(&ls_bytes); + bytes + } + + fn to_vec(&self) -> Vec { + self.to_le_bytes().to_vec() + } + } + )+ + }; +} + +impl Bytes32 for Vec { + fn to_bytes32(&self) -> [u8; 32] { + let mut bytes = [0u8; 32]; + bytes[(32 - self.len())..].copy_from_slice(self); + bytes + } + + fn to_vec(&self) -> Vec { + self.clone() + } +} + +impl Bytes32 for [u8; 20] { + fn to_bytes32(&self) -> [u8; 32] { + let mut bytes = [0u8; 32]; + bytes[12..].copy_from_slice(self); + bytes + } +} + +impl Bytes32 for [u8; 32] { + fn to_bytes32(&self) -> [u8; 32] { + *self + } + + fn to_vec(&self) -> Vec { + self.as_ref().into() + } +} + +impl Bytes32 for &[u8] { + fn to_bytes32(&self) -> [u8; 32] { + let mut bytes = [0u8; 32]; + bytes[(32 - self.len())..].copy_from_slice(self); + bytes + } + + fn to_vec(&self) -> Vec { + (*self).into() + } +} + +impl Bytes32 for () { + fn to_bytes32(&self) -> [u8; 32] { + [0; 32] + } + + fn to_vec(&self) -> Vec { + Default::default() + } +} + +impl Bytes32 for &str { + fn to_bytes32(&self) -> [u8; 32] { + let mut bytes = [0u8; 32]; + bytes[(32 - self.len())..].copy_from_slice(self.as_bytes()); + bytes + } + + fn to_vec(&self) -> Vec { + self.as_bytes().into() + } +} + +impl Bytes32 for bool { + fn to_bytes32(&self) -> [u8; 32] { + let mut output = [0; 32]; + if *self { + output[31] = 1; + } + + output + } +} + +impl_bytes32!(i8, u8, i16, u16, i32, u32, usize, i64, u64, i128, u128); diff --git a/zink/src/ffi/asm.rs b/zink/src/ffi/asm.rs index fc8930e84..b308531a0 100644 --- a/zink/src/ffi/asm.rs +++ b/zink/src/ffi/asm.rs @@ -1,6 +1,6 @@ //! Assembly FFI. -use crate::primitives::Address; +use crate::primitives::{Address, U256}; #[link(wasm_import_module = "asm")] #[allow(improper_ctypes)] @@ -32,6 +32,9 @@ extern "C" { /// Push address to stack pub fn push_address(address: Address); + /// Push u256 to stack + pub fn push_u256(u256: U256); + /// Revert with message in 32 bytes pub fn revert1(message: &'static str); @@ -70,4 +73,7 @@ extern "C" { /// Load address from storage pub fn sload_address() -> Address; + + /// Load address from storage + pub fn sload_u256() -> U256; } diff --git a/zink/src/ffi/evm.rs b/zink/src/ffi/evm.rs index 398ec523c..c832282b0 100644 --- a/zink/src/ffi/evm.rs +++ b/zink/src/ffi/evm.rs @@ -1,5 +1,7 @@ //! EVM FFI. +use crate::primitives::Address; + #[link(wasm_import_module = "evm")] #[allow(improper_ctypes)] extern "C" { @@ -100,7 +102,7 @@ extern "C" { pub fn push31(val: i32); /// Push 32 bytes to the stack. - pub fn push32(val: i32); + pub fn push32(); /// Store a value in the storage pub fn sstore(); @@ -120,6 +122,9 @@ extern "C" { /// Compute Keccak-256 hash pub fn keccak256(); + /// Get the current message sender + pub fn caller() -> Address; + /// Append log record with no topics pub fn log0(name: &'static [u8]); diff --git a/zink/src/ffi/mod.rs b/zink/src/ffi/mod.rs index 2f41799fe..2b46443c5 100644 --- a/zink/src/ffi/mod.rs +++ b/zink/src/ffi/mod.rs @@ -1,6 +1,6 @@ //! Zink FFI. -use crate::primitives::Address; +use crate::primitives::{Address, U256}; pub mod asm; pub mod evm; @@ -14,6 +14,18 @@ extern "C" { /// Equal operation for addresses pub fn address_eq(this: Address, other: Address) -> bool; + /// Equal operation for addresses + pub fn u256_add(this: U256, other: U256) -> U256; + + /// Equal operation for addresses + pub fn u256_sub(this: U256, other: U256) -> U256; + + /// Equal operation for addresses + pub fn u256_lt(this: U256, other: U256) -> bool; + + /// Equal operation for addresses + pub fn u256_max() -> U256; + /// Set up a label for reserving 32 bytes in memory pub fn label_reserve_mem_32(); diff --git a/zink/src/primitives/address.rs b/zink/src/primitives/address.rs index be979b915..92ffb2649 100644 --- a/zink/src/primitives/address.rs +++ b/zink/src/primitives/address.rs @@ -5,10 +5,22 @@ use crate::{ffi, storage::StorageValue, Asm}; #[derive(Clone, Copy)] pub struct Address( #[cfg(target_family = "wasm")] i32, - #[cfg(not(target_family = "wasm"))] [u8; 20], + #[cfg(not(target_family = "wasm"))] pub [u8; 20], ); impl Address { + /// Returns empty address + #[cfg(not(target_family = "wasm"))] + pub const fn empty() -> Self { + Address([0; 20]) + } + + /// Returns empty address + #[cfg(target_family = "wasm")] + pub const fn empty() -> Self { + Address(0) + } + /// if self equal to another /// /// NOTE: not using core::cmp because it uses registers in wasm diff --git a/zink/src/primitives/mod.rs b/zink/src/primitives/mod.rs index d0f41fb0b..67b4af318 100644 --- a/zink/src/primitives/mod.rs +++ b/zink/src/primitives/mod.rs @@ -1,5 +1,11 @@ //! Zink primitive types mod address; +mod u256; pub use address::Address; +pub use u256::U256; + +pub type Bytes20 = Address; +pub type Bytes32 = U256; +pub type String32 = U256; diff --git a/zink/src/primitives/u256.rs b/zink/src/primitives/u256.rs new file mode 100644 index 000000000..65f42bb3f --- /dev/null +++ b/zink/src/primitives/u256.rs @@ -0,0 +1,55 @@ +#![allow(clippy::should_implement_trait)] +use crate::{ffi, storage::StorageValue, Asm}; + +/// Account address +#[repr(C)] +#[derive(Clone, Copy)] +pub struct U256( + #[cfg(target_family = "wasm")] i32, + #[cfg(not(target_family = "wasm"))] [u8; 32], +); + +impl U256 { + /// Returns empty address + #[cfg(not(target_family = "wasm"))] + pub const fn empty() -> Self { + U256([0; 32]) + } + + /// add another value + pub fn add(self, other: Self) -> Self { + unsafe { ffi::u256_add(self, other) } + } + + /// add another value + pub fn lt(self, other: Self) -> bool { + unsafe { ffi::u256_lt(self, other) } + } + + /// add another value + pub fn sub(self, other: Self) -> Self { + unsafe { ffi::u256_sub(self, other) } + } + + /// max of u256 + pub fn max() -> Self { + unsafe { ffi::u256_max() } + } +} + +impl Asm for U256 { + fn push(self) { + unsafe { ffi::asm::push_u256(self) } + } + + #[cfg(not(target_family = "wasm"))] + fn bytes32(&self) -> [u8; 32] { + self.0 + } +} + +impl StorageValue for U256 { + fn sload() -> Self { + unsafe { ffi::asm::sload_u256() } + } +} diff --git a/zink/src/storage/mod.rs b/zink/src/storage/mod.rs index 8eaef9326..d7ecc018e 100644 --- a/zink/src/storage/mod.rs +++ b/zink/src/storage/mod.rs @@ -18,3 +18,9 @@ impl StorageValue for i32 { unsafe { ffi::asm::sload_i32() } } } + +impl StorageValue for u32 { + fn sload() -> Self { + unsafe { ffi::asm::sload_u32() } + } +} diff --git a/zint/src/contract.rs b/zint/src/contract.rs index b53be3d03..e7eb6498e 100644 --- a/zint/src/contract.rs +++ b/zint/src/contract.rs @@ -43,6 +43,7 @@ impl Contract { .finish(self.artifact.runtime_bytecode.clone().into()) .map(|v| v.to_vec())?; + tracing::debug!("runtime bytecode: {}", hex::encode(&bytecode)); Ok(bytecode) }