From 8eecd6129c3cb703090d4a8ad43c39e41d4ee71b Mon Sep 17 00:00:00 2001 From: Paul <108695806+pxrl@users.noreply.github.com> Date: Wed, 4 Jun 2025 09:39:09 +0000 Subject: [PATCH 1/2] chore: Make contract check EIP-7702 aware isContractDeployedToAddress() currently mistakes EIP-7702 delegations for contracts. For most/all purposes in Across, 7702 delegation is preferred to be treated as an EOA. This change permits the caller to specify whether EIP-7702 delegations should be considered as contract code. EIP-7702 delegation is considered as a contract by default, so this is backwards compatible with the existing implementation and must requires opt-in. --- src/utils/AddressUtils.ts | 35 ++++++++++++++++++++++++++++------- 1 file changed, 28 insertions(+), 7 deletions(-) diff --git a/src/utils/AddressUtils.ts b/src/utils/AddressUtils.ts index 62b37ea4c..e030eb915 100644 --- a/src/utils/AddressUtils.ts +++ b/src/utils/AddressUtils.ts @@ -1,23 +1,44 @@ +import { isAddress } from "viem"; import { providers, utils } from "ethers"; import bs58 from "bs58"; import { Address as V2Address } from "@solana/kit"; import { BigNumber, chainIsEvm } from "./"; +/** + * Verify whether an address' bytecode resembles an EIP-7702 delegation. + * @param code Bytecode for a given address. + * @returns True if the bytecode resembles an EIP-7702 delegation, otherwise false. + */ +export function is7702Delegate(code: string): boolean { + // Sample 7702 delegation bytecode: 0xef010063c0c19a282a1b52b07dd5a65b58948a07dae32b + return code.length === 48 && code.startsWith("0xef0100") && isAddress(`0x${code.slice(8)}`); +} + + /** * Checks if a contract is deployed at the given address * @param address The ETH address to check * @param provider A valid Ethers.js provider + * @param ignore7702 A boolean to indicate whether EIP-7702 delegations should be considered as contract code. * @returns A boolean indicating if a contract is deployed at the given address or not (true = contract, false = no contract) */ -export async function isContractDeployedToAddress(address: string, provider: providers.Provider): Promise { +export async function isContractDeployedToAddress(address: string, provider: providers.Provider, ignore7702 = false): Promise { // A base case for if the address is null or malformed - if (!address || !utils.isAddress(address)) { + if (!address || !isAddress(address)) { return false; } - // Retrieve the code at the address + const code = await provider.getCode(address); - // If the code is not empty, then there is a contract at this address - return code !== "0x"; + if (code === "0x") { + return false; + } + + // Ignore EIP-7702 delegations if ignore7702 was set. + if (ignore7702) { + return !is7702Delegate(code); + } + + return true; } export function compareAddresses(addressA: string, addressB: string): 1 | -1 | 0 { @@ -56,7 +77,7 @@ export function toAddress(hexString: string): string { } export function isValidEvmAddress(address: string): boolean { - if (utils.isAddress(address)) { + if (isAddress(address)) { return true; } // We may throw an error here if hexZeroPadFails. This will happen if the address to pad is greater than 20 bytes long, indicating @@ -65,7 +86,7 @@ export function isValidEvmAddress(address: string): boolean { // For both cases, this indicates that the address cannot be casted as a bytes20 EVM address, so we should return false. try { const evmAddress = utils.hexZeroPad(utils.hexStripZeros(address), 20); - return utils.isAddress(utils.getAddress(evmAddress)); + return isAddress(utils.getAddress(evmAddress)); } catch (_e) { return false; } From 7267ba48c5447ab1affd9564dde59e78581e464e Mon Sep 17 00:00:00 2001 From: Paul <108695806+pxrl@users.noreply.github.com> Date: Wed, 4 Jun 2025 10:07:10 +0000 Subject: [PATCH 2/2] prettier --- src/utils/AddressUtils.ts | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/utils/AddressUtils.ts b/src/utils/AddressUtils.ts index e030eb915..a3c95d00c 100644 --- a/src/utils/AddressUtils.ts +++ b/src/utils/AddressUtils.ts @@ -14,7 +14,6 @@ export function is7702Delegate(code: string): boolean { return code.length === 48 && code.startsWith("0xef0100") && isAddress(`0x${code.slice(8)}`); } - /** * Checks if a contract is deployed at the given address * @param address The ETH address to check @@ -22,7 +21,11 @@ export function is7702Delegate(code: string): boolean { * @param ignore7702 A boolean to indicate whether EIP-7702 delegations should be considered as contract code. * @returns A boolean indicating if a contract is deployed at the given address or not (true = contract, false = no contract) */ -export async function isContractDeployedToAddress(address: string, provider: providers.Provider, ignore7702 = false): Promise { +export async function isContractDeployedToAddress( + address: string, + provider: providers.Provider, + ignore7702 = false +): Promise { // A base case for if the address is null or malformed if (!address || !isAddress(address)) { return false;