Skip to content
Draft
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@
"eslint-plugin-react": "^7.19.0",
"eslint-plugin-react-hooks": "^3.0.0",
"ganache-cli": "^6.12.2",
"hardhat": "^2.12.6",
"hardhat": "^2.19.5",
"husky": "^4.2.3",
"lerna": "^3.22.1",
"lint-staged": "^10.1.3",
Expand Down
1 change: 0 additions & 1 deletion packages/scripts/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,6 @@
"@uma/financial-templates-lib": "^2.37.2",
"dotenv": "^6.2.0",
"ethers": "^5.4.2",
"hardhat": "^2.12.6",
"lodash": "^4.17.21",
"minimist": "^1.2.0",
"web3": "^1.6.0",
Expand Down
6 changes: 5 additions & 1 deletion packages/scripts/src/admin-proposals/common/networks.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
const supportedNetworks = ["mainnet", "polygon", "arbitrum", "optimism", "base", "blast"] as const;
export const supportedNetworks = ["mainnet", "polygon", "arbitrum", "optimism", "base", "blast"] as const;
export type SupportedNetwork = typeof supportedNetworks[number];

export const networksNumber: Record<SupportedNetwork, number> = {
Expand All @@ -10,6 +10,10 @@ export const networksNumber: Record<SupportedNetwork, number> = {
blast: 81457,
};

export function isSupportedNetwork(key: string): key is SupportedNetwork {
return (supportedNetworks as readonly string[]).includes(key);
}

export const l2Networks = supportedNetworks.filter((network) => network !== "mainnet");
export type L2Network = typeof l2Networks[number];

Expand Down
57 changes: 57 additions & 0 deletions packages/scripts/src/admin-proposals/executeAndRelayVoteV2.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
// This script executes a governance vote on the Ethereum mainnet or its fork. It also spoofs the relaying of
// the executed proposal on all forked L2 networks when L1 is forked.
// Export following environment variables:
// - MNEMONIC: Mnemonic for the operator to execute the proposal on L1.
// - NODE_URL_1: Mainnet node URL (not required when using localhost for a forked network).
// - NODE_URL_137: Forked Polygon node URL (not required when using mainnet for L1).
// - NODE_URL_10: Forked Optimism node URL (not required when using mainnet for L1).
// - NODE_URL_42161: Forked Arbitrum node URL (not required when using mainnet for L1).
// - NODE_URL_8453: Forked Base node URL (not required when using mainnet for L1).
// - NODE_URL_81457: Forked Blast node URL (not required when using mainnet for L1).
// - L1_EXECUTE_TX: (optional) L1 transaction hash of the executed proposal, will execute the proposal on L1 if not set.
// - PROPOSAL_NUMBER: (optional) Proposal number to execute, will execute the last proposal if not set.
// Then run the script with:
// yarn hardhat run packages/scripts/src/admin-proposals/executeAndRelayVoteV2.ts --network <network>
// Note: use localhost for the forked network, for L1 mainnet need to export NODE_URL_1 environment variable.

import { getMnemonicSigner } from "@uma/common";
import hre from "hardhat";
import {
getJsonRpcProvider,
getL1ExecuteProposalReceipt,
l2Networks,
ovmNetworks,
spoofArbitrumRelay,
spoofOVMRelay,
spoofPolygonRelay,
} from "./common";

async function main() {
const l1Signer = getMnemonicSigner().connect(hre.ethers.provider);

// Executes the proposal on L1 unless L1_EXECUTE_TX was provided.
const l1TxReceipt = await getL1ExecuteProposalReceipt(l1Signer);

// Only spoof the relay on L2 when L1 is forked.
if (hre.network.name !== "localhost") return;

// Checks node URL for each forked L2 network is set.
l2Networks.forEach(getJsonRpcProvider);

// Spoof the relay of executed proposal on each L2.
for (const networkName of ovmNetworks) {
await spoofOVMRelay(networkName, l1TxReceipt);
}
await spoofArbitrumRelay(l1TxReceipt);
await spoofPolygonRelay(l1TxReceipt);
}

main().then(
() => {
process.exit(0);
},
(err) => {
console.error(err);
process.exit(1);
}
);
127 changes: 127 additions & 0 deletions packages/scripts/src/admin-proposals/remove-identifier/0_Propose.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,127 @@
// This script can be run against a public or forked networks.
// Export following environment variables:
// - NODE_URL_1: Mainnet node URL (not required when using localhost for a forked network).
// - NODE_URL_137: Public or forked Polygon node URL.
// - NODE_URL_10: Public or forked Optimism node URL.
// - NODE_URL_42161: Public or forked Arbitrum node URL.
// - NODE_URL_8453: Public or forked Base node URL.
// - NODE_URL_81457: Public or forked Blast node URL.
// - GCKMS_WALLET: GCKMS wallet name, required only on public mainnet.
// - IDENTIFIER: Identifier to remove.
// - UMIP_NUMBER: UMIP number to use in the proposal title.
// Then run the script with:
// yarn hardhat run packages/scripts/src/admin-proposals/remove-identifier/0_Propose.ts --network <network>
// Note: use localhost for the forked network, for L1 mainnet need to export NODE_URL_1 environment variable.

import hre from "hardhat";

import {
GovernorHubEthers,
GovernorRootTunnelEthers,
IdentifierWhitelistEthers,
ParentMessengerBaseEthers,
ProposerV2Ethers,
VotingTokenEthers,
} from "@uma/contracts-node";

import { PopulatedTransaction } from "ethers";
import { formatBytes32String } from "ethers/lib/utils";
import { getContractInstance, getContractInstanceWithProvider } from "../../utils/contracts";
import { fundArbitrumParentMessengerForRelays, relayGovernanceMessages } from "../../utils/relay";
import {
AdminProposalTransaction,
PROPOSER_ADDRESS,
getProposerSigner,
getUmipNumber,
isSupportedNetwork,
networksNumber,
supportedNetworks,
} from "../common";
import { getRetryProvider } from "@uma/common";

async function main() {
const adminProposalTransactions: AdminProposalTransaction[] = [];

if (!process.env.IDENTIFIER) throw new Error("IDENTIFIER is not set");
const oldIdentifier = formatBytes32String(process.env.IDENTIFIER);

const umipNumber = getUmipNumber();

const arbitrumParentMessenger = await getContractInstance<ParentMessengerBaseEthers>("Arbitrum_ParentMessenger");

const governorRootTunnel = await getContractInstance<GovernorRootTunnelEthers>("GovernorRootTunnel"); // for polygon
const governorHub = await getContractInstance<GovernorHubEthers>("GovernorHub"); // rest of l2

const proposerSigner = await getProposerSigner(PROPOSER_ADDRESS);

console.log("1. LOADING DEPLOYED CONTRACT STATE");

const votingToken = await getContractInstance<VotingTokenEthers>("VotingToken");

const proposerV2 = await getContractInstance<ProposerV2Ethers>("ProposerV2");
const identifierWhitelist = await getContractInstance<IdentifierWhitelistEthers>("IdentifierWhitelist");

// remove the identifier from whitelist
const removeIdentifierTx = await identifierWhitelist.populateTransaction.removeSupportedIdentifier(oldIdentifier);
if (!removeIdentifierTx.data) throw "removeIdentifierTx.data is null";
adminProposalTransactions.push({ to: identifierWhitelist.address, value: 0, data: removeIdentifierTx.data });

for (const networkName of supportedNetworks.filter((network) => network !== "mainnet")) {
if (!isSupportedNetwork(networkName)) throw new Error(`Unsupported network: ${networkName}`);
const l2ChainId = networksNumber[networkName];
const isPolygon = l2ChainId === 137;
const isArbitrum = l2ChainId === 42161;

const governanceMessages: { targetAddress: string; tx: PopulatedTransaction }[] = [];

const l2IdentifierWhitelist = await getContractInstanceWithProvider<IdentifierWhitelistEthers>(
"IdentifierWhitelist",
getRetryProvider(l2ChainId)
);

const removeIdentifierL2Tx = await l2IdentifierWhitelist.populateTransaction.removeSupportedIdentifier(
oldIdentifier
);
governanceMessages.push({ targetAddress: l2IdentifierWhitelist.address, tx: removeIdentifierL2Tx });

if (isArbitrum) await fundArbitrumParentMessengerForRelays(arbitrumParentMessenger, proposerSigner, 1);

const relayedMessages = await relayGovernanceMessages(
governanceMessages,
isPolygon ? governorRootTunnel : governorHub,
l2ChainId
);

adminProposalTransactions.push(...relayedMessages);
}

const defaultBond = await proposerV2.bond();
const allowance = await votingToken.allowance(PROPOSER_ADDRESS, proposerV2.address);
if (allowance.lt(defaultBond)) {
console.log("Approving proposer bond");
const approveTx = await votingToken.connect(proposerSigner).approve(proposerV2.address, defaultBond);
await approveTx.wait();
}

const tx = await (await getContractInstance<ProposerV2Ethers>("ProposerV2", proposerV2.address))
.connect(proposerSigner)
.propose(
adminProposalTransactions,
hre.ethers.utils.toUtf8Bytes(`UMIP-${umipNumber} remove ${process.env.IDENTIFIER} identifier`)
);

await tx.wait();

console.log("Proposal done!🎉");
console.log("\nProposal data:\n", tx.data);
}

main().then(
() => {
process.exit(0);
},
(err) => {
console.error(err);
process.exit(1);
}
);
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
// This script can be run against a public or forked networks.
// Export following environment variables:
// - NODE_URL_1: Mainnet node URL (not required when using localhost for a forked network).
// - NODE_URL_137: Public or forked Polygon node URL.
// - NODE_URL_10: Public or forked Optimism node URL.
// - NODE_URL_42161: Public or forked Arbitrum node URL.
// - NODE_URL_8453: Public or forked Base node URL.
// - NODE_URL_81457: Public or forked Blast node URL.
// - IDENTIFIER: Identifier to remove.
// Then run the script with:
// yarn hardhat run packages/scripts/src/admin-proposals/remove-identifier/1_Verify.ts --network <network>
// Note: use localhost for the forked network, for L1 mainnet need to export NODE_URL_1 environment variable.

import { strict as assert } from "assert";
import hre from "hardhat";
import { IdentifierWhitelistEthers } from "@uma/contracts-node";
import { formatBytes32String } from "ethers/lib/utils";
import { getContractInstanceWithProvider } from "../../utils/contracts";
import { getJsonRpcProvider, supportedNetworks } from "../common";

async function main() {
if (!process.env.IDENTIFIER) throw new Error("IDENTIFIER is not set");
const oldIdentifier = formatBytes32String(process.env.IDENTIFIER);

for (const networkName of supportedNetworks) {
const provider = networkName === "mainnet" ? hre.ethers.provider : await getJsonRpcProvider(networkName);
const identifierWhitelist = await getContractInstanceWithProvider<IdentifierWhitelistEthers>(
"IdentifierWhitelist",
provider
);

console.log(
`Validating identifier ${process.env.IDENTIFIER} on ${networkName} at IdentifierWhitelist address: ${identifierWhitelist.address}`
);
assert(!(await identifierWhitelist.isIdentifierSupported(oldIdentifier)));
console.log(`✅ ${process.env.IDENTIFIER} identifier is not supported on ${networkName}`);
}
}

main().then(
() => {
process.exit(0);
},
(err) => {
console.error(err);
process.exit(1);
}
);
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
# Removal of supported identifier

This directory contains scripts to remove an identifier from the IdentifierWhitelist on all supported governance based networks.

## Testing on forked networks

Spin up forked networks, each in a separate terminal, e.g.:

```sh
HARDHAT_CHAIN_ID=1 yarn hardhat node --fork https://mainnet.infura.io/v3/<YOUR-INFURA-KEY> --port 9545 --no-deploy

HARDHAT_CHAIN_ID=137 yarn hardhat node --fork https://polygon-mainnet.infura.io/v3/<YOUR-INFURA-KEY> --port 9546 --no-deploy

HARDHAT_CHAIN_ID=10 yarn hardhat node --fork https://optimism-mainnet.infura.io/v3/<YOUR-INFURA-KEY> --port 9547 --no-deploy

HARDHAT_CHAIN_ID=42161 yarn hardhat node --fork https://arbitrum-mainnet.infura.io/v3/<YOUR-INFURA-KEY> --port 9548 --no-deploy

HARDHAT_CHAIN_ID=8453 yarn hardhat node --fork https://base-mainnet.infura.io/v3/<YOUR-INFURA-KEY> --port 9549 --no-deploy

HARDHAT_CHAIN_ID=81457 yarn hardhat node --fork https://blast-mainnet.infura.io/v3/<YOUR-INFURA-KEY> --port 9550 --no-deploy
```

Note that Ethereum mainnet must use port `9545` as the scripts rely on hardhat `localhost` network when forking. For other networks, you can use any other free port and pass its node URL as an environment variable.

Export the required environment:

```sh
export MNEMONIC=<YOUR-MNEMONIC>
export NODE_URL_137=http://localhost:9546
export NODE_URL_10=http://localhost:9547
export NODE_URL_42161=http://localhost:9548
export NODE_URL_8453=http://localhost:9549
export NODE_URL_81457=http://localhost:9550
export UMIP_NUMBER=<UMIP-NUMBER>
export IDENTIFIER=<IDENTIFIER-TO-REMOVE>
```

Note: make sure the wallet corresponding to the `MNEMONIC` is sufficiently funded on the forked networks (use `hardhat_setBalance` if needed).

Request to impersonate accounts and seed wallet on the forked mainnet that we'll need to propose and vote on the admin proposal:

```sh
./packages/scripts/setupFork.sh
```

Propose the governance vote to remove the supported identifier on the forked mainnet:

```sh
yarn hardhat run packages/scripts/src/admin-proposals/remove-identifier/0_Propose.ts --network localhost
```

Simulate the vote on the proposal without executing it yet:

```sh
SKIP_EXECUTE=1 yarn hardhat run packages/scripts/src/admin-proposals/simulateVoteV2.ts --network localhost
```

Execute the proposal on the forked mainnet and spoof relaying the governance transactions to forked L2 networks:

```sh
yarn hardhat run packages/scripts/src/admin-proposals/executeAndRelayVoteV2.ts --network localhost
```

Verify the proposal execution on the forked networks:

```sh
yarn hardhat run packages/scripts/src/admin-proposals/remove-identifier/1_Verify.ts --network localhost
```

## Public networks

Update the required `NODE_URL_` environment variables to point to public networks.

Propose the governance vote to remove the supported identifier on the mainnet:

```sh
GCKMS_WALLET=deployer yarn hardhat run packages/scripts/src/admin-proposals/remove-identifier/0_Propose.ts --network mainnet
```

Note: make sure to first authenticate to `gcloud`.

If the vote is resolved to approve the migration, execute the proposal on the mainnet and wait for it to be relayed to the L2 networks:

```sh
yarn hardhat run packages/scripts/src/admin-proposals/executeAndRelayVoteV2.ts --network mainnet
```

Verify the proposal execution on all the networks:

```sh
yarn hardhat run packages/scripts/src/admin-proposals/remove-identifier/1_Verify.ts --network mainnet
```
Loading