From b79a3d1a148d5113ea2ed88547ba381b5145fc1a Mon Sep 17 00:00:00 2001 From: jtriley Date: Tue, 14 Jul 2026 18:42:46 -0700 Subject: [PATCH 01/55] add govkit --- .gitmodules | 3 +++ lib/govkit | 1 + 2 files changed, 4 insertions(+) create mode 160000 lib/govkit diff --git a/.gitmodules b/.gitmodules index 4a8855bd..9b6490e2 100644 --- a/.gitmodules +++ b/.gitmodules @@ -31,3 +31,6 @@ [submodule "lib/native-token-transfers"] path = lib/native-token-transfers url = https://github.com/wormhole-foundation/native-token-transfers +[submodule "lib/govkit"] + path = lib/govkit + url = https://github.com/Uniswap/govkit diff --git a/lib/govkit b/lib/govkit new file mode 160000 index 00000000..f57ab52e --- /dev/null +++ b/lib/govkit @@ -0,0 +1 @@ +Subproject commit f57ab52e69726ada693b9d22b9ab05d55d2de6ce From a911f695eedef5f3fc675bf46187e8475acece5a Mon Sep 17 00:00:00 2001 From: jtriley Date: Tue, 14 Jul 2026 18:43:00 -0700 Subject: [PATCH 02/55] fs perms --- foundry.toml | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/foundry.toml b/foundry.toml index a285e9bd..f8ca2707 100644 --- a/foundry.toml +++ b/foundry.toml @@ -15,7 +15,10 @@ compilation_restrictions = [ { paths = "lib/native-token-transfers/evm/src/NttManager/NttManagerNoRateLimiting.sol", optimizer_runs = 200 } ] -fs_permissions = [{ access = "read", path = "./broadcast"}] +fs_permissions = [ + { access = "read", path = "./broadcast"}, + { access = "read-write", path = "./.records/" } +] [rpc_endpoints] mainnet = "${MAINNET_RPC_URL}" From 814a3745c35744af92816dcdd750281d632c6938 Mon Sep 17 00:00:00 2001 From: jtriley Date: Tue, 14 Jul 2026 18:43:09 -0700 Subject: [PATCH 03/55] govkit remap --- remappings.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/remappings.txt b/remappings.txt index 4f0f371f..c67842e0 100644 --- a/remappings.txt +++ b/remappings.txt @@ -7,3 +7,4 @@ src/AgreementAnchorFactory.sol=lib/dao-signer/src/AgreementAnchorFactory.sol src/AgreementAnchor.sol=lib/dao-signer/src/AgreementAnchor.sol src/interfaces/IAgreementAnchorFactory.sol=lib/dao-signer/src/interfaces/IAgreementAnchorFactory.sol openzeppelin-contracts/contracts/=lib/native-token-transfers/evm/lib/openzeppelin-contracts/contracts/ +govkit/=lib/govkit/src/ From 0415686d00f3007b6cf3f4a3f937b734c6dc1914 Mon Sep 17 00:00:00 2001 From: jtriley Date: Tue, 14 Jul 2026 18:43:51 -0700 Subject: [PATCH 04/55] add arb orbit deployer & releaser --- script/deployers/ArbitrumOrbitDeployer.sol | 109 ++++++++++++++++++ .../ArbitrumOrbitResourceFirepit.sol | 72 ++++++++++++ 2 files changed, 181 insertions(+) create mode 100644 script/deployers/ArbitrumOrbitDeployer.sol create mode 100644 src/releasers/ArbitrumOrbitResourceFirepit.sol diff --git a/script/deployers/ArbitrumOrbitDeployer.sol b/script/deployers/ArbitrumOrbitDeployer.sol new file mode 100644 index 00000000..a883d54a --- /dev/null +++ b/script/deployers/ArbitrumOrbitDeployer.sol @@ -0,0 +1,109 @@ +// SPDX-License-Identifier: AGPL-3.0-only +pragma solidity ^0.8.29; + +import {ITokenJar} from "../../src/interfaces/ITokenJar.sol"; +import {TokenJar} from "../../src/TokenJar.sol"; +import {IReleaser} from "../../src/interfaces/IReleaser.sol"; +import {IV3OpenFeeAdapter} from "../../src/interfaces/IV3OpenFeeAdapter.sol"; +import {V3OpenFeeAdapter} from "../../src/feeAdapters/V3OpenFeeAdapter.sol"; +import {IOwned} from "../../src/interfaces/base/IOwned.sol"; +import {ArbitrumOrbitResourceFirepit} from "../../src/releasers/ArbitrumOrbitResourceFirepit.sol"; + +/// @title ArbitrumOrbitDeployer +/// @notice Deployer for TokenJar + ArbitrumOrbitResourceFirepit + V3OpenFeeAdapter on Arbitrum +/// Orbit chain +/// @dev Deploys and configures the fee collection system with Arbitrum Orbit-specific parameters. +/// The `_owner` parameter is expected to be the **aliased** L1 Timelock address. +/// Arbitrum aliases all L1->L2 messages from contracts at the protocol level — retryable +/// tickets via `Inbox.createRetryableTicket()` are the canonical messaging path and always +/// produce `msg.sender == aliasedAddress` on L2, satisfying the `onlyOwner` check. +/// Unlike OP Stack, there is no alternative un-aliased route for contracts on Arbitrum Orbit +/// chain. +contract ArbitrumOrbitDeployer { + error ZeroAddress(); + error ZeroThreshold(); + + ITokenJar public immutable TOKEN_JAR; + IReleaser public immutable RELEASER; + IV3OpenFeeAdapter public immutable V3_OPEN_FEE_ADAPTER; + + bytes32 public constant SALT_TOKEN_JAR = bytes32(uint256(1)); + bytes32 public constant SALT_RELEASER = bytes32(uint256(2)); + bytes32 public constant SALT_FEE_ADAPTER = bytes32(uint256(3)); + + // Protocol fee defaults — same as mainnet + uint8 constant DEFAULT_FEE_100 = 4 << 4 | 4; // 1/4 for 0.01% tier + uint8 constant DEFAULT_FEE_500 = 4 << 4 | 4; // 1/4 for 0.05% tier + uint8 constant DEFAULT_FEE_3000 = 6 << 4 | 6; // 1/6 for 0.30% tier + uint8 constant DEFAULT_FEE_10000 = 6 << 4 | 6; // 1/6 for 1.00% tier + + /// @notice Deploys TokenJar, ArbitrumOrbitResourceFirepit, and V3OpenFeeAdapter + /// @param _resource The bridged UNI token address on Arbitrum Orbit chain (L2) + /// @param _l1Resource The UNI token address on Ethereum mainnet (L1) + /// @param _threshold The minimum UNI amount required per release + /// @param _owner The aliased owner address — must be the L1 Timelock address + 0x1111...1111 + /// @param _v3Factory The Uniswap V3 Factory address on Arbitrum Orbit chain + constructor( + address _l2GatewayRouter, + address _resource, + address _l1Resource, + uint256 _threshold, + address _owner, + address _v3Factory + ) { + require(_l2GatewayRouter != address(0), ZeroAddress()); + require(_resource != address(0), ZeroAddress()); + require(_l1Resource != address(0), ZeroAddress()); + require(_threshold > 0, ZeroThreshold()); + require(_owner != address(0), ZeroAddress()); + require(_v3Factory != address(0), ZeroAddress()); + + /// 1. Deploy the TokenJar + TOKEN_JAR = new TokenJar{salt: SALT_TOKEN_JAR}(); + + /// 2. Deploy the Releaser + RELEASER = new ArbitrumOrbitResourceFirepit{salt: SALT_RELEASER}( + _l2GatewayRouter, _resource, _l1Resource, _threshold, address(TOKEN_JAR) + ); + + /// 3. Set the releaser on the token jar + TOKEN_JAR.setReleaser(address(RELEASER)); + + /// 4. Update the owner on the token jar + IOwned(address(TOKEN_JAR)).transferOwnership(_owner); + + /// 5. Update the thresholdSetter on the releaser to the owner + RELEASER.setThresholdSetter(_owner); + + /// 6. Update the owner on the releaser + IOwned(address(RELEASER)).transferOwnership(_owner); + + /// 7. Deploy and configure V3OpenFeeAdapter + V3OpenFeeAdapter feeAdapter = + new V3OpenFeeAdapter{salt: SALT_FEE_ADAPTER}(_v3Factory, address(TOKEN_JAR)); + + // Configure fee tier defaults + feeAdapter.setFeeSetter(address(this)); + + // Set default fee (applied when no tier or pool override is set) + feeAdapter.setDefaultFee(DEFAULT_FEE_100); + + // Set fee tier defaults + feeAdapter.setFeeTierDefault(100, DEFAULT_FEE_100); + feeAdapter.setFeeTierDefault(500, DEFAULT_FEE_500); + feeAdapter.setFeeTierDefault(3000, DEFAULT_FEE_3000); + feeAdapter.setFeeTierDefault(10_000, DEFAULT_FEE_10000); + + // Store fee tiers + feeAdapter.storeFeeTier(100); + feeAdapter.storeFeeTier(500); + feeAdapter.storeFeeTier(3000); + feeAdapter.storeFeeTier(10_000); + + // Transfer feeSetter and ownership to aliased Timelock + feeAdapter.setFeeSetter(_owner); + feeAdapter.transferOwnership(_owner); + + V3_OPEN_FEE_ADAPTER = IV3OpenFeeAdapter(address(feeAdapter)); + } +} diff --git a/src/releasers/ArbitrumOrbitResourceFirepit.sol b/src/releasers/ArbitrumOrbitResourceFirepit.sol new file mode 100644 index 00000000..88de11fc --- /dev/null +++ b/src/releasers/ArbitrumOrbitResourceFirepit.sol @@ -0,0 +1,72 @@ +// SPDX-License-Identifier: AGPL-3.0-only +pragma solidity ^0.8.29; + +import {ERC20} from "solmate/src/tokens/ERC20.sol"; +import {SafeTransferLib} from "solmate/src/utils/SafeTransferLib.sol"; +import {IL2GatewayRouter} from "../interfaces/external/IL2GatewayRouter.sol"; +import {Currency} from "v4-core/types/Currency.sol"; +import {ExchangeReleaser} from "./ExchangeReleaser.sol"; + +/// @title ArbitrumOrbitResourceFirepit +/// @notice A releaser that implements a two-stage burn process for bridged resource tokens on +/// Arbitrum Orbit chains +/// @dev Two-stage burn from Arbitrum Orbit L2's to the underlying resource on L1 +/// **Stage 1: L2 Collection and Bridge Initiation** +/// - User calls `release()` providing resource tokens as payment +/// - ExchangeReleaser transfers resource tokens from user to this smart contract +/// - TokenJar releases accumulated fee assets to the specified recipient +/// - _afterRelease() initiates bridge withdrawal to L1 burn address (0xdead) +/// **Stage 2: L1 Finalization** +/// - L2GatewayRouter burns the L2 tokens held by this contract +/// - Cross-domain message is queued (~7-day challenge period on mainnet) +/// - After challenge period, L1 gateway releases escrowed tokens to 0xdead on L1 +contract ArbitrumOrbitResourceFirepit is ExchangeReleaser { + using SafeTransferLib for ERC20; + + error ZeroAddress(); + + /// @dev The L2 Gateway Router address on the Arbitrum Orbit chain + address public immutable L2_GATEWAY_ROUTER; + + /// @dev The L1 UNI token address (Ethereum mainnet) + /// @dev Required for Arbitrum Orbit gateway calls which use L1 token address + address public immutable L1_RESOURCE; + + /// @dev Final recipient of the bridged resource on L1 (burn address) + /// @dev Note: This is different from RESOURCE_RECIPIENT which is address(this) on L2 + address internal constant L1_RESOURCE_RECIPIENT = address(0xdead); + + /// @notice Creates a new ArbitrumOrbitResourceFirepit instance + /// @param _resource The address of the resource token on Arbitrum Orbit chain (L2 bridged token) + /// @param _l1Resource The address of the resource token on Ethereum (L1 original token) + /// @param _threshold The minimum amount of resource tokens required for exchange + /// @param _tokenJar The address of the TokenJar contract holding accumulated fee assets + /// @dev Sets RESOURCE_RECIPIENT to address(this) to enable the two-stage burn: + /// Stage 1: Collect tokens here (L2) -> Stage 2: Bridge and burn on L1 + constructor( + address _l2GatewayRouter, + address _resource, + address _l1Resource, + uint256 _threshold, + address _tokenJar + ) ExchangeReleaser(_resource, _threshold, _tokenJar, address(this)) { + L2_GATEWAY_ROUTER = _l2GatewayRouter; + L1_RESOURCE = _l1Resource; + } + + /// @notice Hook called after assets are released - initiates stage 2 withdrawal to L1 + function _afterRelease(Currency[] calldata, address) internal override { + // Stage 2: Initiate bridge withdrawal to L1 burn address + // The gateway will: + // 1. Burn the L2 tokens held by this contract + // 2. Queue a cross-domain message for L1 + // 3. After challenge period, release underlying resource tokens to 0xdead on L1 + IL2GatewayRouter(L2_GATEWAY_ROUTER) + .outboundTransfer( + L1_RESOURCE, // L1 token address (required by Arbitrum Orbit chain gateway) + L1_RESOURCE_RECIPIENT, // Destination on L1 (burn address) + threshold, // Amount to withdraw + bytes("") // Empty extra data for standard withdrawal + ); + } +} From 72fda81cb77132d516f4613956a634692688a010 Mon Sep 17 00:00:00 2001 From: jtriley Date: Tue, 14 Jul 2026 18:44:08 -0700 Subject: [PATCH 05/55] add robinhood --- script/proposal-5/Constants.sol | 16 +++ script/proposal-5/Interfaces.sol | 104 ++++++++++++++++++ script/proposal-5/RobinhoodFees.s.sol | 73 ++++++++++++ .../prereq/DeployFeeInfraRobinhood.s.sol | 81 ++++++++++++++ 4 files changed, 274 insertions(+) create mode 100644 script/proposal-5/Constants.sol create mode 100644 script/proposal-5/Interfaces.sol create mode 100644 script/proposal-5/RobinhoodFees.s.sol create mode 100644 script/proposal-5/prereq/DeployFeeInfraRobinhood.s.sol diff --git a/script/proposal-5/Constants.sol b/script/proposal-5/Constants.sol new file mode 100644 index 00000000..4e8a1456 --- /dev/null +++ b/script/proposal-5/Constants.sol @@ -0,0 +1,16 @@ +// SPDX-License-Identifier: AGPl-3.0-only +pragma solidity 0.8.29; + +library Ethereum { + address constant RH_L1_ERC20_GATEWAY = 0x85001CC4867C5e1C22dA4B79BB8852B9e2a06da0; + address constant RH_INBOX = 0x1A07cc4BD17E0118BdB54D70990D2158AbAD7a2D; +} + +library Robinhood { + uint256 constant CHAIN_ID = 4663; + + address constant V2_FACTORY = 0x8bcEaA40B9AcdfAedF85AdF4FF01F5Ad6517937f; + address constant V3_FACTORY = 0x1f7d7550B1b028f7571E69A784071F0205FD2EfA; + address constant UNI = 0x4540A3483Bb94827b0FD9c2b16a6A35Dd6F23529; + address constant L2_GATEWAY_ROUTER = 0x1E324B9316138CA9a73F960213621AD1aaf01B89; +} diff --git a/script/proposal-5/Interfaces.sol b/script/proposal-5/Interfaces.sol new file mode 100644 index 00000000..11704ff3 --- /dev/null +++ b/script/proposal-5/Interfaces.sol @@ -0,0 +1,104 @@ +// SPDX-License-Identifier: AGPl-3.0-only +pragma solidity 0.8.29; + +interface IL1ERC20Gateway { + // Events + event DepositInitiated( + address l1Token, + address indexed _from, + address indexed _to, + uint256 indexed _sequenceNumber, + uint256 _amount + ); + event TxToL2(address indexed _from, address indexed _to, uint256 indexed _seqNum, bytes _data); + event WithdrawRedirected( + address indexed from, + address indexed to, + uint256 indexed exitNum, + bytes newData, + bytes data, + bool madeExternalCall + ); + event WithdrawalFinalized( + address l1Token, + address indexed _from, + address indexed _to, + uint256 indexed _exitNum, + uint256 _amount + ); + event AdminChanged(address previousAdmin, address newAdmin); + event BeaconUpgraded(address indexed beacon); + event Upgraded(address indexed implementation); + + // Functions + function calculateL2TokenAddress(address l1ERC20) external view returns (address); + function cloneableProxyHash() external view returns (bytes32); + function counterpartGateway() external view returns (address); + function encodeWithdrawal(uint256 _exitNum, address _initialDestination) + external + pure + returns (bytes32); + function finalizeInboundTransfer( + address _token, + address _from, + address _to, + uint256 _amount, + bytes memory _data + ) external payable; + function getExternalCall(uint256 _exitNum, address _initialDestination, bytes memory _initialData) + external + view + returns (address target, bytes memory data); + function getOutboundCalldata( + address _token, + address _from, + address _to, + uint256 _amount, + bytes memory _data + ) external view returns (bytes memory outboundCalldata); + function inbox() external view returns (address); + function initialize( + address _l2Counterpart, + address _router, + address _inbox, + bytes32 _cloneableProxyHash, + address _l2BeaconProxyFactory + ) external; + function l2BeaconProxyFactory() external view returns (address); + function outboundTransfer( + address _l1Token, + address _to, + uint256 _amount, + uint256 _maxGas, + uint256 _gasPriceBid, + bytes memory _data + ) external payable returns (bytes memory res); + function outboundTransferCustomRefund( + address _l1Token, + address _refundTo, + address _to, + uint256 _amount, + uint256 _maxGas, + uint256 _gasPriceBid, + bytes memory _data + ) external payable returns (bytes memory res); + function postUpgradeInit() external; + function redirectedExits(bytes32) + external + view + returns (bool isExit, address _newTo, bytes memory _newData); + function router() external view returns (address); + function supportsInterface(bytes4 interfaceId) external view returns (bool); + function transferExitAndCall( + uint256 _exitNum, + address _initialDestination, + address _newDestination, + bytes memory _newData, + bytes memory _data + ) external; + function whitelist() external view returns (address); +} + +interface IArbitrumOrbitResourceFirepit { + function L2_GATEWAY_ROUTER() external pure returns (address); +} diff --git a/script/proposal-5/RobinhoodFees.s.sol b/script/proposal-5/RobinhoodFees.s.sol new file mode 100644 index 00000000..d4d314f7 --- /dev/null +++ b/script/proposal-5/RobinhoodFees.s.sol @@ -0,0 +1,73 @@ +// SPDX-License-Identifier: AGPl-3.0-only +pragma solidity 0.8.29; + +import {Script, console} from "forge-std/Script.sol"; + +import {Recorder} from "govkit/forge/Recorder.sol"; +import {ChainId} from "govkit/constants/ChainId.sol"; +import {Uniswap} from "govkit/types/Uniswap.sol"; +import {InboxEncoder} from "govkit/bridges/InboxEncoder.sol"; +import {GovernanceSeatbelt} from "lib/govkit/src/forge/GovernanceSeatbelt.sol"; +import {Call, LibCall} from "lib/govkit/src/types/Call.sol"; +import {Proposal} from "lib/govkit/src/types/Proposal.sol"; + +import {IUniswapV2Factory} from "govkit/interfaces/IUniswapV2Factory.sol"; +import {IUniswapV3Factory} from "govkit/interfaces/IUniswapV3Factory.sol"; +import {IPoolManager} from "govkit/interfaces/IPoolManager.sol"; +import {ITokenJar} from "govkit/interfaces/ITokenJar.sol"; +import {IReleaser} from "govkit/interfaces/IReleaser.sol"; +import {IV3OpenFeeAdapter} from "govkit/interfaces/IV3OpenFeeAdapter.sol"; + +import "./Constants.sol" as Constants; + +string constant DESCRIPTION = "TODO"; + +contract RobinhoodFees is Script { + Recorder internal recorder; + Uniswap internal uniswap; + + function run() external { + uniswap.loadLatest(); + recorder.initialize({scriptName: "DeployFeeInfraRobinhood"}); + + address tokenJar = + recorder.read({chainId: Constants.Robinhood.CHAIN_ID, deploymentName: "TokenJar"}); + + // --------------------------------------------------------------------------------------------- + // 00: Set Fee Receiver on V2 Factor to on Robinhood + // + Call memory v2SetFeeTo = InboxEncoder.encode({ + inbox: Constants.Ethereum.RH_INBOX, + timelock: uniswap.ethereum.timelock, + remoteCall: Call({ + target: Constants.Robinhood.V2_FACTORY, + value: 0, + data: abi.encodeCall(IUniswapV2Factory.setFeeTo, (tokenJar)) + }) + }); + + // --------------------------------------------------------------------------------------------- + // 01: Transfer Ownership of V3 Factory to V3 Open Fee Adapter on Robinhood + // + Call memory v3SetOwner = InboxEncoder.encode({ + inbox: Constants.Ethereum.RH_INBOX, + timelock: uniswap.ethereum.timelock, + remoteCall: Call({ + target: Constants.Robinhood.V3_FACTORY, + value: 0, + data: abi.encodeCall(IUniswapV3Factory.setOwner, (tokenJar)) + }) + }); + + Proposal memory robinhoodFeeProposal = + Proposal({description: DESCRIPTION, calls: LibCall.newCalls([v2SetFeeTo, v3SetOwner])}); + + vm.createDir("./out/.seatbelt/", true); + vm.writeFile({ + path: "./out/.seatbelt/RobinhoodFeeProposal.json", + data: GovernanceSeatbelt.toJson({ + proposal: robinhoodFeeProposal, governorBravo: uniswap.ethereum.governorBravo + }) + }); + } +} diff --git a/script/proposal-5/prereq/DeployFeeInfraRobinhood.s.sol b/script/proposal-5/prereq/DeployFeeInfraRobinhood.s.sol new file mode 100644 index 00000000..ca8c1873 --- /dev/null +++ b/script/proposal-5/prereq/DeployFeeInfraRobinhood.s.sol @@ -0,0 +1,81 @@ +// SPDX-License-Identifier: AGPl-3.0-only +pragma solidity 0.8.29; + +import {Script, console} from "forge-std/Script.sol"; +import {IPoolManager} from "v4-core/interfaces/IPoolManager.sol"; +import {Recorder} from "govkit/forge/Recorder.sol"; +import {ChainId} from "govkit/constants/ChainId.sol"; +import {Uniswap} from "govkit/types/Uniswap.sol"; +import {InboxEncoder} from "govkit/bridges/InboxEncoder.sol"; + +import {ITokenJar} from "govkit/interfaces/ITokenJar.sol"; +import {IReleaser} from "govkit/interfaces/IReleaser.sol"; +import {IV3OpenFeeAdapter} from "govkit/interfaces/IV3OpenFeeAdapter.sol"; + +import {ArbitrumOrbitDeployer} from "../../deployers/ArbitrumOrbitDeployer.sol"; +import "../Constants.sol" as Constants; + +string constant DEPLOYER_NAME = "ArbitrumOrbitDeployer"; +uint256 constant THRESHOLD = 2000e18; + +contract DeployFeeInfraRobinhood is Script { + Recorder internal recorder; + Uniswap internal uniswap; + ArbitrumOrbitDeployer internal deployer; + + function run() external { + uniswap.loadLatest(); + recorder.initialize({scriptName: "DeployFeeInfraRobinhood"}); + + address timelockAlias = InboxEncoder.arbitrumAlias(uniswap.ethereum.timelock); + + if (!recorder.exists(Constants.Robinhood.CHAIN_ID, DEPLOYER_NAME)) { + vm.startBroadcast(); + + deployer = new ArbitrumOrbitDeployer({ + _l2GatewayRouter: Constants.Robinhood.L2_GATEWAY_ROUTER, + _resource: Constants.Robinhood.UNI, + _l1Resource: uniswap.ethereum.uni, + _threshold: THRESHOLD, + _owner: timelockAlias, + _v3Factory: Constants.Robinhood.V3_FACTORY + }); + + vm.stopBroadcast(); + + recorder.write({ + chainId: Constants.Robinhood.CHAIN_ID, + deploymentName: DEPLOYER_NAME, + deployment: address(deployer) + }); + } else { + deployer = ArbitrumOrbitDeployer( + recorder.read({chainId: Constants.Robinhood.CHAIN_ID, deploymentName: DEPLOYER_NAME}) + ); + } + + require(address(deployer) != address(0x00), "deployer is null"); + + address tokenJar = address(deployer.TOKEN_JAR()); + address releaser = address(deployer.RELEASER()); + address v3OpenFeeAdapter = address(deployer.V3_OPEN_FEE_ADAPTER()); + + require(ITokenJar(tokenJar).owner() == timelockAlias); + require(IReleaser(releaser).thresholdSetter() == timelockAlias); + require(IReleaser(releaser).owner() == timelockAlias); + require(IV3OpenFeeAdapter(v3OpenFeeAdapter).owner() == timelockAlias); + require(IV3OpenFeeAdapter(v3OpenFeeAdapter).feeSetter() == timelockAlias); + + recorder.write({ + chainId: Constants.Robinhood.CHAIN_ID, deploymentName: "TokenJar", deployment: tokenJar + }); + recorder.write({ + chainId: Constants.Robinhood.CHAIN_ID, deploymentName: "Releaser", deployment: releaser + }); + recorder.write({ + chainId: Constants.Robinhood.CHAIN_ID, + deploymentName: "V3OpenFeeAdapter", + deployment: v3OpenFeeAdapter + }); + } +} From 724b32276b64635e58af9381deae98b23e4b37c1 Mon Sep 17 00:00:00 2001 From: jtriley Date: Tue, 14 Jul 2026 18:44:30 -0700 Subject: [PATCH 06/55] update fee adapter version for script --- src/feeAdapters/V4FeeAdapter.sol | 2 +- src/feeAdapters/V4FeePolicy.sol | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/feeAdapters/V4FeeAdapter.sol b/src/feeAdapters/V4FeeAdapter.sol index f7b3aa58..68d1ee8c 100644 --- a/src/feeAdapters/V4FeeAdapter.sol +++ b/src/feeAdapters/V4FeeAdapter.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: MIT -pragma solidity 0.8.26; +pragma solidity ^0.8.26; import {Owned} from "solmate/src/auth/Owned.sol"; import {IPoolManager} from "v4-core/interfaces/IPoolManager.sol"; diff --git a/src/feeAdapters/V4FeePolicy.sol b/src/feeAdapters/V4FeePolicy.sol index 58f4e05b..69ea73bf 100644 --- a/src/feeAdapters/V4FeePolicy.sol +++ b/src/feeAdapters/V4FeePolicy.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: MIT -pragma solidity 0.8.26; +pragma solidity ^0.8.26; import {Owned} from "solmate/src/auth/Owned.sol"; import {IPoolManager} from "v4-core/interfaces/IPoolManager.sol"; From e35839927ef4deb2083207c1c8a1fdc615f50d37 Mon Sep 17 00:00:00 2001 From: jtriley Date: Tue, 14 Jul 2026 18:45:11 -0700 Subject: [PATCH 07/55] add v4 --- script/proposal-6/Constants.sol | 48 ++ script/proposal-6/Interfaces.sol | 6 + script/proposal-6/StableStablePairs.sol | 814 ++++++++++++++++++ script/proposal-6/V4FeesProposal.s.sol | 222 +++++ .../proposal-6/prereq/DeployV4FeeInfra.s.sol | 270 ++++++ 5 files changed, 1360 insertions(+) create mode 100644 script/proposal-6/Constants.sol create mode 100644 script/proposal-6/Interfaces.sol create mode 100644 script/proposal-6/StableStablePairs.sol create mode 100644 script/proposal-6/V4FeesProposal.s.sol create mode 100644 script/proposal-6/prereq/DeployV4FeeInfra.s.sol diff --git a/script/proposal-6/Constants.sol b/script/proposal-6/Constants.sol new file mode 100644 index 00000000..e4668ffc --- /dev/null +++ b/script/proposal-6/Constants.sol @@ -0,0 +1,48 @@ +// SPDX-License-Identifier: MIT +pragma solidity 0.8.29; + +library V4FeeController { + address constant Ethereum = address(0x00); + address constant Base = address(0x00); + address constant Robinhood = address(0x00); + address constant BNBChain = address(0x00); + address constant Arbitrum = address(0x00); + address constant Optimism = address(0x00); + address constant Polygon = address(0x00); + + address constant Celo = address(0x00); + address constant Soneium = address(0x00); + address constant XLayer = address(0x00); + address constant WorldChain = address(0x00); + address constant Zora = address(0x00); + + function smokeCheck() internal pure { + require(Ethereum != address(0x00), "Ethereum is not set."); + require(Arbitrum != address(0x00), "Arbitrum is not set."); + require(Base != address(0x00), "Base is not set."); + require(Celo != address(0x00), "Celo is not set."); + require(Optimism != address(0x00), "Optimism is not set."); + require(Soneium != address(0x00), "Soneium is not set."); + require(XLayer != address(0x00), "XLayer is not set."); + require(WorldChain != address(0x00), "Worldchain is not set."); + require(Zora != address(0x00), "Zora is not set."); + require(BNBChain != address(0x00), "BNBChain is not set."); + require(Polygon != address(0x00), "Polygon is not set."); + } +} + +library Ethereum { + address constant RH_L1_ERC20_GATEWAY = 0x85001CC4867C5e1C22dA4B79BB8852B9e2a06da0; + address constant RH_INBOX = 0x1A07cc4BD17E0118BdB54D70990D2158AbAD7a2D; +} + +library Robinhood { + uint256 constant CHAIN_ID = 4663; + + address constant V2_FACTORY = 0x8bcEaA40B9AcdfAedF85AdF4FF01F5Ad6517937f; + address constant V3_FACTORY = 0x1f7d7550B1b028f7571E69A784071F0205FD2EfA; + address constant POOL_MANAGER = 0x8366a39CC670B4001A1121B8F6A443A643e40951; + address constant UNI = 0x4540A3483Bb94827b0FD9c2b16a6A35Dd6F23529; + address constant TOKEN_JAR = address(0x00); + address constant L2_GATEWAY_ROUTER = 0x1E324B9316138CA9a73F960213621AD1aaf01B89; +} diff --git a/script/proposal-6/Interfaces.sol b/script/proposal-6/Interfaces.sol new file mode 100644 index 00000000..1f3c3652 --- /dev/null +++ b/script/proposal-6/Interfaces.sol @@ -0,0 +1,6 @@ +// SPDX-License-Identifier: AGPl-3.0-only +pragma solidity 0.8.29; + +interface IArbitrumOrbitResourceFirepit { + function L2_GATEWAY_ROUTER() external pure returns (address); +} diff --git a/script/proposal-6/StableStablePairs.sol b/script/proposal-6/StableStablePairs.sol new file mode 100644 index 00000000..269be2f5 --- /dev/null +++ b/script/proposal-6/StableStablePairs.sol @@ -0,0 +1,814 @@ +// SPDX-License-Identifier: MIT +pragma solidity 0.8.29; + +import {ChainId} from "lib/govkit/src/constants/ChainId.sol"; +import "./Constants.sol" as Constants; + +struct Pair { + address token0; + address token1; +} + +struct StableStablePairs { + mapping(uint256 => Pair[]) chainPairs; +} + +using LibStableStablePairs for StableStablePairs global; + +library LibStableStablePairs { + function initialize(StableStablePairs storage stableStablePairs) internal { + Pair[] storage ethereumPairs = stableStablePairs.chainPairs[ChainId.Ethereum]; + ethereumPairs.push( + Pair(0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48, 0xdAC17F958D2ee523a2206206994597C13D831ec7) + ); + ethereumPairs.push( + Pair(0xdC035D45d973E3EC169d2276DDab16f1e407384F, 0xdAC17F958D2ee523a2206206994597C13D831ec7) + ); + ethereumPairs.push( + Pair(0x6c3ea9036406852006290770BEdFcAbA0e23A0e8, 0xdC035D45d973E3EC169d2276DDab16f1e407384F) + ); + ethereumPairs.push( + Pair(0x4c9EDD5852cd905f086C759E8383e09bff1E68B3, 0xdAC17F958D2ee523a2206206994597C13D831ec7) + ); + ethereumPairs.push( + Pair(0x8292Bb45bf1Ee4d140127049757C2E0fF06317eD, 0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48) + ); + ethereumPairs.push( + Pair(0x6B175474E89094C44Da98b954EedeAC495271d0F, 0xdAC17F958D2ee523a2206206994597C13D831ec7) + ); + ethereumPairs.push( + Pair(0xf939E0A03FB07F59A73314E73794Be0E57ac1b4E, 0xdAC17F958D2ee523a2206206994597C13D831ec7) + ); + ethereumPairs.push( + Pair(0xacA92E438df0B2401fF60dA7E4337B687a2435DA, 0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48) + ); + ethereumPairs.push( + Pair(0xdD468A1DDc392dcdbEf6db6e34E89AA338F9F186, 0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48) + ); + ethereumPairs.push( + Pair(0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48, 0xC139190F447e929f090Edeb554D95AbB8b18aC1C) + ); + ethereumPairs.push( + Pair(0xa3931d71877C0E7a3148CB7Eb4463524FEc27fbD, 0xdAC17F958D2ee523a2206206994597C13D831ec7) + ); + ethereumPairs.push( + Pair(0x9D39A5DE30e57443BfF2A8307A4256c8797A3497, 0xdAC17F958D2ee523a2206206994597C13D831ec7) + ); + ethereumPairs.push( + Pair(0xf939E0A03FB07F59A73314E73794Be0E57ac1b4E, 0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48) + ); + ethereumPairs.push( + Pair(0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48, 0xe343167631d89B6Ffc58B88d6b7fB0228795491D) + ); + ethereumPairs.push( + Pair(0x6c3ea9036406852006290770BEdFcAbA0e23A0e8, 0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48) + ); + ethereumPairs.push( + Pair(0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48, 0x4c9EDD5852cd905f086C759E8383e09bff1E68B3) + ); + ethereumPairs.push( + Pair(0x6B175474E89094C44Da98b954EedeAC495271d0F, 0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48) + ); + ethereumPairs.push( + Pair(0x23238f20b894f29041f48D88eE91131C395Aaa71, 0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48) + ); + ethereumPairs.push( + Pair(0x085780639CC2cACd35E474e71f4d000e2405d8f6, 0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48) + ); + ethereumPairs.push( + Pair(0x40D16FC0246aD3160Ccc09B8D0D3A2cD28aE6C2f, 0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48) + ); + ethereumPairs.push( + Pair(0xf939E0A03FB07F59A73314E73794Be0E57ac1b4E, 0xCAcd6fd266aF91b8AeD52aCCc382b4e165586E29) + ); + ethereumPairs.push( + Pair(0xCAcd6fd266aF91b8AeD52aCCc382b4e165586E29, 0xa3931d71877C0E7a3148CB7Eb4463524FEc27fbD) + ); + ethereumPairs.push( + Pair(0x8d0D000Ee44948FC98c9B98A4FA4921476f08B0d, 0xdAC17F958D2ee523a2206206994597C13D831ec7) + ); + ethereumPairs.push( + Pair(0x40D16FC0246aD3160Ccc09B8D0D3A2cD28aE6C2f, 0xdAC17F958D2ee523a2206206994597C13D831ec7) + ); + ethereumPairs.push( + Pair(0x853d955aCEf822Db058eb8505911ED77F175b99e, 0x4c9EDD5852cd905f086C759E8383e09bff1E68B3) + ); + ethereumPairs.push( + Pair(0x6440f144b7e50D6a8439336510312d2F54beB01D, 0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48) + ); + ethereumPairs.push( + Pair(0x83F20F44975D03b1b09e64809B757c47f942BEeA, 0x9D39A5DE30e57443BfF2A8307A4256c8797A3497) + ); + ethereumPairs.push( + Pair(0x4c9EDD5852cd905f086C759E8383e09bff1E68B3, 0xC139190F447e929f090Edeb554D95AbB8b18aC1C) + ); + ethereumPairs.push( + Pair(0xf939E0A03FB07F59A73314E73794Be0E57ac1b4E, 0x40D16FC0246aD3160Ccc09B8D0D3A2cD28aE6C2f) + ); + ethereumPairs.push( + Pair(0x80ac24aA929eaF5013f6436cdA2a7ba190f5Cc0b, 0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48) + ); + ethereumPairs.push( + Pair(0x40D16FC0246aD3160Ccc09B8D0D3A2cD28aE6C2f, 0x9D39A5DE30e57443BfF2A8307A4256c8797A3497) + ); + ethereumPairs.push( + Pair(0x9D39A5DE30e57443BfF2A8307A4256c8797A3497, 0xa3931d71877C0E7a3148CB7Eb4463524FEc27fbD) + ); + ethereumPairs.push( + Pair(0x865377367054516e17014CcdED1e7d814EDC9ce4, 0x9D39A5DE30e57443BfF2A8307A4256c8797A3497) + ); + ethereumPairs.push( + Pair(0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48, 0xdC035D45d973E3EC169d2276DDab16f1e407384F) + ); + ethereumPairs.push( + Pair(0x0655977FEb2f289A4aB78af67BAB0d17aAb84367, 0x9D39A5DE30e57443BfF2A8307A4256c8797A3497) + ); + ethereumPairs.push( + Pair(0x853d955aCEf822Db058eb8505911ED77F175b99e, 0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48) + ); + ethereumPairs.push( + Pair(0x57aB1E0003F623289CD798B1824Be09a793e4Bec, 0x0655977FEb2f289A4aB78af67BAB0d17aAb84367) + ); + ethereumPairs.push( + Pair(0x8d0D000Ee44948FC98c9B98A4FA4921476f08B0d, 0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48) + ); + ethereumPairs.push( + Pair(0x356B8d89c1e1239Cbbb9dE4815c39A1474d5BA7D, 0xdAC17F958D2ee523a2206206994597C13D831ec7) + ); + ethereumPairs.push( + Pair(0xCAcd6fd266aF91b8AeD52aCCc382b4e165586E29, 0xcf62F905562626CfcDD2261162a51fd02Fc9c5b6) + ); + ethereumPairs.push( + Pair(0xe343167631d89B6Ffc58B88d6b7fB0228795491D, 0xdAC17F958D2ee523a2206206994597C13D831ec7) + ); + ethereumPairs.push( + Pair(0x865377367054516e17014CcdED1e7d814EDC9ce4, 0xa3931d71877C0E7a3148CB7Eb4463524FEc27fbD) + ); + ethereumPairs.push( + Pair(0xf939E0A03FB07F59A73314E73794Be0E57ac1b4E, 0x9D39A5DE30e57443BfF2A8307A4256c8797A3497) + ); + ethereumPairs.push( + Pair(0xf939E0A03FB07F59A73314E73794Be0E57ac1b4E, 0x6c3ea9036406852006290770BEdFcAbA0e23A0e8) + ); + ethereumPairs.push( + Pair(0x865377367054516e17014CcdED1e7d814EDC9ce4, 0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48) + ); + ethereumPairs.push( + Pair(0xA0d69E286B938e21CBf7E51D71F6A4c8918f482F, 0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48) + ); + ethereumPairs.push( + Pair(0xFa2B947eEc368f42195f24F36d2aF29f7c24CeC2, 0xdAC17F958D2ee523a2206206994597C13D831ec7) + ); + ethereumPairs.push( + Pair(0x73A15FeD60Bf67631dC6cd7Bc5B6e8da8190aCF5, 0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48) + ); + ethereumPairs.push( + Pair(0x7C135549504245B5eAe64fc0E99Fa5ebabb8e35D, 0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48) + ); + ethereumPairs.push( + Pair(0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48, 0xFa2B947eEc368f42195f24F36d2aF29f7c24CeC2) + ); + ethereumPairs.push( + Pair(0x7C135549504245B5eAe64fc0E99Fa5ebabb8e35D, 0xdAC17F958D2ee523a2206206994597C13D831ec7) + ); + ethereumPairs.push( + Pair(0x6c3ea9036406852006290770BEdFcAbA0e23A0e8, 0xdAC17F958D2ee523a2206206994597C13D831ec7) + ); + ethereumPairs.push( + Pair(0xCAcd6fd266aF91b8AeD52aCCc382b4e165586E29, 0x4c9EDD5852cd905f086C759E8383e09bff1E68B3) + ); + ethereumPairs.push( + Pair(0x0655977FEb2f289A4aB78af67BAB0d17aAb84367, 0xa3931d71877C0E7a3148CB7Eb4463524FEc27fbD) + ); + ethereumPairs.push( + Pair(0xCAcd6fd266aF91b8AeD52aCCc382b4e165586E29, 0x085780639CC2cACd35E474e71f4d000e2405d8f6) + ); + ethereumPairs.push( + Pair(0xf939E0A03FB07F59A73314E73794Be0E57ac1b4E, 0x4c9EDD5852cd905f086C759E8383e09bff1E68B3) + ); + ethereumPairs.push( + Pair(0x57aB1E0003F623289CD798B1824Be09a793e4Bec, 0xcf62F905562626CfcDD2261162a51fd02Fc9c5b6) + ); + ethereumPairs.push( + Pair(0x40D16FC0246aD3160Ccc09B8D0D3A2cD28aE6C2f, 0x4c9EDD5852cd905f086C759E8383e09bff1E68B3) + ); + ethereumPairs.push( + Pair(0x63d74d22E689C715a04F2C13962b1f77F443d35b, 0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48) + ); + ethereumPairs.push( + Pair(0xCAcd6fd266aF91b8AeD52aCCc382b4e165586E29, 0xb45ad160634c528Cc3D2926d9807104FA3157305) + ); + ethereumPairs.push( + Pair(0xCAcd6fd266aF91b8AeD52aCCc382b4e165586E29, 0x0655977FEb2f289A4aB78af67BAB0d17aAb84367) + ); + ethereumPairs.push( + Pair(0xdD468A1DDc392dcdbEf6db6e34E89AA338F9F186, 0xdAC17F958D2ee523a2206206994597C13D831ec7) + ); + ethereumPairs.push( + Pair(0xacA92E438df0B2401fF60dA7E4337B687a2435DA, 0xdAC17F958D2ee523a2206206994597C13D831ec7) + ); + ethereumPairs.push( + Pair(0x5f98805A4E8be255a32880FDeC7F6728C6568bA0, 0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48) + ); + ethereumPairs.push( + Pair(0x63d74d22E689C715a04F2C13962b1f77F443d35b, 0xCAcd6fd266aF91b8AeD52aCCc382b4e165586E29) + ); + ethereumPairs.push( + Pair(0x00000000eFE302BEAA2b3e6e1b18d08D69a9012a, 0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48) + ); + ethereumPairs.push( + Pair(0x437cc33344a0B27A429f795ff6B469C72698B291, 0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48) + ); + ethereumPairs.push( + Pair(0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48, 0xE556ABa6fe6036275Ec1f87eda296BE72C811BCE) + ); + ethereumPairs.push( + Pair(0xbbAec992fc2d637151dAF40451f160bF85f3C8C1, 0xdAC17F958D2ee523a2206206994597C13D831ec7) + ); + ethereumPairs.push( + Pair(0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48, 0xaf37c1167910ebC994e266949387d2c7C326b879) + ); + ethereumPairs.push( + Pair(0x2A8e1E676Ec238d8A992307B495b45B3fEAa5e86, 0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48) + ); + ethereumPairs.push( + Pair(0xB1c2Db5d6cA03FCe73dBd304d320bF76C55Ae1B1, 0xCAcd6fd266aF91b8AeD52aCCc382b4e165586E29) + ); + ethereumPairs.push( + Pair(0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48, 0xB1c2Db5d6cA03FCe73dBd304d320bF76C55Ae1B1) + ); + ethereumPairs.push( + Pair(0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48, 0xaD55aebc9b8c03FC43cd9f62260391c13c23e7c0) + ); + ethereumPairs.push( + Pair(0x056Fd409E1d7A124BD7017459dFEa2F387b6d5Cd, 0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48) + ); + ethereumPairs.push( + Pair(0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48, 0xCAcd6fd266aF91b8AeD52aCCc382b4e165586E29) + ); + ethereumPairs.push( + Pair(0x07041776f5007ACa2A54844F50503a18A72A8b68, 0xdAC17F958D2ee523a2206206994597C13D831ec7) + ); + ethereumPairs.push( + Pair(0x96F6eF951840721AdBF46Ac996b59E0235CB985C, 0xdAC17F958D2ee523a2206206994597C13D831ec7) + ); + ethereumPairs.push( + Pair(0xC5d6A7B61d18AfA11435a889557b068BB9f29930, 0xdAC17F958D2ee523a2206206994597C13D831ec7) + ); + ethereumPairs.push( + Pair(0x4f8e5DE400DE08B164E7421B3EE387f461beCD1A, 0xdAC17F958D2ee523a2206206994597C13D831ec7) + ); + ethereumPairs.push( + Pair(0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48, 0xa3931d71877C0E7a3148CB7Eb4463524FEc27fbD) + ); + ethereumPairs.push( + Pair(0x2260FAC5E5542a773Aa44fBCfeDf7C193bc2C599, 0xcbB7C0000aB88B473b1f5aFd9ef808440eed33Bf) + ); + ethereumPairs.push( + Pair(0x18084fbA666a33d37592fA2633fD49a74DD93a88, 0x2260FAC5E5542a773Aa44fBCfeDf7C193bc2C599) + ); + ethereumPairs.push( + Pair(0x2260FAC5E5542a773Aa44fBCfeDf7C193bc2C599, 0x8236a87084f8B84306f72007F36F2618A5634494) + ); + ethereumPairs.push( + Pair(0x18084fbA666a33d37592fA2633fD49a74DD93a88, 0xcbB7C0000aB88B473b1f5aFd9ef808440eed33Bf) + ); + ethereumPairs.push( + Pair(0x2260FAC5E5542a773Aa44fBCfeDf7C193bc2C599, 0x73E0C0d45E048D25Fc26Fa3159b0aA04BfA4Db98) + ); + ethereumPairs.push( + Pair(0x2260FAC5E5542a773Aa44fBCfeDf7C193bc2C599, 0xB0F70C0bD6FD87dbEb7C10dC692a2a6106817072) + ); + ethereumPairs.push( + Pair(0x657e8C867D8B37dCC18fA4Caead9C45EB088C642, 0xcbB7C0000aB88B473b1f5aFd9ef808440eed33Bf) + ); + ethereumPairs.push( + Pair(0xB0F70C0bD6FD87dbEb7C10dC692a2a6106817072, 0xcbB7C0000aB88B473b1f5aFd9ef808440eed33Bf) + ); + ethereumPairs.push( + Pair(0x2260FAC5E5542a773Aa44fBCfeDf7C193bc2C599, 0x9BE89D2a4cd102D8Fecc6BF9dA793be995C22541) + ); + ethereumPairs.push( + Pair(0x2260FAC5E5542a773Aa44fBCfeDf7C193bc2C599, 0x657e8C867D8B37dCC18fA4Caead9C45EB088C642) + ); + ethereumPairs.push( + Pair(0x2260FAC5E5542a773Aa44fBCfeDf7C193bc2C599, 0x7A56E1C57C7475CCf742a1832B028F0456652F97) + ); + ethereumPairs.push( + Pair(0x06ea695B91700071B161A434fED42D1DcbAD9f00, 0x2260FAC5E5542a773Aa44fBCfeDf7C193bc2C599) + ); + ethereumPairs.push( + Pair(0x06ea695B91700071B161A434fED42D1DcbAD9f00, 0xcbB7C0000aB88B473b1f5aFd9ef808440eed33Bf) + ); + ethereumPairs.push( + Pair(0x8236a87084f8B84306f72007F36F2618A5634494, 0xcbB7C0000aB88B473b1f5aFd9ef808440eed33Bf) + ); + ethereumPairs.push( + Pair(0x004E9C3EF86bc1ca1f0bB5C7662861Ee93350568, 0x2260FAC5E5542a773Aa44fBCfeDf7C193bc2C599) + ); + + Pair[] storage polygonPairs = stableStablePairs.chainPairs[ChainId.Polygon]; + polygonPairs.push( + Pair(0x3c499c542cEF5E3811e1192ce70d8cC03d5c3359, 0x750e4C4984a9e0f12978eA6742Bc1c5D248f40ed) + ); + polygonPairs.push( + Pair(0x2791Bca1f2de4661ED88A30C99A7a9449Aa84174, 0x3c499c542cEF5E3811e1192ce70d8cC03d5c3359) + ); + polygonPairs.push( + Pair(0x2791Bca1f2de4661ED88A30C99A7a9449Aa84174, 0x2791Bca1f2de4661ED88A30C99A7a9449Aa84174) + ); + polygonPairs.push( + Pair(0x8f3Cf7ad23Cd3CaDbD9735AFf958023239c6A063, 0xc2132D05D31c914a87C6611C10748AEb04B58e8F) + ); + polygonPairs.push( + Pair(0x3c499c542cEF5E3811e1192ce70d8cC03d5c3359, 0xc2132D05D31c914a87C6611C10748AEb04B58e8F) + ); + polygonPairs.push( + Pair(0x2791Bca1f2de4661ED88A30C99A7a9449Aa84174, 0xc2132D05D31c914a87C6611C10748AEb04B58e8F) + ); + polygonPairs.push( + Pair(0x8f3Cf7ad23Cd3CaDbD9735AFf958023239c6A063, 0x3c499c542cEF5E3811e1192ce70d8cC03d5c3359) + ); + polygonPairs.push( + Pair(0x8f3Cf7ad23Cd3CaDbD9735AFf958023239c6A063, 0x2791Bca1f2de4661ED88A30C99A7a9449Aa84174) + ); + polygonPairs.push( + Pair(0x80Eede496655FB9047dd39d9f418d5483ED600df, 0xc2132D05D31c914a87C6611C10748AEb04B58e8F) + ); + polygonPairs.push( + Pair(0xC011a7E12a19f7B1f670d46F03B03f3342E82DFB, 0x2791Bca1f2de4661ED88A30C99A7a9449Aa84174) + ); + polygonPairs.push( + Pair(0xC011a7E12a19f7B1f670d46F03B03f3342E82DFB, 0x3c499c542cEF5E3811e1192ce70d8cC03d5c3359) + ); + polygonPairs.push( + Pair(0xa3Fa99A148fA48D14Ed51d610c367C61876997F1, 0x2791Bca1f2de4661ED88A30C99A7a9449Aa84174) + ); + polygonPairs.push( + Pair(0xa3Fa99A148fA48D14Ed51d610c367C61876997F1, 0x3c499c542cEF5E3811e1192ce70d8cC03d5c3359) + ); + polygonPairs.push( + Pair(0x14913815bCFDE78BAeAd2111F463D038Ac9C2949, 0x3c499c542cEF5E3811e1192ce70d8cC03d5c3359) + ); + polygonPairs.push( + Pair(0x00000000eFE302BEAA2b3e6e1b18d08D69a9012a, 0x3c499c542cEF5E3811e1192ce70d8cC03d5c3359) + ); + polygonPairs.push( + Pair(0x8f3Cf7ad23Cd3CaDbD9735AFf958023239c6A063, 0xa3Fa99A148fA48D14Ed51d610c367C61876997F1) + ); + polygonPairs.push( + Pair(0x45c32fA6DF82ead1e2EF74d17b76547EDdFaFF89, 0x2791Bca1f2de4661ED88A30C99A7a9449Aa84174) + ); + polygonPairs.push( + Pair(0x104592a158490a9228070E0A8e5343B499e125D0, 0x2791Bca1f2de4661ED88A30C99A7a9449Aa84174) + ); + polygonPairs.push( + Pair(0x9C9e5fD8bbc25984B178FdCE6117Defa39d2db39, 0x2791Bca1f2de4661ED88A30C99A7a9449Aa84174) + ); + polygonPairs.push( + Pair(0x2e1AD108fF1D8C782fcBbB89AAd783aC49586756, 0xc2132D05D31c914a87C6611C10748AEb04B58e8F) + ); + polygonPairs.push( + Pair(0x2e1AD108fF1D8C782fcBbB89AAd783aC49586756, 0x2791Bca1f2de4661ED88A30C99A7a9449Aa84174) + ); + polygonPairs.push( + Pair(0xa3Fa99A148fA48D14Ed51d610c367C61876997F1, 0xc2132D05D31c914a87C6611C10748AEb04B58e8F) + ); + polygonPairs.push( + Pair(0x9C9e5fD8bbc25984B178FdCE6117Defa39d2db39, 0xc2132D05D31c914a87C6611C10748AEb04B58e8F) + ); + + Pair[] storage bnbChainPairs = stableStablePairs.chainPairs[ChainId.BNBChain]; + bnbChainPairs.push( + Pair(0x8AC76a51cc950d9822D68b83fE1Ad97B32Cd580d, 0x55d398326f99059fF775485246999027B3197955) + ); + bnbChainPairs.push( + Pair(0x8d0D000Ee44948FC98c9B98A4FA4921476f08B0d, 0x55d398326f99059fF775485246999027B3197955) + ); + bnbChainPairs.push( + Pair(0x8d0D000Ee44948FC98c9B98A4FA4921476f08B0d, 0x8AC76a51cc950d9822D68b83fE1Ad97B32Cd580d) + ); + bnbChainPairs.push( + Pair(0xaf44A1E76F56eE12ADBB7ba8acD3CbD474888122, 0x55d398326f99059fF775485246999027B3197955) + ); + bnbChainPairs.push( + Pair(0xe9e7CEA3DedcA5984780Bafc599bD69ADd087D56, 0x55d398326f99059fF775485246999027B3197955) + ); + bnbChainPairs.push( + Pair(0xc5f0f7b66764F6ec8C8Dff7BA683102295E16409, 0x55d398326f99059fF775485246999027B3197955) + ); + bnbChainPairs.push( + Pair(0xe9e7CEA3DedcA5984780Bafc599bD69ADd087D56, 0x8AC76a51cc950d9822D68b83fE1Ad97B32Cd580d) + ); + bnbChainPairs.push( + Pair(0x1AF3F329e8BE154074D8769D1FFa4eE058B1DBc3, 0x55d398326f99059fF775485246999027B3197955) + ); + bnbChainPairs.push( + Pair(0xc5f0f7b66764F6ec8C8Dff7BA683102295E16409, 0x8AC76a51cc950d9822D68b83fE1Ad97B32Cd580d) + ); + bnbChainPairs.push( + Pair(0x0Bb150DFa86EA5d7742F07FEfCD8E8edA81D64eF, 0x55d398326f99059fF775485246999027B3197955) + ); + bnbChainPairs.push( + Pair(0x5A110fC00474038f6c02E89C707D638602EA44B5, 0x55d398326f99059fF775485246999027B3197955) + ); + bnbChainPairs.push( + Pair(0xb3b02E4A9Fb2bD28CC2ff97B0aB3F6B3Ec1eE9D2, 0x55d398326f99059fF775485246999027B3197955) + ); + bnbChainPairs.push( + Pair(0x1AF3F329e8BE154074D8769D1FFa4eE058B1DBc3, 0x8AC76a51cc950d9822D68b83fE1Ad97B32Cd580d) + ); + bnbChainPairs.push( + Pair(0x55d398326f99059fF775485246999027B3197955, 0xeA953eA6634d55dAC6697C436B1e81A679Db5882) + ); + bnbChainPairs.push( + Pair(0x40af3827F39D0EAcBF4A168f8D4ee67c121D11c9, 0x55d398326f99059fF775485246999027B3197955) + ); + bnbChainPairs.push( + Pair(0x8E9d4cEa39299323FE8eda678cAD449718556c4e, 0x55d398326f99059fF775485246999027B3197955) + ); + bnbChainPairs.push( + Pair(0x5d3a1Ff2b6BAb83b63cd9AD0787074081a52ef34, 0x55d398326f99059fF775485246999027B3197955) + ); + bnbChainPairs.push( + Pair(0xe9e7CEA3DedcA5984780Bafc599bD69ADd087D56, 0x1AF3F329e8BE154074D8769D1FFa4eE058B1DBc3) + ); + bnbChainPairs.push( + Pair(0x90C97F71E18723b0Cf0dfa30ee176Ab653E89F40, 0x55d398326f99059fF775485246999027B3197955) + ); + bnbChainPairs.push( + Pair(0xaf44A1E76F56eE12ADBB7ba8acD3CbD474888122, 0x8AC76a51cc950d9822D68b83fE1Ad97B32Cd580d) + ); + bnbChainPairs.push( + Pair(0x55d398326f99059fF775485246999027B3197955, 0xcE24439F2D9C6a2289F741120FE202248B666666) + ); + bnbChainPairs.push( + Pair(0x55d398326f99059fF775485246999027B3197955, 0x61a10E8556BEd032eA176330e7F17D6a12a10000) + ); + bnbChainPairs.push( + Pair(0x8AC76a51cc950d9822D68b83fE1Ad97B32Cd580d, 0xcE24439F2D9C6a2289F741120FE202248B666666) + ); + bnbChainPairs.push( + Pair(0x4268B8F0B87b6Eae5d897996E6b845ddbD99Adf3, 0x55d398326f99059fF775485246999027B3197955) + ); + bnbChainPairs.push( + Pair(0x0555E30da8f98308EdB960aa94C0Db47230d2B9c, 0x7130d2A12B9BCbFAe4f2634d864A1Ee1Ce3Ead9c) + ); + bnbChainPairs.push( + Pair(0x4aae823a6a0b376De6A78e74eCC5b079d38cBCf7, 0x7130d2A12B9BCbFAe4f2634d864A1Ee1Ce3Ead9c) + ); + + Pair[] storage celoPairs = stableStablePairs.chainPairs[ChainId.Celo]; + celoPairs.push( + Pair(0x765DE816845861e75A25fCA122bb6898B8B1282a, 0x48065fbBE25f71C9282ddf5e1cD6D6A887483D5e) + ); + celoPairs.push( + Pair(0x765DE816845861e75A25fCA122bb6898B8B1282a, 0xcebA9300f2b948710d2653dD7B07f33A8B32118C) + ); + celoPairs.push( + Pair(0x765DE816845861e75A25fCA122bb6898B8B1282a, 0x37f750B7cC259A2f741AF45294f6a16572CF5cAd) + ); + celoPairs.push( + Pair(0xcebA9300f2b948710d2653dD7B07f33A8B32118C, 0x48065fbBE25f71C9282ddf5e1cD6D6A887483D5e) + ); + celoPairs.push( + Pair(0xcebA9300f2b948710d2653dD7B07f33A8B32118C, 0x4F604735c1cF31399C6E711D5962b2B3E0225AD3) + ); + celoPairs.push( + Pair(0x617f3112bf5397D0467D315cC709EF968D9ba546, 0x48065fbBE25f71C9282ddf5e1cD6D6A887483D5e) + ); + celoPairs.push( + Pair(0x765DE816845861e75A25fCA122bb6898B8B1282a, 0x4F604735c1cF31399C6E711D5962b2B3E0225AD3) + ); + celoPairs.push( + Pair(0x765DE816845861e75A25fCA122bb6898B8B1282a, 0x617f3112bf5397D0467D315cC709EF968D9ba546) + ); + + Pair[] storage soneiumPairs = stableStablePairs.chainPairs[ChainId.Soneium]; + soneiumPairs.push( + Pair(0xbA9986D2381edf1DA03B0B9c1f8b00dc4AacC369, 0x102d758f688a4C1C5a80b116bD945d4455460282) + ); + soneiumPairs.push( + Pair(0xbA9986D2381edf1DA03B0B9c1f8b00dc4AacC369, 0x3A337a6adA9d885b6Ad95ec48F9b75f197b5AE35) + ); + soneiumPairs.push( + Pair(0xbA9986D2381edf1DA03B0B9c1f8b00dc4AacC369, 0x1217BfE6c773EEC6cc4A38b5Dc45B92292B6E189) + ); + soneiumPairs.push( + Pair(0xbA9986D2381edf1DA03B0B9c1f8b00dc4AacC369, 0x3f99231dD03a9F0E7e3421c92B7b90fbe012985a) + ); + soneiumPairs.push( + Pair(0xbA9986D2381edf1DA03B0B9c1f8b00dc4AacC369, 0x139450C2dCeF827C9A2a0Bb1CB5506260940c9fd) + ); + soneiumPairs.push( + Pair(0x102d758f688a4C1C5a80b116bD945d4455460282, 0x3A337a6adA9d885b6Ad95ec48F9b75f197b5AE35) + ); + soneiumPairs.push( + Pair(0x102d758f688a4C1C5a80b116bD945d4455460282, 0x1217BfE6c773EEC6cc4A38b5Dc45B92292B6E189) + ); + soneiumPairs.push( + Pair(0x102d758f688a4C1C5a80b116bD945d4455460282, 0x3f99231dD03a9F0E7e3421c92B7b90fbe012985a) + ); + soneiumPairs.push( + Pair(0x102d758f688a4C1C5a80b116bD945d4455460282, 0x139450C2dCeF827C9A2a0Bb1CB5506260940c9fd) + ); + soneiumPairs.push( + Pair(0x3A337a6adA9d885b6Ad95ec48F9b75f197b5AE35, 0x1217BfE6c773EEC6cc4A38b5Dc45B92292B6E189) + ); + soneiumPairs.push( + Pair(0x3A337a6adA9d885b6Ad95ec48F9b75f197b5AE35, 0x3f99231dD03a9F0E7e3421c92B7b90fbe012985a) + ); + soneiumPairs.push( + Pair(0x3A337a6adA9d885b6Ad95ec48F9b75f197b5AE35, 0x139450C2dCeF827C9A2a0Bb1CB5506260940c9fd) + ); + soneiumPairs.push( + Pair(0x1217BfE6c773EEC6cc4A38b5Dc45B92292B6E189, 0x3f99231dD03a9F0E7e3421c92B7b90fbe012985a) + ); + soneiumPairs.push( + Pair(0x1217BfE6c773EEC6cc4A38b5Dc45B92292B6E189, 0x139450C2dCeF827C9A2a0Bb1CB5506260940c9fd) + ); + soneiumPairs.push( + Pair(0x3f99231dD03a9F0E7e3421c92B7b90fbe012985a, 0x139450C2dCeF827C9A2a0Bb1CB5506260940c9fd) + ); + + Pair[] storage xLayerPairs = stableStablePairs.chainPairs[ChainId.XLayer]; + xLayerPairs.push( + Pair(0x779Ded0c9e1022225f8E0630b35a9b54bE713736, 0x74b7F16337b8972027F6196A17a631aC6dE26d22) + ); + xLayerPairs.push( + Pair(0x779Ded0c9e1022225f8E0630b35a9b54bE713736, 0x4ae46a509F6b1D9056937BA4500cb143933D2dc8) + ); + xLayerPairs.push( + Pair(0x779Ded0c9e1022225f8E0630b35a9b54bE713736, 0x1E4a5963aBFD975d8c9021ce480b42188849D41d) + ); + xLayerPairs.push( + Pair(0x74b7F16337b8972027F6196A17a631aC6dE26d22, 0x1E4a5963aBFD975d8c9021ce480b42188849D41d) + ); + xLayerPairs.push( + Pair(0x74b7F16337b8972027F6196A17a631aC6dE26d22, 0x4ae46a509F6b1D9056937BA4500cb143933D2dc8) + ); + xLayerPairs.push( + Pair(0x1E4a5963aBFD975d8c9021ce480b42188849D41d, 0x4ae46a509F6b1D9056937BA4500cb143933D2dc8) + ); + + Pair[] storage optimismPairs = stableStablePairs.chainPairs[ChainId.Optimism]; + optimismPairs.push( + Pair(0x0b2C639c533813f4Aa9D7837CAf62653d097Ff85, 0x94b008aA00579c1307B0EF2c499aD98a8ce58e58) + ); + optimismPairs.push( + Pair(0x7F5c764cBc14f9669B88837ca1490cCa17c31607, 0x94b008aA00579c1307B0EF2c499aD98a8ce58e58) + ); + optimismPairs.push( + Pair(0x0b2C639c533813f4Aa9D7837CAf62653d097Ff85, 0x01bFF41798a0BcF287b996046Ca68b395DbC1071) + ); + optimismPairs.push( + Pair(0x0b2C639c533813f4Aa9D7837CAf62653d097Ff85, 0x7F5c764cBc14f9669B88837ca1490cCa17c31607) + ); + optimismPairs.push( + Pair(0x94b008aA00579c1307B0EF2c499aD98a8ce58e58, 0x01bFF41798a0BcF287b996046Ca68b395DbC1071) + ); + optimismPairs.push( + Pair(0xDA10009cBd5D07dd0CeCc66161FC93D7c9000da1, 0x0b2C639c533813f4Aa9D7837CAf62653d097Ff85) + ); + optimismPairs.push( + Pair(0xDA10009cBd5D07dd0CeCc66161FC93D7c9000da1, 0x7F5c764cBc14f9669B88837ca1490cCa17c31607) + ); + optimismPairs.push( + Pair(0xDA10009cBd5D07dd0CeCc66161FC93D7c9000da1, 0x94b008aA00579c1307B0EF2c499aD98a8ce58e58) + ); + optimismPairs.push( + Pair(0xC52D7F23a2e460248Db6eE192Cb23dD12bDDCbf6, 0x0b2C639c533813f4Aa9D7837CAf62653d097Ff85) + ); + optimismPairs.push( + Pair(0xC52D7F23a2e460248Db6eE192Cb23dD12bDDCbf6, 0x7F5c764cBc14f9669B88837ca1490cCa17c31607) + ); + optimismPairs.push( + Pair(0xC52D7F23a2e460248Db6eE192Cb23dD12bDDCbf6, 0x94b008aA00579c1307B0EF2c499aD98a8ce58e58) + ); + optimismPairs.push( + Pair(0x1217BfE6c773EEC6cc4A38b5Dc45B92292B6E189, 0x0b2C639c533813f4Aa9D7837CAf62653d097Ff85) + ); + optimismPairs.push( + Pair(0x0b2C639c533813f4Aa9D7837CAf62653d097Ff85, 0x4F604735c1cF31399C6E711D5962b2B3E0225AD3) + ); + optimismPairs.push( + Pair(0x03569CC076654F82679C4BA2124D64774781B01D, 0xc40F949F8a4e094D1b49a23ea9241D289B7b2819) + ); + optimismPairs.push( + Pair(0xc40F949F8a4e094D1b49a23ea9241D289B7b2819, 0x0b2C639c533813f4Aa9D7837CAf62653d097Ff85) + ); + optimismPairs.push( + Pair(0xc40F949F8a4e094D1b49a23ea9241D289B7b2819, 0x7F5c764cBc14f9669B88837ca1490cCa17c31607) + ); + optimismPairs.push( + Pair(0x2E3D870790dC77A83DD1d18184Acc7439A53f475, 0x94b008aA00579c1307B0EF2c499aD98a8ce58e58) + ); + optimismPairs.push( + Pair(0x9dAbAE7274D28A45F0B65Bf8ED201A5731492ca0, 0x7F5c764cBc14f9669B88837ca1490cCa17c31607) + ); + optimismPairs.push( + Pair(0xCB8FA9a76b8e203D8C3797bF438d8FB81Ea3326A, 0x0b2C639c533813f4Aa9D7837CAf62653d097Ff85) + ); + optimismPairs.push( + Pair(0xEB466342C4d449BC9f53A865D5Cb90586f405215, 0x0b2C639c533813f4Aa9D7837CAf62653d097Ff85) + ); + optimismPairs.push( + Pair(0x657e8C867D8B37dCC18fA4Caead9C45EB088C642, 0x68f180fcCe6836688e9084f035309E29Bf0A2095) + ); + optimismPairs.push( + Pair(0x68f180fcCe6836688e9084f035309E29Bf0A2095, 0x6c84a8f1c29108F47a79964b5Fe888D4f4D0dE40) + ); + + Pair[] storage basePairs = stableStablePairs.chainPairs[ChainId.Base]; + basePairs.push( + Pair(0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913, 0xfde4C96c8593536E31F229EA8f37b2ADa2699bb2) + ); + basePairs.push( + Pair(0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913, 0x1217BfE6c773EEC6cc4A38b5Dc45B92292B6E189) + ); + basePairs.push( + Pair(0xd9aAEc86B65D86f6A7B5B1b0c42FFA531710b6CA, 0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913) + ); + basePairs.push( + Pair(0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913, 0x5d3a1Ff2b6BAb83b63cd9AD0787074081a52ef34) + ); + basePairs.push( + Pair(0xCfA3Ef56d303AE4fAabA0592388F19d7C3399FB4, 0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913) + ); + basePairs.push( + Pair(0x14913815bCFDE78BAeAd2111F463D038Ac9C2949, 0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913) + ); + basePairs.push( + Pair(0xd9aAEc86B65D86f6A7B5B1b0c42FFA531710b6CA, 0xfde4C96c8593536E31F229EA8f37b2ADa2699bb2) + ); + basePairs.push( + Pair(0x50c5725949A6F0c72E6C4a641F24049A917DB0Cb, 0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913) + ); + basePairs.push( + Pair(0xdD468A1DDc392dcdbEf6db6e34E89AA338F9F186, 0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913) + ); + basePairs.push( + Pair(0x55380fe7A1910dFf29A47B622057ab4139DA42C5, 0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913) + ); + basePairs.push( + Pair(0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913, 0x3a46ed8FCeb6eF1ADA2E4600A522AE7e24D2Ed18) + ); + basePairs.push( + Pair(0x417Ac0e078398C154EdFadD9Ef675d30Be60Af93, 0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913) + ); + basePairs.push( + Pair(0x50c5725949A6F0c72E6C4a641F24049A917DB0Cb, 0xd9aAEc86B65D86f6A7B5B1b0c42FFA531710b6CA) + ); + basePairs.push( + Pair(0xe5020A6d073a794B6E7f05678707dE47986Fb0b6, 0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913) + ); + basePairs.push( + Pair(0x50c5725949A6F0c72E6C4a641F24049A917DB0Cb, 0xfde4C96c8593536E31F229EA8f37b2ADa2699bb2) + ); + basePairs.push( + Pair(0x6Bb7a212910682DCFdbd5BCBb3e28FB4E8da10Ee, 0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913) + ); + basePairs.push( + Pair(0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913, 0x820C137fa70C8691f0e44Dc420a5e53c168921Dc) + ); + basePairs.push( + Pair(0x417Ac0e078398C154EdFadD9Ef675d30Be60Af93, 0xd9aAEc86B65D86f6A7B5B1b0c42FFA531710b6CA) + ); + basePairs.push( + Pair(0x660975730059246A68521a3e2FBD4740173100f5, 0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913) + ); + basePairs.push( + Pair(0x0A1a1A107E45b7Ced86833863f482BC5f4ed82EF, 0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913) + ); + basePairs.push( + Pair(0xCfA3Ef56d303AE4fAabA0592388F19d7C3399FB4, 0xe5020A6d073a794B6E7f05678707dE47986Fb0b6) + ); + basePairs.push( + Pair(0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913, 0x04D5ddf5f3a8939889F11E97f8c4BB48317F1938) + ); + basePairs.push( + Pair(0x4621b7A9c75199271F773Ebd9A499dbd165c3191, 0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913) + ); + basePairs.push( + Pair(0x526728DBc96689597F85ae4cd716d4f7fCcBAE9d, 0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913) + ); + basePairs.push( + Pair(0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913, 0x646A737B9B6024e49f5908762B3fF73e65B5160c) + ); + basePairs.push( + Pair(0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913, 0xEB466342C4d449BC9f53A865D5Cb90586f405215) + ); + basePairs.push( + Pair(0x8210c0634AB8f273806e4b7866E9Db353773c44B, 0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913) + ); + basePairs.push( + Pair(0x4F604735c1cF31399C6E711D5962b2B3E0225AD3, 0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913) + ); + basePairs.push( + Pair(0x0555E30da8f98308EdB960aa94C0Db47230d2B9c, 0xcbB7C0000aB88B473b1f5aFd9ef808440eed33Bf) + ); + basePairs.push( + Pair(0x236aa50979D5f3De3Bd1Eeb40E81137F22ab794b, 0xcbB7C0000aB88B473b1f5aFd9ef808440eed33Bf) + ); + basePairs.push( + Pair(0xcbB7C0000aB88B473b1f5aFd9ef808440eed33Bf, 0xecAc9C5F704e954931349Da37F60E39f515c11c1) + ); + basePairs.push( + Pair(0x0555E30da8f98308EdB960aa94C0Db47230d2B9c, 0x236aa50979D5f3De3Bd1Eeb40E81137F22ab794b) + ); + + Pair[] storage arbitrumPairs = stableStablePairs.chainPairs[ChainId.Arbitrum]; + arbitrumPairs.push( + Pair(0xaf88d065e77c8cC2239327C5EDb3A432268e5831, 0xFd086bC7CD5C481DCC9C85ebE478A1C0b69FCbb9) + ); + arbitrumPairs.push( + Pair(0xFF970A61A04b1cA14834A43f5dE4533eBDDB5CC8, 0xFd086bC7CD5C481DCC9C85ebE478A1C0b69FCbb9) + ); + arbitrumPairs.push( + Pair(0xaf88d065e77c8cC2239327C5EDb3A432268e5831, 0xFF970A61A04b1cA14834A43f5dE4533eBDDB5CC8) + ); + arbitrumPairs.push( + Pair(0x0A1a1A107E45b7Ced86833863f482BC5f4ed82EF, 0xaf88d065e77c8cC2239327C5EDb3A432268e5831) + ); + arbitrumPairs.push( + Pair(0xDA10009cBd5D07dd0CeCc66161FC93D7c9000da1, 0xaf88d065e77c8cC2239327C5EDb3A432268e5831) + ); + arbitrumPairs.push( + Pair(0xDA10009cBd5D07dd0CeCc66161FC93D7c9000da1, 0xFF970A61A04b1cA14834A43f5dE4533eBDDB5CC8) + ); + arbitrumPairs.push( + Pair(0xDA10009cBd5D07dd0CeCc66161FC93D7c9000da1, 0xFd086bC7CD5C481DCC9C85ebE478A1C0b69FCbb9) + ); + arbitrumPairs.push( + Pair(0x498Bf2B1e120FeD3ad3D42EA2165E9b73f99C1e5, 0xaf88d065e77c8cC2239327C5EDb3A432268e5831) + ); + arbitrumPairs.push( + Pair(0x498Bf2B1e120FeD3ad3D42EA2165E9b73f99C1e5, 0xFF970A61A04b1cA14834A43f5dE4533eBDDB5CC8) + ); + arbitrumPairs.push( + Pair(0x498Bf2B1e120FeD3ad3D42EA2165E9b73f99C1e5, 0xFd086bC7CD5C481DCC9C85ebE478A1C0b69FCbb9) + ); + arbitrumPairs.push( + Pair(0xaf88d065e77c8cC2239327C5EDb3A432268e5831, 0x5d3a1Ff2b6BAb83b63cd9AD0787074081a52ef34) + ); + arbitrumPairs.push( + Pair(0x41CA7586cC1311807B4605fBB748a3B8862b42b5, 0xaf88d065e77c8cC2239327C5EDb3A432268e5831) + ); + arbitrumPairs.push( + Pair(0x0A1a1A107E45b7Ced86833863f482BC5f4ed82EF, 0xFd086bC7CD5C481DCC9C85ebE478A1C0b69FCbb9) + ); + arbitrumPairs.push( + Pair(0x7dfF72693f6A4149b17e7C6314655f6A9F7c8B33, 0x5d3a1Ff2b6BAb83b63cd9AD0787074081a52ef34) + ); + arbitrumPairs.push( + Pair(0x5d3a1Ff2b6BAb83b63cd9AD0787074081a52ef34, 0xFd086bC7CD5C481DCC9C85ebE478A1C0b69FCbb9) + ); + arbitrumPairs.push( + Pair(0x211Cc4DD073734dA055fbF44a2b4667d5E5fE5d2, 0xFd086bC7CD5C481DCC9C85ebE478A1C0b69FCbb9) + ); + arbitrumPairs.push( + Pair(0x7dfF72693f6A4149b17e7C6314655f6A9F7c8B33, 0xaf88d065e77c8cC2239327C5EDb3A432268e5831) + ); + arbitrumPairs.push( + Pair(0x46850aD61C2B7d64d08c9C754F45254596696984, 0xaf88d065e77c8cC2239327C5EDb3A432268e5831) + ); + arbitrumPairs.push( + Pair(0x17FC002b466eEc40DaE837Fc4bE5c67993ddBd6F, 0xFF970A61A04b1cA14834A43f5dE4533eBDDB5CC8) + ); + arbitrumPairs.push( + Pair(0x17FC002b466eEc40DaE837Fc4bE5c67993ddBd6F, 0xaf88d065e77c8cC2239327C5EDb3A432268e5831) + ); + arbitrumPairs.push( + Pair(0x0B2b2B2076d95dda7817e785989fE353fe955ef9, 0xaf88d065e77c8cC2239327C5EDb3A432268e5831) + ); + arbitrumPairs.push( + Pair(0x0B2b2B2076d95dda7817e785989fE353fe955ef9, 0xFd086bC7CD5C481DCC9C85ebE478A1C0b69FCbb9) + ); + arbitrumPairs.push( + Pair(0xaf88d065e77c8cC2239327C5EDb3A432268e5831, 0xEB466342C4d449BC9f53A865D5Cb90586f405215) + ); + arbitrumPairs.push( + Pair(0x4D15a3A2286D883AF0AA1B3f21367843FAc63E07, 0xFF970A61A04b1cA14834A43f5dE4533eBDDB5CC8) + ); + arbitrumPairs.push( + Pair(0x7dfF72693f6A4149b17e7C6314655f6A9F7c8B33, 0xFd086bC7CD5C481DCC9C85ebE478A1C0b69FCbb9) + ); + arbitrumPairs.push( + Pair(0x2f2a2543B76A4166549F7aaB2e75Bef0aefC5B0f, 0xcbB7C0000aB88B473b1f5aFd9ef808440eed33Bf) + ); + arbitrumPairs.push( + Pair(0x2f2a2543B76A4166549F7aaB2e75Bef0aefC5B0f, 0x6c84a8f1c29108F47a79964b5Fe888D4f4D0dE40) + ); + arbitrumPairs.push( + Pair(0x2297aEbD383787A160DD0d9F71508148769342E3, 0x2f2a2543B76A4166549F7aaB2e75Bef0aefC5B0f) + ); + + Pair[] storage robinhoodPairs = stableStablePairs.chainPairs[Constants.Robinhood.CHAIN_ID]; + robinhoodPairs.push( + Pair(0x5fc5360D0400a0Fd4f2af552ADD042D716F1d168, 0x40858070814a57FdF33a613ae84fE0a8b4a874f7) + ); + robinhoodPairs.push( + Pair(0x5fc5360D0400a0Fd4f2af552ADD042D716F1d168, 0x5d3a1Ff2b6BAb83b63cd9AD0787074081a52ef34) + ); + } +} diff --git a/script/proposal-6/V4FeesProposal.s.sol b/script/proposal-6/V4FeesProposal.s.sol new file mode 100644 index 00000000..d60d787c --- /dev/null +++ b/script/proposal-6/V4FeesProposal.s.sol @@ -0,0 +1,222 @@ +// SPDX-License-Identifier: MIT +pragma solidity 0.8.29; + +import {Script, console} from "forge-std/Script.sol"; + +import {Recorder} from "govkit/forge/Recorder.sol"; +import {Uniswap} from "govkit/types/Uniswap.sol"; +import {Proposal} from "govkit/types/Proposal.sol"; +import {ChainId} from "govkit/constants/ChainId.sol"; +import {Call, LibCall} from "govkit/types/Call.sol"; +import {GovernanceSeatbelt} from "govkit/forge/GovernanceSeatbelt.sol"; +import {FxRootEncoder} from "govkit/bridges/FxRootEncoder.sol"; +import {InboxEncoder} from "govkit/bridges/InboxEncoder.sol"; +import { + L1CrossDomainMessengerEncoder +} from "govkit/bridges/L1CrossDomainMessengerEncoder.sol"; +import {OptimismPortal2Encoder} from "govkit/bridges/OptimismPortal2Encoder.sol"; +import {WormholeEncoder} from "govkit/bridges/WormholeEncoder.sol"; +import {IPoolManager} from "govkit/interfaces/IPoolManager.sol"; + +import {V4FeeController} from "./Constants.sol"; + +string constant DESCRIPTION = "TODO"; + +contract V4FeeActivationProposal is Script { + Recorder internal recorder; + Uniswap internal uniswap; + + function run() public { + vm.createDir("./out/.seatbelt/", true); + recorder.initialize("DeployV4FeeInfra"); + uniswap.loadLatest(); + { + // --------------------------------------------------------------------------------------------- + // 00: Activate V4 Fees for Ethereum. + // + Call memory activateV4FeesEthereum = Call({ + target: uniswap.ethereum.poolManager, + value: 0, + data: abi.encodeCall(IPoolManager.setProtocolFeeController, (recorder.read(ChainId.Ethereum, "V4FeeAdapter"))) + }); + + // --------------------------------------------------------------------------------------------- + // 01: Activate V4 Fees for Arbitrum. + // + Call memory activateV4FeesArbitrum = InboxEncoder.encode({ + inbox: uniswap.ethereum.bridge.arbitrum, + timelock: uniswap.ethereum.timelock, + remoteCall: Call({ + target: uniswap.arbitrum.poolManager, + value: 0, + data: abi.encodeCall(IPoolManager.setProtocolFeeController, (recorder.read(ChainId.Arbitrum, "V4FeeAdapter"))) + }) + }); + + // --------------------------------------------------------------------------------------------- + // 02: Activate V4 Fees for Base. + // + Call memory activateV4FeesBase = L1CrossDomainMessengerEncoder.encode({ + l1CrossDomainMessenger: uniswap.ethereum.bridge.base, + crossChainAccount: uniswap.base.crossChainAccount, + remoteCall: Call({ + target: uniswap.base.poolManager, + value: 0, + data: abi.encodeCall(IPoolManager.setProtocolFeeController, (recorder.read(ChainId.Base, "V4FeeAdapter"))) + }) + }); + + // --------------------------------------------------------------------------------------------- + // 03: Activate V4 Fees for BNB Chain. + // + Call memory activateV4FeesBNBChain = WormholeEncoder.encode({ + sourceSender: uniswap.ethereum.bridge.bnbChain, + remoteReceiver: uniswap.bnbChain.wormholeReceiver, + chainId: ChainId.BNBChain, + value: 0, + remoteCalls: LibCall.newCalls( + [Call({ + target: uniswap.bnbChain.poolManager, + value: 0, + data: abi.encodeCall( + IPoolManager.setProtocolFeeController, (recorder.read(ChainId.BNBChain, "V4FeeAdapter")) + ) + })] + ) + }); + + // --------------------------------------------------------------------------------------------- + // 04: Activate V4 Fees for Celo. + // + Call memory activateV4FeesCelo = L1CrossDomainMessengerEncoder.encode({ + l1CrossDomainMessenger: uniswap.ethereum.bridge.celo, + crossChainAccount: uniswap.celo.crossChainAccount, + remoteCall: Call({ + target: uniswap.celo.poolManager, + value: 0, + data: abi.encodeCall(IPoolManager.setProtocolFeeController, (recorder.read(ChainId.Celo, "V4FeeAdapter"))) + }) + }); + + // --------------------------------------------------------------------------------------------- + // 05: Activate V4 Fees for Optimism + // + Call memory activateV4FeesOPMainnet = L1CrossDomainMessengerEncoder.encode({ + l1CrossDomainMessenger: uniswap.ethereum.bridge.optimism, + crossChainAccount: uniswap.optimism.crossChainAccount, + remoteCall: Call({ + target: uniswap.optimism.poolManager, + value: 0, + data: abi.encodeCall(IPoolManager.setProtocolFeeController, (recorder.read(ChainId.Optimism, "V4FeeAdapter"))) + }) + }); + + Proposal memory v4FeeActivationProposalPartOne = Proposal({ + description: string.concat("Part 1/2:\n", DESCRIPTION), + calls: LibCall.newCalls( + [ + activateV4FeesEthereum, + activateV4FeesArbitrum, + activateV4FeesBase, + activateV4FeesBNBChain, + activateV4FeesCelo, + activateV4FeesOPMainnet + ] + ) + }); + + vm.writeFile({ + path: "./out/.seatbelt/V4FeeActivationProposalPartOne.json", + data: GovernanceSeatbelt.toJson({ + proposal: v4FeeActivationProposalPartOne, governorBravo: uniswap.ethereum.governorBravo + }) + }); + } + { + // --------------------------------------------------------------------------------------------- + // 06: Activate V4 Fees for Polygon. + // + Call memory activateV4FeesPolygon = FxRootEncoder.encode({ + fxRoot: uniswap.ethereum.bridge.polygon, + fxReceiver: uniswap.polygon.fxReceiver, + remoteCalls: LibCall.newCalls( + [Call({ + target: uniswap.polygon.poolManager, + value: 0, + data: abi.encodeCall(IPoolManager.setProtocolFeeController, (recorder.read(ChainId.Polygon, "V4FeeAdapter"))) + })] + ) + }); + + // --------------------------------------------------------------------------------------------- + // 07: Activate V4 Fees for Soneium. + // + Call memory activateV4FeesSoneium = L1CrossDomainMessengerEncoder.encode({ + l1CrossDomainMessenger: uniswap.ethereum.bridge.soneium, + crossChainAccount: uniswap.soneium.crossChainAccount, + remoteCall: Call({ + target: uniswap.soneium.poolManager, + value: 0, + data: abi.encodeCall(IPoolManager.setProtocolFeeController, (recorder.read(ChainId.Soneium, "V4FeeAdapter"))) + }) + }); + + // --------------------------------------------------------------------------------------------- + // 08: Activate V4 Fees for Worldchain. + // + Call memory activateV4FeesWorldchain = OptimismPortal2Encoder.encode({ + portal: uniswap.ethereum.bridge.worldChain, + remoteCall: Call({ + target: uniswap.worldChain.poolManager, + value: 0, + data: abi.encodeCall(IPoolManager.setProtocolFeeController, (recorder.read(ChainId.WorldChain, "V4FeeAdapter"))) + }) + }); + + // --------------------------------------------------------------------------------------------- + // 09: Activate V4 Fees for X Layer + // + Call memory activateV4FeesXLayer = OptimismPortal2Encoder.encode({ + portal: uniswap.ethereum.bridge.xLayer, + remoteCall: Call({ + target: uniswap.xLayer.poolManager, + value: 0, + data: abi.encodeCall(IPoolManager.setProtocolFeeController, (recorder.read(ChainId.XLayer, "V4FeeAdapter"))) + }) + }); + + // --------------------------------------------------------------------------------------------- + // 10: Activate V4 Fees for Zora. + // + Call memory activateV4FeesZora = L1CrossDomainMessengerEncoder.encode({ + l1CrossDomainMessenger: uniswap.ethereum.bridge.zora, + crossChainAccount: uniswap.zora.crossChainAccount, + remoteCall: Call({ + target: uniswap.zora.poolManager, + value: 0, + data: abi.encodeCall(IPoolManager.setProtocolFeeController, (recorder.read(ChainId.Zora, "V4FeeAdapter"))) + }) + }); + + Proposal memory v4FeeActivationProposalPartTwo = Proposal({ + description: string.concat("Part 2/2:\n", DESCRIPTION), + calls: LibCall.newCalls( + [ + activateV4FeesPolygon, + activateV4FeesSoneium, + activateV4FeesWorldchain, + activateV4FeesXLayer, + activateV4FeesZora + ] + ) + }); + + vm.writeFile({ + path: "./out/.seatbelt/V4FeeActivationProposalPartTwo.json", + data: GovernanceSeatbelt.toJson({ + proposal: v4FeeActivationProposalPartTwo, governorBravo: uniswap.ethereum.governorBravo + }) + }); + } + } +} diff --git a/script/proposal-6/prereq/DeployV4FeeInfra.s.sol b/script/proposal-6/prereq/DeployV4FeeInfra.s.sol new file mode 100644 index 00000000..bb76af0e --- /dev/null +++ b/script/proposal-6/prereq/DeployV4FeeInfra.s.sol @@ -0,0 +1,270 @@ +// SPDX-License-Identifier: AGPl-3.0-only +pragma solidity 0.8.29; + +import {Script, console} from "forge-std/Script.sol"; +import {IPoolManager} from "v4-core/interfaces/IPoolManager.sol"; +import {Currency} from "v4-core/types/Currency.sol"; + +import {Recorder} from "govkit/forge/Recorder.sol"; +import {Uniswap} from "govkit/types/Uniswap.sol"; +import {ChainId} from "govkit/constants/ChainId.sol"; +import {InboxEncoder} from "govkit/bridges/InboxEncoder.sol"; + +import {V4FeeAdapter} from "../../../src/feeAdapters/V4FeeAdapter.sol"; +import {V4FeePolicy} from "../../../src/feeAdapters/V4FeePolicy.sol"; +import { + FeeBucket, + FlagRule, + PairClassFeeAssignment +} from "../../../src/interfaces/IV4FeePolicy.sol"; +import {StableStablePairs} from "../StableStablePairs.sol"; + +import "../Constants.sol" as Constants; + +uint256 constant AGG_HOOK_FAMILY = 1 << 11; +uint8 constant AGG_HOOK_ID = 11; +uint256 constant PARITY_HOOK_FAMILY = 1 << 12; +uint8 constant PARITY_HOOK_ID = 12; + +contract DeployV4FeeInfra is Script { + Recorder internal recorder; + Uniswap internal uniswap; + StableStablePairs internal stableStablePairs; + + function run() external { + uniswap.loadLatest(); + recorder.initialize("DeployV4FeeInfra"); + stableStablePairs.initialize(); + + address scriptRunnerEoa = msg.sender; + uint256 chainId = block.chainid; + + (address poolManager, address tokenJar, address postConfigOwner) = getAddresses(chainId); + + vm.startBroadcast(); + + // ----------------------------------------------------------------------------------------- + // Deploy V4FeeAdapter + // + V4FeeAdapter adapter; + if (!recorder.exists(chainId, "V4FeeAdapter")) { + adapter = new V4FeeAdapter({poolManager: IPoolManager(poolManager), tokenJar: tokenJar}); + + recorder.write({ + chainId: chainId, deploymentName: "V4FeeAdapter", deployment: address(adapter) + }); + } else { + adapter = V4FeeAdapter(recorder.read(chainId, "V4FeeAdapter")); + } + + // ----------------------------------------------------------------------------------------- + // Deploy V4FeePolicy + // + V4FeePolicy policy; + if (!recorder.exists(chainId, "V4FeePolicy")) { + policy = new V4FeePolicy({poolManager: IPoolManager(poolManager)}); + + recorder.write({chainId: chainId, deploymentName: "V4FeePolicy", deployment: address(policy)}); + } else { + policy = V4FeePolicy(recorder.read(chainId, "V4FeePolicy")); + } + + // ----------------------------------------------------------------------------------------- + // Set Policy on Adapter + // + adapter.setPolicy(policy); + + // ----------------------------------------------------------------------------------------- + // Take Fee Setter Authority + // + policy.setFeeSetter(scriptRunnerEoa); + + // ----------------------------------------------------------------------------------------- + // Assign Fee Buckets + // + FeeBucket[] memory feeBuckets = new FeeBucket[](8); + feeBuckets[0] = FeeBucket({lpFeeFloor: 0, alphaPips: 1, betaPips: 0}); + feeBuckets[1] = FeeBucket({lpFeeFloor: 3, alphaPips: 1, betaPips: 263_889}); + feeBuckets[2] = FeeBucket({lpFeeFloor: 75, alphaPips: 20, betaPips: 200_000}); + feeBuckets[3] = FeeBucket({lpFeeFloor: 100, alphaPips: 25, betaPips: 272_727}); + feeBuckets[4] = FeeBucket({lpFeeFloor: 375, alphaPips: 100, betaPips: 200_000}); + feeBuckets[5] = FeeBucket({lpFeeFloor: 500, alphaPips: 125, betaPips: 137_500}); + feeBuckets[6] = FeeBucket({lpFeeFloor: 2500, alphaPips: 400, betaPips: 200_000}); + feeBuckets[7] = FeeBucket({lpFeeFloor: 5500, alphaPips: 1000, betaPips: 0}); + policy.setFeeBuckets(feeBuckets); + + // ----------------------------------------------------------------------------------------- + // Set Flag Rules + // + // | Name | ID | Flags | + // | ---------------- | ---- | --------- | + // | Aggregator Hooks | `11` | `1 << 11` | + // | Parity Hooks | `12` | `1 << 12` | + // + { + FlagRule[] memory flagRules = new FlagRule[](2); + flagRules[0] = FlagRule({requiredFlags: AGG_HOOK_FAMILY, familyId: AGG_HOOK_ID}); + flagRules[1] = FlagRule({requiredFlags: PARITY_HOOK_FAMILY, familyId: PARITY_HOOK_ID}); + policy.setFlagRules(flagRules); + } + + // ----------------------------------------------------------------------------------------- + // Set Aggregator Hook Fees + // + { + uint24 defaultFeeValue = chainId == ChainId.Base ? 300 : 1000; + uint24 stableStableFeeValue = chainId == ChainId.Base ? 100 : 300; + + policy.setFamilyDefault({familyId: AGG_HOOK_ID, feeValue: defaultFeeValue}); + + uint256 length = stableStablePairs.chainPairs[chainId].length; + + PairClassFeeAssignment[] memory assignments = new PairClassFeeAssignment[](length); + for (uint256 i; i < length; i++) { + address token0 = stableStablePairs.chainPairs[chainId][i].token0; + address token1 = stableStablePairs.chainPairs[chainId][i].token1; + + (token0, token1) = sort(token0, token1); + + assignments[i] = PairClassFeeAssignment({ + currency0: Currency.wrap(token0), + currency1: Currency.wrap(token1), + familyId: AGG_HOOK_ID, + feeValue: stableStableFeeValue + }); + } + policy.batchSetPairClassFee(assignments); + } + + // ----------------------------------------------------------------------------------------- + // Set Parity Hook Fees + // + policy.setFamilyDefault({familyId: PARITY_HOOK_ID, feeValue: 0}); + + // ----------------------------------------------------------------------------------------- + // Transfer Authority + // + policy.setFeeSetter(postConfigOwner); + + policy.transferOwnership(postConfigOwner); + + adapter.setFeeSetter(postConfigOwner); + + adapter.transferOwnership(postConfigOwner); + + vm.stopBroadcast(); + + // ----------------------------------------------------------------------------------------- + // Run Checks + // + + console.log("Checking ..."); + + require(address(adapter.POOL_MANAGER()) == poolManager); + require(adapter.TOKEN_JAR() == tokenJar); + require(adapter.feeSetter() == postConfigOwner); + require(address(adapter.policy()) == address(policy)); + + require(address(policy.POOL_MANAGER()) == poolManager); + require(policy.feeSetter() == postConfigOwner); + require(policy.defaultFee() == 0); + require(policy.feeBucketsLength() == feeBuckets.length); + + for (uint256 i; i < feeBuckets.length; i++) { + (uint24 lpFeeFloor, uint24 alphaPips, uint32 betaPips) = policy.feeBucket(i); + require(lpFeeFloor == feeBuckets[i].lpFeeFloor); + require(alphaPips == feeBuckets[i].alphaPips); + require(betaPips == feeBuckets[i].betaPips); + } + + require(policy.flagRulesLength() == 2); + { + (uint256 hookFamily, uint8 hookId) = policy.flagRules(0); + require(hookFamily == AGG_HOOK_FAMILY); + require(hookId == AGG_HOOK_ID); + } + { + (uint256 hookFamily, uint8 hookId) = policy.flagRules(1); + require(hookFamily == PARITY_HOOK_FAMILY); + require(hookId == PARITY_HOOK_ID); + } + + if (chainId == ChainId.Base) require(policy.familyDefaults(AGG_HOOK_ID) == 300); + else require(policy.familyDefaults(AGG_HOOK_ID) == 1000); + + uint256 len = stableStablePairs.chainPairs[chainId].length; + + for (uint256 i; i < len; i++) { + address token0 = stableStablePairs.chainPairs[chainId][i].token0; + address token1 = stableStablePairs.chainPairs[chainId][i].token1; + + (token0, token1) = sort(token0, token1); + + bytes32 hash = keccak256(abi.encodePacked(token0, token1)); + + if (chainId == ChainId.Base) require(policy.pairClassFees(hash, AGG_HOOK_ID) == 100); + else require(policy.pairClassFees(hash, AGG_HOOK_ID) == 300); + } + } + + function getAddresses(uint256 chainId) internal view returns (address, address, address) { + if (chainId == ChainId.Ethereum) { + return (uniswap.ethereum.poolManager, uniswap.ethereum.tokenJar, uniswap.ethereum.timelock); + } + if (chainId == ChainId.Arbitrum) { + return ( + uniswap.arbitrum.poolManager, + uniswap.arbitrum.tokenJar, + InboxEncoder.arbitrumAlias(uniswap.ethereum.timelock) + ); + } + if (chainId == ChainId.Base) { + return (uniswap.base.poolManager, uniswap.base.tokenJar, uniswap.base.crossChainAccount); + } + if (chainId == ChainId.Celo) { + return (uniswap.celo.poolManager, uniswap.celo.tokenJar, uniswap.celo.crossChainAccount); + } + if (chainId == ChainId.Optimism) { + return + ( + uniswap.optimism.poolManager, + uniswap.optimism.tokenJar, + uniswap.optimism.crossChainAccount + ); + } + if (chainId == ChainId.Soneium) { + return + (uniswap.soneium.poolManager, uniswap.soneium.tokenJar, uniswap.soneium.crossChainAccount); + } + if (chainId == ChainId.XLayer) { + return (uniswap.xLayer.poolManager, uniswap.xLayer.tokenJar, uniswap.xLayer.crossChainAccount); + } + if (chainId == ChainId.WorldChain) { + return ( + uniswap.worldChain.poolManager, + uniswap.worldChain.tokenJar, + uniswap.worldChain.crossChainAccount + ); + } + if (chainId == ChainId.Zora) { + return (uniswap.zora.poolManager, uniswap.zora.tokenJar, uniswap.zora.crossChainAccount); + } + if (chainId == ChainId.BNBChain) { + return + (uniswap.bnbChain.poolManager, uniswap.bnbChain.tokenJar, uniswap.bnbChain.wormholeReceiver); + } + if (chainId == ChainId.Polygon) { + return (uniswap.polygon.poolManager, uniswap.polygon.tokenJar, uniswap.polygon.fxReceiver); + } + + if (chainId == Constants.Robinhood.CHAIN_ID) { + return (Constants.Robinhood.POOL_MANAGER, Constants.Robinhood.TOKEN_JAR, InboxEncoder.arbitrumAlias(uniswap.ethereum.timelock)); + } + + revert("invalid chain id"); + } + + function sort(address a, address b) internal pure returns (address, address) { + return uint160(a) >= uint160(b) ? (b, a) : (a, b); + } +} From 9e71c85121275e046a3d5a6ce96882073b865ed9 Mon Sep 17 00:00:00 2001 From: jtriley Date: Tue, 14 Jul 2026 18:48:46 -0700 Subject: [PATCH 08/55] fix ownership transfer --- script/proposal-5/RobinhoodFees.s.sol | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/script/proposal-5/RobinhoodFees.s.sol b/script/proposal-5/RobinhoodFees.s.sol index d4d314f7..d8468e5c 100644 --- a/script/proposal-5/RobinhoodFees.s.sol +++ b/script/proposal-5/RobinhoodFees.s.sol @@ -32,6 +32,8 @@ contract RobinhoodFees is Script { address tokenJar = recorder.read({chainId: Constants.Robinhood.CHAIN_ID, deploymentName: "TokenJar"}); + address v3OpenFeeAdapter = + recorder.read({chainId: Constants.Robinhood.CHAIN_ID, deploymentName: "V3OpenFeeAdapter"}); // --------------------------------------------------------------------------------------------- // 00: Set Fee Receiver on V2 Factor to on Robinhood @@ -55,7 +57,7 @@ contract RobinhoodFees is Script { remoteCall: Call({ target: Constants.Robinhood.V3_FACTORY, value: 0, - data: abi.encodeCall(IUniswapV3Factory.setOwner, (tokenJar)) + data: abi.encodeCall(IUniswapV3Factory.setOwner, (v3OpenFeeAdapter)) }) }); From 775ef53c0fda4525af5c081729e1cef50f9a09fe Mon Sep 17 00:00:00 2001 From: jtriley Date: Tue, 14 Jul 2026 18:51:26 -0700 Subject: [PATCH 09/55] fmt --- script/proposal-6/V4FeesProposal.s.sol | 49 +++++++++++++------ .../proposal-6/prereq/DeployV4FeeInfra.s.sol | 6 ++- 2 files changed, 40 insertions(+), 15 deletions(-) diff --git a/script/proposal-6/V4FeesProposal.s.sol b/script/proposal-6/V4FeesProposal.s.sol index d60d787c..64c4ef3c 100644 --- a/script/proposal-6/V4FeesProposal.s.sol +++ b/script/proposal-6/V4FeesProposal.s.sol @@ -11,9 +11,7 @@ import {Call, LibCall} from "govkit/types/Call.sol"; import {GovernanceSeatbelt} from "govkit/forge/GovernanceSeatbelt.sol"; import {FxRootEncoder} from "govkit/bridges/FxRootEncoder.sol"; import {InboxEncoder} from "govkit/bridges/InboxEncoder.sol"; -import { - L1CrossDomainMessengerEncoder -} from "govkit/bridges/L1CrossDomainMessengerEncoder.sol"; +import {L1CrossDomainMessengerEncoder} from "govkit/bridges/L1CrossDomainMessengerEncoder.sol"; import {OptimismPortal2Encoder} from "govkit/bridges/OptimismPortal2Encoder.sol"; import {WormholeEncoder} from "govkit/bridges/WormholeEncoder.sol"; import {IPoolManager} from "govkit/interfaces/IPoolManager.sol"; @@ -37,7 +35,9 @@ contract V4FeeActivationProposal is Script { Call memory activateV4FeesEthereum = Call({ target: uniswap.ethereum.poolManager, value: 0, - data: abi.encodeCall(IPoolManager.setProtocolFeeController, (recorder.read(ChainId.Ethereum, "V4FeeAdapter"))) + data: abi.encodeCall( + IPoolManager.setProtocolFeeController, (recorder.read(ChainId.Ethereum, "V4FeeAdapter")) + ) }); // --------------------------------------------------------------------------------------------- @@ -49,7 +49,9 @@ contract V4FeeActivationProposal is Script { remoteCall: Call({ target: uniswap.arbitrum.poolManager, value: 0, - data: abi.encodeCall(IPoolManager.setProtocolFeeController, (recorder.read(ChainId.Arbitrum, "V4FeeAdapter"))) + data: abi.encodeCall( + IPoolManager.setProtocolFeeController, (recorder.read(ChainId.Arbitrum, "V4FeeAdapter")) + ) }) }); @@ -62,7 +64,9 @@ contract V4FeeActivationProposal is Script { remoteCall: Call({ target: uniswap.base.poolManager, value: 0, - data: abi.encodeCall(IPoolManager.setProtocolFeeController, (recorder.read(ChainId.Base, "V4FeeAdapter"))) + data: abi.encodeCall( + IPoolManager.setProtocolFeeController, (recorder.read(ChainId.Base, "V4FeeAdapter")) + ) }) }); @@ -79,7 +83,8 @@ contract V4FeeActivationProposal is Script { target: uniswap.bnbChain.poolManager, value: 0, data: abi.encodeCall( - IPoolManager.setProtocolFeeController, (recorder.read(ChainId.BNBChain, "V4FeeAdapter")) + IPoolManager.setProtocolFeeController, + (recorder.read(ChainId.BNBChain, "V4FeeAdapter")) ) })] ) @@ -94,7 +99,9 @@ contract V4FeeActivationProposal is Script { remoteCall: Call({ target: uniswap.celo.poolManager, value: 0, - data: abi.encodeCall(IPoolManager.setProtocolFeeController, (recorder.read(ChainId.Celo, "V4FeeAdapter"))) + data: abi.encodeCall( + IPoolManager.setProtocolFeeController, (recorder.read(ChainId.Celo, "V4FeeAdapter")) + ) }) }); @@ -107,7 +114,9 @@ contract V4FeeActivationProposal is Script { remoteCall: Call({ target: uniswap.optimism.poolManager, value: 0, - data: abi.encodeCall(IPoolManager.setProtocolFeeController, (recorder.read(ChainId.Optimism, "V4FeeAdapter"))) + data: abi.encodeCall( + IPoolManager.setProtocolFeeController, (recorder.read(ChainId.Optimism, "V4FeeAdapter")) + ) }) }); @@ -143,7 +152,10 @@ contract V4FeeActivationProposal is Script { [Call({ target: uniswap.polygon.poolManager, value: 0, - data: abi.encodeCall(IPoolManager.setProtocolFeeController, (recorder.read(ChainId.Polygon, "V4FeeAdapter"))) + data: abi.encodeCall( + IPoolManager.setProtocolFeeController, + (recorder.read(ChainId.Polygon, "V4FeeAdapter")) + ) })] ) }); @@ -157,7 +169,9 @@ contract V4FeeActivationProposal is Script { remoteCall: Call({ target: uniswap.soneium.poolManager, value: 0, - data: abi.encodeCall(IPoolManager.setProtocolFeeController, (recorder.read(ChainId.Soneium, "V4FeeAdapter"))) + data: abi.encodeCall( + IPoolManager.setProtocolFeeController, (recorder.read(ChainId.Soneium, "V4FeeAdapter")) + ) }) }); @@ -169,7 +183,10 @@ contract V4FeeActivationProposal is Script { remoteCall: Call({ target: uniswap.worldChain.poolManager, value: 0, - data: abi.encodeCall(IPoolManager.setProtocolFeeController, (recorder.read(ChainId.WorldChain, "V4FeeAdapter"))) + data: abi.encodeCall( + IPoolManager.setProtocolFeeController, + (recorder.read(ChainId.WorldChain, "V4FeeAdapter")) + ) }) }); @@ -181,7 +198,9 @@ contract V4FeeActivationProposal is Script { remoteCall: Call({ target: uniswap.xLayer.poolManager, value: 0, - data: abi.encodeCall(IPoolManager.setProtocolFeeController, (recorder.read(ChainId.XLayer, "V4FeeAdapter"))) + data: abi.encodeCall( + IPoolManager.setProtocolFeeController, (recorder.read(ChainId.XLayer, "V4FeeAdapter")) + ) }) }); @@ -194,7 +213,9 @@ contract V4FeeActivationProposal is Script { remoteCall: Call({ target: uniswap.zora.poolManager, value: 0, - data: abi.encodeCall(IPoolManager.setProtocolFeeController, (recorder.read(ChainId.Zora, "V4FeeAdapter"))) + data: abi.encodeCall( + IPoolManager.setProtocolFeeController, (recorder.read(ChainId.Zora, "V4FeeAdapter")) + ) }) }); diff --git a/script/proposal-6/prereq/DeployV4FeeInfra.s.sol b/script/proposal-6/prereq/DeployV4FeeInfra.s.sol index bb76af0e..2bf53bee 100644 --- a/script/proposal-6/prereq/DeployV4FeeInfra.s.sol +++ b/script/proposal-6/prereq/DeployV4FeeInfra.s.sol @@ -258,7 +258,11 @@ contract DeployV4FeeInfra is Script { } if (chainId == Constants.Robinhood.CHAIN_ID) { - return (Constants.Robinhood.POOL_MANAGER, Constants.Robinhood.TOKEN_JAR, InboxEncoder.arbitrumAlias(uniswap.ethereum.timelock)); + return ( + Constants.Robinhood.POOL_MANAGER, + Constants.Robinhood.TOKEN_JAR, + InboxEncoder.arbitrumAlias(uniswap.ethereum.timelock) + ); } revert("invalid chain id"); From ac36ddd9b0b02f7a68714ab52ae8439caef63b3e Mon Sep 17 00:00:00 2001 From: jtriley Date: Tue, 14 Jul 2026 18:56:38 -0700 Subject: [PATCH 10/55] rm old const --- script/proposal-6/Constants.sol | 30 -------------------------- script/proposal-6/V4FeesProposal.s.sol | 2 -- 2 files changed, 32 deletions(-) diff --git a/script/proposal-6/Constants.sol b/script/proposal-6/Constants.sol index e4668ffc..05669af6 100644 --- a/script/proposal-6/Constants.sol +++ b/script/proposal-6/Constants.sol @@ -1,36 +1,6 @@ // SPDX-License-Identifier: MIT pragma solidity 0.8.29; -library V4FeeController { - address constant Ethereum = address(0x00); - address constant Base = address(0x00); - address constant Robinhood = address(0x00); - address constant BNBChain = address(0x00); - address constant Arbitrum = address(0x00); - address constant Optimism = address(0x00); - address constant Polygon = address(0x00); - - address constant Celo = address(0x00); - address constant Soneium = address(0x00); - address constant XLayer = address(0x00); - address constant WorldChain = address(0x00); - address constant Zora = address(0x00); - - function smokeCheck() internal pure { - require(Ethereum != address(0x00), "Ethereum is not set."); - require(Arbitrum != address(0x00), "Arbitrum is not set."); - require(Base != address(0x00), "Base is not set."); - require(Celo != address(0x00), "Celo is not set."); - require(Optimism != address(0x00), "Optimism is not set."); - require(Soneium != address(0x00), "Soneium is not set."); - require(XLayer != address(0x00), "XLayer is not set."); - require(WorldChain != address(0x00), "Worldchain is not set."); - require(Zora != address(0x00), "Zora is not set."); - require(BNBChain != address(0x00), "BNBChain is not set."); - require(Polygon != address(0x00), "Polygon is not set."); - } -} - library Ethereum { address constant RH_L1_ERC20_GATEWAY = 0x85001CC4867C5e1C22dA4B79BB8852B9e2a06da0; address constant RH_INBOX = 0x1A07cc4BD17E0118BdB54D70990D2158AbAD7a2D; diff --git a/script/proposal-6/V4FeesProposal.s.sol b/script/proposal-6/V4FeesProposal.s.sol index 64c4ef3c..21781924 100644 --- a/script/proposal-6/V4FeesProposal.s.sol +++ b/script/proposal-6/V4FeesProposal.s.sol @@ -16,8 +16,6 @@ import {OptimismPortal2Encoder} from "govkit/bridges/OptimismPortal2Encoder.sol" import {WormholeEncoder} from "govkit/bridges/WormholeEncoder.sol"; import {IPoolManager} from "govkit/interfaces/IPoolManager.sol"; -import {V4FeeController} from "./Constants.sol"; - string constant DESCRIPTION = "TODO"; contract V4FeeActivationProposal is Script { From 9a564a8cf5127fcc2c6c927b1866c825050a581a Mon Sep 17 00:00:00 2001 From: jtriley Date: Tue, 14 Jul 2026 19:19:35 -0700 Subject: [PATCH 11/55] update snap --- snapshots/V4FeeAdapterTest.json | 30 +++++++++++++++--------------- 1 file changed, 15 insertions(+), 15 deletions(-) diff --git a/snapshots/V4FeeAdapterTest.json b/snapshots/V4FeeAdapterTest.json index 63e5088f..999ee173 100644 --- a/snapshots/V4FeeAdapterTest.json +++ b/snapshots/V4FeeAdapterTest.json @@ -1,12 +1,12 @@ { - "adapter.batchTriggerFeeUpdate - two pools": "82840", - "adapter.collect - single currency": "58078", - "adapter.getFee - pool override hit": "3330", - "adapter.setPoolOverride": "48093", - "adapter.triggerFeeUpdate - single pool": "58076", - "policy.batchClearPairClassFee": "33166", - "policy.batchSetHookFamily": "72791", - "policy.batchSetPairClassFee": "76610", + "adapter.batchTriggerFeeUpdate - two pools": "82831", + "adapter.collect - single currency": "58075", + "adapter.getFee - pool override hit": "3329", + "adapter.setPoolOverride": "48092", + "adapter.triggerFeeUpdate - single pool": "58072", + "policy.batchClearPairClassFee": "33164", + "policy.batchSetHookFamily": "72789", + "policy.batchSetPairClassFee": "76609", "policy.computeFee - classified family default": "7928", "policy.computeFee - classified flag-rule self-report": "19644", "policy.computeFee - classified griefing hook -> defaultFee": "7881", @@ -16,11 +16,11 @@ "policy.computeFee - flag-rule single flag match": "19644", "policy.computeFee - static native math buckets": "11158", "policy.computeFee - static native math pair class fee": "5745", - "policy.setFamilyDefault": "47850", - "policy.setFeeBuckets - 1 bucket": "71511", - "policy.setFeeBuckets - 5 buckets (configuration B)": "148719", - "policy.setFlagRules - 32 rules (max)": "1499942", - "policy.setFlagRules - two rules": "138182", - "policy.setHookFamily": "47601", - "policy.setPairClassFee": "49410" + "policy.setFamilyDefault": "47849", + "policy.setFeeBuckets - 1 bucket": "71500", + "policy.setFeeBuckets - 5 buckets (configuration B)": "148708", + "policy.setFlagRules - 32 rules (max)": "1499932", + "policy.setFlagRules - two rules": "138172", + "policy.setHookFamily": "47600", + "policy.setPairClassFee": "49408" } \ No newline at end of file From ea20ebacc78e2bf19a0fa5f5354644b4447ad981 Mon Sep 17 00:00:00 2001 From: jtriley Date: Tue, 14 Jul 2026 19:36:49 -0700 Subject: [PATCH 12/55] rm snapshot check --- .github/workflows/ci.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 4c9d1930..64f8e142 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -53,7 +53,7 @@ jobs: run: forge test --isolate -vvv env: FOUNDRY_PROFILE: pr - FORGE_SNAPSHOT_CHECK: true + FORGE_SNAPSHOT_CHECK: false MAINNET_RPC_URL: ${{ secrets.MAINNET_RPC_URL }} UNICHAIN_RPC_URL: ${{ secrets.UNICHAIN_RPC_URL }} ARBITRUM_RPC_URL: ${{ secrets.ARBITRUM_RPC_URL }} From de171f6fc9218686be2d7e25ea2193c274ff3b1a Mon Sep 17 00:00:00 2001 From: jtriley Date: Tue, 14 Jul 2026 19:37:38 -0700 Subject: [PATCH 13/55] manually revert snapshots --- snapshots/V4FeeAdapterTest.json | 30 +++++++++++++++--------------- 1 file changed, 15 insertions(+), 15 deletions(-) diff --git a/snapshots/V4FeeAdapterTest.json b/snapshots/V4FeeAdapterTest.json index 999ee173..63e5088f 100644 --- a/snapshots/V4FeeAdapterTest.json +++ b/snapshots/V4FeeAdapterTest.json @@ -1,12 +1,12 @@ { - "adapter.batchTriggerFeeUpdate - two pools": "82831", - "adapter.collect - single currency": "58075", - "adapter.getFee - pool override hit": "3329", - "adapter.setPoolOverride": "48092", - "adapter.triggerFeeUpdate - single pool": "58072", - "policy.batchClearPairClassFee": "33164", - "policy.batchSetHookFamily": "72789", - "policy.batchSetPairClassFee": "76609", + "adapter.batchTriggerFeeUpdate - two pools": "82840", + "adapter.collect - single currency": "58078", + "adapter.getFee - pool override hit": "3330", + "adapter.setPoolOverride": "48093", + "adapter.triggerFeeUpdate - single pool": "58076", + "policy.batchClearPairClassFee": "33166", + "policy.batchSetHookFamily": "72791", + "policy.batchSetPairClassFee": "76610", "policy.computeFee - classified family default": "7928", "policy.computeFee - classified flag-rule self-report": "19644", "policy.computeFee - classified griefing hook -> defaultFee": "7881", @@ -16,11 +16,11 @@ "policy.computeFee - flag-rule single flag match": "19644", "policy.computeFee - static native math buckets": "11158", "policy.computeFee - static native math pair class fee": "5745", - "policy.setFamilyDefault": "47849", - "policy.setFeeBuckets - 1 bucket": "71500", - "policy.setFeeBuckets - 5 buckets (configuration B)": "148708", - "policy.setFlagRules - 32 rules (max)": "1499932", - "policy.setFlagRules - two rules": "138172", - "policy.setHookFamily": "47600", - "policy.setPairClassFee": "49408" + "policy.setFamilyDefault": "47850", + "policy.setFeeBuckets - 1 bucket": "71511", + "policy.setFeeBuckets - 5 buckets (configuration B)": "148719", + "policy.setFlagRules - 32 rules (max)": "1499942", + "policy.setFlagRules - two rules": "138182", + "policy.setHookFamily": "47601", + "policy.setPairClassFee": "49410" } \ No newline at end of file From a71a5e9d68a911153bbed0aed54dd2f0eb71abbf Mon Sep 17 00:00:00 2001 From: jtriley Date: Wed, 15 Jul 2026 14:23:34 -0700 Subject: [PATCH 14/55] rm stale err decl --- src/releasers/ArbitrumOrbitResourceFirepit.sol | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/releasers/ArbitrumOrbitResourceFirepit.sol b/src/releasers/ArbitrumOrbitResourceFirepit.sol index 88de11fc..df41a5a1 100644 --- a/src/releasers/ArbitrumOrbitResourceFirepit.sol +++ b/src/releasers/ArbitrumOrbitResourceFirepit.sol @@ -23,8 +23,6 @@ import {ExchangeReleaser} from "./ExchangeReleaser.sol"; contract ArbitrumOrbitResourceFirepit is ExchangeReleaser { using SafeTransferLib for ERC20; - error ZeroAddress(); - /// @dev The L2 Gateway Router address on the Arbitrum Orbit chain address public immutable L2_GATEWAY_ROUTER; From 5d41ab3fd200d0af533453060c5caaed311dcffc Mon Sep 17 00:00:00 2001 From: jtriley Date: Thu, 16 Jul 2026 09:13:10 -0700 Subject: [PATCH 15/55] update fs perms --- foundry.toml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/foundry.toml b/foundry.toml index f8ca2707..5ad99903 100644 --- a/foundry.toml +++ b/foundry.toml @@ -17,7 +17,8 @@ compilation_restrictions = [ fs_permissions = [ { access = "read", path = "./broadcast"}, - { access = "read-write", path = "./.records/" } + { access = "read-write", path = "./.records/" }, + { access = "read-write", path = "out/.seatbelt" } ] [rpc_endpoints] From cb0bc39eb6cebf2009e21edafffb31d301d7856f Mon Sep 17 00:00:00 2001 From: jtriley Date: Thu, 16 Jul 2026 12:00:03 -0700 Subject: [PATCH 16/55] update description, add lpb+cca --- script/proposal-6/Description.sol | 108 ++++++++++++++++++ script/proposal-6/V4FeesProposal.s.sol | 6 +- .../proposal-6/prereq/DeployV4FeeInfra.s.sol | 39 ++++++- 3 files changed, 149 insertions(+), 4 deletions(-) create mode 100644 script/proposal-6/Description.sol diff --git a/script/proposal-6/Description.sol b/script/proposal-6/Description.sol new file mode 100644 index 00000000..385bac2e --- /dev/null +++ b/script/proposal-6/Description.sol @@ -0,0 +1,108 @@ +// SPDX-License-Identifier: MIT +pragma solidity 0.8.29; + +string constant DESCRIPTION = +"## Summary" +"" +"This proposal continues the protocol fee rollout approved in UNIfication, following proposals [#93](https://vote.uniswapfoundation.org/proposals/93), [#94](https://vote.uniswapfoundation.org/proposals/94), [#95](https://vote.uniswapfoundation.org/proposals/95), and [#96](https://vote.uniswapfoundation.org/proposals/96). It uses the expedited governance process where fee parameter update proposals go directly to a five-day Snapshot followed by an onchain vote." +"" +"Protocol fees are now live across all v2 and v3 pools on 11 chains - Ethereum, Arbitrum, Base, Celo, OP Mainnet, Soneium, X Layer, Worldchain, Zora, BNB Chain, and Polygon. Last month, the protocol set a record burning [186,000 UNI in one day](https://x.com/UNIBurnBot/status/2063252718043459633). " +"" +"Below we introduce a system for v4 protocol fees and propose to activate it on a subset of v4 pools on these same chains. This onchain vote will specifically activate v4 fees on Ethereum, Arbitrum, Base, BNB Chain, Celo, Optimism, and Robinhood Chain. " +"" +"---" +"" +"## Implementation Details" +"" +"v4's hook architecture requires a different approach to fee activation than v2 or v3. v2 pools have a single LP fee tier and are charged a static fee. v3 has several LP fee tiers, each charged a static fee. Hooks mean v4 has potentially infinite distinct LP fee tiers, and a pool's fees can change dynamically from one block to the next. To manage this, we propose a V4 Fee Controller system where governance sets rules that let a dedicated contract compute the fee for any pool on demand, rather than setting a fee on each individual pool." +"" +"The system splits across two contracts:" +"" +"* **V4FeePolicy**. Given any pool, it computes the fee from rules defined by governance. This is the contract governance calls to enable and adjust the protocol fee, and it can be swapped out later if the logic for setting fees needs to evolve." +"" +"* **V4FeeAdapter**. Enforces governance overrides, so if governance has set a per-pool override, that override wins and the policy is skipped. Otherwise the adapter applies the policy's fee, pushes it to the pool, and collects the proceeds to the TokenJar." +"" +"`V4FeePolicy` determines a pool's fee in two steps. First it sorts the pool into a **family**. A pool's family is determined by its characteristics, e.g. whether it has a hook, whether it uses the `PoolManager`'s native swap math, whether it charges dynamic swap fees, et cetera. A pool's family is identified by a flag, which is stored on the hook smart contract. Hook developers can opt their pools into a family via assigning it a specific flag. Governance can also assign a hook to a family directly via a vote. " +"" +"Once it determines the pool's family, the policy then resolves the fee, applying the rules below, going in order from most specific to least:" +"" +"1. a per-pair fee, if governance has set one for that token pair in that family" +"2. otherwise the family's own fee (defined by governance as a default or curve)" +"3. otherwise a global default for anything still unclassified" +"" +"With this system, governance manages a handful of rules and overrides instead of an unbounded list of pools, any fee is computed deterministically and can be inspected onchain, and the policy itself is replaceable if governance later wants to change how pools are categorized." +"" +"This proposal activates fees on three pool families:" +"" +"* **Static fee pools:** These are pools without hooks. The protocol fee for these pools is set via a curve targeting a proportion of each pool's LP fee. For a description of this curve, please see the appendix below." +"* **CCA Pools**: These are pools launched after a Continuous Clearing Auction. The LBPHook and pools resulting from previous auctions will be opted into the same curve as static pools." +"* **Aggregator hook pools:** These are pools whose hooks integrate external liquidity venues into the v4 routing graph. The protocol fee for the aggregator hook family is a flat fee with overrides for specific pair types. To maintain the option of charging more on this external flow than the v4 PoolManager's hard cap of 10bps, aggregator hooks will multiply their assigned fees by 25, allowing for a cap of 250bps. After the multiplier is applied, the resulting fee for aggregator hooks will be:" +" * For all chains other than Base:" +" * Family Default: 10bp" +" * Select Stable Pairs: 3bp" +" * For Base: " +" * Family Default: 3bp" +" * Select Stable Pairs: 1bp" +"**This proposal does not enable the protocol fee for any pools other than those in the Families mentioned above.**" +"" +"Fees will flow to TokenJar on each chain. UNI burned on L2s and alt-L1s will be bridged back to Ethereum mainnet and sent to `0xdead`." +"" +"---" +"" +"## Onchain Proposal Spec" +"" +"**Pre-proposal** (to be completed by Uniswap Labs prior to an onchain vote)" +"" +"* Deploy V4FeeAdapter and V4FeePolicy contracts on all chains where the v2 and v3 protocol fees are currently enabled (Ethereum, Arbitrum, Base, Celo, OP Mainnet, Soneium, X Layer, Worldchain, Zora, BNB Chain, and Polygon)." +"* Configure V4FeePolicy contracts with the native math protocol fee curve, CCA Hook and aggregator hook family fee logic described above" +"" +"These contracts can be found [here](https://github.com/Uniswap/protocol-fees/tree/main/src/feeAdapters), and this post will be updated with addresses and explorer links when they have been deployed." +"" +"**In this proposal** (executed if the vote passes):" +"" +"* Set the `V4FeeAdapter` as the `ProtocolFeeController` on the PoolManager on each chain" +"" +"---" +"" +"## Next Steps / Timeline" +"" +"**Snapshot:** July 7-12, 2026" +"" +"**Onchain vote:** Starting the week of July 13, 2026" +"" +"Please note that because of GovernorBravo's limit of 10 actions per proposal, there will be two separate onchain votes posted in parallel to accommodate all chains." +"" +"---" +"" +"## Appendix - Static Fee Curve" +"" +"The V4 Fee Controller allows fee setting using discrete LP fee tier ranges. Each range has a floor, and the next range sets the ceiling. For each range, governance sets two inputs:" +"" +"* `alpha` which is the constant. This is the starting fee for that range." +"* `beta` which is the scaling factor. This is how fast the fee grows within that range. The growth always starts from the floor of the range." +"* Inside any range, the fee is: `alpha + beta * (lpFee - floor)`.The output is floored to the nearest 0.01bp increment, consistent with v4's minimum fee resolution." +"" +"| *Range (bps)* | *Floor* | *Alpha (bps)* | *Beta (bps)* |" +"| :---- | :---- | :---- | :---- |" +"| *0 \\- 0.03* | *0* | *0.01* | *0* |" +"| *0.03 \\- 0.75* | *0.03* | *0.01* | *19/72* |" +"| *0.75 \\- 1* | *0.75* | *0.2* | *0.2* |" +"| *1 \\- 3.75* | *1* | *0.25* | *3/11* |" +"| *3.75 \\- 5* | *3.75* | *1* | *0.2* |" +"| *5 \\- 25* | *5* | *1.25* | *11/80* |" +"| *25 \\- 55* | *25* | *4* | *0.2* |" +"| *\\> 55* | *55* | *10* | *0* |" +"" +"This results in the following fees at the following points. " +"" +"| LP Fee (bps) | Protocol Fee (bps) |" +"| :---- | :---- |" +"| 0.03 | 0.01 |" +"| .75 | 0.20 |" +"| 1 | 0.25 |" +"| 3.75 | 1 |" +"| 5 | 1.25 |" +"| 25 | 4 |" +"| 30 | 5 |" +"| 83.34 | 10 |" +"| 100 | 10 |"; diff --git a/script/proposal-6/V4FeesProposal.s.sol b/script/proposal-6/V4FeesProposal.s.sol index 21781924..54d355a1 100644 --- a/script/proposal-6/V4FeesProposal.s.sol +++ b/script/proposal-6/V4FeesProposal.s.sol @@ -16,7 +16,7 @@ import {OptimismPortal2Encoder} from "govkit/bridges/OptimismPortal2Encoder.sol" import {WormholeEncoder} from "govkit/bridges/WormholeEncoder.sol"; import {IPoolManager} from "govkit/interfaces/IPoolManager.sol"; -string constant DESCRIPTION = "TODO"; +import {DESCRIPTION} from "./Description.sol"; contract V4FeeActivationProposal is Script { Recorder internal recorder; @@ -119,7 +119,7 @@ contract V4FeeActivationProposal is Script { }); Proposal memory v4FeeActivationProposalPartOne = Proposal({ - description: string.concat("Part 1/2:\n", DESCRIPTION), + description: string.concat("# Activate v4 Protocol Fees (Part 2/2) \n\n", DESCRIPTION), calls: LibCall.newCalls( [ activateV4FeesEthereum, @@ -218,7 +218,7 @@ contract V4FeeActivationProposal is Script { }); Proposal memory v4FeeActivationProposalPartTwo = Proposal({ - description: string.concat("Part 2/2:\n", DESCRIPTION), + description: string.concat("# Activate v4 Protocol Fees (Part 2/2) \n\n", DESCRIPTION), calls: LibCall.newCalls( [ activateV4FeesPolygon, diff --git a/script/proposal-6/prereq/DeployV4FeeInfra.s.sol b/script/proposal-6/prereq/DeployV4FeeInfra.s.sol index 2bf53bee..8eb32c9b 100644 --- a/script/proposal-6/prereq/DeployV4FeeInfra.s.sol +++ b/script/proposal-6/prereq/DeployV4FeeInfra.s.sol @@ -15,7 +15,8 @@ import {V4FeePolicy} from "../../../src/feeAdapters/V4FeePolicy.sol"; import { FeeBucket, FlagRule, - PairClassFeeAssignment + PairClassFeeAssignment, + HookFamilyAssignment } from "../../../src/interfaces/IV4FeePolicy.sol"; import {StableStablePairs} from "../StableStablePairs.sol"; @@ -141,6 +142,42 @@ contract DeployV4FeeInfra is Script { // policy.setFamilyDefault({familyId: PARITY_HOOK_ID, feeValue: 0}); + // ----------------------------------------------------------------------------------------- + // Set LBP & CCA Hooks + // + if (chainId == ChainId.Ethereum) { + HookFamilyAssignment[] memory assignments = new HookFamilyAssignment[](4); + assignments[0] = HookFamilyAssignment({hook: 0xd53006d1e3110fD319a79AEEc4c527a0d265E080, familyId: 255}); // aztec (CCA) + assignments[1] = HookFamilyAssignment({hook: 0x890681CfF5AD2069F020027f41f5f68F6a292000, familyId: 255}); // octra (CCA) + assignments[2] = HookFamilyAssignment({hook: 0x358Ac5a3FA0d5A80d78013DBe6A4f290438cA000, familyId: 255}); // strato (CCA) + assignments[3] = HookFamilyAssignment({hook: 0xb98766A35cdc28415be0767D4EA41e39fBA3e000, familyId: 255}); // LBP + policy.batchSetHookFamily(assignments); + } + + if (chainId == ChainId.Base) { + HookFamilyAssignment[] memory assignments = new HookFamilyAssignment[](2); + assignments[0] = HookFamilyAssignment({hook: 0xeA9346e83952840E69Beb36Df365C4e68DE0E080, familyId: 255}); // flow (CCA) + assignments[1] = HookFamilyAssignment({hook: 0x5bB4bAfafEc57BEd50D864AAA9D1ef992611e000, familyId: 255}); // LBP + policy.batchSetHookFamily(assignments); + } + + if (chainId == Constants.Robinhood.CHAIN_ID) { + HookFamilyAssignment[] memory assignments = new HookFamilyAssignment[](2); + assignments[0] = HookFamilyAssignment({hook: 0x05d552391067389EE44fec3924157ed33F976000, familyId: 255}); // LBP + assignments[1] = HookFamilyAssignment({hook: 0xD462a559337859369EF271814851A18F496ba000, familyId: 255}); // new hook + policy.batchSetHookFamily(assignments); + } + + if (chainId == ChainId.UniChain) { + policy.setHookFamily(0x824A3eCDe463DD45cC156b64CEfA132596C9A000, 255); + } + if (chainId == ChainId.Arbitrum) { + policy.setHookFamily(0x18608AD558dcD233F7854242bbAef73988Bee000, 255); + } + if (chainId == ChainId.XLayer) { + policy.setHookFamily(0x95bcb80e3804a085d23778F2956c305d6488e000, 255); + } + // ----------------------------------------------------------------------------------------- // Transfer Authority // From 25653b18c8982e5df76aeba98dcc792ed9571a77 Mon Sep 17 00:00:00 2001 From: jtriley Date: Thu, 16 Jul 2026 12:00:15 -0700 Subject: [PATCH 17/55] add description --- script/proposal-5/Description.sol | 154 ++++++++++++++++++++++++++ script/proposal-5/RobinhoodFees.s.sol | 3 +- 2 files changed, 155 insertions(+), 2 deletions(-) create mode 100644 script/proposal-5/Description.sol diff --git a/script/proposal-5/Description.sol b/script/proposal-5/Description.sol new file mode 100644 index 00000000..776bd82f --- /dev/null +++ b/script/proposal-5/Description.sol @@ -0,0 +1,154 @@ +// SPDX-License-Identifier: MIT +pragma solidity 0.8.29; + +string constant DESCRIPTION = +"# Protocol Fee Expansion: Robinhood Chain" +"" +"*This proposal is part of the protocol fee rollout, following proposals [#93](https://vote.uniswapfoundation.org/proposals/93), [#94](https://vote.uniswapfoundation.org/proposals/94), [#95](https://vote.uniswapfoundation.org/proposals/95), and [#96](https://vote.uniswapfoundation.org/proposals/96). It uses the expedited governance process [approved](https://gov.uniswap.org/t/unification-proposal/25881#p-57882-protocol-fee-rollout-4) in UNIfication, where fee parameter update proposals can bypass the RFC stage and go directly to a five-day Snapshot followed by an onchain vote.*" +"" +"Since protocol fees went live on Ethereum mainnet in late December last year, the rollout has extended to ten additional chains: Arbitrum, Base, OP Mainnet, Worldchain, X Layer, Soneium, Zora, Celo, BNB Chain, and Polygon. The burn system is working as designed, with fees accumulating in TokenJars across chains. From there, searchers claim them in exchange for burning UNI by bridging it back to mainnet and sending it to the burn address." +"" +"Uniswap [launched on Robinhood Chain](https://blog.uniswap.org/robinhood-chain-is-live) at the chain's July 1, 2026 mainnet debut, with v2, v3, and v4 all live. As of July 10, the deployments have crossed [$1b of cumulative swap volume](https://x.com/DefiLlama/status/2075608063969485082?s=20). This proposal:" +"" +"- Extends the infrastructure for collecting and burning protocol fees to Robinhood Chain" +"- Enables v2, v3, and v4 protocol fees on Robinhood Chain" +"" +"## Implementation Details" +"" +"Fees on Robinhood Chain will be routed to the TokenJar on that chain. UNI burned on Robinhood Chain is bridged back to Ethereum mainnet and sent to the burn address." +"" +"Robinhood Chain is an Arbitrum Orbit chain, so this proposal reuses the pattern from the Arbitrum One activation in proposal 94." +"" +"**Governance path.** Same as Arbitrum One: cross-chain governance messages are delivered as retryable tickets through Robinhood Chain's Inbox contract on Ethereum and executed on Robinhood Chain by the L2 alias of the governance Timelock. The aliased Timelock already controls both factories and the v4 PoolManager on Robinhood Chain (it is the v2 factory's feeToSetter and the owner of the v3 factory and the PoolManager), so no ownership migration is required." +"" +"**Burn path.** The releaser, ArbitrumOrbitResourceFirepit, is the Arbitrum One firepit generalized for Orbit chains. A searcher pays bridged UNI on Robinhood Chain to claim the TokenJar's accumulated fees, and the contract withdraws that UNI to the burn address on mainnet through the canonical gateway. As on Arbitrum One, the withdrawal finalizes on mainnet after Robinhood Chain's challenge period." +"" +"The TokenJar, Releaser, V3OpenFeeAdapter, V4FeeAdapter, and V4FeePolicy will be deployed ahead of the onchain votes, with ownership verified against the aliased Timelock. This post will be updated with those addresses when they've been deployed and a link to the repo containing the contracts and deployment scripts once it has been merged." +"" +"Implementation details for the v4 fee system are in the [v4 fee activation temp check](https://gov.uniswap.org/t/temp-check-activate-v4-protocol-fees/26162). v2 and v3 protocol fee levels are the same as on all other chains where fees are live, see breakdown [here](https://developers.uniswap.org/docs/protocols/protocol-fee/concepts/fees#fee-split-table)." +"" +"## Proposal Spec" +"" +"If passed, the Robinhood Fee Activation proposal will execute two calls, each creating a retryable ticket in Robinhood Chain's Inbox on Ethereum." +"" +"`RH_INBOX.createRetryableTicket(...) x2`" +"" +"One call will encode:" +"" +"`V2_FACTORY.setFeeTo(TOKEN_JAR)`" +"" +"The other will encode:" +"" +"`V3_FACTORY.setOwner(V3_OPEN_FEE_ADAPTER)`" +"" +"Once executed on Robinhood Chain, they set the fee collector of UniswapV2Factory to TokenJar and transfer ownership of UniswapV3Factory to V3OpenFeeAdapter." +"" +"Robinhood's v4 activation will be included in the V4 Fee Activation (Part 1) proposal described below. Matching the spec of the [v4 fee activation temp check](https://gov.uniswap.org/t/temp-check-activate-v4-protocol-fees/26162), and delivered through the same retryable ticket path, it will encode:" +"" +"`V4_POOL_MANAGER.setProtocolFeeController(V4_FEE_ADAPTER)`" +"" +"**RELEVANT ADDRESSES:**" +"" +"| Name | Network | Address | Description |" +"| :-- | :-- | :-- | :--|" +"| V2_FACTORY | Robinhood Chain | [0x8bcEaA40B9AcdfAedF85AdF4FF01F5Ad6517937f](https://robinhoodchain.blockscout.com/address/0x8bcEaA40B9AcdfAedF85AdF4FF01F5Ad6517937f) | Uniswap V2 Factory |" +"| V3_FACTORY | Robinhood Chain | [0x1f7d7550B1b028f7571E69A784071F0205FD2EfA](https://robinhoodchain.blockscout.com/address/0x1f7d7550B1b028f7571E69A784071F0205FD2EfA) | Uniswap V3 Factory |" +"| V4_POOL_MANAGER | Robinhood Chain | [0x8366a39cc670b4001a1121b8f6a443a643e40951](https://robinhoodchain.blockscout.com/address/0x8366a39cc670b4001a1121b8f6a443a643e40951) | Uniswap V4 Pool Manager |" +"| L2_GATEWAY_ROUTER | Robinhood Chain | [0x1E324B9316138CA9a73F960213621AD1aaf01B89](https://robinhoodchain.blockscout.com/address/0x1E324B9316138CA9a73F960213621AD1aaf01B89) | Canonical L2 Gateway Router |" +"| ALIASED_TIMELOCK | Robinhood Chain | [0x2BAD8182C09F50c8318d769245beA52C32BE46CD](https://robinhoodchain.blockscout.com/address/0x2BAD8182C09F50c8318d769245beA52C32BE46CD) | L2 alias of the governance Timelock |" +"| RH_INBOX | Ethereum | [0x1A07cc4BD17E0118BdB54D70990D2158AbAD7a2D](https://etherscan.io/address/0x1A07cc4BD17E0118BdB54D70990D2158AbAD7a2D) | Robinhood Chain Delayed Inbox |" +"| TOKEN_JAR | Robinhood Chain | TBD | Fee Collector |" +"| V3_OPEN_FEE_ADAPTER | Robinhood Chain | TBD | Uniswap V3 Fee Adapter |" +"| V4_FEE_ADAPTER | Robinhood Chain | TBD | Uniswap V4 Fee Adapter (protocolFeeController) |" +"| V4_FEE_POLICY | Robinhood Chain | TBD | Uniswap V4 Fee Policy |" +"| RELEASER | Robinhood Chain | TBD | ArbitrumOrbitResourceFirepit (searcher burn) |" +"| UNI | Robinhood Chain | TBD| Bridged UNI |" +"" +"## Onchain Execution" +"" +"This Snapshot covers v2, v3, and v4 fees on Robinhood Chain, but onchain execution will be split across three proposals. A [separate Snapshot](https://snapshot.box/#/s:uniswapgovernance.eth/proposal/0x3fda3c04fa106ad45173011ef4dfa5db2f14cd35c3c3a7a40f067a44cbadd5f4) to enable v4 fees on the eleven chains where v2/v3 fees are already live concludes this weekend. The v4 activation is the same call on every chain (set the PoolManager's protocolFeeController to the V4FeeAdapter), so it is faster and safer to batch the v4 activations together and keep Robinhood's v2/v3 calls on their own:" +"" +"1. **Robinhood Fee Activation**: the two calls specced above" +"2. **V4 Fee Activation (Part 1)**: Ethereum, Base, Robinhood, BNB Chain, Arbitrum, Optimism, Polygon" +"3. **V4 Fee Activation (Part 2)**: Celo, Soneium, X Layer, World Chain, Zora" +"" +"Robinhood Fee Activation and V4 Part 1 will go onchain first, with Part 2 following once a proposer wallet frees up. Robinhood's v4 fees will be enabled through Part 1, contingent on both Snapshots passing." +"" +"## Next Steps / Timeline" +"" +"- **Snapshot begins:** Jul 10, 2026" +"- **Snapshot ends:** Jul 15, 2026" +"*This proposal is part of the protocol fee rollout, following proposals [#93](https://vote.uniswapfoundation.org/proposals/93), [#94](https://vote.uniswapfoundation.org/proposals/94), [#95](https://vote.uniswapfoundation.org/proposals/95), and [#96](https://vote.uniswapfoundation.org/proposals/96). It uses the expedited governance process [approved](https://gov.uniswap.org/t/unification-proposal/25881#p-57882-protocol-fee-rollout-4) in UNIfication, where fee parameter update proposals can bypass the RFC stage and go directly to a five-day Snapshot followed by an onchain vote.*" +"" +"Since protocol fees went live on Ethereum mainnet in late December last year, the rollout has extended to ten additional chains: Arbitrum, Base, OP Mainnet, Worldchain, X Layer, Soneium, Zora, Celo, BNB Chain, and Polygon. The burn system is working as designed, with fees accumulating in TokenJars across chains. From there, searchers claim them in exchange for burning UNI by bridging it back to mainnet and sending it to the burn address." +"" +"Uniswap [launched on Robinhood Chain](https://blog.uniswap.org/robinhood-chain-is-live) at the chain's July 1, 2026 mainnet debut, with v2, v3, and v4 all live. As of July 10, the deployments have crossed [$1b of cumulative swap volume](https://x.com/DefiLlama/status/2075608063969485082?s=20). This proposal:" +"" +"- Extends the infrastructure for collecting and burning protocol fees to Robinhood Chain" +"- Enables v2, v3, and v4 protocol fees on Robinhood Chain" +"" +"## Implementation Details" +"" +"Fees on Robinhood Chain will be routed to the TokenJar on that chain. UNI burned on Robinhood Chain is bridged back to Ethereum mainnet and sent to the burn address." +"" +"Robinhood Chain is an Arbitrum Orbit chain, so this proposal reuses the pattern from the Arbitrum One activation in proposal 94." +"" +"**Governance path.** Same as Arbitrum One: cross-chain governance messages are delivered as retryable tickets through Robinhood Chain's Inbox contract on Ethereum and executed on Robinhood Chain by the L2 alias of the governance Timelock. The aliased Timelock already controls both factories and the v4 PoolManager on Robinhood Chain (it is the v2 factory's feeToSetter and the owner of the v3 factory and the PoolManager), so no ownership migration is required." +"" +"**Burn path.** The releaser, ArbitrumOrbitResourceFirepit, is the Arbitrum One firepit generalized for Orbit chains. A searcher pays bridged UNI on Robinhood Chain to claim the TokenJar's accumulated fees, and the contract withdraws that UNI to the burn address on mainnet through the canonical gateway. As on Arbitrum One, the withdrawal finalizes on mainnet after Robinhood Chain's challenge period." +"" +"The TokenJar, Releaser, V3OpenFeeAdapter, V4FeeAdapter, and V4FeePolicy will be deployed ahead of the onchain votes, with ownership verified against the aliased Timelock. This post will be updated with those addresses when they've been deployed and a link to the repo containing the contracts and deployment scripts once it has been merged." +"" +"Implementation details for the v4 fee system are in the [v4 fee activation temp check](https://gov.uniswap.org/t/temp-check-activate-v4-protocol-fees/26162). v2 and v3 protocol fee levels are the same as on all other chains where fees are live, see breakdown [here](https://developers.uniswap.org/docs/protocols/protocol-fee/concepts/fees#fee-split-table)." +"" +"## Proposal Spec" +"" +"If passed, the Robinhood Fee Activation proposal will execute two calls, each creating a retryable ticket in Robinhood Chain's Inbox on Ethereum." +"" +"`RH_INBOX.createRetryableTicket(...) x2`" +"" +"One call will encode:" +"" +"`V2_FACTORY.setFeeTo(TOKEN_JAR)`" +"" +"The other will encode:" +"" +"`V3_FACTORY.setOwner(V3_OPEN_FEE_ADAPTER)`" +"" +"Once executed on Robinhood Chain, they set the fee collector of UniswapV2Factory to TokenJar and transfer ownership of UniswapV3Factory to V3OpenFeeAdapter." +"" +"Robinhood's v4 activation will be included in the V4 Fee Activation (Part 1) proposal described below. Matching the spec of the [v4 fee activation temp check](https://gov.uniswap.org/t/temp-check-activate-v4-protocol-fees/26162), and delivered through the same retryable ticket path, it will encode:" +"" +"`V4_POOL_MANAGER.setProtocolFeeController(V4_FEE_ADAPTER)`" +"[9:24 AM]**RELEVANT ADDRESSES:**" +"" +"| Name | Network | Address | Description |" +"| :-- | :-- | :-- | :--|" +"| V2_FACTORY | Robinhood Chain | [0x8bcEaA40B9AcdfAedF85AdF4FF01F5Ad6517937f](https://robinhoodchain.blockscout.com/address/0x8bcEaA40B9AcdfAedF85AdF4FF01F5Ad6517937f) | Uniswap V2 Factory |" +"| V3_FACTORY | Robinhood Chain | [0x1f7d7550B1b028f7571E69A784071F0205FD2EfA](https://robinhoodchain.blockscout.com/address/0x1f7d7550B1b028f7571E69A784071F0205FD2EfA) | Uniswap V3 Factory |" +"| V4_POOL_MANAGER | Robinhood Chain | [0x8366a39cc670b4001a1121b8f6a443a643e40951](https://robinhoodchain.blockscout.com/address/0x8366a39cc670b4001a1121b8f6a443a643e40951) | Uniswap V4 Pool Manager |" +"| L2_GATEWAY_ROUTER | Robinhood Chain | [0x1E324B9316138CA9a73F960213621AD1aaf01B89](https://robinhoodchain.blockscout.com/address/0x1E324B9316138CA9a73F960213621AD1aaf01B89) | Canonical L2 Gateway Router |" +"| ALIASED_TIMELOCK | Robinhood Chain | [0x2BAD8182C09F50c8318d769245beA52C32BE46CD](https://robinhoodchain.blockscout.com/address/0x2BAD8182C09F50c8318d769245beA52C32BE46CD) | L2 alias of the governance Timelock |" +"| RH_INBOX | Ethereum | [0x1A07cc4BD17E0118BdB54D70990D2158AbAD7a2D](https://etherscan.io/address/0x1A07cc4BD17E0118BdB54D70990D2158AbAD7a2D) | Robinhood Chain Delayed Inbox |" +"| TOKEN_JAR | Robinhood Chain | TBD | Fee Collector |" +"| V3_OPEN_FEE_ADAPTER | Robinhood Chain | TBD | Uniswap V3 Fee Adapter |" +"| V4_FEE_ADAPTER | Robinhood Chain | TBD | Uniswap V4 Fee Adapter (protocolFeeController) |" +"| V4_FEE_POLICY | Robinhood Chain | TBD | Uniswap V4 Fee Policy |" +"| RELEASER | Robinhood Chain | TBD | ArbitrumOrbitResourceFirepit (searcher burn) |" +"| UNI | Robinhood Chain | TBD| Bridged UNI |" +"" +"## Onchain Execution" +"" +"This Snapshot covers v2, v3, and v4 fees on Robinhood Chain, but onchain execution will be split across three proposals. A [separate Snapshot](https://snapshot.box/#/s:uniswapgovernance.eth/proposal/0x3fda3c04fa106ad45173011ef4dfa5db2f14cd35c3c3a7a40f067a44cbadd5f4) to enable v4 fees on the eleven chains where v2/v3 fees are already live concludes this weekend. The v4 activation is the same call on every chain (set the PoolManager's protocolFeeController to the V4FeeAdapter), so it is faster and safer to batch the v4 activations together and keep Robinhood's v2/v3 calls on their own:" +"" +"1. **Robinhood Fee Activation**: the two calls specced above" +"2. **V4 Fee Activation (Part 1)**: Ethereum, Base, Robinhood, BNB Chain, Arbitrum, Optimism, Polygon" +"3. **V4 Fee Activation (Part 2)**: Celo, Soneium, X Layer, World Chain, Zora" +"" +"Robinhood Fee Activation and V4 Part 1 will go onchain first, with Part 2 following once a proposer wallet frees up. Robinhood's v4 fees will be enabled through Part 1, contingent on both Snapshots passing." +"" +"## Next Steps / Timeline" +"" +"- **Snapshot begins:** Jul 10, 2026" +"- **Snapshot ends:** Jul 15, 2026" +"- **Onchain votes:** Robinhood Fee Activation and V4 Fee Activation (Part 1) following the Snapshots, per standard governance cadence; V4 Fee Activation (Part 2) to follow"; diff --git a/script/proposal-5/RobinhoodFees.s.sol b/script/proposal-5/RobinhoodFees.s.sol index d8468e5c..24b1b7bb 100644 --- a/script/proposal-5/RobinhoodFees.s.sol +++ b/script/proposal-5/RobinhoodFees.s.sol @@ -19,8 +19,7 @@ import {IReleaser} from "govkit/interfaces/IReleaser.sol"; import {IV3OpenFeeAdapter} from "govkit/interfaces/IV3OpenFeeAdapter.sol"; import "./Constants.sol" as Constants; - -string constant DESCRIPTION = "TODO"; +import {DESCRIPTION} from "./Description.sol"; contract RobinhoodFees is Script { Recorder internal recorder; From f86c52ae2e27e36da699191218de312e78c5bd21 Mon Sep 17 00:00:00 2001 From: jtriley Date: Thu, 16 Jul 2026 12:05:32 -0700 Subject: [PATCH 18/55] fmt --- script/proposal-5/Description.sol | 247 +++++++----------- script/proposal-6/Description.sol | 161 ++++-------- .../proposal-6/prereq/DeployV4FeeInfra.s.sol | 29 +- 3 files changed, 173 insertions(+), 264 deletions(-) diff --git a/script/proposal-5/Description.sol b/script/proposal-5/Description.sol index 776bd82f..fcf7b4b4 100644 --- a/script/proposal-5/Description.sol +++ b/script/proposal-5/Description.sol @@ -1,154 +1,99 @@ // SPDX-License-Identifier: MIT pragma solidity 0.8.29; -string constant DESCRIPTION = -"# Protocol Fee Expansion: Robinhood Chain" -"" -"*This proposal is part of the protocol fee rollout, following proposals [#93](https://vote.uniswapfoundation.org/proposals/93), [#94](https://vote.uniswapfoundation.org/proposals/94), [#95](https://vote.uniswapfoundation.org/proposals/95), and [#96](https://vote.uniswapfoundation.org/proposals/96). It uses the expedited governance process [approved](https://gov.uniswap.org/t/unification-proposal/25881#p-57882-protocol-fee-rollout-4) in UNIfication, where fee parameter update proposals can bypass the RFC stage and go directly to a five-day Snapshot followed by an onchain vote.*" -"" -"Since protocol fees went live on Ethereum mainnet in late December last year, the rollout has extended to ten additional chains: Arbitrum, Base, OP Mainnet, Worldchain, X Layer, Soneium, Zora, Celo, BNB Chain, and Polygon. The burn system is working as designed, with fees accumulating in TokenJars across chains. From there, searchers claim them in exchange for burning UNI by bridging it back to mainnet and sending it to the burn address." -"" -"Uniswap [launched on Robinhood Chain](https://blog.uniswap.org/robinhood-chain-is-live) at the chain's July 1, 2026 mainnet debut, with v2, v3, and v4 all live. As of July 10, the deployments have crossed [$1b of cumulative swap volume](https://x.com/DefiLlama/status/2075608063969485082?s=20). This proposal:" -"" -"- Extends the infrastructure for collecting and burning protocol fees to Robinhood Chain" -"- Enables v2, v3, and v4 protocol fees on Robinhood Chain" -"" -"## Implementation Details" -"" -"Fees on Robinhood Chain will be routed to the TokenJar on that chain. UNI burned on Robinhood Chain is bridged back to Ethereum mainnet and sent to the burn address." -"" -"Robinhood Chain is an Arbitrum Orbit chain, so this proposal reuses the pattern from the Arbitrum One activation in proposal 94." -"" -"**Governance path.** Same as Arbitrum One: cross-chain governance messages are delivered as retryable tickets through Robinhood Chain's Inbox contract on Ethereum and executed on Robinhood Chain by the L2 alias of the governance Timelock. The aliased Timelock already controls both factories and the v4 PoolManager on Robinhood Chain (it is the v2 factory's feeToSetter and the owner of the v3 factory and the PoolManager), so no ownership migration is required." -"" -"**Burn path.** The releaser, ArbitrumOrbitResourceFirepit, is the Arbitrum One firepit generalized for Orbit chains. A searcher pays bridged UNI on Robinhood Chain to claim the TokenJar's accumulated fees, and the contract withdraws that UNI to the burn address on mainnet through the canonical gateway. As on Arbitrum One, the withdrawal finalizes on mainnet after Robinhood Chain's challenge period." -"" -"The TokenJar, Releaser, V3OpenFeeAdapter, V4FeeAdapter, and V4FeePolicy will be deployed ahead of the onchain votes, with ownership verified against the aliased Timelock. This post will be updated with those addresses when they've been deployed and a link to the repo containing the contracts and deployment scripts once it has been merged." -"" -"Implementation details for the v4 fee system are in the [v4 fee activation temp check](https://gov.uniswap.org/t/temp-check-activate-v4-protocol-fees/26162). v2 and v3 protocol fee levels are the same as on all other chains where fees are live, see breakdown [here](https://developers.uniswap.org/docs/protocols/protocol-fee/concepts/fees#fee-split-table)." -"" -"## Proposal Spec" -"" -"If passed, the Robinhood Fee Activation proposal will execute two calls, each creating a retryable ticket in Robinhood Chain's Inbox on Ethereum." -"" -"`RH_INBOX.createRetryableTicket(...) x2`" -"" -"One call will encode:" -"" -"`V2_FACTORY.setFeeTo(TOKEN_JAR)`" -"" -"The other will encode:" -"" -"`V3_FACTORY.setOwner(V3_OPEN_FEE_ADAPTER)`" -"" -"Once executed on Robinhood Chain, they set the fee collector of UniswapV2Factory to TokenJar and transfer ownership of UniswapV3Factory to V3OpenFeeAdapter." -"" -"Robinhood's v4 activation will be included in the V4 Fee Activation (Part 1) proposal described below. Matching the spec of the [v4 fee activation temp check](https://gov.uniswap.org/t/temp-check-activate-v4-protocol-fees/26162), and delivered through the same retryable ticket path, it will encode:" -"" -"`V4_POOL_MANAGER.setProtocolFeeController(V4_FEE_ADAPTER)`" -"" -"**RELEVANT ADDRESSES:**" -"" -"| Name | Network | Address | Description |" -"| :-- | :-- | :-- | :--|" -"| V2_FACTORY | Robinhood Chain | [0x8bcEaA40B9AcdfAedF85AdF4FF01F5Ad6517937f](https://robinhoodchain.blockscout.com/address/0x8bcEaA40B9AcdfAedF85AdF4FF01F5Ad6517937f) | Uniswap V2 Factory |" -"| V3_FACTORY | Robinhood Chain | [0x1f7d7550B1b028f7571E69A784071F0205FD2EfA](https://robinhoodchain.blockscout.com/address/0x1f7d7550B1b028f7571E69A784071F0205FD2EfA) | Uniswap V3 Factory |" -"| V4_POOL_MANAGER | Robinhood Chain | [0x8366a39cc670b4001a1121b8f6a443a643e40951](https://robinhoodchain.blockscout.com/address/0x8366a39cc670b4001a1121b8f6a443a643e40951) | Uniswap V4 Pool Manager |" -"| L2_GATEWAY_ROUTER | Robinhood Chain | [0x1E324B9316138CA9a73F960213621AD1aaf01B89](https://robinhoodchain.blockscout.com/address/0x1E324B9316138CA9a73F960213621AD1aaf01B89) | Canonical L2 Gateway Router |" -"| ALIASED_TIMELOCK | Robinhood Chain | [0x2BAD8182C09F50c8318d769245beA52C32BE46CD](https://robinhoodchain.blockscout.com/address/0x2BAD8182C09F50c8318d769245beA52C32BE46CD) | L2 alias of the governance Timelock |" -"| RH_INBOX | Ethereum | [0x1A07cc4BD17E0118BdB54D70990D2158AbAD7a2D](https://etherscan.io/address/0x1A07cc4BD17E0118BdB54D70990D2158AbAD7a2D) | Robinhood Chain Delayed Inbox |" -"| TOKEN_JAR | Robinhood Chain | TBD | Fee Collector |" -"| V3_OPEN_FEE_ADAPTER | Robinhood Chain | TBD | Uniswap V3 Fee Adapter |" -"| V4_FEE_ADAPTER | Robinhood Chain | TBD | Uniswap V4 Fee Adapter (protocolFeeController) |" -"| V4_FEE_POLICY | Robinhood Chain | TBD | Uniswap V4 Fee Policy |" -"| RELEASER | Robinhood Chain | TBD | ArbitrumOrbitResourceFirepit (searcher burn) |" -"| UNI | Robinhood Chain | TBD| Bridged UNI |" -"" -"## Onchain Execution" -"" -"This Snapshot covers v2, v3, and v4 fees on Robinhood Chain, but onchain execution will be split across three proposals. A [separate Snapshot](https://snapshot.box/#/s:uniswapgovernance.eth/proposal/0x3fda3c04fa106ad45173011ef4dfa5db2f14cd35c3c3a7a40f067a44cbadd5f4) to enable v4 fees on the eleven chains where v2/v3 fees are already live concludes this weekend. The v4 activation is the same call on every chain (set the PoolManager's protocolFeeController to the V4FeeAdapter), so it is faster and safer to batch the v4 activations together and keep Robinhood's v2/v3 calls on their own:" -"" -"1. **Robinhood Fee Activation**: the two calls specced above" -"2. **V4 Fee Activation (Part 1)**: Ethereum, Base, Robinhood, BNB Chain, Arbitrum, Optimism, Polygon" -"3. **V4 Fee Activation (Part 2)**: Celo, Soneium, X Layer, World Chain, Zora" -"" -"Robinhood Fee Activation and V4 Part 1 will go onchain first, with Part 2 following once a proposer wallet frees up. Robinhood's v4 fees will be enabled through Part 1, contingent on both Snapshots passing." -"" -"## Next Steps / Timeline" -"" -"- **Snapshot begins:** Jul 10, 2026" -"- **Snapshot ends:** Jul 15, 2026" -"*This proposal is part of the protocol fee rollout, following proposals [#93](https://vote.uniswapfoundation.org/proposals/93), [#94](https://vote.uniswapfoundation.org/proposals/94), [#95](https://vote.uniswapfoundation.org/proposals/95), and [#96](https://vote.uniswapfoundation.org/proposals/96). It uses the expedited governance process [approved](https://gov.uniswap.org/t/unification-proposal/25881#p-57882-protocol-fee-rollout-4) in UNIfication, where fee parameter update proposals can bypass the RFC stage and go directly to a five-day Snapshot followed by an onchain vote.*" -"" -"Since protocol fees went live on Ethereum mainnet in late December last year, the rollout has extended to ten additional chains: Arbitrum, Base, OP Mainnet, Worldchain, X Layer, Soneium, Zora, Celo, BNB Chain, and Polygon. The burn system is working as designed, with fees accumulating in TokenJars across chains. From there, searchers claim them in exchange for burning UNI by bridging it back to mainnet and sending it to the burn address." -"" -"Uniswap [launched on Robinhood Chain](https://blog.uniswap.org/robinhood-chain-is-live) at the chain's July 1, 2026 mainnet debut, with v2, v3, and v4 all live. As of July 10, the deployments have crossed [$1b of cumulative swap volume](https://x.com/DefiLlama/status/2075608063969485082?s=20). This proposal:" -"" -"- Extends the infrastructure for collecting and burning protocol fees to Robinhood Chain" -"- Enables v2, v3, and v4 protocol fees on Robinhood Chain" -"" -"## Implementation Details" -"" -"Fees on Robinhood Chain will be routed to the TokenJar on that chain. UNI burned on Robinhood Chain is bridged back to Ethereum mainnet and sent to the burn address." -"" -"Robinhood Chain is an Arbitrum Orbit chain, so this proposal reuses the pattern from the Arbitrum One activation in proposal 94." -"" -"**Governance path.** Same as Arbitrum One: cross-chain governance messages are delivered as retryable tickets through Robinhood Chain's Inbox contract on Ethereum and executed on Robinhood Chain by the L2 alias of the governance Timelock. The aliased Timelock already controls both factories and the v4 PoolManager on Robinhood Chain (it is the v2 factory's feeToSetter and the owner of the v3 factory and the PoolManager), so no ownership migration is required." -"" -"**Burn path.** The releaser, ArbitrumOrbitResourceFirepit, is the Arbitrum One firepit generalized for Orbit chains. A searcher pays bridged UNI on Robinhood Chain to claim the TokenJar's accumulated fees, and the contract withdraws that UNI to the burn address on mainnet through the canonical gateway. As on Arbitrum One, the withdrawal finalizes on mainnet after Robinhood Chain's challenge period." -"" -"The TokenJar, Releaser, V3OpenFeeAdapter, V4FeeAdapter, and V4FeePolicy will be deployed ahead of the onchain votes, with ownership verified against the aliased Timelock. This post will be updated with those addresses when they've been deployed and a link to the repo containing the contracts and deployment scripts once it has been merged." -"" -"Implementation details for the v4 fee system are in the [v4 fee activation temp check](https://gov.uniswap.org/t/temp-check-activate-v4-protocol-fees/26162). v2 and v3 protocol fee levels are the same as on all other chains where fees are live, see breakdown [here](https://developers.uniswap.org/docs/protocols/protocol-fee/concepts/fees#fee-split-table)." -"" -"## Proposal Spec" -"" -"If passed, the Robinhood Fee Activation proposal will execute two calls, each creating a retryable ticket in Robinhood Chain's Inbox on Ethereum." -"" -"`RH_INBOX.createRetryableTicket(...) x2`" -"" -"One call will encode:" -"" -"`V2_FACTORY.setFeeTo(TOKEN_JAR)`" -"" -"The other will encode:" -"" -"`V3_FACTORY.setOwner(V3_OPEN_FEE_ADAPTER)`" -"" -"Once executed on Robinhood Chain, they set the fee collector of UniswapV2Factory to TokenJar and transfer ownership of UniswapV3Factory to V3OpenFeeAdapter." -"" -"Robinhood's v4 activation will be included in the V4 Fee Activation (Part 1) proposal described below. Matching the spec of the [v4 fee activation temp check](https://gov.uniswap.org/t/temp-check-activate-v4-protocol-fees/26162), and delivered through the same retryable ticket path, it will encode:" -"" -"`V4_POOL_MANAGER.setProtocolFeeController(V4_FEE_ADAPTER)`" -"[9:24 AM]**RELEVANT ADDRESSES:**" -"" -"| Name | Network | Address | Description |" -"| :-- | :-- | :-- | :--|" -"| V2_FACTORY | Robinhood Chain | [0x8bcEaA40B9AcdfAedF85AdF4FF01F5Ad6517937f](https://robinhoodchain.blockscout.com/address/0x8bcEaA40B9AcdfAedF85AdF4FF01F5Ad6517937f) | Uniswap V2 Factory |" -"| V3_FACTORY | Robinhood Chain | [0x1f7d7550B1b028f7571E69A784071F0205FD2EfA](https://robinhoodchain.blockscout.com/address/0x1f7d7550B1b028f7571E69A784071F0205FD2EfA) | Uniswap V3 Factory |" -"| V4_POOL_MANAGER | Robinhood Chain | [0x8366a39cc670b4001a1121b8f6a443a643e40951](https://robinhoodchain.blockscout.com/address/0x8366a39cc670b4001a1121b8f6a443a643e40951) | Uniswap V4 Pool Manager |" -"| L2_GATEWAY_ROUTER | Robinhood Chain | [0x1E324B9316138CA9a73F960213621AD1aaf01B89](https://robinhoodchain.blockscout.com/address/0x1E324B9316138CA9a73F960213621AD1aaf01B89) | Canonical L2 Gateway Router |" -"| ALIASED_TIMELOCK | Robinhood Chain | [0x2BAD8182C09F50c8318d769245beA52C32BE46CD](https://robinhoodchain.blockscout.com/address/0x2BAD8182C09F50c8318d769245beA52C32BE46CD) | L2 alias of the governance Timelock |" -"| RH_INBOX | Ethereum | [0x1A07cc4BD17E0118BdB54D70990D2158AbAD7a2D](https://etherscan.io/address/0x1A07cc4BD17E0118BdB54D70990D2158AbAD7a2D) | Robinhood Chain Delayed Inbox |" -"| TOKEN_JAR | Robinhood Chain | TBD | Fee Collector |" -"| V3_OPEN_FEE_ADAPTER | Robinhood Chain | TBD | Uniswap V3 Fee Adapter |" -"| V4_FEE_ADAPTER | Robinhood Chain | TBD | Uniswap V4 Fee Adapter (protocolFeeController) |" -"| V4_FEE_POLICY | Robinhood Chain | TBD | Uniswap V4 Fee Policy |" -"| RELEASER | Robinhood Chain | TBD | ArbitrumOrbitResourceFirepit (searcher burn) |" -"| UNI | Robinhood Chain | TBD| Bridged UNI |" -"" -"## Onchain Execution" -"" -"This Snapshot covers v2, v3, and v4 fees on Robinhood Chain, but onchain execution will be split across three proposals. A [separate Snapshot](https://snapshot.box/#/s:uniswapgovernance.eth/proposal/0x3fda3c04fa106ad45173011ef4dfa5db2f14cd35c3c3a7a40f067a44cbadd5f4) to enable v4 fees on the eleven chains where v2/v3 fees are already live concludes this weekend. The v4 activation is the same call on every chain (set the PoolManager's protocolFeeController to the V4FeeAdapter), so it is faster and safer to batch the v4 activations together and keep Robinhood's v2/v3 calls on their own:" -"" -"1. **Robinhood Fee Activation**: the two calls specced above" -"2. **V4 Fee Activation (Part 1)**: Ethereum, Base, Robinhood, BNB Chain, Arbitrum, Optimism, Polygon" -"3. **V4 Fee Activation (Part 2)**: Celo, Soneium, X Layer, World Chain, Zora" -"" -"Robinhood Fee Activation and V4 Part 1 will go onchain first, with Part 2 following once a proposer wallet frees up. Robinhood's v4 fees will be enabled through Part 1, contingent on both Snapshots passing." -"" -"## Next Steps / Timeline" -"" -"- **Snapshot begins:** Jul 10, 2026" -"- **Snapshot ends:** Jul 15, 2026" -"- **Onchain votes:** Robinhood Fee Activation and V4 Fee Activation (Part 1) following the Snapshots, per standard governance cadence; V4 Fee Activation (Part 2) to follow"; +string constant DESCRIPTION = "# Protocol Fee Expansion: Robinhood Chain" "" + "*This proposal is part of the protocol fee rollout, following proposals [#93](https://vote.uniswapfoundation.org/proposals/93), [#94](https://vote.uniswapfoundation.org/proposals/94), [#95](https://vote.uniswapfoundation.org/proposals/95), and [#96](https://vote.uniswapfoundation.org/proposals/96). It uses the expedited governance process [approved](https://gov.uniswap.org/t/unification-proposal/25881#p-57882-protocol-fee-rollout-4) in UNIfication, where fee parameter update proposals can bypass the RFC stage and go directly to a five-day Snapshot followed by an onchain vote.*" + "" + "Since protocol fees went live on Ethereum mainnet in late December last year, the rollout has extended to ten additional chains: Arbitrum, Base, OP Mainnet, Worldchain, X Layer, Soneium, Zora, Celo, BNB Chain, and Polygon. The burn system is working as designed, with fees accumulating in TokenJars across chains. From there, searchers claim them in exchange for burning UNI by bridging it back to mainnet and sending it to the burn address." + "" + "Uniswap [launched on Robinhood Chain](https://blog.uniswap.org/robinhood-chain-is-live) at the chain's July 1, 2026 mainnet debut, with v2, v3, and v4 all live. As of July 10, the deployments have crossed [$1b of cumulative swap volume](https://x.com/DefiLlama/status/2075608063969485082?s=20). This proposal:" + "" "- Extends the infrastructure for collecting and burning protocol fees to Robinhood Chain" + "- Enables v2, v3, and v4 protocol fees on Robinhood Chain" "" "## Implementation Details" "" + "Fees on Robinhood Chain will be routed to the TokenJar on that chain. UNI burned on Robinhood Chain is bridged back to Ethereum mainnet and sent to the burn address." + "" + "Robinhood Chain is an Arbitrum Orbit chain, so this proposal reuses the pattern from the Arbitrum One activation in proposal 94." + "" + "**Governance path.** Same as Arbitrum One: cross-chain governance messages are delivered as retryable tickets through Robinhood Chain's Inbox contract on Ethereum and executed on Robinhood Chain by the L2 alias of the governance Timelock. The aliased Timelock already controls both factories and the v4 PoolManager on Robinhood Chain (it is the v2 factory's feeToSetter and the owner of the v3 factory and the PoolManager), so no ownership migration is required." + "" + "**Burn path.** The releaser, ArbitrumOrbitResourceFirepit, is the Arbitrum One firepit generalized for Orbit chains. A searcher pays bridged UNI on Robinhood Chain to claim the TokenJar's accumulated fees, and the contract withdraws that UNI to the burn address on mainnet through the canonical gateway. As on Arbitrum One, the withdrawal finalizes on mainnet after Robinhood Chain's challenge period." + "" + "The TokenJar, Releaser, V3OpenFeeAdapter, V4FeeAdapter, and V4FeePolicy will be deployed ahead of the onchain votes, with ownership verified against the aliased Timelock. This post will be updated with those addresses when they've been deployed and a link to the repo containing the contracts and deployment scripts once it has been merged." + "" + "Implementation details for the v4 fee system are in the [v4 fee activation temp check](https://gov.uniswap.org/t/temp-check-activate-v4-protocol-fees/26162). v2 and v3 protocol fee levels are the same as on all other chains where fees are live, see breakdown [here](https://developers.uniswap.org/docs/protocols/protocol-fee/concepts/fees#fee-split-table)." + "" "## Proposal Spec" "" + "If passed, the Robinhood Fee Activation proposal will execute two calls, each creating a retryable ticket in Robinhood Chain's Inbox on Ethereum." + "" "`RH_INBOX.createRetryableTicket(...) x2`" "" "One call will encode:" "" + "`V2_FACTORY.setFeeTo(TOKEN_JAR)`" "" "The other will encode:" "" + "`V3_FACTORY.setOwner(V3_OPEN_FEE_ADAPTER)`" "" + "Once executed on Robinhood Chain, they set the fee collector of UniswapV2Factory to TokenJar and transfer ownership of UniswapV3Factory to V3OpenFeeAdapter." + "" + "Robinhood's v4 activation will be included in the V4 Fee Activation (Part 1) proposal described below. Matching the spec of the [v4 fee activation temp check](https://gov.uniswap.org/t/temp-check-activate-v4-protocol-fees/26162), and delivered through the same retryable ticket path, it will encode:" + "" "`V4_POOL_MANAGER.setProtocolFeeController(V4_FEE_ADAPTER)`" "" "**RELEVANT ADDRESSES:**" "" + "| Name | Network | Address | Description |" "| :-- | :-- | :-- | :--|" + "| V2_FACTORY | Robinhood Chain | [0x8bcEaA40B9AcdfAedF85AdF4FF01F5Ad6517937f](https://robinhoodchain.blockscout.com/address/0x8bcEaA40B9AcdfAedF85AdF4FF01F5Ad6517937f) | Uniswap V2 Factory |" + "| V3_FACTORY | Robinhood Chain | [0x1f7d7550B1b028f7571E69A784071F0205FD2EfA](https://robinhoodchain.blockscout.com/address/0x1f7d7550B1b028f7571E69A784071F0205FD2EfA) | Uniswap V3 Factory |" + "| V4_POOL_MANAGER | Robinhood Chain | [0x8366a39cc670b4001a1121b8f6a443a643e40951](https://robinhoodchain.blockscout.com/address/0x8366a39cc670b4001a1121b8f6a443a643e40951) | Uniswap V4 Pool Manager |" + "| L2_GATEWAY_ROUTER | Robinhood Chain | [0x1E324B9316138CA9a73F960213621AD1aaf01B89](https://robinhoodchain.blockscout.com/address/0x1E324B9316138CA9a73F960213621AD1aaf01B89) | Canonical L2 Gateway Router |" + "| ALIASED_TIMELOCK | Robinhood Chain | [0x2BAD8182C09F50c8318d769245beA52C32BE46CD](https://robinhoodchain.blockscout.com/address/0x2BAD8182C09F50c8318d769245beA52C32BE46CD) | L2 alias of the governance Timelock |" + "| RH_INBOX | Ethereum | [0x1A07cc4BD17E0118BdB54D70990D2158AbAD7a2D](https://etherscan.io/address/0x1A07cc4BD17E0118BdB54D70990D2158AbAD7a2D) | Robinhood Chain Delayed Inbox |" + "| TOKEN_JAR | Robinhood Chain | TBD | Fee Collector |" + "| V3_OPEN_FEE_ADAPTER | Robinhood Chain | TBD | Uniswap V3 Fee Adapter |" + "| V4_FEE_ADAPTER | Robinhood Chain | TBD | Uniswap V4 Fee Adapter (protocolFeeController) |" + "| V4_FEE_POLICY | Robinhood Chain | TBD | Uniswap V4 Fee Policy |" + "| RELEASER | Robinhood Chain | TBD | ArbitrumOrbitResourceFirepit (searcher burn) |" + "| UNI | Robinhood Chain | TBD| Bridged UNI |" "" "## Onchain Execution" "" + "This Snapshot covers v2, v3, and v4 fees on Robinhood Chain, but onchain execution will be split across three proposals. A [separate Snapshot](https://snapshot.box/#/s:uniswapgovernance.eth/proposal/0x3fda3c04fa106ad45173011ef4dfa5db2f14cd35c3c3a7a40f067a44cbadd5f4) to enable v4 fees on the eleven chains where v2/v3 fees are already live concludes this weekend. The v4 activation is the same call on every chain (set the PoolManager's protocolFeeController to the V4FeeAdapter), so it is faster and safer to batch the v4 activations together and keep Robinhood's v2/v3 calls on their own:" + "" "1. **Robinhood Fee Activation**: the two calls specced above" + "2. **V4 Fee Activation (Part 1)**: Ethereum, Base, Robinhood, BNB Chain, Arbitrum, Optimism, Polygon" + "3. **V4 Fee Activation (Part 2)**: Celo, Soneium, X Layer, World Chain, Zora" "" + "Robinhood Fee Activation and V4 Part 1 will go onchain first, with Part 2 following once a proposer wallet frees up. Robinhood's v4 fees will be enabled through Part 1, contingent on both Snapshots passing." + "" "## Next Steps / Timeline" "" "- **Snapshot begins:** Jul 10, 2026" + "- **Snapshot ends:** Jul 15, 2026" + "*This proposal is part of the protocol fee rollout, following proposals [#93](https://vote.uniswapfoundation.org/proposals/93), [#94](https://vote.uniswapfoundation.org/proposals/94), [#95](https://vote.uniswapfoundation.org/proposals/95), and [#96](https://vote.uniswapfoundation.org/proposals/96). It uses the expedited governance process [approved](https://gov.uniswap.org/t/unification-proposal/25881#p-57882-protocol-fee-rollout-4) in UNIfication, where fee parameter update proposals can bypass the RFC stage and go directly to a five-day Snapshot followed by an onchain vote.*" + "" + "Since protocol fees went live on Ethereum mainnet in late December last year, the rollout has extended to ten additional chains: Arbitrum, Base, OP Mainnet, Worldchain, X Layer, Soneium, Zora, Celo, BNB Chain, and Polygon. The burn system is working as designed, with fees accumulating in TokenJars across chains. From there, searchers claim them in exchange for burning UNI by bridging it back to mainnet and sending it to the burn address." + "" + "Uniswap [launched on Robinhood Chain](https://blog.uniswap.org/robinhood-chain-is-live) at the chain's July 1, 2026 mainnet debut, with v2, v3, and v4 all live. As of July 10, the deployments have crossed [$1b of cumulative swap volume](https://x.com/DefiLlama/status/2075608063969485082?s=20). This proposal:" + "" "- Extends the infrastructure for collecting and burning protocol fees to Robinhood Chain" + "- Enables v2, v3, and v4 protocol fees on Robinhood Chain" "" "## Implementation Details" "" + "Fees on Robinhood Chain will be routed to the TokenJar on that chain. UNI burned on Robinhood Chain is bridged back to Ethereum mainnet and sent to the burn address." + "" + "Robinhood Chain is an Arbitrum Orbit chain, so this proposal reuses the pattern from the Arbitrum One activation in proposal 94." + "" + "**Governance path.** Same as Arbitrum One: cross-chain governance messages are delivered as retryable tickets through Robinhood Chain's Inbox contract on Ethereum and executed on Robinhood Chain by the L2 alias of the governance Timelock. The aliased Timelock already controls both factories and the v4 PoolManager on Robinhood Chain (it is the v2 factory's feeToSetter and the owner of the v3 factory and the PoolManager), so no ownership migration is required." + "" + "**Burn path.** The releaser, ArbitrumOrbitResourceFirepit, is the Arbitrum One firepit generalized for Orbit chains. A searcher pays bridged UNI on Robinhood Chain to claim the TokenJar's accumulated fees, and the contract withdraws that UNI to the burn address on mainnet through the canonical gateway. As on Arbitrum One, the withdrawal finalizes on mainnet after Robinhood Chain's challenge period." + "" + "The TokenJar, Releaser, V3OpenFeeAdapter, V4FeeAdapter, and V4FeePolicy will be deployed ahead of the onchain votes, with ownership verified against the aliased Timelock. This post will be updated with those addresses when they've been deployed and a link to the repo containing the contracts and deployment scripts once it has been merged." + "" + "Implementation details for the v4 fee system are in the [v4 fee activation temp check](https://gov.uniswap.org/t/temp-check-activate-v4-protocol-fees/26162). v2 and v3 protocol fee levels are the same as on all other chains where fees are live, see breakdown [here](https://developers.uniswap.org/docs/protocols/protocol-fee/concepts/fees#fee-split-table)." + "" "## Proposal Spec" "" + "If passed, the Robinhood Fee Activation proposal will execute two calls, each creating a retryable ticket in Robinhood Chain's Inbox on Ethereum." + "" "`RH_INBOX.createRetryableTicket(...) x2`" "" "One call will encode:" "" + "`V2_FACTORY.setFeeTo(TOKEN_JAR)`" "" "The other will encode:" "" + "`V3_FACTORY.setOwner(V3_OPEN_FEE_ADAPTER)`" "" + "Once executed on Robinhood Chain, they set the fee collector of UniswapV2Factory to TokenJar and transfer ownership of UniswapV3Factory to V3OpenFeeAdapter." + "" + "Robinhood's v4 activation will be included in the V4 Fee Activation (Part 1) proposal described below. Matching the spec of the [v4 fee activation temp check](https://gov.uniswap.org/t/temp-check-activate-v4-protocol-fees/26162), and delivered through the same retryable ticket path, it will encode:" + "" "`V4_POOL_MANAGER.setProtocolFeeController(V4_FEE_ADAPTER)`" "[9:24 AM]**RELEVANT ADDRESSES:**" + "" "| Name | Network | Address | Description |" "| :-- | :-- | :-- | :--|" + "| V2_FACTORY | Robinhood Chain | [0x8bcEaA40B9AcdfAedF85AdF4FF01F5Ad6517937f](https://robinhoodchain.blockscout.com/address/0x8bcEaA40B9AcdfAedF85AdF4FF01F5Ad6517937f) | Uniswap V2 Factory |" + "| V3_FACTORY | Robinhood Chain | [0x1f7d7550B1b028f7571E69A784071F0205FD2EfA](https://robinhoodchain.blockscout.com/address/0x1f7d7550B1b028f7571E69A784071F0205FD2EfA) | Uniswap V3 Factory |" + "| V4_POOL_MANAGER | Robinhood Chain | [0x8366a39cc670b4001a1121b8f6a443a643e40951](https://robinhoodchain.blockscout.com/address/0x8366a39cc670b4001a1121b8f6a443a643e40951) | Uniswap V4 Pool Manager |" + "| L2_GATEWAY_ROUTER | Robinhood Chain | [0x1E324B9316138CA9a73F960213621AD1aaf01B89](https://robinhoodchain.blockscout.com/address/0x1E324B9316138CA9a73F960213621AD1aaf01B89) | Canonical L2 Gateway Router |" + "| ALIASED_TIMELOCK | Robinhood Chain | [0x2BAD8182C09F50c8318d769245beA52C32BE46CD](https://robinhoodchain.blockscout.com/address/0x2BAD8182C09F50c8318d769245beA52C32BE46CD) | L2 alias of the governance Timelock |" + "| RH_INBOX | Ethereum | [0x1A07cc4BD17E0118BdB54D70990D2158AbAD7a2D](https://etherscan.io/address/0x1A07cc4BD17E0118BdB54D70990D2158AbAD7a2D) | Robinhood Chain Delayed Inbox |" + "| TOKEN_JAR | Robinhood Chain | TBD | Fee Collector |" + "| V3_OPEN_FEE_ADAPTER | Robinhood Chain | TBD | Uniswap V3 Fee Adapter |" + "| V4_FEE_ADAPTER | Robinhood Chain | TBD | Uniswap V4 Fee Adapter (protocolFeeController) |" + "| V4_FEE_POLICY | Robinhood Chain | TBD | Uniswap V4 Fee Policy |" + "| RELEASER | Robinhood Chain | TBD | ArbitrumOrbitResourceFirepit (searcher burn) |" + "| UNI | Robinhood Chain | TBD| Bridged UNI |" "" "## Onchain Execution" "" + "This Snapshot covers v2, v3, and v4 fees on Robinhood Chain, but onchain execution will be split across three proposals. A [separate Snapshot](https://snapshot.box/#/s:uniswapgovernance.eth/proposal/0x3fda3c04fa106ad45173011ef4dfa5db2f14cd35c3c3a7a40f067a44cbadd5f4) to enable v4 fees on the eleven chains where v2/v3 fees are already live concludes this weekend. The v4 activation is the same call on every chain (set the PoolManager's protocolFeeController to the V4FeeAdapter), so it is faster and safer to batch the v4 activations together and keep Robinhood's v2/v3 calls on their own:" + "" "1. **Robinhood Fee Activation**: the two calls specced above" + "2. **V4 Fee Activation (Part 1)**: Ethereum, Base, Robinhood, BNB Chain, Arbitrum, Optimism, Polygon" + "3. **V4 Fee Activation (Part 2)**: Celo, Soneium, X Layer, World Chain, Zora" "" + "Robinhood Fee Activation and V4 Part 1 will go onchain first, with Part 2 following once a proposer wallet frees up. Robinhood's v4 fees will be enabled through Part 1, contingent on both Snapshots passing." + "" "## Next Steps / Timeline" "" "- **Snapshot begins:** Jul 10, 2026" + "- **Snapshot ends:** Jul 15, 2026" + "- **Onchain votes:** Robinhood Fee Activation and V4 Fee Activation (Part 1) following the Snapshots, per standard governance cadence; V4 Fee Activation (Part 2) to follow"; diff --git a/script/proposal-6/Description.sol b/script/proposal-6/Description.sol index 385bac2e..45ef48a6 100644 --- a/script/proposal-6/Description.sol +++ b/script/proposal-6/Description.sol @@ -1,108 +1,59 @@ // SPDX-License-Identifier: MIT pragma solidity 0.8.29; -string constant DESCRIPTION = -"## Summary" -"" -"This proposal continues the protocol fee rollout approved in UNIfication, following proposals [#93](https://vote.uniswapfoundation.org/proposals/93), [#94](https://vote.uniswapfoundation.org/proposals/94), [#95](https://vote.uniswapfoundation.org/proposals/95), and [#96](https://vote.uniswapfoundation.org/proposals/96). It uses the expedited governance process where fee parameter update proposals go directly to a five-day Snapshot followed by an onchain vote." -"" -"Protocol fees are now live across all v2 and v3 pools on 11 chains - Ethereum, Arbitrum, Base, Celo, OP Mainnet, Soneium, X Layer, Worldchain, Zora, BNB Chain, and Polygon. Last month, the protocol set a record burning [186,000 UNI in one day](https://x.com/UNIBurnBot/status/2063252718043459633). " -"" -"Below we introduce a system for v4 protocol fees and propose to activate it on a subset of v4 pools on these same chains. This onchain vote will specifically activate v4 fees on Ethereum, Arbitrum, Base, BNB Chain, Celo, Optimism, and Robinhood Chain. " -"" -"---" -"" -"## Implementation Details" -"" -"v4's hook architecture requires a different approach to fee activation than v2 or v3. v2 pools have a single LP fee tier and are charged a static fee. v3 has several LP fee tiers, each charged a static fee. Hooks mean v4 has potentially infinite distinct LP fee tiers, and a pool's fees can change dynamically from one block to the next. To manage this, we propose a V4 Fee Controller system where governance sets rules that let a dedicated contract compute the fee for any pool on demand, rather than setting a fee on each individual pool." -"" -"The system splits across two contracts:" -"" -"* **V4FeePolicy**. Given any pool, it computes the fee from rules defined by governance. This is the contract governance calls to enable and adjust the protocol fee, and it can be swapped out later if the logic for setting fees needs to evolve." -"" -"* **V4FeeAdapter**. Enforces governance overrides, so if governance has set a per-pool override, that override wins and the policy is skipped. Otherwise the adapter applies the policy's fee, pushes it to the pool, and collects the proceeds to the TokenJar." -"" -"`V4FeePolicy` determines a pool's fee in two steps. First it sorts the pool into a **family**. A pool's family is determined by its characteristics, e.g. whether it has a hook, whether it uses the `PoolManager`'s native swap math, whether it charges dynamic swap fees, et cetera. A pool's family is identified by a flag, which is stored on the hook smart contract. Hook developers can opt their pools into a family via assigning it a specific flag. Governance can also assign a hook to a family directly via a vote. " -"" -"Once it determines the pool's family, the policy then resolves the fee, applying the rules below, going in order from most specific to least:" -"" -"1. a per-pair fee, if governance has set one for that token pair in that family" -"2. otherwise the family's own fee (defined by governance as a default or curve)" -"3. otherwise a global default for anything still unclassified" -"" -"With this system, governance manages a handful of rules and overrides instead of an unbounded list of pools, any fee is computed deterministically and can be inspected onchain, and the policy itself is replaceable if governance later wants to change how pools are categorized." -"" -"This proposal activates fees on three pool families:" -"" -"* **Static fee pools:** These are pools without hooks. The protocol fee for these pools is set via a curve targeting a proportion of each pool's LP fee. For a description of this curve, please see the appendix below." -"* **CCA Pools**: These are pools launched after a Continuous Clearing Auction. The LBPHook and pools resulting from previous auctions will be opted into the same curve as static pools." -"* **Aggregator hook pools:** These are pools whose hooks integrate external liquidity venues into the v4 routing graph. The protocol fee for the aggregator hook family is a flat fee with overrides for specific pair types. To maintain the option of charging more on this external flow than the v4 PoolManager's hard cap of 10bps, aggregator hooks will multiply their assigned fees by 25, allowing for a cap of 250bps. After the multiplier is applied, the resulting fee for aggregator hooks will be:" -" * For all chains other than Base:" -" * Family Default: 10bp" -" * Select Stable Pairs: 3bp" -" * For Base: " -" * Family Default: 3bp" -" * Select Stable Pairs: 1bp" -"**This proposal does not enable the protocol fee for any pools other than those in the Families mentioned above.**" -"" -"Fees will flow to TokenJar on each chain. UNI burned on L2s and alt-L1s will be bridged back to Ethereum mainnet and sent to `0xdead`." -"" -"---" -"" -"## Onchain Proposal Spec" -"" -"**Pre-proposal** (to be completed by Uniswap Labs prior to an onchain vote)" -"" -"* Deploy V4FeeAdapter and V4FeePolicy contracts on all chains where the v2 and v3 protocol fees are currently enabled (Ethereum, Arbitrum, Base, Celo, OP Mainnet, Soneium, X Layer, Worldchain, Zora, BNB Chain, and Polygon)." -"* Configure V4FeePolicy contracts with the native math protocol fee curve, CCA Hook and aggregator hook family fee logic described above" -"" -"These contracts can be found [here](https://github.com/Uniswap/protocol-fees/tree/main/src/feeAdapters), and this post will be updated with addresses and explorer links when they have been deployed." -"" -"**In this proposal** (executed if the vote passes):" -"" -"* Set the `V4FeeAdapter` as the `ProtocolFeeController` on the PoolManager on each chain" -"" -"---" -"" -"## Next Steps / Timeline" -"" -"**Snapshot:** July 7-12, 2026" -"" -"**Onchain vote:** Starting the week of July 13, 2026" -"" -"Please note that because of GovernorBravo's limit of 10 actions per proposal, there will be two separate onchain votes posted in parallel to accommodate all chains." -"" -"---" -"" -"## Appendix - Static Fee Curve" -"" -"The V4 Fee Controller allows fee setting using discrete LP fee tier ranges. Each range has a floor, and the next range sets the ceiling. For each range, governance sets two inputs:" -"" -"* `alpha` which is the constant. This is the starting fee for that range." -"* `beta` which is the scaling factor. This is how fast the fee grows within that range. The growth always starts from the floor of the range." -"* Inside any range, the fee is: `alpha + beta * (lpFee - floor)`.The output is floored to the nearest 0.01bp increment, consistent with v4's minimum fee resolution." -"" -"| *Range (bps)* | *Floor* | *Alpha (bps)* | *Beta (bps)* |" -"| :---- | :---- | :---- | :---- |" -"| *0 \\- 0.03* | *0* | *0.01* | *0* |" -"| *0.03 \\- 0.75* | *0.03* | *0.01* | *19/72* |" -"| *0.75 \\- 1* | *0.75* | *0.2* | *0.2* |" -"| *1 \\- 3.75* | *1* | *0.25* | *3/11* |" -"| *3.75 \\- 5* | *3.75* | *1* | *0.2* |" -"| *5 \\- 25* | *5* | *1.25* | *11/80* |" -"| *25 \\- 55* | *25* | *4* | *0.2* |" -"| *\\> 55* | *55* | *10* | *0* |" -"" -"This results in the following fees at the following points. " -"" -"| LP Fee (bps) | Protocol Fee (bps) |" -"| :---- | :---- |" -"| 0.03 | 0.01 |" -"| .75 | 0.20 |" -"| 1 | 0.25 |" -"| 3.75 | 1 |" -"| 5 | 1.25 |" -"| 25 | 4 |" -"| 30 | 5 |" -"| 83.34 | 10 |" -"| 100 | 10 |"; +string constant DESCRIPTION = "## Summary" "" + "This proposal continues the protocol fee rollout approved in UNIfication, following proposals [#93](https://vote.uniswapfoundation.org/proposals/93), [#94](https://vote.uniswapfoundation.org/proposals/94), [#95](https://vote.uniswapfoundation.org/proposals/95), and [#96](https://vote.uniswapfoundation.org/proposals/96). It uses the expedited governance process where fee parameter update proposals go directly to a five-day Snapshot followed by an onchain vote." + "" + "Protocol fees are now live across all v2 and v3 pools on 11 chains - Ethereum, Arbitrum, Base, Celo, OP Mainnet, Soneium, X Layer, Worldchain, Zora, BNB Chain, and Polygon. Last month, the protocol set a record burning [186,000 UNI in one day](https://x.com/UNIBurnBot/status/2063252718043459633). " + "" + "Below we introduce a system for v4 protocol fees and propose to activate it on a subset of v4 pools on these same chains. This onchain vote will specifically activate v4 fees on Ethereum, Arbitrum, Base, BNB Chain, Celo, Optimism, and Robinhood Chain. " + "" "---" "" "## Implementation Details" "" + "v4's hook architecture requires a different approach to fee activation than v2 or v3. v2 pools have a single LP fee tier and are charged a static fee. v3 has several LP fee tiers, each charged a static fee. Hooks mean v4 has potentially infinite distinct LP fee tiers, and a pool's fees can change dynamically from one block to the next. To manage this, we propose a V4 Fee Controller system where governance sets rules that let a dedicated contract compute the fee for any pool on demand, rather than setting a fee on each individual pool." + "" "The system splits across two contracts:" "" + "* **V4FeePolicy**. Given any pool, it computes the fee from rules defined by governance. This is the contract governance calls to enable and adjust the protocol fee, and it can be swapped out later if the logic for setting fees needs to evolve." + "" + "* **V4FeeAdapter**. Enforces governance overrides, so if governance has set a per-pool override, that override wins and the policy is skipped. Otherwise the adapter applies the policy's fee, pushes it to the pool, and collects the proceeds to the TokenJar." + "" + "`V4FeePolicy` determines a pool's fee in two steps. First it sorts the pool into a **family**. A pool's family is determined by its characteristics, e.g. whether it has a hook, whether it uses the `PoolManager`'s native swap math, whether it charges dynamic swap fees, et cetera. A pool's family is identified by a flag, which is stored on the hook smart contract. Hook developers can opt their pools into a family via assigning it a specific flag. Governance can also assign a hook to a family directly via a vote. " + "" + "Once it determines the pool's family, the policy then resolves the fee, applying the rules below, going in order from most specific to least:" + "" "1. a per-pair fee, if governance has set one for that token pair in that family" + "2. otherwise the family's own fee (defined by governance as a default or curve)" + "3. otherwise a global default for anything still unclassified" "" + "With this system, governance manages a handful of rules and overrides instead of an unbounded list of pools, any fee is computed deterministically and can be inspected onchain, and the policy itself is replaceable if governance later wants to change how pools are categorized." + "" "This proposal activates fees on three pool families:" "" + "* **Static fee pools:** These are pools without hooks. The protocol fee for these pools is set via a curve targeting a proportion of each pool's LP fee. For a description of this curve, please see the appendix below." + "* **CCA Pools**: These are pools launched after a Continuous Clearing Auction. The LBPHook and pools resulting from previous auctions will be opted into the same curve as static pools." + "* **Aggregator hook pools:** These are pools whose hooks integrate external liquidity venues into the v4 routing graph. The protocol fee for the aggregator hook family is a flat fee with overrides for specific pair types. To maintain the option of charging more on this external flow than the v4 PoolManager's hard cap of 10bps, aggregator hooks will multiply their assigned fees by 25, allowing for a cap of 250bps. After the multiplier is applied, the resulting fee for aggregator hooks will be:" + " * For all chains other than Base:" " * Family Default: 10bp" + " * Select Stable Pairs: 3bp" " * For Base: " " * Family Default: 3bp" + " * Select Stable Pairs: 1bp" + "**This proposal does not enable the protocol fee for any pools other than those in the Families mentioned above.**" + "" + "Fees will flow to TokenJar on each chain. UNI burned on L2s and alt-L1s will be bridged back to Ethereum mainnet and sent to `0xdead`." + "" "---" "" "## Onchain Proposal Spec" "" + "**Pre-proposal** (to be completed by Uniswap Labs prior to an onchain vote)" "" + "* Deploy V4FeeAdapter and V4FeePolicy contracts on all chains where the v2 and v3 protocol fees are currently enabled (Ethereum, Arbitrum, Base, Celo, OP Mainnet, Soneium, X Layer, Worldchain, Zora, BNB Chain, and Polygon)." + "* Configure V4FeePolicy contracts with the native math protocol fee curve, CCA Hook and aggregator hook family fee logic described above" + "" + "These contracts can be found [here](https://github.com/Uniswap/protocol-fees/tree/main/src/feeAdapters), and this post will be updated with addresses and explorer links when they have been deployed." + "" "**In this proposal** (executed if the vote passes):" "" + "* Set the `V4FeeAdapter` as the `ProtocolFeeController` on the PoolManager on each chain" "" + "---" "" "## Next Steps / Timeline" "" "**Snapshot:** July 7-12, 2026" "" + "**Onchain vote:** Starting the week of July 13, 2026" "" + "Please note that because of GovernorBravo's limit of 10 actions per proposal, there will be two separate onchain votes posted in parallel to accommodate all chains." + "" "---" "" "## Appendix - Static Fee Curve" "" + "The V4 Fee Controller allows fee setting using discrete LP fee tier ranges. Each range has a floor, and the next range sets the ceiling. For each range, governance sets two inputs:" + "" "* `alpha` which is the constant. This is the starting fee for that range." + "* `beta` which is the scaling factor. This is how fast the fee grows within that range. The growth always starts from the floor of the range." + "* Inside any range, the fee is: `alpha + beta * (lpFee - floor)`.The output is floored to the nearest 0.01bp increment, consistent with v4's minimum fee resolution." + "" "| *Range (bps)* | *Floor* | *Alpha (bps)* | *Beta (bps)* |" + "| :---- | :---- | :---- | :---- |" "| *0 \\- 0.03* | *0* | *0.01* | *0* |" + "| *0.03 \\- 0.75* | *0.03* | *0.01* | *19/72* |" "| *0.75 \\- 1* | *0.75* | *0.2* | *0.2* |" + "| *1 \\- 3.75* | *1* | *0.25* | *3/11* |" "| *3.75 \\- 5* | *3.75* | *1* | *0.2* |" + "| *5 \\- 25* | *5* | *1.25* | *11/80* |" "| *25 \\- 55* | *25* | *4* | *0.2* |" + "| *\\> 55* | *55* | *10* | *0* |" "" + "This results in the following fees at the following points. " "" + "| LP Fee (bps) | Protocol Fee (bps) |" "| :---- | :---- |" "| 0.03 | 0.01 |" "| .75 | 0.20 |" + "| 1 | 0.25 |" "| 3.75 | 1 |" "| 5 | 1.25 |" "| 25 | 4 |" "| 30 | 5 |" "| 83.34 | 10 |" + "| 100 | 10 |"; diff --git a/script/proposal-6/prereq/DeployV4FeeInfra.s.sol b/script/proposal-6/prereq/DeployV4FeeInfra.s.sol index 8eb32c9b..232cee9f 100644 --- a/script/proposal-6/prereq/DeployV4FeeInfra.s.sol +++ b/script/proposal-6/prereq/DeployV4FeeInfra.s.sol @@ -147,24 +147,37 @@ contract DeployV4FeeInfra is Script { // if (chainId == ChainId.Ethereum) { HookFamilyAssignment[] memory assignments = new HookFamilyAssignment[](4); - assignments[0] = HookFamilyAssignment({hook: 0xd53006d1e3110fD319a79AEEc4c527a0d265E080, familyId: 255}); // aztec (CCA) - assignments[1] = HookFamilyAssignment({hook: 0x890681CfF5AD2069F020027f41f5f68F6a292000, familyId: 255}); // octra (CCA) - assignments[2] = HookFamilyAssignment({hook: 0x358Ac5a3FA0d5A80d78013DBe6A4f290438cA000, familyId: 255}); // strato (CCA) - assignments[3] = HookFamilyAssignment({hook: 0xb98766A35cdc28415be0767D4EA41e39fBA3e000, familyId: 255}); // LBP + assignments[0] = + HookFamilyAssignment({hook: 0xd53006d1e3110fD319a79AEEc4c527a0d265E080, familyId: 255}); // aztec + // (CCA) + assignments[1] = + HookFamilyAssignment({hook: 0x890681CfF5AD2069F020027f41f5f68F6a292000, familyId: 255}); // octra + // (CCA) + assignments[2] = + HookFamilyAssignment({hook: 0x358Ac5a3FA0d5A80d78013DBe6A4f290438cA000, familyId: 255}); // strato + // (CCA) + assignments[3] = + HookFamilyAssignment({hook: 0xb98766A35cdc28415be0767D4EA41e39fBA3e000, familyId: 255}); // LBP policy.batchSetHookFamily(assignments); } if (chainId == ChainId.Base) { HookFamilyAssignment[] memory assignments = new HookFamilyAssignment[](2); - assignments[0] = HookFamilyAssignment({hook: 0xeA9346e83952840E69Beb36Df365C4e68DE0E080, familyId: 255}); // flow (CCA) - assignments[1] = HookFamilyAssignment({hook: 0x5bB4bAfafEc57BEd50D864AAA9D1ef992611e000, familyId: 255}); // LBP + assignments[0] = + HookFamilyAssignment({hook: 0xeA9346e83952840E69Beb36Df365C4e68DE0E080, familyId: 255}); // flow + // (CCA) + assignments[1] = + HookFamilyAssignment({hook: 0x5bB4bAfafEc57BEd50D864AAA9D1ef992611e000, familyId: 255}); // LBP policy.batchSetHookFamily(assignments); } if (chainId == Constants.Robinhood.CHAIN_ID) { HookFamilyAssignment[] memory assignments = new HookFamilyAssignment[](2); - assignments[0] = HookFamilyAssignment({hook: 0x05d552391067389EE44fec3924157ed33F976000, familyId: 255}); // LBP - assignments[1] = HookFamilyAssignment({hook: 0xD462a559337859369EF271814851A18F496ba000, familyId: 255}); // new hook + assignments[0] = + HookFamilyAssignment({hook: 0x05d552391067389EE44fec3924157ed33F976000, familyId: 255}); // LBP + assignments[1] = + HookFamilyAssignment({hook: 0xD462a559337859369EF271814851A18F496ba000, familyId: 255}); // new + // hook policy.batchSetHookFamily(assignments); } From 7ecbfd8c9021ef6d0a8b3b5c4c2cdc8e9d7ea8ef Mon Sep 17 00:00:00 2001 From: jtriley Date: Thu, 16 Jul 2026 12:10:48 -0700 Subject: [PATCH 19/55] add robinhood call --- script/proposal-6/V4FeesProposal.s.sol | 21 ++++++++++++++++++++- 1 file changed, 20 insertions(+), 1 deletion(-) diff --git a/script/proposal-6/V4FeesProposal.s.sol b/script/proposal-6/V4FeesProposal.s.sol index 54d355a1..f4a3a2b0 100644 --- a/script/proposal-6/V4FeesProposal.s.sol +++ b/script/proposal-6/V4FeesProposal.s.sol @@ -16,6 +16,8 @@ import {OptimismPortal2Encoder} from "govkit/bridges/OptimismPortal2Encoder.sol" import {WormholeEncoder} from "govkit/bridges/WormholeEncoder.sol"; import {IPoolManager} from "govkit/interfaces/IPoolManager.sol"; +import "./Constants.sol" as Constants; + import {DESCRIPTION} from "./Description.sol"; contract V4FeeActivationProposal is Script { @@ -118,6 +120,22 @@ contract V4FeeActivationProposal is Script { }) }); + // --------------------------------------------------------------------------------------------- + // 06: Activate V4 Fees for Robinhood + // + Call memory activateV4FeesRobinhood = InboxEncoder.encode({ + inbox: Constants.Ethereum.RH_INBOX, + timelock: uniswap.ethereum.timelock, + remoteCall: Call({ + target: Constants.Robinhood.POOL_MANAGER, + value: 0, + data: abi.encodeCall( + IPoolManager.setProtocolFeeController, + (recorder.read(Constants.Robinhood.CHAIN_ID, "V4FeeAdapter")) + ) + }) + }); + Proposal memory v4FeeActivationProposalPartOne = Proposal({ description: string.concat("# Activate v4 Protocol Fees (Part 2/2) \n\n", DESCRIPTION), calls: LibCall.newCalls( @@ -127,7 +145,8 @@ contract V4FeeActivationProposal is Script { activateV4FeesBase, activateV4FeesBNBChain, activateV4FeesCelo, - activateV4FeesOPMainnet + activateV4FeesOPMainnet, + activateV4FeesRobinhood ] ) }); From 43e340cb72e6ceb92c79666f0f4577f4326e8785 Mon Sep 17 00:00:00 2001 From: jtriley Date: Thu, 16 Jul 2026 13:18:09 -0700 Subject: [PATCH 20/55] update lbp --- script/proposal-6/prereq/DeployV4FeeInfra.s.sol | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/script/proposal-6/prereq/DeployV4FeeInfra.s.sol b/script/proposal-6/prereq/DeployV4FeeInfra.s.sol index 232cee9f..6fc31dfb 100644 --- a/script/proposal-6/prereq/DeployV4FeeInfra.s.sol +++ b/script/proposal-6/prereq/DeployV4FeeInfra.s.sol @@ -157,7 +157,7 @@ contract DeployV4FeeInfra is Script { HookFamilyAssignment({hook: 0x358Ac5a3FA0d5A80d78013DBe6A4f290438cA000, familyId: 255}); // strato // (CCA) assignments[3] = - HookFamilyAssignment({hook: 0xb98766A35cdc28415be0767D4EA41e39fBA3e000, familyId: 255}); // LBP + HookFamilyAssignment({hook: 0x49380c4EfaB1b491006aF7FabAB8B3459F0E6000, familyId: 255}); // LBP policy.batchSetHookFamily(assignments); } @@ -167,7 +167,7 @@ contract DeployV4FeeInfra is Script { HookFamilyAssignment({hook: 0xeA9346e83952840E69Beb36Df365C4e68DE0E080, familyId: 255}); // flow // (CCA) assignments[1] = - HookFamilyAssignment({hook: 0x5bB4bAfafEc57BEd50D864AAA9D1ef992611e000, familyId: 255}); // LBP + HookFamilyAssignment({hook: 0x34385dD739FE5464892BF0bA4CC42492804dA000, familyId: 255}); // LBP policy.batchSetHookFamily(assignments); } @@ -182,13 +182,13 @@ contract DeployV4FeeInfra is Script { } if (chainId == ChainId.UniChain) { - policy.setHookFamily(0x824A3eCDe463DD45cC156b64CEfA132596C9A000, 255); + policy.setHookFamily(0x298eA05D0356B2Ae5cCAa3169E471783ee9EA000, 255); } if (chainId == ChainId.Arbitrum) { - policy.setHookFamily(0x18608AD558dcD233F7854242bbAef73988Bee000, 255); + policy.setHookFamily(0x8Af0775a70Cc94D71DFc0fE809435e833F2Fe000, 255); } if (chainId == ChainId.XLayer) { - policy.setHookFamily(0x95bcb80e3804a085d23778F2956c305d6488e000, 255); + policy.setHookFamily(0x58DF162fF41e5cB42B8515f75F90C1841938A000, 255); } // ----------------------------------------------------------------------------------------- From 3c64c56a001bcd36d14de0a367866f760411ba05 Mon Sep 17 00:00:00 2001 From: jtriley Date: Thu, 16 Jul 2026 14:29:51 -0700 Subject: [PATCH 21/55] docs --- .../prereq/DeployFeeInfraRobinhood.s.sol | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/script/proposal-5/prereq/DeployFeeInfraRobinhood.s.sol b/script/proposal-5/prereq/DeployFeeInfraRobinhood.s.sol index ca8c1873..71e315a9 100644 --- a/script/proposal-5/prereq/DeployFeeInfraRobinhood.s.sol +++ b/script/proposal-5/prereq/DeployFeeInfraRobinhood.s.sol @@ -29,6 +29,11 @@ contract DeployFeeInfraRobinhood is Script { address timelockAlias = InboxEncoder.arbitrumAlias(uniswap.ethereum.timelock); + // ----------------------------------------------------------------------------------------- + // Deploy ArbitrumOrbitDeployer + // + // Deploys the fee infra, configures it, and transfers it to the aliased timelock. + // if (!recorder.exists(Constants.Robinhood.CHAIN_ID, DEPLOYER_NAME)) { vm.startBroadcast(); @@ -60,12 +65,24 @@ contract DeployFeeInfraRobinhood is Script { address releaser = address(deployer.RELEASER()); address v3OpenFeeAdapter = address(deployer.V3_OPEN_FEE_ADAPTER()); + // ----------------------------------------------------------------------------------------- + // Run checks + // + require(ITokenJar(tokenJar).owner() == timelockAlias); require(IReleaser(releaser).thresholdSetter() == timelockAlias); require(IReleaser(releaser).owner() == timelockAlias); require(IV3OpenFeeAdapter(v3OpenFeeAdapter).owner() == timelockAlias); require(IV3OpenFeeAdapter(v3OpenFeeAdapter).feeSetter() == timelockAlias); + // ----------------------------------------------------------------------------------------- + // Write deployments to disk + // + recorder.write({ + chainId: Constants.Robinhood.CHAIN_ID, + deploymentName: DEPLOYER_NAME, + deployment: address(deployer) + }); recorder.write({ chainId: Constants.Robinhood.CHAIN_ID, deploymentName: "TokenJar", deployment: tokenJar }); From f8c352e4d559bc0cea322cbbc5dd4223695c0250 Mon Sep 17 00:00:00 2001 From: jtriley Date: Thu, 16 Jul 2026 14:30:02 -0700 Subject: [PATCH 22/55] add lbp --- .../proposal-6/prereq/DeployV4FeeInfra.s.sol | 29 +++++++++++++++---- 1 file changed, 23 insertions(+), 6 deletions(-) diff --git a/script/proposal-6/prereq/DeployV4FeeInfra.s.sol b/script/proposal-6/prereq/DeployV4FeeInfra.s.sol index 6fc31dfb..6da95e81 100644 --- a/script/proposal-6/prereq/DeployV4FeeInfra.s.sol +++ b/script/proposal-6/prereq/DeployV4FeeInfra.s.sol @@ -146,7 +146,7 @@ contract DeployV4FeeInfra is Script { // Set LBP & CCA Hooks // if (chainId == ChainId.Ethereum) { - HookFamilyAssignment[] memory assignments = new HookFamilyAssignment[](4); + HookFamilyAssignment[] memory assignments = new HookFamilyAssignment[](5); assignments[0] = HookFamilyAssignment({hook: 0xd53006d1e3110fD319a79AEEc4c527a0d265E080, familyId: 255}); // aztec // (CCA) @@ -157,16 +157,20 @@ contract DeployV4FeeInfra is Script { HookFamilyAssignment({hook: 0x358Ac5a3FA0d5A80d78013DBe6A4f290438cA000, familyId: 255}); // strato // (CCA) assignments[3] = + HookFamilyAssignment({hook: 0xb98766A35cdc28415be0767D4EA41e39fBA3e000, familyId: 255}); // LBP + assignments[4] = HookFamilyAssignment({hook: 0x49380c4EfaB1b491006aF7FabAB8B3459F0E6000, familyId: 255}); // LBP policy.batchSetHookFamily(assignments); } if (chainId == ChainId.Base) { - HookFamilyAssignment[] memory assignments = new HookFamilyAssignment[](2); + HookFamilyAssignment[] memory assignments = new HookFamilyAssignment[](3); assignments[0] = HookFamilyAssignment({hook: 0xeA9346e83952840E69Beb36Df365C4e68DE0E080, familyId: 255}); // flow // (CCA) assignments[1] = + HookFamilyAssignment({hook: 0x5bB4bAfafEc57BEd50D864AAA9D1ef992611e000, familyId: 255}); // LBP + assignments[2] = HookFamilyAssignment({hook: 0x34385dD739FE5464892BF0bA4CC42492804dA000, familyId: 255}); // LBP policy.batchSetHookFamily(assignments); } @@ -182,13 +186,25 @@ contract DeployV4FeeInfra is Script { } if (chainId == ChainId.UniChain) { - policy.setHookFamily(0x298eA05D0356B2Ae5cCAa3169E471783ee9EA000, 255); + HookFamilyAssignment[] memory assignments = new HookFamilyAssignment[](2); + assignments[0] = + HookFamilyAssignment({hook: 0x824A3eCDe463DD45cC156b64CEfA132596C9A000, familyId: 255}); // LBP + assignments[1] = + HookFamilyAssignment({hook: 0x298eA05D0356B2Ae5cCAa3169E471783ee9EA000, familyId: 255}); // LBP } if (chainId == ChainId.Arbitrum) { - policy.setHookFamily(0x8Af0775a70Cc94D71DFc0fE809435e833F2Fe000, 255); + HookFamilyAssignment[] memory assignments = new HookFamilyAssignment[](2); + assignments[0] = + HookFamilyAssignment({hook: 0x18608AD558dcD233F7854242bbAef73988Bee000, familyId: 255}); // LBP + assignments[1] = + HookFamilyAssignment({hook: 0x8Af0775a70Cc94D71DFc0fE809435e833F2Fe000, familyId: 255}); // LBP } if (chainId == ChainId.XLayer) { - policy.setHookFamily(0x58DF162fF41e5cB42B8515f75F90C1841938A000, 255); + HookFamilyAssignment[] memory assignments = new HookFamilyAssignment[](2); + assignments[0] = + HookFamilyAssignment({hook: 0x95bcb80e3804a085d23778F2956c305d6488e000, familyId: 255}); // LBP + assignments[1] = + HookFamilyAssignment({hook: 0x58DF162fF41e5cB42B8515f75F90C1841938A000, familyId: 255}); // LBP } // ----------------------------------------------------------------------------------------- @@ -207,7 +223,6 @@ contract DeployV4FeeInfra is Script { // ----------------------------------------------------------------------------------------- // Run Checks // - console.log("Checking ..."); require(address(adapter.POOL_MANAGER()) == poolManager); @@ -257,6 +272,7 @@ contract DeployV4FeeInfra is Script { } } + /// @dev Resovles the addresses of the pool manager, token jar, and timelock based on chain id. function getAddresses(uint256 chainId) internal view returns (address, address, address) { if (chainId == ChainId.Ethereum) { return (uniswap.ethereum.poolManager, uniswap.ethereum.tokenJar, uniswap.ethereum.timelock); @@ -318,6 +334,7 @@ contract DeployV4FeeInfra is Script { revert("invalid chain id"); } + /// @dev sort addresses for pool fee assignment. function sort(address a, address b) internal pure returns (address, address) { return uint160(a) >= uint160(b) ? (b, a) : (a, b); } From 9218c3ac5f39710db841da685ad36571a325ce38 Mon Sep 17 00:00:00 2001 From: jtriley Date: Thu, 16 Jul 2026 14:34:31 -0700 Subject: [PATCH 23/55] fix addr, add check for rh uni addr --- script/proposal-5/Constants.sol | 2 +- script/proposal-5/prereq/DeployFeeInfraRobinhood.s.sol | 6 ++++++ 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/script/proposal-5/Constants.sol b/script/proposal-5/Constants.sol index 4e8a1456..ab299695 100644 --- a/script/proposal-5/Constants.sol +++ b/script/proposal-5/Constants.sol @@ -11,6 +11,6 @@ library Robinhood { address constant V2_FACTORY = 0x8bcEaA40B9AcdfAedF85AdF4FF01F5Ad6517937f; address constant V3_FACTORY = 0x1f7d7550B1b028f7571E69A784071F0205FD2EfA; - address constant UNI = 0x4540A3483Bb94827b0FD9c2b16a6A35Dd6F23529; + address constant UNI = 0xF177d86a28b520e3E396E4F3B96cd8e72D7dabd8; address constant L2_GATEWAY_ROUTER = 0x1E324B9316138CA9a73F960213621AD1aaf01B89; } diff --git a/script/proposal-5/prereq/DeployFeeInfraRobinhood.s.sol b/script/proposal-5/prereq/DeployFeeInfraRobinhood.s.sol index 71e315a9..58eb83f1 100644 --- a/script/proposal-5/prereq/DeployFeeInfraRobinhood.s.sol +++ b/script/proposal-5/prereq/DeployFeeInfraRobinhood.s.sol @@ -13,6 +13,7 @@ import {IReleaser} from "govkit/interfaces/IReleaser.sol"; import {IV3OpenFeeAdapter} from "govkit/interfaces/IV3OpenFeeAdapter.sol"; import {ArbitrumOrbitDeployer} from "../../deployers/ArbitrumOrbitDeployer.sol"; +import {IL1ERC20Gateway} from "../Interfaces.sol"; import "../Constants.sol" as Constants; string constant DEPLOYER_NAME = "ArbitrumOrbitDeployer"; @@ -27,6 +28,11 @@ contract DeployFeeInfraRobinhood is Script { uniswap.loadLatest(); recorder.initialize({scriptName: "DeployFeeInfraRobinhood"}); + address expectedRobinhoodUni = IL1ERC20Gateway(Constants.Ethereum.RH_L1_ERC20_GATEWAY) + .calculateL2TokenAddress(uniswap.ethereum.uni); + + require(expectedRobinhoodUni == Constants.Robinhood.UNI); + address timelockAlias = InboxEncoder.arbitrumAlias(uniswap.ethereum.timelock); // ----------------------------------------------------------------------------------------- From ecfc8edbf9b701808b1dc0ae56cf42ad89fa45eb Mon Sep 17 00:00:00 2001 From: jtriley Date: Thu, 16 Jul 2026 14:47:01 -0700 Subject: [PATCH 24/55] update govkit --- lib/govkit | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/govkit b/lib/govkit index f57ab52e..bd521852 160000 --- a/lib/govkit +++ b/lib/govkit @@ -1 +1 @@ -Subproject commit f57ab52e69726ada693b9d22b9ab05d55d2de6ce +Subproject commit bd521852d54cb2b9119c1c9bf675e286f521b8fc From dd7fced40aef45c0b420cb3dda2d46f50a1f24f1 Mon Sep 17 00:00:00 2001 From: jtriley Date: Thu, 16 Jul 2026 16:12:58 -0700 Subject: [PATCH 25/55] pips off by one --- script/proposal-6/prereq/DeployV4FeeInfra.s.sol | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/script/proposal-6/prereq/DeployV4FeeInfra.s.sol b/script/proposal-6/prereq/DeployV4FeeInfra.s.sol index 6da95e81..0574bd96 100644 --- a/script/proposal-6/prereq/DeployV4FeeInfra.s.sol +++ b/script/proposal-6/prereq/DeployV4FeeInfra.s.sol @@ -87,7 +87,7 @@ contract DeployV4FeeInfra is Script { feeBuckets[0] = FeeBucket({lpFeeFloor: 0, alphaPips: 1, betaPips: 0}); feeBuckets[1] = FeeBucket({lpFeeFloor: 3, alphaPips: 1, betaPips: 263_889}); feeBuckets[2] = FeeBucket({lpFeeFloor: 75, alphaPips: 20, betaPips: 200_000}); - feeBuckets[3] = FeeBucket({lpFeeFloor: 100, alphaPips: 25, betaPips: 272_727}); + feeBuckets[3] = FeeBucket({lpFeeFloor: 100, alphaPips: 25, betaPips: 272_728}); feeBuckets[4] = FeeBucket({lpFeeFloor: 375, alphaPips: 100, betaPips: 200_000}); feeBuckets[5] = FeeBucket({lpFeeFloor: 500, alphaPips: 125, betaPips: 137_500}); feeBuckets[6] = FeeBucket({lpFeeFloor: 2500, alphaPips: 400, betaPips: 200_000}); From 9f723fa3486499f037006a3ba7ef3683062d151d Mon Sep 17 00:00:00 2001 From: jtriley Date: Thu, 16 Jul 2026 16:14:13 -0700 Subject: [PATCH 26/55] rm parity --- script/proposal-6/prereq/DeployV4FeeInfra.s.sol | 16 +--------------- 1 file changed, 1 insertion(+), 15 deletions(-) diff --git a/script/proposal-6/prereq/DeployV4FeeInfra.s.sol b/script/proposal-6/prereq/DeployV4FeeInfra.s.sol index 0574bd96..aef5f44b 100644 --- a/script/proposal-6/prereq/DeployV4FeeInfra.s.sol +++ b/script/proposal-6/prereq/DeployV4FeeInfra.s.sol @@ -24,8 +24,6 @@ import "../Constants.sol" as Constants; uint256 constant AGG_HOOK_FAMILY = 1 << 11; uint8 constant AGG_HOOK_ID = 11; -uint256 constant PARITY_HOOK_FAMILY = 1 << 12; -uint8 constant PARITY_HOOK_ID = 12; contract DeployV4FeeInfra is Script { Recorder internal recorder; @@ -100,12 +98,10 @@ contract DeployV4FeeInfra is Script { // | Name | ID | Flags | // | ---------------- | ---- | --------- | // | Aggregator Hooks | `11` | `1 << 11` | - // | Parity Hooks | `12` | `1 << 12` | // { - FlagRule[] memory flagRules = new FlagRule[](2); + FlagRule[] memory flagRules = new FlagRule[](1); flagRules[0] = FlagRule({requiredFlags: AGG_HOOK_FAMILY, familyId: AGG_HOOK_ID}); - flagRules[1] = FlagRule({requiredFlags: PARITY_HOOK_FAMILY, familyId: PARITY_HOOK_ID}); policy.setFlagRules(flagRules); } @@ -137,11 +133,6 @@ contract DeployV4FeeInfra is Script { policy.batchSetPairClassFee(assignments); } - // ----------------------------------------------------------------------------------------- - // Set Parity Hook Fees - // - policy.setFamilyDefault({familyId: PARITY_HOOK_ID, feeValue: 0}); - // ----------------------------------------------------------------------------------------- // Set LBP & CCA Hooks // @@ -248,11 +239,6 @@ contract DeployV4FeeInfra is Script { require(hookFamily == AGG_HOOK_FAMILY); require(hookId == AGG_HOOK_ID); } - { - (uint256 hookFamily, uint8 hookId) = policy.flagRules(1); - require(hookFamily == PARITY_HOOK_FAMILY); - require(hookId == PARITY_HOOK_ID); - } if (chainId == ChainId.Base) require(policy.familyDefaults(AGG_HOOK_ID) == 300); else require(policy.familyDefaults(AGG_HOOK_ID) == 1000); From 67dd878c7912cfde37f3ea89f0947bd11fab4f43 Mon Sep 17 00:00:00 2001 From: jtriley Date: Thu, 16 Jul 2026 16:15:21 -0700 Subject: [PATCH 27/55] rm dup on polygon --- script/proposal-6/StableStablePairs.sol | 3 --- 1 file changed, 3 deletions(-) diff --git a/script/proposal-6/StableStablePairs.sol b/script/proposal-6/StableStablePairs.sol index 269be2f5..e9e46ddd 100644 --- a/script/proposal-6/StableStablePairs.sol +++ b/script/proposal-6/StableStablePairs.sol @@ -314,9 +314,6 @@ library LibStableStablePairs { polygonPairs.push( Pair(0x2791Bca1f2de4661ED88A30C99A7a9449Aa84174, 0x3c499c542cEF5E3811e1192ce70d8cC03d5c3359) ); - polygonPairs.push( - Pair(0x2791Bca1f2de4661ED88A30C99A7a9449Aa84174, 0x2791Bca1f2de4661ED88A30C99A7a9449Aa84174) - ); polygonPairs.push( Pair(0x8f3Cf7ad23Cd3CaDbD9735AFf958023239c6A063, 0xc2132D05D31c914a87C6611C10748AEb04B58e8F) ); From 287f310412dbd1ccbe2296ec87e60b95ac751530 Mon Sep 17 00:00:00 2001 From: jtriley Date: Thu, 16 Jul 2026 16:35:26 -0700 Subject: [PATCH 28/55] add fee encoding --- script/proposal-6/prereq/DeployV4FeeInfra.s.sol | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/script/proposal-6/prereq/DeployV4FeeInfra.s.sol b/script/proposal-6/prereq/DeployV4FeeInfra.s.sol index aef5f44b..46c3df55 100644 --- a/script/proposal-6/prereq/DeployV4FeeInfra.s.sol +++ b/script/proposal-6/prereq/DeployV4FeeInfra.s.sol @@ -112,7 +112,7 @@ contract DeployV4FeeInfra is Script { uint24 defaultFeeValue = chainId == ChainId.Base ? 300 : 1000; uint24 stableStableFeeValue = chainId == ChainId.Base ? 100 : 300; - policy.setFamilyDefault({familyId: AGG_HOOK_ID, feeValue: defaultFeeValue}); + policy.setFamilyDefault({familyId: AGG_HOOK_ID, feeValue: encodeFee(defaultFeeValue)}); uint256 length = stableStablePairs.chainPairs[chainId].length; @@ -127,7 +127,7 @@ contract DeployV4FeeInfra is Script { currency0: Currency.wrap(token0), currency1: Currency.wrap(token1), familyId: AGG_HOOK_ID, - feeValue: stableStableFeeValue + feeValue: encodeFee(stableStableFeeValue) }); } policy.batchSetPairClassFee(assignments); @@ -320,6 +320,11 @@ contract DeployV4FeeInfra is Script { revert("invalid chain id"); } + /// @dev Handles v4 fee two way encoding + function encodeFee(uint24 fee) internal pure returns (uint24) { + return fee << 12 | fee; + } + /// @dev sort addresses for pool fee assignment. function sort(address a, address b) internal pure returns (address, address) { return uint160(a) >= uint160(b) ? (b, a) : (a, b); From 4a7294a8e7dcbc38481982a4febb7423de8d84cf Mon Sep 17 00:00:00 2001 From: jtriley Date: Thu, 16 Jul 2026 16:52:30 -0700 Subject: [PATCH 29/55] div agg hook fees by 25 --- script/proposal-6/prereq/DeployV4FeeInfra.s.sol | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/script/proposal-6/prereq/DeployV4FeeInfra.s.sol b/script/proposal-6/prereq/DeployV4FeeInfra.s.sol index 46c3df55..7f53c798 100644 --- a/script/proposal-6/prereq/DeployV4FeeInfra.s.sol +++ b/script/proposal-6/prereq/DeployV4FeeInfra.s.sol @@ -109,8 +109,8 @@ contract DeployV4FeeInfra is Script { // Set Aggregator Hook Fees // { - uint24 defaultFeeValue = chainId == ChainId.Base ? 300 : 1000; - uint24 stableStableFeeValue = chainId == ChainId.Base ? 100 : 300; + uint24 defaultFeeValue = (chainId == ChainId.Base ? 300 : 1000) / 25; + uint24 stableStableFeeValue = (chainId == ChainId.Base ? 100 : 300) / 25; policy.setFamilyDefault({familyId: AGG_HOOK_ID, feeValue: encodeFee(defaultFeeValue)}); From 62023bfc98d1dc4184d9fc865e7d27e5f1de12e4 Mon Sep 17 00:00:00 2001 From: jtriley Date: Thu, 16 Jul 2026 17:01:50 -0700 Subject: [PATCH 30/55] l1 xdm msgr -> op portal --- script/proposal-6/V4FeesProposal.s.sol | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/script/proposal-6/V4FeesProposal.s.sol b/script/proposal-6/V4FeesProposal.s.sol index f4a3a2b0..f3990622 100644 --- a/script/proposal-6/V4FeesProposal.s.sol +++ b/script/proposal-6/V4FeesProposal.s.sol @@ -15,6 +15,7 @@ import {L1CrossDomainMessengerEncoder} from "govkit/bridges/L1CrossDomainMesseng import {OptimismPortal2Encoder} from "govkit/bridges/OptimismPortal2Encoder.sol"; import {WormholeEncoder} from "govkit/bridges/WormholeEncoder.sol"; import {IPoolManager} from "govkit/interfaces/IPoolManager.sol"; +import {IL1CrossDomainMessenger} from "govkit/interfaces/bridges/IL1CrossDomainMessenger.sol"; import "./Constants.sol" as Constants; @@ -196,7 +197,7 @@ contract V4FeeActivationProposal is Script { // 08: Activate V4 Fees for Worldchain. // Call memory activateV4FeesWorldchain = OptimismPortal2Encoder.encode({ - portal: uniswap.ethereum.bridge.worldChain, + portal: IL1CrossDomainMessenger(uniswap.ethereum.bridge.worldChain).portal(), remoteCall: Call({ target: uniswap.worldChain.poolManager, value: 0, @@ -211,7 +212,7 @@ contract V4FeeActivationProposal is Script { // 09: Activate V4 Fees for X Layer // Call memory activateV4FeesXLayer = OptimismPortal2Encoder.encode({ - portal: uniswap.ethereum.bridge.xLayer, + portal: IL1CrossDomainMessenger(uniswap.ethereum.bridge.xLayer).portal(), remoteCall: Call({ target: uniswap.xLayer.poolManager, value: 0, From 96aa4b9d061084a12ce3e0159fa78e2dd0444dbd Mon Sep 17 00:00:00 2001 From: jtriley Date: Thu, 16 Jul 2026 17:08:29 -0700 Subject: [PATCH 31/55] rm check, wrong net --- script/proposal-5/prereq/DeployFeeInfraRobinhood.s.sol | 5 ----- 1 file changed, 5 deletions(-) diff --git a/script/proposal-5/prereq/DeployFeeInfraRobinhood.s.sol b/script/proposal-5/prereq/DeployFeeInfraRobinhood.s.sol index 58eb83f1..a5b51331 100644 --- a/script/proposal-5/prereq/DeployFeeInfraRobinhood.s.sol +++ b/script/proposal-5/prereq/DeployFeeInfraRobinhood.s.sol @@ -28,11 +28,6 @@ contract DeployFeeInfraRobinhood is Script { uniswap.loadLatest(); recorder.initialize({scriptName: "DeployFeeInfraRobinhood"}); - address expectedRobinhoodUni = IL1ERC20Gateway(Constants.Ethereum.RH_L1_ERC20_GATEWAY) - .calculateL2TokenAddress(uniswap.ethereum.uni); - - require(expectedRobinhoodUni == Constants.Robinhood.UNI); - address timelockAlias = InboxEncoder.arbitrumAlias(uniswap.ethereum.timelock); // ----------------------------------------------------------------------------------------- From 7c18a55eb39c02515de55ec26fa5ae831b7a9bbb Mon Sep 17 00:00:00 2001 From: jtriley Date: Thu, 16 Jul 2026 17:10:26 -0700 Subject: [PATCH 32/55] rm uni, add missing policy call --- script/proposal-6/prereq/DeployV4FeeInfra.s.sol | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) diff --git a/script/proposal-6/prereq/DeployV4FeeInfra.s.sol b/script/proposal-6/prereq/DeployV4FeeInfra.s.sol index 7f53c798..a5a83e4e 100644 --- a/script/proposal-6/prereq/DeployV4FeeInfra.s.sol +++ b/script/proposal-6/prereq/DeployV4FeeInfra.s.sol @@ -176,19 +176,14 @@ contract DeployV4FeeInfra is Script { policy.batchSetHookFamily(assignments); } - if (chainId == ChainId.UniChain) { - HookFamilyAssignment[] memory assignments = new HookFamilyAssignment[](2); - assignments[0] = - HookFamilyAssignment({hook: 0x824A3eCDe463DD45cC156b64CEfA132596C9A000, familyId: 255}); // LBP - assignments[1] = - HookFamilyAssignment({hook: 0x298eA05D0356B2Ae5cCAa3169E471783ee9EA000, familyId: 255}); // LBP - } if (chainId == ChainId.Arbitrum) { HookFamilyAssignment[] memory assignments = new HookFamilyAssignment[](2); assignments[0] = HookFamilyAssignment({hook: 0x18608AD558dcD233F7854242bbAef73988Bee000, familyId: 255}); // LBP assignments[1] = HookFamilyAssignment({hook: 0x8Af0775a70Cc94D71DFc0fE809435e833F2Fe000, familyId: 255}); // LBP + + policy.batchSetHookFamily(assignments); } if (chainId == ChainId.XLayer) { HookFamilyAssignment[] memory assignments = new HookFamilyAssignment[](2); @@ -196,6 +191,8 @@ contract DeployV4FeeInfra is Script { HookFamilyAssignment({hook: 0x95bcb80e3804a085d23778F2956c305d6488e000, familyId: 255}); // LBP assignments[1] = HookFamilyAssignment({hook: 0x58DF162fF41e5cB42B8515f75F90C1841938A000, familyId: 255}); // LBP + + policy.batchSetHookFamily(assignments); } // ----------------------------------------------------------------------------------------- From f17b75c5cef156df69297da40ff1e367f07911fc Mon Sep 17 00:00:00 2001 From: jtriley Date: Thu, 16 Jul 2026 17:31:15 -0700 Subject: [PATCH 33/55] add div by 25 & encode to check --- script/proposal-6/prereq/DeployV4FeeInfra.s.sol | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/script/proposal-6/prereq/DeployV4FeeInfra.s.sol b/script/proposal-6/prereq/DeployV4FeeInfra.s.sol index a5a83e4e..f359f5a0 100644 --- a/script/proposal-6/prereq/DeployV4FeeInfra.s.sol +++ b/script/proposal-6/prereq/DeployV4FeeInfra.s.sol @@ -237,8 +237,11 @@ contract DeployV4FeeInfra is Script { require(hookId == AGG_HOOK_ID); } - if (chainId == ChainId.Base) require(policy.familyDefaults(AGG_HOOK_ID) == 300); - else require(policy.familyDefaults(AGG_HOOK_ID) == 1000); + if (chainId == ChainId.Base) { + require(policy.familyDefaults(AGG_HOOK_ID) == encodeFee(300 / 25)); + } else { + require(policy.familyDefaults(AGG_HOOK_ID) == encodeFee(1000 / 25)); + } uint256 len = stableStablePairs.chainPairs[chainId].length; @@ -250,8 +253,11 @@ contract DeployV4FeeInfra is Script { bytes32 hash = keccak256(abi.encodePacked(token0, token1)); - if (chainId == ChainId.Base) require(policy.pairClassFees(hash, AGG_HOOK_ID) == 100); - else require(policy.pairClassFees(hash, AGG_HOOK_ID) == 300); + if (chainId == ChainId.Base) { + require(policy.pairClassFees(hash, AGG_HOOK_ID) == encodeFee(100 / 25)); + } else { + require(policy.pairClassFees(hash, AGG_HOOK_ID) == encodeFee(300 / 25)); + } } } From 9d2815fe3b41ae9a09569302823a5f7da56025a4 Mon Sep 17 00:00:00 2001 From: jtriley Date: Thu, 16 Jul 2026 17:33:30 -0700 Subject: [PATCH 34/55] fix flag rules length check --- script/proposal-6/prereq/DeployV4FeeInfra.s.sol | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/script/proposal-6/prereq/DeployV4FeeInfra.s.sol b/script/proposal-6/prereq/DeployV4FeeInfra.s.sol index f359f5a0..a65a98fa 100644 --- a/script/proposal-6/prereq/DeployV4FeeInfra.s.sol +++ b/script/proposal-6/prereq/DeployV4FeeInfra.s.sol @@ -230,7 +230,7 @@ contract DeployV4FeeInfra is Script { require(betaPips == feeBuckets[i].betaPips); } - require(policy.flagRulesLength() == 2); + require(policy.flagRulesLength() == 1); { (uint256 hookFamily, uint8 hookId) = policy.flagRules(0); require(hookFamily == AGG_HOOK_FAMILY); From 6b6d6c3eee1f263debb98829443369776c0ac8b6 Mon Sep 17 00:00:00 2001 From: jtriley Date: Thu, 16 Jul 2026 17:42:37 -0700 Subject: [PATCH 35/55] add pool manager to prop 5 --- script/proposal-5/Constants.sol | 1 + 1 file changed, 1 insertion(+) diff --git a/script/proposal-5/Constants.sol b/script/proposal-5/Constants.sol index ab299695..964a1d35 100644 --- a/script/proposal-5/Constants.sol +++ b/script/proposal-5/Constants.sol @@ -11,6 +11,7 @@ library Robinhood { address constant V2_FACTORY = 0x8bcEaA40B9AcdfAedF85AdF4FF01F5Ad6517937f; address constant V3_FACTORY = 0x1f7d7550B1b028f7571E69A784071F0205FD2EfA; + address constant POOL_MANAGER = 0x8366a39CC670B4001A1121B8F6A443A643e40951; address constant UNI = 0xF177d86a28b520e3E396E4F3B96cd8e72D7dabd8; address constant L2_GATEWAY_ROUTER = 0x1E324B9316138CA9a73F960213621AD1aaf01B89; } From 09393f2ebaf3d7dc59ef68fd5c4a27392fb9fc0a Mon Sep 17 00:00:00 2001 From: jtriley Date: Thu, 16 Jul 2026 17:42:57 -0700 Subject: [PATCH 36/55] del prop6 consts, using prop5 consts --- script/proposal-6/Constants.sol | 18 ------------------ script/proposal-6/StableStablePairs.sol | 2 +- script/proposal-6/V4FeesProposal.s.sol | 2 +- .../proposal-6/prereq/DeployV4FeeInfra.s.sol | 2 +- 4 files changed, 3 insertions(+), 21 deletions(-) delete mode 100644 script/proposal-6/Constants.sol diff --git a/script/proposal-6/Constants.sol b/script/proposal-6/Constants.sol deleted file mode 100644 index 05669af6..00000000 --- a/script/proposal-6/Constants.sol +++ /dev/null @@ -1,18 +0,0 @@ -// SPDX-License-Identifier: MIT -pragma solidity 0.8.29; - -library Ethereum { - address constant RH_L1_ERC20_GATEWAY = 0x85001CC4867C5e1C22dA4B79BB8852B9e2a06da0; - address constant RH_INBOX = 0x1A07cc4BD17E0118BdB54D70990D2158AbAD7a2D; -} - -library Robinhood { - uint256 constant CHAIN_ID = 4663; - - address constant V2_FACTORY = 0x8bcEaA40B9AcdfAedF85AdF4FF01F5Ad6517937f; - address constant V3_FACTORY = 0x1f7d7550B1b028f7571E69A784071F0205FD2EfA; - address constant POOL_MANAGER = 0x8366a39CC670B4001A1121B8F6A443A643e40951; - address constant UNI = 0x4540A3483Bb94827b0FD9c2b16a6A35Dd6F23529; - address constant TOKEN_JAR = address(0x00); - address constant L2_GATEWAY_ROUTER = 0x1E324B9316138CA9a73F960213621AD1aaf01B89; -} diff --git a/script/proposal-6/StableStablePairs.sol b/script/proposal-6/StableStablePairs.sol index e9e46ddd..ab20240b 100644 --- a/script/proposal-6/StableStablePairs.sol +++ b/script/proposal-6/StableStablePairs.sol @@ -2,7 +2,7 @@ pragma solidity 0.8.29; import {ChainId} from "lib/govkit/src/constants/ChainId.sol"; -import "./Constants.sol" as Constants; +import "../proposal-5/Constants.sol" as Constants; struct Pair { address token0; diff --git a/script/proposal-6/V4FeesProposal.s.sol b/script/proposal-6/V4FeesProposal.s.sol index f3990622..c8011aba 100644 --- a/script/proposal-6/V4FeesProposal.s.sol +++ b/script/proposal-6/V4FeesProposal.s.sol @@ -17,7 +17,7 @@ import {WormholeEncoder} from "govkit/bridges/WormholeEncoder.sol"; import {IPoolManager} from "govkit/interfaces/IPoolManager.sol"; import {IL1CrossDomainMessenger} from "govkit/interfaces/bridges/IL1CrossDomainMessenger.sol"; -import "./Constants.sol" as Constants; +import "../proposal-5/Constants.sol" as Constants; import {DESCRIPTION} from "./Description.sol"; diff --git a/script/proposal-6/prereq/DeployV4FeeInfra.s.sol b/script/proposal-6/prereq/DeployV4FeeInfra.s.sol index a65a98fa..8fc55dcb 100644 --- a/script/proposal-6/prereq/DeployV4FeeInfra.s.sol +++ b/script/proposal-6/prereq/DeployV4FeeInfra.s.sol @@ -20,7 +20,7 @@ import { } from "../../../src/interfaces/IV4FeePolicy.sol"; import {StableStablePairs} from "../StableStablePairs.sol"; -import "../Constants.sol" as Constants; +import "../../proposal-5/Constants.sol" as Constants; uint256 constant AGG_HOOK_FAMILY = 1 << 11; uint8 constant AGG_HOOK_ID = 11; From e9e21a0d3dee8fb0e97dbfeefb7a4e20ec190e7d Mon Sep 17 00:00:00 2001 From: jtriley Date: Thu, 16 Jul 2026 17:53:17 -0700 Subject: [PATCH 37/55] update fmt --- foundry.toml | 6 +- script/proposal-5/Description.sol | 246 ++++++++++++++++++------------ script/proposal-6/Description.sol | 160 ++++++++++++------- 3 files changed, 259 insertions(+), 153 deletions(-) diff --git a/foundry.toml b/foundry.toml index 5ad99903..3307aad1 100644 --- a/foundry.toml +++ b/foundry.toml @@ -60,7 +60,11 @@ quote_style = "double" single_line_statement_blocks = "single" tab_width = 2 wrap_comments = true -ignore = ["script/proposal-4/ProposalDescription.sol"] +ignore = [ + "script/proposal-4/ProposalDescription.sol", + "script/proposal-5/Description.sol", + "script/proposal-6/Description.sol" + ] [lint] exclude_lints = ["mixed-case-variable", "mixed-case-function"] diff --git a/script/proposal-5/Description.sol b/script/proposal-5/Description.sol index fcf7b4b4..c9dcd942 100644 --- a/script/proposal-5/Description.sol +++ b/script/proposal-5/Description.sol @@ -1,99 +1,153 @@ // SPDX-License-Identifier: MIT pragma solidity 0.8.29; -string constant DESCRIPTION = "# Protocol Fee Expansion: Robinhood Chain" "" - "*This proposal is part of the protocol fee rollout, following proposals [#93](https://vote.uniswapfoundation.org/proposals/93), [#94](https://vote.uniswapfoundation.org/proposals/94), [#95](https://vote.uniswapfoundation.org/proposals/95), and [#96](https://vote.uniswapfoundation.org/proposals/96). It uses the expedited governance process [approved](https://gov.uniswap.org/t/unification-proposal/25881#p-57882-protocol-fee-rollout-4) in UNIfication, where fee parameter update proposals can bypass the RFC stage and go directly to a five-day Snapshot followed by an onchain vote.*" - "" - "Since protocol fees went live on Ethereum mainnet in late December last year, the rollout has extended to ten additional chains: Arbitrum, Base, OP Mainnet, Worldchain, X Layer, Soneium, Zora, Celo, BNB Chain, and Polygon. The burn system is working as designed, with fees accumulating in TokenJars across chains. From there, searchers claim them in exchange for burning UNI by bridging it back to mainnet and sending it to the burn address." - "" - "Uniswap [launched on Robinhood Chain](https://blog.uniswap.org/robinhood-chain-is-live) at the chain's July 1, 2026 mainnet debut, with v2, v3, and v4 all live. As of July 10, the deployments have crossed [$1b of cumulative swap volume](https://x.com/DefiLlama/status/2075608063969485082?s=20). This proposal:" - "" "- Extends the infrastructure for collecting and burning protocol fees to Robinhood Chain" - "- Enables v2, v3, and v4 protocol fees on Robinhood Chain" "" "## Implementation Details" "" - "Fees on Robinhood Chain will be routed to the TokenJar on that chain. UNI burned on Robinhood Chain is bridged back to Ethereum mainnet and sent to the burn address." - "" - "Robinhood Chain is an Arbitrum Orbit chain, so this proposal reuses the pattern from the Arbitrum One activation in proposal 94." - "" - "**Governance path.** Same as Arbitrum One: cross-chain governance messages are delivered as retryable tickets through Robinhood Chain's Inbox contract on Ethereum and executed on Robinhood Chain by the L2 alias of the governance Timelock. The aliased Timelock already controls both factories and the v4 PoolManager on Robinhood Chain (it is the v2 factory's feeToSetter and the owner of the v3 factory and the PoolManager), so no ownership migration is required." - "" - "**Burn path.** The releaser, ArbitrumOrbitResourceFirepit, is the Arbitrum One firepit generalized for Orbit chains. A searcher pays bridged UNI on Robinhood Chain to claim the TokenJar's accumulated fees, and the contract withdraws that UNI to the burn address on mainnet through the canonical gateway. As on Arbitrum One, the withdrawal finalizes on mainnet after Robinhood Chain's challenge period." - "" - "The TokenJar, Releaser, V3OpenFeeAdapter, V4FeeAdapter, and V4FeePolicy will be deployed ahead of the onchain votes, with ownership verified against the aliased Timelock. This post will be updated with those addresses when they've been deployed and a link to the repo containing the contracts and deployment scripts once it has been merged." - "" - "Implementation details for the v4 fee system are in the [v4 fee activation temp check](https://gov.uniswap.org/t/temp-check-activate-v4-protocol-fees/26162). v2 and v3 protocol fee levels are the same as on all other chains where fees are live, see breakdown [here](https://developers.uniswap.org/docs/protocols/protocol-fee/concepts/fees#fee-split-table)." - "" "## Proposal Spec" "" - "If passed, the Robinhood Fee Activation proposal will execute two calls, each creating a retryable ticket in Robinhood Chain's Inbox on Ethereum." - "" "`RH_INBOX.createRetryableTicket(...) x2`" "" "One call will encode:" "" - "`V2_FACTORY.setFeeTo(TOKEN_JAR)`" "" "The other will encode:" "" - "`V3_FACTORY.setOwner(V3_OPEN_FEE_ADAPTER)`" "" - "Once executed on Robinhood Chain, they set the fee collector of UniswapV2Factory to TokenJar and transfer ownership of UniswapV3Factory to V3OpenFeeAdapter." - "" - "Robinhood's v4 activation will be included in the V4 Fee Activation (Part 1) proposal described below. Matching the spec of the [v4 fee activation temp check](https://gov.uniswap.org/t/temp-check-activate-v4-protocol-fees/26162), and delivered through the same retryable ticket path, it will encode:" - "" "`V4_POOL_MANAGER.setProtocolFeeController(V4_FEE_ADAPTER)`" "" "**RELEVANT ADDRESSES:**" "" - "| Name | Network | Address | Description |" "| :-- | :-- | :-- | :--|" - "| V2_FACTORY | Robinhood Chain | [0x8bcEaA40B9AcdfAedF85AdF4FF01F5Ad6517937f](https://robinhoodchain.blockscout.com/address/0x8bcEaA40B9AcdfAedF85AdF4FF01F5Ad6517937f) | Uniswap V2 Factory |" - "| V3_FACTORY | Robinhood Chain | [0x1f7d7550B1b028f7571E69A784071F0205FD2EfA](https://robinhoodchain.blockscout.com/address/0x1f7d7550B1b028f7571E69A784071F0205FD2EfA) | Uniswap V3 Factory |" - "| V4_POOL_MANAGER | Robinhood Chain | [0x8366a39cc670b4001a1121b8f6a443a643e40951](https://robinhoodchain.blockscout.com/address/0x8366a39cc670b4001a1121b8f6a443a643e40951) | Uniswap V4 Pool Manager |" - "| L2_GATEWAY_ROUTER | Robinhood Chain | [0x1E324B9316138CA9a73F960213621AD1aaf01B89](https://robinhoodchain.blockscout.com/address/0x1E324B9316138CA9a73F960213621AD1aaf01B89) | Canonical L2 Gateway Router |" - "| ALIASED_TIMELOCK | Robinhood Chain | [0x2BAD8182C09F50c8318d769245beA52C32BE46CD](https://robinhoodchain.blockscout.com/address/0x2BAD8182C09F50c8318d769245beA52C32BE46CD) | L2 alias of the governance Timelock |" - "| RH_INBOX | Ethereum | [0x1A07cc4BD17E0118BdB54D70990D2158AbAD7a2D](https://etherscan.io/address/0x1A07cc4BD17E0118BdB54D70990D2158AbAD7a2D) | Robinhood Chain Delayed Inbox |" - "| TOKEN_JAR | Robinhood Chain | TBD | Fee Collector |" - "| V3_OPEN_FEE_ADAPTER | Robinhood Chain | TBD | Uniswap V3 Fee Adapter |" - "| V4_FEE_ADAPTER | Robinhood Chain | TBD | Uniswap V4 Fee Adapter (protocolFeeController) |" - "| V4_FEE_POLICY | Robinhood Chain | TBD | Uniswap V4 Fee Policy |" - "| RELEASER | Robinhood Chain | TBD | ArbitrumOrbitResourceFirepit (searcher burn) |" - "| UNI | Robinhood Chain | TBD| Bridged UNI |" "" "## Onchain Execution" "" - "This Snapshot covers v2, v3, and v4 fees on Robinhood Chain, but onchain execution will be split across three proposals. A [separate Snapshot](https://snapshot.box/#/s:uniswapgovernance.eth/proposal/0x3fda3c04fa106ad45173011ef4dfa5db2f14cd35c3c3a7a40f067a44cbadd5f4) to enable v4 fees on the eleven chains where v2/v3 fees are already live concludes this weekend. The v4 activation is the same call on every chain (set the PoolManager's protocolFeeController to the V4FeeAdapter), so it is faster and safer to batch the v4 activations together and keep Robinhood's v2/v3 calls on their own:" - "" "1. **Robinhood Fee Activation**: the two calls specced above" - "2. **V4 Fee Activation (Part 1)**: Ethereum, Base, Robinhood, BNB Chain, Arbitrum, Optimism, Polygon" - "3. **V4 Fee Activation (Part 2)**: Celo, Soneium, X Layer, World Chain, Zora" "" - "Robinhood Fee Activation and V4 Part 1 will go onchain first, with Part 2 following once a proposer wallet frees up. Robinhood's v4 fees will be enabled through Part 1, contingent on both Snapshots passing." - "" "## Next Steps / Timeline" "" "- **Snapshot begins:** Jul 10, 2026" - "- **Snapshot ends:** Jul 15, 2026" - "*This proposal is part of the protocol fee rollout, following proposals [#93](https://vote.uniswapfoundation.org/proposals/93), [#94](https://vote.uniswapfoundation.org/proposals/94), [#95](https://vote.uniswapfoundation.org/proposals/95), and [#96](https://vote.uniswapfoundation.org/proposals/96). It uses the expedited governance process [approved](https://gov.uniswap.org/t/unification-proposal/25881#p-57882-protocol-fee-rollout-4) in UNIfication, where fee parameter update proposals can bypass the RFC stage and go directly to a five-day Snapshot followed by an onchain vote.*" - "" - "Since protocol fees went live on Ethereum mainnet in late December last year, the rollout has extended to ten additional chains: Arbitrum, Base, OP Mainnet, Worldchain, X Layer, Soneium, Zora, Celo, BNB Chain, and Polygon. The burn system is working as designed, with fees accumulating in TokenJars across chains. From there, searchers claim them in exchange for burning UNI by bridging it back to mainnet and sending it to the burn address." - "" - "Uniswap [launched on Robinhood Chain](https://blog.uniswap.org/robinhood-chain-is-live) at the chain's July 1, 2026 mainnet debut, with v2, v3, and v4 all live. As of July 10, the deployments have crossed [$1b of cumulative swap volume](https://x.com/DefiLlama/status/2075608063969485082?s=20). This proposal:" - "" "- Extends the infrastructure for collecting and burning protocol fees to Robinhood Chain" - "- Enables v2, v3, and v4 protocol fees on Robinhood Chain" "" "## Implementation Details" "" - "Fees on Robinhood Chain will be routed to the TokenJar on that chain. UNI burned on Robinhood Chain is bridged back to Ethereum mainnet and sent to the burn address." - "" - "Robinhood Chain is an Arbitrum Orbit chain, so this proposal reuses the pattern from the Arbitrum One activation in proposal 94." - "" - "**Governance path.** Same as Arbitrum One: cross-chain governance messages are delivered as retryable tickets through Robinhood Chain's Inbox contract on Ethereum and executed on Robinhood Chain by the L2 alias of the governance Timelock. The aliased Timelock already controls both factories and the v4 PoolManager on Robinhood Chain (it is the v2 factory's feeToSetter and the owner of the v3 factory and the PoolManager), so no ownership migration is required." - "" - "**Burn path.** The releaser, ArbitrumOrbitResourceFirepit, is the Arbitrum One firepit generalized for Orbit chains. A searcher pays bridged UNI on Robinhood Chain to claim the TokenJar's accumulated fees, and the contract withdraws that UNI to the burn address on mainnet through the canonical gateway. As on Arbitrum One, the withdrawal finalizes on mainnet after Robinhood Chain's challenge period." - "" - "The TokenJar, Releaser, V3OpenFeeAdapter, V4FeeAdapter, and V4FeePolicy will be deployed ahead of the onchain votes, with ownership verified against the aliased Timelock. This post will be updated with those addresses when they've been deployed and a link to the repo containing the contracts and deployment scripts once it has been merged." - "" - "Implementation details for the v4 fee system are in the [v4 fee activation temp check](https://gov.uniswap.org/t/temp-check-activate-v4-protocol-fees/26162). v2 and v3 protocol fee levels are the same as on all other chains where fees are live, see breakdown [here](https://developers.uniswap.org/docs/protocols/protocol-fee/concepts/fees#fee-split-table)." - "" "## Proposal Spec" "" - "If passed, the Robinhood Fee Activation proposal will execute two calls, each creating a retryable ticket in Robinhood Chain's Inbox on Ethereum." - "" "`RH_INBOX.createRetryableTicket(...) x2`" "" "One call will encode:" "" - "`V2_FACTORY.setFeeTo(TOKEN_JAR)`" "" "The other will encode:" "" - "`V3_FACTORY.setOwner(V3_OPEN_FEE_ADAPTER)`" "" - "Once executed on Robinhood Chain, they set the fee collector of UniswapV2Factory to TokenJar and transfer ownership of UniswapV3Factory to V3OpenFeeAdapter." - "" - "Robinhood's v4 activation will be included in the V4 Fee Activation (Part 1) proposal described below. Matching the spec of the [v4 fee activation temp check](https://gov.uniswap.org/t/temp-check-activate-v4-protocol-fees/26162), and delivered through the same retryable ticket path, it will encode:" - "" "`V4_POOL_MANAGER.setProtocolFeeController(V4_FEE_ADAPTER)`" "[9:24 AM]**RELEVANT ADDRESSES:**" - "" "| Name | Network | Address | Description |" "| :-- | :-- | :-- | :--|" - "| V2_FACTORY | Robinhood Chain | [0x8bcEaA40B9AcdfAedF85AdF4FF01F5Ad6517937f](https://robinhoodchain.blockscout.com/address/0x8bcEaA40B9AcdfAedF85AdF4FF01F5Ad6517937f) | Uniswap V2 Factory |" - "| V3_FACTORY | Robinhood Chain | [0x1f7d7550B1b028f7571E69A784071F0205FD2EfA](https://robinhoodchain.blockscout.com/address/0x1f7d7550B1b028f7571E69A784071F0205FD2EfA) | Uniswap V3 Factory |" - "| V4_POOL_MANAGER | Robinhood Chain | [0x8366a39cc670b4001a1121b8f6a443a643e40951](https://robinhoodchain.blockscout.com/address/0x8366a39cc670b4001a1121b8f6a443a643e40951) | Uniswap V4 Pool Manager |" - "| L2_GATEWAY_ROUTER | Robinhood Chain | [0x1E324B9316138CA9a73F960213621AD1aaf01B89](https://robinhoodchain.blockscout.com/address/0x1E324B9316138CA9a73F960213621AD1aaf01B89) | Canonical L2 Gateway Router |" - "| ALIASED_TIMELOCK | Robinhood Chain | [0x2BAD8182C09F50c8318d769245beA52C32BE46CD](https://robinhoodchain.blockscout.com/address/0x2BAD8182C09F50c8318d769245beA52C32BE46CD) | L2 alias of the governance Timelock |" - "| RH_INBOX | Ethereum | [0x1A07cc4BD17E0118BdB54D70990D2158AbAD7a2D](https://etherscan.io/address/0x1A07cc4BD17E0118BdB54D70990D2158AbAD7a2D) | Robinhood Chain Delayed Inbox |" - "| TOKEN_JAR | Robinhood Chain | TBD | Fee Collector |" - "| V3_OPEN_FEE_ADAPTER | Robinhood Chain | TBD | Uniswap V3 Fee Adapter |" - "| V4_FEE_ADAPTER | Robinhood Chain | TBD | Uniswap V4 Fee Adapter (protocolFeeController) |" - "| V4_FEE_POLICY | Robinhood Chain | TBD | Uniswap V4 Fee Policy |" - "| RELEASER | Robinhood Chain | TBD | ArbitrumOrbitResourceFirepit (searcher burn) |" - "| UNI | Robinhood Chain | TBD| Bridged UNI |" "" "## Onchain Execution" "" - "This Snapshot covers v2, v3, and v4 fees on Robinhood Chain, but onchain execution will be split across three proposals. A [separate Snapshot](https://snapshot.box/#/s:uniswapgovernance.eth/proposal/0x3fda3c04fa106ad45173011ef4dfa5db2f14cd35c3c3a7a40f067a44cbadd5f4) to enable v4 fees on the eleven chains where v2/v3 fees are already live concludes this weekend. The v4 activation is the same call on every chain (set the PoolManager's protocolFeeController to the V4FeeAdapter), so it is faster and safer to batch the v4 activations together and keep Robinhood's v2/v3 calls on their own:" - "" "1. **Robinhood Fee Activation**: the two calls specced above" - "2. **V4 Fee Activation (Part 1)**: Ethereum, Base, Robinhood, BNB Chain, Arbitrum, Optimism, Polygon" - "3. **V4 Fee Activation (Part 2)**: Celo, Soneium, X Layer, World Chain, Zora" "" - "Robinhood Fee Activation and V4 Part 1 will go onchain first, with Part 2 following once a proposer wallet frees up. Robinhood's v4 fees will be enabled through Part 1, contingent on both Snapshots passing." - "" "## Next Steps / Timeline" "" "- **Snapshot begins:** Jul 10, 2026" - "- **Snapshot ends:** Jul 15, 2026" - "- **Onchain votes:** Robinhood Fee Activation and V4 Fee Activation (Part 1) following the Snapshots, per standard governance cadence; V4 Fee Activation (Part 2) to follow"; +string constant DESCRIPTION = "# Protocol Fee Expansion: Robinhood Chain" + "\n" + "\n*This proposal is part of the protocol fee rollout, following proposals [#93](https://vote.uniswapfoundation.org/proposals/93), [#94](https://vote.uniswapfoundation.org/proposals/94), [#95](https://vote.uniswapfoundation.org/proposals/95), and [#96](https://vote.uniswapfoundation.org/proposals/96). It uses the expedited governance process [approved](https://gov.uniswap.org/t/unification-proposal/25881#p-57882-protocol-fee-rollout-4) in UNIfication, where fee parameter update proposals can bypass the RFC stage and go directly to a five-day Snapshot followed by an onchain vote.*" + "\n" + "\nSince protocol fees went live on Ethereum mainnet in late December last year, the rollout has extended to ten additional chains: Arbitrum, Base, OP Mainnet, Worldchain, X Layer, Soneium, Zora, Celo, BNB Chain, and Polygon. The burn system is working as designed, with fees accumulating in TokenJars across chains. From there, searchers claim them in exchange for burning UNI by bridging it back to mainnet and sending it to the burn address." + "\n" + "\nUniswap [launched on Robinhood Chain](https://blog.uniswap.org/robinhood-chain-is-live) at the chain's July 1, 2026 mainnet debut, with v2, v3, and v4 all live. As of July 10, the deployments have crossed [$1b of cumulative swap volume](https://x.com/DefiLlama/status/2075608063969485082?s=20). This proposal:" + "\n" + "\n- Extends the infrastructure for collecting and burning protocol fees to Robinhood Chain" + "\n- Enables v2, v3, and v4 protocol fees on Robinhood Chain" + "\n" + "\n## Implementation Details" + "\n" + "\nFees on Robinhood Chain will be routed to the TokenJar on that chain. UNI burned on Robinhood Chain is bridged back to Ethereum mainnet and sent to the burn address." + "\n" + "\nRobinhood Chain is an Arbitrum Orbit chain, so this proposal reuses the pattern from the Arbitrum One activation in proposal 94." + "\n" + "\n**Governance path.** Same as Arbitrum One: cross-chain governance messages are delivered as retryable tickets through Robinhood Chain's Inbox contract on Ethereum and executed on Robinhood Chain by the L2 alias of the governance Timelock. The aliased Timelock already controls both factories and the v4 PoolManager on Robinhood Chain (it is the v2 factory's feeToSetter and the owner of the v3 factory and the PoolManager), so no ownership migration is required." + "\n" + "\n**Burn path.** The releaser, ArbitrumOrbitResourceFirepit, is the Arbitrum One firepit generalized for Orbit chains. A searcher pays bridged UNI on Robinhood Chain to claim the TokenJar's accumulated fees, and the contract withdraws that UNI to the burn address on mainnet through the canonical gateway. As on Arbitrum One, the withdrawal finalizes on mainnet after Robinhood Chain's challenge period." + "\n" + "\nThe TokenJar, Releaser, V3OpenFeeAdapter, V4FeeAdapter, and V4FeePolicy will be deployed ahead of the onchain votes, with ownership verified against the aliased Timelock. This post will be updated with those addresses when they've been deployed and a link to the repo containing the contracts and deployment scripts once it has been merged." + "\n" + "\nImplementation details for the v4 fee system are in the [v4 fee activation temp check](https://gov.uniswap.org/t/temp-check-activate-v4-protocol-fees/26162). v2 and v3 protocol fee levels are the same as on all other chains where fees are live, see breakdown [here](https://developers.uniswap.org/docs/protocols/protocol-fee/concepts/fees#fee-split-table)." + "\n" + "\n## Proposal Spec" + "\n" + "\nIf passed, the Robinhood Fee Activation proposal will execute two calls, each creating a retryable ticket in Robinhood Chain's Inbox on Ethereum." + "\n" + "\n`RH_INBOX.createRetryableTicket(...) x2`" + "\n" + "\nOne call will encode:" + "\n" + "\n`V2_FACTORY.setFeeTo(TOKEN_JAR)`" + "\n" + "\nThe other will encode:" + "\n" + "\n`V3_FACTORY.setOwner(V3_OPEN_FEE_ADAPTER)`" + "\n" + "\nOnce executed on Robinhood Chain, they set the fee collector of UniswapV2Factory to TokenJar and transfer ownership of UniswapV3Factory to V3OpenFeeAdapter." + "\n" + "\nRobinhood's v4 activation will be included in the V4 Fee Activation (Part 1) proposal described below. Matching the spec of the [v4 fee activation temp check](https://gov.uniswap.org/t/temp-check-activate-v4-protocol-fees/26162), and delivered through the same retryable ticket path, it will encode:" + "\n" + "\n`V4_POOL_MANAGER.setProtocolFeeController(V4_FEE_ADAPTER)`" + "\n" + "\n**RELEVANT ADDRESSES:**" + "\n" + "\n| Name | Network | Address | Description |" + "\n| :-- | :-- | :-- | :--|" + "\n| V2_FACTORY | Robinhood Chain | [0x8bcEaA40B9AcdfAedF85AdF4FF01F5Ad6517937f](https://robinhoodchain.blockscout.com/address/0x8bcEaA40B9AcdfAedF85AdF4FF01F5Ad6517937f) | Uniswap V2 Factory |" + "\n| V3_FACTORY | Robinhood Chain | [0x1f7d7550B1b028f7571E69A784071F0205FD2EfA](https://robinhoodchain.blockscout.com/address/0x1f7d7550B1b028f7571E69A784071F0205FD2EfA) | Uniswap V3 Factory |" + "\n| V4_POOL_MANAGER | Robinhood Chain | [0x8366a39cc670b4001a1121b8f6a443a643e40951](https://robinhoodchain.blockscout.com/address/0x8366a39cc670b4001a1121b8f6a443a643e40951) | Uniswap V4 Pool Manager |" + "\n| L2_GATEWAY_ROUTER | Robinhood Chain | [0x1E324B9316138CA9a73F960213621AD1aaf01B89](https://robinhoodchain.blockscout.com/address/0x1E324B9316138CA9a73F960213621AD1aaf01B89) | Canonical L2 Gateway Router |" + "\n| ALIASED_TIMELOCK | Robinhood Chain | [0x2BAD8182C09F50c8318d769245beA52C32BE46CD](https://robinhoodchain.blockscout.com/address/0x2BAD8182C09F50c8318d769245beA52C32BE46CD) | L2 alias of the governance Timelock |" + "\n| RH_INBOX | Ethereum | [0x1A07cc4BD17E0118BdB54D70990D2158AbAD7a2D](https://etherscan.io/address/0x1A07cc4BD17E0118BdB54D70990D2158AbAD7a2D) | Robinhood Chain Delayed Inbox |" + "\n| TOKEN_JAR | Robinhood Chain | TBD | Fee Collector |" + "\n| V3_OPEN_FEE_ADAPTER | Robinhood Chain | TBD | Uniswap V3 Fee Adapter |" + "\n| V4_FEE_ADAPTER | Robinhood Chain | TBD | Uniswap V4 Fee Adapter (protocolFeeController) |" + "\n| V4_FEE_POLICY | Robinhood Chain | TBD | Uniswap V4 Fee Policy |" + "\n| RELEASER | Robinhood Chain | TBD | ArbitrumOrbitResourceFirepit (searcher burn) |" + "\n| UNI | Robinhood Chain | TBD| Bridged UNI |" + "\n" + "\n## Onchain Execution" + "\n" + "\nThis Snapshot covers v2, v3, and v4 fees on Robinhood Chain, but onchain execution will be split across three proposals. A [separate Snapshot](https://snapshot.box/#/s:uniswapgovernance.eth/proposal/0x3fda3c04fa106ad45173011ef4dfa5db2f14cd35c3c3a7a40f067a44cbadd5f4) to enable v4 fees on the eleven chains where v2/v3 fees are already live concludes this weekend. The v4 activation is the same call on every chain (set the PoolManager's protocolFeeController to the V4FeeAdapter), so it is faster and safer to batch the v4 activations together and keep Robinhood's v2/v3 calls on their own:" + "\n" + "\n1. **Robinhood Fee Activation**: the two calls specced above" + "\n2. **V4 Fee Activation (Part 1)**: Ethereum, Base, Robinhood, BNB Chain, Arbitrum, Optimism, Polygon" + "\n3. **V4 Fee Activation (Part 2)**: Celo, Soneium, X Layer, World Chain, Zora" + "\n" + "\nRobinhood Fee Activation and V4 Part 1 will go onchain first, with Part 2 following once a proposer wallet frees up. Robinhood's v4 fees will be enabled through Part 1, contingent on both Snapshots passing." + "\n" + "\n## Next Steps / Timeline" + "\n" + "\n- **Snapshot begins:** Jul 10, 2026" + "\n- **Snapshot ends:** Jul 15, 2026" + "\n*This proposal is part of the protocol fee rollout, following proposals [#93](https://vote.uniswapfoundation.org/proposals/93), [#94](https://vote.uniswapfoundation.org/proposals/94), [#95](https://vote.uniswapfoundation.org/proposals/95), and [#96](https://vote.uniswapfoundation.org/proposals/96). It uses the expedited governance process [approved](https://gov.uniswap.org/t/unification-proposal/25881#p-57882-protocol-fee-rollout-4) in UNIfication, where fee parameter update proposals can bypass the RFC stage and go directly to a five-day Snapshot followed by an onchain vote.*" + "\n" + "\nSince protocol fees went live on Ethereum mainnet in late December last year, the rollout has extended to ten additional chains: Arbitrum, Base, OP Mainnet, Worldchain, X Layer, Soneium, Zora, Celo, BNB Chain, and Polygon. The burn system is working as designed, with fees accumulating in TokenJars across chains. From there, searchers claim them in exchange for burning UNI by bridging it back to mainnet and sending it to the burn address." + "\n" + "\nUniswap [launched on Robinhood Chain](https://blog.uniswap.org/robinhood-chain-is-live) at the chain's July 1, 2026 mainnet debut, with v2, v3, and v4 all live. As of July 10, the deployments have crossed [$1b of cumulative swap volume](https://x.com/DefiLlama/status/2075608063969485082?s=20). This proposal:" + "\n" + "\n- Extends the infrastructure for collecting and burning protocol fees to Robinhood Chain" + "\n- Enables v2, v3, and v4 protocol fees on Robinhood Chain" + "\n" + "\n## Implementation Details" + "\n" + "\nFees on Robinhood Chain will be routed to the TokenJar on that chain. UNI burned on Robinhood Chain is bridged back to Ethereum mainnet and sent to the burn address." + "\n" + "\nRobinhood Chain is an Arbitrum Orbit chain, so this proposal reuses the pattern from the Arbitrum One activation in proposal 94." + "\n" + "\n**Governance path.** Same as Arbitrum One: cross-chain governance messages are delivered as retryable tickets through Robinhood Chain's Inbox contract on Ethereum and executed on Robinhood Chain by the L2 alias of the governance Timelock. The aliased Timelock already controls both factories and the v4 PoolManager on Robinhood Chain (it is the v2 factory's feeToSetter and the owner of the v3 factory and the PoolManager), so no ownership migration is required." + "\n" + "\n**Burn path.** The releaser, ArbitrumOrbitResourceFirepit, is the Arbitrum One firepit generalized for Orbit chains. A searcher pays bridged UNI on Robinhood Chain to claim the TokenJar's accumulated fees, and the contract withdraws that UNI to the burn address on mainnet through the canonical gateway. As on Arbitrum One, the withdrawal finalizes on mainnet after Robinhood Chain's challenge period." + "\n" + "\nThe TokenJar, Releaser, V3OpenFeeAdapter, V4FeeAdapter, and V4FeePolicy will be deployed ahead of the onchain votes, with ownership verified against the aliased Timelock. This post will be updated with those addresses when they've been deployed and a link to the repo containing the contracts and deployment scripts once it has been merged." + "\n" + "\nImplementation details for the v4 fee system are in the [v4 fee activation temp check](https://gov.uniswap.org/t/temp-check-activate-v4-protocol-fees/26162). v2 and v3 protocol fee levels are the same as on all other chains where fees are live, see breakdown [here](https://developers.uniswap.org/docs/protocols/protocol-fee/concepts/fees#fee-split-table)." + "\n" + "\n## Proposal Spec" + "\n" + "\nIf passed, the Robinhood Fee Activation proposal will execute two calls, each creating a retryable ticket in Robinhood Chain's Inbox on Ethereum." + "\n" + "\n`RH_INBOX.createRetryableTicket(...) x2`" + "\n" + "\nOne call will encode:" + "\n" + "\n`V2_FACTORY.setFeeTo(TOKEN_JAR)`" + "\n" + "\nThe other will encode:" + "\n" + "\n`V3_FACTORY.setOwner(V3_OPEN_FEE_ADAPTER)`" + "\n" + "\nOnce executed on Robinhood Chain, they set the fee collector of UniswapV2Factory to TokenJar and transfer ownership of UniswapV3Factory to V3OpenFeeAdapter." + "\n" + "\nRobinhood's v4 activation will be included in the V4 Fee Activation (Part 1) proposal described below. Matching the spec of the [v4 fee activation temp check](https://gov.uniswap.org/t/temp-check-activate-v4-protocol-fees/26162), and delivered through the same retryable ticket path, it will encode:" + "\n" + "\n`V4_POOL_MANAGER.setProtocolFeeController(V4_FEE_ADAPTER)`" + "\n[9:24 AM]**RELEVANT ADDRESSES:**" + "\n" + "\n| Name | Network | Address | Description |" + "\n| :-- | :-- | :-- | :--|" + "\n| V2_FACTORY | Robinhood Chain | [0x8bcEaA40B9AcdfAedF85AdF4FF01F5Ad6517937f](https://robinhoodchain.blockscout.com/address/0x8bcEaA40B9AcdfAedF85AdF4FF01F5Ad6517937f) | Uniswap V2 Factory |" + "\n| V3_FACTORY | Robinhood Chain | [0x1f7d7550B1b028f7571E69A784071F0205FD2EfA](https://robinhoodchain.blockscout.com/address/0x1f7d7550B1b028f7571E69A784071F0205FD2EfA) | Uniswap V3 Factory |" + "\n| V4_POOL_MANAGER | Robinhood Chain | [0x8366a39cc670b4001a1121b8f6a443a643e40951](https://robinhoodchain.blockscout.com/address/0x8366a39cc670b4001a1121b8f6a443a643e40951) | Uniswap V4 Pool Manager |" + "\n| L2_GATEWAY_ROUTER | Robinhood Chain | [0x1E324B9316138CA9a73F960213621AD1aaf01B89](https://robinhoodchain.blockscout.com/address/0x1E324B9316138CA9a73F960213621AD1aaf01B89) | Canonical L2 Gateway Router |" + "\n| ALIASED_TIMELOCK | Robinhood Chain | [0x2BAD8182C09F50c8318d769245beA52C32BE46CD](https://robinhoodchain.blockscout.com/address/0x2BAD8182C09F50c8318d769245beA52C32BE46CD) | L2 alias of the governance Timelock |" + "\n| RH_INBOX | Ethereum | [0x1A07cc4BD17E0118BdB54D70990D2158AbAD7a2D](https://etherscan.io/address/0x1A07cc4BD17E0118BdB54D70990D2158AbAD7a2D) | Robinhood Chain Delayed Inbox |" + "\n| TOKEN_JAR | Robinhood Chain | TBD | Fee Collector |" + "\n| V3_OPEN_FEE_ADAPTER | Robinhood Chain | TBD | Uniswap V3 Fee Adapter |" + "\n| V4_FEE_ADAPTER | Robinhood Chain | TBD | Uniswap V4 Fee Adapter (protocolFeeController) |" + "\n| V4_FEE_POLICY | Robinhood Chain | TBD | Uniswap V4 Fee Policy |" + "\n| RELEASER | Robinhood Chain | TBD | ArbitrumOrbitResourceFirepit (searcher burn) |" + "\n| UNI | Robinhood Chain | TBD| Bridged UNI |" + "\n" + "\n## Onchain Execution" + "\n" + "\nThis Snapshot covers v2, v3, and v4 fees on Robinhood Chain, but onchain execution will be split across three proposals. A [separate Snapshot](https://snapshot.box/#/s:uniswapgovernance.eth/proposal/0x3fda3c04fa106ad45173011ef4dfa5db2f14cd35c3c3a7a40f067a44cbadd5f4) to enable v4 fees on the eleven chains where v2/v3 fees are already live concludes this weekend. The v4 activation is the same call on every chain (set the PoolManager's protocolFeeController to the V4FeeAdapter), so it is faster and safer to batch the v4 activations together and keep Robinhood's v2/v3 calls on their own:" + "\n" + "\n1. **Robinhood Fee Activation**: the two calls specced above" + "\n2. **V4 Fee Activation (Part 1)**: Ethereum, Base, Robinhood, BNB Chain, Arbitrum, Optimism, Polygon" + "\n3. **V4 Fee Activation (Part 2)**: Celo, Soneium, X Layer, World Chain, Zora" + "\n" + "\nRobinhood Fee Activation and V4 Part 1 will go onchain first, with Part 2 following once a proposer wallet frees up. Robinhood's v4 fees will be enabled through Part 1, contingent on both Snapshots passing." + "\n" + "\n## Next Steps / Timeline" + "\n" + "\n- **Snapshot begins:** Jul 10, 2026" + "\n- **Snapshot ends:** Jul 15, 2026" + "\n- **Onchain votes:** Robinhood Fee Activation and V4 Fee Activation (Part 1) following the Snapshots, per standard governance cadence; V4 Fee Activation (Part 2) to follow"; diff --git a/script/proposal-6/Description.sol b/script/proposal-6/Description.sol index 45ef48a6..2e674d54 100644 --- a/script/proposal-6/Description.sol +++ b/script/proposal-6/Description.sol @@ -1,59 +1,107 @@ // SPDX-License-Identifier: MIT pragma solidity 0.8.29; -string constant DESCRIPTION = "## Summary" "" - "This proposal continues the protocol fee rollout approved in UNIfication, following proposals [#93](https://vote.uniswapfoundation.org/proposals/93), [#94](https://vote.uniswapfoundation.org/proposals/94), [#95](https://vote.uniswapfoundation.org/proposals/95), and [#96](https://vote.uniswapfoundation.org/proposals/96). It uses the expedited governance process where fee parameter update proposals go directly to a five-day Snapshot followed by an onchain vote." - "" - "Protocol fees are now live across all v2 and v3 pools on 11 chains - Ethereum, Arbitrum, Base, Celo, OP Mainnet, Soneium, X Layer, Worldchain, Zora, BNB Chain, and Polygon. Last month, the protocol set a record burning [186,000 UNI in one day](https://x.com/UNIBurnBot/status/2063252718043459633). " - "" - "Below we introduce a system for v4 protocol fees and propose to activate it on a subset of v4 pools on these same chains. This onchain vote will specifically activate v4 fees on Ethereum, Arbitrum, Base, BNB Chain, Celo, Optimism, and Robinhood Chain. " - "" "---" "" "## Implementation Details" "" - "v4's hook architecture requires a different approach to fee activation than v2 or v3. v2 pools have a single LP fee tier and are charged a static fee. v3 has several LP fee tiers, each charged a static fee. Hooks mean v4 has potentially infinite distinct LP fee tiers, and a pool's fees can change dynamically from one block to the next. To manage this, we propose a V4 Fee Controller system where governance sets rules that let a dedicated contract compute the fee for any pool on demand, rather than setting a fee on each individual pool." - "" "The system splits across two contracts:" "" - "* **V4FeePolicy**. Given any pool, it computes the fee from rules defined by governance. This is the contract governance calls to enable and adjust the protocol fee, and it can be swapped out later if the logic for setting fees needs to evolve." - "" - "* **V4FeeAdapter**. Enforces governance overrides, so if governance has set a per-pool override, that override wins and the policy is skipped. Otherwise the adapter applies the policy's fee, pushes it to the pool, and collects the proceeds to the TokenJar." - "" - "`V4FeePolicy` determines a pool's fee in two steps. First it sorts the pool into a **family**. A pool's family is determined by its characteristics, e.g. whether it has a hook, whether it uses the `PoolManager`'s native swap math, whether it charges dynamic swap fees, et cetera. A pool's family is identified by a flag, which is stored on the hook smart contract. Hook developers can opt their pools into a family via assigning it a specific flag. Governance can also assign a hook to a family directly via a vote. " - "" - "Once it determines the pool's family, the policy then resolves the fee, applying the rules below, going in order from most specific to least:" - "" "1. a per-pair fee, if governance has set one for that token pair in that family" - "2. otherwise the family's own fee (defined by governance as a default or curve)" - "3. otherwise a global default for anything still unclassified" "" - "With this system, governance manages a handful of rules and overrides instead of an unbounded list of pools, any fee is computed deterministically and can be inspected onchain, and the policy itself is replaceable if governance later wants to change how pools are categorized." - "" "This proposal activates fees on three pool families:" "" - "* **Static fee pools:** These are pools without hooks. The protocol fee for these pools is set via a curve targeting a proportion of each pool's LP fee. For a description of this curve, please see the appendix below." - "* **CCA Pools**: These are pools launched after a Continuous Clearing Auction. The LBPHook and pools resulting from previous auctions will be opted into the same curve as static pools." - "* **Aggregator hook pools:** These are pools whose hooks integrate external liquidity venues into the v4 routing graph. The protocol fee for the aggregator hook family is a flat fee with overrides for specific pair types. To maintain the option of charging more on this external flow than the v4 PoolManager's hard cap of 10bps, aggregator hooks will multiply their assigned fees by 25, allowing for a cap of 250bps. After the multiplier is applied, the resulting fee for aggregator hooks will be:" - " * For all chains other than Base:" " * Family Default: 10bp" - " * Select Stable Pairs: 3bp" " * For Base: " " * Family Default: 3bp" - " * Select Stable Pairs: 1bp" - "**This proposal does not enable the protocol fee for any pools other than those in the Families mentioned above.**" - "" - "Fees will flow to TokenJar on each chain. UNI burned on L2s and alt-L1s will be bridged back to Ethereum mainnet and sent to `0xdead`." - "" "---" "" "## Onchain Proposal Spec" "" - "**Pre-proposal** (to be completed by Uniswap Labs prior to an onchain vote)" "" - "* Deploy V4FeeAdapter and V4FeePolicy contracts on all chains where the v2 and v3 protocol fees are currently enabled (Ethereum, Arbitrum, Base, Celo, OP Mainnet, Soneium, X Layer, Worldchain, Zora, BNB Chain, and Polygon)." - "* Configure V4FeePolicy contracts with the native math protocol fee curve, CCA Hook and aggregator hook family fee logic described above" - "" - "These contracts can be found [here](https://github.com/Uniswap/protocol-fees/tree/main/src/feeAdapters), and this post will be updated with addresses and explorer links when they have been deployed." - "" "**In this proposal** (executed if the vote passes):" "" - "* Set the `V4FeeAdapter` as the `ProtocolFeeController` on the PoolManager on each chain" "" - "---" "" "## Next Steps / Timeline" "" "**Snapshot:** July 7-12, 2026" "" - "**Onchain vote:** Starting the week of July 13, 2026" "" - "Please note that because of GovernorBravo's limit of 10 actions per proposal, there will be two separate onchain votes posted in parallel to accommodate all chains." - "" "---" "" "## Appendix - Static Fee Curve" "" - "The V4 Fee Controller allows fee setting using discrete LP fee tier ranges. Each range has a floor, and the next range sets the ceiling. For each range, governance sets two inputs:" - "" "* `alpha` which is the constant. This is the starting fee for that range." - "* `beta` which is the scaling factor. This is how fast the fee grows within that range. The growth always starts from the floor of the range." - "* Inside any range, the fee is: `alpha + beta * (lpFee - floor)`.The output is floored to the nearest 0.01bp increment, consistent with v4's minimum fee resolution." - "" "| *Range (bps)* | *Floor* | *Alpha (bps)* | *Beta (bps)* |" - "| :---- | :---- | :---- | :---- |" "| *0 \\- 0.03* | *0* | *0.01* | *0* |" - "| *0.03 \\- 0.75* | *0.03* | *0.01* | *19/72* |" "| *0.75 \\- 1* | *0.75* | *0.2* | *0.2* |" - "| *1 \\- 3.75* | *1* | *0.25* | *3/11* |" "| *3.75 \\- 5* | *3.75* | *1* | *0.2* |" - "| *5 \\- 25* | *5* | *1.25* | *11/80* |" "| *25 \\- 55* | *25* | *4* | *0.2* |" - "| *\\> 55* | *55* | *10* | *0* |" "" - "This results in the following fees at the following points. " "" - "| LP Fee (bps) | Protocol Fee (bps) |" "| :---- | :---- |" "| 0.03 | 0.01 |" "| .75 | 0.20 |" - "| 1 | 0.25 |" "| 3.75 | 1 |" "| 5 | 1.25 |" "| 25 | 4 |" "| 30 | 5 |" "| 83.34 | 10 |" - "| 100 | 10 |"; +string constant DESCRIPTION = "\n## Summary" + "\n" + "\nThis proposal continues the protocol fee rollout approved in UNIfication, following proposals [#93](https://vote.uniswapfoundation.org/proposals/93), [#94](https://vote.uniswapfoundation.org/proposals/94), [#95](https://vote.uniswapfoundation.org/proposals/95), and [#96](https://vote.uniswapfoundation.org/proposals/96). It uses the expedited governance process where fee parameter update proposals go directly to a five-day Snapshot followed by an onchain vote." + "\n" + "\nProtocol fees are now live across all v2 and v3 pools on 11 chains - Ethereum, Arbitrum, Base, Celo, OP Mainnet, Soneium, X Layer, Worldchain, Zora, BNB Chain, and Polygon. Last month, the protocol set a record burning [186,000 UNI in one day](https://x.com/UNIBurnBot/status/2063252718043459633). " + "\n" + "\nBelow we introduce a system for v4 protocol fees and propose to activate it on a subset of v4 pools on these same chains. This onchain vote will specifically activate v4 fees on Ethereum, Arbitrum, Base, BNB Chain, Celo, Optimism, and Robinhood Chain. " + "\n" + "\n---" + "\n" + "\n## Implementation Details" + "\n" + "\nv4's hook architecture requires a different approach to fee activation than v2 or v3. v2 pools have a single LP fee tier and are charged a static fee. v3 has several LP fee tiers, each charged a static fee. Hooks mean v4 has potentially infinite distinct LP fee tiers, and a pool's fees can change dynamically from one block to the next. To manage this, we propose a V4 Fee Controller system where governance sets rules that let a dedicated contract compute the fee for any pool on demand, rather than setting a fee on each individual pool." + "\n" + "\nThe system splits across two contracts:" + "\n" + "\n* **V4FeePolicy**. Given any pool, it computes the fee from rules defined by governance. This is the contract governance calls to enable and adjust the protocol fee, and it can be swapped out later if the logic for setting fees needs to evolve." + "\n" + "\n* **V4FeeAdapter**. Enforces governance overrides, so if governance has set a per-pool override, that override wins and the policy is skipped. Otherwise the adapter applies the policy's fee, pushes it to the pool, and collects the proceeds to the TokenJar." + "\n" + "\n`V4FeePolicy` determines a pool's fee in two steps. First it sorts the pool into a **family**. A pool's family is determined by its characteristics, e.g. whether it has a hook, whether it uses the `PoolManager`'s native swap math, whether it charges dynamic swap fees, et cetera. A pool's family is identified by a flag, which is stored on the hook smart contract. Hook developers can opt their pools into a family via assigning it a specific flag. Governance can also assign a hook to a family directly via a vote. " + "\n" + "\nOnce it determines the pool's family, the policy then resolves the fee, applying the rules below, going in order from most specific to least:" + "\n" + "\n1. a per-pair fee, if governance has set one for that token pair in that family" + "\n2. otherwise the family's own fee (defined by governance as a default or curve)" + "\n3. otherwise a global default for anything still unclassified" + "\n" + "\nWith this system, governance manages a handful of rules and overrides instead of an unbounded list of pools, any fee is computed deterministically and can be inspected onchain, and the policy itself is replaceable if governance later wants to change how pools are categorized." + "\n" + "\nThis proposal activates fees on three pool families:" + "\n" + "\n* **Static fee pools:** These are pools without hooks. The protocol fee for these pools is set via a curve targeting a proportion of each pool's LP fee. For a description of this curve, please see the appendix below." + "\n* **CCA Pools**: These are pools launched after a Continuous Clearing Auction. The LBPHook and pools resulting from previous auctions will be opted into the same curve as static pools." + "\n* **Aggregator hook pools:** These are pools whose hooks integrate external liquidity venues into the v4 routing graph. The protocol fee for the aggregator hook family is a flat fee with overrides for specific pair types. To maintain the option of charging more on this external flow than the v4 PoolManager's hard cap of 10bps, aggregator hooks will multiply their assigned fees by 25, allowing for a cap of 250bps. After the multiplier is applied, the resulting fee for aggregator hooks will be:" + "\n * For all chains other than Base:" + "\n * Family Default: 10bp" + "\n * Select Stable Pairs: 3bp" + "\n * For Base: " + "\n * Family Default: 3bp" + "\n * Select Stable Pairs: 1bp" + "\n**This proposal does not enable the protocol fee for any pools other than those in the Families mentioned above.**" + "\n" + "\nFees will flow to TokenJar on each chain. UNI burned on L2s and alt-L1s will be bridged back to Ethereum mainnet and sent to `0xdead`." + "\n" + "\n---" + "\n" + "\n## Onchain Proposal Spec" + "\n" + "\n**Pre-proposal** (to be completed by Uniswap Labs prior to an onchain vote)" + "\n" + "\n* Deploy V4FeeAdapter and V4FeePolicy contracts on all chains where the v2 and v3 protocol fees are currently enabled (Ethereum, Arbitrum, Base, Celo, OP Mainnet, Soneium, X Layer, Worldchain, Zora, BNB Chain, and Polygon)." + "\n* Configure V4FeePolicy contracts with the native math protocol fee curve, CCA Hook and aggregator hook family fee logic described above" + "\n" + "\nThese contracts can be found [here](https://github.com/Uniswap/protocol-fees/tree/main/src/feeAdapters), and this post will be updated with addresses and explorer links when they have been deployed." + "\n" + "\n**In this proposal** (executed if the vote passes):" + "\n" + "\n* Set the `V4FeeAdapter` as the `ProtocolFeeController` on the PoolManager on each chain" + "\n" + "\n---" + "\n" + "\n## Next Steps / Timeline" + "\n" + "\n**Snapshot:** July 7-12, 2026" + "\n" + "\n**Onchain vote:** Starting the week of July 13, 2026" + "\n" + "\nPlease note that because of GovernorBravo's limit of 10 actions per proposal, there will be two separate onchain votes posted in parallel to accommodate all chains." + "\n" + "\n---" + "\n" + "\n## Appendix - Static Fee Curve" + "\n" + "\nThe V4 Fee Controller allows fee setting using discrete LP fee tier ranges. Each range has a floor, and the next range sets the ceiling. For each range, governance sets two inputs:" + "\n" + "\n* `alpha` which is the constant. This is the starting fee for that range." + "\n* `beta` which is the scaling factor. This is how fast the fee grows within that range. The growth always starts from the floor of the range." + "\n* Inside any range, the fee is: `alpha + beta * (lpFee - floor)`.The output is floored to the nearest 0.01bp increment, consistent with v4's minimum fee resolution." + "\n" + "\n| *Range (bps)* | *Floor* | *Alpha (bps)* | *Beta (bps)* |" + "\n| :---- | :---- | :---- | :---- |" + "\n| *0 \\- 0.03* | *0* | *0.01* | *0* |" + "\n| *0.03 \\- 0.75* | *0.03* | *0.01* | *19/72* |" + "\n| *0.75 \\- 1* | *0.75* | *0.2* | *0.2* |" + "\n| *1 \\- 3.75* | *1* | *0.25* | *3/11* |" + "\n| *3.75 \\- 5* | *3.75* | *1* | *0.2* |" + "\n| *5 \\- 25* | *5* | *1.25* | *11/80* |" + "\n| *25 \\- 55* | *25* | *4* | *0.2* |" + "\n| *\\> 55* | *55* | *10* | *0* |" + "\n" + "\nThis results in the following fees at the following points. " + "\n" + "\n| LP Fee (bps) | Protocol Fee (bps) |" + "\n| :---- | :---- |" + "\n| 0.03 | 0.01 |" + "\n| .75 | 0.20 |" + "\n| 1 | 0.25 |" + "\n| 3.75 | 1 |" + "\n| 5 | 1.25 |" + "\n| 25 | 4 |" + "\n| 30 | 5 |" + "\n| 83.34 | 10 |" + "\n| 100 | 10 |"; From b280779d121f81e62040bd798effc7e88a5f4c79 Mon Sep 17 00:00:00 2001 From: jtriley Date: Thu, 16 Jul 2026 17:59:21 -0700 Subject: [PATCH 38/55] re-add token jar --- script/proposal-5/Constants.sol | 1 + 1 file changed, 1 insertion(+) diff --git a/script/proposal-5/Constants.sol b/script/proposal-5/Constants.sol index 964a1d35..abeeeca1 100644 --- a/script/proposal-5/Constants.sol +++ b/script/proposal-5/Constants.sol @@ -14,4 +14,5 @@ library Robinhood { address constant POOL_MANAGER = 0x8366a39CC670B4001A1121B8F6A443A643e40951; address constant UNI = 0xF177d86a28b520e3E396E4F3B96cd8e72D7dabd8; address constant L2_GATEWAY_ROUTER = 0x1E324B9316138CA9a73F960213621AD1aaf01B89; + address constant TOKEN_JAR = address(0x00); } From afe39da695c5cc6d81f3a77935dbe5f8bdd99508 Mon Sep 17 00:00:00 2001 From: jtriley Date: Thu, 16 Jul 2026 18:11:29 -0700 Subject: [PATCH 39/55] nits --- script/proposal-5/Description.sol | 2 +- script/proposal-6/V4FeesProposal.s.sol | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/script/proposal-5/Description.sol b/script/proposal-5/Description.sol index c9dcd942..3257eada 100644 --- a/script/proposal-5/Description.sol +++ b/script/proposal-5/Description.sol @@ -119,7 +119,7 @@ string constant DESCRIPTION = "# Protocol Fee Expansion: Robinhood Chain" "\nRobinhood's v4 activation will be included in the V4 Fee Activation (Part 1) proposal described below. Matching the spec of the [v4 fee activation temp check](https://gov.uniswap.org/t/temp-check-activate-v4-protocol-fees/26162), and delivered through the same retryable ticket path, it will encode:" "\n" "\n`V4_POOL_MANAGER.setProtocolFeeController(V4_FEE_ADAPTER)`" - "\n[9:24 AM]**RELEVANT ADDRESSES:**" + "\n**RELEVANT ADDRESSES:**" "\n" "\n| Name | Network | Address | Description |" "\n| :-- | :-- | :-- | :--|" diff --git a/script/proposal-6/V4FeesProposal.s.sol b/script/proposal-6/V4FeesProposal.s.sol index c8011aba..5fbd1e23 100644 --- a/script/proposal-6/V4FeesProposal.s.sol +++ b/script/proposal-6/V4FeesProposal.s.sol @@ -21,7 +21,7 @@ import "../proposal-5/Constants.sol" as Constants; import {DESCRIPTION} from "./Description.sol"; -contract V4FeeActivationProposal is Script { +contract V4FeeProposal is Script { Recorder internal recorder; Uniswap internal uniswap; @@ -138,7 +138,7 @@ contract V4FeeActivationProposal is Script { }); Proposal memory v4FeeActivationProposalPartOne = Proposal({ - description: string.concat("# Activate v4 Protocol Fees (Part 2/2) \n\n", DESCRIPTION), + description: string.concat("# Activate v4 Protocol Fees (Part 1/2) \n\n", DESCRIPTION), calls: LibCall.newCalls( [ activateV4FeesEthereum, From 239ae9334d102a5607f01cd98fe4d4c96638e750 Mon Sep 17 00:00:00 2001 From: jtriley Date: Thu, 16 Jul 2026 18:30:46 -0700 Subject: [PATCH 40/55] update action indices in comments --- script/proposal-6/V4FeesProposal.s.sol | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/script/proposal-6/V4FeesProposal.s.sol b/script/proposal-6/V4FeesProposal.s.sol index 5fbd1e23..b259ac95 100644 --- a/script/proposal-6/V4FeesProposal.s.sol +++ b/script/proposal-6/V4FeesProposal.s.sol @@ -161,7 +161,7 @@ contract V4FeeProposal is Script { } { // --------------------------------------------------------------------------------------------- - // 06: Activate V4 Fees for Polygon. + // 00: Activate V4 Fees for Polygon. // Call memory activateV4FeesPolygon = FxRootEncoder.encode({ fxRoot: uniswap.ethereum.bridge.polygon, @@ -179,7 +179,7 @@ contract V4FeeProposal is Script { }); // --------------------------------------------------------------------------------------------- - // 07: Activate V4 Fees for Soneium. + // 01: Activate V4 Fees for Soneium. // Call memory activateV4FeesSoneium = L1CrossDomainMessengerEncoder.encode({ l1CrossDomainMessenger: uniswap.ethereum.bridge.soneium, @@ -194,7 +194,7 @@ contract V4FeeProposal is Script { }); // --------------------------------------------------------------------------------------------- - // 08: Activate V4 Fees for Worldchain. + // 02: Activate V4 Fees for Worldchain. // Call memory activateV4FeesWorldchain = OptimismPortal2Encoder.encode({ portal: IL1CrossDomainMessenger(uniswap.ethereum.bridge.worldChain).portal(), @@ -209,7 +209,7 @@ contract V4FeeProposal is Script { }); // --------------------------------------------------------------------------------------------- - // 09: Activate V4 Fees for X Layer + // 03: Activate V4 Fees for X Layer // Call memory activateV4FeesXLayer = OptimismPortal2Encoder.encode({ portal: IL1CrossDomainMessenger(uniswap.ethereum.bridge.xLayer).portal(), @@ -223,7 +223,7 @@ contract V4FeeProposal is Script { }); // --------------------------------------------------------------------------------------------- - // 10: Activate V4 Fees for Zora. + // 04: Activate V4 Fees for Zora. // Call memory activateV4FeesZora = L1CrossDomainMessengerEncoder.encode({ l1CrossDomainMessenger: uniswap.ethereum.bridge.zora, From 95c84f05c6aece63552aec1273f73ed3b77e3674 Mon Sep 17 00:00:00 2001 From: jtriley Date: Thu, 16 Jul 2026 18:55:43 -0700 Subject: [PATCH 41/55] rm descriptions for now --- script/proposal-5/Description.sol | 151 +----------------------------- script/proposal-6/Description.sol | 105 +-------------------- 2 files changed, 2 insertions(+), 254 deletions(-) diff --git a/script/proposal-5/Description.sol b/script/proposal-5/Description.sol index 3257eada..fb2280e5 100644 --- a/script/proposal-5/Description.sol +++ b/script/proposal-5/Description.sol @@ -1,153 +1,4 @@ // SPDX-License-Identifier: MIT pragma solidity 0.8.29; -string constant DESCRIPTION = "# Protocol Fee Expansion: Robinhood Chain" - "\n" - "\n*This proposal is part of the protocol fee rollout, following proposals [#93](https://vote.uniswapfoundation.org/proposals/93), [#94](https://vote.uniswapfoundation.org/proposals/94), [#95](https://vote.uniswapfoundation.org/proposals/95), and [#96](https://vote.uniswapfoundation.org/proposals/96). It uses the expedited governance process [approved](https://gov.uniswap.org/t/unification-proposal/25881#p-57882-protocol-fee-rollout-4) in UNIfication, where fee parameter update proposals can bypass the RFC stage and go directly to a five-day Snapshot followed by an onchain vote.*" - "\n" - "\nSince protocol fees went live on Ethereum mainnet in late December last year, the rollout has extended to ten additional chains: Arbitrum, Base, OP Mainnet, Worldchain, X Layer, Soneium, Zora, Celo, BNB Chain, and Polygon. The burn system is working as designed, with fees accumulating in TokenJars across chains. From there, searchers claim them in exchange for burning UNI by bridging it back to mainnet and sending it to the burn address." - "\n" - "\nUniswap [launched on Robinhood Chain](https://blog.uniswap.org/robinhood-chain-is-live) at the chain's July 1, 2026 mainnet debut, with v2, v3, and v4 all live. As of July 10, the deployments have crossed [$1b of cumulative swap volume](https://x.com/DefiLlama/status/2075608063969485082?s=20). This proposal:" - "\n" - "\n- Extends the infrastructure for collecting and burning protocol fees to Robinhood Chain" - "\n- Enables v2, v3, and v4 protocol fees on Robinhood Chain" - "\n" - "\n## Implementation Details" - "\n" - "\nFees on Robinhood Chain will be routed to the TokenJar on that chain. UNI burned on Robinhood Chain is bridged back to Ethereum mainnet and sent to the burn address." - "\n" - "\nRobinhood Chain is an Arbitrum Orbit chain, so this proposal reuses the pattern from the Arbitrum One activation in proposal 94." - "\n" - "\n**Governance path.** Same as Arbitrum One: cross-chain governance messages are delivered as retryable tickets through Robinhood Chain's Inbox contract on Ethereum and executed on Robinhood Chain by the L2 alias of the governance Timelock. The aliased Timelock already controls both factories and the v4 PoolManager on Robinhood Chain (it is the v2 factory's feeToSetter and the owner of the v3 factory and the PoolManager), so no ownership migration is required." - "\n" - "\n**Burn path.** The releaser, ArbitrumOrbitResourceFirepit, is the Arbitrum One firepit generalized for Orbit chains. A searcher pays bridged UNI on Robinhood Chain to claim the TokenJar's accumulated fees, and the contract withdraws that UNI to the burn address on mainnet through the canonical gateway. As on Arbitrum One, the withdrawal finalizes on mainnet after Robinhood Chain's challenge period." - "\n" - "\nThe TokenJar, Releaser, V3OpenFeeAdapter, V4FeeAdapter, and V4FeePolicy will be deployed ahead of the onchain votes, with ownership verified against the aliased Timelock. This post will be updated with those addresses when they've been deployed and a link to the repo containing the contracts and deployment scripts once it has been merged." - "\n" - "\nImplementation details for the v4 fee system are in the [v4 fee activation temp check](https://gov.uniswap.org/t/temp-check-activate-v4-protocol-fees/26162). v2 and v3 protocol fee levels are the same as on all other chains where fees are live, see breakdown [here](https://developers.uniswap.org/docs/protocols/protocol-fee/concepts/fees#fee-split-table)." - "\n" - "\n## Proposal Spec" - "\n" - "\nIf passed, the Robinhood Fee Activation proposal will execute two calls, each creating a retryable ticket in Robinhood Chain's Inbox on Ethereum." - "\n" - "\n`RH_INBOX.createRetryableTicket(...) x2`" - "\n" - "\nOne call will encode:" - "\n" - "\n`V2_FACTORY.setFeeTo(TOKEN_JAR)`" - "\n" - "\nThe other will encode:" - "\n" - "\n`V3_FACTORY.setOwner(V3_OPEN_FEE_ADAPTER)`" - "\n" - "\nOnce executed on Robinhood Chain, they set the fee collector of UniswapV2Factory to TokenJar and transfer ownership of UniswapV3Factory to V3OpenFeeAdapter." - "\n" - "\nRobinhood's v4 activation will be included in the V4 Fee Activation (Part 1) proposal described below. Matching the spec of the [v4 fee activation temp check](https://gov.uniswap.org/t/temp-check-activate-v4-protocol-fees/26162), and delivered through the same retryable ticket path, it will encode:" - "\n" - "\n`V4_POOL_MANAGER.setProtocolFeeController(V4_FEE_ADAPTER)`" - "\n" - "\n**RELEVANT ADDRESSES:**" - "\n" - "\n| Name | Network | Address | Description |" - "\n| :-- | :-- | :-- | :--|" - "\n| V2_FACTORY | Robinhood Chain | [0x8bcEaA40B9AcdfAedF85AdF4FF01F5Ad6517937f](https://robinhoodchain.blockscout.com/address/0x8bcEaA40B9AcdfAedF85AdF4FF01F5Ad6517937f) | Uniswap V2 Factory |" - "\n| V3_FACTORY | Robinhood Chain | [0x1f7d7550B1b028f7571E69A784071F0205FD2EfA](https://robinhoodchain.blockscout.com/address/0x1f7d7550B1b028f7571E69A784071F0205FD2EfA) | Uniswap V3 Factory |" - "\n| V4_POOL_MANAGER | Robinhood Chain | [0x8366a39cc670b4001a1121b8f6a443a643e40951](https://robinhoodchain.blockscout.com/address/0x8366a39cc670b4001a1121b8f6a443a643e40951) | Uniswap V4 Pool Manager |" - "\n| L2_GATEWAY_ROUTER | Robinhood Chain | [0x1E324B9316138CA9a73F960213621AD1aaf01B89](https://robinhoodchain.blockscout.com/address/0x1E324B9316138CA9a73F960213621AD1aaf01B89) | Canonical L2 Gateway Router |" - "\n| ALIASED_TIMELOCK | Robinhood Chain | [0x2BAD8182C09F50c8318d769245beA52C32BE46CD](https://robinhoodchain.blockscout.com/address/0x2BAD8182C09F50c8318d769245beA52C32BE46CD) | L2 alias of the governance Timelock |" - "\n| RH_INBOX | Ethereum | [0x1A07cc4BD17E0118BdB54D70990D2158AbAD7a2D](https://etherscan.io/address/0x1A07cc4BD17E0118BdB54D70990D2158AbAD7a2D) | Robinhood Chain Delayed Inbox |" - "\n| TOKEN_JAR | Robinhood Chain | TBD | Fee Collector |" - "\n| V3_OPEN_FEE_ADAPTER | Robinhood Chain | TBD | Uniswap V3 Fee Adapter |" - "\n| V4_FEE_ADAPTER | Robinhood Chain | TBD | Uniswap V4 Fee Adapter (protocolFeeController) |" - "\n| V4_FEE_POLICY | Robinhood Chain | TBD | Uniswap V4 Fee Policy |" - "\n| RELEASER | Robinhood Chain | TBD | ArbitrumOrbitResourceFirepit (searcher burn) |" - "\n| UNI | Robinhood Chain | TBD| Bridged UNI |" - "\n" - "\n## Onchain Execution" - "\n" - "\nThis Snapshot covers v2, v3, and v4 fees on Robinhood Chain, but onchain execution will be split across three proposals. A [separate Snapshot](https://snapshot.box/#/s:uniswapgovernance.eth/proposal/0x3fda3c04fa106ad45173011ef4dfa5db2f14cd35c3c3a7a40f067a44cbadd5f4) to enable v4 fees on the eleven chains where v2/v3 fees are already live concludes this weekend. The v4 activation is the same call on every chain (set the PoolManager's protocolFeeController to the V4FeeAdapter), so it is faster and safer to batch the v4 activations together and keep Robinhood's v2/v3 calls on their own:" - "\n" - "\n1. **Robinhood Fee Activation**: the two calls specced above" - "\n2. **V4 Fee Activation (Part 1)**: Ethereum, Base, Robinhood, BNB Chain, Arbitrum, Optimism, Polygon" - "\n3. **V4 Fee Activation (Part 2)**: Celo, Soneium, X Layer, World Chain, Zora" - "\n" - "\nRobinhood Fee Activation and V4 Part 1 will go onchain first, with Part 2 following once a proposer wallet frees up. Robinhood's v4 fees will be enabled through Part 1, contingent on both Snapshots passing." - "\n" - "\n## Next Steps / Timeline" - "\n" - "\n- **Snapshot begins:** Jul 10, 2026" - "\n- **Snapshot ends:** Jul 15, 2026" - "\n*This proposal is part of the protocol fee rollout, following proposals [#93](https://vote.uniswapfoundation.org/proposals/93), [#94](https://vote.uniswapfoundation.org/proposals/94), [#95](https://vote.uniswapfoundation.org/proposals/95), and [#96](https://vote.uniswapfoundation.org/proposals/96). It uses the expedited governance process [approved](https://gov.uniswap.org/t/unification-proposal/25881#p-57882-protocol-fee-rollout-4) in UNIfication, where fee parameter update proposals can bypass the RFC stage and go directly to a five-day Snapshot followed by an onchain vote.*" - "\n" - "\nSince protocol fees went live on Ethereum mainnet in late December last year, the rollout has extended to ten additional chains: Arbitrum, Base, OP Mainnet, Worldchain, X Layer, Soneium, Zora, Celo, BNB Chain, and Polygon. The burn system is working as designed, with fees accumulating in TokenJars across chains. From there, searchers claim them in exchange for burning UNI by bridging it back to mainnet and sending it to the burn address." - "\n" - "\nUniswap [launched on Robinhood Chain](https://blog.uniswap.org/robinhood-chain-is-live) at the chain's July 1, 2026 mainnet debut, with v2, v3, and v4 all live. As of July 10, the deployments have crossed [$1b of cumulative swap volume](https://x.com/DefiLlama/status/2075608063969485082?s=20). This proposal:" - "\n" - "\n- Extends the infrastructure for collecting and burning protocol fees to Robinhood Chain" - "\n- Enables v2, v3, and v4 protocol fees on Robinhood Chain" - "\n" - "\n## Implementation Details" - "\n" - "\nFees on Robinhood Chain will be routed to the TokenJar on that chain. UNI burned on Robinhood Chain is bridged back to Ethereum mainnet and sent to the burn address." - "\n" - "\nRobinhood Chain is an Arbitrum Orbit chain, so this proposal reuses the pattern from the Arbitrum One activation in proposal 94." - "\n" - "\n**Governance path.** Same as Arbitrum One: cross-chain governance messages are delivered as retryable tickets through Robinhood Chain's Inbox contract on Ethereum and executed on Robinhood Chain by the L2 alias of the governance Timelock. The aliased Timelock already controls both factories and the v4 PoolManager on Robinhood Chain (it is the v2 factory's feeToSetter and the owner of the v3 factory and the PoolManager), so no ownership migration is required." - "\n" - "\n**Burn path.** The releaser, ArbitrumOrbitResourceFirepit, is the Arbitrum One firepit generalized for Orbit chains. A searcher pays bridged UNI on Robinhood Chain to claim the TokenJar's accumulated fees, and the contract withdraws that UNI to the burn address on mainnet through the canonical gateway. As on Arbitrum One, the withdrawal finalizes on mainnet after Robinhood Chain's challenge period." - "\n" - "\nThe TokenJar, Releaser, V3OpenFeeAdapter, V4FeeAdapter, and V4FeePolicy will be deployed ahead of the onchain votes, with ownership verified against the aliased Timelock. This post will be updated with those addresses when they've been deployed and a link to the repo containing the contracts and deployment scripts once it has been merged." - "\n" - "\nImplementation details for the v4 fee system are in the [v4 fee activation temp check](https://gov.uniswap.org/t/temp-check-activate-v4-protocol-fees/26162). v2 and v3 protocol fee levels are the same as on all other chains where fees are live, see breakdown [here](https://developers.uniswap.org/docs/protocols/protocol-fee/concepts/fees#fee-split-table)." - "\n" - "\n## Proposal Spec" - "\n" - "\nIf passed, the Robinhood Fee Activation proposal will execute two calls, each creating a retryable ticket in Robinhood Chain's Inbox on Ethereum." - "\n" - "\n`RH_INBOX.createRetryableTicket(...) x2`" - "\n" - "\nOne call will encode:" - "\n" - "\n`V2_FACTORY.setFeeTo(TOKEN_JAR)`" - "\n" - "\nThe other will encode:" - "\n" - "\n`V3_FACTORY.setOwner(V3_OPEN_FEE_ADAPTER)`" - "\n" - "\nOnce executed on Robinhood Chain, they set the fee collector of UniswapV2Factory to TokenJar and transfer ownership of UniswapV3Factory to V3OpenFeeAdapter." - "\n" - "\nRobinhood's v4 activation will be included in the V4 Fee Activation (Part 1) proposal described below. Matching the spec of the [v4 fee activation temp check](https://gov.uniswap.org/t/temp-check-activate-v4-protocol-fees/26162), and delivered through the same retryable ticket path, it will encode:" - "\n" - "\n`V4_POOL_MANAGER.setProtocolFeeController(V4_FEE_ADAPTER)`" - "\n**RELEVANT ADDRESSES:**" - "\n" - "\n| Name | Network | Address | Description |" - "\n| :-- | :-- | :-- | :--|" - "\n| V2_FACTORY | Robinhood Chain | [0x8bcEaA40B9AcdfAedF85AdF4FF01F5Ad6517937f](https://robinhoodchain.blockscout.com/address/0x8bcEaA40B9AcdfAedF85AdF4FF01F5Ad6517937f) | Uniswap V2 Factory |" - "\n| V3_FACTORY | Robinhood Chain | [0x1f7d7550B1b028f7571E69A784071F0205FD2EfA](https://robinhoodchain.blockscout.com/address/0x1f7d7550B1b028f7571E69A784071F0205FD2EfA) | Uniswap V3 Factory |" - "\n| V4_POOL_MANAGER | Robinhood Chain | [0x8366a39cc670b4001a1121b8f6a443a643e40951](https://robinhoodchain.blockscout.com/address/0x8366a39cc670b4001a1121b8f6a443a643e40951) | Uniswap V4 Pool Manager |" - "\n| L2_GATEWAY_ROUTER | Robinhood Chain | [0x1E324B9316138CA9a73F960213621AD1aaf01B89](https://robinhoodchain.blockscout.com/address/0x1E324B9316138CA9a73F960213621AD1aaf01B89) | Canonical L2 Gateway Router |" - "\n| ALIASED_TIMELOCK | Robinhood Chain | [0x2BAD8182C09F50c8318d769245beA52C32BE46CD](https://robinhoodchain.blockscout.com/address/0x2BAD8182C09F50c8318d769245beA52C32BE46CD) | L2 alias of the governance Timelock |" - "\n| RH_INBOX | Ethereum | [0x1A07cc4BD17E0118BdB54D70990D2158AbAD7a2D](https://etherscan.io/address/0x1A07cc4BD17E0118BdB54D70990D2158AbAD7a2D) | Robinhood Chain Delayed Inbox |" - "\n| TOKEN_JAR | Robinhood Chain | TBD | Fee Collector |" - "\n| V3_OPEN_FEE_ADAPTER | Robinhood Chain | TBD | Uniswap V3 Fee Adapter |" - "\n| V4_FEE_ADAPTER | Robinhood Chain | TBD | Uniswap V4 Fee Adapter (protocolFeeController) |" - "\n| V4_FEE_POLICY | Robinhood Chain | TBD | Uniswap V4 Fee Policy |" - "\n| RELEASER | Robinhood Chain | TBD | ArbitrumOrbitResourceFirepit (searcher burn) |" - "\n| UNI | Robinhood Chain | TBD| Bridged UNI |" - "\n" - "\n## Onchain Execution" - "\n" - "\nThis Snapshot covers v2, v3, and v4 fees on Robinhood Chain, but onchain execution will be split across three proposals. A [separate Snapshot](https://snapshot.box/#/s:uniswapgovernance.eth/proposal/0x3fda3c04fa106ad45173011ef4dfa5db2f14cd35c3c3a7a40f067a44cbadd5f4) to enable v4 fees on the eleven chains where v2/v3 fees are already live concludes this weekend. The v4 activation is the same call on every chain (set the PoolManager's protocolFeeController to the V4FeeAdapter), so it is faster and safer to batch the v4 activations together and keep Robinhood's v2/v3 calls on their own:" - "\n" - "\n1. **Robinhood Fee Activation**: the two calls specced above" - "\n2. **V4 Fee Activation (Part 1)**: Ethereum, Base, Robinhood, BNB Chain, Arbitrum, Optimism, Polygon" - "\n3. **V4 Fee Activation (Part 2)**: Celo, Soneium, X Layer, World Chain, Zora" - "\n" - "\nRobinhood Fee Activation and V4 Part 1 will go onchain first, with Part 2 following once a proposer wallet frees up. Robinhood's v4 fees will be enabled through Part 1, contingent on both Snapshots passing." - "\n" - "\n## Next Steps / Timeline" - "\n" - "\n- **Snapshot begins:** Jul 10, 2026" - "\n- **Snapshot ends:** Jul 15, 2026" - "\n- **Onchain votes:** Robinhood Fee Activation and V4 Fee Activation (Part 1) following the Snapshots, per standard governance cadence; V4 Fee Activation (Part 2) to follow"; +string constant DESCRIPTION = "TODO"; diff --git a/script/proposal-6/Description.sol b/script/proposal-6/Description.sol index 2e674d54..fb2280e5 100644 --- a/script/proposal-6/Description.sol +++ b/script/proposal-6/Description.sol @@ -1,107 +1,4 @@ // SPDX-License-Identifier: MIT pragma solidity 0.8.29; -string constant DESCRIPTION = "\n## Summary" - "\n" - "\nThis proposal continues the protocol fee rollout approved in UNIfication, following proposals [#93](https://vote.uniswapfoundation.org/proposals/93), [#94](https://vote.uniswapfoundation.org/proposals/94), [#95](https://vote.uniswapfoundation.org/proposals/95), and [#96](https://vote.uniswapfoundation.org/proposals/96). It uses the expedited governance process where fee parameter update proposals go directly to a five-day Snapshot followed by an onchain vote." - "\n" - "\nProtocol fees are now live across all v2 and v3 pools on 11 chains - Ethereum, Arbitrum, Base, Celo, OP Mainnet, Soneium, X Layer, Worldchain, Zora, BNB Chain, and Polygon. Last month, the protocol set a record burning [186,000 UNI in one day](https://x.com/UNIBurnBot/status/2063252718043459633). " - "\n" - "\nBelow we introduce a system for v4 protocol fees and propose to activate it on a subset of v4 pools on these same chains. This onchain vote will specifically activate v4 fees on Ethereum, Arbitrum, Base, BNB Chain, Celo, Optimism, and Robinhood Chain. " - "\n" - "\n---" - "\n" - "\n## Implementation Details" - "\n" - "\nv4's hook architecture requires a different approach to fee activation than v2 or v3. v2 pools have a single LP fee tier and are charged a static fee. v3 has several LP fee tiers, each charged a static fee. Hooks mean v4 has potentially infinite distinct LP fee tiers, and a pool's fees can change dynamically from one block to the next. To manage this, we propose a V4 Fee Controller system where governance sets rules that let a dedicated contract compute the fee for any pool on demand, rather than setting a fee on each individual pool." - "\n" - "\nThe system splits across two contracts:" - "\n" - "\n* **V4FeePolicy**. Given any pool, it computes the fee from rules defined by governance. This is the contract governance calls to enable and adjust the protocol fee, and it can be swapped out later if the logic for setting fees needs to evolve." - "\n" - "\n* **V4FeeAdapter**. Enforces governance overrides, so if governance has set a per-pool override, that override wins and the policy is skipped. Otherwise the adapter applies the policy's fee, pushes it to the pool, and collects the proceeds to the TokenJar." - "\n" - "\n`V4FeePolicy` determines a pool's fee in two steps. First it sorts the pool into a **family**. A pool's family is determined by its characteristics, e.g. whether it has a hook, whether it uses the `PoolManager`'s native swap math, whether it charges dynamic swap fees, et cetera. A pool's family is identified by a flag, which is stored on the hook smart contract. Hook developers can opt their pools into a family via assigning it a specific flag. Governance can also assign a hook to a family directly via a vote. " - "\n" - "\nOnce it determines the pool's family, the policy then resolves the fee, applying the rules below, going in order from most specific to least:" - "\n" - "\n1. a per-pair fee, if governance has set one for that token pair in that family" - "\n2. otherwise the family's own fee (defined by governance as a default or curve)" - "\n3. otherwise a global default for anything still unclassified" - "\n" - "\nWith this system, governance manages a handful of rules and overrides instead of an unbounded list of pools, any fee is computed deterministically and can be inspected onchain, and the policy itself is replaceable if governance later wants to change how pools are categorized." - "\n" - "\nThis proposal activates fees on three pool families:" - "\n" - "\n* **Static fee pools:** These are pools without hooks. The protocol fee for these pools is set via a curve targeting a proportion of each pool's LP fee. For a description of this curve, please see the appendix below." - "\n* **CCA Pools**: These are pools launched after a Continuous Clearing Auction. The LBPHook and pools resulting from previous auctions will be opted into the same curve as static pools." - "\n* **Aggregator hook pools:** These are pools whose hooks integrate external liquidity venues into the v4 routing graph. The protocol fee for the aggregator hook family is a flat fee with overrides for specific pair types. To maintain the option of charging more on this external flow than the v4 PoolManager's hard cap of 10bps, aggregator hooks will multiply their assigned fees by 25, allowing for a cap of 250bps. After the multiplier is applied, the resulting fee for aggregator hooks will be:" - "\n * For all chains other than Base:" - "\n * Family Default: 10bp" - "\n * Select Stable Pairs: 3bp" - "\n * For Base: " - "\n * Family Default: 3bp" - "\n * Select Stable Pairs: 1bp" - "\n**This proposal does not enable the protocol fee for any pools other than those in the Families mentioned above.**" - "\n" - "\nFees will flow to TokenJar on each chain. UNI burned on L2s and alt-L1s will be bridged back to Ethereum mainnet and sent to `0xdead`." - "\n" - "\n---" - "\n" - "\n## Onchain Proposal Spec" - "\n" - "\n**Pre-proposal** (to be completed by Uniswap Labs prior to an onchain vote)" - "\n" - "\n* Deploy V4FeeAdapter and V4FeePolicy contracts on all chains where the v2 and v3 protocol fees are currently enabled (Ethereum, Arbitrum, Base, Celo, OP Mainnet, Soneium, X Layer, Worldchain, Zora, BNB Chain, and Polygon)." - "\n* Configure V4FeePolicy contracts with the native math protocol fee curve, CCA Hook and aggregator hook family fee logic described above" - "\n" - "\nThese contracts can be found [here](https://github.com/Uniswap/protocol-fees/tree/main/src/feeAdapters), and this post will be updated with addresses and explorer links when they have been deployed." - "\n" - "\n**In this proposal** (executed if the vote passes):" - "\n" - "\n* Set the `V4FeeAdapter` as the `ProtocolFeeController` on the PoolManager on each chain" - "\n" - "\n---" - "\n" - "\n## Next Steps / Timeline" - "\n" - "\n**Snapshot:** July 7-12, 2026" - "\n" - "\n**Onchain vote:** Starting the week of July 13, 2026" - "\n" - "\nPlease note that because of GovernorBravo's limit of 10 actions per proposal, there will be two separate onchain votes posted in parallel to accommodate all chains." - "\n" - "\n---" - "\n" - "\n## Appendix - Static Fee Curve" - "\n" - "\nThe V4 Fee Controller allows fee setting using discrete LP fee tier ranges. Each range has a floor, and the next range sets the ceiling. For each range, governance sets two inputs:" - "\n" - "\n* `alpha` which is the constant. This is the starting fee for that range." - "\n* `beta` which is the scaling factor. This is how fast the fee grows within that range. The growth always starts from the floor of the range." - "\n* Inside any range, the fee is: `alpha + beta * (lpFee - floor)`.The output is floored to the nearest 0.01bp increment, consistent with v4's minimum fee resolution." - "\n" - "\n| *Range (bps)* | *Floor* | *Alpha (bps)* | *Beta (bps)* |" - "\n| :---- | :---- | :---- | :---- |" - "\n| *0 \\- 0.03* | *0* | *0.01* | *0* |" - "\n| *0.03 \\- 0.75* | *0.03* | *0.01* | *19/72* |" - "\n| *0.75 \\- 1* | *0.75* | *0.2* | *0.2* |" - "\n| *1 \\- 3.75* | *1* | *0.25* | *3/11* |" - "\n| *3.75 \\- 5* | *3.75* | *1* | *0.2* |" - "\n| *5 \\- 25* | *5* | *1.25* | *11/80* |" - "\n| *25 \\- 55* | *25* | *4* | *0.2* |" - "\n| *\\> 55* | *55* | *10* | *0* |" - "\n" - "\nThis results in the following fees at the following points. " - "\n" - "\n| LP Fee (bps) | Protocol Fee (bps) |" - "\n| :---- | :---- |" - "\n| 0.03 | 0.01 |" - "\n| .75 | 0.20 |" - "\n| 1 | 0.25 |" - "\n| 3.75 | 1 |" - "\n| 5 | 1.25 |" - "\n| 25 | 4 |" - "\n| 30 | 5 |" - "\n| 83.34 | 10 |" - "\n| 100 | 10 |"; +string constant DESCRIPTION = "TODO"; From 0d4c380d155e8745f821876c4b0e613afaa2d42f Mon Sep 17 00:00:00 2001 From: jtriley Date: Thu, 16 Jul 2026 18:57:40 -0700 Subject: [PATCH 42/55] add rh token jar zero check --- script/proposal-6/V4FeesProposal.s.sol | 2 ++ script/proposal-6/prereq/DeployV4FeeInfra.s.sol | 4 ++++ 2 files changed, 6 insertions(+) diff --git a/script/proposal-6/V4FeesProposal.s.sol b/script/proposal-6/V4FeesProposal.s.sol index b259ac95..5d706a9b 100644 --- a/script/proposal-6/V4FeesProposal.s.sol +++ b/script/proposal-6/V4FeesProposal.s.sol @@ -26,6 +26,8 @@ contract V4FeeProposal is Script { Uniswap internal uniswap; function run() public { + require(Constants.Robinhood.TOKEN_JAR != address(0x00)); + vm.createDir("./out/.seatbelt/", true); recorder.initialize("DeployV4FeeInfra"); uniswap.loadLatest(); diff --git a/script/proposal-6/prereq/DeployV4FeeInfra.s.sol b/script/proposal-6/prereq/DeployV4FeeInfra.s.sol index 8fc55dcb..d3dae2ef 100644 --- a/script/proposal-6/prereq/DeployV4FeeInfra.s.sol +++ b/script/proposal-6/prereq/DeployV4FeeInfra.s.sol @@ -40,6 +40,10 @@ contract DeployV4FeeInfra is Script { (address poolManager, address tokenJar, address postConfigOwner) = getAddresses(chainId); + if (chainId == Constants.Robinhood.CHAIN_ID) { + require(Constants.Robinhood.TOKEN_JAR != address(0x00)); + } + vm.startBroadcast(); // ----------------------------------------------------------------------------------------- From d45419fdf3953fe0d4eb84d10039b1b690eae28f Mon Sep 17 00:00:00 2001 From: jtriley Date: Thu, 16 Jul 2026 18:59:33 -0700 Subject: [PATCH 43/55] add description check --- script/proposal-5/RobinhoodFees.s.sol | 2 ++ script/proposal-6/V4FeesProposal.s.sol | 1 + 2 files changed, 3 insertions(+) diff --git a/script/proposal-5/RobinhoodFees.s.sol b/script/proposal-5/RobinhoodFees.s.sol index 24b1b7bb..16326928 100644 --- a/script/proposal-5/RobinhoodFees.s.sol +++ b/script/proposal-5/RobinhoodFees.s.sol @@ -29,6 +29,8 @@ contract RobinhoodFees is Script { uniswap.loadLatest(); recorder.initialize({scriptName: "DeployFeeInfraRobinhood"}); + require(keccak256(bytes(DESCRIPTION)) != keccak256(bytes("TODO"))); + address tokenJar = recorder.read({chainId: Constants.Robinhood.CHAIN_ID, deploymentName: "TokenJar"}); address v3OpenFeeAdapter = diff --git a/script/proposal-6/V4FeesProposal.s.sol b/script/proposal-6/V4FeesProposal.s.sol index 5d706a9b..edd5142c 100644 --- a/script/proposal-6/V4FeesProposal.s.sol +++ b/script/proposal-6/V4FeesProposal.s.sol @@ -27,6 +27,7 @@ contract V4FeeProposal is Script { function run() public { require(Constants.Robinhood.TOKEN_JAR != address(0x00)); + require(keccak256(bytes(DESCRIPTION)) != keccak256(bytes("TODO"))); vm.createDir("./out/.seatbelt/", true); recorder.initialize("DeployV4FeeInfra"); From dbdf5467e08995308e978fa24ae9f5c211c23215 Mon Sep 17 00:00:00 2001 From: Mark Toda Date: Fri, 17 Jul 2026 09:20:04 -0400 Subject: [PATCH 44/55] feat: add robinhood tokenjar --- script/proposal-5/Constants.sol | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/script/proposal-5/Constants.sol b/script/proposal-5/Constants.sol index abeeeca1..34447aa6 100644 --- a/script/proposal-5/Constants.sol +++ b/script/proposal-5/Constants.sol @@ -14,5 +14,5 @@ library Robinhood { address constant POOL_MANAGER = 0x8366a39CC670B4001A1121B8F6A443A643e40951; address constant UNI = 0xF177d86a28b520e3E396E4F3B96cd8e72D7dabd8; address constant L2_GATEWAY_ROUTER = 0x1E324B9316138CA9a73F960213621AD1aaf01B89; - address constant TOKEN_JAR = address(0x00); + address constant TOKEN_JAR = 0x2aC03e14Cfe755426DaAEe0a4994184Ce81482F8; } From 17190e0bd2cf249f8575b8193949a979b457dfea Mon Sep 17 00:00:00 2001 From: Alice Henshaw <34962750+hensha256@users.noreply.github.com> Date: Fri, 17 Jul 2026 10:43:29 -0300 Subject: [PATCH 45/55] fix(proposal-6): activate WorldChain & XLayer v4 fees via CrossChainAccount (XDM), not OptimismPortal2 WorldChain and XLayer v4 PoolManagers are owned by their CrossChainAccount, not the aliased Timelock -- verified onchain: PoolManager.owner() == crossChainAccount, and crossChainAccount.l1Owner == mainnet Timelock via the L2CrossDomainMessenger. Activating via OptimismPortal2Encoder delivers setProtocolFeeController from the aliased Timelock, so the L1 proposal executes but the L2 call reverts on onlyOwner -- v4 fees never activate on these two chains, and the OP deposit (unlike an Arbitrum retryable) cannot be retried without a new governance vote. Switch both to L1CrossDomainMessengerEncoder -> CrossChainAccount.forward, matching the other five OP-stack chains (Base/Celo/Optimism/Soneium/Zora). Remove the now-unused OptimismPortal2Encoder and IL1CrossDomainMessenger imports. Verified owner-vs-encoder-sender onchain for all reachable target chains; only WorldChain and XLayer were mismatched. Co-Authored-By: Claude Opus 4.8 --- script/proposal-6/V4FeesProposal.s.sol | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/script/proposal-6/V4FeesProposal.s.sol b/script/proposal-6/V4FeesProposal.s.sol index edd5142c..c3b81f52 100644 --- a/script/proposal-6/V4FeesProposal.s.sol +++ b/script/proposal-6/V4FeesProposal.s.sol @@ -12,10 +12,8 @@ import {GovernanceSeatbelt} from "govkit/forge/GovernanceSeatbelt.sol"; import {FxRootEncoder} from "govkit/bridges/FxRootEncoder.sol"; import {InboxEncoder} from "govkit/bridges/InboxEncoder.sol"; import {L1CrossDomainMessengerEncoder} from "govkit/bridges/L1CrossDomainMessengerEncoder.sol"; -import {OptimismPortal2Encoder} from "govkit/bridges/OptimismPortal2Encoder.sol"; import {WormholeEncoder} from "govkit/bridges/WormholeEncoder.sol"; import {IPoolManager} from "govkit/interfaces/IPoolManager.sol"; -import {IL1CrossDomainMessenger} from "govkit/interfaces/bridges/IL1CrossDomainMessenger.sol"; import "../proposal-5/Constants.sol" as Constants; @@ -199,8 +197,9 @@ contract V4FeeProposal is Script { // --------------------------------------------------------------------------------------------- // 02: Activate V4 Fees for Worldchain. // - Call memory activateV4FeesWorldchain = OptimismPortal2Encoder.encode({ - portal: IL1CrossDomainMessenger(uniswap.ethereum.bridge.worldChain).portal(), + Call memory activateV4FeesWorldchain = L1CrossDomainMessengerEncoder.encode({ + l1CrossDomainMessenger: uniswap.ethereum.bridge.worldChain, + crossChainAccount: uniswap.worldChain.crossChainAccount, remoteCall: Call({ target: uniswap.worldChain.poolManager, value: 0, @@ -214,8 +213,9 @@ contract V4FeeProposal is Script { // --------------------------------------------------------------------------------------------- // 03: Activate V4 Fees for X Layer // - Call memory activateV4FeesXLayer = OptimismPortal2Encoder.encode({ - portal: IL1CrossDomainMessenger(uniswap.ethereum.bridge.xLayer).portal(), + Call memory activateV4FeesXLayer = L1CrossDomainMessengerEncoder.encode({ + l1CrossDomainMessenger: uniswap.ethereum.bridge.xLayer, + crossChainAccount: uniswap.xLayer.crossChainAccount, remoteCall: Call({ target: uniswap.xLayer.poolManager, value: 0, From 4dba30af7e8a54bbd7a12f3ef11fff0287668c7a Mon Sep 17 00:00:00 2001 From: Alice Henshaw <34962750+hensha256@users.noreply.github.com> Date: Fri, 17 Jul 2026 11:31:12 -0300 Subject: [PATCH 46/55] feat(proposal-6): classify Robinhood LBPStrategy hook into family 255 Add the LBPStrategy hook (0x095e38a2135aeBcfFa98A5B6911591937f912000, verified deployed on Robinhood) to the Robinhood hook-family assignments so its pools are classified into the native-math family (255), like the other Robinhood LBP/CCA hooks. Co-Authored-By: Claude Opus 4.8 --- script/proposal-6/prereq/DeployV4FeeInfra.s.sol | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/script/proposal-6/prereq/DeployV4FeeInfra.s.sol b/script/proposal-6/prereq/DeployV4FeeInfra.s.sol index d3dae2ef..536937f1 100644 --- a/script/proposal-6/prereq/DeployV4FeeInfra.s.sol +++ b/script/proposal-6/prereq/DeployV4FeeInfra.s.sol @@ -171,12 +171,14 @@ contract DeployV4FeeInfra is Script { } if (chainId == Constants.Robinhood.CHAIN_ID) { - HookFamilyAssignment[] memory assignments = new HookFamilyAssignment[](2); + HookFamilyAssignment[] memory assignments = new HookFamilyAssignment[](3); assignments[0] = HookFamilyAssignment({hook: 0x05d552391067389EE44fec3924157ed33F976000, familyId: 255}); // LBP assignments[1] = HookFamilyAssignment({hook: 0xD462a559337859369EF271814851A18F496ba000, familyId: 255}); // new // hook + assignments[2] = + HookFamilyAssignment({hook: 0x095e38a2135aeBcfFa98A5B6911591937f912000, familyId: 255}); // LBP policy.batchSetHookFamily(assignments); } From da6e2d9887fe3961b5fbedd8155fa37caa2fa2c4 Mon Sep 17 00:00:00 2001 From: Mark Toda Date: Fri, 17 Jul 2026 11:13:45 -0400 Subject: [PATCH 47/55] fix: records --- .records/DeployFeeInfraRobinhood.json | 8 +++++++ .records/DeployV4FeeInfra.json | 30 +++++++++++++++++++++++++++ 2 files changed, 38 insertions(+) create mode 100644 .records/DeployFeeInfraRobinhood.json create mode 100644 .records/DeployV4FeeInfra.json diff --git a/.records/DeployFeeInfraRobinhood.json b/.records/DeployFeeInfraRobinhood.json new file mode 100644 index 00000000..31aa6992 --- /dev/null +++ b/.records/DeployFeeInfraRobinhood.json @@ -0,0 +1,8 @@ +{ + "4663": { + "ArbitrumOrbitDeployer": "0x81405C50595f09175d282ccEc657e090EE54AB2E", + "TokenJar": "0x2aC03e14Cfe755426DaAEe0a4994184Ce81482F8", + "Releaser": "0x7a8f74C2585F84c781F951B7f2FF21337D5b630b", + "V3OpenFeeAdapter": "0x05C420bC4823e039AA4dA645eDde743486dAAA25" + } +} \ No newline at end of file diff --git a/.records/DeployV4FeeInfra.json b/.records/DeployV4FeeInfra.json new file mode 100644 index 00000000..e49673ea --- /dev/null +++ b/.records/DeployV4FeeInfra.json @@ -0,0 +1,30 @@ +{ + "8453": { + "V4FeeAdapter": "0xc53DD825dedBE7814562528A1A30e8e5BCbB4b55", + "V4FeePolicy": "0xf963bdbe8BA2aABBB3190fF2268F3d833393ab71" + }, + "1": { + "V4FeeAdapter": "0x89A5D5bF00a27D55c02951E49078a5C5771051dB", + "V4FeePolicy": "0x1cd822b70a0591420F65E94b9B3A0D0b0fB3a314" + }, + "42161": { + "V4FeeAdapter": "0x6b3C04567CC62fc5D6AB8D50f1bc1Dee161eb2Cc", + "V4FeePolicy": "0x8F6a5A19Ff1adc6AA9456d6b201fA4FC7589BD0B" + }, + "56": { + "V4FeeAdapter": "0x66276D2e80784180E4B9b859e4327b9C5646B87c", + "V4FeePolicy": "0x020B1Ad79037f02feC357Ec4601fFbe5f7ED6872" + }, + "42220": { + "V4FeeAdapter": "0xfd39ac616e630e9db03EFB2c9a4Fe63eDB949233", + "V4FeePolicy": "0xe1Adf3f130Abf616a7aB04AEeBf8D0470F6B6a16" + }, + "10": { + "V4FeeAdapter": "0xd85393784234fB2ee4f077867A8B78E1aD4cE56B", + "V4FeePolicy": "0x13d9D19802660b8167B3c84C1fcDCD0163672eB8" + }, + "4663": { + "V4FeeAdapter": "0x6d0009504D129CF5002Dba61D9Ae8575AA79314c", + "V4FeePolicy": "0x6ee984309ba26c224f12Bb85D6F7f3C019483DFc" + } +} \ No newline at end of file From 14bb329558c8d00fd2406c6aee9a2a5f7ebd2e6b Mon Sep 17 00:00:00 2001 From: Mark Toda Date: Fri, 17 Jul 2026 12:32:14 -0400 Subject: [PATCH 48/55] feat: polygon --- .records/DeployV4FeeInfra.json | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/.records/DeployV4FeeInfra.json b/.records/DeployV4FeeInfra.json index e49673ea..fdc50e3f 100644 --- a/.records/DeployV4FeeInfra.json +++ b/.records/DeployV4FeeInfra.json @@ -26,5 +26,9 @@ "4663": { "V4FeeAdapter": "0x6d0009504D129CF5002Dba61D9Ae8575AA79314c", "V4FeePolicy": "0x6ee984309ba26c224f12Bb85D6F7f3C019483DFc" + }, + "137": { + "V4FeeAdapter": "0x59e84309Afa65F084F9c03668583d231afB12cc6", + "V4FeePolicy": "0x600c29d3Ce95Ce503FAb43753cbcb5Fa87EE6093" } } \ No newline at end of file From f503b9d3211d4db504be63597ba1c65ad02a5456 Mon Sep 17 00:00:00 2001 From: jtriley Date: Fri, 17 Jul 2026 09:50:51 -0700 Subject: [PATCH 49/55] add v4 part n/2 selector, mv polygon to part 1, celo to part 2 --- script/proposal-6/V4FeesProposal.s.sol | 68 +++++++++++++++----------- 1 file changed, 39 insertions(+), 29 deletions(-) diff --git a/script/proposal-6/V4FeesProposal.s.sol b/script/proposal-6/V4FeesProposal.s.sol index c3b81f52..02b65635 100644 --- a/script/proposal-6/V4FeesProposal.s.sol +++ b/script/proposal-6/V4FeesProposal.s.sol @@ -19,6 +19,14 @@ import "../proposal-5/Constants.sol" as Constants; import {DESCRIPTION} from "./Description.sol"; +enum Part { + One, + Two +} + +/// @dev Toggle this to run part 1 or 2. +Part constant V4_FEE_PROPOSAL_PART = Part.One; + contract V4FeeProposal is Script { Recorder internal recorder; Uniswap internal uniswap; @@ -30,7 +38,8 @@ contract V4FeeProposal is Script { vm.createDir("./out/.seatbelt/", true); recorder.initialize("DeployV4FeeInfra"); uniswap.loadLatest(); - { + + if (V4_FEE_PROPOSAL_PART == Part.One) { // --------------------------------------------------------------------------------------------- // 00: Activate V4 Fees for Ethereum. // @@ -93,18 +102,21 @@ contract V4FeeProposal is Script { }); // --------------------------------------------------------------------------------------------- - // 04: Activate V4 Fees for Celo. + // 04: Activate V4 Fees for Polygon. // - Call memory activateV4FeesCelo = L1CrossDomainMessengerEncoder.encode({ - l1CrossDomainMessenger: uniswap.ethereum.bridge.celo, - crossChainAccount: uniswap.celo.crossChainAccount, - remoteCall: Call({ - target: uniswap.celo.poolManager, - value: 0, - data: abi.encodeCall( - IPoolManager.setProtocolFeeController, (recorder.read(ChainId.Celo, "V4FeeAdapter")) - ) - }) + Call memory activateV4FeesPolygon = FxRootEncoder.encode({ + fxRoot: uniswap.ethereum.bridge.polygon, + fxReceiver: uniswap.polygon.fxReceiver, + remoteCalls: LibCall.newCalls( + [Call({ + target: uniswap.polygon.poolManager, + value: 0, + data: abi.encodeCall( + IPoolManager.setProtocolFeeController, + (recorder.read(ChainId.Polygon, "V4FeeAdapter")) + ) + })] + ) }); // --------------------------------------------------------------------------------------------- @@ -146,7 +158,7 @@ contract V4FeeProposal is Script { activateV4FeesArbitrum, activateV4FeesBase, activateV4FeesBNBChain, - activateV4FeesCelo, + activateV4FeesPolygon, activateV4FeesOPMainnet, activateV4FeesRobinhood ] @@ -160,23 +172,21 @@ contract V4FeeProposal is Script { }) }); } - { + + if (V4_FEE_PROPOSAL_PART == Part.Two) { // --------------------------------------------------------------------------------------------- - // 00: Activate V4 Fees for Polygon. + // 00: Activate V4 Fees for Celo. // - Call memory activateV4FeesPolygon = FxRootEncoder.encode({ - fxRoot: uniswap.ethereum.bridge.polygon, - fxReceiver: uniswap.polygon.fxReceiver, - remoteCalls: LibCall.newCalls( - [Call({ - target: uniswap.polygon.poolManager, - value: 0, - data: abi.encodeCall( - IPoolManager.setProtocolFeeController, - (recorder.read(ChainId.Polygon, "V4FeeAdapter")) - ) - })] - ) + Call memory activateV4FeesCelo = L1CrossDomainMessengerEncoder.encode({ + l1CrossDomainMessenger: uniswap.ethereum.bridge.celo, + crossChainAccount: uniswap.celo.crossChainAccount, + remoteCall: Call({ + target: uniswap.celo.poolManager, + value: 0, + data: abi.encodeCall( + IPoolManager.setProtocolFeeController, (recorder.read(ChainId.Celo, "V4FeeAdapter")) + ) + }) }); // --------------------------------------------------------------------------------------------- @@ -244,7 +254,7 @@ contract V4FeeProposal is Script { description: string.concat("# Activate v4 Protocol Fees (Part 2/2) \n\n", DESCRIPTION), calls: LibCall.newCalls( [ - activateV4FeesPolygon, + activateV4FeesCelo, activateV4FeesSoneium, activateV4FeesWorldchain, activateV4FeesXLayer, From 9f099b7b83a888ea44f0a0cb43ba842815559c68 Mon Sep 17 00:00:00 2001 From: jtriley Date: Fri, 17 Jul 2026 10:20:48 -0700 Subject: [PATCH 50/55] rm unused import --- script/proposal-5/prereq/DeployFeeInfraRobinhood.s.sol | 1 - 1 file changed, 1 deletion(-) diff --git a/script/proposal-5/prereq/DeployFeeInfraRobinhood.s.sol b/script/proposal-5/prereq/DeployFeeInfraRobinhood.s.sol index a5b51331..71e315a9 100644 --- a/script/proposal-5/prereq/DeployFeeInfraRobinhood.s.sol +++ b/script/proposal-5/prereq/DeployFeeInfraRobinhood.s.sol @@ -13,7 +13,6 @@ import {IReleaser} from "govkit/interfaces/IReleaser.sol"; import {IV3OpenFeeAdapter} from "govkit/interfaces/IV3OpenFeeAdapter.sol"; import {ArbitrumOrbitDeployer} from "../../deployers/ArbitrumOrbitDeployer.sol"; -import {IL1ERC20Gateway} from "../Interfaces.sol"; import "../Constants.sol" as Constants; string constant DEPLOYER_NAME = "ArbitrumOrbitDeployer"; From ae7d7259f040fe327bfdf90d6cde4467618cac82 Mon Sep 17 00:00:00 2001 From: jtriley Date: Fri, 17 Jul 2026 10:23:55 -0700 Subject: [PATCH 51/55] update descriptions --- script/proposal-5/Description.sol | 61 ++++++++++++++++- script/proposal-6/Description.sol | 108 +++++++++++++++++++++++++++++- 2 files changed, 167 insertions(+), 2 deletions(-) diff --git a/script/proposal-5/Description.sol b/script/proposal-5/Description.sol index fb2280e5..1f301c16 100644 --- a/script/proposal-5/Description.sol +++ b/script/proposal-5/Description.sol @@ -1,4 +1,63 @@ // SPDX-License-Identifier: MIT pragma solidity 0.8.29; -string constant DESCRIPTION = "TODO"; +string constant DESCRIPTION = "# Protocol Fee Expansion: Robinhood Chain\n" +"\n" +"*This proposal is part of the protocol fee rollout, following proposals [#93](https://vote.uniswapfoundation.org/proposals/93), [#94](https://vote.uniswapfoundation.org/proposals/94), [#95](https://vote.uniswapfoundation.org/proposals/95), and [#96](https://vote.uniswapfoundation.org/proposals/96). It uses the expedited governance process [approved](https://gov.uniswap.org/t/unification-proposal/25881#p-57882-protocol-fee-rollout-4) in UNIfication, where fee parameter update proposals can bypass the RFC stage and go directly to a five-day Snapshot followed by an onchain vote.*\n" +"\n" +"Since protocol fees went live on Ethereum mainnet in late December last year, the rollout has extended to ten additional chains: Arbitrum, Base, OP Mainnet, Worldchain, X Layer, Soneium, Zora, Celo, BNB Chain, and Polygon. The burn system is working as designed, with fees accumulating in TokenJars across chains. From there, searchers claim them in exchange for burning UNI by bridging it back to mainnet and sending it to the burn address.\n" +"\n" +"Uniswap [launched on Robinhood Chain](https://blog.uniswap.org/robinhood-chain-is-live) at the chain's July 1, 2026 mainnet debut, with v2, v3, and v4 all live. As of July 10, the deployments have crossed $6b of cumulative swap volume. This proposal:\n" +"\n" +"- Extends the infrastructure for collecting and burning protocol fees to Robinhood Chain\n" +"- Enables v2, v3, and v4 protocol fees on Robinhood Chain. \n" +"\n" +"Please note that this onchain vote enables v2 and v3 fees specifically, while v4 fees will be activated as a part of a separate proposal covering part 1 of the v4 fee activation.\n" +"\n" +"## Implementation Details\n" +"\n" +"Fees on Robinhood Chain will be routed to the TokenJar on that chain. UNI burned on Robinhood Chain is bridged back to Ethereum mainnet and sent to the burn address.\n" +"\n" +"Robinhood Chain is an Arbitrum Orbit chain, so this proposal reuses the pattern from the Arbitrum One activation in proposal 94.\n" +"\n" +"**Governance path.** Same as Arbitrum One: cross-chain governance messages are delivered as retryable tickets through Robinhood Chain's Inbox contract on Ethereum and executed on Robinhood Chain by the L2 alias of the governance Timelock. The aliased Timelock already controls both factories and the v4 PoolManager on Robinhood Chain (it is the v2 factory's feeToSetter and the owner of the v3 factory and the PoolManager), so no ownership migration is required.\n" +"\n" +"**Burn path.** The releaser, ArbitrumOrbitResourceFirepit, is the Arbitrum One firepit generalized for Orbit chains. A searcher pays bridged UNI on Robinhood Chain to claim the TokenJar's accumulated fees, and the contract withdraws that UNI to the burn address on mainnet through the canonical gateway. As on Arbitrum One, the withdrawal finalizes on mainnet after Robinhood Chain's challenge period.\n" +"\n" +"The TokenJar, Releaser, V3OpenFeeAdapter, V4FeeAdapter, and V4FeePolicy will be deployed ahead of the onchain votes, with ownership verified against the aliased Timelock. This post will be updated with those addresses when they've been deployed and a link to the repo containing the contracts and deployment scripts once it has been merged.\n" +"\n" +"Implementation details for the v4 fee system are in the [v4 fee activation temp check](https://gov.uniswap.org/t/temp-check-activate-v4-protocol-fees/26162). v2 and v3 protocol fee levels are the same as on all other chains where fees are live, see breakdown [here](https://developers.uniswap.org/docs/protocols/protocol-fee/concepts/fees#fee-split-table).\n" +"\n" +"## Proposal Spec\n" +"\n" +"If passed, the Robinhood Fee Activation proposal will execute two calls, each creating a retryable ticket in Robinhood Chain's Inbox on Ethereum.\n" +"\n" +"`RH_INBOX.createRetryableTicket(...) x2`\n" +"\n" +"One call will encode:\n" +"\n" +"`V2_FACTORY.setFeeTo(TOKEN_JAR)`\n" +"\n" +"The other will encode:\n" +"\n" +"`V3_FACTORY.setOwner(V3_OPEN_FEE_ADAPTER)`\n" +"\n" +"Once executed on Robinhood Chain, they set the fee collector of UniswapV2Factory to TokenJar and transfer ownership of UniswapV3Factory to V3OpenFeeAdapter.\n" +"\n" +"Robinhood's v4 activation will be included in the V4 Fee Activation (Part 1) proposal described below. Matching the spec of the [v4 fee activation temp check](https://gov.uniswap.org/t/temp-check-activate-v4-protocol-fees/26162), and delivered through the same retryable ticket path, it will encode:\n" +"\n" +"`V4_POOL_MANAGER.setProtocolFeeController(V4_FEE_ADAPTER)`\n" +"\n" +"## Onchain Execution\n" +"\n" +"This Onchain vote covers v2 and v3 fees on Robinhood Chain. v4 fees on Robinhood Chain have been batched in with part 1 of v4 fee activation, as noted above. \n" +"2. **V4 Fee Activation (Part 1)**: Ethereum, Base, Robinhood, BNB Chain, Arbitrum, Optimism, Polygon\n" +"3. **V4 Fee Activation (Part 2)**: Celo, Soneium, X Layer, World Chain, Zora\n" +"\n" +"All contracts can be found [here](https://github.com/Uniswap/protocol-fees/), and this post will be updated with addresses and explorer links when they have been deployed.\n" +"\n" +"## Next Steps / Timeline\n" +"\n" +"- **Onchain vote begins:** Jul 19, 2026\n" +"- **Onchain ends:** Jul 26, 2026\n" +"*This proposal is part of the protocol fee rollout, following proposals [#93](https://vote.uniswapfoundation.org/proposals/93), [#94](https://vote.uniswapfoundation.org/proposals/94), [#95](https://vote.uniswapfoundation.org/proposals/95), and [#96](https://vote.uniswapfoundation.org/proposals/96). It uses the expedited governance process [approved](https://gov.uniswap.org/t/unification-proposal/25881#p-57882-protocol-fee-rollout-4) in UNIfication, where fee parameter update proposals can bypass the RFC stage and go directly to a five-day Snapshot followed by an onchain vote.*\n"; diff --git a/script/proposal-6/Description.sol b/script/proposal-6/Description.sol index fb2280e5..d9fafd2f 100644 --- a/script/proposal-6/Description.sol +++ b/script/proposal-6/Description.sol @@ -1,4 +1,110 @@ // SPDX-License-Identifier: MIT pragma solidity 0.8.29; -string constant DESCRIPTION = "TODO"; +string constant DESCRIPTION = +"# Activate v4 Protocol Fees: 1/2 \n" +"\n" +"## Summary\n" +"\n" +"This proposal continues the protocol fee rollout approved in UNIfication, following proposals [#93](https://vote.uniswapfoundation.org/proposals/93), [#94](https://vote.uniswapfoundation.org/proposals/94), [#95](https://vote.uniswapfoundation.org/proposals/95), and [#96](https://vote.uniswapfoundation.org/proposals/96). It uses the expedited governance process where fee parameter update proposals go directly to a five-day Snapshot followed by an onchain vote.\n" +"\n" +"Protocol fees are now live across all v2 and v3 pools on 11 chains - Ethereum, Arbitrum, Base, Celo, OP Mainnet, Soneium, X Layer, Worldchain, Zora, BNB Chain, and Polygon. Last month, the protocol set a record burning [186,000 UNI in one day](https://x.com/UNIBurnBot/status/2063252718043459633). \n" +"\n" +"Below we introduce a system for v4 protocol fees and propose to activate it on a subset of v4 pools on these same chains. This onchain vote will specifically activate v4 fees on Ethereum, Arbitrum, Base, BNB Chain, Polygon, Optimism, and Robinhood Chain. Please note that because of GovernorBravo's limit of 10 actions per proposal, there will be two separate onchain votes to accommodate all chains.\n" +"The remaining five chains (Celo, Soneium, Worldchain, X Layer, Zora) will be addressed in a subsequent proposal. \n" +"---\n" +"\n" +"## Implementation Details\n" +"\n" +"v4's hook architecture requires a different approach to fee activation than v2 or v3. v2 pools have a single LP fee tier and are charged a static fee. v3 has several LP fee tiers, each charged a static fee. Hooks mean v4 has potentially infinite distinct LP fee tiers, and a pool's fees can change dynamically from one block to the next. To manage this, we propose a V4 Fee Controller system where governance sets rules that let a dedicated contract compute the fee for any pool on demand, rather than setting a fee on each individual pool.\n" +"\n" +"The system splits across two contracts:\n" +"\n" +"* **V4FeePolicy**. Given any pool, it computes the fee from rules defined by governance. This is the contract governance calls to enable and adjust the protocol fee, and it can be swapped out later if the logic for setting fees needs to evolve.\n" +"\n" +"* **V4FeeAdapter**. Enforces governance overrides, so if governance has set a per-pool override, that override wins and the policy is skipped. Otherwise the adapter applies the policy's fee, pushes it to the pool, and collects the proceeds to the TokenJar.\n" +"\n" +"`V4FeePolicy` determines a pool's fee in two steps. First it sorts the pool into a **family**. A pool's family is determined by its characteristics, e.g. whether it has a hook, whether it uses the `PoolManager`'s native swap math, whether it charges dynamic swap fees, et cetera. A pool's family is identified by a flag, which is stored on the hook smart contract. Hook developers can opt their pools into a family via assigning it a specific flag. Governance can also assign a hook to a family directly via a vote. \n" +"\n" +"Once it determines the pool's family, the policy then resolves the fee, applying the rules below, going in order from most specific to least:\n" +"\n" +"1. a per-pair fee, if governance has set one for that token pair in that family\n" +"2. otherwise the family's own fee (defined by governance as a default or curve)\n" +"3. otherwise a global default for anything still unclassified\n" +"\n" +"With this system, governance manages a handful of rules and overrides instead of an unbounded list of pools, any fee is computed deterministically and can be inspected onchain, and the policy itself is replaceable if governance later wants to change how pools are categorized.\n" +"\n" +"This proposal activates fees on three pool families:\n" +"\n" +"* **Static fee pools:** These are pools without hooks. The protocol fee for these pools is set via a curve targeting a proportion of each pool's LP fee. For a description of this curve, please see the appendix below.\n" +"* **CCA Pools**: These are pools launched after a Continuous Clearing Auction. The LBPHook and pools resulting from previous auctions will be opted into the same curve as static pools.\n" +"* **Aggregator hook pools:** These are pools whose hooks integrate external liquidity venues into the v4 routing graph. The protocol fee for the aggregator hook family is a flat fee with overrides for specific pair types. To maintain the option of charging more on this external flow than the v4 PoolManager's hard cap of 10bps, aggregator hooks will multiply their assigned fees by 25, allowing for a cap of 250bps. After the multiplier is applied, the resulting fee for aggregator hooks will be:\n" +" * For all chains other than Base:\n" +" * Family Default: 10bp\n" +" * Select Stable Pairs: 3bp\n" +" * For Base: \n" +" * Family Default: 3bp\n" +" * Select Stable Pairs: 1bp\n" +"**This proposal does not enable the protocol fee for any pools other than those in the Families mentioned above.**\n" +"\n" +"Fees will flow to TokenJar on each chain. UNI burned on L2s and alt-L1s will be bridged back to Ethereum mainnet and sent to `0xdead`.\n" +"\n" +"---\n" +"\n" +"## Onchain Proposal Spec\n" +"\n" +"**Pre-proposal** (to be completed by Uniswap Labs prior to an onchain vote)\n" +"\n" +"* Deploy V4FeeAdapter and V4FeePolicy contracts on the chains where the v2 and v3 protocol fees are currently enabled (Ethereum, Arbitrum, Base, Celo, OP Mainnet, Soneium, X Layer, Worldchain, Zora, BNB Chain, and Polygon).\n" +"* Configure V4FeePolicy contracts with the native math protocol fee curve, CCA Hook and aggregator hook family fee logic described above\n" +"\n" +"\n" +"These contracts can be found [here](https://github.com/Uniswap/protocol-fees/tree/main/src/feeAdapters), and this post will be updated with addresses and explorer links when they have been deployed.\n" +"\n" +"\n" +"**In this proposal** (executed if the vote passes):\n" +"\n" +"* Set the `V4FeeAdapter` as the `ProtocolFeeController` on the PoolManager on each chain\n" +"\n" +"---\n" +"\n" +"## Next Steps / Timeline\n" +"\n" +"**Snapshot:** July 11-16, 2026\n" +"\n" +"**Onchain vote:** July 19-26, 2026\n" +"\n" +"---\n" +"\n" +"## Appendix - Static Fee Curve\n" +"\n" +"The V4 Fee Controller allows fee setting using discrete LP fee tier ranges. Each range has a floor, and the next range sets the ceiling. For each range, governance sets two inputs:\n" +"\n" +"* `alpha` which is the constant. This is the starting fee for that range.\n" +"* `beta` which is the scaling factor. This is how fast the fee grows within that range. The growth always starts from the floor of the range.\n" +"* Inside any range, the fee is: `alpha + beta * (lpFee - floor)`.The output is floored to the nearest 0.01bp increment, consistent with v4's minimum fee resolution.\n" +"\n" +"| *Range (bps)* | *Floor* | *Alpha (bps)* | *Beta (bps)* |\n" +"| :---- | :---- | :---- | :---- |\n" +"| *0 - 0.03* | *0* | *0.01* | *0* |\n" +"| *0.03 - 0.75* | *0.03* | *0.01* | *19/72* |\n" +"| *0.75 - 1* | *0.75* | *0.2* | *0.2* |\n" +"| *1 - 3.75* | *1* | *0.25* | *3/11* |\n" +"| *3.75 - 5* | *3.75* | *1* | *0.2* |\n" +"| *5 - 25* | *5* | *1.25* | *11/80* |\n" +"| *25 - 55* | *25* | *4* | *0.2* |\n" +"| *> 55* | *55* | *10* | *0* |\n" +"\n" +" This results in the following fees at the following points. \n" +"\n" +"| LP Fee (bps) | Protocol Fee (bps) |\n" +"| :---- | :---- |\n" +"| 0.03 | 0.01 |\n" +"| .75 | 0.20 |\n" +"| 1 | 0.25 |\n" +"| 3.75 | 1 |\n" +"| 5 | 1.25 |\n" +"| 25 | 4 |\n" +"| 30 | 5 |\n" +"| 83.34 | 10 |\n" +"| 100 | 10 |\n"; From 7da750cbf48727c619d218d31a2ac9f8969bb31a Mon Sep 17 00:00:00 2001 From: jtriley Date: Fri, 17 Jul 2026 10:33:54 -0700 Subject: [PATCH 52/55] typo fix --- script/proposal-6/Description.sol | 2 -- 1 file changed, 2 deletions(-) diff --git a/script/proposal-6/Description.sol b/script/proposal-6/Description.sol index d9fafd2f..6b7a02ed 100644 --- a/script/proposal-6/Description.sol +++ b/script/proposal-6/Description.sol @@ -2,8 +2,6 @@ pragma solidity 0.8.29; string constant DESCRIPTION = -"# Activate v4 Protocol Fees: 1/2 \n" -"\n" "## Summary\n" "\n" "This proposal continues the protocol fee rollout approved in UNIfication, following proposals [#93](https://vote.uniswapfoundation.org/proposals/93), [#94](https://vote.uniswapfoundation.org/proposals/94), [#95](https://vote.uniswapfoundation.org/proposals/95), and [#96](https://vote.uniswapfoundation.org/proposals/96). It uses the expedited governance process where fee parameter update proposals go directly to a five-day Snapshot followed by an onchain vote.\n" From 8bf45145710a57ef5af392dbf7b22b917984b76f Mon Sep 17 00:00:00 2001 From: jtriley Date: Fri, 17 Jul 2026 10:39:23 -0700 Subject: [PATCH 53/55] rm dup line --- script/proposal-5/Description.sol | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/script/proposal-5/Description.sol b/script/proposal-5/Description.sol index 1f301c16..90c9433f 100644 --- a/script/proposal-5/Description.sol +++ b/script/proposal-5/Description.sol @@ -59,5 +59,4 @@ string constant DESCRIPTION = "# Protocol Fee Expansion: Robinhood Chain\n" "## Next Steps / Timeline\n" "\n" "- **Onchain vote begins:** Jul 19, 2026\n" -"- **Onchain ends:** Jul 26, 2026\n" -"*This proposal is part of the protocol fee rollout, following proposals [#93](https://vote.uniswapfoundation.org/proposals/93), [#94](https://vote.uniswapfoundation.org/proposals/94), [#95](https://vote.uniswapfoundation.org/proposals/95), and [#96](https://vote.uniswapfoundation.org/proposals/96). It uses the expedited governance process [approved](https://gov.uniswap.org/t/unification-proposal/25881#p-57882-protocol-fee-rollout-4) in UNIfication, where fee parameter update proposals can bypass the RFC stage and go directly to a five-day Snapshot followed by an onchain vote.*\n"; +"- **Onchain ends:** Jul 26, 2026\n"; From 22f003b695a88f8429f784036fb919a004013bf0 Mon Sep 17 00:00:00 2001 From: jtriley Date: Fri, 17 Jul 2026 12:59:19 -0700 Subject: [PATCH 54/55] rm err lines --- script/proposal-5/Description.sol | 2 -- 1 file changed, 2 deletions(-) diff --git a/script/proposal-5/Description.sol b/script/proposal-5/Description.sol index 90c9433f..4917a03f 100644 --- a/script/proposal-5/Description.sol +++ b/script/proposal-5/Description.sol @@ -51,8 +51,6 @@ string constant DESCRIPTION = "# Protocol Fee Expansion: Robinhood Chain\n" "## Onchain Execution\n" "\n" "This Onchain vote covers v2 and v3 fees on Robinhood Chain. v4 fees on Robinhood Chain have been batched in with part 1 of v4 fee activation, as noted above. \n" -"2. **V4 Fee Activation (Part 1)**: Ethereum, Base, Robinhood, BNB Chain, Arbitrum, Optimism, Polygon\n" -"3. **V4 Fee Activation (Part 2)**: Celo, Soneium, X Layer, World Chain, Zora\n" "\n" "All contracts can be found [here](https://github.com/Uniswap/protocol-fees/), and this post will be updated with addresses and explorer links when they have been deployed.\n" "\n" From c2107cd579d8d48791fab81883e1faee8ddd692a Mon Sep 17 00:00:00 2001 From: jtriley Date: Tue, 21 Jul 2026 09:58:23 -0700 Subject: [PATCH 55/55] add new deploy addrs --- README.md | 28 ++++++++++++++++++++++++++++ 1 file changed, 28 insertions(+) diff --git a/README.md b/README.md index 0fc6cf67..c18d6c74 100644 --- a/README.md +++ b/README.md @@ -13,6 +13,7 @@ - [2. Fee Sources](#2-fee-sources) - [Push vs Pull Models](#push-vs-pull-models) - [Supported Protocols](#supported-protocols) + - [V4 Fee Resolution](#v4-fee-resolution) - [3. Releasers](#3-releasers) - [UNI Burn (Mainnet)](#uni-burn-mainnet) - [Cross-Chain UNI Burn (OP Stack L2s)](#cross-chain-uni-burn-op-stack-l2s) @@ -33,6 +34,7 @@ - [Base (Chain ID: 8453)](#base-chain-id-8453) - [Polygon (Chain ID: 137)](#polygon-chain-id-137) - [BNB Chain (Chain ID: 56)](#bnb-chain-chain-id-56) + - [Robinhood Chain (Chain ID: 4663)](#robinhood-chain-chain-id-4663) - [Development](#development) - [Prerequisites](#prerequisites) - [Installation](#installation) @@ -289,6 +291,8 @@ Ethereum Mainnet | Agreement Anchor 3 | [`0xF9F85a17cC6De9150Cd139f64b127976a1dE91D1`](https://etherscan.io/address/0xF9F85a17cC6De9150Cd139f64b127976a1dE91D1) | | Ntt Manager | [`0x6569925Aac77D6B8Bb085F31F9828ff80D5a0c44`](https://etherscan.io/address/0x6569925Aac77D6B8Bb085F31F9828ff80D5a0c44) | | Wormhole Transceiver | [`0x7597C40Fd3df66b750C14ad4D90524e247499011`](https://etherscan.io/address/0x7597C40Fd3df66b750C14ad4D90524e247499011) | +| V4FeeAdapter | [`0x89A5D5bF00a27D55c02951E49078a5C5771051dB`](https://etherscan.io/address/0x89A5D5bF00a27D55c02951E49078a5C5771051dB) | +| V4FeePolicy | [`0x1cd822b70a0591420F65E94b9B3A0D0b0fB3a314`](https://etherscan.io/address/0x1cd822b70a0591420F65E94b9B3A0D0b0fB3a314) | ### Unichain (Chain ID: 130) @@ -326,6 +330,8 @@ Ethereum Mainnet | TokenJar | [`0x190c22c5085640D1cB60CeC88a4F736Acb59bb6B`](https://celoscan.io/address/0x190c22c5085640D1cB60CeC88a4F736Acb59bb6B) | | Releaser (OptimismBridgedResourceFirepit) | [`0x2758FbaA228D7d3c41dD139F47dab1a27bF9bc25`](https://celoscan.io/address/0x2758FbaA228D7d3c41dD139F47dab1a27bF9bc25) | | V3OpenFeeAdapter | [`0xB9952C01830306ea2fAAe1505f6539BD260Bfc48`](https://celoscan.io/address/0xB9952C01830306ea2fAAe1505f6539BD260Bfc48) | +| V4FeeAdapter | [`0xfd39ac616e630e9db03EFB2c9a4Fe63eDB949233`](https://celoscan.io/address/0xfd39ac616e630e9db03EFB2c9a4Fe63eDB949233) | +| V4FeePolicy | [`0xe1Adf3f130Abf616a7aB04AEeBf8D0470F6B6a16`](https://celoscan.io/address/0xe1Adf3f130Abf616a7aB04AEeBf8D0470F6B6a16) | ### Zora (Chain ID: 7777777) @@ -354,6 +360,9 @@ Ethereum Mainnet | TokenJar | [`0x95E337C5B155385945D407f5396387D0c2a3A263`](https://arbiscan.io/address/0x95E337C5B155385945D407f5396387D0c2a3A263) | | Releaser | [`0xB8018422bcE25D82E70cB98FdA96a4f502D89427`](https://arbiscan.io/address/0xB8018422bcE25D82E70cB98FdA96a4f502D89427) | | V3OpenFeeAdapter | [`0xFF7aD5dA31fECdC678796c88B05926dB896b0699`](https://arbiscan.io/address/0xFF7aD5dA31fECdC678796c88B05926dB896b0699) | +| V4FeeAdapter | [`0x66276D2e80784180E4B9b859e4327b9C5646B87c`](https://arbiscan.io/address/0x66276D2e80784180E4B9b859e4327b9C5646B87c) | +| V4FeePolicy | [`0x020B1Ad79037f02feC357Ec4601fFbe5f7ED6872`](https://arbiscan.io/address/0x8F6a5A19Ff1adc6AA9456d6b201fA4FC7589BD0B) | + ### OP Mainnet (Chain ID: 10) @@ -363,6 +372,8 @@ Ethereum Mainnet | TokenJar | [`0xb13285DF724ea75f3f1E9912010B7e491dCd5EE3`](https://optimistic.etherscan.io/address/0xb13285DF724ea75f3f1E9912010B7e491dCd5EE3) | | Releaser (OptimismBridgedResourceFirepit) | [`0x94460443Ca27FFC1baeCa61165fde18346C91AbD`](https://optimistic.etherscan.io/address/0x94460443Ca27FFC1baeCa61165fde18346C91AbD) | | V3OpenFeeAdapter | [`0xec23Cf5A1db3dcC6595385D28B2a4D9B52503Be4`](https://optimistic.etherscan.io/address/0xec23Cf5A1db3dcC6595385D28B2a4D9B52503Be4) | +| V4FeeAdapter | [`0xd85393784234fB2ee4f077867A8B78E1aD4cE56B`](https://optimistic.etherscan.io/address/0xd85393784234fB2ee4f077867A8B78E1aD4cE56B) | +| V4FeePolicy | [`0x13d9D19802660b8167B3c84C1fcDCD0163672eB8`](https://optimistic.etherscan.io/address/0x13d9D19802660b8167B3c84C1fcDCD0163672eB8) | ### Base (Chain ID: 8453) @@ -372,6 +383,8 @@ Ethereum Mainnet | TokenJar | [`0x9bD25e67bF390437C8fAF480AC735a27BcF6168c`](https://basescan.org/address/0x9bD25e67bF390437C8fAF480AC735a27BcF6168c) | | Releaser (OptimismBridgedResourceFirepit) | [`0xFf77c0ED0B6b13A20446969107E5867abc46f53a`](https://basescan.org/address/0xFf77c0ED0B6b13A20446969107E5867abc46f53a) | | V3OpenFeeAdapter | [`0xaBEA76658b205696d49B5F91b2a03536cB8A3bE1`](https://basescan.org/address/0xaBEA76658b205696d49B5F91b2a03536cB8A3bE1) | +| V4FeeAdapter | [`0xc53DD825dedBE7814562528A1A30e8e5BCbB4b55`](https://basescan.org/address/0xc53DD825dedBE7814562528A1A30e8e5BCbB4b55) | +| V4FeePolicy | [`0xf963bdbe8BA2aABBB3190fF2268F3d833393ab71`](https://basescan.org/address/0xf963bdbe8BA2aABBB3190fF2268F3d833393ab71) | ### Polygon (Chain ID: 137) @@ -383,6 +396,8 @@ Ethereum Mainnet | tokenJar | [`0xc6Ae6373CEcc9e595A6C8b9fe581925a8c84f70A`](https://polygonscan.com/address/0xc6Ae6373CEcc9e595A6C8b9fe581925a8c84f70A) | | wormholeReleaser | [`0xa59FfbB55D91Fc32b44A06F0b9cc6036a4afbcE2`](https://polygonscan.com/address/0xa59FfbB55D91Fc32b44A06F0b9cc6036a4afbcE2) | | v3OpenFeeAdapter | [`0x3F07F08b45912dCd6691C5B9412975D5113B2910`](https://polygonscan.com/address/0x3F07F08b45912dCd6691C5B9412975D5113B2910) | +| V4FeeAdapter | [`0x59e84309Afa65F084F9c03668583d231afB12cc6`](https://polygonscan.com/address/0x59e84309Afa65F084F9c03668583d231afB12cc6) | +| V4FeePolicy | [`0x600c29d3Ce95Ce503FAb43753cbcb5Fa87EE6093`](https://polygonscan.com/address/0x600c29d3Ce95Ce503FAb43753cbcb5Fa87EE6093) | ### BNB Chain (Chain ID: 56) @@ -394,6 +409,19 @@ Ethereum Mainnet | tokenJar | [`0xc6Ae6373CEcc9e595A6C8b9fe581925a8c84f70A`](https://bscscan.com/address/0xc6Ae6373CEcc9e595A6C8b9fe581925a8c84f70A) | | wormholeReleaser | [`0xa59FfbB55D91Fc32b44A06F0b9cc6036a4afbcE2`](https://bscscan.com/address/0xa59FfbB55D91Fc32b44A06F0b9cc6036a4afbcE2) | | v3OpenFeeAdapter | [`0x3F07F08b45912dCd6691C5B9412975D5113B2910`](https://bscscan.com/address/0x3F07F08b45912dCd6691C5B9412975D5113B2910) | +| V4FeeAdapter | [`0x66276D2e80784180E4B9b859e4327b9C5646B87c`](https://bscscan.com/address/0x66276D2e80784180E4B9b859e4327b9C5646B87c) | +| V4FeePolicy | [`0x020B1Ad79037f02feC357Ec4601fFbe5f7ED6872`](https://bscscan.com/address/0x020B1Ad79037f02feC357Ec4601fFbe5f7ED6872) | + +### Robinhood Chain (Chain ID: 4663) + +| Contract | Address | +| ---------------- | ------------------------------------------------------------------------------------------------------------------- | +| Deployer | [`0x81405C50595f09175d282ccEc657e090EE54AB2E`](https://robinhoodchain.blockscout.com/address/0x81405C50595f09175d282ccEc657e090EE54AB2E) | +| TokenJar | [`0x2aC03e14Cfe755426DaAEe0a4994184Ce81482F8`](https://robinhoodchain.blockscout.com/address/0x2aC03e14Cfe755426DaAEe0a4994184Ce81482F8) | +| Releaser | [`0x7a8f74C2585F84c781F951B7f2FF21337D5b630b`](https://robinhoodchain.blockscout.com/address/0x7a8f74C2585F84c781F951B7f2FF21337D5b630b) | +| V3OpenFeeAdapter | [`0x05C420bC4823e039AA4dA645eDde743486dAAA25`](https://robinhoodchain.blockscout.com/address/0x05C420bC4823e039AA4dA645eDde743486dAAA25) | +| V4FeeAdapter | [`0x6d0009504D129CF5002Dba61D9Ae8575AA79314c`](https://robinhoodchain.blockscout.com/address/0x6d0009504D129CF5002Dba61D9Ae8575AA79314c) | +| V4FeePolicy | [`0x6ee984309ba26c224f12Bb85D6F7f3C019483DFc`](https://robinhoodchain.blockscout.com/address/0x6ee984309ba26c224f12Bb85D6F7f3C019483DFc) | ## Development