Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

ArbOS30 AIP: add AIPNovaFeeRoutingAction #230

Merged
merged 5 commits into from
Jul 9, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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)));
}
}
Loading