-
Notifications
You must be signed in to change notification settings - Fork 189
feat: oracle bridge upgrade on admin networks #4817
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Open
Reinis-FRP
wants to merge
3
commits into
master
Choose a base branch
from
reinis-frp/admin-oracle-bridge-upgrade
base: master
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
Open
Changes from all commits
Commits
File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or 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 hidden or 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 hidden or 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 hidden or 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 hidden or 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 @@ | ||
out |
This file contains hidden or 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,39 @@ | ||
import fs from "fs"; | ||
import path from "path"; | ||
import { runTypeChain } from "typechain"; | ||
import { getSafeL2SingletonDeployment, getMultiSendCallOnlyDeployment } from "@safe-global/safe-deployments"; | ||
|
||
const version = "1.3.0"; | ||
|
||
async function main() { | ||
const safeL2SingletonDeployment130 = getSafeL2SingletonDeployment({ version }); | ||
if (!safeL2SingletonDeployment130) throw new Error(`SafeL2SingletonDeployment v${version} not found!`); | ||
const multiSendCallOnlyDeployment130 = getMultiSendCallOnlyDeployment({ version }); | ||
if (!multiSendCallOnlyDeployment130) throw new Error(`MultiSendCallOnlyDeployment v${version} not found!`); | ||
const deployments = [safeL2SingletonDeployment130, multiSendCallOnlyDeployment130]; | ||
|
||
const abiDir = path.resolve(__dirname, "./build/abi"); | ||
const typechainDir = path.resolve(__dirname, "./build/typechain"); | ||
fs.mkdirSync(abiDir, { recursive: true }); | ||
fs.mkdirSync(typechainDir, { recursive: true }); | ||
|
||
const abiPaths = deployments.map((deployment) => { | ||
const abiPath = path.join(abiDir, `${deployment.contractName}${version.replace(/\./g, "")}.json`); | ||
fs.writeFileSync(abiPath, JSON.stringify(deployment.abi, null, 2)); | ||
return abiPath; | ||
}); | ||
|
||
const { filesGenerated } = await runTypeChain({ | ||
cwd: __dirname, | ||
filesToProcess: abiPaths, | ||
allFiles: abiPaths, | ||
outDir: typechainDir, | ||
target: "ethers-v5", | ||
}); | ||
console.log(`Generated ethers-v5 typechain for ${filesGenerated} files`); | ||
} | ||
|
||
main().catch((err) => { | ||
console.error("Failed to generate types:", err); | ||
process.exit(1); | ||
}); |
This file contains hidden or 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 hidden or 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 |
---|---|---|
|
@@ -8,6 +8,7 @@ | |
// - Verify: Add --verify flag to Propose command. | ||
|
||
require("dotenv").config(); | ||
const isEqual = require("lodash.isequal"); | ||
const assert = require("assert"); | ||
const Web3 = require("web3"); | ||
const { utf8ToHex, toChecksumAddress } = Web3.utils; | ||
|
@@ -424,10 +425,10 @@ async function run() { | |
fromBlock: 0, | ||
} | ||
); | ||
const relayedRegisterContractEvent = relayedTransactions.find( | ||
(e) => | ||
e.returnValues.calls === | ||
[{ to: contractsByNetId[network.chainId].registry.options.address, data: registerContractData }] | ||
const relayedRegisterContractEvent = relayedTransactions.find((e) => | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The newer typescript version errored to build this as it does not allow comparison of object references |
||
isEqual(e.returnValues.calls, [ | ||
{ to: contractsByNetId[network.chainId].registry.options.address, data: registerContractData }, | ||
]) | ||
); | ||
// It's hard to test whether the addMember and removeMember transactions were relayed as well, since those | ||
// governance transactions could have been executed many blocks before and after the registerContract | ||
|
@@ -442,10 +443,10 @@ async function run() { | |
} | ||
); | ||
assert( | ||
beforeRelayedRegistryTransactions.find( | ||
(e) => | ||
e.returnValues.calls === | ||
[{ to: contractsByNetId[network.chainId].registry.options.address, data: addMemberData }] | ||
beforeRelayedRegistryTransactions.find((e) => | ||
isEqual(e.returnValues.calls, [ | ||
{ to: contractsByNetId[network.chainId].registry.options.address, data: addMemberData }, | ||
]) | ||
), | ||
"Could not find RelayedGovernanceRequest matching expected relayed addMemberData transaction" | ||
); | ||
|
@@ -458,10 +459,10 @@ async function run() { | |
} | ||
); | ||
assert( | ||
afterRelayedRegistryTransactions.find( | ||
(e) => | ||
e.returnValues.calls === | ||
[{ to: contractsByNetId[network.chainId].registry.options.address, data: removeMemberData }] | ||
afterRelayedRegistryTransactions.find((e) => | ||
isEqual(e.returnValues.calls, [ | ||
{ to: contractsByNetId[network.chainId].registry.options.address, data: removeMemberData }, | ||
]) | ||
), | ||
"Could not find RelayedGovernanceRequest matching expected relayed removeMemberData transaction" | ||
); | ||
|
110 changes: 110 additions & 0 deletions
110
...ages/scripts/src/admin-proposals/upgrade-oo-request-bridging-admin/0_PayloadAdminChain.ts
This file contains hidden or 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,110 @@ | ||
// This script creates a safe payload to be executed on a multisig controlled child messenger for the Oracle bridging | ||
// contracts upgrade. Make sure that the newly deployed bridging contracts have been added to the networks config in the | ||
// `core` package and the `contracts-node` package has been rebuilt. This will also impersonate the multisig owners and | ||
// execute the safe payload on a forked network. | ||
// Export following environment variables: | ||
// - NODE_URL_X: Child chain ID specific node URL (not required when using localhost for a forked network). | ||
// Then run the script with: | ||
// yarn hardhat run packages/scripts/src/admin-proposals/upgrade-oo-request-bridging-admin/0_PayloadAdminChain.ts --network <network> | ||
// Note: use localhost for the forked network, for public network also need to export the chain ID specific NODE_URL_X | ||
// environment variable. | ||
|
||
import { FinderEthers, getAbi, getAddress, OptimisticOracleV3Ethers } from "@uma/contracts-node"; | ||
import { interfaceName } from "@uma/common"; | ||
import { AdminChildMessenger } from "@uma/contracts-node/dist/packages/contracts-node/typechain/core/ethers"; | ||
import { utils as ethersUtils, constants as ethersConstants, BytesLike } from "ethers"; | ||
import fs from "fs"; | ||
import path from "path"; | ||
import hre from "hardhat"; | ||
import { getContractInstance } from "../../utils/contracts"; | ||
import { | ||
appendTxToSafePayload, | ||
baseSafePayload, | ||
getContractMethod, | ||
simulateSafePayload, | ||
} from "../../utils/gnosisPayload"; | ||
|
||
async function main() { | ||
const chainId = (await hre.ethers.provider.getNetwork()).chainId; | ||
const oracleSpokeAddress = await getAddress("OracleSpoke", chainId); | ||
const governorSpokeAddress = await getAddress("GovernorSpoke", chainId); | ||
const adminChildMessenger = await getContractInstance<AdminChildMessenger>( | ||
"Admin_ChildMessenger", | ||
undefined, | ||
chainId | ||
); | ||
const adminChildMessengerAbi = getAbi("Admin_ChildMessenger"); | ||
const finder = await getContractInstance<FinderEthers>("Finder", undefined, chainId); | ||
const optimisticOracleV3 = await getContractInstance<OptimisticOracleV3Ethers>( | ||
"OptimisticOracleV3", | ||
undefined, | ||
chainId | ||
); | ||
|
||
// Will construct the safe payload for the multisig owners to execute. | ||
const multisig = await adminChildMessenger.owner(); | ||
|
||
// Update the OracleSpoke address in the AdminChildMessenger | ||
let safePayload = baseSafePayload(chainId, "", "", multisig); | ||
safePayload = appendTxToSafePayload( | ||
safePayload, | ||
adminChildMessenger.address, | ||
getContractMethod(adminChildMessengerAbi, "setOracleSpoke"), | ||
{ | ||
newOracleSpoke: oracleSpokeAddress, | ||
} | ||
); | ||
|
||
// Set OracleSpoke as Oracle in L2 Finder and sync the cached value in OptimisticOracleV3 atomically | ||
const relayedTransactions: { | ||
to: string; | ||
data: BytesLike; | ||
}[] = []; | ||
const changeImplementationAddressTx = await finder.populateTransaction.changeImplementationAddress( | ||
ethersUtils.formatBytes32String(interfaceName.Oracle), | ||
oracleSpokeAddress | ||
); | ||
if (!changeImplementationAddressTx.data) throw new Error("changeImplementationAddressTx.data is empty"); | ||
relayedTransactions.push({ to: finder.address, data: changeImplementationAddressTx.data }); | ||
const syncUmaParamsTx = await optimisticOracleV3.populateTransaction.syncUmaParams( | ||
ethersUtils.formatBytes32String(""), | ||
ethersConstants.AddressZero | ||
); | ||
if (!syncUmaParamsTx.data) throw new Error("syncUmaParamsTx.data is empty"); | ||
relayedTransactions.push({ to: optimisticOracleV3.address, data: syncUmaParamsTx.data }); | ||
const encodedCalls = ethersUtils.defaultAbiCoder.encode(["tuple(address to, bytes data)[]"], [relayedTransactions]); | ||
safePayload = appendTxToSafePayload( | ||
safePayload, | ||
adminChildMessenger.address, | ||
getContractMethod(adminChildMessengerAbi, "processMessageFromCrossChainParent"), | ||
{ | ||
data: encodedCalls, | ||
target: governorSpokeAddress, | ||
} | ||
); | ||
|
||
const outDir = path.resolve(__dirname, "../../../out"); // root of the scripts package, must check when moving the script | ||
fs.mkdirSync(outDir, { recursive: true }); | ||
const savePath = path.join(outDir, `${path.basename(__dirname)}_${chainId}.json`); | ||
fs.writeFileSync(savePath, JSON.stringify(safePayload, null, 4)); | ||
|
||
console.log(`Safe payload for ${multisig} saved to ${savePath}`); | ||
|
||
// Only spoof the execution on a forked network. | ||
if (hre.network.name === "localhost") { | ||
// The version only impacts which MultiSendCallOnly contract is called as the used safe interfaces for the | ||
// simulation are the same across the versions. | ||
const safeVersion = "1.3.0"; | ||
await simulateSafePayload(hre.ethers.provider, safePayload, safeVersion); | ||
} | ||
} | ||
|
||
main().then( | ||
() => { | ||
process.exit(0); | ||
}, | ||
(err) => { | ||
console.error(err); | ||
process.exit(1); | ||
} | ||
); |
73 changes: 73 additions & 0 deletions
73
packages/scripts/src/admin-proposals/upgrade-oo-request-bridging-admin/1_Verify.ts
This file contains hidden or 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,73 @@ | ||
// This script verifies the governance payload on Oracle bridging contracts upgrade has been properly executed. | ||
// Export following environment variables: | ||
// - NODE_URL_X: Child chain ID specific node URL (not required when using localhost for a forked network). | ||
// Then run the script with: | ||
// yarn hardhat run packages/scripts/src/admin-proposals/upgrade-oo-request-bridging-admin/1_Verify.ts --network <network> | ||
// Note: use localhost for the forked network, for public network also need to export the chain ID specific NODE_URL_X | ||
// environment variable. | ||
|
||
import { strict as assert } from "assert"; | ||
import { FinderEthers, getAddress, OptimisticOracleV3Ethers } from "@uma/contracts-node"; | ||
import { interfaceName } from "@uma/common"; | ||
import { utils as ethersUtils } from "ethers"; | ||
import { getContractInstance } from "../../utils/contracts"; | ||
import { AdminChildMessenger } from "@uma/contracts-node/typechain/core/ethers"; | ||
|
||
async function verifyChildMessenger(chainId: number) { | ||
const oracleSpokeAddress = await getAddress("OracleSpoke", chainId); | ||
const adminChildMessenger = await getContractInstance<AdminChildMessenger>( | ||
"Admin_ChildMessenger", | ||
undefined, | ||
chainId | ||
); | ||
assert((await adminChildMessenger.oracleSpoke()) === oracleSpokeAddress); | ||
console.log( | ||
` ✅ OracleSpoke ${oracleSpokeAddress} is set in the admin child messenger ${adminChildMessenger.address} on chainId ${chainId}` | ||
); | ||
} | ||
|
||
async function verifyOracleImplementation(chainId: number) { | ||
const oracleSpokeAddress = await getAddress("OracleSpoke", chainId); | ||
const finder = await getContractInstance<FinderEthers>("Finder", undefined, chainId); | ||
assert( | ||
(await finder.getImplementationAddress(ethersUtils.formatBytes32String(interfaceName.Oracle))) === | ||
oracleSpokeAddress | ||
); | ||
console.log(` ✅ OracleSpoke ${oracleSpokeAddress} is set as Oracle in the Finder on chainId ${chainId}`); | ||
} | ||
|
||
async function verifyCachedOracle(chainId: number) { | ||
const oracleSpokeAddress = await getAddress("OracleSpoke", chainId); | ||
const optimisticOracleV3 = await getContractInstance<OptimisticOracleV3Ethers>( | ||
"OptimisticOracleV3", | ||
undefined, | ||
chainId | ||
); | ||
assert((await optimisticOracleV3.cachedOracle()) === oracleSpokeAddress); | ||
console.log( | ||
` ✅ OracleSpoke ${oracleSpokeAddress} is cached as Oracle in the OptimisticOracleV3 on chainId ${chainId}` | ||
); | ||
} | ||
|
||
async function main() { | ||
const chainId = (await hre.ethers.provider.getNetwork()).chainId; | ||
|
||
console.log(" 1. Validating OracleSpoke address is set in the admin child messenger"); | ||
await verifyChildMessenger(chainId); | ||
|
||
console.log(" 2. Validating OracleSpoke address is set as Oracle in the child chain Finder contract"); | ||
await verifyOracleImplementation(chainId); | ||
|
||
console.log(" 3. Validating OracleSpoke address is cached as Oracle in the child chain OptimisticOracleV3 contract"); | ||
await verifyCachedOracle(chainId); | ||
} | ||
|
||
main().then( | ||
() => { | ||
process.exit(0); | ||
}, | ||
(err) => { | ||
console.error(err); | ||
process.exit(1); | ||
} | ||
); |
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@safe-global/types-kit
was not compatible with older typescipt from the root of the workspace.