diff --git a/processor/ethereum/erc20/contracts/IERC20.sol b/processor/ethereum/erc20/contracts/IERC20.sol index c2de5ca0..6298592a 100644 --- a/processor/ethereum/erc20/contracts/IERC20.sol +++ b/processor/ethereum/erc20/contracts/IERC20.sol @@ -18,3 +18,17 @@ interface IERC20 { function approve(address spender, uint256 value) external returns (bool); function allowance(address owner, address spender) external view returns (uint256); } + +interface SeraiIERC20 { + function transferWithInInstruction01BB244A8A( + address to, + uint256 value, + bytes calldata inInstruction + ) external returns (bool); + function transferFromWithInInstruction00081948E0( + address from, + address to, + uint256 value, + bytes calldata inInstruction + ) external returns (bool); +} diff --git a/processor/ethereum/erc20/src/lib.rs b/processor/ethereum/erc20/src/lib.rs index df0a3922..953bab88 100644 --- a/processor/ethereum/erc20/src/lib.rs +++ b/processor/ethereum/erc20/src/lib.rs @@ -28,8 +28,15 @@ mod abi { alloy_sol_macro::sol!("contracts/IERC20.sol"); } use abi::IERC20::{IERC20Calls, transferCall, transferFromCall}; +use abi::SeraiIERC20::{ + SeraiIERC20Calls, transferWithInInstruction01BB244A8ACall as transferWithInInstructionCall, + transferFromWithInInstruction00081948E0Call as transferFromWithInInstructionCall, +}; pub use abi::IERC20::Transfer; +#[cfg(test)] +mod tests; + /// A top-level ERC20 transfer /// /// This does not include `token`, `to` fields. Those are assumed contextual to the creation of @@ -139,8 +146,18 @@ impl Erc20 { } // Read the data appended after - let encoded = call.abi_encode(); - let data = transaction.inner.input().as_ref()[encoded.len() ..].to_vec(); + let data = if let Ok(call) = SeraiIERC20Calls::abi_decode(transaction.inner.input(), true) { + match call { + SeraiIERC20Calls::transferWithInInstruction01BB244A8A( + transferWithInInstructionCall { inInstruction, .. }, + ) | + SeraiIERC20Calls::transferFromWithInInstruction00081948E0( + transferFromWithInInstructionCall { inInstruction, .. }, + ) => Vec::from(inInstruction), + } + } else { + vec![] + }; return Ok(Some(TopLevelTransfer { id: LogIndex { block_hash: *block_hash, index_within_block: log_index }, diff --git a/processor/ethereum/erc20/src/tests.rs b/processor/ethereum/erc20/src/tests.rs new file mode 100644 index 00000000..2218e19b --- /dev/null +++ b/processor/ethereum/erc20/src/tests.rs @@ -0,0 +1,13 @@ +use alloy_sol_types::SolCall; + +#[test] +fn selector_collisions() { + assert_eq!( + crate::abi::IERC20::transferCall::SELECTOR, + crate::abi::SeraiIERC20::transferWithInInstruction01BB244A8ACall::SELECTOR + ); + assert_eq!( + crate::abi::IERC20::transferFromCall::SELECTOR, + crate::abi::SeraiIERC20::transferFromWithInInstruction00081948E0Call::SELECTOR + ); +} diff --git a/processor/ethereum/router/contracts/Router.sol b/processor/ethereum/router/contracts/Router.sol index ade72a8c..e0bc77bb 100644 --- a/processor/ethereum/router/contracts/Router.sol +++ b/processor/ethereum/router/contracts/Router.sol @@ -29,8 +29,7 @@ contract Router is IRouterWithoutCollisions { bytes32 constant ACCOUNT_WITHOUT_CODE_CODEHASH = keccak256(""); /// @dev The address in transient storage used for the reentrancy guard - bytes32 constant REENTRANCY_GUARD_SLOT = - bytes32(uint256(keccak256("ReentrancyGuard Router")) - 1); + bytes32 constant REENTRANCY_GUARD_SLOT = bytes32(uint256(keccak256("ReentrancyGuard Router")) - 1); /** * @dev The next nonce used to determine the address of contracts deployed with CREATE. This is