Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
97 changes: 97 additions & 0 deletions crates/fe/tests/event_dynamic.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
use std::path::PathBuf;

use contract_harness::{ExecutionOptions, RuntimeInstance};
use fe::bench_support::compile_fe_sonatina_bytecode;
use tiny_keccak::{Hasher, Keccak};

fn keccak(input: &str) -> Vec<u8> {
let mut hasher = Keccak::v256();
hasher.update(input.as_bytes());
let mut out = [0; 32];
hasher.finalize(&mut out);
out.to_vec()
}

fn selector(signature: &str) -> Vec<u8> {
keccak(signature)[..4].to_vec()
}

fn word(value: u8) -> String {
format!("{value:064x}")
}

#[test]
fn add_liquidity_log_matches_solidity_vector() {
let fixture = PathBuf::from(env!("CARGO_MANIFEST_DIR"))
.join("tests/fixtures/fe_test/event_dyn_array_u256.fe");
let source = std::fs::read_to_string(fixture).expect("read dynamic event fixture");
let bytecode = compile_fe_sonatina_bytecode(&source, "event_dyn_array_u256", "Probe")
.expect("compile dynamic event fixture");
let mut instance =
RuntimeInstance::deploy(&hex::encode(bytecode.deploy)).expect("deploy Probe");

let outcome = instance
.call_raw_with_logs(&[0x26, 0x12, 0x1f, 0xf0], ExecutionOptions::default())
.expect("call f()");
assert_eq!(outcome.raw_logs.len(), 1);

let log = &outcome.raw_logs[0];
let topics = log.data.topics();
assert_eq!(topics.len(), 2);
assert_eq!(
topics[0].as_slice(),
hex::decode("df21883620cd435343a655a765cce604ee5e962b399aed0ebbbd63a9c7e570c4").unwrap(),
);
assert_eq!(topics[1].as_slice(), &[0u8; 32]);

let expected_data = hex::decode(concat!(
"0000000000000000000000000000000000000000000000000000000000000040",
"0000000000000000000000000000000000000000000000000000000000000003",
"0000000000000000000000000000000000000000000000000000000000000001",
"0000000000000000000000000000000000000000000000000000000000000007",
))
.unwrap();
assert_eq!(log.data.data.as_ref(), expected_data);

let outcome = instance
.call_raw_with_logs(&selector("single()"), ExecutionOptions::default())
.expect("call single()");
assert_eq!(outcome.raw_logs.len(), 1);
let log = &outcome.raw_logs[0];
assert_eq!(log.data.topics().len(), 1);
assert_eq!(
log.data.topics()[0].as_slice(),
keccak("SingleDynamic(uint256[])")
);
assert_eq!(
log.data.data.as_ref(),
hex::decode(format!("{}{}{}", word(32), word(1), word(7))).unwrap(),
);

let outcome = instance
.call_raw_with_logs(&selector("fixed_array()"), ExecutionOptions::default())
.expect("call fixed_array()");
assert_eq!(outcome.raw_logs.len(), 1);
let log = &outcome.raw_logs[0];
assert_eq!(log.data.topics().len(), 1);
assert_eq!(
log.data.topics()[0].as_slice(),
keccak("FixedArrayElements(uint256[2][])")
);
assert_eq!(
log.data.data.as_ref(),
hex::decode(format!("{}{}{}{}", word(32), word(1), word(7), word(9))).unwrap(),
);

let outcome = instance
.call_raw_with_logs(&selector("shadowed()"), ExecutionOptions::default())
.expect("call shadowed()");
assert_eq!(outcome.raw_logs.len(), 1);
let log = &outcome.raw_logs[0];
assert_eq!(log.data.topics().len(), 1);
assert_eq!(
log.data.topics()[0].as_slice(),
keccak("ShadowedDynArray(uint256)")
);
assert_eq!(log.data.data.as_ref(), hex::decode(word(11)).unwrap());
}
16 changes: 16 additions & 0 deletions crates/fe/tests/fixtures/fe_test/event_bytes.fe
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
use std::abi::{Bytes, bytes_from_words_prefix}
use std::evm::{Log, RawMem}

#[event]
struct BytesEvent { value: Bytes }

