Proxy patterns, such as Transparent and UUPS (Universal Upgradeable Proxy Standard), are critical in the upgradeable design of smart contracts. However, discerning the implementation contract address from a proxy through block explorers can be challenging. This article aims to instruct developers on extracting this information using JavaScript and web3.js, enhancing the integration of Web 2.0 with smart contracts.
Proxy contracts serve as an intermediary, delegating operations to implementation contracts, which contain the executable logic. This delegation facilitates the upgradeability of contracts without altering the deployed address. Think about a proxy, a controller, where you can change the logic in an API service but don’t need to change the endpoint or how to call the method. The implementation can be related to a service class. It holds the logic and can be updated if needed.
Patterns: Transparent proxies separate the proxy administration and logic concerns, allowing different addresses for admin and logic operations. In contrast, UUPS proxies embed the upgrade logic within the implementation contract itself, offering a more gas-efficient and elegant upgrade mechanism. Also, Transparent proxies have a separate Admin contract responsible for Access Control, Upgrades, and Ownership control.
Block Explorer showing the current implementation contract address
Limitations of Block Explorers: Block explorers can show the current implementation address, but in some cases, these addresses can be different from the registered on-chain. If you deploy a new implementation contract, it will take a few minutes for the Block Explorer to update this information. If any error occurs, this information will not be trustworthy.
Extracting the Address from the blockchain: To circumvent this limitation, developers can employ web3.js to interact with the Ethereum blockchain and programmatically determine the implementation contract’s address. The UUSP and Transparent Proxy patterns should follow the EIP-1967, which sets a specific storage slot to hold the implementation contract address. Based on this information, you can use the code below to extract the current address.
import Web3 from 'web3';
const web3 = new Web3("https://polygon-testnet.public.blastapi.io/");
const implementationStorage = await web3.eth.getStorageAt(proxyAddress, "0x360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc");
if (implementationStorage === "0x0000000000000000000000000000000000000000000000000000000000000000") return null;
const implementationAddressHex = "0x" + implementationStorage.slice(-40); // Extract the last 40 characters (20 bytes) from the storage content
const currentImplementationAddress = web3.utils.toChecksumAddress(implementationAddressHex); // Validate the address
console.log("Found current Implementation contract address", currentImplementationAddress);
For the Transparent pattern, you can also get the Proxy Admin contract using the code below:
import Web3 from 'web3';
const web3 = new Web3("https://polygon-testnet.public.blastapi.io/");
const implementationProxyAdminStorageContentTransparent = await web3.eth.getStorageAt(proxyAddress, "0xb53127684a568b3173ae13b9f8a6016e243e63b6e8ee1178d6a717850b5d6103");
if (implementationProxyAdminStorageContentTransparent !== "0x0000000000000000000000000000000000000000000000000000000000000000") {
const implementationAddressProxyAdminHexTransparent = "0x" + implementationProxyAdminStorageContentTransparent.slice(-40);
_proxyInfo.currentProxyAdminAddress = web3.utils.toChecksumAddress(implementationAddressProxyAdminHexTransparent); // Validate the address
console.log("Found Proxy Admin contract address in Transparent pattern", _proxyInfo.currentImplementationAddress);
}
Remember to initialize Web3 with your own RPC before using it.
If you are a Web 2.0 developer, this code will provide a more assertive way to know the current implementation contract and how to interact with it if needed.
To help you read, change, test, and deploy contracts. Check out my project: DevWeb3.co
Top comments (2)
Hey, great article. Can we achieve this using ethers ?
Sure, I just used web3js because I already have the code here.
The most important part is the correct storage address.