How to deploy upgradeable smart contract in the frontend - globe-and-citizen/cnc-portal GitHub Wiki
- Implementation contract address
- In cnc-portal we can get it from
contract/ignition/deployments/chain-11155111/deployed_addresses.json
and change thechain-11155111
to your desired chainId. after that, we can get the value of the implementation contract address inBankProxyModule#Bank
- In cnc-portal we can get it from
- Upgradeable Proxy abi
- In cnc-portal we can get it from
contract/ignition/deployments/chain-11155111/artifacts/BankProxyModule#TransparentUpgradeableProxy.json
and change thechain-11155111
to your desired chainId. after that, we can get the value inabi
- In cnc-portal we can get it from
- Upgradeable Proxy bytecode
- In cnc-portal we can get it by going into the
/contract
folder then open up a terminal and typenpx hardhat console
this will open a hardhat console, and then we write something like:
- In cnc-portal we can get it by going into the
const proxyFactory = await ethers.getContractFactory("TransparentUpgradeableProxy")
proxyFactory.bytecode
we can copy the bytecode that printed after we write the syntax above
- 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
- 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
]
- Store bytecode to your frontend folder, In this case, I store it in
app/src/artifacts/bytecode/proxy.ts
export const PROXY_BYTECODE =
'0x608060405261000c61000e565b005b7f00000000000000000000000000000000000000000000000000000000000000006001600160a01b0316330361007b576000356001600160e01b03191663278f794360e11b14610071576040516334ad5dbb60e21b815260040160405180910390fd5b610079610083565b565b6100796100b2565b6000806100933660048184610312565b8101906100a09190610352565b915091506100ae82826100c2565b5050565b6100796100bd61011d565b610155565b6100cb82610179565b6040516001600160a01b038316907fbc7cd75a20ee27fd9adebab32041f755214dbc6bffa90cc0225b39da2e5c2d3b90600090a28051156101155761011082826101f5565b505050565b6100ae61026b565b60006101507f360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc546001600160a01b031690565b905090565b3660008037600080366000845af43d6000803e808015610174573d6000f35b3d6000fd5b806001600160a01b03163b6000036101b457604051634c9c8ce360e01b81526001600160a01b03821660048201526024015b60405180910390fd5b7f360894a13ba1a3210667c828492db98dca3e2076cc3735a920a3ca505d382bbc80546001600160a01b0319166001600160a01b0392909216919091179055565b6060600080846001600160a01b0316846040516102129190610422565b600060405180830381855af49150503d806000811461024d576040519150601f19603f3d011682016040523d82523d6000602084013e610252565b606091505b509150915061026285838361028a565b95945050505050565b34156100795760405163b398979f60e01b815260040160405180910390fd5b60608261029f5761029a826102e9565b6102e2565b81511580156102b657506001600160a01b0384163b155b156102df57604051639996b31560e01b81526001600160a01b03851660048201526024016101ab565b50805b9392505050565b8051156102f95780518082602001fd5b604051630a12f52160e11b815260040160405180910390fd5b6000808585111561032257600080fd5b8386111561032f57600080fd5b5050820193919092039150565b634e487b7160e01b600052604160045260246000fd5b6000806040838503121561036557600080fd5b82356001600160a01b038116811461037c57600080fd5b9150602083013567ffffffffffffffff8082111561039957600080fd5b818501915085601f8301126103ad57600080fd5b8135818111156103bf576103bf61033c565b604051601f8201601f19908116603f011681019083821181831017156103e7576103e761033c565b8160405282815288602084870101111561040057600080fd5b8260208601602083013760006020848301015280955050505050509250929050565b6000825160005b818110156104435760208186018101518583015201610429565b50600092019182525091905056fea2646970667358221220947e0a1ded970813b78f19358c6b3a1fbfd998f79756b5caa23ff5a68d62528564736f6c63430008180033'
- 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()); }