Skip to content

Commit

Permalink
Arbos 31 actions (#296)
Browse files Browse the repository at this point in the history
* 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 <cpbuckland88@gmail.com>

* 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 <im@gzeon.dev>

---------

Co-authored-by: Daniel Goldman <dzgoldman@wesleyan.edu>
Co-authored-by: Chris Buckland <cpbuckland88@gmail.com>
Co-authored-by: gzeon <im@gzeon.dev>
  • Loading branch information
4 people authored Aug 6, 2024
1 parent 544dcc4 commit fe89753
Show file tree
Hide file tree
Showing 10 changed files with 440 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, μ: 340009, ~: 339543)
SecurityCouncilMemberElectionGovernorTest:testSelectTopNominees(uint256) (runs: 256, μ: 339999, ~: 339822)
SecurityCouncilMemberElectionGovernorTest:testSelectTopNomineesFails() (gas: 273335)
SecurityCouncilMemberElectionGovernorTest:testSetFullWeightDuration() (gas: 34951)
SecurityCouncilMemberElectionGovernorTest:testVotesToWeight() (gas: 152898)
Expand Down
Original file line number Diff line number Diff line change
@@ -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
)
{}
}
Original file line number Diff line number Diff line change
@@ -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
)
{}
}
Original file line number Diff line number Diff line change
@@ -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
)
{}
}
Original file line number Diff line number Diff line change
@@ -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
)
{}
}
Original file line number Diff line number Diff line change
@@ -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) {}
}
Original file line number Diff line number Diff line change
@@ -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"
);
}
}
Original file line number Diff line number Diff line change
@@ -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"
);
}
}
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 = 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");
}
}
Loading

0 comments on commit fe89753

Please sign in to comment.