diff --git a/Cargo.lock b/Cargo.lock index f53f63147..b686078e1 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1790,6 +1790,21 @@ dependencies = [ "tokio", ] +[[package]] +name = "erc1155-receiver-mock-example" +version = "0.3.0-alpha.1" +dependencies = [ + "alloy", + "alloy-primitives", + "alloy-sol-macro", + "alloy-sol-types", + "e2e", + "eyre", + "openzeppelin-stylus", + "stylus-sdk", + "tokio", +] + [[package]] name = "erc1155-supply-example" version = "0.3.0-alpha.1" diff --git a/Cargo.toml b/Cargo.toml index c8384c70f..ef200c5d0 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -36,6 +36,7 @@ members = [ "examples/uups-proxy", "examples/erc721-holder", "examples/erc1155-holder", + "examples/erc1155-receiver-mock", ] default-members = [ "contracts", @@ -71,6 +72,7 @@ default-members = [ "examples/uups-proxy", "examples/erc721-holder", "examples/erc1155-holder", + "examples/erc1155-receiver-mock", ] exclude = ["fuzz"] diff --git a/examples/erc1155-receiver-mock/Cargo.toml b/examples/erc1155-receiver-mock/Cargo.toml new file mode 100644 index 000000000..32113f8cf --- /dev/null +++ b/examples/erc1155-receiver-mock/Cargo.toml @@ -0,0 +1,31 @@ +[package] +name = "erc1155-receiver-mock-example" +edition.workspace = true +license.workspace = true +repository.workspace = true +publish = false +version.workspace = true + +[dependencies] +openzeppelin-stylus.workspace = true +alloy-primitives.workspace = true +alloy-sol-macro.workspace = true +alloy-sol-types.workspace = true +stylus-sdk.workspace = true + +[dev-dependencies] +alloy.workspace = true +e2e.workspace = true +tokio.workspace = true +eyre.workspace = true + +[features] +e2e = [] +export-abi = ["openzeppelin-stylus/export-abi"] + +[lib] +crate-type = ["lib", "cdylib"] + +[[bin]] +name = "erc1155-receiver-mock-example" +path = "src/main.rs" diff --git a/examples/erc1155-receiver-mock/src/lib.rs b/examples/erc1155-receiver-mock/src/lib.rs new file mode 100644 index 000000000..2c5c9b84f --- /dev/null +++ b/examples/erc1155-receiver-mock/src/lib.rs @@ -0,0 +1,187 @@ +#![cfg_attr(not(any(test, feature = "export-abi")), no_main)] +extern crate alloc; + +use alloc::vec::Vec; + +use openzeppelin_stylus::{ + token::erc1155::{ + receiver::{ + IErc1155Receiver, BATCH_TRANSFER_FN_SELECTOR, + SINGLE_TRANSFER_FN_SELECTOR, + }, + utils::Erc1155Holder, + }, + utils::introspection::erc165::IErc165, +}; +pub use sol::*; +use stylus_sdk::{ + abi::Bytes, + alloy_primitives::{ + aliases::{B32, U8}, + Address, U256, + }, + evm, + prelude::*, + storage::StorageU8, +}; + +mod sol { + use alloy_sol_macro::sol; + sol! { + #[derive(Debug)] + #[allow(missing_docs)] + event Received(address operator, address from, uint256 id, uint256 value, bytes data); + + #[derive(Debug)] + #[allow(missing_docs)] + event BatchReceived(address operator, address from, uint256[] ids, uint256[] values, bytes data); + } + + sol! { + #[derive(Debug)] + #[allow(missing_docs)] + error CustomError(bytes4 data); + } +} + +#[derive(SolidityError, Debug)] +pub enum Error { + MockCustomError(CustomError), +} + +/// Enum representing different revert types for testing. +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub enum RevertType { + None, + CustomError, + Panic, +} + +impl From for RevertType { + fn from(value: u8) -> Self { + match value { + 1 => RevertType::CustomError, + 2 => RevertType::Panic, + _ => RevertType::None, + } + } +} + +impl From for RevertType { + fn from(value: U8) -> Self { + let revert_type: u8 = u8::try_from(value).expect("should be valid"); + revert_type.into() + } +} + +impl From for u8 { + fn from(value: RevertType) -> Self { + match value { + RevertType::None => 0, + RevertType::CustomError => 1, + RevertType::Panic => 2, + } + } +} + +#[entrypoint] +#[storage] +struct Erc1155ReceiverMock { + error_type: StorageU8, + holder: Erc1155Holder, +} + +#[public] +#[implements(IErc1155Receiver, IErc165)] +impl Erc1155ReceiverMock { + #[constructor] + fn constructor(&mut self, error_type: U8) { + self.error_type.set(error_type) + } +} + +#[public] +impl IErc1155Receiver for Erc1155ReceiverMock { + #[selector(name = "onERC1155Received")] + fn on_erc1155_received( + &mut self, + operator: Address, + from: Address, + id: U256, + value: U256, + data: Bytes, + ) -> Result> { + let error_type: RevertType = self.error_type.get().into(); + + match error_type { + RevertType::CustomError => { + Err(Error::MockCustomError(CustomError { + data: SINGLE_TRANSFER_FN_SELECTOR, + }) + .into()) + } + RevertType::Panic => { + // simulate a panic by dividing by [`U256::ZERO`]. + let _ = U256::from(0) / U256::from(0); + unreachable!() + } + RevertType::None => { + #[allow(deprecated)] + evm::log(Received { + operator, + from, + id, + value, + data: data.to_vec().into(), + }); + self.holder.on_erc1155_received(operator, from, id, value, data) + } + } + } + + #[selector(name = "onERC1155BatchReceived")] + fn on_erc1155_batch_received( + &mut self, + operator: Address, + from: Address, + ids: Vec, + values: Vec, + data: Bytes, + ) -> Result> { + let error_type: RevertType = self.error_type.get().into(); + + match error_type { + RevertType::CustomError => { + Err(Error::MockCustomError(CustomError { + data: BATCH_TRANSFER_FN_SELECTOR, + }) + .into()) + } + RevertType::Panic => { + // simulate a panic by dividing by [`U256::ZERO`]. + let _ = U256::from(0) / U256::from(0); + unreachable!() + } + RevertType::None => { + #[allow(deprecated)] + evm::log(BatchReceived { + operator, + from, + ids: ids.clone(), + values: values.clone(), + data: data.to_vec().into(), + }); + self.holder.on_erc1155_batch_received( + operator, from, ids, values, data, + ) + } + } + } +} + +#[public] +impl IErc165 for Erc1155ReceiverMock { + fn supports_interface(&self, interface_id: B32) -> bool { + self.holder.supports_interface(interface_id) + } +} diff --git a/examples/erc1155-receiver-mock/src/main.rs b/examples/erc1155-receiver-mock/src/main.rs new file mode 100644 index 000000000..07c7b7b16 --- /dev/null +++ b/examples/erc1155-receiver-mock/src/main.rs @@ -0,0 +1,10 @@ +#![cfg_attr(not(any(test, feature = "export-abi")), no_main)] + +#[cfg(not(any(test, feature = "export-abi")))] +#[no_mangle] +pub extern "C" fn main() {} + +#[cfg(feature = "export-abi")] +fn main() { + erc1155_receiver_mock_example::print_from_args(); +} diff --git a/examples/erc1155-receiver-mock/tests/abi/mod.rs b/examples/erc1155-receiver-mock/tests/abi/mod.rs new file mode 100644 index 000000000..823fc673c --- /dev/null +++ b/examples/erc1155-receiver-mock/tests/abi/mod.rs @@ -0,0 +1,30 @@ +#![allow(dead_code)] +use alloy::sol; + +sol!( + #[sol(rpc)] + contract Erc1155ReceiverMock { + #[derive(Debug)] + function onERC1155Received( + address operator, + address from, + uint256 id, + uint256 value, + bytes calldata data + ) external returns (bytes4); + + #[derive(Debug)] + function onERC1155BatchReceived( + address operator, + address from, + uint256[] calldata ids, + uint256[] calldata values, + bytes calldata data + ) external returns (bytes4); + + function supportsInterface(bytes4 interface_id) external view returns (bool supportsInterface); + + #[derive(Debug, PartialEq)] + error CustomError(bytes4 data); + } +); diff --git a/examples/erc1155-receiver-mock/tests/erc1155-receiver-mock.rs b/examples/erc1155-receiver-mock/tests/erc1155-receiver-mock.rs new file mode 100644 index 000000000..bd49a039c --- /dev/null +++ b/examples/erc1155-receiver-mock/tests/erc1155-receiver-mock.rs @@ -0,0 +1,253 @@ +#![cfg(feature = "e2e")] + +use abi::Erc1155ReceiverMock; +use alloy::primitives::{uint, Bytes, U256, U8}; +use e2e::{constructor, Account, Constructor, Panic, PanicCode, Revert}; +use eyre::Result; +use openzeppelin_stylus::token::erc1155::receiver::{ + BATCH_TRANSFER_FN_SELECTOR, SINGLE_TRANSFER_FN_SELECTOR, +}; + +mod abi; + +const REVERT_TYPE_NONE: U8 = uint!(0_U8); +const REVERT_TYPE_CUSTOM_ERROR: U8 = uint!(1_U8); +const REVERT_TYPE_PANIC: U8 = uint!(2_U8); + +fn constructor(error_type: U8) -> Constructor { + constructor!(error_type) +} + +// ============================================================================ +// Happy Path Tests +// ============================================================================ + +#[e2e::test] +async fn returns_correct_selector_for_single_transfer( + alice: Account, +) -> Result<()> { + let contract_addr = alice + .as_deployer() + .with_constructor(constructor(REVERT_TYPE_NONE)) + .deploy() + .await? + .contract_address; + + let contract = Erc1155ReceiverMock::new(contract_addr, &alice.wallet); + + let operator = alice.address(); + let from = alice.address(); + let id = U256::from(1); + let value = U256::from(1); + let data = Bytes::from(vec![0xde, 0xad, 0xbe, 0xef]); + let interface_selector = contract + .onERC1155Received(operator, from, id, value, data) + .call() + .await? + ._0; + + assert_eq!(SINGLE_TRANSFER_FN_SELECTOR, interface_selector); + + Ok(()) +} + +#[e2e::test] +async fn returns_correct_selector_for_batch_transfer( + alice: Account, +) -> Result<()> { + let contract_addr = alice + .as_deployer() + .with_constructor(constructor(REVERT_TYPE_NONE)) + .deploy() + .await? + .contract_address; + + let contract = Erc1155ReceiverMock::new(contract_addr, &alice.wallet); + + let operator = alice.address(); + let from = alice.address(); + let ids = vec![U256::from(1), U256::from(2)]; + let values = vec![U256::from(1), U256::from(2)]; + let data = Bytes::from(vec![0xde, 0xad, 0xbe, 0xef]); + let interface_selector = contract + .onERC1155BatchReceived(operator, from, ids, values, data) + .call() + .await? + ._0; + + assert_eq!(BATCH_TRANSFER_FN_SELECTOR, interface_selector); + + Ok(()) +} + +// ============================================================================ +// Error Handling Tests - Single Transfer +// ============================================================================ + +#[e2e::test] +async fn reverts_without_message_for_single_transfer( + alice: Account, +) -> Result<()> { + let contract_addr = alice + .as_deployer() + .with_constructor(constructor(REVERT_TYPE_CUSTOM_ERROR)) + .deploy() + .await? + .contract_address; + + let contract = Erc1155ReceiverMock::new(contract_addr, &alice.wallet); + + let operator = alice.address(); + let from = alice.address(); + let id = U256::from(1); + let value = U256::from(1); + let data = Bytes::new(); + + let err = contract + .onERC1155Received(operator, from, id, value, data) + .call() + .await + .expect_err("should revert with custom error"); + + assert!(err.reverted_with(Erc1155ReceiverMock::CustomError { + data: SINGLE_TRANSFER_FN_SELECTOR + })); + + Ok(()) +} + +#[e2e::test] +async fn panics_for_single_transfer(alice: Account) -> Result<()> { + let contract_addr = alice + .as_deployer() + .with_constructor(constructor(REVERT_TYPE_PANIC)) + .deploy() + .await? + .contract_address; + + let contract = Erc1155ReceiverMock::new(contract_addr, &alice.wallet); + + let operator = alice.address(); + let from = alice.address(); + let id = U256::from(1); + let value = U256::from(1); + let data = Bytes::new(); + + let err = contract + .onERC1155Received(operator, from, id, value, data) + .call() + .await + .expect_err("should panic"); + + assert!(err.panicked_with(PanicCode::DivisionByZero)); + + Ok(()) +} + +// ============================================================================ +// Error Handling Tests - Batch Transfer +// ============================================================================ + +#[e2e::test] +async fn reverts_with_custom_error_for_batch_transfer( + alice: Account, +) -> Result<()> { + let contract_addr = alice + .as_deployer() + .with_constructor(constructor(REVERT_TYPE_CUSTOM_ERROR)) + .deploy() + .await? + .contract_address; + + let contract = Erc1155ReceiverMock::new(contract_addr, &alice.wallet); + + let operator = alice.address(); + let from = alice.address(); + let ids = vec![U256::from(1), U256::from(2)]; + let values = vec![U256::from(1), U256::from(2)]; + let data = Bytes::new(); + + let err = contract + .onERC1155BatchReceived(operator, from, ids, values, data) + .call() + .await + .expect_err("should revert with custom error"); + + assert!(err.reverted_with(Erc1155ReceiverMock::CustomError { + data: BATCH_TRANSFER_FN_SELECTOR + })); + + Ok(()) +} + +#[e2e::test] +async fn panics_for_batch_transfer(alice: Account) -> Result<()> { + let contract_addr = alice + .as_deployer() + .with_constructor(constructor(REVERT_TYPE_PANIC)) + .deploy() + .await? + .contract_address; + + let contract = Erc1155ReceiverMock::new(contract_addr, &alice.wallet); + + let operator = alice.address(); + let from = alice.address(); + let ids = vec![U256::from(1), U256::from(2)]; + let values = vec![U256::from(1), U256::from(2)]; + let data = Bytes::new(); + + let err = contract + .onERC1155BatchReceived(operator, from, ids, values, data) + .call() + .await + .expect_err("should panic"); + + assert!(err.panicked_with(PanicCode::DivisionByZero)); + + Ok(()) +} + +// ============================================================================ +// Interface Support Tests +// ============================================================================ + +#[e2e::test] +async fn supports_interface(alice: Account) -> Result<()> { + let contract_addr = alice + .as_deployer() + .with_constructor(constructor(REVERT_TYPE_NONE)) + .deploy() + .await? + .contract_address; + let contract = Erc1155ReceiverMock::new(contract_addr, &alice.wallet); + + let invalid_interface_id: u32 = 0xffffffff; + assert!( + !contract + .supportsInterface(invalid_interface_id.into()) + .call() + .await? + .supportsInterface + ); + + let erc1155_holder_interface_id: u32 = 0x4e2312e0; + assert!( + contract + .supportsInterface(erc1155_holder_interface_id.into()) + .call() + .await? + .supportsInterface + ); + + let erc165_interface_id: u32 = 0x01ffc9a7; + assert!( + contract + .supportsInterface(erc165_interface_id.into()) + .call() + .await? + .supportsInterface + ); + + Ok(()) +} diff --git a/examples/erc1155/tests/abi/mod.rs b/examples/erc1155/tests/abi/mod.rs index 7eca18970..5de50fe7c 100644 --- a/examples/erc1155/tests/abi/mod.rs +++ b/examples/erc1155/tests/abi/mod.rs @@ -34,4 +34,14 @@ sol!( #[derive(Debug, PartialEq)] event ApprovalForAll(address indexed account, address indexed operator, bool approved); } + + contract Erc1155ReceiverMock { + #[derive(Debug, PartialEq)] + event Received(address operator, address from, uint256 id, uint256 value, bytes data); + #[derive(Debug, PartialEq)] + event BatchReceived(address operator, address from, uint256[] ids, uint256[] values, bytes data); + + #[derive(Debug, PartialEq)] + error CustomError(bytes4 data); + } ); diff --git a/examples/erc1155/tests/erc1155.rs b/examples/erc1155/tests/erc1155.rs index 18a54db1f..d07f1f965 100644 --- a/examples/erc1155/tests/erc1155.rs +++ b/examples/erc1155/tests/erc1155.rs @@ -1,15 +1,24 @@ #![cfg(feature = "e2e")] -use abi::Erc1155; +use abi::{Erc1155, Erc1155ReceiverMock}; use alloy::{ - primitives::{uint, Address, U256}, + primitives::{uint, Address, U256, U8}, sol_types::SolError, }; -use e2e::{receipt, send, watch, Account, EventExt, PanicCode, Revert}; -use mock::{receiver, receiver::ERC1155ReceiverMock}; +use e2e::{ + constructor, receipt, send, watch, Account, Constructor, EventExt, + PanicCode, Revert, +}; mod abi; -mod mock; + +fn mock_receiver_constructor(error_type: U8) -> Constructor { + constructor!(error_type) +} + +const REVERT_TYPE_NONE: U8 = uint!(0_U8); +const REVERT_TYPE_CUSTOM_ERROR: U8 = uint!(1_U8); +const REVERT_TYPE_PANIC: U8 = uint!(2_U8); fn random_token_ids(size: usize) -> Vec { (0..size).map(U256::from).collect() @@ -128,9 +137,13 @@ async fn mints_to_receiver_contract(alice: Account) -> eyre::Result<()> { let contract_addr = alice.as_deployer().deploy().await?.contract_address; let contract = Erc1155::new(contract_addr, &alice.wallet); - let receiver_addr = - receiver::deploy(&alice.wallet, ERC1155ReceiverMock::RevertType::None) - .await?; + let receiver_addr = alice + .as_deployer() + .with_constructor(mock_receiver_constructor(REVERT_TYPE_NONE)) + .with_example_name("erc1155-receiver-mock") + .deploy() + .await? + .contract_address; let alice_addr = alice.address(); let token_id = random_token_ids(1)[0]; @@ -150,7 +163,7 @@ async fn mints_to_receiver_contract(alice: Account) -> eyre::Result<()> { value })); - assert!(receipt.emits(ERC1155ReceiverMock::Received { + assert!(receipt.emits(Erc1155ReceiverMock::Received { operator: alice_addr, from: Address::ZERO, id: token_id, @@ -172,11 +185,13 @@ async fn errors_when_receiver_reverts_with_reason_in_mint( let contract_addr = alice.as_deployer().deploy().await?.contract_address; let contract = Erc1155::new(contract_addr, &alice.wallet); - let receiver_address = receiver::deploy( - &alice.wallet, - ERC1155ReceiverMock::RevertType::RevertWithMessage, - ) - .await?; + let receiver_address = alice + .as_deployer() + .with_constructor(mock_receiver_constructor(REVERT_TYPE_CUSTOM_ERROR)) + .with_example_name("erc1155-receiver-mock") + .deploy() + .await? + .contract_address; let token_id = random_token_ids(1)[0]; let value = random_values(1)[0]; @@ -199,37 +214,6 @@ async fn errors_when_receiver_reverts_with_reason_in_mint( Ok(()) } -#[e2e::test] -async fn errors_when_receiver_reverts_without_reason_in_mint( - alice: Account, -) -> eyre::Result<()> { - let contract_addr = alice.as_deployer().deploy().await?.contract_address; - let contract = Erc1155::new(contract_addr, &alice.wallet); - - let receiver_address = receiver::deploy( - &alice.wallet, - ERC1155ReceiverMock::RevertType::RevertWithoutMessage, - ) - .await?; - - let token_id = random_token_ids(1)[0]; - let value = random_values(1)[0]; - - let err = send!(contract.mint( - receiver_address, - token_id, - value, - vec![0, 1, 2, 3].into() - )) - .expect_err("should not mint when receiver reverts"); - - assert!(err.reverted_with(Erc1155::ERC1155InvalidReceiver { - receiver: receiver_address - })); - - Ok(()) -} - #[e2e::test] async fn errors_when_receiver_panics_in_mint( alice: Account, @@ -237,9 +221,13 @@ async fn errors_when_receiver_panics_in_mint( let contract_addr = alice.as_deployer().deploy().await?.contract_address; let contract = Erc1155::new(contract_addr, &alice.wallet); - let receiver_address = - receiver::deploy(&alice.wallet, ERC1155ReceiverMock::RevertType::Panic) - .await?; + let receiver_address = alice + .as_deployer() + .with_constructor(mock_receiver_constructor(REVERT_TYPE_PANIC)) + .with_example_name("erc1155-receiver-mock") + .deploy() + .await? + .contract_address; let token_id = random_token_ids(1)[0]; let value = random_values(1)[0]; @@ -335,9 +323,13 @@ async fn mint_batch_transfer_to_receiver_contract( let contract_addr = alice.as_deployer().deploy().await?.contract_address; let contract = Erc1155::new(contract_addr, &alice.wallet); - let receiver_addr = - receiver::deploy(&alice.wallet, ERC1155ReceiverMock::RevertType::None) - .await?; + let receiver_addr = alice + .as_deployer() + .with_constructor(mock_receiver_constructor(REVERT_TYPE_NONE)) + .with_example_name("erc1155-receiver-mock") + .deploy() + .await? + .contract_address; let alice_addr = alice.address(); let token_ids = random_token_ids(2); @@ -367,7 +359,7 @@ async fn mint_batch_transfer_to_receiver_contract( values: values.clone() })); - assert!(receipt.emits(ERC1155ReceiverMock::BatchReceived { + assert!(receipt.emits(Erc1155ReceiverMock::BatchReceived { operator: alice_addr, from: Address::ZERO, ids: token_ids.clone(), @@ -401,11 +393,13 @@ async fn errors_when_receiver_reverts_with_reason_in_batch_mint( let contract_addr = alice.as_deployer().deploy().await?.contract_address; let contract = Erc1155::new(contract_addr, &alice.wallet); - let receiver_address = receiver::deploy( - &alice.wallet, - ERC1155ReceiverMock::RevertType::RevertWithMessage, - ) - .await?; + let receiver_address = alice + .as_deployer() + .with_constructor(mock_receiver_constructor(REVERT_TYPE_CUSTOM_ERROR)) + .with_example_name("erc1155-receiver-mock") + .deploy() + .await? + .contract_address; let token_ids = random_token_ids(2); let values = random_values(2); @@ -428,37 +422,6 @@ async fn errors_when_receiver_reverts_with_reason_in_batch_mint( Ok(()) } -#[e2e::test] -async fn errors_when_receiver_reverts_without_reason_in_batch_mint( - alice: Account, -) -> eyre::Result<()> { - let contract_addr = alice.as_deployer().deploy().await?.contract_address; - let contract = Erc1155::new(contract_addr, &alice.wallet); - - let receiver_address = receiver::deploy( - &alice.wallet, - ERC1155ReceiverMock::RevertType::RevertWithoutMessage, - ) - .await?; - - let token_ids = random_token_ids(2); - let values = random_values(2); - - let err = send!(contract.mintBatch( - receiver_address, - token_ids.clone(), - values.clone(), - vec![].into() - )) - .expect_err("should not mint batch when receiver reverts"); - - assert!(err.reverted_with(Erc1155::ERC1155InvalidReceiver { - receiver: receiver_address - })); - - Ok(()) -} - #[e2e::test] async fn errors_when_receiver_panics_in_batch_mint( alice: Account, @@ -466,9 +429,13 @@ async fn errors_when_receiver_panics_in_batch_mint( let contract_addr = alice.as_deployer().deploy().await?.contract_address; let contract = Erc1155::new(contract_addr, &alice.wallet); - let receiver_address = - receiver::deploy(&alice.wallet, ERC1155ReceiverMock::RevertType::Panic) - .await?; + let receiver_address = alice + .as_deployer() + .with_constructor(mock_receiver_constructor(REVERT_TYPE_PANIC)) + .with_example_name("erc1155-receiver-mock") + .deploy() + .await? + .contract_address; let token_ids = random_token_ids(2); let values = random_values(2); @@ -731,9 +698,13 @@ async fn safe_transfer_to_receiver_contract( let contract_addr = alice.as_deployer().deploy().await?.contract_address; let contract = Erc1155::new(contract_addr, &alice.wallet); - let receiver_addr = - receiver::deploy(&alice.wallet, ERC1155ReceiverMock::RevertType::None) - .await?; + let receiver_addr = alice + .as_deployer() + .with_constructor(mock_receiver_constructor(REVERT_TYPE_NONE)) + .with_example_name("erc1155-receiver-mock") + .deploy() + .await? + .contract_address; let alice_addr = alice.address(); let token_id = random_token_ids(1)[0]; @@ -767,7 +738,7 @@ async fn safe_transfer_to_receiver_contract( value })); - assert!(receipt.emits(ERC1155ReceiverMock::Received { + assert!(receipt.emits(Erc1155ReceiverMock::Received { operator: alice_addr, from: alice_addr, id: token_id, @@ -793,11 +764,13 @@ async fn errors_when_receiver_reverts_with_reason( let contract_addr = alice.as_deployer().deploy().await?.contract_address; let contract = Erc1155::new(contract_addr, &alice.wallet); - let receiver_address = receiver::deploy( - &alice.wallet, - ERC1155ReceiverMock::RevertType::RevertWithMessage, - ) - .await?; + let receiver_address = alice + .as_deployer() + .with_constructor(mock_receiver_constructor(REVERT_TYPE_CUSTOM_ERROR)) + .with_example_name("erc1155-receiver-mock") + .deploy() + .await? + .contract_address; let alice_addr = alice.address(); let token_id = random_token_ids(1)[0]; @@ -829,54 +802,18 @@ async fn errors_when_receiver_reverts_with_reason( Ok(()) } -#[e2e::test] -async fn errors_when_receiver_reverts_without_reason( - alice: Account, -) -> eyre::Result<()> { - let contract_addr = alice.as_deployer().deploy().await?.contract_address; - let contract = Erc1155::new(contract_addr, &alice.wallet); - - let receiver_address = receiver::deploy( - &alice.wallet, - ERC1155ReceiverMock::RevertType::RevertWithoutMessage, - ) - .await?; - - let alice_addr = alice.address(); - let token_id = random_token_ids(1)[0]; - let value = random_values(1)[0]; - - watch!(contract.mint( - alice_addr, - token_id, - value, - vec![0, 1, 2, 3].into() - ))?; - - let err = send!(contract.safeTransferFrom( - alice_addr, - receiver_address, - token_id, - value, - vec![].into() - )) - .expect_err("should not transfer when receiver reverts"); - - assert!(err.reverted_with(Erc1155::ERC1155InvalidReceiver { - receiver: receiver_address - })); - - Ok(()) -} - #[e2e::test] async fn errors_when_receiver_panics(alice: Account) -> eyre::Result<()> { let contract_addr = alice.as_deployer().deploy().await?.contract_address; let contract = Erc1155::new(contract_addr, &alice.wallet); - let receiver_address = - receiver::deploy(&alice.wallet, ERC1155ReceiverMock::RevertType::Panic) - .await?; + let receiver_address = alice + .as_deployer() + .with_constructor(mock_receiver_constructor(REVERT_TYPE_PANIC)) + .with_example_name("erc1155-receiver-mock") + .deploy() + .await? + .contract_address; let alice_addr = alice.address(); let token_id = random_token_ids(1)[0]; @@ -1116,9 +1053,13 @@ async fn safe_batch_transfer_to_receiver_contract( let contract_addr = alice.as_deployer().deploy().await?.contract_address; let contract = Erc1155::new(contract_addr, &alice.wallet); - let receiver_addr = - receiver::deploy(&alice.wallet, ERC1155ReceiverMock::RevertType::None) - .await?; + let receiver_addr = alice + .as_deployer() + .with_constructor(mock_receiver_constructor(REVERT_TYPE_NONE)) + .with_example_name("erc1155-receiver-mock") + .deploy() + .await? + .contract_address; let alice_addr = alice.address(); let token_ids = random_token_ids(2); @@ -1162,7 +1103,7 @@ async fn safe_batch_transfer_to_receiver_contract( values: values.clone() })); - assert!(receipt.emits(ERC1155ReceiverMock::BatchReceived { + assert!(receipt.emits(Erc1155ReceiverMock::BatchReceived { operator: alice_addr, from: alice_addr, ids: token_ids.clone(), @@ -1202,11 +1143,13 @@ async fn errors_when_receiver_reverts_with_reason_in_batch_transfer( let contract_addr = alice.as_deployer().deploy().await?.contract_address; let contract = Erc1155::new(contract_addr, &alice.wallet); - let receiver_address = receiver::deploy( - &alice.wallet, - ERC1155ReceiverMock::RevertType::RevertWithMessage, - ) - .await?; + let receiver_address = alice + .as_deployer() + .with_constructor(mock_receiver_constructor(REVERT_TYPE_CUSTOM_ERROR)) + .with_example_name("erc1155-receiver-mock") + .deploy() + .await? + .contract_address; let alice_addr = alice.address(); let token_ids = random_token_ids(2); @@ -1238,46 +1181,6 @@ async fn errors_when_receiver_reverts_with_reason_in_batch_transfer( Ok(()) } -#[e2e::test] -async fn errors_when_receiver_reverts_without_reason_in_batch_transfer( - alice: Account, -) -> eyre::Result<()> { - let contract_addr = alice.as_deployer().deploy().await?.contract_address; - let contract = Erc1155::new(contract_addr, &alice.wallet); - - let receiver_address = receiver::deploy( - &alice.wallet, - ERC1155ReceiverMock::RevertType::RevertWithoutMessage, - ) - .await?; - - let alice_addr = alice.address(); - let token_ids = random_token_ids(2); - let values = random_values(2); - - watch!(contract.mintBatch( - alice_addr, - token_ids.clone(), - values.clone(), - vec![].into() - ))?; - - let err = send!(contract.safeBatchTransferFrom( - alice_addr, - receiver_address, - token_ids, - values, - vec![].into() - )) - .expect_err("should not transfer when receiver reverts"); - - assert!(err.reverted_with(Erc1155::ERC1155InvalidReceiver { - receiver: receiver_address - })); - - Ok(()) -} - #[e2e::test] async fn errors_when_receiver_panics_in_batch_transfer( alice: Account, @@ -1285,9 +1188,13 @@ async fn errors_when_receiver_panics_in_batch_transfer( let contract_addr = alice.as_deployer().deploy().await?.contract_address; let contract = Erc1155::new(contract_addr, &alice.wallet); - let receiver_address = - receiver::deploy(&alice.wallet, ERC1155ReceiverMock::RevertType::Panic) - .await?; + let receiver_address = alice + .as_deployer() + .with_constructor(mock_receiver_constructor(REVERT_TYPE_PANIC)) + .with_example_name("erc1155-receiver-mock") + .deploy() + .await? + .contract_address; let alice_addr = alice.address(); let token_ids = random_token_ids(2); diff --git a/examples/erc1155/tests/mock/mod.rs b/examples/erc1155/tests/mock/mod.rs deleted file mode 100644 index 4c0db7eaa..000000000 --- a/examples/erc1155/tests/mock/mod.rs +++ /dev/null @@ -1 +0,0 @@ -pub mod receiver; diff --git a/examples/erc1155/tests/mock/receiver.rs b/examples/erc1155/tests/mock/receiver.rs deleted file mode 100644 index 15e7f0168..000000000 --- a/examples/erc1155/tests/mock/receiver.rs +++ /dev/null @@ -1,113 +0,0 @@ -#![allow(dead_code)] -#![cfg(feature = "e2e")] -use alloy::{ - primitives::{aliases::B32, Address, U256}, - sol, -}; -use e2e::Wallet; -use stylus_sdk::{abi::Bytes, function_selector}; - -const REC_RETVAL: B32 = B32::new(function_selector!( - "onERC1155Received", - Address, - Address, - U256, - U256, - Bytes -)); - -const BAT_RETVAL: B32 = B32::new(function_selector!( - "onERC1155BatchReceived", - Address, - Address, - Vec, - Vec, - Bytes -)); - -sol! { - #[allow(missing_docs)] - // Built with Remix IDE; solc 0.8.24+commit.e11b9ed9 - #[sol(rpc, bytecode="60e060405234801562000010575f80fd5b5060405162000f7e38038062000f7e833981810160405281019062000036919062000181565b827bffffffffffffffffffffffffffffffffffffffffffffffffffffffff19166080817bffffffffffffffffffffffffffffffffffffffffffffffffffffffff191681525050817bffffffffffffffffffffffffffffffffffffffffffffffffffffffff191660a0817bffffffffffffffffffffffffffffffffffffffffffffffffffffffff191681525050806004811115620000d857620000d7620001da565b5b60c0816004811115620000f057620000ef620001da565b5b8152505050505062000207565b5f80fd5b5f7fffffffff0000000000000000000000000000000000000000000000000000000082169050919050565b620001378162000101565b811462000142575f80fd5b50565b5f8151905062000155816200012c565b92915050565b6005811062000168575f80fd5b50565b5f815190506200017b816200015b565b92915050565b5f805f606084860312156200019b576200019a620000fd565b5b5f620001aa8682870162000145565b9350506020620001bd8682870162000145565b9250506040620001d0868287016200016b565b9150509250925092565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52602160045260245ffd5b60805160a05160c051610d0d620002715f395f8181610153015281816101a30152818161022a015281816102d2015281816103a4015281816103f40152818161047b015261052301525f61036001525f8181610262015281816104b301526105ad0152610d0d5ff3fe608060405234801561000f575f80fd5b506004361061003f575f3560e01c806301ffc9a714610043578063bc197c8114610073578063f23a6e61146100a3575b5f80fd5b61005d60048036038101906100589190610635565b6100d3565b60405161006a919061067a565b60405180910390f35b61008d600480360381019061008891906107a3565b61013c565b60405161009a9190610889565b60405180910390f35b6100bd60048036038101906100b891906108d5565b61038d565b6040516100ca9190610889565b60405180910390f35b5f7f01ffc9a7000000000000000000000000000000000000000000000000000000007bffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916827bffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916149050919050565b5f600160048111156101515761015061096b565b5b7f000000000000000000000000000000000000000000000000000000000000000060048111156101845761018361096b565b5b0361018d575f80fd5b600260048111156101a1576101a061096b565b5b7f000000000000000000000000000000000000000000000000000000000000000060048111156101d4576101d361096b565b5b03610214576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161020b90610a18565b60405180910390fd5b600360048111156102285761022761096b565b5b7f0000000000000000000000000000000000000000000000000000000000000000600481111561025b5761025a61096b565b5b036102bd577f00000000000000000000000000000000000000000000000000000000000000006040517f66435bc00000000000000000000000000000000000000000000000000000000081526004016102b49190610889565b60405180910390fd5b6004808111156102d0576102cf61096b565b5b7f000000000000000000000000000000000000000000000000000000000000000060048111156103035761030261096b565b5b03610319575f805f6103159190610a63565b9050505b7f9facaeece8596899cc39b65f0d1e262008ade8403076a2dfb6df2004fc8d96528989898989898989604051610356989796959493929190610b74565b60405180910390a17f0000000000000000000000000000000000000000000000000000000000000000905098975050505050505050565b5f600160048111156103a2576103a161096b565b5b7f000000000000000000000000000000000000000000000000000000000000000060048111156103d5576103d461096b565b5b036103de575f80fd5b600260048111156103f2576103f161096b565b5b7f000000000000000000000000000000000000000000000000000000000000000060048111156104255761042461096b565b5b03610465576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040161045c90610c50565b60405180910390fd5b600360048111156104795761047861096b565b5b7f000000000000000000000000000000000000000000000000000000000000000060048111156104ac576104ab61096b565b5b0361050e577f00000000000000000000000000000000000000000000000000000000000000006040517f66435bc00000000000000000000000000000000000000000000000000000000081526004016105059190610889565b60405180910390fd5b6004808111156105215761052061096b565b5b7f000000000000000000000000000000000000000000000000000000000000000060048111156105545761055361096b565b5b0361056a575f805f6105669190610a63565b9050505b7fe4b060c773f3fcca980bf840b0e2856ca36598bb4da2c0c3913b89050630df378787878787876040516105a396959493929190610c7d565b60405180910390a17f000000000000000000000000000000000000000000000000000000000000000090509695505050505050565b5f80fd5b5f80fd5b5f7fffffffff0000000000000000000000000000000000000000000000000000000082169050919050565b610614816105e0565b811461061e575f80fd5b50565b5f8135905061062f8161060b565b92915050565b5f6020828403121561064a576106496105d8565b5b5f61065784828501610621565b91505092915050565b5f8115159050919050565b61067481610660565b82525050565b5f60208201905061068d5f83018461066b565b92915050565b5f73ffffffffffffffffffffffffffffffffffffffff82169050919050565b5f6106bc82610693565b9050919050565b6106cc816106b2565b81146106d6575f80fd5b50565b5f813590506106e7816106c3565b92915050565b5f80fd5b5f80fd5b5f80fd5b5f8083601f84011261070e5761070d6106ed565b5b8235905067ffffffffffffffff81111561072b5761072a6106f1565b5b602083019150836020820283011115610747576107466106f5565b5b9250929050565b5f8083601f840112610763576107626106ed565b5b8235905067ffffffffffffffff8111156107805761077f6106f1565b5b60208301915083600182028301111561079c5761079b6106f5565b5b9250929050565b5f805f805f805f8060a0898b0312156107bf576107be6105d8565b5b5f6107cc8b828c016106d9565b98505060206107dd8b828c016106d9565b975050604089013567ffffffffffffffff8111156107fe576107fd6105dc565b5b61080a8b828c016106f9565b9650965050606089013567ffffffffffffffff81111561082d5761082c6105dc565b5b6108398b828c016106f9565b9450945050608089013567ffffffffffffffff81111561085c5761085b6105dc565b5b6108688b828c0161074e565b92509250509295985092959890939650565b610883816105e0565b82525050565b5f60208201905061089c5f83018461087a565b92915050565b5f819050919050565b6108b4816108a2565b81146108be575f80fd5b50565b5f813590506108cf816108ab565b92915050565b5f805f805f8060a087890312156108ef576108ee6105d8565b5b5f6108fc89828a016106d9565b965050602061090d89828a016106d9565b955050604061091e89828a016108c1565b945050606061092f89828a016108c1565b935050608087013567ffffffffffffffff8111156109505761094f6105dc565b5b61095c89828a0161074e565b92509250509295509295509295565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52602160045260245ffd5b5f82825260208201905092915050565b7f4552433131353552656365697665724d6f636b3a20726576657274696e67206f5f8201527f6e20626174636820726563656976650000000000000000000000000000000000602082015250565b5f610a02602f83610998565b9150610a0d826109a8565b604082019050919050565b5f6020820190508181035f830152610a2f816109f6565b9050919050565b7f4e487b71000000000000000000000000000000000000000000000000000000005f52601260045260245ffd5b5f610a6d826108a2565b9150610a78836108a2565b925082610a8857610a87610a36565b5b828204905092915050565b610a9c816106b2565b82525050565b5f82825260208201905092915050565b5f80fd5b82818337505050565b5f610aca8385610aa2565b93507f07ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff831115610afd57610afc610ab2565b5b602083029250610b0e838584610ab6565b82840190509392505050565b5f82825260208201905092915050565b828183375f83830152505050565b5f601f19601f8301169050919050565b5f610b538385610b1a565b9350610b60838584610b2a565b610b6983610b38565b840190509392505050565b5f60a082019050610b875f83018b610a93565b610b94602083018a610a93565b8181036040830152610ba781888a610abf565b90508181036060830152610bbc818688610abf565b90508181036080830152610bd1818486610b48565b90509998505050505050505050565b7f4552433131353552656365697665724d6f636b3a20726576657274696e67206f5f8201527f6e20726563656976650000000000000000000000000000000000000000000000602082015250565b5f610c3a602983610998565b9150610c4582610be0565b604082019050919050565b5f6020820190508181035f830152610c6781610c2e565b9050919050565b610c77816108a2565b82525050565b5f60a082019050610c905f830189610a93565b610c9d6020830188610a93565b610caa6040830187610c6e565b610cb76060830186610c6e565b8181036080830152610cca818486610b48565b905097965050505050505056fea26469706673582212208c442b680a6062015caa02f3c4c74cff54e26169331c5af35a3fa1703a3cc02364736f6c63430008180033")] - contract ERC1155ReceiverMock is ERC165, IERC1155Receiver { - enum RevertType { - None, - RevertWithoutMessage, - RevertWithMessage, - RevertWithCustomError, - Panic - } - - bytes4 private immutable _recRetval; - bytes4 private immutable _batRetval; - RevertType private immutable _error; - - #[derive(Debug, PartialEq)] - event Received(address operator, address from, uint256 id, uint256 value, bytes data); - - #[derive(Debug, PartialEq)] - event BatchReceived(address operator, address from, uint256[] ids, uint256[] values, bytes data); - - error CustomError(bytes4); - - constructor(bytes4 recRetval, bytes4 batRetval, RevertType error) { - _recRetval = recRetval; - _batRetval = batRetval; - _error = error; - } - - function onERC1155Received( - address operator, - address from, - uint256 id, - uint256 value, - bytes calldata data - ) external returns (bytes4) { - if (_error == RevertType.RevertWithoutMessage) { - revert(); - } else if (_error == RevertType.RevertWithMessage) { - revert("ERC1155ReceiverMock: reverting on receive"); - } else if (_error == RevertType.RevertWithCustomError) { - revert CustomError(_recRetval); - } else if (_error == RevertType.Panic) { - uint256 a = uint256(0) / uint256(0); - a; - } - - emit Received(operator, from, id, value, data); - return _recRetval; - } - - function onERC1155BatchReceived( - address operator, - address from, - uint256[] calldata ids, - uint256[] calldata values, - bytes calldata data - ) external returns (bytes4) { - if (_error == RevertType.RevertWithoutMessage) { - revert(); - } else if (_error == RevertType.RevertWithMessage) { - revert("ERC1155ReceiverMock: reverting on batch receive"); - } else if (_error == RevertType.RevertWithCustomError) { - revert CustomError(_recRetval); - } else if (_error == RevertType.Panic) { - uint256 a = uint256(0) / uint256(0); - a; - } - - emit BatchReceived(operator, from, ids, values, data); - return _batRetval; - } - } -} - -pub async fn deploy( - wallet: &Wallet, - error: ERC1155ReceiverMock::RevertType, -) -> eyre::Result
{ - let contract = - ERC1155ReceiverMock::deploy(wallet, REC_RETVAL, BAT_RETVAL, error) - .await?; - Ok(*contract.address()) -}