Skip to content

Commit

Permalink
ArbOS30 AIP: add AIPNovaFeeRoutingAction (#230)
Browse files Browse the repository at this point in the history
* 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>
  • Loading branch information
DZGoldman and godzillaba authored Jul 9, 2024
1 parent 06039ce commit 20b1404
Show file tree
Hide file tree
Showing 3 changed files with 203 additions and 1 deletion.
3 changes: 2 additions & 1 deletion .gas-snapshot
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
AIP1Point2ActionTest:testAction() (gas: 629328)
AIPNovaFeeRoutingActionTest:testAction() (gas: 3074)
ArbitrumDAOConstitutionTest:testConstructor() (gas: 259383)
ArbitrumDAOConstitutionTest:testMonOwnerCannotSetHash() (gas: 262836)
ArbitrumDAOConstitutionTest:testOwnerCanSetHash() (gas: 261148)
Expand Down Expand Up @@ -143,7 +144,7 @@ SecurityCouncilMemberElectionGovernorTest:testOnlyNomineeElectionGovernorCanProp
SecurityCouncilMemberElectionGovernorTest:testProperInitialization() (gas: 49388)
SecurityCouncilMemberElectionGovernorTest:testProposeReverts() (gas: 32916)
SecurityCouncilMemberElectionGovernorTest:testRelay() (gas: 42229)
SecurityCouncilMemberElectionGovernorTest:testSelectTopNominees(uint256) (runs: 256, μ: 339956, ~: 339703)
SecurityCouncilMemberElectionGovernorTest:testSelectTopNominees(uint256) (runs: 256, μ: 339646, ~: 339587)
SecurityCouncilMemberElectionGovernorTest:testSelectTopNomineesFails() (gas: 273335)
SecurityCouncilMemberElectionGovernorTest:testSetFullWeightDuration() (gas: 34951)
SecurityCouncilMemberElectionGovernorTest:testVotesToWeight() (gas: 152898)
Expand Down
140 changes: 140 additions & 0 deletions src/gov-action-contracts/AIPs/AIPNovaFeeRoutingAction.sol
Original file line number Diff line number Diff line change
@@ -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 = 0xd9a2e0E5d7509F0BF1B2d33884F8C1b4D4490879;

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");
}
}
61 changes: 61 additions & 0 deletions test/gov-actions/AIPNovaFeeRoutingAction.t.sol
Original file line number Diff line number Diff line change
@@ -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)));
}
}

0 comments on commit 20b1404

Please sign in to comment.