#[test]
fn test_bytes_event() uses (log: mut Log, raw: mut RawMem) {
assert!(
BytesEvent::TOPIC0 == 0x9ee3485561a302141390e6d886e41f4922a82c93c6ab2d9f52c30cbd682994f1,
)
let value = bytes_from_words_prefix<1, 1>(
[0xaa00000000000000000000000000000000000000000000000000000000000000],
)
log.emit(BytesEvent { value: value })
}
18 changes: 18 additions & 0 deletions crates/fe/tests/fixtures/fe_test/event_dyn_array_address.fe
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
use std::abi::DynArray
use std::evm::{Log, RawMem}

#[event]
struct AddressList { values: DynArray<Address> }

#[test]
fn test_dyn_array_address_event() uses (log: mut Log, raw: mut RawMem) {
assert!(
AddressList::TOPIC0 == 0x561020ffe511e96c2b2544836b77735d4b65afe2bcc8ab164cbec96d16fa392c,
)
let ptr = core::ptr::alloc_bytes(64)
raw.mstore(addr: ptr, value: 1)
raw.mstore(addr: core::ptr::offset_bytes(ptr, 32), value: 0x1234)
log.emit(
AddressList { values: DynArray::from_abi_tail(data: ptr, len: 1, size: 64) },
)
}
112 changes: 112 additions & 0 deletions crates/fe/tests/fixtures/fe_test/event_dyn_array_u256.fe
Original file line number Diff line number Diff line change
@@ -0,0 +1,112 @@
use core::abi::{Abi, AbiSize, Encode, store_word}
use std::abi::{DynArray as Values, SolCompat}
use std::evm::{Log, RawMem, encode_abi_payload}

struct DynArray<T> { value: T }

impl AbiSize for DynArray<u256> {
const HEAD_SIZE: u256 = 32
const IS_DYNAMIC: bool = false
}

impl<A: Abi> Encode<A> for DynArray<u256> {
fn encode(own self, _ ptr: *u8) {
store_word(ptr: ptr, value: self.value)
}
}

impl SolCompat for DynArray<u256> {
type S = String<7>
const SOL_TYPE: Self::S = "uint256"
}

#[event]
struct AddLiquidity {
#[indexed]
provider: Address,
token_amounts: Values<u256>,
invariant: u256,
}

#[event]
struct SingleDynamic { values: Values<u256> }

#[event]
struct FixedArrayElements { values: Values<[u256; 2]> }

#[event]
struct ShadowedDynArray { value: DynArray<u256> }

msg M {
#[selector = sol("f()")]
F,
#[selector = sol("single()")]
Single,
#[selector = sol("fixed_array()")]
FixedArray,
#[selector = sol("shadowed()")]
Shadowed,
}

pub contract Probe uses (log: mut Log, raw: mut RawMem) {
recv M {
F uses (mut log, mut raw) {
let ptr = core::ptr::alloc_bytes(64)
raw.mstore(addr: ptr, value: 1)
raw.mstore(addr: core::ptr::offset_bytes(ptr, 32), value: 7)
log.emit(
AddLiquidity {
provider: Address::zero(),
token_amounts: Values::from_abi_tail(data: ptr, len: 1, size: 64),
invariant: 3,
},
)
}
Single uses (mut log, mut raw) {
let ptr = core::ptr::alloc_bytes(64)
raw.mstore(addr: ptr, value: 1)
raw.mstore(addr: core::ptr::offset_bytes(ptr, 32), value: 7)
log.emit(
SingleDynamic { values: Values::from_abi_tail(data: ptr, len: 1, size: 64) },
)
}
FixedArray uses (mut log, mut raw) {
let ptr = core::ptr::alloc_bytes(96)
raw.mstore(addr: ptr, value: 1)
raw.mstore(addr: core::ptr::offset_bytes(ptr, 32), value: 7)
raw.mstore(addr: core::ptr::offset_bytes(ptr, 64), value: 9)
log.emit(
FixedArrayElements { values: Values::from_abi_tail(data: ptr, len: 1, size: 96) },
)
}
Shadowed uses (mut log) {
log.emit(ShadowedDynArray { value: DynArray { value: 11 } })
}
}
}

