Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

add xor selectors trait #4

Merged
merged 4 commits into from
Jun 19, 2024
Merged
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
65 changes: 35 additions & 30 deletions src/erc165/mod.rs
Original file line number Diff line number Diff line change
@@ -1,27 +1,41 @@
use thiserror::Error;
use alloy_primitives::Address;
use alloy_sol_types::{sol, SolCall};
use alloy_sol_types::{sol, SolCall, SolInterface};
use alloy_ethers_typecast::transaction::{ReadContractParameters, ReadableClientHttp};

// IERC165 contract alloy bindings
sol!("lib/forge-std/src/interfaces/IERC165.sol");

/// get interface id from the given array of selectors, the array of selectors
/// should include all the functions (and only function) selectors of the
/// interface, in alloy and using its sol! macro bindings, the functions selectors
/// can be accessed through: `{AlloyContractName}::{AlloyContractNameCalls}::SELECTORS``
///
/// related info can be found here:
/// https://eips.ethereum.org/EIPS/eip-165#how-interfaces-are-identified
pub fn get_interface_id(selectors: &[[u8; 4]]) -> [u8; 4] {
if selectors.is_empty() {
panic!("no selectors")
}
let mut result = u32::from_be_bytes(selectors[0]);
for selector in &selectors[1..] {
result ^= u32::from_be_bytes(*selector);
#[derive(Error, Debug)]
pub enum XorSelectorsError {
#[error("no selectors")]
NoSelectors,
}

/// Calculates XOR of the selectors of a type that implements SolInterface
pub trait XorSelectors<T: SolInterface> {
/// get xor of all the selectors.
///
/// in order to get interface id the array of selectors should include all the functions
/// (and only function) selectors of the interface, in alloy and using its sol! macro
/// bindings, the generated Calls enum includes all the fn selectors:
/// `{AlloyContractName}::{AlloyContractNameCalls}`
///
/// related info can be found here:
/// https://eips.ethereum.org/EIPS/eip-165#how-interfaces-are-identified
fn xor_selectors() -> Result<[u8; 4], XorSelectorsError> {
let selectors = T::selectors().collect::<Vec<_>>();
if selectors.is_empty() {
return Err(XorSelectorsError::NoSelectors);
}
let mut result = u32::from_be_bytes(selectors[0]);
for selector in &selectors[1..] {
result ^= u32::from_be_bytes(*selector);
}
Ok(result.to_be_bytes())
}
result.to_be_bytes()
}
impl<T: SolInterface> XorSelectors<T> for T {}

/// the first check for checking if a contract supports erc165
async fn supports_erc165_check1(client: &ReadableClientHttp, contract_address: Address) -> bool {
Expand Down Expand Up @@ -71,35 +85,26 @@ mod tests {
transaction::ReadableClient,
};
use ethers::types::{transaction::eip2718::TypedTransaction, BlockNumber};
use super::XorSelectors;

// test contracts bindings
sol! {
interface ITest {
function externalFn1() external pure returns (bool);
function externalFn2(uint256 val1, uint256 val2) external returns (uint256, bool);
function externalFn3(address add) external returns (address);
error SomeError();
event SomeEvent(uint256 value);
}
}

#[test]
fn test_get_interface_id() {
let selectors = vec![
//[1 2 3 4]
[0b0001, 0b0010, 0b0011, 0b0100],
//[5 6 7 8]
[0b0101, 0b0110, 0b0111, 0b1000],
//[9 10 11 12]
[0b1001, 0b1010, 0b1011, 0b1100],
];
let result = get_interface_id(&selectors);
let expected: [u8; 4] = [0b1101, 0b1110, 0b1111, 0b0000]; // [13 14 15 0]
assert_eq!(result, expected);

let result = get_interface_id(IERC165::IERC165Calls::SELECTORS);
let result = IERC165::IERC165Calls::xor_selectors().unwrap();
let expected: [u8; 4] = 0x01ffc9a7u32.to_be_bytes(); // known IERC165 interface id
assert_eq!(result, expected);

let result = get_interface_id(ITest::ITestCalls::SELECTORS);
let result = ITest::ITestCalls::xor_selectors().unwrap();
let expected: [u8; 4] = 0x3dcd3fedu32.to_be_bytes(); // known ITest interface id
assert_eq!(result, expected);
}
Expand Down
Loading