From baf9859fa3fccb9ccea4f6ad94d1fcdcf8493695 Mon Sep 17 00:00:00 2001 From: Anton Kovalchuk Date: Tue, 11 Jun 2024 15:43:40 +0200 Subject: [PATCH] update upgrade/deploy scripts --- package.json | 20 +- scripts/optimism/deploy-new-impls.ts | 88 ----- scripts/optimism/deploy-oracle.ts | 85 ---- scripts/optimism/deploy-scratch.ts | 98 +++++ scripts/optimism/upgrade.ts | 109 ++++++ .../optimism.integration.test.ts | 48 ++- ...bridging-non-rebasable.integration.test.ts | 2 +- .../bridging-rebasable.integration.test.ts | 11 +- test/optimism/pushingTokenRate.e2e.test.ts | 2 +- utils/deployment.ts | 66 +++- utils/optimism/deployment.ts | 369 ++++++++++++++++++ .../optimism/deploymentNewImplementations.ts | 239 ------------ utils/optimism/index.ts | 4 +- utils/optimism/testing.ts | 38 +- utils/optimism/upgrade.ts | 329 ++++++++++++++++ utils/testing/env.ts | 27 +- 16 files changed, 1046 insertions(+), 489 deletions(-) delete mode 100644 scripts/optimism/deploy-new-impls.ts delete mode 100644 scripts/optimism/deploy-oracle.ts create mode 100644 scripts/optimism/deploy-scratch.ts create mode 100644 scripts/optimism/upgrade.ts create mode 100644 utils/optimism/deployment.ts delete mode 100644 utils/optimism/deploymentNewImplementations.ts create mode 100644 utils/optimism/upgrade.ts diff --git a/package.json b/package.json index 06cba522..a4560476 100644 --- a/package.json +++ b/package.json @@ -7,21 +7,19 @@ "compile": "hardhat compile", "compile:force": "hardhat compile --force", "coverage": "hardhat coverage --testfiles './test/**/*.unit.test.ts'", - "test:e2e": "hardhat test ./test/**/*.e2e.test.ts", - "test:unit": "hardhat test ./test/**/*.unit.test.ts", - "test:integration": "hardhat test ./test/**/*.integration.test.ts", "fork:eth:mainnet": "hardhat node:fork eth_mainnet 8545", "fork:eth:sepolia": "hardhat node:fork eth_sepolia 8545", "fork:opt:sepolia": "hardhat node:fork opt_sepolia 9545", "fork:opt:mainnet": "hardhat node:fork opt_mainnet 9545", - "optimism:deploy": "ts-node --files ./scripts/optimism/deploy-bridge.ts", - "optimism:finalize-message": "ts-node --files ./scripts/optimism/finalize-message.ts", - "optimism:test:e2e": "hardhat test ./test/optimism/*.e2e.test.ts", - "optimism:test:unit": "hardhat test ./test/optimism/*.unit.test.ts", - "optimism:test:integration": "hardhat test ./test/optimism/*.integration.test.ts", - "optimism:test:acceptance": "hardhat test ./test/optimism/*.acceptance.test.ts", - "optimism:test:executor": "hardhat test ./test/bridge-executor/optimism.integration.test.ts", - "optimism:test:launch": "REVERT=false hardhat test ./test/optimism/{_launch.test.ts,bridging.integration.test.ts}" + "deploy": "ts-node --files ./scripts/optimism/deploy-scratch.ts", + "upgrade": "ts-node --files ./scripts/optimism/upgrade.ts", + "finalize-message": "ts-node --files ./scripts/optimism/finalize-message.ts", + "test:e2e": "hardhat test ./test/optimism/*.e2e.test.ts", + "test:unit": "hardhat test ./test/optimism/*.unit.test.ts", + "test:integration": "hardhat test ./test/optimism/*.integration.test.ts", + "test:acceptance": "hardhat test ./test/optimism/*.acceptance.test.ts", + "test:executor": "hardhat test ./test/bridge-executor/optimism.integration.test.ts", + "test:launch": "REVERT=false hardhat test ./test/optimism/{_launch.test.ts,bridging.integration.test.ts}" }, "keywords": [], "author": "", diff --git a/scripts/optimism/deploy-new-impls.ts b/scripts/optimism/deploy-new-impls.ts deleted file mode 100644 index bd6104c3..00000000 --- a/scripts/optimism/deploy-new-impls.ts +++ /dev/null @@ -1,88 +0,0 @@ -import env from "../../utils/env"; -import prompt from "../../utils/prompt"; -import network from "../../utils/network"; -import deployment from "../../utils/deployment"; - -import deploymentNewImplementations from "../../utils/optimism/deploymentNewImplementations"; -import { BigNumber } from "ethers"; - -async function main() { - const networkName = env.network(); - const ethOptNetwork = network.multichain(["eth", "opt"], networkName); - - const [ethDeployer] = ethOptNetwork.getSigners(env.privateKey(), { - forking: env.forking(), - }); - const [, optDeployer] = ethOptNetwork.getSigners( - env.string("OPT_DEPLOYER_PRIVATE_KEY"), - { - forking: env.forking(), - } - ); - - const deploymentConfig = deployment.loadMultiChainDeploymentConfig(); - - const [l1DeployScript, l2DeployScript] = await deploymentNewImplementations( - networkName, - { logger: console } - ) - .deployScript( - { - deployer: ethDeployer, - admins: { - proxy: deploymentConfig.l1.proxyAdmin, - bridge: ethDeployer.address - }, - contractsShift: 0, - tokenProxyAddress: deploymentConfig.l1Token, - tokenRebasableProxyAddress: deploymentConfig.l1RebasableToken, - opStackTokenRatePusherImplAddress: deploymentConfig.l1OpStackTokenRatePusher, - tokenBridgeProxyAddress: deploymentConfig.l1TokenBridge, - }, - { - deployer: optDeployer, - admins: { - proxy: deploymentConfig.l2.proxyAdmin, - bridge: optDeployer.address, - }, - contractsShift: 0, - tokenBridgeProxyAddress: deploymentConfig.l2TokenBridge, - tokenProxyAddress: deploymentConfig.l2Token, - tokenRateOracle: { - proxyAddress: deploymentConfig.l2TokenRateOracle, - rateOutdatedDelay: BigNumber.from(deploymentConfig.tokenRateOutdatedDelay), - maxAllowedL2ToL1ClockLag: BigNumber.from(86400), - maxAllowedTokenRateDeviationPerDay: BigNumber.from(500) - }, - token: { - name: "name", - symbol: "symbol", - version: "1" - }, - tokenRebasable: { - name: "name", - symbol: "symbol", - version: "1" - } - } - ); - - await deployment.printMultiChainDeploymentConfig( - "Deploy new implementations: bridges, wstETH, stETH", - ethDeployer, - optDeployer, - deploymentConfig, - l1DeployScript, - l2DeployScript - ); - - await prompt.proceed(); - - await l1DeployScript.run(); - await l2DeployScript.run(); -} - -main().catch((error) => { - console.error(error); - process.exitCode = 1; -}); diff --git a/scripts/optimism/deploy-oracle.ts b/scripts/optimism/deploy-oracle.ts deleted file mode 100644 index 88013720..00000000 --- a/scripts/optimism/deploy-oracle.ts +++ /dev/null @@ -1,85 +0,0 @@ -import env from "../../utils/env"; -import prompt from "../../utils/prompt"; -import network from "../../utils/network"; -import optimism from "../../utils/optimism"; -import deployment from "../../utils/deployment"; -import { TokenRateNotifier__factory } from "../../typechain"; - -async function main() { - const networkName = env.network(); - const ethOptNetwork = network.multichain(["eth", "opt"], networkName); - - const [ethDeployer] = ethOptNetwork.getSigners(env.privateKey(), { - forking: env.forking(), - }); - const [, optDeployer] = ethOptNetwork.getSigners( - env.string("OPT_DEPLOYER_PRIVATE_KEY"), - { - forking: env.forking(), - } - ); - - const blockNumber = await optDeployer.provider.getBlockNumber(); - const blockTimestamp = (await optDeployer.provider.getBlock(blockNumber)).timestamp; - - const deploymentConfig = deployment.loadMultiChainDeploymentConfig(); - - const [l1DeployScript, l2DeployScript] = await optimism - .deploymentOracle(networkName, { logger: console }) - .oracleDeployScript( - deploymentConfig.l1Token, - deploymentConfig.accountingOracle, - deploymentConfig.l2GasLimitForPushingTokenRate, - deploymentConfig.tokenRateOutdatedDelay, - { - deployer: ethDeployer, - admins: { - proxy: deploymentConfig.l1.proxyAdmin, - bridge: ethDeployer.address, - }, - contractsShift: 0 - }, - { - deployer: optDeployer, - admins: { - proxy: deploymentConfig.l2.proxyAdmin, - bridge: optDeployer.address, - }, - contractsShift: 0, - tokenRateOracle: { - maxAllowedL2ToL1ClockLag: 86400, - maxAllowedTokenRateDeviationPerDay: 500, - tokenRate: 1164454276599657236000000000, - l1Timestamp: blockTimestamp - } - } - ); - - await deployment.printMultiChainDeploymentConfig( - "Deploy Token Rate Oracle", - ethDeployer, - optDeployer, - deploymentConfig, - l1DeployScript, - l2DeployScript - ); - - await prompt.proceed(); - - await l1DeployScript.run(); - await l2DeployScript.run(); - - /// setup by adding observer - const tokenRateNotifier = TokenRateNotifier__factory.connect( - l1DeployScript.tokenRateNotifierImplAddress, - ethDeployer - ); - await tokenRateNotifier - .connect(ethDeployer) - .addObserver(l1DeployScript.opStackTokenRatePusherImplAddress); -} - -main().catch((error) => { - console.error(error); - process.exitCode = 1; -}); diff --git a/scripts/optimism/deploy-scratch.ts b/scripts/optimism/deploy-scratch.ts new file mode 100644 index 00000000..165155ec --- /dev/null +++ b/scripts/optimism/deploy-scratch.ts @@ -0,0 +1,98 @@ +import env from "../../utils/env"; +import prompt from "../../utils/prompt"; +import network from "../../utils/network"; +import deployment from "../../utils/deployment"; +import { BridgingManagement } from "../../utils/bridging-management"; +import deploymentAllFromScratch from "../../utils/optimism/deployment"; + +async function main() { + const networkName = env.network(); + const ethOptNetwork = network.multichain(["eth", "opt"], networkName); + + const [ethDeployer] = ethOptNetwork.getSigners(env.privateKey(), { + forking: env.forking(), + }); + const [, optDeployer] = ethOptNetwork.getSigners( + env.string("OPT_DEPLOYER_PRIVATE_KEY"), + { + forking: env.forking(), + } + ); + + const deploymentConfig = deployment.loadMultiChainDeploymentConfig(); + + const [l1DeployScript, l2DeployScript] = await deploymentAllFromScratch (networkName, { logger: console }) + .deployAllScript( + { + l1TokenNonRebasable: deploymentConfig.l1TokenNonRebasable, + l1TokenRebasable: deploymentConfig.l1RebasableToken, + accountingOracle: deploymentConfig.accountingOracle, + l2GasLimitForPushingTokenRate: deploymentConfig.l2GasLimitForPushingTokenRate, + + deployer: ethDeployer, + admins: { + proxy: deploymentConfig.l1.proxyAdmin, + bridge: ethDeployer.address + }, + contractsShift: 0, + }, + { + tokenRateOracle: { + tokenRateOutdatedDelay: deploymentConfig.tokenRateOutdatedDelay, + maxAllowedL2ToL1ClockLag: deploymentConfig.maxAllowedL2ToL1ClockLag, + maxAllowedTokenRateDeviationPerDayBp: deploymentConfig.maxAllowedTokenRateDeviationPerDayBp, + oldestRateAllowedInPauseTimeSpan: deploymentConfig.oldestRateAllowedInPauseTimeSpan, + maxAllowedTimeBetweenTokenRateUpdates: deploymentConfig.maxAllowedTimeBetweenTokenRateUpdates, + tokenRate: deploymentConfig.tokenRateValue, + l1Timestamp: deploymentConfig.tokenRateL1Timestamp + }, + l2TokenNonRebasable: { + version: "1" + }, + l2TokenRebasable: { + version: "1" + }, + + deployer: optDeployer, + admins: { + proxy: deploymentConfig.l2.proxyAdmin, + bridge: optDeployer.address, + }, + contractsShift: 0, + } + ); + + await deployment.printMultiChainDeploymentConfig( + "Deploy Optimism Bridge", + ethDeployer, + optDeployer, + deploymentConfig, + l1DeployScript, + l2DeployScript + ); + + await prompt.proceed(); + + await l1DeployScript.run(); + await l2DeployScript.run(); + + const l1BridgingManagement = new BridgingManagement( + l1DeployScript.bridgeProxyAddress, + ethDeployer, + { logger: console } + ); + + const l2BridgingManagement = new BridgingManagement( + l2DeployScript.tokenBridgeProxyAddress, + optDeployer, + { logger: console } + ); + + await l1BridgingManagement.setup(deploymentConfig.l1); + await l2BridgingManagement.setup(deploymentConfig.l2); +} + +main().catch((error) => { + console.error(error); + process.exitCode = 1; +}); diff --git a/scripts/optimism/upgrade.ts b/scripts/optimism/upgrade.ts new file mode 100644 index 00000000..c4d969b7 --- /dev/null +++ b/scripts/optimism/upgrade.ts @@ -0,0 +1,109 @@ +import env from "../../utils/env"; +import prompt from "../../utils/prompt"; +import network from "../../utils/network"; +import deployment from "../../utils/deployment"; +import { BridgingManagement } from "../../utils/bridging-management"; +import upgrade from "../../utils/optimism/upgrade"; + +async function main() { + const networkName = env.network(); + const ethOptNetwork = network.multichain(["eth", "opt"], networkName); + + const [ethDeployer] = ethOptNetwork.getSigners(env.privateKey(), { + forking: env.forking(), + }); + const [, optDeployer] = ethOptNetwork.getSigners( + env.string("OPT_DEPLOYER_PRIVATE_KEY"), + { + forking: env.forking(), + } + ); + + const deploymentConfig = deployment.loadMultiChainDeploymentConfig(); + + const [l1DeployScript, l2DeployScript] = await upgrade (networkName, { logger: console }) + .upgradeScript( + { + l1TokenNonRebasable: deploymentConfig.l1TokenNonRebasable, + l1TokenRebasable: deploymentConfig.l1RebasableToken, + accountingOracle: deploymentConfig.accountingOracle, + l2GasLimitForPushingTokenRate: deploymentConfig.l2GasLimitForPushingTokenRate, + + l1TokenBridge: deploymentConfig.l1TokenBridge, + + deployer: ethDeployer, + admins: { + proxy: deploymentConfig.l1.proxyAdmin, + bridge: ethDeployer.address + }, + contractsShift: 0, + }, + { + tokenRateOracle: { + constructor: { + tokenRateOutdatedDelay: deploymentConfig.tokenRateOutdatedDelay, + maxAllowedL2ToL1ClockLag: deploymentConfig.maxAllowedL2ToL1ClockLag, + maxAllowedTokenRateDeviationPerDayBp: deploymentConfig.maxAllowedTokenRateDeviationPerDayBp, + oldestRateAllowedInPauseTimeSpan: deploymentConfig.oldestRateAllowedInPauseTimeSpan, + maxAllowedTimeBetweenTokenRateUpdates: deploymentConfig.maxAllowedTimeBetweenTokenRateUpdates + }, + initialize: { + tokenRate: deploymentConfig.tokenRateValue, + l1Timestamp: deploymentConfig.tokenRateL1Timestamp + } + }, + + l2TokenBridge: deploymentConfig.l2TokenBridge, + + l2TokenNonRebasable: { + address: deploymentConfig.l2TokenNonRebasable, + version: "1" + }, + + l2TokenRebasable: { + version: "1" + }, + + deployer: optDeployer, + admins: { + proxy: deploymentConfig.l2.proxyAdmin, + bridge: optDeployer.address, + }, + contractsShift: 0, + } + ); + + await deployment.printMultiChainDeploymentConfig( + "Upgrade Optimism Bridge", + ethDeployer, + optDeployer, + deploymentConfig, + l1DeployScript, + l2DeployScript + ); + + await prompt.proceed(); + + await l1DeployScript.run(); + await l2DeployScript.run(); + + const l1BridgingManagement = new BridgingManagement( + l1DeployScript.bridgeProxyAddress, + ethDeployer, + { logger: console } + ); + + const l2BridgingManagement = new BridgingManagement( + l2DeployScript.tokenBridgeProxyAddress, + optDeployer, + { logger: console } + ); + + await l1BridgingManagement.setup(deploymentConfig.l1); + await l2BridgingManagement.setup(deploymentConfig.l2); +} + +main().catch((error) => { + console.error(error); + process.exitCode = 1; +}); diff --git a/test/bridge-executor/optimism.integration.test.ts b/test/bridge-executor/optimism.integration.test.ts index c573d5e2..7159ce59 100644 --- a/test/bridge-executor/optimism.integration.test.ts +++ b/test/bridge-executor/optimism.integration.test.ts @@ -13,11 +13,12 @@ import { wei } from "../../utils/wei"; import optimism from "../../utils/optimism"; import testing, { scenario } from "../../utils/testing"; import { BridgingManagerRole } from "../../utils/bridging-management"; +import { getExchangeRate } from "../../utils/testing/helpers"; import env from "../../utils/env"; import network from "../../utils/network"; import { getBridgeExecutorParams } from "../../utils/bridge-executor"; -import deploymentAll from "../../utils/optimism/deploymentAllFromScratch"; +import deploymentAll from "../../utils/optimism/deployment"; scenario("Optimism :: Bridge Executor integration test", ctxFactory) .step("Activate L2 bridge", async (ctx) => { @@ -204,6 +205,14 @@ async function ctxFactory() { .multichain(["eth", "opt"], networkName) .getProviders({ forking: true }); + const tokenRateDecimals = BigNumber.from(27); + const totalPooledEther = BigNumber.from('9309904612343950493629678'); + const totalShares = BigNumber.from('7975822843597609202337218'); + const maxAllowedL2ToL1ClockLag = BigNumber.from(86400); + const maxAllowedTokenRateDeviationPerDay = BigNumber.from(500); + const oldestRateAllowedInPauseTimeSpan = BigNumber.from(86400*3); + const maxAllowedTimeBetweenTokenRateUpdates = BigNumber.from(3600); + const exchangeRate = getExchangeRate(tokenRateDecimals, totalPooledEther, totalShares); const l1Deployer = testing.accounts.deployer(l1Provider); const l2Deployer = testing.accounts.deployer(l2Provider); @@ -219,7 +228,8 @@ async function ctxFactory() { l1Token.address, "Test Token", "TT", - BigNumber.from('1164454276599657236000000000') + totalPooledEther, + totalShares ); const accountingOracle = await new AccountingOracleStub__factory(l1Deployer).deploy(1,2,3); @@ -245,28 +255,46 @@ async function ctxFactory() { const [, optDeployScript] = await deploymentAll( networkName ).deployAllScript( - l1Token.address, - l1TokenRebasable.address, - accountingOracle.address, { + l1TokenNonRebasable: l1Token.address, + l1TokenRebasable: l1TokenRebasable.address, + accountingOracle: accountingOracle.address, + l2GasLimitForPushingTokenRate: BigNumber.from(300_000), deployer: l1Deployer, admins: { proxy: l1Deployer.address, bridge: l1Deployer.address }, - contractsShift: 0 + contractsShift: 0, }, { + tokenRateOracle: { + tokenRateOutdatedDelay: BigNumber.from(1000), + maxAllowedL2ToL1ClockLag: maxAllowedL2ToL1ClockLag, + maxAllowedTokenRateDeviationPerDayBp: maxAllowedTokenRateDeviationPerDay, + oldestRateAllowedInPauseTimeSpan: oldestRateAllowedInPauseTimeSpan, + maxAllowedTimeBetweenTokenRateUpdates: maxAllowedTimeBetweenTokenRateUpdates, + tokenRate: exchangeRate, + l1Timestamp: BigNumber.from('1000') + }, + l2TokenNonRebasable: { + name: "wstETH", + symbol: "WST", + version: "1", + decimals: 18 + }, + l2TokenRebasable: { + name: "stETH", + symbol: "ST", + version: "1", + decimals: 18 + }, deployer: l2Deployer, admins: { proxy: govBridgeExecutor.address, bridge: govBridgeExecutor.address, }, contractsShift: 0, - tokenRateOracle: { - tokenRate: BigNumber.from(10), - l1Timestamp:BigNumber.from(2) - } } ); diff --git a/test/optimism/bridging-non-rebasable.integration.test.ts b/test/optimism/bridging-non-rebasable.integration.test.ts index 428a25b9..2026f7a4 100644 --- a/test/optimism/bridging-non-rebasable.integration.test.ts +++ b/test/optimism/bridging-non-rebasable.integration.test.ts @@ -583,7 +583,6 @@ function ctxFactory(depositAmount: BigNumber, withdrawalAmount: BigNumber) { const tokenRateDecimals = BigNumber.from(27); const totalPooledEther = BigNumber.from('9309904612343950493629678'); const totalShares = BigNumber.from('7975822843597609202337218'); - const tokenRate = getExchangeRate(tokenRateDecimals, totalPooledEther, totalShares); const { l1Provider, @@ -596,6 +595,7 @@ function ctxFactory(depositAmount: BigNumber, withdrawalAmount: BigNumber) { const l1Snapshot = await l1Provider.send("evm_snapshot", []); const l2Snapshot = await l2Provider.send("evm_snapshot", []); + const tokenRate = await contracts.l1Token.getStETHByWstETH(BigNumber.from(10).pow(tokenRateDecimals)); await optimism.testing(networkName).stubL1CrossChainMessengerContract(); diff --git a/test/optimism/bridging-rebasable.integration.test.ts b/test/optimism/bridging-rebasable.integration.test.ts index a6656157..dab782f9 100644 --- a/test/optimism/bridging-rebasable.integration.test.ts +++ b/test/optimism/bridging-rebasable.integration.test.ts @@ -11,8 +11,7 @@ import { nonRebasableFromRebasableL1, nonRebasableFromRebasableL2, rebasableFromNonRebasableL1, - rebasableFromNonRebasableL2, - getExchangeRate + rebasableFromNonRebasableL2 } from "../../utils/testing/helpers"; type ContextType = Awaited>> @@ -95,7 +94,7 @@ function bridgingTestsSuit(scenarioInstance: ScenarioTest) { const { accountA: tokenHolderA } = ctx.accounts; const { depositAmountOfRebasableToken, tokenRate } = ctx.constants; - /// warp L1 + /// wrap L1 const depositAmountNonRebasable = nonRebasableFromRebasableL1( depositAmountOfRebasableToken, ctx.constants.totalPooledEther, @@ -103,7 +102,7 @@ function bridgingTestsSuit(scenarioInstance: ScenarioTest) { ); console.log("depositAmountOfRebasableToken=",depositAmountOfRebasableToken); - console.log("warp L1: depositAmountNonRebasable=",depositAmountNonRebasable); + console.log("wrap L1: depositAmountNonRebasable=",depositAmountNonRebasable); await l1TokenRebasable .connect(tokenHolderA.l1Signer) @@ -709,8 +708,6 @@ function ctxFactory( return async () => { const networkName = env.network("TESTING_OPT_NETWORK", "mainnet"); - const exchangeRate = getExchangeRate(tokenRateDecimals, totalPooledEther, totalShares); - const { l1Provider, l2Provider, @@ -722,6 +719,8 @@ function ctxFactory( const l1Snapshot = await l1Provider.send("evm_snapshot", []); const l2Snapshot = await l2Provider.send("evm_snapshot", []); + const exchangeRate = await contracts.l1Token.getStETHByWstETH(BigNumber.from(10).pow(tokenRateDecimals)); + await optimism.testing(networkName).stubL1CrossChainMessengerContract(); const accountA = testing.accounts.accountA(l1Provider, l2Provider); diff --git a/test/optimism/pushingTokenRate.e2e.test.ts b/test/optimism/pushingTokenRate.e2e.test.ts index a7cb3cc7..9fa24bcc 100644 --- a/test/optimism/pushingTokenRate.e2e.test.ts +++ b/test/optimism/pushingTokenRate.e2e.test.ts @@ -79,7 +79,7 @@ async function loadDeployedContracts( ) { return { l1Token: ERC20WrapperStub__factory.connect( - testingUtils.env.OPT_L1_TOKEN(), + testingUtils.env.OPT_L1_NON_REBASABLE_TOKEN(), l1SignerOrProvider ), tokenRateNotifier: TokenRateNotifier__factory.connect( diff --git a/utils/deployment.ts b/utils/deployment.ts index ea444a39..d9fc7406 100644 --- a/utils/deployment.ts +++ b/utils/deployment.ts @@ -1,5 +1,5 @@ import chalk from "chalk"; -import { Wallet } from "ethers"; +import { BigNumber, Wallet } from "ethers"; import env from "./env"; import { DeployScript } from "./deployment/DeployScript"; @@ -10,16 +10,29 @@ interface ChainDeploymentConfig extends BridgingManagerSetupConfig { } interface MultiChainDeploymentConfig { - l1Token: string; + /// L1 + l1TokenNonRebasable: string; l1RebasableToken: string; accountingOracle: string; - l1OpStackTokenRatePusher: string; - l2GasLimitForPushingTokenRate: number; - tokenRateOutdatedDelay: number; + l2GasLimitForPushingTokenRate: BigNumber; l1TokenBridge: string; + + /// L2 + /// Oracle + tokenRateOutdatedDelay: BigNumber; + maxAllowedL2ToL1ClockLag: BigNumber; + maxAllowedTokenRateDeviationPerDayBp: BigNumber; + oldestRateAllowedInPauseTimeSpan: BigNumber; + maxAllowedTimeBetweenTokenRateUpdates: BigNumber; + tokenRateValue: BigNumber; + tokenRateL1Timestamp: BigNumber; + + /// wstETH address to upgrade + l2TokenNonRebasable: string; + + /// bridge l2TokenBridge: string; - l2Token: string; - l2TokenRateOracle: string; + govBridgeExecutor: string; l1: ChainDeploymentConfig; l2: ChainDeploymentConfig; @@ -27,16 +40,26 @@ interface MultiChainDeploymentConfig { export function loadMultiChainDeploymentConfig(): MultiChainDeploymentConfig { return { - l1Token: env.address("TOKEN"), - l1RebasableToken: env.address("REBASABLE_TOKEN"), + /// L1 Part + l1TokenNonRebasable: env.address("L1_NON_REBASABLE_TOKEN"), + l1RebasableToken: env.address("L1_REBASABLE_TOKEN"), accountingOracle: env.address("ACCOUNTING_ORACLE"), - l1OpStackTokenRatePusher: env.address("L1_OP_STACK_TOKEN_RATE_PUSHER"), - l2GasLimitForPushingTokenRate: Number(env.string("L2_GAS_LIMIT_FOR_PUSHING_TOKEN_RATE")), - tokenRateOutdatedDelay: Number(env.string("TOKEN_RATE_OUTDATED_DELAY")), + l2GasLimitForPushingTokenRate: BigNumber.from(env.string("L2_GAS_LIMIT_FOR_PUSHING_TOKEN_RATE")), l1TokenBridge: env.address("L1_TOKEN_BRIDGE"), + + /// L2 Part + /// TokenRateOracle + tokenRateOutdatedDelay: BigNumber.from(env.string("TOKEN_RATE_OUTDATED_DELAY")), + maxAllowedL2ToL1ClockLag: BigNumber.from(env.string("MAX_ALLOWED_L2_TO_L1_CLOCK_LAG")), + maxAllowedTokenRateDeviationPerDayBp: BigNumber.from(env.string("MAX_ALLOWED_TOKEN_RATE_DEVIATION_PER_DAY_BP")), + oldestRateAllowedInPauseTimeSpan: BigNumber.from(env.string("OLDEST_RATE_ALLOWED_IN_PAUSE_TIME_SPAN")), + maxAllowedTimeBetweenTokenRateUpdates: BigNumber.from(env.string("MAX_ALLOWED_TIME_BETWEEN_TOKEN_RATE_UPDATES")), + tokenRateValue: BigNumber.from(env.string("TOKEN_RATE")), + tokenRateL1Timestamp: BigNumber.from(env.string("TOKEN_RATE_L1_TIMESTAMP")), + + l2TokenNonRebasable: env.address("L2_TOKEN_NON_REBASABLE"), l2TokenBridge: env.address("L2_TOKEN_BRIDGE"), - l2Token: env.address("L2_TOKEN"), - l2TokenRateOracle: env.address("L2_TOKEN_RATE_ORACLE"), + govBridgeExecutor: env.address("GOV_BRIDGE_EXECUTOR"), l1: { proxyAdmin: env.address("L1_PROXY_ADMIN"), @@ -61,6 +84,12 @@ export function loadMultiChainDeploymentConfig(): MultiChainDeploymentConfig { }; } +export async function printDeploymentConfig() { + const pad = " ".repeat(4); + console.log(`${pad}· Network: ${env.string("NETWORK")}`); + console.log(`${pad}· Forking: ${env.bool("FORKING")}`); +} + export async function printMultiChainDeploymentConfig( title: string, l1Deployer: Wallet, @@ -69,8 +98,13 @@ export async function printMultiChainDeploymentConfig( l1DeployScript: DeployScript, l2DeployScript: DeployScript ) { - const { l1Token, l1RebasableToken, l1, l2 } = deploymentParams; - console.log(chalk.bold(`${title} :: ${chalk.underline(l1Token)} :: ${chalk.underline(l1RebasableToken)}\n`)); + const { l1TokenNonRebasable, l1RebasableToken, l1, l2 } = deploymentParams; + console.log(chalk.bold(`${title} :: ${chalk.underline(l1TokenNonRebasable)} :: ${chalk.underline(l1RebasableToken)}\n`)); + + console.log(chalk.bold(" · Deployment Params:")); + await printDeploymentConfig(); + console.log(); + console.log(chalk.bold(" · L1 Deployment Params:")); await printChainDeploymentConfig(l1Deployer, l1); console.log(); diff --git a/utils/optimism/deployment.ts b/utils/optimism/deployment.ts new file mode 100644 index 00000000..3464ed08 --- /dev/null +++ b/utils/optimism/deployment.ts @@ -0,0 +1,369 @@ +import { assert } from "chai"; +import { BigNumber, Wallet } from "ethers"; +import addresses from "./addresses"; +import { OptDeploymentOptions, DeployScriptParams } from "./types"; +import network, { NetworkName } from "../network"; +import { DeployScript, Logger } from "../deployment/DeployScript"; +import { + ERC20BridgedPermit__factory, + ERC20RebasableBridgedPermit__factory, + L1LidoTokensBridge__factory, + L2ERC20ExtendedTokensBridge__factory, + OssifiableProxy__factory, + TokenRateOracle__factory, + TokenRateNotifier__factory, + OpStackTokenRatePusher__factory, + IERC20Metadata__factory +} from "../../typechain"; + +interface OptL1DeployScriptParams extends DeployScriptParams { + l1TokenNonRebasable: string; + l1TokenRebasable: string; + accountingOracle: string; + l2GasLimitForPushingTokenRate: BigNumber; +} + +interface OptL2DeployScriptParams extends DeployScriptParams { + l2TokenNonRebasable: { + name?: string; + symbol?: string; + version: string; + decimals?: number; + }; + l2TokenRebasable: { + name?: string; + symbol?: string; + version: string; + decimals?: number; + }; + tokenRateOracle: { + tokenRateOutdatedDelay: BigNumber; + maxAllowedL2ToL1ClockLag: BigNumber; + maxAllowedTokenRateDeviationPerDayBp: BigNumber; + oldestRateAllowedInPauseTimeSpan: BigNumber; + maxAllowedTimeBetweenTokenRateUpdates: BigNumber; + tokenRate: BigNumber; + l1Timestamp: BigNumber; + } +} + +export class L1DeployAllScript extends DeployScript { + + constructor( + deployer: Wallet, + bridgeImplAddress: string, + bridgeProxyAddress: string, + tokenRateNotifierImplAddress: string, + opStackTokenRatePusherImplAddress: string, + logger?: Logger + ) { + super(deployer, logger); + this.bridgeImplAddress = bridgeImplAddress; + this.bridgeProxyAddress = bridgeProxyAddress; + this.tokenRateNotifierImplAddress = tokenRateNotifierImplAddress; + this.opStackTokenRatePusherImplAddress = opStackTokenRatePusherImplAddress; + } + + public bridgeImplAddress: string; + public bridgeProxyAddress: string; + public tokenRateNotifierImplAddress: string; + public opStackTokenRatePusherImplAddress: string; +} + +export class L2DeployAllScript extends DeployScript { + + constructor( + deployer: Wallet, + tokenImplAddress: string, + tokenProxyAddress: string, + tokenRebasableImplAddress: string, + tokenRebasableProxyAddress: string, + tokenBridgeImplAddress: string, + tokenBridgeProxyAddress: string, + tokenRateOracleImplAddress: string, + tokenRateOracleProxyAddress: string, + logger?: Logger + ) { + super(deployer, logger); + this.tokenImplAddress = tokenImplAddress; + this.tokenProxyAddress = tokenProxyAddress; + this.tokenRebasableImplAddress = tokenRebasableImplAddress; + this.tokenRebasableProxyAddress = tokenRebasableProxyAddress; + this.tokenBridgeImplAddress = tokenBridgeImplAddress; + this.tokenBridgeProxyAddress = tokenBridgeProxyAddress; + this.tokenRateOracleImplAddress = tokenRateOracleImplAddress; + this.tokenRateOracleProxyAddress = tokenRateOracleProxyAddress; + } + + public tokenImplAddress: string; + public tokenProxyAddress: string; + public tokenRebasableImplAddress: string; + public tokenRebasableProxyAddress: string; + public tokenBridgeImplAddress: string; + public tokenBridgeProxyAddress: string; + public tokenRateOracleImplAddress: string; + public tokenRateOracleProxyAddress: string; +} + +/// Deploy all from scratch +/// L1 part +/// L1LidoTokensBridge + Proxy +/// TokenRateNotifier +/// OpStackTokenRatePusher +/// L2 part +/// TokenRateOracle + Proxy +/// ERC20BridgedPermit + Proxy +/// ERC20RebasableBridgedPermit + Proxy +/// L2ERC20ExtendedTokensBridge + Proxy +export default function deploymentAll( + networkName: NetworkName, + options: OptDeploymentOptions = {} +) { + const optAddresses = addresses(networkName, options); + return { + async deployAllScript( + l1Params: OptL1DeployScriptParams, + l2Params: OptL2DeployScriptParams, + ): Promise<[L1DeployAllScript, L2DeployAllScript]> { + + const [ + expectedL1TokenBridgeImplAddress, + expectedL1TokenBridgeProxyAddress, + expectedL1TokenRateNotifierImplAddress, + expectedL1OpStackTokenRatePusherImplAddress, + ] = await network.predictAddresses(l1Params.deployer, l1Params.contractsShift + 4); + + const [ + expectedL2TokenRateOracleImplAddress, + expectedL2TokenRateOracleProxyAddress, + expectedL2TokenImplAddress, + expectedL2TokenProxyAddress, + expectedL2TokenRebasableImplAddress, + expectedL2TokenRebasableProxyAddress, + expectedL2TokenBridgeImplAddress, + expectedL2TokenBridgeProxyAddress + ] = await network.predictAddresses(l2Params.deployer, l2Params.contractsShift + 8); + + const l1DeployScript = new L1DeployAllScript( + l1Params.deployer, + expectedL1TokenBridgeImplAddress, + expectedL1TokenBridgeProxyAddress, + expectedL1TokenRateNotifierImplAddress, + expectedL1OpStackTokenRatePusherImplAddress, + options?.logger + ) + .addStep({ + factory: L1LidoTokensBridge__factory, + args: [ + optAddresses.L1CrossDomainMessenger, + expectedL2TokenBridgeProxyAddress, + l1Params.l1TokenNonRebasable, + l1Params.l1TokenRebasable, + expectedL2TokenProxyAddress, + expectedL2TokenRebasableProxyAddress, + l1Params.accountingOracle, + options?.overrides, + ], + afterDeploy: (c) => + assert.equal(c.address, expectedL1TokenBridgeImplAddress), + }) + .addStep({ + factory: OssifiableProxy__factory, + args: [ + expectedL1TokenBridgeImplAddress, + l1Params.admins.proxy, + L1LidoTokensBridge__factory.createInterface().encodeFunctionData( + "initialize", + [l1Params.admins.bridge] + ), + options?.overrides, + ], + afterDeploy: (c) => + assert.equal(c.address, expectedL1TokenBridgeProxyAddress), + }) + .addStep({ + factory: TokenRateNotifier__factory, + args: [ + l1Params.deployer.address, + options?.overrides, + ], + afterDeploy: (c) => + assert.equal(c.address, expectedL1TokenRateNotifierImplAddress), + }) + .addStep({ + factory: OpStackTokenRatePusher__factory, + args: [ + optAddresses.L1CrossDomainMessenger, + l1Params.l1TokenNonRebasable, + l1Params.accountingOracle, + expectedL2TokenRateOracleProxyAddress, + l1Params.l2GasLimitForPushingTokenRate, + options?.overrides, + ], + afterDeploy: (c) => + assert.equal(c.address, expectedL1OpStackTokenRatePusherImplAddress), + }); + + const l1TokenNonRebasableInfo = IERC20Metadata__factory.connect( + l1Params.l1TokenNonRebasable, + l1Params.deployer + ); + + const l1TokenRebasableInfo = IERC20Metadata__factory.connect( + l1Params.l1TokenRebasable, + l1Params.deployer + ); + + const [ + l2TokenNonRebasableDecimals, l2TokenNonRebasableName, l2TokenNonRebasableSymbol, + l2TokenRebasableDecimals, l2TokenRebasableName, l2TokenRebasableSymbol + ] = await Promise.all([ + l1TokenNonRebasableInfo.decimals(), + l2Params.l2TokenNonRebasable?.name ?? l1TokenNonRebasableInfo.name(), + l2Params.l2TokenNonRebasable?.symbol ?? l1TokenNonRebasableInfo.symbol(), + l1TokenRebasableInfo.decimals(), + l2Params.l2TokenRebasable?.name ?? l1TokenRebasableInfo.name(), + l2Params.l2TokenRebasable?.symbol ?? l1TokenRebasableInfo.symbol(), + ]); + + const l2DeployScript = new L2DeployAllScript( + l2Params.deployer, + expectedL2TokenImplAddress, + expectedL2TokenProxyAddress, + expectedL2TokenRebasableImplAddress, + expectedL2TokenRebasableProxyAddress, + expectedL2TokenBridgeImplAddress, + expectedL2TokenBridgeProxyAddress, + expectedL2TokenRateOracleImplAddress, + expectedL2TokenRateOracleProxyAddress, + options?.logger + ) + .addStep({ + factory: TokenRateOracle__factory, + args: [ + optAddresses.L2CrossDomainMessenger, + expectedL2TokenBridgeProxyAddress, + expectedL1OpStackTokenRatePusherImplAddress, + l2Params.tokenRateOracle.tokenRateOutdatedDelay, + l2Params.tokenRateOracle.maxAllowedL2ToL1ClockLag, + l2Params.tokenRateOracle.maxAllowedTokenRateDeviationPerDayBp, + l2Params.tokenRateOracle.oldestRateAllowedInPauseTimeSpan, + l2Params.tokenRateOracle.maxAllowedTimeBetweenTokenRateUpdates, + options?.overrides, + ], + afterDeploy: (c) => + assert.equal(c.address, expectedL2TokenRateOracleImplAddress), + }) + .addStep({ + factory: OssifiableProxy__factory, + args: [ + expectedL2TokenRateOracleImplAddress, + l2Params.admins.proxy, + TokenRateOracle__factory.createInterface().encodeFunctionData( + "initialize", + [ + l2Params.admins.bridge, + l2Params.tokenRateOracle.tokenRate, + l2Params.tokenRateOracle.l1Timestamp + ] + ), + options?.overrides, + ], + afterDeploy: (c) => + assert.equal(c.address, expectedL2TokenRateOracleProxyAddress), + }) + .addStep({ + factory: ERC20BridgedPermit__factory, + args: [ + l2TokenNonRebasableName, + l2TokenNonRebasableSymbol, + l2Params.l2TokenNonRebasable.version, + l2TokenNonRebasableDecimals, + expectedL2TokenBridgeProxyAddress, + options?.overrides, + ], + afterDeploy: (c) => + assert.equal(c.address, expectedL2TokenImplAddress), + }) + .addStep({ + factory: OssifiableProxy__factory, + args: [ + expectedL2TokenImplAddress, + l2Params.admins.proxy, + ERC20BridgedPermit__factory.createInterface().encodeFunctionData( + "initialize", + [ + l2TokenNonRebasableName, + l2TokenNonRebasableSymbol, + l2Params.l2TokenNonRebasable.version + ] + ), + options?.overrides, + ], + afterDeploy: (c) => + assert.equal(c.address, expectedL2TokenProxyAddress), + }) + .addStep({ + factory: ERC20RebasableBridgedPermit__factory, + args: [ + l2TokenRebasableName, + l2TokenRebasableSymbol, + l2Params.l2TokenRebasable.version, + l2TokenRebasableDecimals, + expectedL2TokenProxyAddress, + expectedL2TokenRateOracleProxyAddress, + expectedL2TokenBridgeProxyAddress, + options?.overrides, + ], + afterDeploy: (c) => + assert.equal(c.address, expectedL2TokenRebasableImplAddress), + }) + .addStep({ + factory: OssifiableProxy__factory, + args: [ + expectedL2TokenRebasableImplAddress, + l2Params.admins.proxy, + ERC20RebasableBridgedPermit__factory.createInterface().encodeFunctionData( + "initialize", + [ + l2TokenRebasableName, + l2TokenRebasableSymbol, + l2Params.l2TokenRebasable.version + ] + ), + options?.overrides, + ], + afterDeploy: (c) => + assert.equal(c.address, expectedL2TokenRebasableProxyAddress), + }) + .addStep({ + factory: L2ERC20ExtendedTokensBridge__factory, + args: [ + optAddresses.L2CrossDomainMessenger, + expectedL1TokenBridgeProxyAddress, + l1Params.l1TokenNonRebasable, + l1Params.l1TokenRebasable, + expectedL2TokenProxyAddress, + expectedL2TokenRebasableProxyAddress, + options?.overrides, + ], + afterDeploy: (c) => + assert.equal(c.address, expectedL2TokenBridgeImplAddress), + }) + .addStep({ + factory: OssifiableProxy__factory, + args: [ + expectedL2TokenBridgeImplAddress, + l2Params.admins.proxy, + L2ERC20ExtendedTokensBridge__factory.createInterface().encodeFunctionData( + "initialize", + [l2Params.admins.bridge] + ), + options?.overrides, + ], + }); + + return [l1DeployScript as L1DeployAllScript, l2DeployScript as L2DeployAllScript]; + }, + }; +} diff --git a/utils/optimism/deploymentNewImplementations.ts b/utils/optimism/deploymentNewImplementations.ts deleted file mode 100644 index d2080086..00000000 --- a/utils/optimism/deploymentNewImplementations.ts +++ /dev/null @@ -1,239 +0,0 @@ -import { assert } from "chai"; -import { BigNumber, Wallet } from "ethers"; -import addresses from "./addresses"; -import { OptDeploymentOptions, DeployScriptParams } from "./types"; -import network, { NetworkName } from "../network"; -import { DeployScript, Logger } from "../deployment/DeployScript"; -import { - ERC20BridgedPermit__factory, - ERC20RebasableBridgedPermit__factory, - IERC20Metadata__factory, - L1LidoTokensBridge__factory, - L2ERC20ExtendedTokensBridge__factory, - OssifiableProxy__factory, - TokenRateOracle__factory -} from "../../typechain"; - -interface OptL1DeployScriptParams extends DeployScriptParams { - tokenProxyAddress: string; - tokenRebasableProxyAddress: string; - opStackTokenRatePusherImplAddress: string; - tokenBridgeProxyAddress: string; - deployer: Wallet; - admins: { - proxy: string; - bridge: string - }; - contractsShift: number; -} - -interface OptL2DeployScriptParams extends DeployScriptParams { - tokenBridgeProxyAddress: string; - tokenProxyAddress: string; - tokenRateOracle: { - proxyAddress: string; - rateOutdatedDelay: BigNumber; - maxAllowedL2ToL1ClockLag: BigNumber; - maxAllowedTokenRateDeviationPerDay: BigNumber; - } - token: { - name: string; - symbol: string; - version: string; - }; - tokenRebasable: { - name: string; - symbol: string; - version: string; - }; -} - -export class BridgeL1DeployScript extends DeployScript { - - constructor( - deployer: Wallet, - bridgeImplAddress: string, - logger?: Logger - ) { - super(deployer, logger); - this.bridgeImplAddress = bridgeImplAddress; - } - - public bridgeImplAddress: string; -} - -export class BridgeL2DeployScript extends DeployScript { - - constructor( - deployer: Wallet, - tokenImplAddress: string, - tokenRebasableImplAddress: string, - tokenRebasableProxyAddress: string, - tokenBridgeImplAddress: string, - tokenRateOracleImplAddress: string, - logger?: Logger - ) { - super(deployer, logger); - this.tokenImplAddress = tokenImplAddress; - this.tokenRebasableImplAddress = tokenRebasableImplAddress; - this.tokenRebasableProxyAddress = tokenRebasableProxyAddress; - this.tokenBridgeImplAddress = tokenBridgeImplAddress; - this.tokenRateOracleImplAddress = tokenRateOracleImplAddress; - } - - public tokenImplAddress: string; - public tokenRebasableImplAddress: string; - public tokenRebasableProxyAddress: string; - public tokenBridgeImplAddress: string; - public tokenRateOracleImplAddress: string; -} - -/// deploys -/// - new L1Bridge Impl -/// - new L2Bridge Impl -/// - RebasableToken(stETH) Impl and Proxy (because it was never deployed before) -/// - Non-rebasable token (wstETH) new Impl with Permissions -export default function deploymentNewImplementations( - networkName: NetworkName, - options: OptDeploymentOptions = {} -) { - const optAddresses = addresses(networkName, options); - return { - async deployScript( - l1Params: OptL1DeployScriptParams, - l2Params: OptL2DeployScriptParams, - ): Promise<[BridgeL1DeployScript, BridgeL2DeployScript]> { - - const [ - expectedL1TokenBridgeImplAddress, - ] = await network.predictAddresses(l1Params.deployer, l1Params.contractsShift + 1); - - const [ - expectedL2TokenImplAddress, - expectedL2TokenRebasableImplAddress, - expectedL2TokenRebasableProxyAddress, - expectedL2TokenBridgeImplAddress, - expectedL2TokenRateOracleImplAddress - ] = await network.predictAddresses(l2Params.deployer, l2Params.contractsShift + 5); - - const l1DeployScript = new BridgeL1DeployScript( - l1Params.deployer, - expectedL1TokenBridgeImplAddress, - options?.logger - ) - .addStep({ - factory: L1LidoTokensBridge__factory, - args: [ - optAddresses.L1CrossDomainMessenger, - l2Params.tokenBridgeProxyAddress, - l1Params.tokenProxyAddress, - l1Params.tokenRebasableProxyAddress, - l2Params.tokenProxyAddress, - expectedL2TokenRebasableProxyAddress, - options?.overrides, - ], - afterDeploy: (c) => - assert.equal(c.address, expectedL1TokenBridgeImplAddress), - }); - - const l1TokenInfo = IERC20Metadata__factory.connect( - l1Params.tokenProxyAddress, - l1Params.deployer - ); - - const l1TokenRebasableInfo = IERC20Metadata__factory.connect( - l1Params.tokenRebasableProxyAddress, - l1Params.deployer - ); - const [decimals, l2TokenName, l2TokenSymbol, l2TokenRebasableName, l2TokenRebasableSymbol] = await Promise.all([ - l1TokenInfo.decimals(), - l2Params.token?.name ?? l1TokenInfo.name(), - l2Params.token?.symbol ?? l1TokenInfo.symbol(), - l2Params.tokenRebasable?.name ?? l1TokenRebasableInfo.name(), - l2Params.tokenRebasable?.symbol ?? l1TokenRebasableInfo.symbol(), - ]); - - const l2DeployScript = new BridgeL2DeployScript( - l2Params.deployer, - expectedL2TokenImplAddress, - expectedL2TokenRebasableImplAddress, - expectedL2TokenRebasableProxyAddress, - expectedL2TokenBridgeImplAddress, - expectedL2TokenRateOracleImplAddress, - options?.logger - ) - .addStep({ - factory: ERC20BridgedPermit__factory, - args: [ - l2TokenName, - l2TokenSymbol, - l2Params.token.version, - decimals, - l2Params.tokenBridgeProxyAddress, - options?.overrides, - ], - afterDeploy: (c) => - assert.equal(c.address, expectedL2TokenImplAddress), - }) - .addStep({ - factory: ERC20RebasableBridgedPermit__factory, - args: [ - l2TokenRebasableName, - l2TokenRebasableSymbol, - l2Params.tokenRebasable.version, - decimals, - l2Params.tokenProxyAddress, - l2Params.tokenRateOracle.proxyAddress, - l2Params.tokenBridgeProxyAddress, - options?.overrides, - ], - afterDeploy: (c) => - assert.equal(c.address, expectedL2TokenRebasableImplAddress), - }) - .addStep({ - factory: OssifiableProxy__factory, - args: [ - expectedL2TokenRebasableImplAddress, - l2Params.admins.proxy, - ERC20RebasableBridgedPermit__factory.createInterface().encodeFunctionData( - "initialize", - [l2TokenRebasableName, l2TokenRebasableSymbol, l2Params.tokenRebasable.version] - ), - options?.overrides, - ], - afterDeploy: (c) => - assert.equal(c.address, expectedL2TokenRebasableProxyAddress), - }) - .addStep({ - factory: L2ERC20ExtendedTokensBridge__factory, - args: [ - optAddresses.L2CrossDomainMessenger, - l1Params.tokenBridgeProxyAddress, - l1Params.tokenProxyAddress, - l1Params.tokenRebasableProxyAddress, - l2Params.tokenProxyAddress, - expectedL2TokenRebasableProxyAddress, - options?.overrides, - ], - afterDeploy: (c) => - assert.equal(c.address, expectedL2TokenBridgeImplAddress), - }) - .addStep({ - factory: TokenRateOracle__factory, - args: [ - optAddresses.L2CrossDomainMessenger, - l2Params.tokenBridgeProxyAddress, - l1Params.opStackTokenRatePusherImplAddress, - l2Params.tokenRateOracle.rateOutdatedDelay, - l2Params.tokenRateOracle.maxAllowedL2ToL1ClockLag, - l2Params.tokenRateOracle.maxAllowedTokenRateDeviationPerDay, - options?.overrides, - ], - afterDeploy: (c) => - assert.equal(c.address, expectedL2TokenRateOracleImplAddress), - }); - - return [l1DeployScript as BridgeL1DeployScript, l2DeployScript as BridgeL2DeployScript]; - }, - }; -} diff --git a/utils/optimism/index.ts b/utils/optimism/index.ts index d9732d61..330f3644 100644 --- a/utils/optimism/index.ts +++ b/utils/optimism/index.ts @@ -1,6 +1,5 @@ import addresses from "./addresses"; import contracts from "./contracts"; -import deploymentOracle from "./deploymentOracle"; import testing from "./testing"; import messaging from "./messaging"; @@ -8,7 +7,6 @@ export default { testing, addresses, contracts, - messaging, - deploymentOracle + messaging }; diff --git a/utils/optimism/testing.ts b/utils/optimism/testing.ts index d2c5a0ee..f27e1f8b 100644 --- a/utils/optimism/testing.ts +++ b/utils/optimism/testing.ts @@ -15,11 +15,12 @@ import { L2ERC20ExtendedTokensBridge__factory, CrossDomainMessengerStub__factory, ERC20RebasableBridgedPermit__factory, - AccountingOracleStub__factory + AccountingOracleStub__factory, + IAccountingOracle__factory, } from "../../typechain"; import addresses from "./addresses"; import contracts from "./contracts"; -import deploymentAll from "./deploymentAllFromScratch"; +import deploymentAll from "./deployment"; import testingUtils from "../testing"; import { BridgingManagement } from "../bridging-management"; import network, { NetworkName, SignerOrProvider } from "../network"; @@ -160,7 +161,7 @@ async function loadDeployedBridges( ) { return { l1Token: ERC20WrapperStub__factory.connect( - testingUtils.env.OPT_L1_TOKEN(), + testingUtils.env.OPT_L1_NON_REBASABLE_TOKEN(), l1SignerOrProvider ), l1TokenRebasable: IERC20__factory.connect( @@ -168,14 +169,14 @@ async function loadDeployedBridges( l1SignerOrProvider ), accountingOracle: AccountingOracleStub__factory.connect( - testingUtils.env.OPT_L1_REBASABLE_TOKEN(), + testingUtils.env.OPT_L1_ACCOUNTING_ORACLE(), l1SignerOrProvider ), ...connectBridgeContracts( { tokenRateOracle: testingUtils.env.OPT_L2_TOKEN_RATE_ORACLE(), - l2Token: testingUtils.env.OPT_L2_TOKEN(), + l2Token: testingUtils.env.OPT_L2_NON_REBASABLE_TOKEN(), l2TokenRebasable: testingUtils.env.OPT_L2_REBASABLE_TOKEN(), l1LidoTokensBridge: testingUtils.env.OPT_L1_ERC20_TOKEN_BRIDGE(), l2ERC20ExtendedTokensBridge: testingUtils.env.OPT_L2_ERC20_TOKEN_BRIDGE(), @@ -193,6 +194,18 @@ async function deployTestBridge( ethProvider: JsonRpcProvider, optProvider: JsonRpcProvider ) { + const tokenRate = BigNumber.from(10).pow(27) + .mul(totalPooledEther) + .div(totalShares); + const genesisTime = 1; + const secondsPerSlot = 2; + const lastProcessingRefSlot = 3; + const maxAllowedL2ToL1ClockLag = BigNumber.from(86400); + const maxAllowedTokenRateDeviationPerDay = BigNumber.from(500); + const oldestRateAllowedInPauseTimeSpan = BigNumber.from(86400*3); + const maxAllowedTimeBetweenTokenRateUpdates = BigNumber.from(3600); + const tokenRateOutdatedDelay = BigNumber.from(86400); + const ethDeployer = testingUtils.accounts.deployer(ethProvider); const optDeployer = testingUtils.accounts.deployer(optProvider); @@ -209,31 +222,20 @@ async function deployTestBridge( totalShares ); - const tokenRate = BigNumber.from(10).pow(27).mul(totalPooledEther).div(totalShares); - const genesisTime = 1; - const secondsPerSlot = 2; - const lastProcessingRefSlot = 3; - const accountingOracle = await new AccountingOracleStub__factory(ethDeployer).deploy( genesisTime, secondsPerSlot, lastProcessingRefSlot ); - const maxAllowedL2ToL1ClockLag = BigNumber.from(86400); - const maxAllowedTokenRateDeviationPerDay = BigNumber.from(500); - const oldestRateAllowedInPauseTimeSpan = BigNumber.from(86400*3); - const maxAllowedTimeBetweenTokenRateUpdates = BigNumber.from(3600); - const tokenRateOutdatedDelay = BigNumber.from(86400); - const [ethDeployScript, optDeployScript] = await deploymentAll( networkName ).deployAllScript( { - l1Token: l1TokenNonRebasable.address, + l1TokenNonRebasable: l1TokenNonRebasable.address, l1TokenRebasable: l1TokenRebasable.address, accountingOracle: accountingOracle.address, - l2GasLimitForPushingTokenRate: 300_000, + l2GasLimitForPushingTokenRate: BigNumber.from(300_000), deployer: ethDeployer, admins: { proxy: ethDeployer.address, bridge: ethDeployer.address }, contractsShift: 0 diff --git a/utils/optimism/upgrade.ts b/utils/optimism/upgrade.ts new file mode 100644 index 00000000..a01cb864 --- /dev/null +++ b/utils/optimism/upgrade.ts @@ -0,0 +1,329 @@ +import { assert } from "chai"; +import { BigNumber, Wallet } from "ethers"; +import addresses from "./addresses"; +import { OptDeploymentOptions, DeployScriptParams } from "./types"; +import network, { NetworkName } from "../network"; +import { DeployScript, Logger } from "../deployment/DeployScript"; +import { + ERC20BridgedPermit__factory, + ERC20RebasableBridgedPermit__factory, + L1LidoTokensBridge__factory, + L2ERC20ExtendedTokensBridge__factory, + OssifiableProxy__factory, + TokenRateOracle__factory, + TokenRateNotifier__factory, + OpStackTokenRatePusher__factory, + IERC20Metadata__factory +} from "../../typechain"; + +interface OptL1UpgradeScriptParams extends DeployScriptParams { + l1TokenNonRebasable: string; + l1TokenRebasable: string; + accountingOracle: string; + l2GasLimitForPushingTokenRate: BigNumber; + l1TokenBridge: string; +} + +interface OptL2UpgradeScriptParams extends DeployScriptParams { + l2TokenBridge: string; + l2TokenNonRebasable: { + address: string; + name?: string; + symbol?: string; + version: string; + decimals?: BigNumber; + }; + l2TokenRebasable: { + name?: string; + symbol?: string; + version: string; + decimals?: BigNumber; + }; + tokenRateOracle: { + constructor: { + tokenRateOutdatedDelay: BigNumber; + maxAllowedL2ToL1ClockLag: BigNumber; + maxAllowedTokenRateDeviationPerDayBp: BigNumber; + oldestRateAllowedInPauseTimeSpan: BigNumber; + maxAllowedTimeBetweenTokenRateUpdates: BigNumber; + } + initialize: { + tokenRate: BigNumber; + l1Timestamp: BigNumber; + } + } +} + +export class L1UpgradeScript extends DeployScript { + + constructor( + deployer: Wallet, + bridgeProxyAddress: string, + bridgeImplAddress: string, + tokenRateNotifierImplAddress: string, + opStackTokenRatePusherImplAddress: string, + logger?: Logger + ) { + super(deployer, logger); + this.bridgeProxyAddress = bridgeProxyAddress; + this.bridgeImplAddress = bridgeImplAddress; + this.tokenRateNotifierImplAddress = tokenRateNotifierImplAddress; + this.opStackTokenRatePusherImplAddress = opStackTokenRatePusherImplAddress; + } + + public bridgeProxyAddress: string; + public bridgeImplAddress: string; + public tokenRateNotifierImplAddress: string; + public opStackTokenRatePusherImplAddress: string; +} + +export class L2UpgradeScript extends DeployScript { + + constructor( + deployer: Wallet, + tokenImplAddress: string, + tokenRebasableImplAddress: string, + tokenRebasableProxyAddress: string, + tokenBridgeProxyAddress: string, + tokenBridgeImplAddress: string, + tokenRateOracleImplAddress: string, + tokenRateOracleProxyAddress: string, + logger?: Logger + ) { + super(deployer, logger); + this.tokenImplAddress = tokenImplAddress; + this.tokenRebasableImplAddress = tokenRebasableImplAddress; + this.tokenRebasableProxyAddress = tokenRebasableProxyAddress; + this.tokenBridgeProxyAddress = tokenBridgeProxyAddress; + this.tokenBridgeImplAddress = tokenBridgeImplAddress; + this.tokenRateOracleImplAddress = tokenRateOracleImplAddress; + this.tokenRateOracleProxyAddress = tokenRateOracleProxyAddress; + } + + public tokenImplAddress: string; + public tokenRebasableImplAddress: string; + public tokenRebasableProxyAddress: string; + public tokenBridgeProxyAddress: string; + public tokenBridgeImplAddress: string; + public tokenRateOracleImplAddress: string; + public tokenRateOracleProxyAddress: string; +} + + +/// L1 part +/// new TokenRateNotifier Impl +/// new OpStackTokenRatePusher Impl +/// new L1Bridge Impl +/// L2 part +/// TokenRateOracle + proxy +/// new L2Bridge Impl +/// RebasableToken(stETH) Impl and Proxy (because it was never deployed before) +/// Non-rebasable token (wstETH) new Impl with Permissions + +export default function upgrade( + networkName: NetworkName, + options: OptDeploymentOptions = {} +) { + const optAddresses = addresses(networkName, options); + return { + async upgradeScript( + l1Params: OptL1UpgradeScriptParams, + l2Params: OptL2UpgradeScriptParams, + ): Promise<[L1UpgradeScript, L2UpgradeScript]> { + + const [ + expectedL1TokenBridgeImplAddress, + expectedL1TokenRateNotifierImplAddress, + expectedL1OpStackTokenRatePusherImplAddress, + ] = await network.predictAddresses(l1Params.deployer, l1Params.contractsShift + 3); + + const [ + // Oracle + Proxy + expectedL2TokenRateOracleImplAddress, + expectedL2TokenRateOracleProxyAddress, + // wstETH Impl + expectedL2TokenImplAddress, + // stETH Impl + Proxy + expectedL2TokenRebasableImplAddress, + expectedL2TokenRebasableProxyAddress, + // L2Bridge Impl + expectedL2TokenBridgeImplAddress + ] = await network.predictAddresses(l2Params.deployer, l2Params.contractsShift + 6); + + const l1UpgradeScript = new L1UpgradeScript( + l1Params.deployer, + l1Params.l1TokenBridge, + expectedL1TokenBridgeImplAddress, + expectedL1TokenRateNotifierImplAddress, + expectedL1OpStackTokenRatePusherImplAddress, + options?.logger + ) + .addStep({ + factory: L1LidoTokensBridge__factory, + args: [ + optAddresses.L1CrossDomainMessenger, + l2Params.l2TokenBridge, + l1Params.l1TokenNonRebasable, + l1Params.l1TokenRebasable, + l2Params.l2TokenNonRebasable.address, + expectedL2TokenRebasableProxyAddress, + l1Params.accountingOracle, + options?.overrides, + ], + afterDeploy: (c) => + assert.equal(c.address, expectedL1TokenBridgeImplAddress), + }) + .addStep({ + factory: TokenRateNotifier__factory, + args: [ + l1Params.deployer.address, + options?.overrides, + ], + afterDeploy: (c) => + assert.equal(c.address, expectedL1TokenRateNotifierImplAddress), + }) + .addStep({ + factory: OpStackTokenRatePusher__factory, + args: [ + optAddresses.L1CrossDomainMessenger, + l1Params.l1TokenNonRebasable, + l1Params.accountingOracle, + expectedL2TokenRateOracleProxyAddress, + l1Params.l2GasLimitForPushingTokenRate, + options?.overrides, + ], + afterDeploy: (c) => + assert.equal(c.address, expectedL1OpStackTokenRatePusherImplAddress), + }); + + const l1TokenNonRebasableInfo = IERC20Metadata__factory.connect( + l1Params.l1TokenNonRebasable, + l1Params.deployer + ); + + const l1TokenRebasableInfo = IERC20Metadata__factory.connect( + l1Params.l1TokenRebasable, + l1Params.deployer + ); + const [ + l2TokenNonRebasableDecimals, l2TokenNonRebasableName, l2TokenNonRebasableSymbol, + l2TokenRebasableDecimals, l2TokenRebasableName, l2TokenRebasableSymbol + ] = await Promise.all([ + l1TokenNonRebasableInfo.decimals(), + l2Params.l2TokenNonRebasable?.name ?? l1TokenNonRebasableInfo.name(), + l2Params.l2TokenNonRebasable?.symbol ?? l1TokenNonRebasableInfo.symbol(), + l1TokenRebasableInfo.decimals(), + l2Params.l2TokenRebasable?.name ?? l1TokenRebasableInfo.name(), + l2Params.l2TokenRebasable?.symbol ?? l1TokenRebasableInfo.symbol(), + ]); + + const l2UpgradeScript = new L2UpgradeScript( + l2Params.deployer, + expectedL2TokenImplAddress, + expectedL2TokenRebasableImplAddress, + expectedL2TokenRebasableProxyAddress, + l2Params.l2TokenBridge, + expectedL2TokenBridgeImplAddress, + expectedL2TokenRateOracleImplAddress, + expectedL2TokenRateOracleProxyAddress, + options?.logger + ) + .addStep({ + factory: TokenRateOracle__factory, + args: [ + optAddresses.L2CrossDomainMessenger, + l2Params.l2TokenBridge, + expectedL1OpStackTokenRatePusherImplAddress, + l2Params.tokenRateOracle.constructor.tokenRateOutdatedDelay, + l2Params.tokenRateOracle.constructor.maxAllowedL2ToL1ClockLag, + l2Params.tokenRateOracle.constructor.maxAllowedTokenRateDeviationPerDayBp, + l2Params.tokenRateOracle.constructor.oldestRateAllowedInPauseTimeSpan, + l2Params.tokenRateOracle.constructor.maxAllowedTimeBetweenTokenRateUpdates, + options?.overrides, + ], + afterDeploy: (c) => + assert.equal(c.address, expectedL2TokenRateOracleImplAddress), + }) + .addStep({ + factory: OssifiableProxy__factory, + args: [ + expectedL2TokenRateOracleImplAddress, + l2Params.admins.proxy, + TokenRateOracle__factory.createInterface().encodeFunctionData( + "initialize", + [ + l2Params.admins.bridge, + l2Params.tokenRateOracle.initialize.tokenRate, + l2Params.tokenRateOracle.initialize.l1Timestamp + ] + ), + options?.overrides, + ], + afterDeploy: (c) => + assert.equal(c.address, expectedL2TokenRateOracleProxyAddress), + }) + .addStep({ + factory: ERC20BridgedPermit__factory, + args: [ + l2TokenNonRebasableName, + l2TokenNonRebasableSymbol, + l2Params.l2TokenNonRebasable.version, + l2TokenNonRebasableDecimals, + l2Params.l2TokenBridge, + options?.overrides, + ], + afterDeploy: (c) => + assert.equal(c.address, expectedL2TokenImplAddress), + }) + .addStep({ + factory: ERC20RebasableBridgedPermit__factory, + args: [ + l2TokenRebasableName, + l2TokenRebasableSymbol, + l2Params.l2TokenRebasable.version, + l2TokenRebasableDecimals, + l2Params.l2TokenNonRebasable.address, + expectedL2TokenRateOracleProxyAddress, + l2Params.l2TokenBridge, + options?.overrides, + ], + afterDeploy: (c) => + assert.equal(c.address, expectedL2TokenRebasableImplAddress), + }) + .addStep({ + factory: OssifiableProxy__factory, + args: [ + expectedL2TokenRebasableImplAddress, + l2Params.admins.proxy, + ERC20RebasableBridgedPermit__factory.createInterface().encodeFunctionData( + "initialize", + [ + l2TokenRebasableName, + l2TokenRebasableSymbol, + l2Params.l2TokenRebasable.version + ] + ), + options?.overrides, + ], + afterDeploy: (c) => + assert.equal(c.address, expectedL2TokenRebasableProxyAddress), + }) + .addStep({ + factory: L2ERC20ExtendedTokensBridge__factory, + args: [ + optAddresses.L2CrossDomainMessenger, + l1Params.l1TokenBridge, + l1Params.l1TokenNonRebasable, + l1Params.l1TokenRebasable, + l2Params.l2TokenNonRebasable.address, + expectedL2TokenRebasableProxyAddress, + options?.overrides, + ], + afterDeploy: (c) => + assert.equal(c.address, expectedL2TokenBridgeImplAddress), + }); + + return [l1UpgradeScript as L1UpgradeScript, l2UpgradeScript as L2UpgradeScript]; + }, + }; +} diff --git a/utils/testing/env.ts b/utils/testing/env.ts index 218ca7df..bebddeb4 100644 --- a/utils/testing/env.ts +++ b/utils/testing/env.ts @@ -4,30 +4,35 @@ export default { USE_DEPLOYED_CONTRACTS(defaultValue: boolean = false) { return env.bool("TESTING_USE_DEPLOYED_CONTRACTS", defaultValue); }, - OPT_L1_TOKEN() { - return env.address("TESTING_OPT_L1_TOKEN"); + OPT_L1_NON_REBASABLE_TOKEN() { + return env.address("TESTING_OPT_L1_NON_REBASABLE_TOKEN"); }, - OPT_L2_TOKEN() { - return env.address("TESTING_OPT_L2_TOKEN"); + OPT_L1_REBASABLE_TOKEN() { + return env.address("TESTING_OPT_L1_REBASABLE_TOKEN"); }, - OPT_L1_TOKEN_RATE_NOTIFIER() { - return env.address("TESTING_OPT_L1_TOKEN_RATE_NOTIFIER"); + OPT_L1_ACCOUNTING_ORACLE() { + return env.address("TESTING_OPT_L1_ACCOUNTING_ORACLE"); }, + OPT_L1_ERC20_TOKEN_BRIDGE() { + return env.address("TESTING_OPT_L1_ERC20_TOKEN_BRIDGE"); + }, + OPT_L2_TOKEN_RATE_ORACLE() { return env.address("TESTING_OPT_L2_TOKEN_RATE_ORACLE"); }, - OPT_L1_REBASABLE_TOKEN() { - return env.address("TESTING_OPT_L1_REBASABLE_TOKEN"); + OPT_L2_NON_REBASABLE_TOKEN() { + return env.address("TESTING_OPT_L2_NON_REBASABLE_TOKEN"); }, OPT_L2_REBASABLE_TOKEN() { return env.address("TESTING_OPT_L2_REBASABLE_TOKEN"); }, - OPT_L1_ERC20_TOKEN_BRIDGE() { - return env.address("TESTING_OPT_L1_ERC20_TOKEN_BRIDGE"); - }, OPT_L2_ERC20_TOKEN_BRIDGE() { return env.address("TESTING_OPT_L2_ERC20_TOKEN_BRIDGE"); }, + + OPT_L1_TOKEN_RATE_NOTIFIER() { + return env.address("TESTING_OPT_L1_TOKEN_RATE_NOTIFIER"); + }, OPT_GOV_BRIDGE_EXECUTOR() { return env.address("TESTING_OPT_GOV_BRIDGE_EXECUTOR"); },