How to deploy upgradeable smart contract in the frontend - globe-and-citizen/cnc-portal GitHub Wiki

How to deploy upgradeable smart contracts in the frontend

Prerequisite

  • Implementation contract address
    • In cnc-portal we can get it from contract/ignition/deployments/chain-11155111/deployed_addresses.json and change the chain-11155111 to your desired chainId. after that, we can get the value of the implementation contract address in BankProxyModule#Bank
  • Upgradeable Proxy abi
    • In cnc-portal we can get it from contract/ignition/deployments/chain-11155111/artifacts/BankProxyModule#TransparentUpgradeableProxy.json and change the chain-11155111 to your desired chainId. after that, we can get the value in abi
  • Upgradeable Proxy bytecode
    • In cnc-portal we can get it by going into the /contract folder then open up a terminal and type npx hardhat console this will open a hardhat console, and then we write something like:
const proxyFactory = await ethers.getContractFactory("TransparentUpgradeableProxy")
proxyFactory.bytecode

we can copy the bytecode that printed after we write the syntax above

How to deploy in the frontend

  1. Store the value of the implementation contract address, In this case, I store it on .env
export const BANK_IMPL_ADDRESS = import.meta.env.VITE_BANK_IMPL_ADDRESS
  1. Store the value of proxy abi in your frontend directory, In this case, I store it in app/src/artifacts/abi/proxy.json
[
  {
    "inputs": [
      {
        "internalType": "address",
        "name": "_logic",
        "type": "address"
      },
      {
        "internalType": "address",
        "name": "initialOwner",
        "type": "address"
      },
      {
        "internalType": "bytes",
        "name": "_data",
        "type": "bytes"
      }
    ],
    "stateMutability": "payable",
    "type": "constructor"
  }
// ...
// so on
]
  1. Store bytecode to your frontend folder, In this case, I store it in app/src/artifacts/bytecode/proxy.ts
export const PROXY_BYTECODE =
  '0x608060405261000c61000e565b005b7f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316330361007b576000356001600160e01b03191663278f794360e11b14610071576040516334ad5dbb60e21b815260040160405180910390fd5b610079610083565b565b6100796100b2565b6000806100933660048184610312565b8101906100a09190610352565b915091506100ae82826100c2565b5050565b6100796100bd61011d565b610155565b6100cb82610179565b6040516001600160a01b038316907fbc7cd75a20ee27fd9adebab32041f755214dbc6bffa90cc0225b39da2e5c2d3b90600090a28051156101155761011082826101f5565b505050565b6100ae61026b565b60006101507f360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc546001600160a01b031690565b905090565b3660008037600080366000845af43d6000803e808015610174573d6000f35b3d6000fd5b806001600160a01b03163b6000036101b457604051634c9c8ce360e01b81526001600160a01b03821660048201526024015b60405180910390fd5b7f360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc80546001600160a01b0319166001600160a01b0392909216919091179055565b6060600080846001600160a01b0316846040516102129190610422565b600060405180830381855af49150503d806000811461024d576040519150601f19603f3d011682016040523d82523d6000602084013e610252565b606091505b509150915061026285838361028a565b95945050505050565b34156100795760405163b398979f60e01b815260040160405180910390fd5b60608261029f5761029a826102e9565b6102e2565b81511580156102b657506001600160a01b0384163b155b156102df57604051639996b31560e01b81526001600160a01b03851660048201526024016101ab565b50805b9392505050565b8051156102f95780518082602001fd5b604051630a12f52160e11b815260040160405180910390fd5b6000808585111561032257600080fd5b8386111561032f57600080fd5b5050820193919092039150565b634e487b7160e01b600052604160045260246000fd5b6000806040838503121561036557600080fd5b82356001600160a01b038116811461037c57600080fd5b9150602083013567ffffffffffffffff8082111561039957600080fd5b818501915085601f8301126103ad57600080fd5b8135818111156103bf576103bf61033c565b604051601f8201601f19908116603f011681019083821181831017156103e7576103e761033c565b8160405282815288602084870101111561040057600080fd5b8260208601602083013760006020848301015280955050505050509250929050565b6000825160005b818110156104435760208186018101518583015201610429565b50600092019182525091905056fea2646970667358221220947e0a1ded970813b78f19358c6b3a1fbfd998f79756b5caa23ff5a68d62528564736f6c63430008180033'
  1. Create the deployment function
  private async deployBankContract(): Promise<string> {
    // this.web3Library basically an abstraction of ethersJs library
    const proxyFactory = await this.web3Library.getFactoryContract(PROXY_ABI, PROXY_BYTECODE)
    const bankImplementation = await this.web3Library.getFactoryContract(BANK_ABI, BANK_BYTECODE) // Another way to do it by using implementation address and bank ABI
    const proxyDeployment = await proxyFactory.deploy(
      BANK_IMPL_ADDRESS,
      await this.web3Library.getAddress(), // address of the user that connect to our DApp
      bankImplementation.interface.encodeFunctionData('initialize', [TIPS_ADDRESS]) // If we don't have any initialize function then we can just pass it `0x`
    )
    const proxy = await proxyDeployment.waitForDeployment()
    await proxyDeployment.waitForDeployment()

    return await proxy.getAddress()
  }

Proxy contract needs implementation contract address, initialOwner, and the data is a initialize function signature that we can pass if we have initialize function in our implementation contract, As we can see Openzeppelin TransparentUpgradeableProxy constructor needs 3 arguments

/**
 * @dev Initializes an upgradeable proxy managed by an instance of a {ProxyAdmin} with an `initialOwner`,
 * backed by the implementation at `_logic`, and optionally initialized with `_data` as explained in
 * {ERC1967Proxy-constructor}.
 */
 constructor(address _logic, address initialOwner, bytes memory _data) payable ERC1967Proxy(_logic, _data) {
   _admin = address(new ProxyAdmin(initialOwner));
   // Set the storage value and emit an event for ERC-1967 compatibility
   ERC1967Utils.changeAdmin(_proxyAdmin());
 }
⚠️ **GitHub.com Fallback** ⚠️