Skip to content

Commit

Permalink
Merge pull request #4 from rainlanguage/2024-06-17-xor-selectors
Browse files Browse the repository at this point in the history
add xor selectors trait
  • Loading branch information
hardyjosh authored Jun 19, 2024
2 parents 6855a2d + c72a0ff commit f857ce5
Showing 1 changed file with 35 additions and 30 deletions.
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

0 comments on commit f857ce5

Please sign in to comment.