From fe89753974bdbd6f2d975f90c9a09a5e52e0708d Mon Sep 17 00:00:00 2001 From: Henry <11198460+godzillaba@users.noreply.github.com> Date: Tue, 6 Aug 2024 08:23:53 -0400 Subject: [PATCH] Arbos 31 actions (#296) * ArbOS30 AIP: add AIPNovaFeeRoutingAction (#230) * add AIPNovaFeeRoutingAction * add novaToL1Router address * AIPNovaFeeRoutingAction test and new balance requirement (#279) * add fork test * gas snapshot * update router --------- Co-authored-by: Henry <11198460+godzillaba@users.noreply.github.com> * stylus upgrade actions (#276) * stylus upgrade actions * verify isCacheManager; setWasmModuleRoot last * Updated AIPArbOS30UpgradeChallengeManagerAction to have public condroot and perform function typo --------- Co-authored-by: Chris Buckland * chore: cache manager addresses * chore: rename to arbos31 * feat: set cond root to current 0x8b10 * chore: remove todo * feat: update new and cond osp * feat: update challenge manager * feat: update v31 wasm root * update router (#300) * Arbos 31 timed upgrade action (#301) * feat: UpgradeArbOSVersionAtTimestampAction * upgrade at timestamp --------- Co-authored-by: gzeon --------- Co-authored-by: Daniel Goldman Co-authored-by: Chris Buckland Co-authored-by: gzeon --- .gas-snapshot | 3 +- ...OneAIPArbOS31AddWasmCacheManagerAction.sol | 14 ++ ...IPArbOS31UpgradeChallengeManagerAction.sol | 24 +++ ...ovaAIPArbOS31AddWasmCacheManagerAction.sol | 14 ++ ...IPArbOS31UpgradeChallengeManagerAction.sol | 22 +++ .../AIPArbOS31/SetArbOS31VersionAction.sol | 10 ++ .../AIPArbOS31AddWasmCacheManagerAction.sol | 56 +++++++ ...IPArbOS31UpgradeChallengeManagerAction.sol | 97 ++++++++++++ .../AIPs/AIPNovaFeeRoutingAction.sol | 140 ++++++++++++++++++ .../gov-actions/AIPNovaFeeRoutingAction.t.sol | 61 ++++++++ 10 files changed, 440 insertions(+), 1 deletion(-) create mode 100644 src/gov-action-contracts/AIPs/AIPArbOS31/ArbOneAIPArbOS31AddWasmCacheManagerAction.sol create mode 100644 src/gov-action-contracts/AIPs/AIPArbOS31/ArbOneAIPArbOS31UpgradeChallengeManagerAction.sol create mode 100644 src/gov-action-contracts/AIPs/AIPArbOS31/NovaAIPArbOS31AddWasmCacheManagerAction.sol create mode 100644 src/gov-action-contracts/AIPs/AIPArbOS31/NovaAIPArbOS31UpgradeChallengeManagerAction.sol create mode 100644 src/gov-action-contracts/AIPs/AIPArbOS31/SetArbOS31VersionAction.sol create mode 100644 src/gov-action-contracts/AIPs/AIPArbOS31/parent_contracts/AIPArbOS31AddWasmCacheManagerAction.sol create mode 100644 src/gov-action-contracts/AIPs/AIPArbOS31/parent_contracts/AIPArbOS31UpgradeChallengeManagerAction.sol create mode 100644 src/gov-action-contracts/AIPs/AIPNovaFeeRoutingAction.sol create mode 100644 test/gov-actions/AIPNovaFeeRoutingAction.t.sol diff --git a/.gas-snapshot b/.gas-snapshot index 524dcb07..ea2decea 100644 --- a/.gas-snapshot +++ b/.gas-snapshot @@ -1,4 +1,5 @@ AIP1Point2ActionTest:testAction() (gas: 629328) +AIPNovaFeeRoutingActionTest:testAction() (gas: 3074) ArbitrumDAOConstitutionTest:testConstructor() (gas: 259383) ArbitrumDAOConstitutionTest:testMonOwnerCannotSetHash() (gas: 262836) ArbitrumDAOConstitutionTest:testOwnerCanSetHash() (gas: 261148) @@ -143,7 +144,7 @@ SecurityCouncilMemberElectionGovernorTest:testOnlyNomineeElectionGovernorCanProp SecurityCouncilMemberElectionGovernorTest:testProperInitialization() (gas: 49388) SecurityCouncilMemberElectionGovernorTest:testProposeReverts() (gas: 32916) SecurityCouncilMemberElectionGovernorTest:testRelay() (gas: 42229) -SecurityCouncilMemberElectionGovernorTest:testSelectTopNominees(uint256) (runs: 256, μ: 340009, ~: 339543) +SecurityCouncilMemberElectionGovernorTest:testSelectTopNominees(uint256) (runs: 256, μ: 339999, ~: 339822) SecurityCouncilMemberElectionGovernorTest:testSelectTopNomineesFails() (gas: 273335) SecurityCouncilMemberElectionGovernorTest:testSetFullWeightDuration() (gas: 34951) SecurityCouncilMemberElectionGovernorTest:testVotesToWeight() (gas: 152898) diff --git a/src/gov-action-contracts/AIPs/AIPArbOS31/ArbOneAIPArbOS31AddWasmCacheManagerAction.sol b/src/gov-action-contracts/AIPs/AIPArbOS31/ArbOneAIPArbOS31AddWasmCacheManagerAction.sol new file mode 100644 index 00000000..0c6459dc --- /dev/null +++ b/src/gov-action-contracts/AIPs/AIPArbOS31/ArbOneAIPArbOS31AddWasmCacheManagerAction.sol @@ -0,0 +1,14 @@ +// SPDX-License-Identifier: Apache-2.0 +pragma solidity 0.8.16; + +import "./parent_contracts/AIPArbOS31AddWasmCacheManagerAction.sol"; + +/// @notice for deloployment on Arb One +contract ArbOneAIPArbOS31AddWasmCacheManagerAction is AIPArbOS31AddWasmCacheManagerAction { + constructor() + AIPArbOS31AddWasmCacheManagerAction( + 0x51dEDBD2f190E0696AFbEE5E60bFdE96d86464ec, // wasm cache manager + 31 // target arb os version + ) + {} +} diff --git a/src/gov-action-contracts/AIPs/AIPArbOS31/ArbOneAIPArbOS31UpgradeChallengeManagerAction.sol b/src/gov-action-contracts/AIPs/AIPArbOS31/ArbOneAIPArbOS31UpgradeChallengeManagerAction.sol new file mode 100644 index 00000000..1356e18d --- /dev/null +++ b/src/gov-action-contracts/AIPs/AIPArbOS31/ArbOneAIPArbOS31UpgradeChallengeManagerAction.sol @@ -0,0 +1,24 @@ +// SPDX-License-Identifier: Apache-2.0 +pragma solidity 0.8.16; + +import "./parent_contracts/AIPArbOS31UpgradeChallengeManagerAction.sol"; +import "@arbitrum/nitro-contracts/src/osp/IOneStepProofEntry.sol"; +import "../../address-registries/L1AddressRegistry.sol"; +import "@openzeppelin/contracts/proxy/transparent/ProxyAdmin.sol"; + +/// @notice for deloployment on L1 Ethereum +contract ArbOneAIPArbOS31UpgradeChallengeManagerAction is + AIPArbOS31UpgradeChallengeManagerAction +{ + constructor() + AIPArbOS31UpgradeChallengeManagerAction( + L1AddressRegistry(0xd514C2b3aaBDBfa10800B9C96dc1eB25427520A0), // l1 address registry + 0x260f5fa5c3176a856893642e149cf128b5a8de9f828afec8d11184415dd8dc69, // wasm module root + ProxyAdmin(0x554723262467F125Ac9e1cDFa9Ce15cc53822dbD), // l1 core proxy admin + 0x914B7b3053B35B84A24df08D7c9ceBCaEA4E2948, // challenge manager impl + IOneStepProofEntry(0xa328BAF257A937b7934429a5d8458d98693C6FC7), // new osp + 0x8b104a2e80ac6165dc58b9048de12f301d70b02a0ab51396c22b4b4b802a16a4, // cond root + IOneStepProofEntry(0x83fA8eD860514370fbcC5f04eA7969475F48CfEb) // cond osp + ) + {} +} diff --git a/src/gov-action-contracts/AIPs/AIPArbOS31/NovaAIPArbOS31AddWasmCacheManagerAction.sol b/src/gov-action-contracts/AIPs/AIPArbOS31/NovaAIPArbOS31AddWasmCacheManagerAction.sol new file mode 100644 index 00000000..094b24d1 --- /dev/null +++ b/src/gov-action-contracts/AIPs/AIPArbOS31/NovaAIPArbOS31AddWasmCacheManagerAction.sol @@ -0,0 +1,14 @@ +// SPDX-License-Identifier: Apache-2.0 +pragma solidity 0.8.16; + +import "./parent_contracts/AIPArbOS31AddWasmCacheManagerAction.sol"; + +/// @notice for deplpoyment on Nova +contract NovaAIPArbOS31AddWasmCacheManagerAction is AIPArbOS31AddWasmCacheManagerAction { + constructor() + AIPArbOS31AddWasmCacheManagerAction( + 0x20586F83bF11a7cee0A550C53B9DC9A5887de1b7, // wasm cache manager + 31 // target arb os version + ) + {} +} diff --git a/src/gov-action-contracts/AIPs/AIPArbOS31/NovaAIPArbOS31UpgradeChallengeManagerAction.sol b/src/gov-action-contracts/AIPs/AIPArbOS31/NovaAIPArbOS31UpgradeChallengeManagerAction.sol new file mode 100644 index 00000000..30e2c89e --- /dev/null +++ b/src/gov-action-contracts/AIPs/AIPArbOS31/NovaAIPArbOS31UpgradeChallengeManagerAction.sol @@ -0,0 +1,22 @@ +// SPDX-License-Identifier: Apache-2.0 +pragma solidity 0.8.16; + +import "./parent_contracts/AIPArbOS31UpgradeChallengeManagerAction.sol"; +import "@arbitrum/nitro-contracts/src/osp/IOneStepProofEntry.sol"; +import "../../address-registries/L1AddressRegistry.sol"; +import "@openzeppelin/contracts/proxy/transparent/ProxyAdmin.sol"; + +/// @notice for deloployment on L1 Ethereum +contract NovaAIPArbOS31UpgradeChallengeManagerAction is AIPArbOS31UpgradeChallengeManagerAction { + constructor() + AIPArbOS31UpgradeChallengeManagerAction( + L1AddressRegistry(0x2F06643fc2CC18585Ae790b546388F0DE4Ec6635), // l1 address registry + 0x260f5fa5c3176a856893642e149cf128b5a8de9f828afec8d11184415dd8dc69, // wasm module root + ProxyAdmin(0x71D78dC7cCC0e037e12de1E50f5470903ce37148), // l1 core proxy admin + 0x914B7b3053B35B84A24df08D7c9ceBCaEA4E2948, // challenge manager impl + IOneStepProofEntry(0xa328BAF257A937b7934429a5d8458d98693C6FC7), // new osp + 0x8b104a2e80ac6165dc58b9048de12f301d70b02a0ab51396c22b4b4b802a16a4, // cond root + IOneStepProofEntry(0x83fA8eD860514370fbcC5f04eA7969475F48CfEb) // cond osp + ) + {} +} diff --git a/src/gov-action-contracts/AIPs/AIPArbOS31/SetArbOS31VersionAction.sol b/src/gov-action-contracts/AIPs/AIPArbOS31/SetArbOS31VersionAction.sol new file mode 100644 index 00000000..cb97c2c2 --- /dev/null +++ b/src/gov-action-contracts/AIPs/AIPArbOS31/SetArbOS31VersionAction.sol @@ -0,0 +1,10 @@ +// SPDX-License-Identifier: Apache-2.0 +pragma solidity 0.8.16; + +import "../../arbos-upgrade/UpgradeArbOSVersionAtTimestampAction.sol"; + +/// @notice Sets the ArbOS version to 31. To be run on an arbitrum chain +/// @dev Identical copies of this contract to be deployed on Arb One and Nova for ArbOS31 upgrades +contract SetArbOS31VersionAction is UpgradeArbOSVersionAtTimestampAction { + constructor() UpgradeArbOSVersionAtTimestampAction(uint64(31), 1725382800) {} +} diff --git a/src/gov-action-contracts/AIPs/AIPArbOS31/parent_contracts/AIPArbOS31AddWasmCacheManagerAction.sol b/src/gov-action-contracts/AIPs/AIPArbOS31/parent_contracts/AIPArbOS31AddWasmCacheManagerAction.sol new file mode 100644 index 00000000..93182a53 --- /dev/null +++ b/src/gov-action-contracts/AIPs/AIPArbOS31/parent_contracts/AIPArbOS31AddWasmCacheManagerAction.sol @@ -0,0 +1,56 @@ + // SPDX-License-Identifier: Apache-2.0 +pragma solidity 0.8.16; + +import "@arbitrum/nitro-contracts/src/precompiles/ArbSys.sol"; + +// ArbOwner interface will include addWasmCacheManager as of stylus upgrade +interface IUpdatedArbOwner { + function addWasmCacheManager(address manager) external; +} + +// ArbWasmCache precompile interface +interface IArbWasmCache { + /// @notice See if the user is a cache manager. + function isCacheManager(address manager) external view returns (bool); +} + +// @notice For deployment on an Arbitrum chain; +// adds wasm cache manager only when the stylus ArbOS upgrade activates. +// Should be called via retryable ticket so that if executed before the ArbOS upgrade, +// it reverts can be retried until the target ArbOS version number is active. +contract AIPArbOS31AddWasmCacheManagerAction { + // wasm cache manager to add + address public immutable wasmCachemanager; + + // ArbOS version; use value as it's set and commonly used, NOT the value returned by + // ArbSys, which adds 55. E.g., the value here should be 31, not 85 + uint256 public immutable targetArbOSVersion; + + constructor(address _wasmCachemanager, uint256 _targetArbOSVersion) { + wasmCachemanager = _wasmCachemanager; + targetArbOSVersion = _targetArbOSVersion; + } + + function perform() external { + // getter returns a value offset by 55: https://github.com/OffchainLabs/nitro/blob/a20a1c70cc11ac52c7cfe6a20f00c880c2009a8f/precompiles/ArbSys.go#L64 + uint256 currentArbOsVersion = + ArbSys(0x0000000000000000000000000000000000000064).arbOSVersion() - 55; + // revert if target arbos version not reached; since this is executed by a retryable, can be re-executed until target version is reached + require( + targetArbOSVersion == currentArbOsVersion, + "AIPArbOS31AddWasmCacheManagerAction: ArbOS version" + ); + + IUpdatedArbOwner(0x0000000000000000000000000000000000000070).addWasmCacheManager( + wasmCachemanager + ); + + // verify: + require( + IArbWasmCache(0x0000000000000000000000000000000000000072).isCacheManager( + wasmCachemanager + ), + "AIPArbOS31AddWasmCacheManagerAction: is cache manager" + ); + } +} diff --git a/src/gov-action-contracts/AIPs/AIPArbOS31/parent_contracts/AIPArbOS31UpgradeChallengeManagerAction.sol b/src/gov-action-contracts/AIPs/AIPArbOS31/parent_contracts/AIPArbOS31UpgradeChallengeManagerAction.sol new file mode 100644 index 00000000..90fe3254 --- /dev/null +++ b/src/gov-action-contracts/AIPs/AIPArbOS31/parent_contracts/AIPArbOS31UpgradeChallengeManagerAction.sol @@ -0,0 +1,97 @@ +// SPDX-License-Identifier: Apache-2.0 +pragma solidity 0.8.16; + +import "@arbitrum/nitro-contracts/src/precompiles/ArbSys.sol"; +import "@arbitrum/nitro-contracts/src/osp/IOneStepProofEntry.sol"; +import "@openzeppelin/contracts/utils/Address.sol"; +import "@openzeppelin/contracts/proxy/transparent/ProxyAdmin.sol"; +import "../../../address-registries/L1AddressRegistry.sol"; + +interface IChallengeManagerUpgradeInit { + function postUpgradeInit(IOneStepProofEntry osp_, bytes32 condRoot, IOneStepProofEntry condOsp) + external; + function osp() external returns (address); +} + +// @notice set wasm module root and upgrade challenge manager for stylus ArbOS upgrade +contract AIPArbOS31UpgradeChallengeManagerAction { + L1AddressRegistry public immutable l1AddressRegistry; + bytes32 public immutable newWasmModuleRoot; + ProxyAdmin public immutable govProxyAdmin; + address public immutable newChallengeManagerImpl; + IOneStepProofEntry public immutable osp; + bytes32 public immutable condRoot; + IOneStepProofEntry public immutable condOsp; + + constructor( + L1AddressRegistry _l1AddressRegistry, + bytes32 _newWasmModuleRoot, + ProxyAdmin _govProxyAdmin, + address _newChallengeManagerImpl, + IOneStepProofEntry _osp, + bytes32 _condRoot, + IOneStepProofEntry _condOsp + ) { + require( + Address.isContract(address(_l1AddressRegistry)), + "AIPArbOS31UpgradeChallengeManagerAction: l1AddressRegistry is not a contract" + ); + require( + Address.isContract(address(_govProxyAdmin)), + "AIPArbOS31UpgradeChallengeManagerAction: _govProxyAdmin is not a contract" + ); + require( + Address.isContract(_newChallengeManagerImpl), + "AIPArbOS31UpgradeChallengeManagerAction: _newChallengeManagerImpl is not a contract" + ); + + require( + Address.isContract(address(_osp)), + "AIPArbOS31UpgradeChallengeManagerAction: _osp is not a contract" + ); + + require( + Address.isContract(address(_condOsp)), + "AIPArbOS31UpgradeChallengeManagerAction: _condOsp is not a contract" + ); + l1AddressRegistry = _l1AddressRegistry; + newWasmModuleRoot = _newWasmModuleRoot; + govProxyAdmin = _govProxyAdmin; + newChallengeManagerImpl = _newChallengeManagerImpl; + osp = _osp; + condRoot = _condRoot; + condOsp = _condOsp; + } + + function perform() external { + // set the new challenge manager impl + TransparentUpgradeableProxy challengeManager = TransparentUpgradeableProxy( + payable(address(l1AddressRegistry.rollup().challengeManager())) + ); + govProxyAdmin.upgradeAndCall( + challengeManager, + newChallengeManagerImpl, + abi.encodeCall(IChallengeManagerUpgradeInit.postUpgradeInit, (osp, condRoot, condOsp)) + ); + + // verify + require( + govProxyAdmin.getProxyImplementation(challengeManager) == newChallengeManagerImpl, + "AIPArbOS31UpgradeChallengeManagerAction: new challenge manager implementation set" + ); + require( + IChallengeManagerUpgradeInit(address(challengeManager)).osp() == address(osp), + "AIPArbOS31UpgradeChallengeManagerAction: new OSP not set" + ); + + // set new wasm module root + IRollupCore rollup = l1AddressRegistry.rollup(); + IRollupAdmin(address(rollup)).setWasmModuleRoot(newWasmModuleRoot); + + // verify: + require( + rollup.wasmModuleRoot() == newWasmModuleRoot, + "AIPArbOS31UpgradeChallengeManagerAction: wasm module root not set" + ); + } +} diff --git a/src/gov-action-contracts/AIPs/AIPNovaFeeRoutingAction.sol b/src/gov-action-contracts/AIPs/AIPNovaFeeRoutingAction.sol new file mode 100644 index 00000000..24e51852 --- /dev/null +++ b/src/gov-action-contracts/AIPs/AIPNovaFeeRoutingAction.sol @@ -0,0 +1,140 @@ +// SPDX-License-Identifier: Apache-2.0 +pragma solidity 0.8.16; + +import "@openzeppelin/contracts/utils/Address.sol"; + +interface IRewardDistributor { + function distributeAndUpdateRecipients( + address[] memory currentRecipients, + uint256[] memory currentWeights, + address[] memory newRecipients, + uint256[] memory newWeights + ) external; + + function currentRecipientGroup() external view returns (bytes32); + function currentRecipientWeights() external view returns (bytes32); +} + +/// @notice Governance action to be deployed on Nova. Updates all l1 timelock alias recipients in all fee distribution +/// contracts to the nova-to-l1 router address; preserves all other recipients and all weights. +/// Note that the Nova L1 Base fee distributor is not updated since the timelock alias is not a recipient. +contract AIPNovaFeeRoutingAction { + address public immutable l1GovTimelockAlias = 0xf7951D92B0C345144506576eC13Ecf5103aC905a; + uint256 public immutable fullWeight = 10_000; + + address public immutable novaL1SurplusFeeDistr = 0x509386DbF5C0BE6fd68Df97A05fdB375136c32De; + + address public immutable novaL2SurplusFeeDistr = 0x3B68a689c929327224dBfCe31C1bf72Ffd2559Ce; + + address public immutable novaL2BaseFeeDistr = 0x9fCB6F75D99029f28F6F4a1d277bae49c5CAC79f; + uint256 public immutable novaL2BaseWeight0 = 8000; + uint256 public immutable novaL2BaseWeight1 = 375; + uint256 public immutable novaL2BaseWeight2 = 373; + uint256 public immutable novaL2BaseWeight3 = 373; + uint256 public immutable novaL2BaseWeight4 = 373; + uint256 public immutable novaL2BaseWeight5 = 373; + uint256 public immutable novaL2BaseWeight6 = 133; + + // novaL2BaseRecipient0 is the gov timelock alias + address public immutable novaL2BaseRecipient1 = 0xD0749b3e537Ed52DE4e6a3Ae1eB6fc26059d0895; + address public immutable novaL2BaseRecipient2 = 0x41C327d5fc9e29680CcD45e5E52446E0DB3DAdFd; + address public immutable novaL2BaseRecipient3 = 0x02C2599aa929e2509741b44F3a13029745aB1AB2; + address public immutable novaL2BaseRecipient4 = 0xA221f29236996BDEfA5C585acdD407Ec84D78447; + address public immutable novaL2BaseRecipient5 = 0x0fB1f1a31429F1A90a19Ab5486a6DFb384179641; + address public immutable novaL2BaseRecipient6 = 0xb814441ed86e98e8B83d31eEC095e4a5A36Fc3c2; + + address public immutable novaToL1Router = 0x36D0170D92F66e8949eB276C3AC4FEA64f83704d; + + error NotAContract(address addr); + + constructor() { + // sanity check: + if (!Address.isContract(novaToL1Router)) { + revert NotAContract(novaToL1Router); + } + } + + function perform() external { + // upgrade executor should have at least 3 * fullWeight ETH to fund the distributors + // we need each of the reward distributors to have at least fullWeight in balance + // otherwise we may get NoFundsToDistribute() errors + require(address(this).balance >= 3 * fullWeight, "AIPNovaFeeRoutingAction: insufficient balance"); + _fundDistributor(novaL1SurplusFeeDistr); + _fundDistributor(novaL2SurplusFeeDistr); + _fundDistributor(novaL2BaseFeeDistr); + + // L1 surplus: replace only recipient (timelock alias) with the router + address[] memory currentNovaL1SurplusRecipients = new address[](1); + currentNovaL1SurplusRecipients[0] = l1GovTimelockAlias; + + uint256[] memory currentNovaL1SurplusWeights = new uint256[](1); + currentNovaL1SurplusWeights[0] = fullWeight; + + address[] memory newL1SurplusRecipients = new address[](1); + newL1SurplusRecipients[0] = novaToL1Router; + + // preserve current weights, update recipients + IRewardDistributor(novaL1SurplusFeeDistr).distributeAndUpdateRecipients({ + currentRecipients: currentNovaL1SurplusRecipients, + currentWeights: currentNovaL1SurplusWeights, + newRecipients: newL1SurplusRecipients, + newWeights: currentNovaL1SurplusWeights + }); + + // L2 surplus: replace only recipient (timelock alias) with the router + address[] memory currentNovaL2SurplusRecipients = new address[](1); + currentNovaL2SurplusRecipients[0] = l1GovTimelockAlias; + + uint256[] memory novaL2SurplusWeights = new uint256[](1); + novaL2SurplusWeights[0] = fullWeight; + + address[] memory newL2SurplusRecipients = new address[](1); + newL2SurplusRecipients[0] = novaToL1Router; + + // preserve current weights, update recipients + IRewardDistributor(novaL2SurplusFeeDistr).distributeAndUpdateRecipients({ + currentRecipients: currentNovaL2SurplusRecipients, + currentWeights: novaL2SurplusWeights, + newRecipients: newL2SurplusRecipients, + newWeights: novaL2SurplusWeights + }); + + // L2 base: replace first recipient (timelock alias) with router; keep other recipients the same. + address[] memory currentNovaL2BaseRecipients = new address[](7); + currentNovaL2BaseRecipients[0] = l1GovTimelockAlias; + currentNovaL2BaseRecipients[1] = novaL2BaseRecipient1; + currentNovaL2BaseRecipients[2] = novaL2BaseRecipient2; + currentNovaL2BaseRecipients[3] = novaL2BaseRecipient3; + currentNovaL2BaseRecipients[4] = novaL2BaseRecipient4; + currentNovaL2BaseRecipients[5] = novaL2BaseRecipient5; + currentNovaL2BaseRecipients[6] = novaL2BaseRecipient6; + + address[] memory newNovaL2BaseRecipients = new address[](7); + newNovaL2BaseRecipients[0] = novaToL1Router; + for (uint256 i = 1; i < currentNovaL2BaseRecipients.length; i++) { + newNovaL2BaseRecipients[i] = currentNovaL2BaseRecipients[i]; + } + + uint256[] memory currentNovaL2BaseWeights = new uint256[](7); + currentNovaL2BaseWeights[0] = novaL2BaseWeight0; + currentNovaL2BaseWeights[1] = novaL2BaseWeight1; + currentNovaL2BaseWeights[2] = novaL2BaseWeight2; + currentNovaL2BaseWeights[3] = novaL2BaseWeight3; + currentNovaL2BaseWeights[4] = novaL2BaseWeight4; + currentNovaL2BaseWeights[5] = novaL2BaseWeight5; + currentNovaL2BaseWeights[6] = novaL2BaseWeight6; + + // preserve current weights, update recipients + IRewardDistributor(novaL2BaseFeeDistr).distributeAndUpdateRecipients({ + currentRecipients: currentNovaL2BaseRecipients, + currentWeights: currentNovaL2BaseWeights, + newRecipients: newNovaL2BaseRecipients, + newWeights: currentNovaL2BaseWeights + }); + } + + function _fundDistributor(address recipient) internal { + (bool b, ) = recipient.call{value: fullWeight}(""); + require(b, "AIPNovaFeeRoutingAction: funding failed"); + } +} diff --git a/test/gov-actions/AIPNovaFeeRoutingAction.t.sol b/test/gov-actions/AIPNovaFeeRoutingAction.t.sol new file mode 100644 index 00000000..93b79753 --- /dev/null +++ b/test/gov-actions/AIPNovaFeeRoutingAction.t.sol @@ -0,0 +1,61 @@ +// SPDX-License-Identifier: Apache-2.0 +pragma solidity 0.8.16; + +// this test is meant to be run with a nova fork url + +import "forge-std/Test.sol"; + +import "../../src/gov-action-contracts/AIPs/AIPNovaFeeRoutingAction.sol"; +import "../../src/UpgradeExecutor.sol"; + +contract AIPNovaFeeRoutingActionTest is Test { + UpgradeExecutor constant upExec = UpgradeExecutor(0x86a02dD71363c440b21F4c0E5B2Ad01Ffe1A7482); + + function testAction() public { + if (!isFork()) { + return; + } + + AIPNovaFeeRoutingAction action = new AIPNovaFeeRoutingAction(); + + // before we run the action, we need to make sure the upgrade executor has at least this much ETH + vm.deal(address(upExec), 3*action.fullWeight()); + + vm.prank(0xf7951D92B0C345144506576eC13Ecf5103aC905a); + upExec.execute(address(action), abi.encodeWithSignature("perform()")); + + // make sure the new recipients are set + + address[1] memory expectedL1SurplusRecipients = [0xd9a2e0E5d7509F0BF1B2d33884F8C1b4D4490879]; + uint256[1] memory expectedL1SurplusWeights = [uint(10_000)]; + + assertEq(IRewardDistributor(action.novaL1SurplusFeeDistr()).currentRecipientGroup(), keccak256(abi.encodePacked(expectedL1SurplusRecipients))); + assertEq(IRewardDistributor(action.novaL1SurplusFeeDistr()).currentRecipientWeights(), keccak256(abi.encodePacked(expectedL1SurplusWeights))); + assertEq(IRewardDistributor(action.novaL2SurplusFeeDistr()).currentRecipientGroup(), keccak256(abi.encodePacked(expectedL1SurplusRecipients))); + assertEq(IRewardDistributor(action.novaL2SurplusFeeDistr()).currentRecipientWeights(), keccak256(abi.encodePacked(expectedL1SurplusWeights))); + + + address[7] memory expectedBaseFeeRecipients = [ + 0xd9a2e0E5d7509F0BF1B2d33884F8C1b4D4490879, // nova to l1 router + 0xD0749b3e537Ed52DE4e6a3Ae1eB6fc26059d0895, // rest are same as current + 0x41C327d5fc9e29680CcD45e5E52446E0DB3DAdFd, + 0x02C2599aa929e2509741b44F3a13029745aB1AB2, + 0xA221f29236996BDEfA5C585acdD407Ec84D78447, + 0x0fB1f1a31429F1A90a19Ab5486a6DFb384179641, + 0xb814441ed86e98e8B83d31eEC095e4a5A36Fc3c2 + ]; + + uint256[7] memory expectedBaseFeeWeights = [ + uint256(8000), + uint256(375), + uint256(373), + uint256(373), + uint256(373), + uint256(373), + uint256(133) + ]; + + assertEq(IRewardDistributor(action.novaL2BaseFeeDistr()).currentRecipientGroup(), keccak256(abi.encodePacked(expectedBaseFeeRecipients))); + assertEq(IRewardDistributor(action.novaL2BaseFeeDistr()).currentRecipientWeights(), keccak256(abi.encodePacked(expectedBaseFeeWeights))); + } +} \ No newline at end of file