Skip to content

Commit 8918e3a

Browse files
authored
chore: Make contract check EIP-7702 aware (#1074)
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.
1 parent 14d4941 commit 8918e3a

File tree

1 file changed

+31
-7
lines changed

1 file changed

+31
-7
lines changed

src/utils/AddressUtils.ts

Lines changed: 31 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,22 +1,46 @@
1+
import { isAddress } from "viem";
12
import { providers, utils } from "ethers";
23
import bs58 from "bs58";
34
import { BigNumber, chainIsEvm, chainIsSvm } from "./";
45

6+
/**
7+
* Verify whether an address' bytecode resembles an EIP-7702 delegation.
8+
* @param code Bytecode for a given address.
9+
* @returns True if the bytecode resembles an EIP-7702 delegation, otherwise false.
10+
*/
11+
export function is7702Delegate(code: string): boolean {
12+
// Sample 7702 delegation bytecode: 0xef010063c0c19a282a1b52b07dd5a65b58948a07dae32b
13+
return code.length === 48 && code.startsWith("0xef0100") && isAddress(`0x${code.slice(8)}`);
14+
}
15+
516
/**
617
* Checks if a contract is deployed at the given address
718
* @param address The ETH address to check
819
* @param provider A valid Ethers.js provider
20+
* @param ignore7702 A boolean to indicate whether EIP-7702 delegations should be considered as contract code.
921
* @returns A boolean indicating if a contract is deployed at the given address or not (true = contract, false = no contract)
1022
*/
11-
export async function isContractDeployedToAddress(address: string, provider: providers.Provider): Promise<boolean> {
23+
export async function isContractDeployedToAddress(
24+
address: string,
25+
provider: providers.Provider,
26+
ignore7702 = false
27+
): Promise<boolean> {
1228
// A base case for if the address is null or malformed
13-
if (!address || !utils.isAddress(address)) {
29+
if (!address || !isAddress(address)) {
1430
return false;
1531
}
16-
// Retrieve the code at the address
32+
1733
const code = await provider.getCode(address);
18-
// If the code is not empty, then there is a contract at this address
19-
return code !== "0x";
34+
if (code === "0x") {
35+
return false;
36+
}
37+
38+
// Ignore EIP-7702 delegations if ignore7702 was set.
39+
if (ignore7702) {
40+
return !is7702Delegate(code);
41+
}
42+
43+
return true;
2044
}
2145

2246
export function compareAddresses(addressA: string, addressB: string): 1 | -1 | 0 {
@@ -56,7 +80,7 @@ export function toEvmAddress(hexString: string): string {
5680
}
5781

5882
export function isValidEvmAddress(address: string): boolean {
59-
if (utils.isAddress(address)) {
83+
if (isAddress(address)) {
6084
return true;
6185
}
6286
// 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 +89,7 @@ export function isValidEvmAddress(address: string): boolean {
6589
// For both cases, this indicates that the address cannot be casted as a bytes20 EVM address, so we should return false.
6690
try {
6791
const evmAddress = utils.hexZeroPad(utils.hexStripZeros(address), 20);
68-
return utils.isAddress(utils.getAddress(evmAddress));
92+
return isAddress(utils.getAddress(evmAddress));
6993
} catch (_e) {
7094
return false;
7195
}

0 commit comments

Comments
 (0)