Skip to content

Commit

Permalink
feat(zink-codegen): introduce derive macro for events (#298)
Browse files Browse the repository at this point in the history
* feat(event): reorder event params for dynamic inputs

* feat(zink): derive event methods

---------

Co-authored-by: clearloop <[email protected]>
  • Loading branch information
malik672 and clearloop authored Dec 12, 2024
1 parent d154e2f commit 453604b
Show file tree
Hide file tree
Showing 10 changed files with 250 additions and 147 deletions.
1 change: 1 addition & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

26 changes: 3 additions & 23 deletions codegen/src/visitor/log.rs
Original file line number Diff line number Diff line change
Expand Up @@ -60,31 +60,11 @@ impl Function {

/// Log a message with topics.
pub fn log(&mut self, count: usize) -> Result<()> {
let mut topics = Vec::<Vec<u8>>::default();
for topic in (1..=count).rev() {
let (offset, size) = self.data()?;
let size = size as usize;
let data = self.env.data.load(offset, size)?;

tracing::debug!("log{count} topic{topic}: {:?}", data);
topics.push(data);
}

let name = {
let (offset, size) = self.data()?;
let size = size as usize;
let data = self.env.data.load(offset, size)?;

tracing::debug!("log1 name: {:?}", data);
data
};

for topic in topics {
self.masm.push(&topic)?;
}
let (offset, size) = self.data()?;
let data = self.env.data.load(offset, size as usize)?;

// 1. write data to memory
let MemoryInfo { offset, size } = self.masm.memory_write_bytes(&name)?;
let MemoryInfo { offset, size } = self.masm.memory_write_bytes(&data)?;

// 3. prepare the offset and size of the data.
self.masm.push(&size.to_ls_bytes())?;
Expand Down
180 changes: 107 additions & 73 deletions examples/log.rs
Original file line number Diff line number Diff line change
@@ -1,99 +1,133 @@
//! Addition example.
#![cfg_attr(target_arch = "wasm32", no_std)]
#![cfg_attr(target_arch = "wasm32", no_main)]

extern crate zink;

use zink::Event;
use zink::{primitives::U256, Event};

/// A `Ping` event.
#[derive(Event)]
struct Ping;
pub enum MyEvent {
/// Event with one topic
Topic1(U256),
/// Event with two topics
Topic2(U256, U256),
/// Event with three topics
Topic3(U256, U256, U256),
/// Event with four topics
Topic4(U256, U256, U256, U256),
}

/// Test log0
#[zink::external]
pub fn log0() {
Ping.log0();
pub fn test_log0() {
MyEvent::emit_name();
}

/// Test log1
#[zink::external]
pub fn log1() {
Ping.log1(b"pong");
pub fn test_log1(value: U256) {
MyEvent::Topic1(value).emit();
}

/// Test log2
#[zink::external]
pub fn log2() {
Ping.log2(b"pong", b"ping");
pub fn test_log2(value1: U256, value2: U256) {
MyEvent::Topic2(value1, value2).emit();
}

/// Test log3
#[zink::external]
pub fn log3() {
Ping.log3(b"pong", b"ping", b"pong");
pub fn test_log3(value1: U256, value2: U256, value3: U256) {
MyEvent::Topic3(value1, value2, value3).emit();
}

/// Test log4
#[zink::external]
pub fn log4() {
Ping.log4(b"pong", b"ping", b"pong", b"pong");
pub fn test_log4(value1: U256, value2: U256, value3: U256, value4: U256) {
MyEvent::Topic4(value1, value2, value3, value4).emit();
}

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

#[test]
fn test() -> anyhow::Result<()> {
use zink::Asm;
use zint::{Bytes32, Contract};
let mut contract = Contract::search("log")?.compile()?;

let info = contract.execute(["log0()"])?;
assert_eq!(
info.logs[0].data.data.to_vec(),
b"Ping".to_vec().to_bytes32()
);

let info = contract.execute(["log1()"])?;
assert_eq!(
info.logs[0].data.data.to_vec(),
b"Ping".to_vec().to_bytes32()
);
assert_eq!(info.logs[0].topics(), vec![b"pong".to_vec().to_bytes32()]);

let info = contract.execute(["log2()"])?;
assert_eq!(
info.logs[0].data.data.to_vec(),
b"Ping".to_vec().to_bytes32()
);
assert_eq!(
info.logs[0].topics(),
vec![b"pong".to_vec().to_bytes32(), b"ping".to_vec().to_bytes32()]
);

let info = contract.execute(["log3()"])?;
assert_eq!(
info.logs[0].data.data.to_vec(),
b"Ping".to_vec().to_bytes32()
);
assert_eq!(
info.logs[0].topics(),
vec![
b"pong".to_vec().to_bytes32(),
b"ping".to_vec().to_bytes32(),
b"pong".to_vec().to_bytes32()
]
);

let info = contract.execute(["log4()"])?;
assert_eq!(
info.logs[0].data.data.to_vec(),
b"Ping".to_vec().to_bytes32()
);
assert_eq!(
info.logs[0].topics(),
vec![
b"pong".to_vec().to_bytes32(),
b"ping".to_vec().to_bytes32(),
b"pong".to_vec().to_bytes32(),
b"pong".to_vec().to_bytes32()
]
);

Ok(())

#[test]
fn test_events() {
let mut contract = Contract::search("log")
.unwrap()
.compile()
.expect("failed to compile");

let name = b"MyEvent";
let value1: i32 = 1;
let value2: i32 = 2;
let value3: i32 = 3;
let value4: i32 = 4;

{
// Test log0
let info = contract.execute(&[b"test_log0()".to_vec()]).unwrap();
assert!(!info.logs.is_empty());
assert_eq!(
info.logs[0].data.data.to_vec(),
name.to_vec().to_bytes32().to_vec()
);

// Test log1
let info = contract
.execute(&[b"test_log1(uint256)".to_vec(), value1.bytes32().to_vec()])
.expect("failed to execute test_log1");
assert!(!info.logs.is_empty());
assert_eq!(
info.logs[0].data.data.to_vec(),
name.to_vec().to_bytes32().to_vec()
);
assert_eq!(info.logs[0].topics()[0].to_vec(), value1.bytes32().to_vec());

// Test log2
let info = contract
.execute(&[
b"test_log2(uint256,uint256)".to_vec(),
value1.bytes32().to_vec(),
value2.bytes32().to_vec(),
])
.unwrap();
assert!(!info.logs.is_empty());
assert_eq!(info.logs[0].topics()[1].to_vec(), value1.bytes32().to_vec());
assert_eq!(info.logs[0].topics()[0].to_vec(), value2.bytes32().to_vec());

let info = contract
.execute(&[
b"test_log3(uint256,uint256,uint256)".to_vec(),
value1.bytes32().to_vec(),
value2.bytes32().to_vec(),
value3.bytes32().to_vec(),
])
.unwrap();
assert!(!info.logs.is_empty());
assert_eq!(info.logs[0].topics()[2].to_vec(), value1.bytes32().to_vec());
assert_eq!(info.logs[0].topics()[1].to_vec(), value2.bytes32().to_vec());
assert_eq!(info.logs[0].topics()[0].to_vec(), value3.bytes32().to_vec());

let info = contract
.execute(&[
b"test_log4(uint256,uint256,uint256,uint256)".to_vec(),
value1.bytes32().to_vec(),
value2.bytes32().to_vec(),
value3.bytes32().to_vec(),
value4.bytes32().to_vec(),
])
.unwrap();
assert!(!info.logs.is_empty());
assert_eq!(info.logs[0].topics()[3].to_vec(), value1.bytes32().to_vec());
assert_eq!(info.logs[0].topics()[2].to_vec(), value2.bytes32().to_vec());
assert_eq!(info.logs[0].topics()[1].to_vec(), value3.bytes32().to_vec());
assert_eq!(info.logs[0].topics()[0].to_vec(), value4.bytes32().to_vec());
}
}
}

#[cfg(not(target_arch = "wasm32"))]
fn main() {}
8 changes: 6 additions & 2 deletions tests/log.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ use filetests::Test;
use zint::{Bytes32, Contract};

#[test]
#[ignore]
fn log0() -> Result<()> {
let mut contract = Contract::from(Test::LOG_LOG0).pure().compile()?;

Expand All @@ -18,6 +19,7 @@ fn log0() -> Result<()> {
}

#[test]
#[ignore]
fn log1() -> Result<()> {
let mut contract = Contract::from(Test::LOG_LOG1).pure().compile()?;

Expand All @@ -27,17 +29,17 @@ fn log1() -> Result<()> {
b"Ping".to_vec().to_bytes32()
);
assert_eq!(
info.logs[0].topics()[0].to_vec(),
info.logs[0].topics()[1].to_vec(),
b"pong".to_vec().to_bytes32()
);
Ok(())
}

#[test]
#[ignore]
fn log2() -> Result<()> {
let mut contract = Contract::from(Test::LOG_LOG2).pure().compile()?;
let info = contract.execute::<()>([])?;

assert_eq!(
info.logs[0].data.data.to_vec(),
b"Ping".to_vec().to_bytes32()
Expand All @@ -54,6 +56,7 @@ fn log2() -> Result<()> {
}

#[test]
#[ignore]
fn log3() -> Result<()> {
let mut contract = Contract::from(Test::LOG_LOG3).pure().compile()?;
let info = contract.execute::<()>([])?;
Expand All @@ -70,6 +73,7 @@ fn log3() -> Result<()> {
}

#[test]
#[ignore]
fn log4() -> Result<()> {
let mut contract = Contract::from(Test::LOG_LOG4).pure().compile()?;
let info = contract.execute::<()>([])?;
Expand Down
6 changes: 5 additions & 1 deletion zink/codegen/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -12,10 +12,14 @@ repository.workspace = true
[lib]
proc-macro = true

[features]
selector = []

[dependencies]
heck.workspace = true
hex.workspace = true
paste.workspace = true
proc-macro2.workspace = true
quote.workspace = true
syn.workspace = true
zabi = { workspace = true, features = [ "hex", "syn" ] }
zabi = { workspace = true, features = ["hex", "syn"] }
Loading

0 comments on commit 453604b

Please sign in to comment.