diff --git a/.changeset/warm-clocks-sell.md b/.changeset/warm-clocks-sell.md new file mode 100644 index 00000000000..b6fd0980fa9 --- /dev/null +++ b/.changeset/warm-clocks-sell.md @@ -0,0 +1,5 @@ +--- +"@thirdweb-dev/sdk": patch +--- + +Resolve beacon from beacon-proxy diff --git a/packages/sdk/src/evm/common/feature-detection/resolveContractUriFromAddress.ts b/packages/sdk/src/evm/common/feature-detection/resolveContractUriFromAddress.ts index d86bfb66e52..c63b8c1999e 100644 --- a/packages/sdk/src/evm/common/feature-detection/resolveContractUriFromAddress.ts +++ b/packages/sdk/src/evm/common/feature-detection/resolveContractUriFromAddress.ts @@ -36,7 +36,10 @@ export async function resolveImplementation( address: string, provider: providers.Provider, ): Promise<{ address: string; bytecode: string }> { - const bytecode = await fetchBytecode(address, provider); + const [bytecode, beacon] = await Promise.all([ + fetchBytecode(address, provider), + getBeaconFromStorageSlot(address, provider), + ]); // check minimal proxy first synchronously const minimalProxyImplementationAddress = @@ -52,6 +55,11 @@ export async function resolveImplementation( } // check other proxy types + if (beacon && beacon !== constants.AddressZero) { + // In case of a BeaconProxy, it is setup as BeaconProxy --> Beacon --> Implementation + // Hence we replace the proxy address with Beacon address, and continue further resolving below + address = beacon; + } const impl = await Promise.all([ getImplementationFromStorageSlot(address, provider), getImplementationFromContractCall(address, provider), @@ -108,6 +116,30 @@ async function getImplementationFromStorageSlot( } } +async function getBeaconFromStorageSlot( + address: string, + provider: providers.Provider, +): Promise { + /** + * The storage slot of the Beacon as defined in EIP-1967 + * See https://eips.ethereum.org/EIPS/eip-1967#beacon-contract-address + * + * bytes32(uint256(keccak256('eip1967.proxy.beacon')) - 1)) + */ + + try { + const proxyStorage = await provider.getStorageAt( + address, + BigNumber.from( + "0xa3f0ad74e5423aebfd80d3ef4346578335a9a72aeaee59ff6cb3582b35133d50", + ), + ); + return `0x${proxyStorage.slice(-40)}`; + } catch (e) { + return undefined; + } +} + async function getImplementationFromContractCall( address: string, provider: providers.Provider,