-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat: Make Ojo Contract EIP-1967 proxy upgradable (#17)
* feat: Make Ojo Contract EIP-1967 proxy upgradable * comment * implement upgradable util methods * fix tests * lint * update deployMockOjo to use CREATE2 * Update contracts/Ojo.sol Co-authored-by: Adam Wozniak <29418299+adamewozniak@users.noreply.github.com> * Update contracts/OjoProxy.sol Co-authored-by: Adam Wozniak <29418299+adamewozniak@users.noreply.github.com> * key in deploy fix --------- Co-authored-by: Adam Wozniak <29418299+adamewozniak@users.noreply.github.com>
- Loading branch information
1 parent
430663e
commit accd1eb
Showing
11 changed files
with
383 additions
and
229 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,15 @@ | ||
// SPDX-License-Identifier: MIT | ||
pragma solidity ^0.8.20; | ||
|
||
import '@axelar-network/axelar-gmp-sdk-solidity/contracts/upgradable/InitProxy.sol'; | ||
|
||
contract OjoProxy is InitProxy { | ||
function contractId() | ||
internal | ||
pure | ||
override | ||
returns (bytes32) | ||
{ | ||
return keccak256('ojo-v1'); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,49 +1,46 @@ | ||
import { Wallet, AbiCoder, ethers } from "ethers"; | ||
import { Wallet, ethers } from "ethers"; | ||
import Ojo from '../artifacts/contracts/Ojo.sol/Ojo.json'; | ||
import Create3Deployer from '@axelar-network/axelar-gmp-sdk-solidity/artifacts/contracts/deploy/Create3Deployer.sol/Create3Deployer.json'; | ||
import OjoProxy from '../artifacts/contracts/OjoProxy.sol/OjoProxy.json'; | ||
import testnet_chains from '../testnet_chains.json'; | ||
import { deployCreate2InitUpgradable } from './utils/upgradable'; | ||
|
||
async function main() { | ||
const axelarGasReceiverAddress = "0xbE406F0189A0B4cf3A05C286473D23791Dd44Cc6"; | ||
const create3DeployerAddress = "0x6513Aedb4D1593BA12e50644401D976aebDc90d8"; | ||
const create2DeployerAddress = "0x98b2920d53612483f91f12ed7754e51b4a77919e"; | ||
const ojoChain = "ojo"; | ||
const ojoAddress = "ojo1es9mhmnunh208ucwq8rlrl97hqulxrz8k37dcu"; | ||
const resolveWindow = 7200 | ||
|
||
const privateKey = process.env.PRIVATE_KEY; | ||
|
||
if (!privateKey) { | ||
throw new Error('Invalid private key. Make sure the PRIVATE_KEY environment variable is set.'); | ||
throw new Error('Invalid private key. Make sure the PRIVATE_KEY environment variable is set.'); | ||
} | ||
|
||
const evmChains = testnet_chains.map((chain) => ({ ...chain })); | ||
const key = Date.now(); | ||
|
||
for (const chain of evmChains) { | ||
const provider = new ethers.JsonRpcProvider(chain.rpc) | ||
const wallet = new Wallet(privateKey, provider); | ||
const balance = await provider.getBalance(wallet.address) | ||
console.log(`${chain.name} wallet balance: ${ethers.formatEther(balance.toString())} ${chain.tokenSymbol}`); | ||
|
||
const deployerContract = new ethers.Contract(create3DeployerAddress, Create3Deployer.abi, wallet); | ||
|
||
const salt = ethers.zeroPadValue(ethers.toUtf8Bytes("Ojo"), 32); | ||
|
||
const coder = AbiCoder.defaultAbiCoder() | ||
const creationCode = ethers.solidityPacked( | ||
["bytes", "bytes"], | ||
[Ojo.bytecode, coder.encode(["address", "address", "string", "string", "address", "uint256"],[chain.gateway, axelarGasReceiverAddress, ojoChain, ojoAddress, wallet.address, resolveWindow])] | ||
); | ||
|
||
// perform static call to log address of the contract | ||
const deployedAddress = await deployerContract.deploy.staticCallResult(creationCode, salt); | ||
console.log(`${chain.name}, address: ${deployedAddress}`); | ||
|
||
// perform actual deploy tx | ||
await deployerContract.deploy(creationCode, salt); | ||
const provider = new ethers.JsonRpcProvider(chain.rpc) | ||
const wallet = new Wallet(privateKey, provider); | ||
const balance = await provider.getBalance(wallet.address) | ||
console.log(`${chain.name} wallet balance: ${ethers.formatEther(balance.toString())} ${chain.tokenSymbol}`); | ||
|
||
const deployedContract = await deployCreate2InitUpgradable( | ||
create2DeployerAddress, | ||
wallet, | ||
Ojo, | ||
OjoProxy, | ||
[chain.gateway, axelarGasReceiverAddress], | ||
ethers.AbiCoder.defaultAbiCoder().encode(["string", "string", "uint256"],[ojoChain, ojoAddress, resolveWindow]), | ||
key | ||
); | ||
|
||
console.log(`${chain.name}, address: ${await deployedContract.getAddress()}`); | ||
} | ||
} | ||
|
||
main().catch((error) => { | ||
console.error(error); | ||
process.exitCode = 1; | ||
console.error(error); | ||
process.exitCode = 1; | ||
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,38 @@ | ||
import { Wallet, ethers } from "ethers"; | ||
import Ojo from '../artifacts/contracts/Ojo.sol/Ojo.json'; | ||
import testnet_chains from '../testnet_chains.json'; | ||
import { upgradeUpgradable } from './utils/upgradable'; | ||
|
||
async function main() { | ||
const ojoProxyAddress = "0x4C49Bca23BB402e4938B59Af14f17FA8178c1BA3"; | ||
const axelarGasReceiverAddress = "0xbE406F0189A0B4cf3A05C286473D23791Dd44Cc6"; | ||
|
||
const privateKey = process.env.PRIVATE_KEY; | ||
|
||
if (!privateKey) { | ||
throw new Error('Invalid private key. Make sure the PRIVATE_KEY environment variable is set.'); | ||
} | ||
|
||
const evmChains = testnet_chains.map((chain) => ({ ...chain })); | ||
|
||
for (const chain of evmChains) { | ||
const provider = new ethers.JsonRpcProvider(chain.rpc) | ||
const wallet = new Wallet(privateKey, provider); | ||
const balance = await provider.getBalance(wallet.address) | ||
console.log(`${chain.name} wallet balance: ${ethers.formatEther(balance.toString())} ${chain.tokenSymbol}`); | ||
|
||
const upgradeTx = await upgradeUpgradable( | ||
ojoProxyAddress, | ||
wallet, | ||
Ojo, | ||
[chain.gateway, axelarGasReceiverAddress] | ||
); | ||
|
||
console.log(`${chain.name}, upgrade tx: ${upgradeTx.hash}`); | ||
} | ||
} | ||
|
||
main().catch((error) => { | ||
console.error(error); | ||
process.exitCode = 1; | ||
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,71 @@ | ||
import { AbiCoder, ethers } from "ethers"; | ||
import Create2Deployer from '@axelar-network/axelar-gmp-sdk-solidity/artifacts/contracts/deploy/Create2Deployer.sol/Create2Deployer.json'; | ||
import IUpgradable from '@axelar-network/axelar-gmp-sdk-solidity/artifacts/contracts/interfaces/IUpgradable.sol/IUpgradable.json'; | ||
|
||
export async function deployCreate2InitUpgradable( | ||
create2DeployerAddress: any, | ||
wallet: ethers.Wallet, | ||
implementationJson: { abi: ethers.Interface | ethers.InterfaceAbi; bytecode: ethers.BytesLike | { object: string; }; }, | ||
proxyJson: { abi: ethers.Interface | ethers.InterfaceAbi; bytecode: ethers.BytesLike | { object: string; }; }, | ||
implementationConstructorArgs: any, | ||
setupParams = '0x', | ||
key = Date.now(), | ||
) { | ||
const implementationFactory = new ethers.ContractFactory(implementationJson.abi, implementationJson.bytecode, wallet); | ||
|
||
const implementation = await implementationFactory.deploy(...implementationConstructorArgs); | ||
await implementation.waitForDeployment(); | ||
|
||
const proxy = await create2DeployAndInitContract( | ||
create2DeployerAddress, | ||
wallet, | ||
proxyJson, | ||
key, | ||
[await implementation.getAddress(), wallet.address, setupParams] | ||
); | ||
|
||
return new ethers.Contract(await proxy.getAddress(), implementationJson.abi, wallet); | ||
} | ||
|
||
export async function upgradeUpgradable( | ||
proxyAddress: any, | ||
wallet: ethers.Wallet, | ||
contractJson: { bytecode: any | ethers.Overrides; abi: ethers.Interface | ethers.InterfaceAbi; }, | ||
implementationConstructorArgs: any, | ||
setupParams = '0x' | ||
) { | ||
const proxy = new ethers.Contract(proxyAddress, IUpgradable.abi, wallet); | ||
|
||
const implementationFactory = new ethers.ContractFactory(contractJson.abi, contractJson.bytecode, wallet); | ||
|
||
const implementation = await implementationFactory.deploy(...implementationConstructorArgs); | ||
await implementation.waitForDeployment(); | ||
|
||
const implementationCode = await wallet.provider!.getCode(await implementation.getAddress()); | ||
const implementationCodeHash = ethers.keccak256(implementationCode); | ||
|
||
const tx = await proxy.upgrade(await implementation.getAddress(), implementationCodeHash, setupParams); | ||
await tx.wait(); | ||
|
||
return tx; | ||
} | ||
|
||
async function create2DeployAndInitContract ( | ||
deployerAddress: any, | ||
wallet: ethers.Wallet, | ||
contractJson: { bytecode: any | ethers.Overrides; abi: ethers.Interface | ethers.InterfaceAbi; }, | ||
key: number, | ||
initArgs: any, | ||
confirmations = null, | ||
) { | ||
const deployer = new ethers.Contract(deployerAddress, Create2Deployer.abi, wallet); | ||
const salt = ethers.keccak256(AbiCoder.defaultAbiCoder().encode(['string'], [key.toString()])); | ||
const address = await deployer.deployedAddress(contractJson.bytecode, wallet.address, salt); | ||
const contract = new ethers.Contract(address, contractJson.abi, wallet); | ||
const initData = (await contract.init.populateTransaction(...initArgs)).data; | ||
|
||
const tx = await deployer.deployAndInit(contractJson.bytecode, salt, initData); | ||
await tx.wait(confirmations); | ||
|
||
return contract; | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.