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.
The Maker token does not implement the optional parameters symbol and name
as proposed by EIP-20
:
As you can see here, Maker uses bytes32 for symbol and name.
, where in the case of EIP-20 they are both strings.
I would suggest you just check for bytes32 and strings when you try to get the name and symbol of a ERC20 token. That should cover most cases.
Best Answer
How the proxy pattern usually works is that the main token address -
0x1bfd67037b42cf73acf2047067bd4f2c47d9bfd6
in your case - holds the contract's storage, and forwards all function calls to theimplementation
contract usingdelegatecall
. This will execute the functions inimplementation
contract on the storage of the main proxy contract.So if you want to read the right storage values, you need to read them from the main proxy contract.
You can use the implementation contract's ABI on the main proxy contract itself.