github.com/ethereum-optimism/optimism@v1.7.2/packages/core-utils/src/helpers/setupProxyContract.ts (about)

     1  import assert from 'assert'
     2  
     3  import { ethers, utils } from 'ethers'
     4  
     5  const { getAddress } = utils
     6  
     7  type ProxyConfig = {
     8    targetImplAddress: string
     9    targetProxyOwnerAddress: string
    10    postUpgradeCallCalldata?: string
    11  }
    12  
    13  // Sets up the newly deployed proxy contract such that:
    14  // 1. The proxy's implementation is set to the target implementation
    15  // 2. The proxy's admin is set to the target proxy owner
    16  //
    17  // If the values are set correctly already, it makes no transactions.
    18  
    19  const setupProxyContract = async (
    20    proxyContract: ethers.Contract,
    21    signer: ethers.Signer,
    22    {
    23      targetImplAddress,
    24      targetProxyOwnerAddress,
    25      postUpgradeCallCalldata,
    26    }: ProxyConfig
    27  ) => {
    28    const currentAdmin = await proxyContract
    29      .connect(ethers.constants.AddressZero)
    30      .callStatic.admin()
    31  
    32    const signerAddress = await signer.getAddress()
    33  
    34    // Gets the current implementation address the proxy is pointing to.
    35    // callStatic is used since the `Proxy.implementation()` is not a view function and ethers will
    36    // try to make a transaction if we don't use callStatic. Using the zero address as `from` lets us
    37    // call functions on the proxy and not trigger the delegatecall. See Proxy.sol proxyCallIfNotAdmin
    38    // modifier for more details.
    39    const currentImplementation = await proxyContract
    40      .connect(ethers.constants.AddressZero)
    41      .callStatic.implementation()
    42    console.log(`implementation currently set to ${currentImplementation}`)
    43  
    44    if (getAddress(currentImplementation) !== getAddress(targetImplAddress)) {
    45      // If the proxy isn't pointing to the correct implementation, we need to set it to the correct
    46      // one, then call initialize() in the proxy's context.
    47  
    48      console.log('implementation not set to correct contract')
    49      console.log(`Setting implementation to ${targetImplAddress}`)
    50  
    51      // The signer needs to be the current admin, otherwise we don't have permission
    52      // to update the implementation or admin
    53      assert(
    54        signerAddress === currentAdmin,
    55        'the passed signer is not the admin, cannot update implementation'
    56      )
    57  
    58      let tx: ethers.providers.TransactionResponse
    59      if (!postUpgradeCallCalldata) {
    60        console.log(
    61          'postUpgradeCallCalldata is not provided. Using Proxy.upgrade()'
    62        )
    63        // Point the proxy to the target implementation
    64        tx = await proxyContract.connect(signer).upgradeTo(targetImplAddress)
    65      } else {
    66        console.log(
    67          'postUpgradeCallCalldata is provided. Using Proxy.upgradeAndCall()'
    68        )
    69        // Point the proxy to the target implementation,
    70        // and call function in the proxy's context
    71        tx = await proxyContract
    72          .connect(signer)
    73          .upgradeToAndCall(targetImplAddress, postUpgradeCallCalldata)
    74      }
    75  
    76      const receipt = await tx.wait()
    77  
    78      console.log(`implementation set in ${receipt.transactionHash}`)
    79    } else {
    80      console.log(`implementation already set correctly to ${targetImplAddress}`)
    81    }
    82  
    83    console.log(`admin set to ${currentAdmin}`)
    84    if (getAddress(currentAdmin) !== getAddress(targetProxyOwnerAddress)) {
    85      // If the proxy admin isn't the l2ProxyOwnerAddress, we need to update it
    86      // We're assuming that the proxy admin is the ddd right now.
    87  
    88      console.log('detected admin is not set correctly')
    89      console.log(`Setting admin to ${targetProxyOwnerAddress}`)
    90  
    91      // The signer needs to be the current admin, otherwise we don't have permission
    92      // to update the implementation or admin
    93      assert(
    94        signerAddress === currentAdmin,
    95        'proxyOwnerSigner is not the admin, cannot update admin'
    96      )
    97  
    98      // change admin to the l2ProxyOwnerAddress
    99      const tx = await proxyContract
   100        .connect(signer)
   101        .changeAdmin(targetProxyOwnerAddress)
   102  
   103      const receipt = await tx.wait()
   104  
   105      console.log(`admin set in ${receipt.transactionHash}`)
   106    } else {
   107      console.log(`admin already set correctly to ${targetProxyOwnerAddress}`)
   108    }
   109  
   110    const updatedImplementation = await proxyContract
   111      .connect(ethers.constants.AddressZero)
   112      .callStatic.implementation()
   113  
   114    const updatedAdmin = await proxyContract
   115      .connect(ethers.constants.AddressZero)
   116      .callStatic.admin()
   117  
   118    assert(
   119      getAddress(updatedAdmin) === getAddress(targetProxyOwnerAddress),
   120      'Something went wrong - admin not set correctly after transaction'
   121    )
   122    assert(
   123      getAddress(updatedImplementation) === getAddress(targetImplAddress),
   124      'Something went wrong - implementation not set correctly after transaction'
   125    )
   126  
   127    console.log(
   128      `Proxy at ${proxyContract.address} is set up with implementation: ${updatedImplementation} and admin: ${updatedAdmin}`
   129    )
   130  }
   131  
   132  export { setupProxyContract }