#[test]
fn test_dyn_array_u256_event() uses (log: mut Log, raw: mut RawMem) {
assert!(
AddLiquidity::TOPIC0 == 0xdf21883620cd435343a655a765cce604ee5e962b399aed0ebbbd63a9c7e570c4,
)

let ptr = core::ptr::alloc_bytes(64)
raw.mstore(addr: ptr, value: 1)
raw.mstore(addr: core::ptr::offset_bytes(ptr, 32), value: 7)
let values = Values::from_abi_tail(data: ptr, len: 1, size: 64)
let buffer = encode_abi_payload((values, 3 as u256))
let data = buffer.ptr()
let len = buffer.len()

// Solidity: abi.encode(uint256[](7), uint256(3)).
assert!(len == 128)
assert!(raw.mload(data) == 64)
assert!(raw.mload(core::ptr::offset_bytes(data, 32)) == 3)
assert!(raw.mload(core::ptr::offset_bytes(data, 64)) == 1)
assert!(raw.mload(core::ptr::offset_bytes(data, 96)) == 7)

log.emit(
AddLiquidity { provider: Address::zero(), token_amounts: values, invariant: 3 },
)
}
21 changes: 21 additions & 0 deletions crates/fe/tests/fixtures/fe_test/event_dyn_string.fe
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
use std::abi::DynString
use std::evm::{Log, RawMem}

#[event]
struct StringEvent { value: DynString }

#[test]
fn test_dyn_string_event() uses (log: mut Log, raw: mut RawMem) {
assert!(
StringEvent::TOPIC0 == 0x617cf8a4400dd7963ed519ebe655a16e8da1282bb8fea36a21f634af912f54ab,
)
let ptr = core::ptr::alloc_bytes(64)
raw.mstore(addr: ptr, value: 1)
raw.mstore(
addr: core::ptr::offset_bytes(ptr, 32),
value: 0x6100000000000000000000000000000000000000000000000000000000000000,
)
log.emit(
StringEvent { value: DynString::from_abi_tail(data: ptr, len: 1, size: 64) },
)
}
46 changes: 46 additions & 0 deletions crates/fe/tests/fixtures/fe_test/event_dynamic_mixed.fe
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
use std::abi::{Bytes, DynArray, bytes_from_words_prefix}
use std::evm::{Log, RawMem, encode_abi_payload}

#[event]
struct Mixed {
#[indexed]
provider: Address,
values: DynArray<u256>,
extra: Bytes,
invariant: u256,
}

#[test]
fn test_mixed_dynamic_event() uses (log: mut Log, raw: mut RawMem) {
assert!(
Mixed::TOPIC0 == 0x97cd0165cf79835d3d0313bb5b69cd7cffaba79923a1a4d398385b5c9950a6a2,
)

let array_ptr = core::ptr::alloc_bytes(64)
raw.mstore(addr: array_ptr, value: 1)
raw.mstore(addr: core::ptr::offset_bytes(array_ptr, 32), value: 7)
let values = DynArray::from_abi_tail(data: array_ptr, len: 1, size: 64)
let extra = bytes_from_words_prefix<1, 1>(
[0xaa00000000000000000000000000000000000000000000000000000000000000],
)
let buffer = encode_abi_payload((values, extra, 3 as u256))
let data = buffer.ptr()
let len = buffer.len()

// Solidity: abi.encode(uint256[](7), bytes(hex"aa"), uint256(3)).
assert!(len == 224)
assert!(raw.mload(data) == 96)
assert!(raw.mload(core::ptr::offset_bytes(data, 32)) == 160)
assert!(raw.mload(core::ptr::offset_bytes(data, 64)) == 3)
assert!(raw.mload(core::ptr::offset_bytes(data, 96)) == 1)
assert!(raw.mload(core::ptr::offset_bytes(data, 128)) == 7)
assert!(raw.mload(core::ptr::offset_bytes(data, 160)) == 1)
assert!(
raw.mload(core::ptr::offset_bytes(data, 192))
== 0xaa00000000000000000000000000000000000000000000000000000000000000,
)

log.emit(
Mixed { provider: Address::zero(), values: values, extra: extra, invariant: 3 },
)
}
Loading
Loading