import assert from 'assert' import { ethers, utils } from 'ethers' const { getAddress } = utils type ProxyConfig = { targetImplAddress: string targetProxyOwnerAddress: string postUpgradeCallCalldata?: string } // Sets up the newly deployed proxy contract such that: // 1. The proxy's implementation is set to the target implementation // 2. The proxy's admin is set to the target proxy owner // // If the values are set correctly already, it makes no transactions. const setupProxyContract = async ( proxyContract: ethers.Contract, signer: ethers.Signer, { targetImplAddress, targetProxyOwnerAddress, postUpgradeCallCalldata, }: ProxyConfig ) => { const currentAdmin = await proxyContract .connect(ethers.constants.AddressZero) .callStatic.admin() const signerAddress = await signer.getAddress() // Gets the current implementation address the proxy is pointing to. // callStatic is used since the `Proxy.implementation()` is not a view function and ethers will // try to make a transaction if we don't use callStatic. Using the zero address as `from` lets us // call functions on the proxy and not trigger the delegatecall. See Proxy.sol proxyCallIfNotAdmin // modifier for more details. const currentImplementation = await proxyContract .connect(ethers.constants.AddressZero) .callStatic.implementation() console.log(`implementation currently set to ${currentImplementation}`) if (getAddress(currentImplementation) !== getAddress(targetImplAddress)) { // If the proxy isn't pointing to the correct implementation, we need to set it to the correct // one, then call initialize() in the proxy's context. console.log('implementation not set to correct contract') console.log(`Setting implementation to ${targetImplAddress}`) // The signer needs to be the current admin, otherwise we don't have permission // to update the implementation or admin assert( signerAddress === currentAdmin, 'the passed signer is not the admin, cannot update implementation' ) let tx: ethers.providers.TransactionResponse if (!postUpgradeCallCalldata) { console.log( 'postUpgradeCallCalldata is not provided. Using Proxy.upgrade()' ) // Point the proxy to the target implementation tx = await proxyContract.connect(signer).upgradeTo(targetImplAddress) } else { console.log( 'postUpgradeCallCalldata is provided. Using Proxy.upgradeAndCall()' ) // Point the proxy to the target implementation, // and call function in the proxy's context tx = await proxyContract .connect(signer) .upgradeToAndCall(targetImplAddress, postUpgradeCallCalldata) } const receipt = await tx.wait() console.log(`implementation set in ${receipt.transactionHash}`) } else { console.log(`implementation already set correctly to ${targetImplAddress}`) } console.log(`admin set to ${currentAdmin}`) if (getAddress(currentAdmin) !== getAddress(targetProxyOwnerAddress)) { // If the proxy admin isn't the l2ProxyOwnerAddress, we need to update it // We're assuming that the proxy admin is the ddd right now. console.log('detected admin is not set correctly') console.log(`Setting admin to ${targetProxyOwnerAddress}`) // The signer needs to be the current admin, otherwise we don't have permission // to update the implementation or admin assert( signerAddress === currentAdmin, 'proxyOwnerSigner is not the admin, cannot update admin' ) // change admin to the l2ProxyOwnerAddress const tx = await proxyContract .connect(signer) .changeAdmin(targetProxyOwnerAddress) const receipt = await tx.wait() console.log(`admin set in ${receipt.transactionHash}`) } else { console.log(`admin already set correctly to ${targetProxyOwnerAddress}`) } const updatedImplementation = await proxyContract .connect(ethers.constants.AddressZero) .callStatic.implementation() const updatedAdmin = await proxyContract .connect(ethers.constants.AddressZero) .callStatic.admin() assert( getAddress(updatedAdmin) === getAddress(targetProxyOwnerAddress), 'Something went wrong - admin not set correctly after transaction' ) assert( getAddress(updatedImplementation) === getAddress(targetImplAddress), 'Something went wrong - implementation not set correctly after transaction' ) console.log( `Proxy at ${proxyContract.address} is set up with implementation: ${updatedImplementation} and admin: ${updatedAdmin}` ) } export { setupProxyContract }