1

Let's say I wanted to write code which looks at a smart contract, detects whether it is an upgradeable proxy, and then fetches the contract address.

Something like so:

const functionNames = abi.map((func) => func.name)
const isEip1967 = ((functionNames.includes('implementation')) && (functionNames.includes('upgradeTo')))

if (isEip1967) {
    const implementation = contract.getImplementation()
}

Now, this fails, as under Eip1967, only the contract admin has the rights to call the implementation function. Looking at an example eip 1967 proxy (https://etherscan.io/address/0x777777c9898d384f785ee44acfe945efdff5f3e0#writeContract),

the notes on the implementation function say:

Returns the current implementation. NOTE: 
Only the admin can call this function. See {ProxyAdmin-getProxyImplementation}. 
TIP: To get this value clients can read directly from the storage slot shown below (specified by EIP1967) 
using the https://eth.wiki/json-rpc/API#eth_getstorageat[`eth_getStorageAt`] RPC call. 
`0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc`

Fine, so far so good. Is there a way to get this storage address for an arbitrary eip1967 smart contract, so that I can write an automatic script to fetch implementation contracts?

1 Answer 1

0

You should be able to fetch the implementation contracts off most EIP-1967 proxies at the storage slot 0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc since this is the standard. See EIP-1967 https://eips.ethereum.org/EIPS/eip-1967.

So basically it's eth_getStorageAt and then 0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc, and it'll return the implementation address.

Here's an example with web3js v4+ for Uniswap V3 proxy https://etherscan.io/address/0xEe6A57eC80ea46401049E92587E52f5Ec1c24785

const { Web3 } = require('web3');

// Put a node endpoint to do the call
const web3 = new Web3(new Web3.providers.HttpProvider('NODE_ENDPOINT'));

// Fixed storage slot for the implementation address as per EIP-1967
const IMPLEMENTATION_SLOT = '0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc';

async function getImplementationAddress(proxyAddress) {
    // Do eth_getStorageAt and then the default storage slot
    const implAddress = await web3.eth.getStorageAt(proxyAddress, IMPLEMENTATION_SLOT);

    return implAddress;
}

const proxyAddress = '0xEe6A57eC80ea46401049E92587E52f5Ec1c24785';
getImplementationAddress(proxyAddress).then(console.log);

This is just a read call. You don't have to be an admin (or do a transaction) to do the call.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.