1

This smart contract (https://etherscan.io/address/0x4fabb145d64652a948d72533023f6e7a623c7c53#code) is a proxy. You can tell this by clicking on 'Read as Proxy'.

However, when one looks at the 'Read Contract' tab and one clicks on the implementation data item, it reports the implementation address as 0x.

I notice in the code that the implementation function is not public, so I suppose that's why it's not returning a value, but then...

Question: How does Etherscan know that the contract is a proxy and what fourbyte code does it use to query the proxied-to address?

0

Implementation is stored as value in IMPLEMENTATION_SLOT (see the source code), so you can get the underlined Contract with:

let proxyContract = "0x4fabb145d64652a948d72533023f6e7a623c7c53";
let storagePosition = "0x7050c9e0f4ca769c69bd3a8ef740bc37934f8e2c036e5a723fd8ee048ed3f8c3";
let result = await web3Client.eth.getStorageAt(proxyContract, storagePosition);

// > 0x0000000000000000000000005864c777697bf9881220328bf2f16908c9afcd7e

implementation method you are calling is allowed only with admin rights. I think, this method is there for testing or a quick check only.

Etherscan also shows 0x5864c777697bf9881220328bf2f16908c9afcd7e as a target implementation contract.

But, what I'm wondering is, why when reading totalSupply in proxy 0x4fabb145d64652a948d72533023f6e7a623c7c53 it returns 9023954154960000000000000000, but directly inside the implementation 0x5864c777697bf9881220328bf2f16908c9afcd7e it returns 0. May be smbd. here will have a clue.

EDIT

Ok, found it - storage belongs to the proxy, means the proxy is like a storage container, and when it calls implementation methods, they are called in the proxy storage context.

0x5864c777697bf9881220328bf2f16908c9afcd7e#code

/**
 * @title BUSDImplementation
 * NOTE: The storage defined here will actually be held in the Proxy
 * contract and all calls to this contract should be made through
 * the proxy, including admin actions done as owner or supplyController.
 * Any call to transfer against this contract should fail
 * with insufficient funds since no tokens will be issued there.
 */

EDIT

Implementation Storage Slot

Usually, the unstructed pattern is used to persist the Implementation Contract Address, as otherwise it could conflict with the implementation. There is also a standard for this https://eips.ethereum.org/EIPS/eip-1967 , but before openzeppelin had its own approach https://blog.openzeppelin.com/proxy-patterns/

In both cases the slots are hardcoded:

  • bytes32(uint256(keccak256('eip1967.proxy.implementation')) - 1)) resolves to 0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc
  • keccak256("org.zeppelinos.proxy.implementation") resolves to 0x7050c9e0f4ca769c69bd3a8ef740bc37934f8e2c036e5a723fd8ee048ed3f8c3

I don't know, if from ABI you can distinguish both this standard implementations, please check the differences, but anyway you can try to read both storage slots.

If the contract uses not standard proxy patterns, then you have to know the implementation.

2
  • I understand your solution thank you. If I’m not mistaken, the solution needs me to have the source code so that I can find the storage location. Is that right? I was looking for a solution that was generic that didn’t require the source code. With the ABI I know there’s an implementation function without the source code i don’t know anything about the storage locations. – Thomas Jay Rush Jun 9 at 14:03
  • @ThomasJayRush see my last edits, do you have any other questions? – tenbits Jun 16 at 22:29

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

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