diff --git a/contracts/lib/UnstructuredStorage.sol b/contracts/lib/UnstructuredStorage.sol index 994b4e34..aa815392 100644 --- a/contracts/lib/UnstructuredStorage.sol +++ b/contracts/lib/UnstructuredStorage.sol @@ -10,14 +10,6 @@ library UnstructuredStorage { assembly { data := sload(position) } } - function getStorageAddress(bytes32 position) internal view returns (address data) { - assembly { data := sload(position) } - } - - function getStorageBytes32(bytes32 position) internal view returns (bytes32 data) { - assembly { data := sload(position) } - } - function getStorageUint256(bytes32 position) internal view returns (uint256 data) { assembly { data := sload(position) } } @@ -26,14 +18,6 @@ library UnstructuredStorage { assembly { sstore(position, data) } } - function setStorageAddress(bytes32 position, address data) internal { - assembly { sstore(position, data) } - } - - function setStorageBytes32(bytes32 position, bytes32 data) internal { - assembly { sstore(position, data) } - } - function setStorageUint256(bytes32 position, uint256 data) internal { assembly { sstore(position, data) } } diff --git a/contracts/lido/TokenRateNotifier.sol b/contracts/lido/TokenRateNotifier.sol index 587bb45d..e6e60f81 100644 --- a/contracts/lido/TokenRateNotifier.sol +++ b/contracts/lido/TokenRateNotifier.sol @@ -27,6 +27,9 @@ interface IPostTokenRebaseReceiver { contract TokenRateNotifier is Ownable, IPostTokenRebaseReceiver { using ERC165Checker for address; + /// @notice Address of lido core protocol contract that is allowed to call handlePostTokenRebase. + address public immutable LIDO; + /// @notice Maximum amount of observers to be supported. uint256 public constant MAX_OBSERVERS_COUNT = 32; @@ -40,11 +43,16 @@ contract TokenRateNotifier is Ownable, IPostTokenRebaseReceiver { address[] public observers; /// @param initialOwner_ initial owner - constructor(address initialOwner_) { + /// @param lido_ Address of lido core protocol contract that is allowed to call handlePostTokenRebase. + constructor(address initialOwner_, address lido_) { if (initialOwner_ == address(0)) { revert ErrorZeroAddressOwner(); } + if (lido_ == address(0)) { + revert ErrorZeroAddressLido(); + } _transferOwnership(initialOwner_); + LIDO = lido_; } /// @notice Add a `observer_` to the back of array @@ -85,6 +93,7 @@ contract TokenRateNotifier is Ownable, IPostTokenRebaseReceiver { /// @inheritdoc IPostTokenRebaseReceiver /// @dev Parameters aren't used because all required data further components fetch by themselves. + /// Allowed to called by Lido contract. See Lido._completeTokenRebase. function handlePostTokenRebase( uint256, /* reportTimestamp */ uint256, /* timeElapsed */ @@ -94,6 +103,9 @@ contract TokenRateNotifier is Ownable, IPostTokenRebaseReceiver { uint256, /* postTotalEther */ uint256 /* sharesMintedAsFees */ ) external { + if (msg.sender != LIDO) { + revert ErrorNotAuthorizedRebaseCaller(); + } uint256 cachedObserversLength = observers.length; for (uint256 obIndex = 0; obIndex < cachedObserversLength; obIndex++) { // solhint-disable-next-line no-empty-blocks @@ -141,5 +153,7 @@ contract TokenRateNotifier is Ownable, IPostTokenRebaseReceiver { error ErrorMaxObserversCountExceeded(); error ErrorNoObserverToRemove(); error ErrorZeroAddressOwner(); + error ErrorZeroAddressLido(); + error ErrorNotAuthorizedRebaseCaller(); error ErrorAddExistedObserver(); } diff --git a/contracts/optimism/TokenRateOracle.sol b/contracts/optimism/TokenRateOracle.sol index be73b316..03255a85 100644 --- a/contracts/optimism/TokenRateOracle.sol +++ b/contracts/optimism/TokenRateOracle.sol @@ -55,8 +55,8 @@ contract TokenRateOracle is ITokenRateOracle, CrossDomainEnabled, AccessControl, /// See the 'pauseTokenRateUpdates()' method uint256 public immutable OLDEST_RATE_ALLOWED_IN_PAUSE_TIME_SPAN; - /// @notice The maximum delta time that is allowed between two L1 timestamps of token rate updates. - uint256 public immutable MAX_ALLOWED_TIME_BETWEEN_TOKEN_RATE_UPDATES; + /// @notice The minimum delta time between two L1 timestamps of token rate updates. + uint256 public immutable MIN_TIME_BETWEEN_TOKEN_RATE_UPDATES; /// @notice Decimals of the oracle response. uint8 public constant DECIMALS = 27; @@ -95,7 +95,7 @@ contract TokenRateOracle is ITokenRateOracle, CrossDomainEnabled, AccessControl, /// Can't be bigger than BASIS_POINT_SCALE. /// @param oldestRateAllowedInPauseTimeSpan_ Maximum allowed time difference between the current time /// and the last received token rate update that can be set during a pause. - /// @param maxAllowedTimeBetweenTokenRateUpdates_ the maximum delta time that is allowed between two + /// @param minTimeBetweenTokenRateUpdates_ Minimum delta time between two /// L1 timestamps of token rate updates. constructor( address messenger_, @@ -105,7 +105,7 @@ contract TokenRateOracle is ITokenRateOracle, CrossDomainEnabled, AccessControl, uint256 maxAllowedL2ToL1ClockLag_, uint256 maxAllowedTokenRateDeviationPerDayBp_, uint256 oldestRateAllowedInPauseTimeSpan_, - uint256 maxAllowedTimeBetweenTokenRateUpdates_ + uint256 minTimeBetweenTokenRateUpdates_ ) CrossDomainEnabled(messenger_) { if (l2ERC20TokenBridge_ == address(0)) { revert ErrorZeroAddressL2ERC20TokenBridge(); @@ -122,7 +122,7 @@ contract TokenRateOracle is ITokenRateOracle, CrossDomainEnabled, AccessControl, MAX_ALLOWED_L2_TO_L1_CLOCK_LAG = maxAllowedL2ToL1ClockLag_; MAX_ALLOWED_TOKEN_RATE_DEVIATION_PER_DAY_BP = maxAllowedTokenRateDeviationPerDayBp_; OLDEST_RATE_ALLOWED_IN_PAUSE_TIME_SPAN = oldestRateAllowedInPauseTimeSpan_; - MAX_ALLOWED_TIME_BETWEEN_TOKEN_RATE_UPDATES = maxAllowedTimeBetweenTokenRateUpdates_; + MIN_TIME_BETWEEN_TOKEN_RATE_UPDATES = minTimeBetweenTokenRateUpdates_; } /// @notice Initializes the contract from scratch. @@ -180,6 +180,9 @@ contract TokenRateOracle is ITokenRateOracle, CrossDomainEnabled, AccessControl, if (rateUpdatedL1Timestamp_ > block.timestamp + MAX_ALLOWED_L2_TO_L1_CLOCK_LAG) { revert ErrorL1TimestampExceededMaxAllowedClockLag(rateUpdatedL1Timestamp_); } + if (rateUpdatedL1Timestamp_ < _getLastTokenRate().rateUpdatedL1Timestamp) { + revert ErrorL1TimestampOlderThanPrevious(rateUpdatedL1Timestamp_); + } _addTokenRate(tokenRate_, rateUpdatedL1Timestamp_, block.timestamp); _setPause(false); emit TokenRateUpdatesResumed(tokenRate_, rateUpdatedL1Timestamp_); @@ -263,8 +266,8 @@ contract TokenRateOracle is ITokenRateOracle, CrossDomainEnabled, AccessControl, return; } - /// @dev This condition was made under the assumption that the L1 timestamps can be hacked. - if (rateUpdatedL1Timestamp_ < tokenRateData.rateUpdatedL1Timestamp + MAX_ALLOWED_TIME_BETWEEN_TOKEN_RATE_UPDATES) { + /// @dev This condition was made under the assumption that the L1 timestamps can be manipulated. + if (rateUpdatedL1Timestamp_ < tokenRateData.rateUpdatedL1Timestamp + MIN_TIME_BETWEEN_TOKEN_RATE_UPDATES) { emit UpdateRateIsTooOften(rateUpdatedL1Timestamp_, tokenRateData.rateUpdatedL1Timestamp); return; } @@ -428,4 +431,5 @@ contract TokenRateOracle is ITokenRateOracle, CrossDomainEnabled, AccessControl, error ErrorMaxTokenRateDeviationIsOutOfRange(); error ErrorTokenRateIsOutOfSaneRange(uint256 tokenRate_); error ErrorL1TimestampExceededMaxAllowedClockLag(uint256 rateL1Timestamp_); + error ErrorL1TimestampOlderThanPrevious(uint256 rateL1Timestamp_); } diff --git a/contracts/token/ERC20RebasableBridged.sol b/contracts/token/ERC20RebasableBridged.sol index f2adfe12..972b427f 100644 --- a/contracts/token/ERC20RebasableBridged.sol +++ b/contracts/token/ERC20RebasableBridged.sol @@ -415,7 +415,6 @@ contract ERC20RebasableBridged is IERC20, IERC20Wrapper, IBridgeWrapper, ERC20Me error ErrorZeroSharesWrap(); error ErrorZeroTokensUnwrap(); error ErrorZeroSharesUnwrap(); - error ErrorTokenRateDecimalsIsZero(); error ErrorTransferToRebasableContract(); error ErrorNotEnoughBalance(); error ErrorNotEnoughAllowance(); 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-bridge.ts b/scripts/optimism/deploy-scratch.ts similarity index 55% rename from scripts/optimism/deploy-bridge.ts rename to scripts/optimism/deploy-scratch.ts index 57538f49..cfb01f9f 100644 --- a/scripts/optimism/deploy-bridge.ts +++ b/scripts/optimism/deploy-scratch.ts @@ -1,9 +1,9 @@ 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 { BridgingManagement } from "../../utils/bridging-management"; +import deploymentAll from "../../utils/optimism/deployment"; async function main() { const networkName = env.network(); @@ -21,27 +21,45 @@ async function main() { const deploymentConfig = deployment.loadMultiChainDeploymentConfig(); - const [l1DeployScript, l2DeployScript] = await optimism - .deployment(networkName, { logger: console }) - .erc20TokenBridgeDeployScript( - deploymentConfig.l1Token, - deploymentConfig.l1RebasableToken, - deploymentConfig.l2TokenRateOracle, + const [l1DeployScript, l2DeployScript] = await deploymentAll (networkName, { logger: console }) + .deployAllScript( { + l1TokenNonRebasable: deploymentConfig.l1TokenNonRebasable, + l1TokenRebasable: deploymentConfig.l1RebasableToken, + accountingOracle: deploymentConfig.accountingOracle, + l2GasLimitForPushingTokenRate: deploymentConfig.l2GasLimitForPushingTokenRate, + lido: deploymentConfig.lido, + deployer: ethDeployer, admins: { proxy: deploymentConfig.l1.proxyAdmin, bridge: ethDeployer.address }, - contractsShift: 0 + contractsShift: 0, }, { + tokenRateOracle: { + tokenRateOutdatedDelay: deploymentConfig.tokenRateOutdatedDelay, + maxAllowedL2ToL1ClockLag: deploymentConfig.maxAllowedL2ToL1ClockLag, + maxAllowedTokenRateDeviationPerDayBp: deploymentConfig.maxAllowedTokenRateDeviationPerDayBp, + oldestRateAllowedInPauseTimeSpan: deploymentConfig.oldestRateAllowedInPauseTimeSpan, + minTimeBetweenTokenRateUpdates: deploymentConfig.minTimeBetweenTokenRateUpdates, + tokenRate: deploymentConfig.tokenRateValue, + l1Timestamp: deploymentConfig.tokenRateL1Timestamp + }, + l2TokenNonRebasable: { + version: "1" + }, + l2TokenRebasable: { + version: "1" + }, + deployer: optDeployer, admins: { proxy: deploymentConfig.l2.proxyAdmin, bridge: optDeployer.address, }, - contractsShift: 0 + contractsShift: 0, } ); @@ -59,16 +77,14 @@ async function main() { await l1DeployScript.run(); await l2DeployScript.run(); - const l1ERC20ExtendedTokensBridgeProxyDeployStepIndex = 1; const l1BridgingManagement = new BridgingManagement( - l1DeployScript.getContractAddress(l1ERC20ExtendedTokensBridgeProxyDeployStepIndex), + l1DeployScript.bridgeProxyAddress, ethDeployer, { logger: console } ); - const l2ERC20ExtendedTokensBridgeProxyDeployStepIndex = 5; const l2BridgingManagement = new BridgingManagement( - l2DeployScript.getContractAddress(l2ERC20ExtendedTokensBridgeProxyDeployStepIndex), + l2DeployScript.tokenBridgeProxyAddress, optDeployer, { logger: console } ); diff --git a/scripts/optimism/upgrade.ts b/scripts/optimism/upgrade.ts new file mode 100644 index 00000000..a41f9c9f --- /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, + minTimeBetweenTokenRateUpdates: deploymentConfig.minTimeBetweenTokenRateUpdates + }, + 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..f557e071 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 minTimeBetweenTokenRateUpdates = 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, + minTimeBetweenTokenRateUpdates: minTimeBetweenTokenRateUpdates, + 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/L2ERC20ExtendedTokensBridge.unit.test.ts b/test/optimism/L2ERC20ExtendedTokensBridge.unit.test.ts index c857a22e..a288dbee 100644 --- a/test/optimism/L2ERC20ExtendedTokensBridge.unit.test.ts +++ b/test/optimism/L2ERC20ExtendedTokensBridge.unit.test.ts @@ -1234,7 +1234,7 @@ async function ctxFactory() { const maxAllowedL2ToL1ClockLag = BigNumber.from(86400); const maxAllowedTokenRateDeviationPerDay = BigNumber.from(500); const oldestRateAllowedInPauseTimeSpan = BigNumber.from(86400*3); - const maxAllowedTimeBetweenTokenRateUpdates = BigNumber.from(3600); + const minTimeBetweenTokenRateUpdates = BigNumber.from(3600); const l2MessengerStub = await new CrossDomainMessengerStub__factory( deployer @@ -1284,7 +1284,7 @@ async function ctxFactory() { maxAllowedL2ToL1ClockLag, maxAllowedTokenRateDeviationPerDay, oldestRateAllowedInPauseTimeSpan, - maxAllowedTimeBetweenTokenRateUpdates + minTimeBetweenTokenRateUpdates ); const provider = await hre.ethers.provider; @@ -1416,7 +1416,7 @@ async function getL2TokenBridgeImpl(deployer: SignerWithAddress, l1TokenBridge: const maxAllowedL2ToL1ClockLag = BigNumber.from(86400); const maxAllowedTokenRateDeviationPerDay = BigNumber.from(500); const oldestRateAllowedInPauseTimeSpan = BigNumber.from(86400*3); - const maxAllowedTimeBetweenTokenRateUpdates = BigNumber.from(3600); + const minTimeBetweenTokenRateUpdates = BigNumber.from(3600); const l2MessengerStub = await new CrossDomainMessengerStub__factory( deployer @@ -1459,7 +1459,7 @@ async function getL2TokenBridgeImpl(deployer: SignerWithAddress, l1TokenBridge: maxAllowedL2ToL1ClockLag, maxAllowedTokenRateDeviationPerDay, oldestRateAllowedInPauseTimeSpan, - maxAllowedTimeBetweenTokenRateUpdates + minTimeBetweenTokenRateUpdates ); const provider = await hre.ethers.provider; diff --git a/test/optimism/TokenRateNotifier.unit.test.ts b/test/optimism/TokenRateNotifier.unit.test.ts index cd19b9aa..476253db 100644 --- a/test/optimism/TokenRateNotifier.unit.test.ts +++ b/test/optimism/TokenRateNotifier.unit.test.ts @@ -21,14 +21,23 @@ import { unit("TokenRateNotifier", ctxFactory) .test("deploy with zero address owner", async (ctx) => { - const { deployer } = ctx.accounts; + const { deployer, lido } = ctx.accounts; await assert.revertsWith( - new TokenRateNotifier__factory(deployer).deploy(ethers.constants.AddressZero), + new TokenRateNotifier__factory(deployer).deploy(ethers.constants.AddressZero, lido.address), "ErrorZeroAddressOwner()" ); }) + .test("deploy with zero address rebase caller", async (ctx) => { + const { deployer } = ctx.accounts; + + await assert.revertsWith( + new TokenRateNotifier__factory(deployer).deploy(deployer.address, ethers.constants.AddressZero), + "ErrorZeroAddressLido()" + ); + }) + .test("initial state", async (ctx) => { const { tokenRateNotifier } = ctx.contracts; @@ -63,9 +72,9 @@ unit("TokenRateNotifier", ctxFactory) .test("addObserver() :: revert on adding observer with bad interface", async (ctx) => { const { tokenRateNotifier } = ctx.contracts; - const { deployer } = ctx.accounts; + const { deployer, lido } = ctx.accounts; - const observer = await new TokenRateNotifier__factory(deployer).deploy(deployer.address); + const observer = await new TokenRateNotifier__factory(deployer).deploy(deployer.address, lido.address); await assert.revertsWith( tokenRateNotifier .connect(ctx.accounts.owner) @@ -76,7 +85,7 @@ unit("TokenRateNotifier", ctxFactory) .test("addObserver() :: revert on adding too many observers", async (ctx) => { const { tokenRateNotifier, opStackTokenRatePusher } = ctx.contracts; - const { deployer, owner, tokenRateOracle } = ctx.accounts; + const { deployer, owner, tokenRateOracle, lido } = ctx.accounts; const { l2GasLimitForPushingTokenRate, tokenRate, totalPooledEther, totalShares, genesisTime, secondsPerSlot, lastProcessingRefSlot } = ctx.constants; assert.equalBN(await tokenRateNotifier.observersLength(), 0); @@ -95,7 +104,8 @@ unit("TokenRateNotifier", ctxFactory) deployer, owner, tokenRateOracle, - l2GasLimitForPushingTokenRate + l2GasLimitForPushingTokenRate, + lido ); await tokenRateNotifier @@ -183,23 +193,33 @@ unit("TokenRateNotifier", ctxFactory) assert.equalBN(await tokenRateNotifier.observersLength(), 0); }) + .test("handlePostTokenRebase() :: unauthorized caller", async (ctx) => { + const { tokenRateNotifier } = ctx.contracts; + const { stranger } = ctx.accounts; + + await assert.revertsWith( + tokenRateNotifier.connect(stranger).handlePostTokenRebase(1, 2, 3, 4, 5, 6, 7), + "ErrorNotAuthorizedRebaseCaller()" + ); + }) + .test("handlePostTokenRebase() :: failed with some error", async (ctx) => { const { tokenRateNotifier } = ctx.contracts; - const { deployer } = ctx.accounts; + const { deployer, lido } = ctx.accounts; const observer = await new OpStackTokenRatePusherWithSomeErrorStub__factory(deployer).deploy(); await tokenRateNotifier .connect(ctx.accounts.owner) .addObserver(observer.address); - const tx = await tokenRateNotifier.handlePostTokenRebase(1, 2, 3, 4, 5, 6, 7); + const tx = await tokenRateNotifier.connect(lido).handlePostTokenRebase(1, 2, 3, 4, 5, 6, 7); await assert.emits(tokenRateNotifier, tx, "PushTokenRateFailed", [observer.address, "0x332e27d2"]); }) .test("handlePostTokenRebase() :: revert when observer has out of gas error", async (ctx) => { const { tokenRateNotifier } = ctx.contracts; - const { deployer } = ctx.accounts; + const { deployer, lido } = ctx.accounts; const observer = await new OpStackTokenRatePusherWithOutOfGasErrorStub__factory(deployer).deploy(); await tokenRateNotifier @@ -207,7 +227,7 @@ unit("TokenRateNotifier", ctxFactory) .addObserver(observer.address); await assert.revertsWith( - tokenRateNotifier.handlePostTokenRebase(1, 2, 3, 4, 5, 6, 7), + tokenRateNotifier.connect(lido).handlePostTokenRebase(1, 2, 3, 4, 5, 6, 7), "ErrorTokenRateNotifierRevertedWithNoData()" ); }) @@ -218,7 +238,7 @@ unit("TokenRateNotifier", ctxFactory) l1MessengerStub, opStackTokenRatePusher } = ctx.contracts; - const { tokenRateOracle } = ctx.accounts; + const { tokenRateOracle, lido } = ctx.accounts; const { l2GasLimitForPushingTokenRate, tokenRate, genesisTime, secondsPerSlot, lastProcessingRefSlot } = ctx.constants; const updateRateTime = genesisTime.add(secondsPerSlot.mul(lastProcessingRefSlot)); @@ -226,7 +246,7 @@ unit("TokenRateNotifier", ctxFactory) await tokenRateNotifier .connect(ctx.accounts.owner) .addObserver(opStackTokenRatePusher.address); - let tx = await tokenRateNotifier.handlePostTokenRebase(1, 2, 3, 4, 5, 6, 7); + let tx = await tokenRateNotifier.connect(lido).handlePostTokenRebase(1, 2, 3, 4, 5, 6, 7); await assert.emits(l1MessengerStub, tx, "SentMessage", [ tokenRateOracle.address, @@ -246,7 +266,7 @@ unit("TokenRateNotifier", ctxFactory) .run(); async function ctxFactory() { - const [deployer, owner, stranger, tokenRateOracle] = await ethers.getSigners(); + const [deployer, owner, stranger, tokenRateOracle, lido] = await ethers.getSigners(); const totalPooledEther = BigNumber.from('9309904612343950493629678'); const totalShares = BigNumber.from('7975822843597609202337218'); const tokenRateDecimals = BigNumber.from(27); @@ -270,7 +290,8 @@ async function ctxFactory() { deployer, owner, tokenRateOracle, - l2GasLimitForPushingTokenRate + l2GasLimitForPushingTokenRate, + lido ); return { @@ -278,7 +299,8 @@ async function ctxFactory() { deployer, owner, stranger, - tokenRateOracle + tokenRateOracle, + lido }, contracts: { tokenRateNotifier, @@ -307,9 +329,13 @@ async function createContracts( deployer: SignerWithAddress, owner: SignerWithAddress, tokenRateOracle: SignerWithAddress, - l2GasLimitForPushingTokenRate: number) { + l2GasLimitForPushingTokenRate: number, + lido: SignerWithAddress) { - const tokenRateNotifier = await new TokenRateNotifier__factory(deployer).deploy(owner.address); + const tokenRateNotifier = await new TokenRateNotifier__factory(deployer).deploy( + owner.address, + lido.address + ); const l1MessengerStub = await new CrossDomainMessengerStub__factory(deployer) .deploy({ value: wei.toBigNumber(wei`1 ether`) }); diff --git a/test/optimism/TokenRateOracle.unit.test.ts b/test/optimism/TokenRateOracle.unit.test.ts index c54fc3cc..e291bedf 100644 --- a/test/optimism/TokenRateOracle.unit.test.ts +++ b/test/optimism/TokenRateOracle.unit.test.ts @@ -138,7 +138,7 @@ unit("TokenRateOracle", ctxFactory) maxAllowedL2ToL1ClockLag, maxAllowedTokenRateDeviationPerDay, oldestRateAllowedInPauseTimeSpan, - maxAllowedTimeBetweenTokenRateUpdates + minTimeBetweenTokenRateUpdates } = ctx.constants; const tokenRateMin = BigNumber.from(10).pow(27-2); @@ -154,7 +154,7 @@ unit("TokenRateOracle", ctxFactory) maxAllowedL2ToL1ClockLag, maxAllowedTokenRateDeviationPerDay, oldestRateAllowedInPauseTimeSpan, - maxAllowedTimeBetweenTokenRateUpdates, + minTimeBetweenTokenRateUpdates, tokenRateMin.sub(1), blockTimestampOfDeployment ), @@ -171,7 +171,7 @@ unit("TokenRateOracle", ctxFactory) maxAllowedL2ToL1ClockLag, maxAllowedTokenRateDeviationPerDay, oldestRateAllowedInPauseTimeSpan, - maxAllowedTimeBetweenTokenRateUpdates, + minTimeBetweenTokenRateUpdates, tokenRateMax.add(1), blockTimestampOfDeployment ), @@ -189,7 +189,7 @@ unit("TokenRateOracle", ctxFactory) maxAllowedL2ToL1ClockLag, maxAllowedTokenRateDeviationPerDay, oldestRateAllowedInPauseTimeSpan, - maxAllowedTimeBetweenTokenRateUpdates + minTimeBetweenTokenRateUpdates } = ctx.constants; const wrongTimeMax = blockTimestampOfDeployment.add(maxAllowedL2ToL1ClockLag).add(20); @@ -204,7 +204,7 @@ unit("TokenRateOracle", ctxFactory) maxAllowedL2ToL1ClockLag, maxAllowedTokenRateDeviationPerDay, oldestRateAllowedInPauseTimeSpan, - maxAllowedTimeBetweenTokenRateUpdates, + minTimeBetweenTokenRateUpdates, tokenRate, wrongTimeMax ), @@ -309,9 +309,9 @@ unit("TokenRateOracle", ctxFactory) .test("updateRate() :: token rate updates too often", async (ctx) => { const { tokenRateOracle } = ctx.contracts; const { bridge } = ctx.accounts; - const { tokenRate, rateL1Timestamp, maxAllowedTimeBetweenTokenRateUpdates } = ctx.constants; + const { tokenRate, rateL1Timestamp, minTimeBetweenTokenRateUpdates } = ctx.constants; - const rateL1TimestampWithinTooOften = rateL1Timestamp.add(maxAllowedTimeBetweenTokenRateUpdates).sub(1); + const rateL1TimestampWithinTooOften = rateL1Timestamp.add(minTimeBetweenTokenRateUpdates).sub(1); const tx0 = await tokenRateOracle .connect(bridge) @@ -328,10 +328,10 @@ unit("TokenRateOracle", ctxFactory) tokenRate, blockTimestampOfDeployment, maxAllowedTokenRateDeviationPerDay, - maxAllowedTimeBetweenTokenRateUpdates + minTimeBetweenTokenRateUpdates } = ctx.constants; - const blockTimestampForNextUpdate = blockTimestampOfDeployment.add(maxAllowedTimeBetweenTokenRateUpdates).add(1); + const blockTimestampForNextUpdate = blockTimestampOfDeployment.add(minTimeBetweenTokenRateUpdates).add(1); const tokenRateTooBig = tokenRate.mul( BigNumber.from('10000') .add(maxAllowedTokenRateDeviationPerDay) @@ -414,7 +414,7 @@ unit("TokenRateOracle", ctxFactory) .test("updateRate() :: token rate limits", async (ctx) => { const { deployer, bridge, l1TokenBridgeEOA } = ctx.accounts; - const { tokenRate, maxAllowedTimeBetweenTokenRateUpdates } = ctx.constants; + const { tokenRate, minTimeBetweenTokenRateUpdates } = ctx.constants; const tokenRateOutdatedDelay = BigNumber.from(86400); // 1 day const maxAllowedL2ToL1ClockLag = BigNumber.from(86400 * 2); // 2 days @@ -439,22 +439,22 @@ unit("TokenRateOracle", ctxFactory) ); const maxAllowedTokenRate = await tokenRateOracle.MAX_SANE_TOKEN_RATE(); - await tokenRateOracle.connect(bridge).updateRate(maxAllowedTokenRate, blockTimestampOfDeployment.add(maxAllowedTimeBetweenTokenRateUpdates).add(1)); + await tokenRateOracle.connect(bridge).updateRate(maxAllowedTokenRate, blockTimestampOfDeployment.add(minTimeBetweenTokenRateUpdates).add(1)); assert.equalBN(await tokenRateOracle.latestAnswer(), maxAllowedTokenRate); const minAllowedTokenRate = await tokenRateOracle.MIN_SANE_TOKEN_RATE(); - await tokenRateOracle.connect(bridge).updateRate(minAllowedTokenRate, blockTimestampOfDeployment.add(maxAllowedTimeBetweenTokenRateUpdates.mul(2)).add(1)); + await tokenRateOracle.connect(bridge).updateRate(minAllowedTokenRate, blockTimestampOfDeployment.add(minTimeBetweenTokenRateUpdates.mul(2)).add(1)); assert.equalBN(await tokenRateOracle.latestAnswer(), minAllowedTokenRate); }) .test("updateRate() :: happy path called by bridge", async (ctx) => { const { tokenRateOracle } = ctx.contracts; const { bridge } = ctx.accounts; - const { tokenRate, blockTimestampOfDeployment, maxAllowedTimeBetweenTokenRateUpdates } = ctx.constants; + const { tokenRate, blockTimestampOfDeployment, minTimeBetweenTokenRateUpdates } = ctx.constants; const newTokenRate = tokenRate.mul(BigNumber.from('104')).div(BigNumber.from('100')); // 104% - const blockTimestampInFuture = blockTimestampOfDeployment.add(maxAllowedTimeBetweenTokenRateUpdates).add(1); + const blockTimestampInFuture = blockTimestampOfDeployment.add(minTimeBetweenTokenRateUpdates).add(1); const tx = await tokenRateOracle.connect(bridge).updateRate(newTokenRate, blockTimestampInFuture); await assert.emits(tokenRateOracle, tx, "TokenRateL1TimestampIsInFuture", [ @@ -489,13 +489,13 @@ unit("TokenRateOracle", ctxFactory) .test("updateRate() :: happy path called by messenger with correct cross-domain sender", async (ctx) => { const { tokenRateOracle, l2MessengerStub } = ctx.contracts; const { l2MessengerStubEOA, l1TokenBridgeEOA } = ctx.accounts; - const { tokenRate, blockTimestampOfDeployment, maxAllowedTimeBetweenTokenRateUpdates } = ctx.constants; + const { tokenRate, blockTimestampOfDeployment, minTimeBetweenTokenRateUpdates } = ctx.constants; await l2MessengerStub.setXDomainMessageSender(l1TokenBridgeEOA.address); const newTokenRate = tokenRate.mul(BigNumber.from('104')).div(BigNumber.from('100')); // 104% - const blockTimestampInFuture = blockTimestampOfDeployment.add(maxAllowedTimeBetweenTokenRateUpdates).add(1); + const blockTimestampInFuture = blockTimestampOfDeployment.add(minTimeBetweenTokenRateUpdates).add(1); const tx = await tokenRateOracle.connect(l2MessengerStubEOA).updateRate(newTokenRate, blockTimestampInFuture); await assert.emits(tokenRateOracle, tx, "TokenRateL1TimestampIsInFuture", [ @@ -604,7 +604,7 @@ unit("TokenRateOracle", ctxFactory) .test("pauseTokenRateUpdates() :: success", async (ctx) => { const { tokenRateOracle } = ctx.contracts; const { multisig, bridge } = ctx.accounts; - const { tokenRate, blockTimestampOfDeployment, maxAllowedTimeBetweenTokenRateUpdates } = ctx.constants; + const { tokenRate, blockTimestampOfDeployment, minTimeBetweenTokenRateUpdates } = ctx.constants; const disablerRole = await tokenRateOracle.RATE_UPDATE_DISABLER_ROLE(); await tokenRateOracle.grantRole(disablerRole, multisig.address); @@ -614,10 +614,10 @@ unit("TokenRateOracle", ctxFactory) const tokenRate2 = tokenRate.add(300); const tokenRate3 = tokenRate.add(300); - const blockTimestampOfDeployment0 = blockTimestampOfDeployment.add(maxAllowedTimeBetweenTokenRateUpdates).add(1); - const blockTimestampOfDeployment1 = blockTimestampOfDeployment.add(maxAllowedTimeBetweenTokenRateUpdates.mul(2)).add(1); - const blockTimestampOfDeployment2 = blockTimestampOfDeployment.add(maxAllowedTimeBetweenTokenRateUpdates.mul(3)).add(1); - const blockTimestampOfDeployment3 = blockTimestampOfDeployment.add(maxAllowedTimeBetweenTokenRateUpdates.mul(4)).add(1); + const blockTimestampOfDeployment0 = blockTimestampOfDeployment.add(minTimeBetweenTokenRateUpdates).add(1); + const blockTimestampOfDeployment1 = blockTimestampOfDeployment.add(minTimeBetweenTokenRateUpdates.mul(2)).add(1); + const blockTimestampOfDeployment2 = blockTimestampOfDeployment.add(minTimeBetweenTokenRateUpdates.mul(3)).add(1); + const blockTimestampOfDeployment3 = blockTimestampOfDeployment.add(minTimeBetweenTokenRateUpdates.mul(4)).add(1); const tx0 = await tokenRateOracle.connect(bridge).updateRate(tokenRate0, blockTimestampOfDeployment0); await assert.emits(tokenRateOracle, tx0, "RateUpdated", [tokenRate0, blockTimestampOfDeployment0]); @@ -677,10 +677,75 @@ unit("TokenRateOracle", ctxFactory) ); }) + .test("resumeTokenRateUpdates() :: token rate is out of range", async (ctx) => { + const { tokenRateOracle } = ctx.contracts; + const { multisig, bridge } = ctx.accounts; + const { tokenRate, blockTimestampOfDeployment } = ctx.constants; + + const disablerRole = await tokenRateOracle.RATE_UPDATE_DISABLER_ROLE(); + await tokenRateOracle.grantRole(disablerRole, multisig.address); + + const enablerRole = await tokenRateOracle.RATE_UPDATE_ENABLER_ROLE(); + await tokenRateOracle.grantRole(enablerRole, multisig.address); + + await tokenRateOracle.connect(bridge).updateRate(tokenRate, blockTimestampOfDeployment.add(1000)); + await tokenRateOracle.connect(multisig).pauseTokenRateUpdates(0); + + await assert.revertsWith( + tokenRateOracle.connect(multisig).resumeTokenRateUpdates(1, blockTimestampOfDeployment), + "ErrorTokenRateIsOutOfSaneRange(1)" + ); + }) + + .test("resumeTokenRateUpdates() :: time is out of clock lag", async (ctx) => { + const { tokenRateOracle } = ctx.contracts; + const { multisig, bridge } = ctx.accounts; + const { tokenRate, blockTimestampOfDeployment } = ctx.constants; + + const disablerRole = await tokenRateOracle.RATE_UPDATE_DISABLER_ROLE(); + await tokenRateOracle.grantRole(disablerRole, multisig.address); + + const enablerRole = await tokenRateOracle.RATE_UPDATE_ENABLER_ROLE(); + await tokenRateOracle.grantRole(enablerRole, multisig.address); + + await tokenRateOracle.connect(bridge).updateRate(tokenRate, blockTimestampOfDeployment.add(1000)); + await tokenRateOracle.connect(multisig).pauseTokenRateUpdates(0); + + const MAX_ALLOWED_L2_TO_L1_CLOCK_LAG = await tokenRateOracle.MAX_ALLOWED_L2_TO_L1_CLOCK_LAG(); + const newWrongTime = blockTimestampOfDeployment.add(MAX_ALLOWED_L2_TO_L1_CLOCK_LAG).add(1000); + + await assert.revertsWith( + tokenRateOracle.connect(multisig).resumeTokenRateUpdates(tokenRate, newWrongTime), + "ErrorL1TimestampExceededMaxAllowedClockLag("+newWrongTime+")" + ); + }) + + .test("resumeTokenRateUpdates() :: time is older than previous", async (ctx) => { + const { tokenRateOracle } = ctx.contracts; + const { multisig, bridge } = ctx.accounts; + const { tokenRate, blockTimestampOfDeployment } = ctx.constants; + + const disablerRole = await tokenRateOracle.RATE_UPDATE_DISABLER_ROLE(); + await tokenRateOracle.grantRole(disablerRole, multisig.address); + + const enablerRole = await tokenRateOracle.RATE_UPDATE_ENABLER_ROLE(); + await tokenRateOracle.grantRole(enablerRole, multisig.address); + + await tokenRateOracle.connect(bridge).updateRate(tokenRate, blockTimestampOfDeployment.add(1000)); + await tokenRateOracle.connect(multisig).pauseTokenRateUpdates(0); + + const newWrongTime = blockTimestampOfDeployment.sub(1000); + + await assert.revertsWith( + tokenRateOracle.connect(multisig).resumeTokenRateUpdates(tokenRate, newWrongTime), + "ErrorL1TimestampOlderThanPrevious("+newWrongTime+")" + ); + }) + .test("resumeTokenRateUpdates() :: success", async (ctx) => { const { tokenRateOracle } = ctx.contracts; const { multisig, bridge } = ctx.accounts; - const { tokenRate, blockTimestampOfDeployment, maxAllowedTimeBetweenTokenRateUpdates } = ctx.constants; + const { tokenRate, blockTimestampOfDeployment, minTimeBetweenTokenRateUpdates } = ctx.constants; const disablerRole = await tokenRateOracle.RATE_UPDATE_DISABLER_ROLE(); await tokenRateOracle.grantRole(disablerRole, multisig.address); @@ -688,7 +753,7 @@ unit("TokenRateOracle", ctxFactory) const enablerRole = await tokenRateOracle.RATE_UPDATE_ENABLER_ROLE(); await tokenRateOracle.grantRole(enablerRole, multisig.address); - await tokenRateOracle.connect(bridge).updateRate(tokenRate, blockTimestampOfDeployment.add(maxAllowedTimeBetweenTokenRateUpdates).add(1)); + await tokenRateOracle.connect(bridge).updateRate(tokenRate, blockTimestampOfDeployment.add(minTimeBetweenTokenRateUpdates).add(1)); assert.isFalse(await tokenRateOracle.isTokenRateUpdatesPaused()); await tokenRateOracle.connect(multisig).pauseTokenRateUpdates(0); assert.isTrue(await tokenRateOracle.isTokenRateUpdatesPaused()); @@ -707,7 +772,7 @@ unit("TokenRateOracle", ctxFactory) [tokenRate, blockTimestampOfDeployment] ); - const newTime = blockTimestampOfDeployment.add(maxAllowedTimeBetweenTokenRateUpdates.mul(2)).add(1); + const newTime = blockTimestampOfDeployment.add(minTimeBetweenTokenRateUpdates.mul(2)).add(1); const tx = await tokenRateOracle.connect(bridge).updateRate(tokenRate.add(100), newTime); await assert.emits(tokenRateOracle, tx, @@ -729,7 +794,7 @@ async function ctxFactory() { const maxAllowedL2ToL1ClockLag = BigNumber.from(86400 * 2); // 2 days const maxAllowedTokenRateDeviationPerDay = BigNumber.from(500); // 5% const oldestRateAllowedInPauseTimeSpan = BigNumber.from(86400*3); // 3 days - const maxAllowedTimeBetweenTokenRateUpdates = BigNumber.from(3600); // 1 hour + const minTimeBetweenTokenRateUpdates = BigNumber.from(3600); // 1 hour const [deployer, bridge, stranger, l1TokenBridgeEOA, multisig] = await hre.ethers.getSigners(); const zero = await hre.ethers.getSigner(hre.ethers.constants.AddressZero); @@ -752,7 +817,7 @@ async function ctxFactory() { maxAllowedL2ToL1ClockLag, maxAllowedTokenRateDeviationPerDay, oldestRateAllowedInPauseTimeSpan, - maxAllowedTimeBetweenTokenRateUpdates, + minTimeBetweenTokenRateUpdates, tokenRate, rateL1Timestamp ); @@ -780,7 +845,7 @@ async function ctxFactory() { maxAllowedL2ToL1ClockLag, maxAllowedTokenRateDeviationPerDay, oldestRateAllowedInPauseTimeSpan, - maxAllowedTimeBetweenTokenRateUpdates + minTimeBetweenTokenRateUpdates }, provider }; 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/test/optimism/pushingTokenRate.integration.test.ts b/test/optimism/pushingTokenRate.integration.test.ts index cab7d3da..4dd83d43 100644 --- a/test/optimism/pushingTokenRate.integration.test.ts +++ b/test/optimism/pushingTokenRate.integration.test.ts @@ -16,7 +16,8 @@ import { OptimismBridgeExecutor__factory, TokenRateNotifier__factory, TokenRateOracle__factory, - AccountingOracleStub__factory + AccountingOracleStub__factory, + EmptyContractStub__factory } from "../../typechain"; scenario("Optimism :: Token Rate Oracle integration test", ctxFactory) @@ -30,13 +31,12 @@ scenario("Optimism :: Token Rate Oracle integration test", ctxFactory) genesisTime, secondsPerSlot, lastProcessingRefSlot, - tokenRate + tokenRate, + lido } = ctx; - const account = ctx.accounts.accountA; - const tx = await tokenRateNotifier - .connect(account.l1Signer) + .connect(lido) .handlePostTokenRebase(1, 2, 3, 4, 5, 6, 7); const messageNonce = await l1CrossDomainMessenger.messageNonce(); @@ -112,7 +112,7 @@ async function ctxFactory() { const maxAllowedL2ToL1ClockLag = BigNumber.from(86400); const maxAllowedTokenRateDeviationPerDay = BigNumber.from(500); const oldestRateAllowedInPauseTimeSpan = BigNumber.from(86400*3); - const maxAllowedTimeBetweenTokenRateUpdates = BigNumber.from(3600); + const minTimeBetweenTokenRateUpdates = BigNumber.from(3600); const totalPooledEther = BigNumber.from('9309904612343950493629678'); const totalShares = BigNumber.from('7975822843597609202337218'); const tokenRateDecimals = BigNumber.from(27); @@ -169,6 +169,9 @@ async function ctxFactory() { const [l2ERC20TokenBridge] = await hre.ethers.getSigners(); + const lido = await new EmptyContractStub__factory(l1Deployer).deploy({ value: wei.toBigNumber(wei`1 ether`) }); + const lidoAsEOA = await testing.impersonate(lido.address, l1Provider); + const [ethDeployScript, optDeployScript] = await deploymentOracle( networkName ).oracleDeployScript( @@ -178,6 +181,7 @@ async function ctxFactory() { l2GasLimitForPushingTokenRate, tokenRateOutdatedDelay, { + lido: lido.address, deployer: l1Deployer, admins: { proxy: l1Deployer.address, @@ -196,7 +200,7 @@ async function ctxFactory() { maxAllowedL2ToL1ClockLag: maxAllowedL2ToL1ClockLag, maxAllowedTokenRateDeviationPerDayBp: maxAllowedTokenRateDeviationPerDay, oldestRateAllowedInPauseTimeSpan: oldestRateAllowedInPauseTimeSpan, - maxAllowedTimeBetweenTokenRateUpdates: maxAllowedTimeBetweenTokenRateUpdates, + minTimeBetweenTokenRateUpdates: minTimeBetweenTokenRateUpdates, tokenRate: tokenRate, l1Timestamp: BigNumber.from(blockTimestampInPast) } @@ -245,6 +249,7 @@ async function ctxFactory() { blockTimestamp, tokenRate, genesisTime, secondsPerSlot, lastProcessingRefSlot, + lido: lidoAsEOA, accounts: { accountA, l1CrossDomainMessengerAliased diff --git a/utils/deployment.ts b/utils/deployment.ts index ea444a39..9f7cc16e 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,30 @@ 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; + lido: string; + + /// L2 + /// Oracle + tokenRateOutdatedDelay: BigNumber; + maxAllowedL2ToL1ClockLag: BigNumber; + maxAllowedTokenRateDeviationPerDayBp: BigNumber; + oldestRateAllowedInPauseTimeSpan: BigNumber; + minTimeBetweenTokenRateUpdates: 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 +41,27 @@ 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"), + lido: env.address("LIDO"), + + /// 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")), + minTimeBetweenTokenRateUpdates: BigNumber.from(env.string("MIN_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 +86,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 +100,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/deploymentAllFromScratch.ts b/utils/optimism/deployment.ts similarity index 85% rename from utils/optimism/deploymentAllFromScratch.ts rename to utils/optimism/deployment.ts index 39457f3e..505d3151 100644 --- a/utils/optimism/deploymentAllFromScratch.ts +++ b/utils/optimism/deployment.ts @@ -7,40 +7,42 @@ import { DeployScript, Logger } from "../deployment/DeployScript"; import { ERC20BridgedPermit__factory, ERC20RebasableBridgedPermit__factory, - IERC20Metadata__factory, L1LidoTokensBridge__factory, L2ERC20ExtendedTokensBridge__factory, OssifiableProxy__factory, TokenRateOracle__factory, TokenRateNotifier__factory, - OpStackTokenRatePusher__factory + OpStackTokenRatePusher__factory, + IERC20Metadata__factory } from "../../typechain"; interface OptL1DeployScriptParams extends DeployScriptParams { - l1Token: string; + l1TokenNonRebasable: string; l1TokenRebasable: string; accountingOracle: string; - l2GasLimitForPushingTokenRate: number; + l2GasLimitForPushingTokenRate: BigNumber; + lido: string; } + interface OptL2DeployScriptParams extends DeployScriptParams { l2TokenNonRebasable: { - name: string; - symbol: string; + name?: string; + symbol?: string; version: string; - decimals: number; + decimals?: number; }; l2TokenRebasable: { - name: string; - symbol: string; + name?: string; + symbol?: string; version: string; - decimals: number; + decimals?: number; }; tokenRateOracle: { tokenRateOutdatedDelay: BigNumber; maxAllowedL2ToL1ClockLag: BigNumber; maxAllowedTokenRateDeviationPerDayBp: BigNumber; oldestRateAllowedInPauseTimeSpan: BigNumber; - maxAllowedTimeBetweenTokenRateUpdates: BigNumber; + minTimeBetweenTokenRateUpdates: BigNumber; tokenRate: BigNumber; l1Timestamp: BigNumber; } @@ -156,7 +158,7 @@ export default function deploymentAll( args: [ optAddresses.L1CrossDomainMessenger, expectedL2TokenBridgeProxyAddress, - l1Params.l1Token, + l1Params.l1TokenNonRebasable, l1Params.l1TokenRebasable, expectedL2TokenProxyAddress, expectedL2TokenRebasableProxyAddress, @@ -184,6 +186,7 @@ export default function deploymentAll( factory: TokenRateNotifier__factory, args: [ l1Params.deployer.address, + l1Params.lido, options?.overrides, ], afterDeploy: (c) => @@ -193,7 +196,7 @@ export default function deploymentAll( factory: OpStackTokenRatePusher__factory, args: [ optAddresses.L1CrossDomainMessenger, - l1Params.l1Token, + l1Params.l1TokenNonRebasable, l1Params.accountingOracle, expectedL2TokenRateOracleProxyAddress, l1Params.l2GasLimitForPushingTokenRate, @@ -203,6 +206,28 @@ export default function deploymentAll( 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, @@ -225,7 +250,7 @@ export default function deploymentAll( l2Params.tokenRateOracle.maxAllowedL2ToL1ClockLag, l2Params.tokenRateOracle.maxAllowedTokenRateDeviationPerDayBp, l2Params.tokenRateOracle.oldestRateAllowedInPauseTimeSpan, - l2Params.tokenRateOracle.maxAllowedTimeBetweenTokenRateUpdates, + l2Params.tokenRateOracle.minTimeBetweenTokenRateUpdates, options?.overrides, ], afterDeploy: (c) => @@ -252,10 +277,10 @@ export default function deploymentAll( .addStep({ factory: ERC20BridgedPermit__factory, args: [ - l2Params.l2TokenNonRebasable.name, - l2Params.l2TokenNonRebasable.symbol, + l2TokenNonRebasableName, + l2TokenNonRebasableSymbol, l2Params.l2TokenNonRebasable.version, - l2Params.l2TokenNonRebasable.decimals, + l2TokenNonRebasableDecimals, expectedL2TokenBridgeProxyAddress, options?.overrides, ], @@ -270,8 +295,8 @@ export default function deploymentAll( ERC20BridgedPermit__factory.createInterface().encodeFunctionData( "initialize", [ - l2Params.l2TokenNonRebasable.name, - l2Params.l2TokenNonRebasable.symbol, + l2TokenNonRebasableName, + l2TokenNonRebasableSymbol, l2Params.l2TokenNonRebasable.version ] ), @@ -283,10 +308,10 @@ export default function deploymentAll( .addStep({ factory: ERC20RebasableBridgedPermit__factory, args: [ - l2Params.l2TokenRebasable.name, - l2Params.l2TokenRebasable.symbol, + l2TokenRebasableName, + l2TokenRebasableSymbol, l2Params.l2TokenRebasable.version, - l2Params.l2TokenRebasable.decimals, + l2TokenRebasableDecimals, expectedL2TokenProxyAddress, expectedL2TokenRateOracleProxyAddress, expectedL2TokenBridgeProxyAddress, @@ -303,8 +328,8 @@ export default function deploymentAll( ERC20RebasableBridgedPermit__factory.createInterface().encodeFunctionData( "initialize", [ - l2Params.l2TokenRebasable.name, - l2Params.l2TokenRebasable.symbol, + l2TokenRebasableName, + l2TokenRebasableSymbol, l2Params.l2TokenRebasable.version ] ), @@ -318,7 +343,7 @@ export default function deploymentAll( args: [ optAddresses.L2CrossDomainMessenger, expectedL1TokenBridgeProxyAddress, - l1Params.l1Token, + l1Params.l1TokenNonRebasable, l1Params.l1TokenRebasable, expectedL2TokenProxyAddress, expectedL2TokenRebasableProxyAddress, 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/deploymentOracle.ts b/utils/optimism/deploymentOracle.ts index d715ddfe..d1390da3 100644 --- a/utils/optimism/deploymentOracle.ts +++ b/utils/optimism/deploymentOracle.ts @@ -12,14 +12,16 @@ import { OpStackTokenRatePusher__factory } from "../../typechain"; -interface OptDeployScriptParams extends DeployScriptParams { } +interface OptDeployScriptParams extends DeployScriptParams { + lido: string; +} interface OptL2DeployScriptParams extends DeployScriptParams { tokenRateOracle: { maxAllowedL2ToL1ClockLag: BigNumber; maxAllowedTokenRateDeviationPerDayBp: BigNumber; oldestRateAllowedInPauseTimeSpan: BigNumber; - maxAllowedTimeBetweenTokenRateUpdates: BigNumber; + minTimeBetweenTokenRateUpdates: BigNumber; tokenRate: BigNumber; l1Timestamp: BigNumber; } @@ -99,6 +101,7 @@ export default function deploymentOracle( factory: TokenRateNotifier__factory, args: [ l1Params.deployer.address, + l1Params.lido, options?.overrides, ], afterDeploy: (c) => @@ -134,7 +137,7 @@ export default function deploymentOracle( l2Params.tokenRateOracle.maxAllowedL2ToL1ClockLag, l2Params.tokenRateOracle.maxAllowedTokenRateDeviationPerDayBp, l2Params.tokenRateOracle.oldestRateAllowedInPauseTimeSpan, - l2Params.tokenRateOracle.maxAllowedTimeBetweenTokenRateUpdates, + l2Params.tokenRateOracle.minTimeBetweenTokenRateUpdates, options?.overrides, ], afterDeploy: (c) => 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..02f07e9a 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 minTimeBetweenTokenRateUpdates = BigNumber.from(3600); + const tokenRateOutdatedDelay = BigNumber.from(86400); + const ethDeployer = testingUtils.accounts.deployer(ethProvider); const optDeployer = testingUtils.accounts.deployer(optProvider); @@ -209,31 +222,21 @@ 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), + lido: ethDeployer.address, deployer: ethDeployer, admins: { proxy: ethDeployer.address, bridge: ethDeployer.address }, contractsShift: 0 @@ -247,7 +250,7 @@ async function deployTestBridge( maxAllowedL2ToL1ClockLag: maxAllowedL2ToL1ClockLag, maxAllowedTokenRateDeviationPerDayBp: maxAllowedTokenRateDeviationPerDay, oldestRateAllowedInPauseTimeSpan: oldestRateAllowedInPauseTimeSpan, - maxAllowedTimeBetweenTokenRateUpdates: maxAllowedTimeBetweenTokenRateUpdates, + minTimeBetweenTokenRateUpdates: minTimeBetweenTokenRateUpdates, tokenRate: tokenRate, l1Timestamp: BigNumber.from('1000') }, diff --git a/utils/optimism/upgrade.ts b/utils/optimism/upgrade.ts new file mode 100644 index 00000000..6acf7d3f --- /dev/null +++ b/utils/optimism/upgrade.ts @@ -0,0 +1,331 @@ +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; + lido: 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; + minTimeBetweenTokenRateUpdates: 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, + l1Params.lido, + 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.minTimeBetweenTokenRateUpdates, + 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/contractsFactory.ts b/utils/testing/contractsFactory.ts index 8d377eea..14b1d3ea 100644 --- a/utils/testing/contractsFactory.ts +++ b/utils/testing/contractsFactory.ts @@ -52,7 +52,7 @@ export async function tokenRateOracleUnderProxy( maxAllowedL2ToL1ClockLag: BigNumber, maxAllowedTokenRateDeviationPerDay: BigNumber, oldestRateAllowedInPauseTimeSpan: BigNumber, - maxAllowedTimeBetweenTokenRateUpdates: BigNumber, + minTimeBetweenTokenRateUpdates: BigNumber, tokenRate: BigNumber, rateL1Timestamp: BigNumber ) { @@ -64,7 +64,7 @@ export async function tokenRateOracleUnderProxy( maxAllowedL2ToL1ClockLag, maxAllowedTokenRateDeviationPerDay, oldestRateAllowedInPauseTimeSpan, - maxAllowedTimeBetweenTokenRateUpdates + minTimeBetweenTokenRateUpdates ); const tokenRateOracleProxy = new OssifiableProxy__factory(deployer); 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"); },