From e43296d9c6a2d0f339ed94fe7138a4f489246f1d Mon Sep 17 00:00:00 2001 From: John Feras Date: Tue, 19 Nov 2024 12:33:53 -0500 Subject: [PATCH 01/16] Adding documentation for proposal bridging to various L2 chains --- docs/concepts/governance/02-process.md | 2 +- docs/concepts/proposals-for-L2s.md | 506 +++++++++++++++++++++++++ 2 files changed, 507 insertions(+), 1 deletion(-) create mode 100644 docs/concepts/proposals-for-L2s.md diff --git a/docs/concepts/governance/02-process.md b/docs/concepts/governance/02-process.md index 2654034e0..f973ca9f6 100644 --- a/docs/concepts/governance/02-process.md +++ b/docs/concepts/governance/02-process.md @@ -78,7 +78,7 @@ To create an onchain Governance Proposal: 2. Create a topic in the [Governance Forum](https://gov.uniswap.org/) titled "Governance Proposal — [Your Title Here]" and link to previous forum posts and the Temperature Check Snapshot poll. -3. Create your proposal. This can be done either through an interface (e.g. [Tally](https://tally.xyz/gov/uniswap)) or through writing the calldata for more complicated proposal logic. If the proposal passed, this calldata will execute. If writing the calldata yourself, please review the logic with a qualified Uniswap community member prior to posting the proposal. +3. Create your proposal. This can be done either through an interface (e.g. [Tally](https://tally.xyz/gov/uniswap)) or through writing the calldata for more complicated proposal logic. If the proposal passed, this calldata will execute. If writing the calldata yourself, please review the logic with a qualified Uniswap community member prior to posting the proposal. For more information on developing and advancing multi-chain Uniswap Governance Proposals, see [Multi-Chain Governance](../proposals-for-L2s.md). 4. Ensure that at least 1 million UNI is delegated to your address in order to submit a proposal, or find a delegate who has enough delegated UNI to meet the proposal threshold to propose on your behalf. diff --git a/docs/concepts/proposals-for-L2s.md b/docs/concepts/proposals-for-L2s.md new file mode 100644 index 000000000..026f97856 --- /dev/null +++ b/docs/concepts/proposals-for-L2s.md @@ -0,0 +1,506 @@ +--- +id: proposals-on-L2 +title: Proposals on L2 +--- + +This is a living document which represents the current process guidelines for developing and advancing multi-chain Uniswap Governance Proposals. It was last updated Novenber 2024. + +## Introduction + +Uniswap V3 has now been deployed across 24 Layer 2 blockchains. However, because Uniswap governance (using GovernorBravo and its associated Timelock contract) executes approved proposals on Ethereum mainnet, implementing changes on these L2 blockchains requires some extra steps in the proposals' executable code. + +For a proposal to successfully enact changes on a Layer 2 chain, the proposal's code must transmit the necessary code effect the change to the specific target L2 for execution. +A variety of mechanisms are used my Uniswap V3 on the individual Layer 2 chains to receive the proposal code, although in some cases, similar or even identical communication methods and contracts are used to bridge proposals from Ethereum mainnet to the target chain. + +This document provides a detailed guide on constructing such proposals for each target L2 chain, including Solidity code snippets for each general approach. +The code snippets are written as Forge/Foundry scripts, and assume they are being run on the Forge platform, in a code repository with the necessary libraries (forge-std, uniswap-v3-core, and uniswap-v3-periphery) imported. +It is important to note that the code provided here is a general template and may require modification to suit the specific requirements of the target chain. + +## Overview + +There are 9 chains where Uniswap V3 is deployed that use Optimism style cross-chain communication, using the `L1CrossDomainMessenger` contract for sending proposals to L2, and the `CrossChainAccount` contract for forwarding the executable proposals to Uniswap V3 on the L2 network. +A Solidity code example for Optimism is provided [below](#crosschainaccount-proposal-bridging-example). This code can also be used for sending proposals to the other 8 chains, with minor changes to the actual deployed address constants for the targeted contracts. + +There are 4 chains where Uniswap V3 is deployed that use Wormhole for bridging proposals. They all use the same Ethereum mainnet instance of the `UniswapWormholeMessageSender` contract for sending proposals to L2. In addition to the proposal message parameters, the `sendMessage` function of this contract also takes both a target address for the message receiving contract on the L2 chain, as well as the Chain ID of the destination chain as parameters, allowing the function to be used for sending to multiple L2 chains. +A Solidity code example for sending a proposal to one these chains (BNB Chain) is provided [below](#wormhole-proposal-bridging-example). +This code can be used for sending proposals to the other 3 chains (Gnosis Chain, Moonbeam, and Rootstock), with the only changes needed being the actual depoloyed address constants for the targeted contracts. + +Each of the sections below provides a high-level description of the method used to bridge proposals to each target L2 chain. + +#### Arbitrum Cross-chain Communication + +Arbitrum uses an approach where the owner of the V3Factory is a special aliased address (offset by the value 0x1111000000000000000000000000000000001111) that (when the offset is subtracted away) is the L1 address of the Uniswap DAO Timelock contract. A Solidity code example for sending a proposal to Arbitrum is shown [below](#arbitrum-proposal-bridging-details). + +#### AVAX Cross-chain Communication + +AVAX makes use of a contract called `OmnichainGovernanceExecutor`.. TODO: Add details + +#### Base Cross-chain Communication Via CrossChainAccount (Optimism Style) + +Base is one of the 9 L2 chains that makes use of the CrossChainAccount contract. +See the Solidity code example provided for Optimism [below](#optimism-style-proposal-bridging-details). + +#### Blast Chain Cross-chain Communication Via CrossChainAccount (Optimism Style) + +Blast Chain is one of the 9 L2 Chains that makes use of the CrossChainAccount contract. +See the Solidity code example provided for Optimism [below](#optimism-style-proposal-bridging-details). + +#### BNB Chain Cross-chain Communication + +BNB Chain is one of the 4 L2 chains that makes use of a contract called `UniswapWormholeMessageReceiver`. On the Ethereum mainnet L1 side, the `UniswapWormholeMessageSender` contract is used to send proposals to the L2 chain. A Solidity code example for sending a proposal to BNB Chain is [below](#wormhole-proposal-bridging-details). + +#### Boba Cross-chain Communication Via CrossChainAccount (Optimism Style) + +Boba is one of the 9 L2 Chains that makes use of the CrossChainAccount contract. +See the Solidity code example provided for Optimism [below](#optimism-style-proposal-bridging-details). + +#### Celo Chain Cross-chain Communication + +Celo Chain makes use of a contract called: Unverified contract TODO: Add details + +#### Filecoin EVM Cross-chain Communication + +Filecoin EVM ?? TODO: Add details + +#### Gnosis Cross-chain Communication + +Gnosis Chain is one of the 4 L2 chains that makes use of a contract called `UniswapWormholeMessageReceiver`. On the Ethereum mainnet L1 side, the `UniswapWormholeMessageSender` contract is used to send proposals to the L2 chain. See the Solidity code example provided for the BNB Chain [below](#wormhole-proposal-bridging-details). + + +#### Linea Cross-chain Communication Via CrossChainAccount (Optimism Style) + +Linea is one of the 9 L2 Chains that makes use of the CrossChainAccount contract. +Linea, however, uses a slightly different contracts from L1CrossDomainMessenger, called L1MessageService for sending messages, +with different call parameters. See the Linea-specific Solidity code example provided [below](#linea-proposal-bridging-details). + +#### Manta Pacific Cross-chain Communication Via CrossChainAccount (Optimism Style) + +Manta Pacific is one of the 9 L2 Chains that makes use of the CrossChainAccount contract. +See the Solidity code example provided for Optimism [below](#optimism-style-proposal-bridging-details). + +#### Mantle Cross-chain Communication Via CrossChainAccount (Optimism Style) + +Mantle is one of the 9 L2 Chains that makes use of the CrossChainAccount contract. +See the Solidity code example provided for Optimism [below](#optimism-style-proposal-bridging-details). + +#### Moonbeam Cross-chain Communication + +Moonbeam is one of the 4 L2 chains that makes use of a contract called `UniswapWormholeMessageReceiver`. On the Ethereum mainnet L1 side, the `UniswapWormholeMessageSender` contract is used to send proposals to the L2 chain. See the Solidity code example provided for the BNB Chain [below](#wormhole-proposal-bridging-details). + + +#### Optimism Cross-chain Communication Via CrossChainAccount (Optimism Style) + +Optimism is one of the 9 L2 Chains that makes use of the CrossChainAccount contract. +See the Solidity code example provided for Optimism [below](#optimism-style-proposal-bridging-details). + +#### Polygon Cross-chain Communication + +Polygon makes use of an L1 contract called FxRoot for sending messages (in the case of Uniswap, executable proposals), and contracts called FxChild and EthereumProxy on the Polygon L2 for forwarding the executable proposals to Uniswap V3 on Polygon. A Solidity code example for sending a proposal to Polygon is [below](#polygon-ethereumproxy-proposal-bridging-details). + +#### Polygon zkEVM Cross-chain Communication + +Polygon zkEVM uses a different approach than Polygon TODO: Add details + +#### Redstone Cross-chain Communication Via CrossChainAccount (Optimism Style) + +Redstone is one of the 9 L2 Chains that makes use of the CrossChainAccount contract. +See the Solidity code example provided for Optimism [below](#optimism-style-proposal-bridging-details). + +#### Rootstock Cross-chain Communication + +Rootstock is one of the 4 L2 chains that makes use of a contract called `UniswapWormholeMessageReceiver`. On the Ethereum mainnet L1 side, the `UniswapWormholeMessageSender` contract is used to send proposals to the L2 chain. See the Solidity code example provided for the BNB Chain [below](#wormhole-proposal-bridging-details). + + +#### Scroll Cross-chain Communication + +Scroll makes use of a contract called: GnosisSafeProxy TODO: Add details + +#### Sei Cross-chain Communication + +Sei does not have a contract for sending messages. TODO: Add details + +#### Taiko Cross-chain Communication + +Taiko makes use of a contract called: InvokableAccount TODO: Add details + +#### Worldcoin Cross-chain Communication + +Worldcoin makes use of an unverified contract. TODO: Add details + +#### ZkSync Era Cross-chain Communication + +ZkSync Era uses the Arbitrum-style approach where the parent of the V3Factory contract is an L2 aliased address, but the method of sending the proposal is different than the one used for Arbitrum. TODO: Add details + +#### Zora Cross-chain Communication Via CrossChainAccount (Optimism Style) + +Zora is one of the 9 L2 Chains that makes use of the CrossChainAccount contract. +See the Solidity code example provided for Optimism [below](#optimism-style-proposal-bridging-details). + + +## Arbitrum Proposal Bridging Details +To create a proposal that (when successfully passed, queued, and executed) would effect a change on Uniswap on Arbitrum, the proposal would have to contain a call to the function called `createRetryableTicket` of the Inbox contract that is located at address 0x4Dbd4fc535Ac27206064B68FfCf827b0A60BAB3f on Ethereum mainnet. The call would have wrapped calldata for a Uniswap contract function call that would effect the change. An example: +``` +// SPDX-License-Identifier: UNLICENSED +pragma solidity ^0.8.28; + +import {console2} from "forge-std/console2.sol"; +import {Script} from "forge-std/script.sol"; +import {IUniswapV3Factory} from "lib/v3-core/contracts/interfaces/IUniswapV3Factory.sol"; + +interface IUniswapGovernorBravoDelegator { + function propose( + address[] memory targets, + uint256[] memory values, + string[] memory signatures, + bytes[] memory calldatas, + string memory description + ) external returns (uint256); +} + +contract EthereumToArbitrumSender is Script { + // target address for UniSwap DAO governor bravo delegate + address uniswapGovernorBravoDelegatorAddress = 0x408ED6354d4973f66138C91495F2f2FCbd8724C3; + + // Address of the Arbitrum Inbox on Ethereum (L1) + address constant INBOX_ADDRESS = 0x4Dbd4fc535Ac27206064B68FfCf827b0A60BAB3f; + + // target addresses on Arbitrum L2 + address v3FactoryTargetAddress = 0x1F98431c8aD98523631AE4a59f267346ea31F984; + + // function to create a proposal on Ethereum L1 to enable a fee amount on the Uniswap V3 Factory on Arbitrum L2. + // Upon execution of the proposal, the Inbox contract function createRetryableTicket is called, containing calldata for the + // uniswap v3 factory to enable a fee amount on Uniswap V3 Factory on Arbitrum L2 + function proposeForExecutionOnArbitrumL2() external payable { + // setup calldata for the L2 uniswap v3Factory.enableFeeAmount call + bytes memory _v3FactoryEnableFeeAmounCalldata = + abi.encodeWithSelector(IUniswapV3Factory.enableFeeAmount.selector, 10000, 205); + + // setup calldata for the creating retryable ticket + uint256 l2CallValue = 0; // Amount of ETH to send with the call on L2 + uint256 maxSubmissionCost = 0.01 ether; // Estimated cost for submission + uint256 maxGas = 1_000_000; // Maximum gas for L2 execution + uint256 gasPriceBid = 1 gwei; // Gas price for L2 execution + + // Define refund addresses for excess fees and call value + address excessFeeRefundAddress = msg.sender; + address callValueRefundAddress = msg.sender; + + // setup calldata for the proposal + bytes memory _proposalCalldata = abi.encode( + v3FactoryTargetAddress, + l2CallValue, + maxSubmissionCost, + excessFeeRefundAddress, + callValueRefundAddress, + callValueRefundAddress, + maxGas, + gasPriceBid, + _v3FactoryEnableFeeAmounCalldata + ); + + // make the proposal on the L1 side + IUniswapGovernorBravoDelegator governor = IUniswapGovernorBravoDelegator(uniswapGovernorBravoDelegatorAddress); + address[] memory _targets = new address[](1); + uint256[] memory _values = new uint256[](1); + string[] memory _signatures = new string[](1); + bytes[] memory _calldatas = new bytes[](1); + _targets[0] = INBOX_ADDRESS; + _values[0] = 0; + _signatures[0] = "createRetryableTicket(address,uint256,uint256,address,address,uint256,uint256,bytes)"; + _calldatas[0] = _proposalCalldata; + uint256 _proposalId = governor.propose( + _targets, + _values, + _signatures, + _calldatas, + "Proposal to enable 10000 fee amount of 205 on Uniswap V3 Factory on Arbitrum L2" + ); + console2.log("Proposal ID: %d", _proposalId); + } +} +``` +## Optimism-Style Proposal Bridging Details + +To create a proposal that (when successfully passed, queued, and executed) would effect a change on Uniswap V3 on Optimism style blockchains, the proposal would have to contain a call to the function called `sendMessage` on an instance of the L1CrossDomainMessenger contract on Ethereum mainnet. The call would have doubly-wrapped calldata for the CrossChainAccount `forward` function as well as the ultimate Uniswap contract function call that would effect the change. An example for Optimism: +``` +// SPDX-License-Identifier: UNLICENSED +pragma solidity ^0.8.28; + +import {console2} from "forge-std/console2.sol"; +import {Script} from "forge-std/script.sol"; +import {IUniswapV3Factory} from "lib/v3-core/contracts/interfaces/IUniswapV3Factory.sol"; + +interface ICrossChainAccount { + function forward(address target, bytes memory data) external; +} + +interface IUniswapGovernorBravoDelegator { + function propose( + address[] memory targets, + uint256[] memory values, + string[] memory signatures, + bytes[] memory calldatas, + string memory description + ) external returns (uint256); +} + +contract OptimismExample is Script { + // target address for the cross domain messenger on Ethereum L1 responsible for sending messages to Optimism L2 + // (this address would be different for Base, Blast, or Zora) + address constant l1CrossDomainMessengerAddress = 0x25ace71c97B33Cc4729CF772ae268934F7ab5fA1; + + // target addresses on Optimism L2 (these addresses would be different for Base, Blast, or Zora) + address constant v3FactoryTargetAddress = 0x1F98431c8aD98523631AE4a59f267346ea31F984; + address constant crossChainAccountTargetAddress = 0xa1dD330d602c32622AA270Ea73d078B803Cb3518; + + // target address for UniSwap DAO governor bravo delegate contract on Ethereum L1 + address uniswapGovernorBravoDelegatorAddress = 0x408ED6354d4973f66138C91495F2f2FCbd8724C3; + + // function to create a proposal on Ethereum L1 to enable a fee amount on the Uniswap V3 Factory on Optimism L2. + // Upon execution of the proposal, the L1CrossDomainMessenger function sendMessage is called containing calldata for the + // CrossChainAccount.forward function on Optimism L2 will be called containing calldata for the uniswap v3 factory + // to enable a fee amount on Uniswap V3 Factory on Optimism L2 + function proposeForExecutionOnOptimismL2() public { + // setup calldata for the L2 uniswap v3Factory.enableFeeAmount call + bytes memory _v3FactoryEnableFeeAmounCalldata = + abi.encodeWithSelector(IUniswapV3Factory.enableFeeAmount.selector, 10000, 205); + + // encode the calldata for the CrossChainAccount.forward call + bytes memory _messageForwardCalldata = abi.encodeWithSelector( + ICrossChainAccount.forward.selector, v3FactoryTargetAddress, _v3FactoryEnableFeeAmounCalldata + ); + + // make the proposal on the L1 side + IUniswapGovernorBravoDelegator governor = IUniswapGovernorBravoDelegator(uniswapGovernorBravoDelegatorAddress); + address[] memory _targets = new address[](1); + uint256[] memory _values = new uint256[](1); + string[] memory _signatures = new string[](1); + bytes[] memory _calldatas = new bytes[](1); + _targets[0] = address(l1CrossDomainMessengerAddress); + _values[0] = 0; + _signatures[0] = "sendMessage(address,bytes,uint256)"; + _calldatas[0] = abi.encode(address(crossChainAccountTargetAddress), _messageForwardCalldata, 500_000); + uint256 _proposalId = governor.propose( + _targets, + _values, + _signatures, + _calldatas, + "Proposal to enable 10000 fee amount of 205 on Uniswap V3 Factory on Optimism L2" + ); + console2.log("Proposal ID: %d", _proposalId); + } +} +``` +Because 9 networks all use the same CrossChainAccount approach for communication, the only modifications to the above code snippet for those networks would be to change the first 3 address constant definitions. The definitions for the other 8 networks are below: + +For Base: +* l1CrossDomainMessengerAddress would be: 0x866E82a600A1414e583f7F13623F1aC5d58b0Afa +* v3FactoryTargetAddress would be: 0x33128a8fC17869897dcE68Ed026d694621f6FDfD +* crossChainAccountTargetAddress would be: 0x31FAfd4889FA1269F7a13A66eE0fB458f27D72A9 + +For Blast: +* l1CrossDomainMessengerAddress would be: 0x5D4472f31Bd9385709ec61305AFc749F0fA8e9d0 +* v3FactoryTargetAddress would be: 0x792edAdE80af5fC680d96a2eD80A44247D2Cf6Fd +* crossChainAccountTargetAddress would be: 0x2339C0d23b60739B3E5ABF201F05903D24A26C77 + +For Boba: +* l1CrossDomainMessengerAddress would be: 0x6D4528d192dB72E282265D6092F4B872f9Dff69e +* v3FactoryTargetAddress would be: 0xFFCd7Aed9C627E82A765c3247d562239507f6f1B +* crossChainAccountTargetAddress would be: 0x53163235746CeB81Da32293bb0932e1A599256B4 + +For Linea: +* l1CrossDomainMessengerAddress would be: 0xd19d4B5d358258f05D7B411E21A1460D11B0876F +* v3FactoryTargetAddress would be: 0x31FAfd4889FA1269F7a13A66eE0fB458f27D72A9 +* crossChainAccountTargetAddress would be: 0x581F86Da293A1D5Cd087a10E7227a75d2d2201A8 +* Slightly different sending code for Linea, see section below. TODO + +For Manta Pacific: +* l1CrossDomainMessengerAddress would be: 0x635ba609680c55C3bDd0B3627b4c5dB21b13c310 +* v3FactoryTargetAddress would be: 0x06D830e15081f65923674268121FF57Cc54e4e23 +* crossChainAccountTargetAddress would be: 0x683553d74D9779955a15d57D208234C956B6Eae6 + +For Mantle: +* l1CrossDomainMessengerAddress would be: 0x676A795fe6E43C17c668de16730c3F690FEB7120 +* v3FactoryTargetAddress would be: 0x0d922Fb1Bc191F64970ac40376643808b4B74Df9 +* crossChainAccountTargetAddress would be: 0x9b7aC6735b23578E81260acD34E3668D0cc6000A + +For Redstone: +* l1CrossDomainMessengerAddress would be: 0x592C1299e0F8331D81A28C0FC7352Da24eDB444a +* v3FactoryTargetAddress would be: 0xece75613Aa9b1680f0421E5B2eF376DF68aa83Bb +* crossChainAccountTargetAddress would be: 0x2d00e94d78Fc307FC5E6195BBe2fB6aFC2FC07d4 + +For Zora: +* l1CrossDomainMessengerAddress would be: 0xdC40a14d9abd6F410226f1E6de71aE03441ca506 +* v3FactoryTargetAddress would be: 0x7145F8aeef1f6510E92164038E1B6F8cB2c42Cbb +* crossChainAccountTargetAddress would be: 0x36eEC182D0B24Df3DC23115D64DB521A93D5154f + +## Wormhole Proposal Bridging Details + +To create a proposal that (when successfully passed, queued, and executed) would effect a change on Uniswap V3 on an L2 chain via wormhole, the proposal would have to contain a call to the function called `sendMessage` on the one instance of the `UniswapWormholeMessageSender` contract on Ethereum mainnet. +In addition to the proposal message parameters, the function of also takes both a target address for the message receiving contract on the L2 chain, as well as the Chain ID of the destination chain as parameters, allowing the function to be used for sending to multiple L2 chains. + +There are 4 chains where Uniswap V3 is deployed that use the same Ethereum mainnet instance of the `UniswapWormholeMessageSender` contract for sending proposals to L2. +A Solidity code example for sending a proposal to one these chains (BNB Chain) is provided below. +This code can be used for sending proposals to the other 3 chains, with the only changes needed being the actual depoloyed address constants for the targeted contracts. + + +``` +// SPDX-License-Identifier: UNLICENSED +pragma solidity ^0.8.28; + +import {console2} from "forge-std/console2.sol"; +import {Script} from "forge-std/script.sol"; +import {IUniswapV3Factory} from "lib/v3-core/contracts/interfaces/IUniswapV3Factory.sol"; + +interface IUniswapGovernorBravoDelegator { + // function to create a proposal on Ethereum L1 using the Uniswap Governor Bravo Delegator contract + function propose( + address[] memory targets, + uint256[] memory values, + string[] memory signatures, + bytes[] memory calldatas, + string memory description + ) external returns (uint256); +} + +contract EthereumToAPolygonSender is Script { + // target address for UniSwap DAO governor bravo delegate + address uniswapGovernorBravoDelegatorAddress = 0x408ED6354d4973f66138C91495F2f2FCbd8724C3; + + // Address of the WormholdMessageSender contract on Ethereum (L1) + address constant WORMHOLE_MESSAGE_SENDER_ADDRESS = 0xf5F4496219F31CDCBa6130B5402873624585615a; + + // Address of the WormholeMessageReceiver contract on BNB Chain (L2) + address constant WORMHOLE_MESSAGE_RECEIVER_ADDRESS = 0x341c1511141022cf8eE20824Ae0fFA3491F1302b; + + // BNB Chain (L2) chainId + uint256 constant CHAIN_ID = 56; + + // target addresses on BNB Chain L2 + address v3FactoryTargetAddress = 0xdB1d10011AD0Ff90774D0C6Bb92e5C5c8b4461F7; + + // function to create a proposal on Ethereum L1 to enable a fee amount on the Uniswap V3 Factory on BNB Chain L2. + // Upon execution of the proposal, the WormholeMessageSender contract function sendMessage is called, containing calldata for the + // uniswap v3 factory to enable a fee amount on Uniswap V3 Factory on Arbitrum L2. + function proposeForExecutionOnBnbChainL2() external payable { + // setup calldata for the L2 uniswap v3Factory.enableFeeAmount call + bytes memory _v3FactoryEnableFeeAmounCalldata = + abi.encodeWithSelector(IUniswapV3Factory.enableFeeAmount.selector, 10000, 205); + + // setup calldata for calling WormholeMessageSender sendMessage + address[] memory _sendMessageTargets = new address[](1); + uint256[] memory _sendMessageValues = new uint256[](1); + bytes[] memory _sendMessageCalldatas = new bytes[](1); + _sendMessageTargets[0] = v3FactoryTargetAddress; + _sendMessageValues[0] = 0; + _sendMessageCalldatas[0] = _v3FactoryEnableFeeAmounCalldata; + bytes memory _sendMessageCalldata = abi.encode( + _sendMessageTargets, + _sendMessageValues, + _sendMessageCalldatas, + WORMHOLE_MESSAGE_RECEIVER_ADDRESS, + CHAIN_ID + ); + + // make the proposal on the L1 side + IUniswapGovernorBravoDelegator governor = IUniswapGovernorBravoDelegator(uniswapGovernorBravoDelegatorAddress); + address[] memory _targets = new address[](1); + uint256[] memory _values = new uint256[](1); + string[] memory _signatures = new string[](1); + bytes[] memory _calldatas = new bytes[](1); + _targets[0] = WORMHOLE_MESSAGE_SENDER_ADDRESS; + _values[0] = 0; + _signatures[0] = "sendMessage(address[],uint256[],bytes[],address,uint16)"; + _calldatas[0] = _sendMessageCalldata; + uint256 _proposalId = governor.propose( + _targets, + _values, + _signatures, + _calldatas, + "Proposal to enable 10000 fee amount of 205 on Uniswap V3 Factory on Polygon L2" + ); + console2.log("Proposal ID: %d", _proposalId); + } +} +``` +Because 4 networks all use the same Wormhole-based approach for communication, the only modifications to the above code snippet for those networks would be to change the first 3 address constant definitions. The definitions for the other 3 networks are below: + +For Gnosis: +* WORMHOLE_MESSAGE_RECEIVER_ADDRESS would be: 0xfFA5599136fBaB9af7799A6703b57BB33E5390Cf +* CHAIN_ID would be: 100 +* V3FactoryTargetAddress would be: 0xe32F7dD7e3f098D518ff19A22d5f028e076489B1 + +For Moonbeam: +* WORMHOLE_MESSAGE_RECEIVER_ADDRESS would be: 0xB2af16D6c7074228fC487F17929De830303E6531 +* CHAIN_ID would be: 1284 +* V3FactoryTargetAddress would be: 0xe32F7dD7e3f098D518ff19A22d5f028e076489B1 + +For Rootstock: +* WORMHOLE_MESSAGE_RECEIVER_ADDRESS would be: 0x38aE7De6f9c51e17f49cF5730DD5F2d29fa20758 +* CHAIN_ID would be: 30 +* V3FactoryTargetAddress would be: 0xaF37EC98A00FD63689CF3060BF3B6784E00caD82 + + +## Polygon EthereumProxy Proposal Bridging Details + +To create a proposal that (when successfully passed, queued, and executed) would effect a change on Uniswap V3 on Polygon, the proposal would have to contain a call to the function called `sendMessageToChild` of the FxRoot contract that is located at address 0xfe5e5D361b2ad62c541bAb87C45a0B9B018389a2 on Ethereum mainnet. The call would have wrapped calldata for a Uniswap contract function call that would effect the change. The FxRoot/FxChild tunnel would bridge that message to Polygon where FxChild would route the message the EthereumProxy parent contract of UniSwap V3. An example: +``` +// SPDX-License-Identifier: UNLICENSED +pragma solidity ^0.8.28; + +import {console2} from "forge-std/console2.sol"; +import {Script} from "forge-std/script.sol"; +import {IUniswapV3Factory} from "lib/v3-core/contracts/interfaces/IUniswapV3Factory.sol"; + +interface IUniswapGovernorBravoDelegator { + function propose( + address[] memory targets, + uint256[] memory values, + string[] memory signatures, + bytes[] memory calldatas, + string memory description + ) external returns (uint256); +} + +contract PolygonExample is Script { + // target address for UniSwap DAO governor bravo delegate + address uniswapGovernorBravoDelegatorAddress = 0x408ED6354d4973f66138C91495F2f2FCbd8724C3; + + // Address of the FxRoot contract Ethereum (L1) + address constant FXROOT_ADDRESS = 0xfe5e5D361b2ad62c541bAb87C45a0B9B018389a2; + + // target addresses on Polygon L2 + address v3FactoryTargetAddress = 0x1F98431c8aD98523631AE4a59f267346ea31F984; + + // function to create a proposal on Ethereum L1 to enable a fee amount on the Uniswap V3 Factory on Polygon L2. + // Upon execution of the proposal, the FxRoot contract function sendMessageToChild is called, containing calldata for the + // uniswap v3 factory to enable a fee amount on Uniswap V3 Factory on Arbitrum L2. + function proposeForExecutionOnPolygonL2() external payable { + // setup calldata for the L2 uniswap v3Factory.enableFeeAmount call + bytes memory _v3FactoryEnableFeeAmounCalldata = + abi.encodeWithSelector(IUniswapV3Factory.enableFeeAmount.selector, 10000, 205); + + // setup calldata for calling FxRoot.sendMessageToChild + bytes memory _proposalCalldata = abi.encode(v3FactoryTargetAddress, _v3FactoryEnableFeeAmounCalldata); + + // make the proposal on the L1 side + IUniswapGovernorBravoDelegator governor = IUniswapGovernorBravoDelegator(uniswapGovernorBravoDelegatorAddress); + address[] memory _targets = new address[](1); + uint256[] memory _values = new uint256[](1); + string[] memory _signatures = new string[](1); + bytes[] memory _calldatas = new bytes[](1); + _targets[0] = FXROOT_ADDRESS; + _values[0] = 0; + _signatures[0] = "sendMessageToChild(address,bytes)"; + _calldatas[0] = _proposalCalldata; + uint256 _proposalId = governor.propose( + _targets, + _values, + _signatures, + _calldatas, + "Proposal to enable 10000 fee amount of 205 on Uniswap V3 Factory on Polygon L2" + ); + console2.log("Proposal ID: %d", _proposalId); + } +} +``` From c3d792b2a47a6ed60f40d392fedcb45a14b4b9e0 Mon Sep 17 00:00:00 2001 From: John Feras Date: Wed, 20 Nov 2024 16:14:15 -0500 Subject: [PATCH 02/16] Removed use of L2 terminolgy --- docs/concepts/governance/02-process.md | 2 +- ...ls-for-L2s.md => multi-chain-proposals.md} | 135 +++++++++--------- 2 files changed, 69 insertions(+), 68 deletions(-) rename docs/concepts/{proposals-for-L2s.md => multi-chain-proposals.md} (76%) diff --git a/docs/concepts/governance/02-process.md b/docs/concepts/governance/02-process.md index f973ca9f6..a43c4819c 100644 --- a/docs/concepts/governance/02-process.md +++ b/docs/concepts/governance/02-process.md @@ -78,7 +78,7 @@ To create an onchain Governance Proposal: 2. Create a topic in the [Governance Forum](https://gov.uniswap.org/) titled "Governance Proposal — [Your Title Here]" and link to previous forum posts and the Temperature Check Snapshot poll. -3. Create your proposal. This can be done either through an interface (e.g. [Tally](https://tally.xyz/gov/uniswap)) or through writing the calldata for more complicated proposal logic. If the proposal passed, this calldata will execute. If writing the calldata yourself, please review the logic with a qualified Uniswap community member prior to posting the proposal. For more information on developing and advancing multi-chain Uniswap Governance Proposals, see [Multi-Chain Governance](../proposals-for-L2s.md). +3. Create your proposal. This can be done either through an interface (e.g. [Tally](https://tally.xyz/gov/uniswap)) or through writing the calldata for more complicated proposal logic. If the proposal passed, this calldata will execute. If writing the calldata yourself, please review the logic with a qualified Uniswap community member prior to posting the proposal. For more information on developing and advancing multi-chain Uniswap Governance Proposals, see [Multi-Chain Governance](../multi-chain-proposals.md). 4. Ensure that at least 1 million UNI is delegated to your address in order to submit a proposal, or find a delegate who has enough delegated UNI to meet the proposal threshold to propose on your behalf. diff --git a/docs/concepts/proposals-for-L2s.md b/docs/concepts/multi-chain-proposals.md similarity index 76% rename from docs/concepts/proposals-for-L2s.md rename to docs/concepts/multi-chain-proposals.md index 026f97856..5b96c23ab 100644 --- a/docs/concepts/proposals-for-L2s.md +++ b/docs/concepts/multi-chain-proposals.md @@ -1,57 +1,62 @@ --- -id: proposals-on-L2 -title: Proposals on L2 +id: multi-chain-proposals +title: Multi-Chain Proposals --- This is a living document which represents the current process guidelines for developing and advancing multi-chain Uniswap Governance Proposals. It was last updated Novenber 2024. ## Introduction -Uniswap V3 has now been deployed across 24 Layer 2 blockchains. However, because Uniswap governance (using GovernorBravo and its associated Timelock contract) executes approved proposals on Ethereum mainnet, implementing changes on these L2 blockchains requires some extra steps in the proposals' executable code. +Uniswap V3 has now been deployed across 24 blockchain networks, including many Layer 2 chains. +However, because Uniswap governance (using GovernorBravo and its associated Timelock contract) executes approved proposals on Ethereum mainnet, implementing changes on these other blockchains requires some extra steps in the proposals' executable code. -For a proposal to successfully enact changes on a Layer 2 chain, the proposal's code must transmit the necessary code effect the change to the specific target L2 for execution. -A variety of mechanisms are used my Uniswap V3 on the individual Layer 2 chains to receive the proposal code, although in some cases, similar or even identical communication methods and contracts are used to bridge proposals from Ethereum mainnet to the target chain. +For a proposal to successfully enact changes on a non-mainnet chain, the proposal's code must transmit the necessary code effect the change to the specific target chain for execution. +A variety of mechanisms are used my Uniswap V3 on the individual non-mainnet chains to receive the proposal code, although in some cases, similar or even identical communication methods and contracts are used to bridge proposals from Ethereum mainnet to the target chain. -This document provides a detailed guide on constructing such proposals for each target L2 chain, including Solidity code snippets for each general approach. +This document provides a detailed guide on constructing such proposals for each target non-mainnet chain, including Solidity code snippets for each general approach. The code snippets are written as Forge/Foundry scripts, and assume they are being run on the Forge platform, in a code repository with the necessary libraries (forge-std, uniswap-v3-core, and uniswap-v3-periphery) imported. It is important to note that the code provided here is a general template and may require modification to suit the specific requirements of the target chain. ## Overview -There are 9 chains where Uniswap V3 is deployed that use Optimism style cross-chain communication, using the `L1CrossDomainMessenger` contract for sending proposals to L2, and the `CrossChainAccount` contract for forwarding the executable proposals to Uniswap V3 on the L2 network. +There are 9 chains where Uniswap V3 is deployed that use Optimism style cross-chain communication, using the `L1CrossDomainMessenger` contract for sending proposals to the destination chain, and the `CrossChainAccount` contract for forwarding the executable proposals to Uniswap V3 on the Optimism-style network. A Solidity code example for Optimism is provided [below](#crosschainaccount-proposal-bridging-example). This code can also be used for sending proposals to the other 8 chains, with minor changes to the actual deployed address constants for the targeted contracts. -There are 4 chains where Uniswap V3 is deployed that use Wormhole for bridging proposals. They all use the same Ethereum mainnet instance of the `UniswapWormholeMessageSender` contract for sending proposals to L2. In addition to the proposal message parameters, the `sendMessage` function of this contract also takes both a target address for the message receiving contract on the L2 chain, as well as the Chain ID of the destination chain as parameters, allowing the function to be used for sending to multiple L2 chains. +There are 4 chains where Uniswap V3 is deployed that use Wormhole for bridging proposals. +They all use the same Ethereum mainnet instance of the `UniswapWormholeMessageSender` contract for sending proposals to the target chain. +In addition to the proposal message parameters, the `sendMessage` function of this contract also takes both a target address for the message receiving contract on the non-mainnet chain, as well as its Chain ID as parameters, allowing the function to be used for sending to multiple destination chains. A Solidity code example for sending a proposal to one these chains (BNB Chain) is provided [below](#wormhole-proposal-bridging-example). This code can be used for sending proposals to the other 3 chains (Gnosis Chain, Moonbeam, and Rootstock), with the only changes needed being the actual depoloyed address constants for the targeted contracts. -Each of the sections below provides a high-level description of the method used to bridge proposals to each target L2 chain. +Each of the sections below provides a high-level description of the method used to bridge proposals to each target non-mainnet chain. #### Arbitrum Cross-chain Communication Arbitrum uses an approach where the owner of the V3Factory is a special aliased address (offset by the value 0x1111000000000000000000000000000000001111) that (when the offset is subtracted away) is the L1 address of the Uniswap DAO Timelock contract. A Solidity code example for sending a proposal to Arbitrum is shown [below](#arbitrum-proposal-bridging-details). -#### AVAX Cross-chain Communication +#### Avalanch/AVAX Cross-chain Communication -AVAX makes use of a contract called `OmnichainGovernanceExecutor`.. TODO: Add details +AVAX makes use of a contract called `OmnichainGovernanceExecutor` to receive proposals from Ethereum mainnet. +From Ethereum mainnet, the contract called LayerZero:EndpointV2 0x1a44076050125825900e736c501f859c50fe728c is used, via function `send` to send proposals to the `OmnichainGovernanceExecutor` contract on AVAX. A Solidity code example for sending a proposal to AVAX is [below](#avax-proposal-bridging-details). #### Base Cross-chain Communication Via CrossChainAccount (Optimism Style) -Base is one of the 9 L2 chains that makes use of the CrossChainAccount contract. +Base is one of the 9 chains that makes use of the CrossChainAccount contract. See the Solidity code example provided for Optimism [below](#optimism-style-proposal-bridging-details). #### Blast Chain Cross-chain Communication Via CrossChainAccount (Optimism Style) -Blast Chain is one of the 9 L2 Chains that makes use of the CrossChainAccount contract. +Blast Chain is one of the 9 chains that makes use of the CrossChainAccount contract. See the Solidity code example provided for Optimism [below](#optimism-style-proposal-bridging-details). #### BNB Chain Cross-chain Communication -BNB Chain is one of the 4 L2 chains that makes use of a contract called `UniswapWormholeMessageReceiver`. On the Ethereum mainnet L1 side, the `UniswapWormholeMessageSender` contract is used to send proposals to the L2 chain. A Solidity code example for sending a proposal to BNB Chain is [below](#wormhole-proposal-bridging-details). +BNB Chain is one of the 4 chains that makes use of a contract called `UniswapWormholeMessageReceiver`. +On the Ethereum mainnet L1 side, the `UniswapWormholeMessageSender` contract is used to send proposals to the destination chain. A Solidity code example for sending a proposal to BNB Chain is [below](#wormhole-proposal-bridging-details). #### Boba Cross-chain Communication Via CrossChainAccount (Optimism Style) -Boba is one of the 9 L2 Chains that makes use of the CrossChainAccount contract. +Boba is one of the 9 chains that makes use of the CrossChainAccount contract. See the Solidity code example provided for Optimism [below](#optimism-style-proposal-bridging-details). #### Celo Chain Cross-chain Communication @@ -64,38 +69,38 @@ Filecoin EVM ?? TODO: Add details #### Gnosis Cross-chain Communication -Gnosis Chain is one of the 4 L2 chains that makes use of a contract called `UniswapWormholeMessageReceiver`. On the Ethereum mainnet L1 side, the `UniswapWormholeMessageSender` contract is used to send proposals to the L2 chain. See the Solidity code example provided for the BNB Chain [below](#wormhole-proposal-bridging-details). +Gnosis Chain is one of the 4 chains that makes use of a contract called `UniswapWormholeMessageReceiver`. On the Ethereum mainnet L1 side, the `UniswapWormholeMessageSender` contract is used to send proposals to the destination chain. See the Solidity code example provided for the BNB Chain [below](#wormhole-proposal-bridging-details). #### Linea Cross-chain Communication Via CrossChainAccount (Optimism Style) -Linea is one of the 9 L2 Chains that makes use of the CrossChainAccount contract. +Linea is one of the 9 chains that makes use of the CrossChainAccount contract. Linea, however, uses a slightly different contracts from L1CrossDomainMessenger, called L1MessageService for sending messages, with different call parameters. See the Linea-specific Solidity code example provided [below](#linea-proposal-bridging-details). #### Manta Pacific Cross-chain Communication Via CrossChainAccount (Optimism Style) -Manta Pacific is one of the 9 L2 Chains that makes use of the CrossChainAccount contract. +Manta Pacific is one of the 9 chains that makes use of the CrossChainAccount contract. See the Solidity code example provided for Optimism [below](#optimism-style-proposal-bridging-details). #### Mantle Cross-chain Communication Via CrossChainAccount (Optimism Style) -Mantle is one of the 9 L2 Chains that makes use of the CrossChainAccount contract. +Mantle is one of the 9 chains that makes use of the CrossChainAccount contract. See the Solidity code example provided for Optimism [below](#optimism-style-proposal-bridging-details). #### Moonbeam Cross-chain Communication -Moonbeam is one of the 4 L2 chains that makes use of a contract called `UniswapWormholeMessageReceiver`. On the Ethereum mainnet L1 side, the `UniswapWormholeMessageSender` contract is used to send proposals to the L2 chain. See the Solidity code example provided for the BNB Chain [below](#wormhole-proposal-bridging-details). +Moonbeam is one of the 4 chains that makes use of a contract called `UniswapWormholeMessageReceiver`. On the Ethereum mainnet L1 side, the `UniswapWormholeMessageSender` contract is used to send proposals to the destination chain. See the Solidity code example provided for the BNB Chain [below](#wormhole-proposal-bridging-details). #### Optimism Cross-chain Communication Via CrossChainAccount (Optimism Style) -Optimism is one of the 9 L2 Chains that makes use of the CrossChainAccount contract. +Optimism is one of the 9 chains that makes use of the CrossChainAccount contract. See the Solidity code example provided for Optimism [below](#optimism-style-proposal-bridging-details). #### Polygon Cross-chain Communication -Polygon makes use of an L1 contract called FxRoot for sending messages (in the case of Uniswap, executable proposals), and contracts called FxChild and EthereumProxy on the Polygon L2 for forwarding the executable proposals to Uniswap V3 on Polygon. A Solidity code example for sending a proposal to Polygon is [below](#polygon-ethereumproxy-proposal-bridging-details). +Polygon makes use of an L1 contract called FxRoot for sending messages (in the case of Uniswap, executable proposals), and contracts called FxChild and EthereumProxy on the Polygon chain for forwarding the executable proposals to Uniswap V3 on Polygon. A Solidity code example for sending a proposal to Polygon is [below](#polygon-ethereumproxy-proposal-bridging-details). #### Polygon zkEVM Cross-chain Communication @@ -103,12 +108,12 @@ Polygon zkEVM uses a different approach than Polygon TODO: Add details #### Redstone Cross-chain Communication Via CrossChainAccount (Optimism Style) -Redstone is one of the 9 L2 Chains that makes use of the CrossChainAccount contract. +Redstone is one of the 9 chains that makes use of the CrossChainAccount contract. See the Solidity code example provided for Optimism [below](#optimism-style-proposal-bridging-details). #### Rootstock Cross-chain Communication -Rootstock is one of the 4 L2 chains that makes use of a contract called `UniswapWormholeMessageReceiver`. On the Ethereum mainnet L1 side, the `UniswapWormholeMessageSender` contract is used to send proposals to the L2 chain. See the Solidity code example provided for the BNB Chain [below](#wormhole-proposal-bridging-details). +Rootstock is one of the 4 chains that makes use of a contract called `UniswapWormholeMessageReceiver`. On the Ethereum mainnet L1 side, the `UniswapWormholeMessageSender` contract is used to send proposals to the destination chain. See the Solidity code example provided for the BNB Chain [below](#wormhole-proposal-bridging-details). #### Scroll Cross-chain Communication @@ -129,11 +134,11 @@ Worldcoin makes use of an unverified contract. TODO: Add details #### ZkSync Era Cross-chain Communication -ZkSync Era uses the Arbitrum-style approach where the parent of the V3Factory contract is an L2 aliased address, but the method of sending the proposal is different than the one used for Arbitrum. TODO: Add details +ZkSync Era uses the Arbitrum-style approach where the parent of the V3Factory contract is an aliased address, but the method of sending the proposal is different than the one used for Arbitrum. TODO: Add details #### Zora Cross-chain Communication Via CrossChainAccount (Optimism Style) -Zora is one of the 9 L2 Chains that makes use of the CrossChainAccount contract. +Zora is one of the 9 chains that makes use of the CrossChainAccount contract. See the Solidity code example provided for Optimism [below](#optimism-style-proposal-bridging-details). @@ -164,22 +169,22 @@ contract EthereumToArbitrumSender is Script { // Address of the Arbitrum Inbox on Ethereum (L1) address constant INBOX_ADDRESS = 0x4Dbd4fc535Ac27206064B68FfCf827b0A60BAB3f; - // target addresses on Arbitrum L2 + // target addresses on Arbitrum address v3FactoryTargetAddress = 0x1F98431c8aD98523631AE4a59f267346ea31F984; - // function to create a proposal on Ethereum L1 to enable a fee amount on the Uniswap V3 Factory on Arbitrum L2. + // function to create a proposal on Ethereum L1 to enable a fee amount on the Uniswap V3 Factory on Arbitrum. // Upon execution of the proposal, the Inbox contract function createRetryableTicket is called, containing calldata for the - // uniswap v3 factory to enable a fee amount on Uniswap V3 Factory on Arbitrum L2 - function proposeForExecutionOnArbitrumL2() external payable { - // setup calldata for the L2 uniswap v3Factory.enableFeeAmount call + // uniswap v3 factory to enable a fee amount on Uniswap V3 Factory on Arbitrum + function proposeForExecutionOnArbitrum() external payable { + // setup calldata for the Arbitrum uniswap v3Factory.enableFeeAmount call bytes memory _v3FactoryEnableFeeAmounCalldata = abi.encodeWithSelector(IUniswapV3Factory.enableFeeAmount.selector, 10000, 205); // setup calldata for the creating retryable ticket - uint256 l2CallValue = 0; // Amount of ETH to send with the call on L2 + uint256 callValue = 0; // Amount of ETH to send with the call on Arbitrum uint256 maxSubmissionCost = 0.01 ether; // Estimated cost for submission - uint256 maxGas = 1_000_000; // Maximum gas for L2 execution - uint256 gasPriceBid = 1 gwei; // Gas price for L2 execution + uint256 maxGas = 1_000_000; // Maximum gas for Arbitrum execution + uint256 gasPriceBid = 1 gwei; // Gas price for Arbitrum execution // Define refund addresses for excess fees and call value address excessFeeRefundAddress = msg.sender; @@ -188,7 +193,7 @@ contract EthereumToArbitrumSender is Script { // setup calldata for the proposal bytes memory _proposalCalldata = abi.encode( v3FactoryTargetAddress, - l2CallValue, + callValue, maxSubmissionCost, excessFeeRefundAddress, callValueRefundAddress, @@ -213,7 +218,7 @@ contract EthereumToArbitrumSender is Script { _values, _signatures, _calldatas, - "Proposal to enable 10000 fee amount of 205 on Uniswap V3 Factory on Arbitrum L2" + "Proposal to enable 10000 fee amount of 205 on Uniswap V3 Factory on Arbitrum" ); console2.log("Proposal ID: %d", _proposalId); } @@ -245,23 +250,23 @@ interface IUniswapGovernorBravoDelegator { } contract OptimismExample is Script { - // target address for the cross domain messenger on Ethereum L1 responsible for sending messages to Optimism L2 + // target address for the cross domain messenger on Ethereum L1 responsible for sending messages to Optimism // (this address would be different for Base, Blast, or Zora) address constant l1CrossDomainMessengerAddress = 0x25ace71c97B33Cc4729CF772ae268934F7ab5fA1; - // target addresses on Optimism L2 (these addresses would be different for Base, Blast, or Zora) + // target addresses on Optimism (these addresses would be different for Base, Blast, or Zora) address constant v3FactoryTargetAddress = 0x1F98431c8aD98523631AE4a59f267346ea31F984; address constant crossChainAccountTargetAddress = 0xa1dD330d602c32622AA270Ea73d078B803Cb3518; // target address for UniSwap DAO governor bravo delegate contract on Ethereum L1 address uniswapGovernorBravoDelegatorAddress = 0x408ED6354d4973f66138C91495F2f2FCbd8724C3; - // function to create a proposal on Ethereum L1 to enable a fee amount on the Uniswap V3 Factory on Optimism L2. + // function to create a proposal on Ethereum L1 to enable a fee amount on the Uniswap V3 Factory on Optimism. // Upon execution of the proposal, the L1CrossDomainMessenger function sendMessage is called containing calldata for the - // CrossChainAccount.forward function on Optimism L2 will be called containing calldata for the uniswap v3 factory - // to enable a fee amount on Uniswap V3 Factory on Optimism L2 - function proposeForExecutionOnOptimismL2() public { - // setup calldata for the L2 uniswap v3Factory.enableFeeAmount call + // CrossChainAccount.forward function on Optimism that will be called containing calldata for the uniswap v3 factory + // to enable a fee amount on Uniswap V3 Factory on Optimism + function proposeForExecutionOnOptimism() public { + // setup calldata for the Optimism uniswap v3Factory.enableFeeAmount call bytes memory _v3FactoryEnableFeeAmounCalldata = abi.encodeWithSelector(IUniswapV3Factory.enableFeeAmount.selector, 10000, 205); @@ -285,7 +290,7 @@ contract OptimismExample is Script { _values, _signatures, _calldatas, - "Proposal to enable 10000 fee amount of 205 on Uniswap V3 Factory on Optimism L2" + "Proposal to enable 10000 fee amount of 205 on Uniswap V3 Factory on Optimism" ); console2.log("Proposal ID: %d", _proposalId); } @@ -336,10 +341,10 @@ For Zora: ## Wormhole Proposal Bridging Details -To create a proposal that (when successfully passed, queued, and executed) would effect a change on Uniswap V3 on an L2 chain via wormhole, the proposal would have to contain a call to the function called `sendMessage` on the one instance of the `UniswapWormholeMessageSender` contract on Ethereum mainnet. -In addition to the proposal message parameters, the function of also takes both a target address for the message receiving contract on the L2 chain, as well as the Chain ID of the destination chain as parameters, allowing the function to be used for sending to multiple L2 chains. +To create a proposal that (when successfully passed, queued, and executed) would effect a change on Uniswap V3 on a destination chain via wormhole, the proposal would have to contain a call to the function called `sendMessage` on the one instance of the `UniswapWormholeMessageSender` contract on Ethereum mainnet. +In addition to the proposal message parameters, the function also takes both a target address for the message receiving contract on the destination chain, as well as the Chain ID of the destination chain as parameters, allowing the function to be used for sending to multiple destination chains. -There are 4 chains where Uniswap V3 is deployed that use the same Ethereum mainnet instance of the `UniswapWormholeMessageSender` contract for sending proposals to L2. +There are 4 chains where Uniswap V3 is deployed that use the same Ethereum mainnet instance of the `UniswapWormholeMessageSender` contract for sending proposals to the target. A Solidity code example for sending a proposal to one these chains (BNB Chain) is provided below. This code can be used for sending proposals to the other 3 chains, with the only changes needed being the actual depoloyed address constants for the targeted contracts. @@ -363,27 +368,27 @@ interface IUniswapGovernorBravoDelegator { ) external returns (uint256); } -contract EthereumToAPolygonSender is Script { +contract EthereumToBnbChainSender is Script { // target address for UniSwap DAO governor bravo delegate address uniswapGovernorBravoDelegatorAddress = 0x408ED6354d4973f66138C91495F2f2FCbd8724C3; // Address of the WormholdMessageSender contract on Ethereum (L1) address constant WORMHOLE_MESSAGE_SENDER_ADDRESS = 0xf5F4496219F31CDCBa6130B5402873624585615a; - // Address of the WormholeMessageReceiver contract on BNB Chain (L2) + // Address of the WormholeMessageReceiver contract on BNB Chain address constant WORMHOLE_MESSAGE_RECEIVER_ADDRESS = 0x341c1511141022cf8eE20824Ae0fFA3491F1302b; - // BNB Chain (L2) chainId + // BNB Chain chainId uint256 constant CHAIN_ID = 56; - // target addresses on BNB Chain L2 + // target addresses on BNB Chain address v3FactoryTargetAddress = 0xdB1d10011AD0Ff90774D0C6Bb92e5C5c8b4461F7; - // function to create a proposal on Ethereum L1 to enable a fee amount on the Uniswap V3 Factory on BNB Chain L2. + // function to create a proposal on Ethereum L1 to enable a fee amount on the Uniswap V3 Factory on BNB Chain. // Upon execution of the proposal, the WormholeMessageSender contract function sendMessage is called, containing calldata for the - // uniswap v3 factory to enable a fee amount on Uniswap V3 Factory on Arbitrum L2. - function proposeForExecutionOnBnbChainL2() external payable { - // setup calldata for the L2 uniswap v3Factory.enableFeeAmount call + // uniswap v3 factory to enable a fee amount on Uniswap V3 Factory on BNB Chain. + function proposeForExecutionOnBnbChain() external payable { + // setup calldata for the destination chain uniswap v3Factory.enableFeeAmount call bytes memory _v3FactoryEnableFeeAmounCalldata = abi.encodeWithSelector(IUniswapV3Factory.enableFeeAmount.selector, 10000, 205); @@ -395,11 +400,7 @@ contract EthereumToAPolygonSender is Script { _sendMessageValues[0] = 0; _sendMessageCalldatas[0] = _v3FactoryEnableFeeAmounCalldata; bytes memory _sendMessageCalldata = abi.encode( - _sendMessageTargets, - _sendMessageValues, - _sendMessageCalldatas, - WORMHOLE_MESSAGE_RECEIVER_ADDRESS, - CHAIN_ID + _sendMessageTargets, _sendMessageValues, _sendMessageCalldatas, WORMHOLE_MESSAGE_RECEIVER_ADDRESS, CHAIN_ID ); // make the proposal on the L1 side @@ -417,7 +418,7 @@ contract EthereumToAPolygonSender is Script { _values, _signatures, _calldatas, - "Proposal to enable 10000 fee amount of 205 on Uniswap V3 Factory on Polygon L2" + "Proposal to enable 10000 fee amount of 205 on Uniswap V3 Factory on BNB Chain" ); console2.log("Proposal ID: %d", _proposalId); } @@ -469,14 +470,14 @@ contract PolygonExample is Script { // Address of the FxRoot contract Ethereum (L1) address constant FXROOT_ADDRESS = 0xfe5e5D361b2ad62c541bAb87C45a0B9B018389a2; - // target addresses on Polygon L2 + // target addresses on Polygon address v3FactoryTargetAddress = 0x1F98431c8aD98523631AE4a59f267346ea31F984; - // function to create a proposal on Ethereum L1 to enable a fee amount on the Uniswap V3 Factory on Polygon L2. + // function to create a proposal on Ethereum L1 to enable a fee amount on the Uniswap V3 Factory on Polygon. // Upon execution of the proposal, the FxRoot contract function sendMessageToChild is called, containing calldata for the - // uniswap v3 factory to enable a fee amount on Uniswap V3 Factory on Arbitrum L2. - function proposeForExecutionOnPolygonL2() external payable { - // setup calldata for the L2 uniswap v3Factory.enableFeeAmount call + // uniswap v3 factory to enable a fee amount on Uniswap V3 Factory on Polygon. + function proposeForExecutionOnPolygon() external payable { + // setup calldata for the uniswap v3Factory.enableFeeAmount call bytes memory _v3FactoryEnableFeeAmounCalldata = abi.encodeWithSelector(IUniswapV3Factory.enableFeeAmount.selector, 10000, 205); @@ -498,7 +499,7 @@ contract PolygonExample is Script { _values, _signatures, _calldatas, - "Proposal to enable 10000 fee amount of 205 on Uniswap V3 Factory on Polygon L2" + "Proposal to enable 10000 fee amount of 205 on Uniswap V3 Factory on Polygon" ); console2.log("Proposal ID: %d", _proposalId); } From 05eeb25b8204f318b66eaf87fb2d38597c1a5480 Mon Sep 17 00:00:00 2001 From: John Feras Date: Mon, 2 Dec 2024 11:05:41 -0500 Subject: [PATCH 03/16] Added examples for more chains --- docs/concepts/multi-chain-proposals.md | 362 +++++++++++++++++++++++-- 1 file changed, 337 insertions(+), 25 deletions(-) diff --git a/docs/concepts/multi-chain-proposals.md b/docs/concepts/multi-chain-proposals.md index 5b96c23ab..ca7e6ecf8 100644 --- a/docs/concepts/multi-chain-proposals.md +++ b/docs/concepts/multi-chain-proposals.md @@ -19,10 +19,10 @@ It is important to note that the code provided here is a general template and ma ## Overview -There are 9 chains where Uniswap V3 is deployed that use Optimism style cross-chain communication, using the `L1CrossDomainMessenger` contract for sending proposals to the destination chain, and the `CrossChainAccount` contract for forwarding the executable proposals to Uniswap V3 on the Optimism-style network. +There are 10 chains where Uniswap V3 is deployed that use Optimism style cross-chain communication, using the `L1CrossDomainMessenger` contract for sending proposals to the destination chain, and the `CrossChainAccount` contract for forwarding the executable proposals to Uniswap V3 on the Optimism-style network. A Solidity code example for Optimism is provided [below](#crosschainaccount-proposal-bridging-example). This code can also be used for sending proposals to the other 8 chains, with minor changes to the actual deployed address constants for the targeted contracts. -There are 4 chains where Uniswap V3 is deployed that use Wormhole for bridging proposals. +There are 5 chains where Uniswap V3 is deployed that use Wormhole for bridging proposals. They all use the same Ethereum mainnet instance of the `UniswapWormholeMessageSender` contract for sending proposals to the target chain. In addition to the proposal message parameters, the `sendMessage` function of this contract also takes both a target address for the message receiving contract on the non-mainnet chain, as well as its Chain ID as parameters, allowing the function to be used for sending to multiple destination chains. A Solidity code example for sending a proposal to one these chains (BNB Chain) is provided [below](#wormhole-proposal-bridging-example). @@ -37,7 +37,7 @@ Arbitrum uses an approach where the owner of the V3Factory is a special aliased #### Avalanch/AVAX Cross-chain Communication AVAX makes use of a contract called `OmnichainGovernanceExecutor` to receive proposals from Ethereum mainnet. -From Ethereum mainnet, the contract called LayerZero:EndpointV2 0x1a44076050125825900e736c501f859c50fe728c is used, via function `send` to send proposals to the `OmnichainGovernanceExecutor` contract on AVAX. A Solidity code example for sending a proposal to AVAX is [below](#avax-proposal-bridging-details). +From Ethereum mainnet, the contract called LayerZero:EndpointV2 at 0x1a44076050125825900e736c501f859c50fe728c is used, via function `send` to send proposals to the `OmnichainGovernanceExecutor` contract on AVAX. A Solidity code example for sending a proposal to AVAX is [below](#avax-proposal-bridging-details). #### Base Cross-chain Communication Via CrossChainAccount (Optimism Style) @@ -51,7 +51,7 @@ See the Solidity code example provided for Optimism [below](#optimism-style-prop #### BNB Chain Cross-chain Communication -BNB Chain is one of the 4 chains that makes use of a contract called `UniswapWormholeMessageReceiver`. +BNB Chain is one of the 5 chains that makes use of a contract called `UniswapWormholeMessageReceiver`. On the Ethereum mainnet L1 side, the `UniswapWormholeMessageSender` contract is used to send proposals to the destination chain. A Solidity code example for sending a proposal to BNB Chain is [below](#wormhole-proposal-bridging-details). #### Boba Cross-chain Communication Via CrossChainAccount (Optimism Style) @@ -61,7 +61,9 @@ See the Solidity code example provided for Optimism [below](#optimism-style-prop #### Celo Chain Cross-chain Communication -Celo Chain makes use of a contract called: Unverified contract TODO: Add details +Celo is one of the 5 chains that makes use of a contract called `UniswapWormholeMessageReceiver`. +On the Ethereum mainnet L1 side, the `UniswapWormholeMessageSender` contract is used to send proposals to the destination chain. A Solidity code example for sending a proposal to BNB Chain is [below](#wormhole-proposal-bridging-details). + #### Filecoin EVM Cross-chain Communication @@ -69,7 +71,7 @@ Filecoin EVM ?? TODO: Add details #### Gnosis Cross-chain Communication -Gnosis Chain is one of the 4 chains that makes use of a contract called `UniswapWormholeMessageReceiver`. On the Ethereum mainnet L1 side, the `UniswapWormholeMessageSender` contract is used to send proposals to the destination chain. See the Solidity code example provided for the BNB Chain [below](#wormhole-proposal-bridging-details). +Gnosis Chain is one of the 5 chains that makes use of a contract called `UniswapWormholeMessageReceiver`. On the Ethereum mainnet L1 side, the `UniswapWormholeMessageSender` contract is used to send proposals to the destination chain. See the Solidity code example provided for the BNB Chain [below](#wormhole-proposal-bridging-details). #### Linea Cross-chain Communication Via CrossChainAccount (Optimism Style) @@ -90,7 +92,7 @@ See the Solidity code example provided for Optimism [below](#optimism-style-prop #### Moonbeam Cross-chain Communication -Moonbeam is one of the 4 chains that makes use of a contract called `UniswapWormholeMessageReceiver`. On the Ethereum mainnet L1 side, the `UniswapWormholeMessageSender` contract is used to send proposals to the destination chain. See the Solidity code example provided for the BNB Chain [below](#wormhole-proposal-bridging-details). +Moonbeam is one of the 5 chains that makes use of a contract called `UniswapWormholeMessageReceiver`. On the Ethereum mainnet L1 side, the `UniswapWormholeMessageSender` contract is used to send proposals to the destination chain. See the Solidity code example provided for the BNB Chain [below](#wormhole-proposal-bridging-details). #### Optimism Cross-chain Communication Via CrossChainAccount (Optimism Style) @@ -113,28 +115,33 @@ See the Solidity code example provided for Optimism [below](#optimism-style-prop #### Rootstock Cross-chain Communication -Rootstock is one of the 4 chains that makes use of a contract called `UniswapWormholeMessageReceiver`. On the Ethereum mainnet L1 side, the `UniswapWormholeMessageSender` contract is used to send proposals to the destination chain. See the Solidity code example provided for the BNB Chain [below](#wormhole-proposal-bridging-details). +Rootstock is one of the 5 chains that makes use of a contract called `UniswapWormholeMessageReceiver`. On the Ethereum mainnet L1 side, the `UniswapWormholeMessageSender` contract is used to send proposals to the destination chain. See the Solidity code example provided for the BNB Chain [below](#wormhole-proposal-bridging-details). #### Scroll Cross-chain Communication -Scroll makes use of a contract called: GnosisSafeProxy TODO: Add details +Scroll also makes use of the CrossChainAccount contract for receiving proposals from Ethereum mainnet, but the way that messages are sent is different from the Optimism-style approach. +A contract called L1ScrollMessenger is used to send messages to the destination chain. +See the Solidity code example provided for Scroll [below](#scroll-proposal-bridging-details). #### Sei Cross-chain Communication -Sei does not have a contract for sending messages. TODO: Add details +Sei does not have a contract for sending messages. The owner of the V3Factory is an EOA. TODO: Add details #### Taiko Cross-chain Communication -Taiko makes use of a contract called: InvokableAccount TODO: Add details +Taiko makes use of a contract called: InvokableAccount. On the Ethereum mainnet L1 side, a contract called SignalService has an "emitSignal" function that must be called with appropriate calldata by the successful proposal to effect a change on the Taiko target chain. +See the Solidity code example provided for Taiko [below](#taiko-proposal-bridging-details). -#### Worldcoin Cross-chain Communication +#### Worldcoin Cross-chain Communication (Optimism Style) -Worldcoin makes use of an unverified contract. TODO: Add details +Worldcoin is one of the 10 chains that makes use of the CrossChainAccount contract. +See the Solidity code example provided for Optimism [below](#optimism-style-proposal-bridging-details). #### ZkSync Era Cross-chain Communication -ZkSync Era uses the Arbitrum-style approach where the parent of the V3Factory contract is an aliased address, but the method of sending the proposal is different than the one used for Arbitrum. TODO: Add details +ZkSync Era uses the Arbitrum-style approach where the parent of the V3Factory contract is an aliased address, but the method of sending the proposal is different than the one used for Arbitrum. +See the Solidity code example provided for ZkSync Era [below](#zksync-era-proposal-bridging-details). #### Zora Cross-chain Communication Via CrossChainAccount (Optimism Style) @@ -224,6 +231,84 @@ contract EthereumToArbitrumSender is Script { } } ``` +## Avax Proposal Bridging Details + +The Solidity script code example below show how to use the `send` function of the `LayerZero:EndpointV2` contract on Ethereum mainnet to send a proposal to the `OmnichainGovernanceExecutor` contract on AVAX. +``` +// SPDX-License-Identifier: UNLICENSED +pragma solidity ^0.8.28; + +import {console2} from "forge-std/console2.sol"; +import {Script} from "forge-std/script.sol"; +import {IUniswapV3Factory} from "lib/v3-core/contracts/interfaces/IUniswapV3Factory.sol"; + +interface IUniswapGovernorBravoDelegator { + // function to create a proposal on Ethereum L1 using the Uniswap Governor Bravo Delegator contract + function propose( + address[] memory targets, + uint256[] memory values, + string[] memory signatures, + bytes[] memory calldatas, + string memory description + ) external returns (uint256); +} + +contract EthereumToAvaxSender is Script { + // target address for UniSwap DAO governor bravo delegate + address uniswapGovernorBravoDelegatorAddress = 0x408ED6354d4973f66138C91495F2f2FCbd8724C3; + + // Address of the LayerZero:EndpointV2 contract on Ethereum (L1) + address constant LAYERZERO_ENDPOINT_ADDRESS = 0x1a44076050125825900e736c501f859c50fE728c; + + // Address of the OmniChainExecutor contract on AVAX + address constant OMNICHAIN_EXECUTOR_ADDRESS = 0x341c1511141022cf8eE20824Ae0fFA3491F1302b; + + // target addresses on AVAX + address v3FactoryTargetAddress = 0x740b1c1de25031C31FF4fC9A62f554A55cdC1baD; + + // AVAX chainId + uint256 constant CHAIN_ID = 43114; + + // function to create a proposal on Ethereum L1 to enable a fee amount on the Uniswap V3 Factory on AVAX. + // Upon execution of the proposal, the WormholeMessageSender contract function sendMessage is called, containing calldata for the + // uniswap v3 factory to enable a fee amount on Uniswap V3 Factory on AVAX. + function proposeForExecutionOnAvax() external payable { + // setup calldata for the AVAX uniswap v3Factory.enableFeeAmount call + bytes memory _v3FactoryEnableFeeAmounCalldata = + abi.encodeWithSelector(IUniswapV3Factory.enableFeeAmount.selector, 10000, 205); + + // encode the calldata for the LayerZeroEndpoint.send call + bytes memory _sendCalldata = abi.encode( + CHAIN_ID, // Destination chain ID + OMNICHAIN_EXECUTOR_ADDRESS, // Address on the destination chain + _v3FactoryEnableFeeAmounCalldata, // Message payload + address(this), // Refund address for unused gas + address(0), // Address for paying fees in ZRO (if used) + 0 // Parameters for adapter services (e.g., gas limits) + ); + + // make the proposal on the L1 side + IUniswapGovernorBravoDelegator governor = IUniswapGovernorBravoDelegator(uniswapGovernorBravoDelegatorAddress); + address[] memory _targets = new address[](1); + uint256[] memory _values = new uint256[](1); + string[] memory _signatures = new string[](1); + bytes[] memory _calldatas = new bytes[](1); + _targets[0] = LAYERZERO_ENDPOINT_ADDRESS; + _values[0] = 0; + _signatures[0] = "send(uint16,bytes,bytes,address,address,bytes)"; + _calldatas[0] = _sendCalldata; + uint256 _proposalId = governor.propose( + _targets, + _values, + _signatures, + _calldatas, + "Proposal to enable 10000 fee amount of 205 on Uniswap V3 Factory on AVAX" + ); + console2.log("Proposal ID: %d", _proposalId); + } +} +``` + ## Optimism-Style Proposal Bridging Details To create a proposal that (when successfully passed, queued, and executed) would effect a change on Uniswap V3 on Optimism style blockchains, the proposal would have to contain a call to the function called `sendMessage` on an instance of the L1CrossDomainMessenger contract on Ethereum mainnet. The call would have doubly-wrapped calldata for the CrossChainAccount `forward` function as well as the ultimate Uniswap contract function call that would effect the change. An example for Optimism: @@ -254,7 +339,7 @@ contract OptimismExample is Script { // (this address would be different for Base, Blast, or Zora) address constant l1CrossDomainMessengerAddress = 0x25ace71c97B33Cc4729CF772ae268934F7ab5fA1; - // target addresses on Optimism (these addresses would be different for Base, Blast, or Zora) + // target addresses on Optimism (these addresses would be different for various target chains) address constant v3FactoryTargetAddress = 0x1F98431c8aD98523631AE4a59f267346ea31F984; address constant crossChainAccountTargetAddress = 0xa1dD330d602c32622AA270Ea73d078B803Cb3518; @@ -281,7 +366,7 @@ contract OptimismExample is Script { uint256[] memory _values = new uint256[](1); string[] memory _signatures = new string[](1); bytes[] memory _calldatas = new bytes[](1); - _targets[0] = address(l1CrossDomainMessengerAddress); + _targets[0] = l1CrossDomainMessengerAddress; _values[0] = 0; _signatures[0] = "sendMessage(address,bytes,uint256)"; _calldatas[0] = abi.encode(address(crossChainAccountTargetAddress), _messageForwardCalldata, 500_000); @@ -317,7 +402,7 @@ For Linea: * l1CrossDomainMessengerAddress would be: 0xd19d4B5d358258f05D7B411E21A1460D11B0876F * v3FactoryTargetAddress would be: 0x31FAfd4889FA1269F7a13A66eE0fB458f27D72A9 * crossChainAccountTargetAddress would be: 0x581F86Da293A1D5Cd087a10E7227a75d2d2201A8 -* Slightly different sending code for Linea, see section below. TODO +* Slightly different sending code for Linea, with the 'sendMessage' function's `fee` and `message` parameters being swapped. For Manta Pacific: * l1CrossDomainMessengerAddress would be: 0x635ba609680c55C3bDd0B3627b4c5dB21b13c310 @@ -334,6 +419,11 @@ For Redstone: * v3FactoryTargetAddress would be: 0xece75613Aa9b1680f0421E5B2eF376DF68aa83Bb * crossChainAccountTargetAddress would be: 0x2d00e94d78Fc307FC5E6195BBe2fB6aFC2FC07d4 +For Worldcoin: +* l1CrossDomainMessengerAddress would be: 0xf931a81D18B1766d15695ffc7c1920a62b7e710a +* v3FactoryTargetAddress would be: 0x7a5028BDa40e7B173C278C5342087826455ea25a +* crossChainAccountTargetAddress would be: 0xcb2436774C3e191c85056d248EF4260ce5f27A9D + For Zora: * l1CrossDomainMessengerAddress would be: 0xdC40a14d9abd6F410226f1E6de71aE03441ca506 * v3FactoryTargetAddress would be: 0x7145F8aeef1f6510E92164038E1B6F8cB2c42Cbb @@ -369,12 +459,6 @@ interface IUniswapGovernorBravoDelegator { } contract EthereumToBnbChainSender is Script { - // target address for UniSwap DAO governor bravo delegate - address uniswapGovernorBravoDelegatorAddress = 0x408ED6354d4973f66138C91495F2f2FCbd8724C3; - - // Address of the WormholdMessageSender contract on Ethereum (L1) - address constant WORMHOLE_MESSAGE_SENDER_ADDRESS = 0xf5F4496219F31CDCBa6130B5402873624585615a; - // Address of the WormholeMessageReceiver contract on BNB Chain address constant WORMHOLE_MESSAGE_RECEIVER_ADDRESS = 0x341c1511141022cf8eE20824Ae0fFA3491F1302b; @@ -382,7 +466,13 @@ contract EthereumToBnbChainSender is Script { uint256 constant CHAIN_ID = 56; // target addresses on BNB Chain - address v3FactoryTargetAddress = 0xdB1d10011AD0Ff90774D0C6Bb92e5C5c8b4461F7; + address constant v3FactoryTargetAddress = 0xdB1d10011AD0Ff90774D0C6Bb92e5C5c8b4461F7; + + // target address for UniSwap DAO governor bravo delegate + address uniswapGovernorBravoDelegatorAddress = 0x408ED6354d4973f66138C91495F2f2FCbd8724C3; + + // Address of the WormholdMessageSender contract on Ethereum (L1) + address constant WORMHOLE_MESSAGE_SENDER_ADDRESS = 0xf5F4496219F31CDCBa6130B5402873624585615a; // function to create a proposal on Ethereum L1 to enable a fee amount on the Uniswap V3 Factory on BNB Chain. // Upon execution of the proposal, the WormholeMessageSender contract function sendMessage is called, containing calldata for the @@ -424,7 +514,12 @@ contract EthereumToBnbChainSender is Script { } } ``` -Because 4 networks all use the same Wormhole-based approach for communication, the only modifications to the above code snippet for those networks would be to change the first 3 address constant definitions. The definitions for the other 3 networks are below: +Because 5 networks all use the same Wormhole-based approach for communication, the only modifications to the above code snippet for those networks would be to change the first 3 address constant definitions. The definitions for the other 4 networks are below: + +For Celo: +* WORMHOLE_MESSAGE_RECEIVER_ADDRESS would be: 0x0Eb863541278308c3A64F8E908BC646e27BFD071 +* CHAIN_ID would be: 42220 +* V3FactoryTargetAddress would be: 0xAfE208a311B21f13EF87E33A90049fC17A7acDEc For Gnosis: * WORMHOLE_MESSAGE_RECEIVER_ADDRESS would be: 0xfFA5599136fBaB9af7799A6703b57BB33E5390Cf @@ -505,3 +600,220 @@ contract PolygonExample is Script { } } ``` + +## Scroll Proposal Bridging Details + +To create a proposal that (when successfully passed, queued, and executed) would effect a change on Uniswap V3 on the Scroll blockchain, the proposal would have to contain a call to the function called `sendMessage` on an instance of the L1ScrollMessenger contract on Ethereum mainnet. The call would have doubly-wrapped calldata for the CrossChainAccount `forward` function as well as the ultimate Uniswap contract function call that would effect the change. An example: +``` +// SPDX-License-Identifier: UNLICENSED +pragma solidity ^0.8.28; + +import {console2} from "forge-std/console2.sol"; +import {Script} from "forge-std/script.sol"; +import {IUniswapV3Factory} from "lib/v3-core/contracts/interfaces/IUniswapV3Factory.sol"; + +interface ICrossChainAccount { + function forward(address target, bytes memory data) external; +} + +interface IUniswapGovernorBravoDelegator { + function propose( + address[] memory targets, + uint256[] memory values, + string[] memory signatures, + bytes[] memory calldatas, + string memory description + ) external returns (uint256); +} + +contract ScrollExample is Script { + // target address for the cross domain messenger on Ethereum L1 responsible for sending messages to Scroll + // (this address would be different for Base, Blast, or Zora) + address constant L1_SCROLL_MESSENGER_ADDRESS = 0x6774Bcbd5ceCeF1336b5300fb5186a12DDD8b367; + + // target addresses on Scroll + address constant v3FactoryTargetAddress = 0x70C62C8b8e801124A4Aa81ce07b637A3e83cb919; + address constant crossChainAccountTargetAddress = 0x3b441EEa8e042dcB9517b0FC1afFb0d8CBa0f388; + + // target address for UniSwap DAO governor bravo delegate contract on Ethereum L1 + address uniswapGovernorBravoDelegatorAddress = 0x408ED6354d4973f66138C91495F2f2FCbd8724C3; + + // function to create a proposal on Ethereum L1 to enable a fee amount on the Uniswap V3 Factory on Scroll. + // Upon execution of the proposal, the L1ScrollMessenger function sendMessage is called containing calldata for the + // CrossChainAccount.forward function on Scroll that will be called containing calldata for the uniswap v3 factory + // to enable a fee amount on Uniswap V3 Factory on Scroll + function proposeForExecutionOnScroll() public { + // setup calldata for the Scroll uniswap v3Factory.enableFeeAmount call + bytes memory _v3FactoryEnableFeeAmounCalldata = + abi.encodeWithSelector(IUniswapV3Factory.enableFeeAmount.selector, 10000, 205); + + // encode the calldata for the CrossChainAccount.forward call + bytes memory _messageForwardCalldata = abi.encodeWithSelector( + ICrossChainAccount.forward.selector, v3FactoryTargetAddress, _v3FactoryEnableFeeAmounCalldata + ); + + // make the proposal on the L1 side + IUniswapGovernorBravoDelegator governor = IUniswapGovernorBravoDelegator(uniswapGovernorBravoDelegatorAddress); + address[] memory _targets = new address[](1); + uint256[] memory _values = new uint256[](1); + string[] memory _signatures = new string[](1); + bytes[] memory _calldatas = new bytes[](1); + _targets[0] = L1_SCROLL_MESSENGER_ADDRESS; + _values[0] = 0; + _signatures[0] = "sendMessage(address,uint256,bytes,uint256)"; + _calldatas[0] = abi.encode(address(crossChainAccountTargetAddress), 0, _messageForwardCalldata, 500_000); + uint256 _proposalId = governor.propose( + _targets, + _values, + _signatures, + _calldatas, + "Proposal to enable 10000 fee amount of 205 on Uniswap V3 Factory on Scroll" + ); + console2.log("Proposal ID: %d", _proposalId); + } +} +``` + +## Taiko Proposal Bridging Details + +To create a proposal that (when successfully passed, queued, and executed) would effect a change on Uniswap V3 on the Taiko blockchain, the proposal would have to contain a call to the function called `emitSignal` on an instance of the Taiko SignalService contract on Ethereum mainnet. The call would have wrapped calldata for the Uniswap contract function call that would effect the change. An example: +``` +// SPDX-License-Identifier: UNLICENSED +pragma solidity ^0.8.28; + +import {console2} from "forge-std/console2.sol"; +import {Script} from "forge-std/script.sol"; +import {IUniswapV3Factory} from "lib/v3-core/contracts/interfaces/IUniswapV3Factory.sol"; + +interface IUniswapGovernorBravoDelegator { + function propose( + address[] memory targets, + uint256[] memory values, + string[] memory signatures, + bytes[] memory calldatas, + string memory description + ) external returns (uint256); +} + +contract TaikoExample is Script { + // target address for the Taiko Signal Service contract on Ethereum L1 responsible for sending messages to Taiko + address constant TAIKO_SIGNAL_SERVICE_ADDRESS = 0x9e0a24964e5397B566c1ed39258e21aB5E35C77C; + + // target addresses on Taiko + address constant v3FactoryTargetAddress = 0x75FC67473A91335B5b8F8821277262a13B38c9b3; + + // target address for UniSwap DAO governor bravo delegate contract on Ethereum L1 + address uniswapGovernorBravoDelegatorAddress = 0x408ED6354d4973f66138C91495F2f2FCbd8724C3; + + // function to create a proposal on Ethereum L1 to enable a fee amount on the Uniswap V3 Factory on Taiko. + // Upon execution of the proposal, the Signal Service function "emitSignal" is called containing calldata for the + // uniswap v3 factory to enable a fee amount on Uniswap V3 Factory on Taiko. + function proposeForExecutionOnTaiko() public { + // setup calldata for the Taiko uniswap v3Factory.enableFeeAmount call + bytes memory _v3FactoryEnableFeeAmounCalldata = abi.encode(int24(10000), int24(205)); + + // make the proposal on the L1 side + IUniswapGovernorBravoDelegator governor = IUniswapGovernorBravoDelegator(uniswapGovernorBravoDelegatorAddress); + address[] memory _targets = new address[](1); + uint256[] memory _values = new uint256[](1); + string[] memory _signatures = new string[](1); + bytes[] memory _calldatas = new bytes[](1); + _targets[0] = TAIKO_SIGNAL_SERVICE_ADDRESS; + _values[0] = 0; + _signatures[0] = "emitSignal(bytes)"; + _calldatas[0] = abi.encode(address(v3FactoryTargetAddress), _v3FactoryEnableFeeAmounCalldata); + uint256 _proposalId = governor.propose( + _targets, + _values, + _signatures, + _calldatas, + "Proposal to enable 10000 fee amount of 205 on Uniswap V3 Factory on Taiko" + ); + console2.log("Proposal ID: %d", _proposalId); + } +} +``` + +## ZkSync Era Proposal Bridging Details +To create a proposal that (when successfully passed, queued, and executed) would effect a change on Uniswap on ZkSync Era, the proposal would have to contain a call to the function called `requestL2Transaction` of the MailBoxFacet contract that is located at address 0x63b5EC36B09384fFA7106A80Ec7cfdFCa521fD08 on Ethereum mainnet. The call would have wrapped calldata for a Uniswap contract function call that would effect the change. An example: +``` +// SPDX-License-Identifier: UNLICENSED +pragma solidity ^0.8.28; + +import {console2} from "forge-std/console2.sol"; +import {Script} from "forge-std/script.sol"; +import {IUniswapV3Factory} from "lib/v3-core/contracts/interfaces/IUniswapV3Factory.sol"; + +interface IUniswapGovernorBravoDelegator { + function propose( + address[] memory targets, + uint256[] memory values, + string[] memory signatures, + bytes[] memory calldatas, + string memory description + ) external returns (uint256); +} + +contract EthereumToZkSyncSender is Script { + // target address for UniSwap DAO governor bravo delegate + address uniswapGovernorBravoDelegatorAddress = 0x408ED6354d4973f66138C91495F2f2FCbd8724C3; + + // Address of the MailboxFacet contract on Ethereum (L1) + address constant MAILBOX_FACET_ADDRESS = 0x63b5EC36B09384fFA7106A80Ec7cfdFCa521fD08; + + // target addresses on ZkSync Era + address v3FactoryTargetAddress = 0x8FdA5a7a8dCA67BBcDd10F02Fa0649A937215422; + + // function to create a proposal on Ethereum L1 to enable a fee amount on the Uniswap V3 Factory on ZkSync Era. + // Upon execution of the proposal, the MailboxFacet contract function requestL2Transaction is called, containing calldata for the + // uniswap v3 factory to enable a fee amount on Uniswap V3 Factory on ZkSync Era. + function proposeForExecutionOnZkSync() external payable { + // setup calldata for the ZkSync Era uniswap v3Factory.enableFeeAmount call + bytes memory _v3FactoryEnableFeeAmounCalldata = + abi.encodeWithSelector(IUniswapV3Factory.enableFeeAmount.selector, 10000, 205); + + // setup calldata for the creating retryable ticket + uint256 callValue = 0; // Amount of ETH to send with the call on ZkSync Era + uint256 maxSubmissionCost = 0.01 ether; // Estimated cost for submission + uint256 maxGas = 1_000_000; // Maximum gas for ZkSync Era execution + + // Define refund addresses + address refundRecipientAddress = msg.sender; + + // Define known L2 dependency addresses + bytes[] memory knownL2DependentAddresses = new bytes[](1); + + // setup calldata for the proposal + bytes memory _proposalCalldata = abi.encode( + v3FactoryTargetAddress, + callValue, + _v3FactoryEnableFeeAmounCalldata, + maxGas, + maxSubmissionCost, + knownL2DependentAddresses, + refundRecipientAddress + ); + + // make the proposal on the L1 side + IUniswapGovernorBravoDelegator governor = IUniswapGovernorBravoDelegator(uniswapGovernorBravoDelegatorAddress); + address[] memory _targets = new address[](1); + uint256[] memory _values = new uint256[](1); + string[] memory _signatures = new string[](1); + bytes[] memory _calldatas = new bytes[](1); + _targets[0] = MAILBOX_FACET_ADDRESS; + _values[0] = 0; + _signatures[0] = "requestL2Transaction(address,uint256,bytes,uint256,uint256,bytes[],address)"; + _calldatas[0] = _proposalCalldata; + uint256 _proposalId = governor.propose( + _targets, + _values, + _signatures, + _calldatas, + "Proposal to enable 10000 fee amount of 205 on Uniswap V3 Factory on ZkSync Era" + ); + console2.log("Proposal ID: %d", _proposalId); + } +} +``` + + From 858ff4d1200388e3ff79ee6351d0057f647de6c5 Mon Sep 17 00:00:00 2001 From: John Feras Date: Mon, 6 Jan 2025 15:42:38 -0500 Subject: [PATCH 04/16] Refactored for readability --- docs/concepts/multi-chain-proposals.md | 457 +++++++++---------------- 1 file changed, 166 insertions(+), 291 deletions(-) diff --git a/docs/concepts/multi-chain-proposals.md b/docs/concepts/multi-chain-proposals.md index ca7e6ecf8..bf46f1662 100644 --- a/docs/concepts/multi-chain-proposals.md +++ b/docs/concepts/multi-chain-proposals.md @@ -19,138 +19,32 @@ It is important to note that the code provided here is a general template and ma ## Overview -There are 10 chains where Uniswap V3 is deployed that use Optimism style cross-chain communication, using the `L1CrossDomainMessenger` contract for sending proposals to the destination chain, and the `CrossChainAccount` contract for forwarding the executable proposals to Uniswap V3 on the Optimism-style network. -A Solidity code example for Optimism is provided [below](#crosschainaccount-proposal-bridging-example). This code can also be used for sending proposals to the other 8 chains, with minor changes to the actual deployed address constants for the targeted contracts. +Of the 24 chains where Uniswap V3 is deployed there are 10 chains that are OP Stack based and use an `L1CrossDomainMessenger` contract for bridging proposals. +There are 6 chains that use Wormhole for bridging proposals. +The remaining 8 chains use a variety of other methods for bridging proposals, all described below. -There are 5 chains where Uniswap V3 is deployed that use Wormhole for bridging proposals. -They all use the same Ethereum mainnet instance of the `UniswapWormholeMessageSender` contract for sending proposals to the target chain. -In addition to the proposal message parameters, the `sendMessage` function of this contract also takes both a target address for the message receiving contract on the non-mainnet chain, as well as its Chain ID as parameters, allowing the function to be used for sending to multiple destination chains. -A Solidity code example for sending a proposal to one these chains (BNB Chain) is provided [below](#wormhole-proposal-bridging-example). -This code can be used for sending proposals to the other 3 chains (Gnosis Chain, Moonbeam, and Rootstock), with the only changes needed being the actual depoloyed address constants for the targeted contracts. +The table below contains links to the various proposal methods for each chain. -Each of the sections below provides a high-level description of the method used to bridge proposals to each target non-mainnet chain. +| Proposal Method | Chain(s) | +| ----------------------------------------------------- | ------------------------------------------------------------------------------------ | +| [Arbitrum - Aliased Timelock](#arbitrum) | Arbitrum | +| [Avalanche - OmnichainGovernanceExecutor](#avalanche) | Avalanche | +| [Filecoin - TODO](#filecoin) | Filecoin | +| [Op Stack - CrossChainAccount](#op-stack) | Base, Blast, Boba, Linea, Manta Pacific, Mantle, Optimism, Redstone, WorldCoin, Zora | +| [Polygon - FxChild](#polygon) | Polygon | +| [Polygon zkEVM - TODO](#polygon-zkevm) | Polygon zkEVM | +| [Scroll - CrossChainAccount](#scroll) | Scroll | +| [Taiko - InvokableAccount](#taiko) | Taiko | +| [Wormhole](#wormhole) | BNB Chain, Celo, Gnosis Chain, Moonbeam, Rootstock, Sei | +| [ZkSync Era - Aliased Timelock](#zksync-era) | ZkSync Era | -#### Arbitrum Cross-chain Communication +## Arbitrum -Arbitrum uses an approach where the owner of the V3Factory is a special aliased address (offset by the value 0x1111000000000000000000000000000000001111) that (when the offset is subtracted away) is the L1 address of the Uniswap DAO Timelock contract. A Solidity code example for sending a proposal to Arbitrum is shown [below](#arbitrum-proposal-bridging-details). +Arbitrum uses an approach where the owner of the V3Factory is a special aliased address (offset by the value 0x1111000000000000000000000000000000001111) that (when the offset is subtracted away) is the L1 address of the Uniswap DAO Timelock contract. A Solidity code example for sending a proposal to Arbitrum is shown below. -#### Avalanch/AVAX Cross-chain Communication +Proposal calldata intended for Arbitrum should be forwarded through a call to `createRetryableTicket` on the Arbitrum Inbox contract. +The call would have wrapped calldata for a Uniswap contract function call that would effect the change. An example: -AVAX makes use of a contract called `OmnichainGovernanceExecutor` to receive proposals from Ethereum mainnet. -From Ethereum mainnet, the contract called LayerZero:EndpointV2 at 0x1a44076050125825900e736c501f859c50fe728c is used, via function `send` to send proposals to the `OmnichainGovernanceExecutor` contract on AVAX. A Solidity code example for sending a proposal to AVAX is [below](#avax-proposal-bridging-details). - -#### Base Cross-chain Communication Via CrossChainAccount (Optimism Style) - -Base is one of the 9 chains that makes use of the CrossChainAccount contract. -See the Solidity code example provided for Optimism [below](#optimism-style-proposal-bridging-details). - -#### Blast Chain Cross-chain Communication Via CrossChainAccount (Optimism Style) - -Blast Chain is one of the 9 chains that makes use of the CrossChainAccount contract. -See the Solidity code example provided for Optimism [below](#optimism-style-proposal-bridging-details). - -#### BNB Chain Cross-chain Communication - -BNB Chain is one of the 5 chains that makes use of a contract called `UniswapWormholeMessageReceiver`. -On the Ethereum mainnet L1 side, the `UniswapWormholeMessageSender` contract is used to send proposals to the destination chain. A Solidity code example for sending a proposal to BNB Chain is [below](#wormhole-proposal-bridging-details). - -#### Boba Cross-chain Communication Via CrossChainAccount (Optimism Style) - -Boba is one of the 9 chains that makes use of the CrossChainAccount contract. -See the Solidity code example provided for Optimism [below](#optimism-style-proposal-bridging-details). - -#### Celo Chain Cross-chain Communication - -Celo is one of the 5 chains that makes use of a contract called `UniswapWormholeMessageReceiver`. -On the Ethereum mainnet L1 side, the `UniswapWormholeMessageSender` contract is used to send proposals to the destination chain. A Solidity code example for sending a proposal to BNB Chain is [below](#wormhole-proposal-bridging-details). - - -#### Filecoin EVM Cross-chain Communication - -Filecoin EVM ?? TODO: Add details - -#### Gnosis Cross-chain Communication - -Gnosis Chain is one of the 5 chains that makes use of a contract called `UniswapWormholeMessageReceiver`. On the Ethereum mainnet L1 side, the `UniswapWormholeMessageSender` contract is used to send proposals to the destination chain. See the Solidity code example provided for the BNB Chain [below](#wormhole-proposal-bridging-details). - - -#### Linea Cross-chain Communication Via CrossChainAccount (Optimism Style) - -Linea is one of the 9 chains that makes use of the CrossChainAccount contract. -Linea, however, uses a slightly different contracts from L1CrossDomainMessenger, called L1MessageService for sending messages, -with different call parameters. See the Linea-specific Solidity code example provided [below](#linea-proposal-bridging-details). - -#### Manta Pacific Cross-chain Communication Via CrossChainAccount (Optimism Style) - -Manta Pacific is one of the 9 chains that makes use of the CrossChainAccount contract. -See the Solidity code example provided for Optimism [below](#optimism-style-proposal-bridging-details). - -#### Mantle Cross-chain Communication Via CrossChainAccount (Optimism Style) - -Mantle is one of the 9 chains that makes use of the CrossChainAccount contract. -See the Solidity code example provided for Optimism [below](#optimism-style-proposal-bridging-details). - -#### Moonbeam Cross-chain Communication - -Moonbeam is one of the 5 chains that makes use of a contract called `UniswapWormholeMessageReceiver`. On the Ethereum mainnet L1 side, the `UniswapWormholeMessageSender` contract is used to send proposals to the destination chain. See the Solidity code example provided for the BNB Chain [below](#wormhole-proposal-bridging-details). - - -#### Optimism Cross-chain Communication Via CrossChainAccount (Optimism Style) - -Optimism is one of the 9 chains that makes use of the CrossChainAccount contract. -See the Solidity code example provided for Optimism [below](#optimism-style-proposal-bridging-details). - -#### Polygon Cross-chain Communication - -Polygon makes use of an L1 contract called FxRoot for sending messages (in the case of Uniswap, executable proposals), and contracts called FxChild and EthereumProxy on the Polygon chain for forwarding the executable proposals to Uniswap V3 on Polygon. A Solidity code example for sending a proposal to Polygon is [below](#polygon-ethereumproxy-proposal-bridging-details). - -#### Polygon zkEVM Cross-chain Communication - -Polygon zkEVM uses a different approach than Polygon TODO: Add details - -#### Redstone Cross-chain Communication Via CrossChainAccount (Optimism Style) - -Redstone is one of the 9 chains that makes use of the CrossChainAccount contract. -See the Solidity code example provided for Optimism [below](#optimism-style-proposal-bridging-details). - -#### Rootstock Cross-chain Communication - -Rootstock is one of the 5 chains that makes use of a contract called `UniswapWormholeMessageReceiver`. On the Ethereum mainnet L1 side, the `UniswapWormholeMessageSender` contract is used to send proposals to the destination chain. See the Solidity code example provided for the BNB Chain [below](#wormhole-proposal-bridging-details). - - -#### Scroll Cross-chain Communication - -Scroll also makes use of the CrossChainAccount contract for receiving proposals from Ethereum mainnet, but the way that messages are sent is different from the Optimism-style approach. -A contract called L1ScrollMessenger is used to send messages to the destination chain. -See the Solidity code example provided for Scroll [below](#scroll-proposal-bridging-details). - -#### Sei Cross-chain Communication - -Sei does not have a contract for sending messages. The owner of the V3Factory is an EOA. TODO: Add details - -#### Taiko Cross-chain Communication - -Taiko makes use of a contract called: InvokableAccount. On the Ethereum mainnet L1 side, a contract called SignalService has an "emitSignal" function that must be called with appropriate calldata by the successful proposal to effect a change on the Taiko target chain. -See the Solidity code example provided for Taiko [below](#taiko-proposal-bridging-details). - -#### Worldcoin Cross-chain Communication (Optimism Style) - -Worldcoin is one of the 10 chains that makes use of the CrossChainAccount contract. -See the Solidity code example provided for Optimism [below](#optimism-style-proposal-bridging-details). - -#### ZkSync Era Cross-chain Communication - -ZkSync Era uses the Arbitrum-style approach where the parent of the V3Factory contract is an aliased address, but the method of sending the proposal is different than the one used for Arbitrum. -See the Solidity code example provided for ZkSync Era [below](#zksync-era-proposal-bridging-details). - -#### Zora Cross-chain Communication Via CrossChainAccount (Optimism Style) - -Zora is one of the 9 chains that makes use of the CrossChainAccount contract. -See the Solidity code example provided for Optimism [below](#optimism-style-proposal-bridging-details). - - -## Arbitrum Proposal Bridging Details -To create a proposal that (when successfully passed, queued, and executed) would effect a change on Uniswap on Arbitrum, the proposal would have to contain a call to the function called `createRetryableTicket` of the Inbox contract that is located at address 0x4Dbd4fc535Ac27206064B68FfCf827b0A60BAB3f on Ethereum mainnet. The call would have wrapped calldata for a Uniswap contract function call that would effect the change. An example: ``` // SPDX-License-Identifier: UNLICENSED pragma solidity ^0.8.28; @@ -231,9 +125,13 @@ contract EthereumToArbitrumSender is Script { } } ``` -## Avax Proposal Bridging Details -The Solidity script code example below show how to use the `send` function of the `LayerZero:EndpointV2` contract on Ethereum mainnet to send a proposal to the `OmnichainGovernanceExecutor` contract on AVAX. +## Avalanche + +Avalanche makes use of a contract called `OmnichainGovernanceExecutor` to receive proposals from Ethereum mainnet. +Proposal calldata on mainnet intended for Avalanche should be forwarded through a call to `send` on the contract called LayerZero:EndpointV2. +A Solidity code example for sending a proposal to AVAX is below. + ``` // SPDX-License-Identifier: UNLICENSED pragma solidity ^0.8.28; @@ -309,9 +207,15 @@ contract EthereumToAvaxSender is Script { } ``` -## Optimism-Style Proposal Bridging Details +## Filecoin + +Filecoin EVM ?? TODO: Add details + +## OP Stack + +Proposal calldata on mainnet meant for an OP stack chain should be forwarded through a call to sendMessage on the chain's corresponding mainnet L1CrossDomainMessenger contract. +The call would have doubly-wrapped calldata for the CrossChainAccount `forward` function as well as the ultimate Uniswap contract function call that would effect the change. An example for Optimism: -To create a proposal that (when successfully passed, queued, and executed) would effect a change on Uniswap V3 on Optimism style blockchains, the proposal would have to contain a call to the function called `sendMessage` on an instance of the L1CrossDomainMessenger contract on Ethereum mainnet. The call would have doubly-wrapped calldata for the CrossChainAccount `forward` function as well as the ultimate Uniswap contract function call that would effect the change. An example for Optimism: ``` // SPDX-License-Identifier: UNLICENSED pragma solidity ^0.8.28; @@ -381,165 +285,30 @@ contract OptimismExample is Script { } } ``` -Because 9 networks all use the same CrossChainAccount approach for communication, the only modifications to the above code snippet for those networks would be to change the first 3 address constant definitions. The definitions for the other 8 networks are below: - -For Base: -* l1CrossDomainMessengerAddress would be: 0x866E82a600A1414e583f7F13623F1aC5d58b0Afa -* v3FactoryTargetAddress would be: 0x33128a8fC17869897dcE68Ed026d694621f6FDfD -* crossChainAccountTargetAddress would be: 0x31FAfd4889FA1269F7a13A66eE0fB458f27D72A9 - -For Blast: -* l1CrossDomainMessengerAddress would be: 0x5D4472f31Bd9385709ec61305AFc749F0fA8e9d0 -* v3FactoryTargetAddress would be: 0x792edAdE80af5fC680d96a2eD80A44247D2Cf6Fd -* crossChainAccountTargetAddress would be: 0x2339C0d23b60739B3E5ABF201F05903D24A26C77 - -For Boba: -* l1CrossDomainMessengerAddress would be: 0x6D4528d192dB72E282265D6092F4B872f9Dff69e -* v3FactoryTargetAddress would be: 0xFFCd7Aed9C627E82A765c3247d562239507f6f1B -* crossChainAccountTargetAddress would be: 0x53163235746CeB81Da32293bb0932e1A599256B4 - -For Linea: -* l1CrossDomainMessengerAddress would be: 0xd19d4B5d358258f05D7B411E21A1460D11B0876F -* v3FactoryTargetAddress would be: 0x31FAfd4889FA1269F7a13A66eE0fB458f27D72A9 -* crossChainAccountTargetAddress would be: 0x581F86Da293A1D5Cd087a10E7227a75d2d2201A8 -* Slightly different sending code for Linea, with the 'sendMessage' function's `fee` and `message` parameters being swapped. - -For Manta Pacific: -* l1CrossDomainMessengerAddress would be: 0x635ba609680c55C3bDd0B3627b4c5dB21b13c310 -* v3FactoryTargetAddress would be: 0x06D830e15081f65923674268121FF57Cc54e4e23 -* crossChainAccountTargetAddress would be: 0x683553d74D9779955a15d57D208234C956B6Eae6 - -For Mantle: -* l1CrossDomainMessengerAddress would be: 0x676A795fe6E43C17c668de16730c3F690FEB7120 -* v3FactoryTargetAddress would be: 0x0d922Fb1Bc191F64970ac40376643808b4B74Df9 -* crossChainAccountTargetAddress would be: 0x9b7aC6735b23578E81260acD34E3668D0cc6000A - -For Redstone: -* l1CrossDomainMessengerAddress would be: 0x592C1299e0F8331D81A28C0FC7352Da24eDB444a -* v3FactoryTargetAddress would be: 0xece75613Aa9b1680f0421E5B2eF376DF68aa83Bb -* crossChainAccountTargetAddress would be: 0x2d00e94d78Fc307FC5E6195BBe2fB6aFC2FC07d4 - -For Worldcoin: -* l1CrossDomainMessengerAddress would be: 0xf931a81D18B1766d15695ffc7c1920a62b7e710a -* v3FactoryTargetAddress would be: 0x7a5028BDa40e7B173C278C5342087826455ea25a -* crossChainAccountTargetAddress would be: 0xcb2436774C3e191c85056d248EF4260ce5f27A9D - -For Zora: -* l1CrossDomainMessengerAddress would be: 0xdC40a14d9abd6F410226f1E6de71aE03441ca506 -* v3FactoryTargetAddress would be: 0x7145F8aeef1f6510E92164038E1B6F8cB2c42Cbb -* crossChainAccountTargetAddress would be: 0x36eEC182D0B24Df3DC23115D64DB521A93D5154f - -## Wormhole Proposal Bridging Details - -To create a proposal that (when successfully passed, queued, and executed) would effect a change on Uniswap V3 on a destination chain via wormhole, the proposal would have to contain a call to the function called `sendMessage` on the one instance of the `UniswapWormholeMessageSender` contract on Ethereum mainnet. -In addition to the proposal message parameters, the function also takes both a target address for the message receiving contract on the destination chain, as well as the Chain ID of the destination chain as parameters, allowing the function to be used for sending to multiple destination chains. - -There are 4 chains where Uniswap V3 is deployed that use the same Ethereum mainnet instance of the `UniswapWormholeMessageSender` contract for sending proposals to the target. -A Solidity code example for sending a proposal to one these chains (BNB Chain) is provided below. -This code can be used for sending proposals to the other 3 chains, with the only changes needed being the actual depoloyed address constants for the targeted contracts. - - -``` -// SPDX-License-Identifier: UNLICENSED -pragma solidity ^0.8.28; - -import {console2} from "forge-std/console2.sol"; -import {Script} from "forge-std/script.sol"; -import {IUniswapV3Factory} from "lib/v3-core/contracts/interfaces/IUniswapV3Factory.sol"; - -interface IUniswapGovernorBravoDelegator { - // function to create a proposal on Ethereum L1 using the Uniswap Governor Bravo Delegator contract - function propose( - address[] memory targets, - uint256[] memory values, - string[] memory signatures, - bytes[] memory calldatas, - string memory description - ) external returns (uint256); -} - -contract EthereumToBnbChainSender is Script { - // Address of the WormholeMessageReceiver contract on BNB Chain - address constant WORMHOLE_MESSAGE_RECEIVER_ADDRESS = 0x341c1511141022cf8eE20824Ae0fFA3491F1302b; - - // BNB Chain chainId - uint256 constant CHAIN_ID = 56; - - // target addresses on BNB Chain - address constant v3FactoryTargetAddress = 0xdB1d10011AD0Ff90774D0C6Bb92e5C5c8b4461F7; - - // target address for UniSwap DAO governor bravo delegate - address uniswapGovernorBravoDelegatorAddress = 0x408ED6354d4973f66138C91495F2f2FCbd8724C3; - - // Address of the WormholdMessageSender contract on Ethereum (L1) - address constant WORMHOLE_MESSAGE_SENDER_ADDRESS = 0xf5F4496219F31CDCBa6130B5402873624585615a; - - // function to create a proposal on Ethereum L1 to enable a fee amount on the Uniswap V3 Factory on BNB Chain. - // Upon execution of the proposal, the WormholeMessageSender contract function sendMessage is called, containing calldata for the - // uniswap v3 factory to enable a fee amount on Uniswap V3 Factory on BNB Chain. - function proposeForExecutionOnBnbChain() external payable { - // setup calldata for the destination chain uniswap v3Factory.enableFeeAmount call - bytes memory _v3FactoryEnableFeeAmounCalldata = - abi.encodeWithSelector(IUniswapV3Factory.enableFeeAmount.selector, 10000, 205); - - // setup calldata for calling WormholeMessageSender sendMessage - address[] memory _sendMessageTargets = new address[](1); - uint256[] memory _sendMessageValues = new uint256[](1); - bytes[] memory _sendMessageCalldatas = new bytes[](1); - _sendMessageTargets[0] = v3FactoryTargetAddress; - _sendMessageValues[0] = 0; - _sendMessageCalldatas[0] = _v3FactoryEnableFeeAmounCalldata; - bytes memory _sendMessageCalldata = abi.encode( - _sendMessageTargets, _sendMessageValues, _sendMessageCalldatas, WORMHOLE_MESSAGE_RECEIVER_ADDRESS, CHAIN_ID - ); - - // make the proposal on the L1 side - IUniswapGovernorBravoDelegator governor = IUniswapGovernorBravoDelegator(uniswapGovernorBravoDelegatorAddress); - address[] memory _targets = new address[](1); - uint256[] memory _values = new uint256[](1); - string[] memory _signatures = new string[](1); - bytes[] memory _calldatas = new bytes[](1); - _targets[0] = WORMHOLE_MESSAGE_SENDER_ADDRESS; - _values[0] = 0; - _signatures[0] = "sendMessage(address[],uint256[],bytes[],address,uint16)"; - _calldatas[0] = _sendMessageCalldata; - uint256 _proposalId = governor.propose( - _targets, - _values, - _signatures, - _calldatas, - "Proposal to enable 10000 fee amount of 205 on Uniswap V3 Factory on BNB Chain" - ); - console2.log("Proposal ID: %d", _proposalId); - } -} -``` -Because 5 networks all use the same Wormhole-based approach for communication, the only modifications to the above code snippet for those networks would be to change the first 3 address constant definitions. The definitions for the other 4 networks are below: - -For Celo: -* WORMHOLE_MESSAGE_RECEIVER_ADDRESS would be: 0x0Eb863541278308c3A64F8E908BC646e27BFD071 -* CHAIN_ID would be: 42220 -* V3FactoryTargetAddress would be: 0xAfE208a311B21f13EF87E33A90049fC17A7acDEc -For Gnosis: -* WORMHOLE_MESSAGE_RECEIVER_ADDRESS would be: 0xfFA5599136fBaB9af7799A6703b57BB33E5390Cf -* CHAIN_ID would be: 100 -* V3FactoryTargetAddress would be: 0xe32F7dD7e3f098D518ff19A22d5f028e076489B1 +Because 10 networks all use the same CrossChainAccount approach for communication, the only modifications to the above code snippet for those networks would be to change the first 3 address constant definitions. The definitions for all of the OP Stack based networks are below: -For Moonbeam: -* WORMHOLE_MESSAGE_RECEIVER_ADDRESS would be: 0xB2af16D6c7074228fC487F17929De830303E6531 -* CHAIN_ID would be: 1284 -* V3FactoryTargetAddress would be: 0xe32F7dD7e3f098D518ff19A22d5f028e076489B1 +| Network | l1CrossDomainMessengerAddress | v3FactoryTargetAddress | crossChainAccountTargetAddress | +| ------------- | ------------------------------------------ | ------------------------------------------ | ------------------------------------------ | +| Base | 0x866E82a600A1414e583f7F13623F1aC5d58b0Afa | 0x33128a8fC17869897dcE68Ed026d694621f6FDfD | 0x31FAfd4889FA1269F7a13A66eE0fB458f27D72A9 | +| Blast | 0x5D4472f31Bd9385709ec61305AFc749F0fA8e9d0 | 0x792edAdE80af5fC680d96a2eD80A44247D2Cf6Fd | 0x2339C0d23b60739B3E5ABF201F05903D24A26C77 | +| Boba | 0x6D4528d192dB72E282265D6092F4B872f9Dff69e | 0xFFCd7Aed9C627E82A765c3247d562239507f6f1B | 0x53163235746CeB81Da32293bb0932e1A599256B4 | +| Linea - 1 | 0xd19d4B5d358258f05D7B411E21A1460D11B0876F | 0x31FAfd4889FA1269F7a13A66eE0fB458f27D72A9 | 0x581F86Da293A1D5Cd087a10E7227a75d2d2201A8 | +| Manta Pacific | 0x635ba609680c55C3bDd0B3627b4c5dB21b13c310 | 0x06D830e15081f65923674268121FF57Cc54e4e23 | 0x683553d74D9779955a15d57D208234C956B6Eae6 | +| Mantle | 0x676A795fe6E43C17c668de16730c3F690FEB7120 | 0x0d922Fb1Bc191F64970ac40376643808b4B74Df9 | 0x9b7aC6735b23578E81260acD34E3668D0cc6000A | +| Optimism | 0x25ace71c97B33Cc4729CF772ae268934F7ab5fA1 | 0x1F98431c8aD98523631AE4a59f267346ea31F984 | 0xa1dD330d602c32622AA270Ea73d078B803Cb3518 | +| Redstone | 0x592C1299e0F8331D81A28C0FC7352Da24eDB444a | 0xece75613Aa9b1680f0421E5B2eF376DF68aa83Bb | 0x2d00e94d78Fc307FC5E6195BBe2fB6aFC2FC07d4 | +| Worldcoin | 0xf931a81D18B1766d15695ffc7c1920a62b7e710a | 0x7a5028BDa40e7B173C278C5342087826455ea25a | 0xcb2436774C3e191c85056d248EF4260ce5f27A9D | +| Zora | 0xdC40a14d9abd6F410226f1E6de71aE03441ca506 | 0x7145F8aeef1f6510E92164038E1B6F8cB2c42Cbb | 0x36eEC182D0B24Df3DC23115D64DB521A93D5154f | -For Rootstock: -* WORMHOLE_MESSAGE_RECEIVER_ADDRESS would be: 0x38aE7De6f9c51e17f49cF5730DD5F2d29fa20758 -* CHAIN_ID would be: 30 -* V3FactoryTargetAddress would be: 0xaF37EC98A00FD63689CF3060BF3B6784E00caD82 +1 - Slightly different sending code for Linea, with the 'sendMessage' function's `fee` and `message` parameters swapped. +## Polygon -## Polygon EthereumProxy Proposal Bridging Details +Polygon makes use of an L1 contract called FxRoot for sending messages (in the case of Uniswap, executable proposals), and contracts called FxChild and EthereumProxy on the Polygon chain for forwarding the executable proposals to Uniswap V3 on Polygon. A Solidity code example for sending a proposal to Polygon is below. +Proposal calldata on mainnet meant for Polygon should be forwarded through a call to `sendMessageToChild` on the chain's corresponding mainnet `FxRoot` contract. +The call would have wrapped calldata for a Uniswap contract function call that would effect the change. The FxRoot/FxChild tunnel would bridge that message to Polygon where FxChild would route the message the EthereumProxy parent contract of UniSwap V3. An example: -To create a proposal that (when successfully passed, queued, and executed) would effect a change on Uniswap V3 on Polygon, the proposal would have to contain a call to the function called `sendMessageToChild` of the FxRoot contract that is located at address 0xfe5e5D361b2ad62c541bAb87C45a0B9B018389a2 on Ethereum mainnet. The call would have wrapped calldata for a Uniswap contract function call that would effect the change. The FxRoot/FxChild tunnel would bridge that message to Polygon where FxChild would route the message the EthereumProxy parent contract of UniSwap V3. An example: ``` // SPDX-License-Identifier: UNLICENSED pragma solidity ^0.8.28; @@ -601,9 +370,15 @@ contract PolygonExample is Script { } ``` -## Scroll Proposal Bridging Details +## Polygon zkEVM + +Polygon zkEVM uses a different approach than Polygon TODO: Add details + +## Scroll + +Scroll also makes use of the CrossChainAccount contract for receiving proposals from Ethereum mainnet, but the way that messages are sent is different from the OP Stack approach. +Proposal calldata on mainnet meant for the Scroll chain should be forwarded through a call to `sendMessage` on the chain's corresponding mainnet `L1ScrollMessenger` contract. -To create a proposal that (when successfully passed, queued, and executed) would effect a change on Uniswap V3 on the Scroll blockchain, the proposal would have to contain a call to the function called `sendMessage` on an instance of the L1ScrollMessenger contract on Ethereum mainnet. The call would have doubly-wrapped calldata for the CrossChainAccount `forward` function as well as the ultimate Uniswap contract function call that would effect the change. An example: ``` // SPDX-License-Identifier: UNLICENSED pragma solidity ^0.8.28; @@ -674,9 +449,12 @@ contract ScrollExample is Script { } ``` -## Taiko Proposal Bridging Details +## Taiko + +Taiko makes use of a contract called `InvokableAccount`. +On the Ethereum mainnet L1 side, a contract called `SignalService` has an 'emitSignal`function that must be called with appropriate calldata by the successful proposal to effect a change on the Taiko target chain. Proposal calldata on mainnet meant for the Taiko chain should be forwarded through a call to`emitSignal`on the chain's corresponding mainnet`SignalService` contract. +The call would have wrapped calldata for the Uniswap contract function call that would effect the change. An example: -To create a proposal that (when successfully passed, queued, and executed) would effect a change on Uniswap V3 on the Taiko blockchain, the proposal would have to contain a call to the function called `emitSignal` on an instance of the Taiko SignalService contract on Ethereum mainnet. The call would have wrapped calldata for the Uniswap contract function call that would effect the change. An example: ``` // SPDX-License-Identifier: UNLICENSED pragma solidity ^0.8.28; @@ -734,8 +512,107 @@ contract TaikoExample is Script { } ``` -## ZkSync Era Proposal Bridging Details -To create a proposal that (when successfully passed, queued, and executed) would effect a change on Uniswap on ZkSync Era, the proposal would have to contain a call to the function called `requestL2Transaction` of the MailBoxFacet contract that is located at address 0x63b5EC36B09384fFA7106A80Ec7cfdFCa521fD08 on Ethereum mainnet. The call would have wrapped calldata for a Uniswap contract function call that would effect the change. An example: +## Wormhole + +There are 5 chains where Uniswap V3 is deployed that use the same Ethereum mainnet instance of the `UniswapWormholeMessageSender` contract for sending proposals to the target. +Proposal calldata on mainnet meant for a target chain using Wormhole should be forwarded through a call to `sendMessage` on the the mainnet `UniswapWormholeMessageSender` contract. +The function takes both a target address for the message receiving contract on the destination chain, as well as the Chain ID of the destination chain as parameters, allowing the function to be used for sending to multiple destination chains. + +A Solidity code example for sending a proposal to one these chains (BNB Chain) is provided below. + +``` +// SPDX-License-Identifier: UNLICENSED +pragma solidity ^0.8.28; + +import {console2} from "forge-std/console2.sol"; +import {Script} from "forge-std/script.sol"; +import {IUniswapV3Factory} from "lib/v3-core/contracts/interfaces/IUniswapV3Factory.sol"; + +interface IUniswapGovernorBravoDelegator { + // function to create a proposal on Ethereum L1 using the Uniswap Governor Bravo Delegator contract + function propose( + address[] memory targets, + uint256[] memory values, + string[] memory signatures, + bytes[] memory calldatas, + string memory description + ) external returns (uint256); +} + +contract EthereumToBnbChainSender is Script { + // Address of the WormholeMessageReceiver contract on BNB Chain + address constant WORMHOLE_MESSAGE_RECEIVER_ADDRESS = 0x341c1511141022cf8eE20824Ae0fFA3491F1302b; + + // BNB Chain chainId + uint256 constant CHAIN_ID = 56; + + // target addresses on BNB Chain + address constant v3FactoryTargetAddress = 0xdB1d10011AD0Ff90774D0C6Bb92e5C5c8b4461F7; + + // target address for UniSwap DAO governor bravo delegate + address uniswapGovernorBravoDelegatorAddress = 0x408ED6354d4973f66138C91495F2f2FCbd8724C3; + + // Address of the WormholdMessageSender contract on Ethereum (L1) + address constant WORMHOLE_MESSAGE_SENDER_ADDRESS = 0xf5F4496219F31CDCBa6130B5402873624585615a; + + // function to create a proposal on Ethereum L1 to enable a fee amount on the Uniswap V3 Factory on BNB Chain. + // Upon execution of the proposal, the WormholeMessageSender contract function sendMessage is called, containing calldata for the + // uniswap v3 factory to enable a fee amount on Uniswap V3 Factory on BNB Chain. + function proposeForExecutionOnBnbChain() external payable { + // setup calldata for the destination chain uniswap v3Factory.enableFeeAmount call + bytes memory _v3FactoryEnableFeeAmounCalldata = + abi.encodeWithSelector(IUniswapV3Factory.enableFeeAmount.selector, 10000, 205); + + // setup calldata for calling WormholeMessageSender sendMessage + address[] memory _sendMessageTargets = new address[](1); + uint256[] memory _sendMessageValues = new uint256[](1); + bytes[] memory _sendMessageCalldatas = new bytes[](1); + _sendMessageTargets[0] = v3FactoryTargetAddress; + _sendMessageValues[0] = 0; + _sendMessageCalldatas[0] = _v3FactoryEnableFeeAmounCalldata; + bytes memory _sendMessageCalldata = abi.encode( + _sendMessageTargets, _sendMessageValues, _sendMessageCalldatas, WORMHOLE_MESSAGE_RECEIVER_ADDRESS, CHAIN_ID + ); + + // make the proposal on the L1 side + IUniswapGovernorBravoDelegator governor = IUniswapGovernorBravoDelegator(uniswapGovernorBravoDelegatorAddress); + address[] memory _targets = new address[](1); + uint256[] memory _values = new uint256[](1); + string[] memory _signatures = new string[](1); + bytes[] memory _calldatas = new bytes[](1); + _targets[0] = WORMHOLE_MESSAGE_SENDER_ADDRESS; + _values[0] = 0; + _signatures[0] = "sendMessage(address[],uint256[],bytes[],address,uint16)"; + _calldatas[0] = _sendMessageCalldata; + uint256 _proposalId = governor.propose( + _targets, + _values, + _signatures, + _calldatas, + "Proposal to enable 10000 fee amount of 205 on Uniswap V3 Factory on BNB Chain" + ); + console2.log("Proposal ID: %d", _proposalId); + } +} +``` + +Because 5 networks all use the same Wormhole-based approach for communication, the only modifications to the above code snippet for those networks would be to change the first 3 address constant definitions. The definitions for all 5 networks that use Wormhole communicatino are below: + +| Network | WORMHOLE_MESSAGE_RECEIVER_ADDRESS | CHAIN_ID | V3FactoryTargetAddress | +| --------- | ------------------------------------------ | -------- | ------------------------------------------ | +| Celo | 0x0Eb863541278308c3A64F8E908BC646e27BFD071 | 42220 | 0xAfE208a311B21f13EF87E33A90049fC17A7acDEc | +| Gnosis | 0xfFA5599136fBaB9af7799A6703b57BB33E5390Cf | 100 | 0xe32F7dD7e3f098D518ff19A22d5f028e076489B1 | +| Moonbeam | 0xB2af16D6c7074228fC487F17929De830303E6531 | 1284 | 0xe32F7dD7e3f098D518ff19A22d5f028e076489B1 | +| Rootstock | 0x38aE7De6f9c51e17f49cF5730DD5F2d29fa20758 | 1329 | 0xaF37EC98A00FD63689CF3060BF3B6784E00caD82 | +| Sei | 0xDAA94C43133c6c645017134Ba372DDa4829F34A6 | 30 | 0x75FC67473A91335B5b8F8821277262a13B38c9b3 | + +## ZkSync Era + +ZkSync Era uses the Arbitrum-style approach where the parent of the V3Factory contract is an aliased address, but the method of sending the proposal is different than the one used for Arbitrum. +See the Solidity code example provided for ZkSync Era below. +Proposal calldata on mainnet intended for ZkSync Era should be forwarded through a call to `requestL2Transaction` on the `MailBoxFacet` contract. +The call would have wrapped calldata for a Uniswap contract function call that would effect the change. An example: + ``` // SPDX-License-Identifier: UNLICENSED pragma solidity ^0.8.28; @@ -815,5 +692,3 @@ contract EthereumToZkSyncSender is Script { } } ``` - - From 0b9197d28bc49a2d71d074ee09cc747dea914cc6 Mon Sep 17 00:00:00 2001 From: John Feras Date: Tue, 7 Jan 2025 12:15:16 -0500 Subject: [PATCH 05/16] Cleanup text, fix typos --- docs/concepts/multi-chain-proposals.md | 19 +++++++++---------- 1 file changed, 9 insertions(+), 10 deletions(-) diff --git a/docs/concepts/multi-chain-proposals.md b/docs/concepts/multi-chain-proposals.md index bf46f1662..63c93f440 100644 --- a/docs/concepts/multi-chain-proposals.md +++ b/docs/concepts/multi-chain-proposals.md @@ -10,8 +10,8 @@ This is a living document which represents the current process guidelines for de Uniswap V3 has now been deployed across 24 blockchain networks, including many Layer 2 chains. However, because Uniswap governance (using GovernorBravo and its associated Timelock contract) executes approved proposals on Ethereum mainnet, implementing changes on these other blockchains requires some extra steps in the proposals' executable code. -For a proposal to successfully enact changes on a non-mainnet chain, the proposal's code must transmit the necessary code effect the change to the specific target chain for execution. -A variety of mechanisms are used my Uniswap V3 on the individual non-mainnet chains to receive the proposal code, although in some cases, similar or even identical communication methods and contracts are used to bridge proposals from Ethereum mainnet to the target chain. +For a proposal to successfully enact changes on a non-mainnet chain, the proposal's code must transmit the necessary code to effect the change on the specific target chain for execution. +A variety of mechanisms are used on the individual non-mainnet chains to receive the proposal code, although in some cases, similar or even identical communication methods and contracts are used to bridge proposals from Ethereum mainnet to the target chain. This document provides a detailed guide on constructing such proposals for each target non-mainnet chain, including Solidity code snippets for each general approach. The code snippets are written as Forge/Foundry scripts, and assume they are being run on the Forge platform, in a code repository with the necessary libraries (forge-std, uniswap-v3-core, and uniswap-v3-periphery) imported. @@ -42,7 +42,7 @@ The table below contains links to the various proposal methods for each chain. Arbitrum uses an approach where the owner of the V3Factory is a special aliased address (offset by the value 0x1111000000000000000000000000000000001111) that (when the offset is subtracted away) is the L1 address of the Uniswap DAO Timelock contract. A Solidity code example for sending a proposal to Arbitrum is shown below. -Proposal calldata intended for Arbitrum should be forwarded through a call to `createRetryableTicket` on the Arbitrum Inbox contract. +Proposal calldata on mainnet intended for Arbitrum should be forwarded through a call to `createRetryableTicket` on the Arbitrum Inbox contract. The call would have wrapped calldata for a Uniswap contract function call that would effect the change. An example: ``` @@ -305,7 +305,8 @@ Because 10 networks all use the same CrossChainAccount approach for communicatio ## Polygon -Polygon makes use of an L1 contract called FxRoot for sending messages (in the case of Uniswap, executable proposals), and contracts called FxChild and EthereumProxy on the Polygon chain for forwarding the executable proposals to Uniswap V3 on Polygon. A Solidity code example for sending a proposal to Polygon is below. +Polygon makes use of an L1 contract called FxRoot for sending messages (in the case of Uniswap, executable proposals), and contracts called FxChild and EthereumProxy on the Polygon chain for forwarding the executable proposals to Uniswap V3 on Polygon. + Proposal calldata on mainnet meant for Polygon should be forwarded through a call to `sendMessageToChild` on the chain's corresponding mainnet `FxRoot` contract. The call would have wrapped calldata for a Uniswap contract function call that would effect the change. The FxRoot/FxChild tunnel would bridge that message to Polygon where FxChild would route the message the EthereumProxy parent contract of UniSwap V3. An example: @@ -376,7 +377,7 @@ Polygon zkEVM uses a different approach than Polygon TODO: Add details ## Scroll -Scroll also makes use of the CrossChainAccount contract for receiving proposals from Ethereum mainnet, but the way that messages are sent is different from the OP Stack approach. +Scroll also makes use of the CrossChainAccount contract for receiving proposals from Ethereum mainnet, but the way that messages are sent is different from the other OP Stack approaches. Proposal calldata on mainnet meant for the Scroll chain should be forwarded through a call to `sendMessage` on the chain's corresponding mainnet `L1ScrollMessenger` contract. ``` @@ -403,7 +404,6 @@ interface IUniswapGovernorBravoDelegator { contract ScrollExample is Script { // target address for the cross domain messenger on Ethereum L1 responsible for sending messages to Scroll - // (this address would be different for Base, Blast, or Zora) address constant L1_SCROLL_MESSENGER_ADDRESS = 0x6774Bcbd5ceCeF1336b5300fb5186a12DDD8b367; // target addresses on Scroll @@ -452,7 +452,7 @@ contract ScrollExample is Script { ## Taiko Taiko makes use of a contract called `InvokableAccount`. -On the Ethereum mainnet L1 side, a contract called `SignalService` has an 'emitSignal`function that must be called with appropriate calldata by the successful proposal to effect a change on the Taiko target chain. Proposal calldata on mainnet meant for the Taiko chain should be forwarded through a call to`emitSignal`on the chain's corresponding mainnet`SignalService` contract. +Proposal calldata on mainnet meant for the Taiko chain should be forwarded through a call to`emitSignal`on the chain's corresponding mainnet`SignalService` contract. The call would have wrapped calldata for the Uniswap contract function call that would effect the change. An example: ``` @@ -515,7 +515,7 @@ contract TaikoExample is Script { ## Wormhole There are 5 chains where Uniswap V3 is deployed that use the same Ethereum mainnet instance of the `UniswapWormholeMessageSender` contract for sending proposals to the target. -Proposal calldata on mainnet meant for a target chain using Wormhole should be forwarded through a call to `sendMessage` on the the mainnet `UniswapWormholeMessageSender` contract. +Proposal calldata on mainnet meant for a target chain using Wormhole should be forwarded through a call to the `sendMessage` function of that contract. The function takes both a target address for the message receiving contract on the destination chain, as well as the Chain ID of the destination chain as parameters, allowing the function to be used for sending to multiple destination chains. A Solidity code example for sending a proposal to one these chains (BNB Chain) is provided below. @@ -596,7 +596,7 @@ contract EthereumToBnbChainSender is Script { } ``` -Because 5 networks all use the same Wormhole-based approach for communication, the only modifications to the above code snippet for those networks would be to change the first 3 address constant definitions. The definitions for all 5 networks that use Wormhole communicatino are below: +Because 5 networks all use the same Wormhole-based approach for communication, the only modifications to the above code snippet for those networks would be to change the first 3 constant definitions. The definitions for all 5 networks that use Wormhole communication are below: | Network | WORMHOLE_MESSAGE_RECEIVER_ADDRESS | CHAIN_ID | V3FactoryTargetAddress | | --------- | ------------------------------------------ | -------- | ------------------------------------------ | @@ -609,7 +609,6 @@ Because 5 networks all use the same Wormhole-based approach for communication, t ## ZkSync Era ZkSync Era uses the Arbitrum-style approach where the parent of the V3Factory contract is an aliased address, but the method of sending the proposal is different than the one used for Arbitrum. -See the Solidity code example provided for ZkSync Era below. Proposal calldata on mainnet intended for ZkSync Era should be forwarded through a call to `requestL2Transaction` on the `MailBoxFacet` contract. The call would have wrapped calldata for a Uniswap contract function call that would effect the change. An example: From f994fda988ccbfdd3b31697b705ca19d20b85084 Mon Sep 17 00:00:00 2001 From: John Feras Date: Wed, 22 Jan 2025 09:55:42 -0500 Subject: [PATCH 06/16] Updated modification date --- docs/concepts/multi-chain-proposals.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/concepts/multi-chain-proposals.md b/docs/concepts/multi-chain-proposals.md index 63c93f440..067f9c576 100644 --- a/docs/concepts/multi-chain-proposals.md +++ b/docs/concepts/multi-chain-proposals.md @@ -3,7 +3,7 @@ id: multi-chain-proposals title: Multi-Chain Proposals --- -This is a living document which represents the current process guidelines for developing and advancing multi-chain Uniswap Governance Proposals. It was last updated Novenber 2024. +This is a living document which represents the current process guidelines for developing and advancing multi-chain Uniswap Governance Proposals. It was last updated January 2025. ## Introduction From 1dd38064d5d44bae59078d290caf84a63ea77add Mon Sep 17 00:00:00 2001 From: John Feras Date: Wed, 22 Jan 2025 09:59:37 -0500 Subject: [PATCH 07/16] Change table layout in overview --- docs/concepts/multi-chain-proposals.md | 39 +++++++++++++++++--------- 1 file changed, 26 insertions(+), 13 deletions(-) diff --git a/docs/concepts/multi-chain-proposals.md b/docs/concepts/multi-chain-proposals.md index 067f9c576..ee4bfa547 100644 --- a/docs/concepts/multi-chain-proposals.md +++ b/docs/concepts/multi-chain-proposals.md @@ -22,21 +22,34 @@ It is important to note that the code provided here is a general template and ma Of the 24 chains where Uniswap V3 is deployed there are 10 chains that are OP Stack based and use an `L1CrossDomainMessenger` contract for bridging proposals. There are 6 chains that use Wormhole for bridging proposals. The remaining 8 chains use a variety of other methods for bridging proposals, all described below. - The table below contains links to the various proposal methods for each chain. -| Proposal Method | Chain(s) | -| ----------------------------------------------------- | ------------------------------------------------------------------------------------ | -| [Arbitrum - Aliased Timelock](#arbitrum) | Arbitrum | -| [Avalanche - OmnichainGovernanceExecutor](#avalanche) | Avalanche | -| [Filecoin - TODO](#filecoin) | Filecoin | -| [Op Stack - CrossChainAccount](#op-stack) | Base, Blast, Boba, Linea, Manta Pacific, Mantle, Optimism, Redstone, WorldCoin, Zora | -| [Polygon - FxChild](#polygon) | Polygon | -| [Polygon zkEVM - TODO](#polygon-zkevm) | Polygon zkEVM | -| [Scroll - CrossChainAccount](#scroll) | Scroll | -| [Taiko - InvokableAccount](#taiko) | Taiko | -| [Wormhole](#wormhole) | BNB Chain, Celo, Gnosis Chain, Moonbeam, Rootstock, Sei | -| [ZkSync Era - Aliased Timelock](#zksync-era) | ZkSync Era | +| Chain(s) | Proposal Method | +| ------------- | ----------------------------------------------------- | +| Arbitrum | [Arbitrum - Aliased Timelock](#arbitrum) | +| Avalanche | [Avalanche - OmnichainGovernanceExecutor](#avalanche) | +| Base | [OP Stack - CrossChainAccount](#op-stack) | +| Blast | [OP Stack - CrossChainAccount](#op-stack) | +| BNB Chain | [Wormhole](#wormhole) | +| Boba | [OP Stack - CrossChainAccount](#op-stack) | +| Celo | [Wormhole](#wormhole) | +| Filecoin | [Filecoin - TODO](#filecoin) | +| Gnosis Chain | [Wormhole](#wormhole) | +| Linea | [OP Stack - CrossChainAccount](#op-stack) | +| Manta Pacific | [OP Stack - CrossChainAccount](#op-stack) | +| Mantle | [OP Stack - CrossChainAccount](#op-stack) | +| Moonbeam | [Wormhole](#wormhole) | +| Optimism | [OP Stack - CrossChainAccount](#op-stack) | +| Polygon | [Polygon - FxChild](#polygon) | +| Polygon zkEVM | [Polygon zkEVM - TODO](#polygon-zkevm) | +| Redstone | [OP Stack - CrossChainAccount](#op-stack) | +| Rootstock | [Wormhole](#wormhole) | +| Scroll | [Scroll - CrossChainAccount](#scroll) | +| Sei | [Sei - Wormhole](#wormhole) | +| Taiko | [Taiko - InvokableAccount](#taiko) | +| WorldCoin | [OP Stack - CrossChainAccount](#op-stack) | +| ZkSync Era | [ZkSync Era - Aliased Timelock](#zksync-era) | +| Zora | [OP Stack - CrossChainAccount](#op-stack) | ## Arbitrum From a2456e19d9dc4552d87077a72b6df11f7e7dc929 Mon Sep 17 00:00:00 2001 From: John Feras Date: Wed, 22 Jan 2025 10:08:51 -0500 Subject: [PATCH 08/16] Code formatted addresses and contract names --- docs/concepts/multi-chain-proposals.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/docs/concepts/multi-chain-proposals.md b/docs/concepts/multi-chain-proposals.md index ee4bfa547..9a8daa890 100644 --- a/docs/concepts/multi-chain-proposals.md +++ b/docs/concepts/multi-chain-proposals.md @@ -53,7 +53,7 @@ The table below contains links to the various proposal methods for each chain. ## Arbitrum -Arbitrum uses an approach where the owner of the V3Factory is a special aliased address (offset by the value 0x1111000000000000000000000000000000001111) that (when the offset is subtracted away) is the L1 address of the Uniswap DAO Timelock contract. A Solidity code example for sending a proposal to Arbitrum is shown below. +Arbitrum uses an approach where the owner of the V3Factory is a special aliased address (offset by the value `0x1111000000000000000000000000000000001111`) that (when the offset is subtracted away) is the L1 address of the Uniswap DAO Timelock contract. A Solidity code example for sending a proposal to Arbitrum is shown below. Proposal calldata on mainnet intended for Arbitrum should be forwarded through a call to `createRetryableTicket` on the Arbitrum Inbox contract. The call would have wrapped calldata for a Uniswap contract function call that would effect the change. An example: @@ -142,7 +142,7 @@ contract EthereumToArbitrumSender is Script { ## Avalanche Avalanche makes use of a contract called `OmnichainGovernanceExecutor` to receive proposals from Ethereum mainnet. -Proposal calldata on mainnet intended for Avalanche should be forwarded through a call to `send` on the contract called LayerZero:EndpointV2. +Proposal calldata on mainnet intended for Avalanche should be forwarded through a call to `send` on the contract called `LayerZero:EndpointV2`. A Solidity code example for sending a proposal to AVAX is below. ``` @@ -226,8 +226,8 @@ Filecoin EVM ?? TODO: Add details ## OP Stack -Proposal calldata on mainnet meant for an OP stack chain should be forwarded through a call to sendMessage on the chain's corresponding mainnet L1CrossDomainMessenger contract. -The call would have doubly-wrapped calldata for the CrossChainAccount `forward` function as well as the ultimate Uniswap contract function call that would effect the change. An example for Optimism: +Proposal calldata on mainnet meant for an OP stack chain should be forwarded through a call to sendMessage on the chain's corresponding mainnet `L1CrossDomainMessenger` contract. +The call would have doubly-wrapped calldata for the `CrossChainAccount:forward` function as well as the ultimate Uniswap contract function call that would effect the change. An example for Optimism: ``` // SPDX-License-Identifier: UNLICENSED From 3ee0676ce20b543553306f00aa64af157f36f76f Mon Sep 17 00:00:00 2001 From: John Feras Date: Wed, 22 Jan 2025 10:22:32 -0500 Subject: [PATCH 09/16] Op Stack and Wormhole table cleanup --- docs/concepts/multi-chain-proposals.md | 154 +++++++++++++++++++++---- 1 file changed, 133 insertions(+), 21 deletions(-) diff --git a/docs/concepts/multi-chain-proposals.md b/docs/concepts/multi-chain-proposals.md index 9a8daa890..7aa5fe082 100644 --- a/docs/concepts/multi-chain-proposals.md +++ b/docs/concepts/multi-chain-proposals.md @@ -301,20 +301,132 @@ contract OptimismExample is Script { Because 10 networks all use the same CrossChainAccount approach for communication, the only modifications to the above code snippet for those networks would be to change the first 3 address constant definitions. The definitions for all of the OP Stack based networks are below: -| Network | l1CrossDomainMessengerAddress | v3FactoryTargetAddress | crossChainAccountTargetAddress | -| ------------- | ------------------------------------------ | ------------------------------------------ | ------------------------------------------ | -| Base | 0x866E82a600A1414e583f7F13623F1aC5d58b0Afa | 0x33128a8fC17869897dcE68Ed026d694621f6FDfD | 0x31FAfd4889FA1269F7a13A66eE0fB458f27D72A9 | -| Blast | 0x5D4472f31Bd9385709ec61305AFc749F0fA8e9d0 | 0x792edAdE80af5fC680d96a2eD80A44247D2Cf6Fd | 0x2339C0d23b60739B3E5ABF201F05903D24A26C77 | -| Boba | 0x6D4528d192dB72E282265D6092F4B872f9Dff69e | 0xFFCd7Aed9C627E82A765c3247d562239507f6f1B | 0x53163235746CeB81Da32293bb0932e1A599256B4 | -| Linea - 1 | 0xd19d4B5d358258f05D7B411E21A1460D11B0876F | 0x31FAfd4889FA1269F7a13A66eE0fB458f27D72A9 | 0x581F86Da293A1D5Cd087a10E7227a75d2d2201A8 | -| Manta Pacific | 0x635ba609680c55C3bDd0B3627b4c5dB21b13c310 | 0x06D830e15081f65923674268121FF57Cc54e4e23 | 0x683553d74D9779955a15d57D208234C956B6Eae6 | -| Mantle | 0x676A795fe6E43C17c668de16730c3F690FEB7120 | 0x0d922Fb1Bc191F64970ac40376643808b4B74Df9 | 0x9b7aC6735b23578E81260acD34E3668D0cc6000A | -| Optimism | 0x25ace71c97B33Cc4729CF772ae268934F7ab5fA1 | 0x1F98431c8aD98523631AE4a59f267346ea31F984 | 0xa1dD330d602c32622AA270Ea73d078B803Cb3518 | -| Redstone | 0x592C1299e0F8331D81A28C0FC7352Da24eDB444a | 0xece75613Aa9b1680f0421E5B2eF376DF68aa83Bb | 0x2d00e94d78Fc307FC5E6195BBe2fB6aFC2FC07d4 | -| Worldcoin | 0xf931a81D18B1766d15695ffc7c1920a62b7e710a | 0x7a5028BDa40e7B173C278C5342087826455ea25a | 0xcb2436774C3e191c85056d248EF4260ce5f27A9D | -| Zora | 0xdC40a14d9abd6F410226f1E6de71aE03441ca506 | 0x7145F8aeef1f6510E92164038E1B6F8cB2c42Cbb | 0x36eEC182D0B24Df3DC23115D64DB521A93D5154f | - -1 - Slightly different sending code for Linea, with the 'sendMessage' function's `fee` and `message` parameters swapped. +:::warning +**Linea**'s `sendMessage` reverses the order of the `fee` and `message` parameters. +::: + +The relevant OP Stack contract addresses are as follows: + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
NetworkContract Addresses
Base + l1CrossDomainMessengerAddress:
+ 0x866E82a600A1414e583f7F13623F1aC5d58b0Afa
+ v3FactoryTargetAddress:
+ 0x33128a8fC17869897dcE68Ed026d694621f6FDfD
+ crossChainAccountTarget:
+ 0x31FAfd4889FA1269F7a13A66eE0fB458f27D72A9
+
Blast + l1CrossDomainMessengerAddress:
+ 0x5D4472f31Bd9385709ec61305AFc749F0fA8e9d0
+ v3FactoryTargetAddress:
+ 0x792edAdE80af5fC680d96a2eD80A44247D2Cf6Fd
+ crossChainAccountTarget:
+ 0x2339C0d23b60739B3E5ABF201F05903D24A26C77
+
Boba + l1CrossDomainMessengerAddress:
+ 0x6D4528d192dB72E282265D6092F4B872f9Dff69e
+ v3FactoryTargetAddress:
+ 0xFFCd7Aed9C627E82A765c3247d562239507f6f1B
+ crossChainAccountTarget:
+ 0x53163235746CeB81Da32293bb0932e1A599256B4
+
Linea (see warning) + l1CrossDomainMessengerAddress:
+ 0xd19d4B5d358258f05D7B411E21A1460D11B0876F
+ v3FactoryTargetAddress:
+ 0x1111??
+ crossChainAccountTarget:
+ 0x581F86Da293A1D5Cd087a10E7227a75d2d2201A8
+
Manta Pacific + l1CrossDomainMessengerAddress:
+ 0x635ba609680c55C3bDd0B3627b4c5dB21b13c310
+ v3FactoryTargetAddress:
+ 0x06D830e15081f65923674268121FF57Cc54e4e23
+ crossChainAccountTarget:
+ 0x683553d74D9779955a15d57D208234C956B6Eae6
+
Mantle + l1CrossDomainMessengerAddress:
+ 0x676A795fe6E43C17c668de16730c3F690FEB7120
+ v3FactoryTargetAddress:
+ 0x0d922Fb1Bc191F64970ac40376643808b4B74Df9
+ crossChainAccountTarget:
+ 0x9b7aC6735b23578E81260acD34E3668D0cc6000A
+
Optimism + l1CrossDomainMessengerAddress:
+ 0x25ace71c97B33Cc4729CF772ae268934F7ab5fA1
+ v3FactoryTargetAddress:
+ 0x1F98431c8aD98523631AE4a59f267346ea31F984
+ crossChainAccountTarget:
+ 0xa1dD330d602c32622AA270Ea73d078B803Cb3518
+
Redstone + l1CrossDomainMessengerAddress:
+ 0x592C1299e0F8331D81A28C0FC7352Da24eDB444a
+ v3FactoryTargetAddress:
+ 0xece75613Aa9b1680f0421E5B2eF376DF68aa83Bb
+ crossChainAccountTarget:
+ 0x2d00e94d78Fc307FC5E6195BBe2fB6aFC2FC07d4
+
Worldcoin + l1CrossDomainMessengerAddress:
+ 0xf931a81D18B1766d15695ffc7c1920a62b7e710a
+ v3FactoryTargetAddress:
+ 0x7a5028BDa40e7B173C278C5342087826455ea25a
+ crossChainAccountTarget:
+ 0xcb2436774C3e191c85056d248EF4260ce5f27A9D
+
Worldcoin + l1CrossDomainMessengerAddress:
+ 0xdC40a14d9abd6F410226f1E6de71aE03441ca506
+ v3FactoryTargetAddress:
+ 0x7145F8aeef1f6510E92164038E1B6F8cB2c42Cbb
+ crossChainAccountTarget:
+ 0x36eEC182D0B24Df3DC23115D64DB521A93D5154f
+
## Polygon @@ -611,13 +723,13 @@ contract EthereumToBnbChainSender is Script { Because 5 networks all use the same Wormhole-based approach for communication, the only modifications to the above code snippet for those networks would be to change the first 3 constant definitions. The definitions for all 5 networks that use Wormhole communication are below: -| Network | WORMHOLE_MESSAGE_RECEIVER_ADDRESS | CHAIN_ID | V3FactoryTargetAddress | -| --------- | ------------------------------------------ | -------- | ------------------------------------------ | -| Celo | 0x0Eb863541278308c3A64F8E908BC646e27BFD071 | 42220 | 0xAfE208a311B21f13EF87E33A90049fC17A7acDEc | -| Gnosis | 0xfFA5599136fBaB9af7799A6703b57BB33E5390Cf | 100 | 0xe32F7dD7e3f098D518ff19A22d5f028e076489B1 | -| Moonbeam | 0xB2af16D6c7074228fC487F17929De830303E6531 | 1284 | 0xe32F7dD7e3f098D518ff19A22d5f028e076489B1 | -| Rootstock | 0x38aE7De6f9c51e17f49cF5730DD5F2d29fa20758 | 1329 | 0xaF37EC98A00FD63689CF3060BF3B6784E00caD82 | -| Sei | 0xDAA94C43133c6c645017134Ba372DDa4829F34A6 | 30 | 0x75FC67473A91335B5b8F8821277262a13B38c9b3 | +| Network | WORMHOLE_MESSAGE_RECEIVER_ADDRESS | CHAIN_ID | V3FactoryTargetAddress | +| --------- | -------------------------------------------- | -------- | -------------------------------------------- | +| Celo | `0x0Eb863541278308c3A64F8E908BC646e27BFD071` | 42220 | `0xAfE208a311B21f13EF87E33A90049fC17A7acDEc` | +| Gnosis | `0xfFA5599136fBaB9af7799A6703b57BB33E5390Cf` | 100 | `0xe32F7dD7e3f098D518ff19A22d5f028e076489B1` | +| Moonbeam | `0xB2af16D6c7074228fC487F17929De830303E6531` | 1284 | `0xe32F7dD7e3f098D518ff19A22d5f028e076489B1` | +| Rootstock | `0x38aE7De6f9c51e17f49cF5730DD5F2d29fa20758` | 1329 | `0xaF37EC98A00FD63689CF3060BF3B6784E00caD82` | +| Sei | `0xDAA94C43133c6c645017134Ba372DDa4829F34A6` | 30 | `0x75FC67473A91335B5b8F8821277262a13B38c9b3` | ## ZkSync Era From 545c308612540b50d6a0918d299dcd833eaadd9f Mon Sep 17 00:00:00 2001 From: John Feras Date: Mon, 27 Jan 2025 09:44:42 -0500 Subject: [PATCH 10/16] Combined overview and introduction sections --- docs/concepts/multi-chain-proposals.md | 2 -- 1 file changed, 2 deletions(-) diff --git a/docs/concepts/multi-chain-proposals.md b/docs/concepts/multi-chain-proposals.md index 7aa5fe082..0879828b5 100644 --- a/docs/concepts/multi-chain-proposals.md +++ b/docs/concepts/multi-chain-proposals.md @@ -17,8 +17,6 @@ This document provides a detailed guide on constructing such proposals for each The code snippets are written as Forge/Foundry scripts, and assume they are being run on the Forge platform, in a code repository with the necessary libraries (forge-std, uniswap-v3-core, and uniswap-v3-periphery) imported. It is important to note that the code provided here is a general template and may require modification to suit the specific requirements of the target chain. -## Overview - Of the 24 chains where Uniswap V3 is deployed there are 10 chains that are OP Stack based and use an `L1CrossDomainMessenger` contract for bridging proposals. There are 6 chains that use Wormhole for bridging proposals. The remaining 8 chains use a variety of other methods for bridging proposals, all described below. From a8d8a13aa7e81fe102224db90d6290cb5cb50f6b Mon Sep 17 00:00:00 2001 From: John Feras Date: Thu, 30 Jan 2025 16:37:04 -0500 Subject: [PATCH 11/16] Updated to get multi-chain doc in governance folder --- docs/concepts/governance/02-process.md | 9 ++++----- .../05-multi-chain-proposals.md} | 0 .../governance/{05-glossary.md => 06-glossary.md} | 0 3 files changed, 4 insertions(+), 5 deletions(-) rename docs/concepts/{multi-chain-proposals.md => governance/05-multi-chain-proposals.md} (100%) rename docs/concepts/governance/{05-glossary.md => 06-glossary.md} (100%) diff --git a/docs/concepts/governance/02-process.md b/docs/concepts/governance/02-process.md index a43c4819c..78e2c1bb1 100644 --- a/docs/concepts/governance/02-process.md +++ b/docs/concepts/governance/02-process.md @@ -3,7 +3,7 @@ id: process title: Process --- -This is a living document which represents the current process guidelines for developing and advancing Uniswap Governance Proposals. It was last updated September 2024. +This is a living document which represents the current process guidelines for developing and advancing Uniswap Governance Proposals. It was last updated January 2025. ## Tools @@ -11,7 +11,7 @@ Uniswap Governance takes place in several venues. Each serves its own particular 1. [_Governance Forum_](https://gov.uniswap.org/) -A Discourse-hosted forum for governance-related discussion. Community members must register for an account before sharing or liking posts. New members must read 4 topics and a combined 15 posts over the course of at least 10 minutes before they may post themselves. +A Discourse-hosted forum for governance-related discussion. Community members must register for an account before sharing or liking posts. New members must read 4 topics and a combined 15 posts over the course of at least 10 minutes before they may post themselves. 2. [_Snapshot_](https://snapshot.box/#/s:uniswapgovernance.eth) @@ -21,7 +21,6 @@ A simple voting interface that allows users to signal sentiment off-chain. Votes The [Uniswap Foundation](https://www.uniswapfoundation.org) supports this voting and delegation interface. [Tally](https://www.tally.xyz/gov/uniswap) is another excellent app that supports proposal creation, delegation, and voting. - ## Process Below we outline the current Uniswap governance process, detailing where these venues fit in. These processes are subject to change according to feedback from the Uniswap community. @@ -62,7 +61,7 @@ At the end of 5 days, the option with the majority of votes wins. There must be _Timeframe_: 2 day waiting period, 7 day voting period, 2 day timelock - _Threshold_: 1M UNI +_Threshold_: 1M UNI _Quorum_: 40M UNI votes in favor @@ -78,7 +77,7 @@ To create an onchain Governance Proposal: 2. Create a topic in the [Governance Forum](https://gov.uniswap.org/) titled "Governance Proposal — [Your Title Here]" and link to previous forum posts and the Temperature Check Snapshot poll. -3. Create your proposal. This can be done either through an interface (e.g. [Tally](https://tally.xyz/gov/uniswap)) or through writing the calldata for more complicated proposal logic. If the proposal passed, this calldata will execute. If writing the calldata yourself, please review the logic with a qualified Uniswap community member prior to posting the proposal. For more information on developing and advancing multi-chain Uniswap Governance Proposals, see [Multi-Chain Governance](../multi-chain-proposals.md). +3. Create your proposal. This can be done either through an interface (e.g. [Tally](https://tally.xyz/gov/uniswap)) or through writing the calldata for more complicated proposal logic. If the proposal passed, this calldata will execute. If writing the calldata yourself, please review the logic with a qualified Uniswap community member prior to posting the proposal. For more information on developing and advancing multi-chain Uniswap Governance Proposals, see [Multi-Chain Governance](./05-multi-chain-proposals.md). 4. Ensure that at least 1 million UNI is delegated to your address in order to submit a proposal, or find a delegate who has enough delegated UNI to meet the proposal threshold to propose on your behalf. diff --git a/docs/concepts/multi-chain-proposals.md b/docs/concepts/governance/05-multi-chain-proposals.md similarity index 100% rename from docs/concepts/multi-chain-proposals.md rename to docs/concepts/governance/05-multi-chain-proposals.md diff --git a/docs/concepts/governance/05-glossary.md b/docs/concepts/governance/06-glossary.md similarity index 100% rename from docs/concepts/governance/05-glossary.md rename to docs/concepts/governance/06-glossary.md From 25e149d7c94071c5631c4ea6db9bd4bd89f9cc31 Mon Sep 17 00:00:00 2001 From: John Feras Date: Mon, 3 Feb 2025 13:46:57 -0500 Subject: [PATCH 12/16] Added multi-chain example files --- examples/multi-chain/arbitrum-example.sol | 78 ++++++++++++++++++++++ examples/multi-chain/avalanche-example.sol | 72 ++++++++++++++++++++ examples/multi-chain/opstack-example.sol | 67 +++++++++++++++++++ examples/multi-chain/polygon-example.sol | 58 ++++++++++++++++ examples/multi-chain/scroll-example.sol | 66 ++++++++++++++++++ examples/multi-chain/taiko-example.sol | 54 +++++++++++++++ examples/multi-chain/wormhole-example.sol | 73 ++++++++++++++++++++ examples/multi-chain/zksync-example.sol | 77 +++++++++++++++++++++ 8 files changed, 545 insertions(+) create mode 100644 examples/multi-chain/arbitrum-example.sol create mode 100644 examples/multi-chain/avalanche-example.sol create mode 100644 examples/multi-chain/opstack-example.sol create mode 100644 examples/multi-chain/polygon-example.sol create mode 100644 examples/multi-chain/scroll-example.sol create mode 100644 examples/multi-chain/taiko-example.sol create mode 100644 examples/multi-chain/wormhole-example.sol create mode 100644 examples/multi-chain/zksync-example.sol diff --git a/examples/multi-chain/arbitrum-example.sol b/examples/multi-chain/arbitrum-example.sol new file mode 100644 index 000000000..f7796eef8 --- /dev/null +++ b/examples/multi-chain/arbitrum-example.sol @@ -0,0 +1,78 @@ +// SPDX-License-Identifier: UNLICENSED +pragma solidity ^0.8.28; + +import {console2} from "forge-std/console2.sol"; +import {Script} from "forge-std/script.sol"; +import {IUniswapV3Factory} from "lib/v3-core/contracts/interfaces/IUniswapV3Factory.sol"; + +interface IUniswapGovernorBravoDelegator { + function propose( + address[] memory targets, + uint256[] memory values, + string[] memory signatures, + bytes[] memory calldatas, + string memory description + ) external returns (uint256); +} + +contract EthereumToArbitrumSender is Script { + // target address for UniSwap DAO governor bravo delegate + address uniswapGovernorBravoDelegatorAddress = 0x408ED6354d4973f66138C91495F2f2FCbd8724C3; + + // Address of the Arbitrum Inbox on Ethereum (L1) + address constant INBOX_ADDRESS = 0x4Dbd4fc535Ac27206064B68FfCf827b0A60BAB3f; + + // target addresses on Arbitrum + address v3FactoryTargetAddress = 0x1F98431c8aD98523631AE4a59f267346ea31F984; + + // function to create a proposal on Ethereum L1 to enable a fee amount on the Uniswap V3 Factory on Arbitrum. + // Upon execution of the proposal, the Inbox contract function createRetryableTicket is called, containing calldata for the + // uniswap v3 factory to enable a fee amount on Uniswap V3 Factory on Arbitrum + function proposeForExecutionOnArbitrum() external payable { + // setup calldata for the Arbitrum uniswap v3Factory.enableFeeAmount call + bytes memory _v3FactoryEnableFeeAmounCalldata = + abi.encodeWithSelector(IUniswapV3Factory.enableFeeAmount.selector, 10000, 205); + + // setup calldata for the creating retryable ticket + uint256 callValue = 0; // Amount of ETH to send with the call on Arbitrum + uint256 maxSubmissionCost = 0.01 ether; // Estimated cost for submission + uint256 maxGas = 1_000_000; // Maximum gas for Arbitrum execution + uint256 gasPriceBid = 1 gwei; // Gas price for Arbitrum execution + + // Define refund addresses for excess fees and call value + address excessFeeRefundAddress = msg.sender; + address callValueRefundAddress = msg.sender; + + // setup calldata for the proposal + bytes memory _proposalCalldata = abi.encode( + v3FactoryTargetAddress, + callValue, + maxSubmissionCost, + excessFeeRefundAddress, + callValueRefundAddress, + callValueRefundAddress, + maxGas, + gasPriceBid, + _v3FactoryEnableFeeAmounCalldata + ); + + // make the proposal on the L1 side + IUniswapGovernorBravoDelegator governor = IUniswapGovernorBravoDelegator(uniswapGovernorBravoDelegatorAddress); + address[] memory _targets = new address[](1); + uint256[] memory _values = new uint256[](1); + string[] memory _signatures = new string[](1); + bytes[] memory _calldatas = new bytes[](1); + _targets[0] = INBOX_ADDRESS; + _values[0] = 0; + _signatures[0] = "createRetryableTicket(address,uint256,uint256,address,address,uint256,uint256,bytes)"; + _calldatas[0] = _proposalCalldata; + uint256 _proposalId = governor.propose( + _targets, + _values, + _signatures, + _calldatas, + "Proposal to enable 10000 fee amount of 205 on Uniswap V3 Factory on Arbitrum" + ); + console2.log("Proposal ID: %d", _proposalId); + } +} diff --git a/examples/multi-chain/avalanche-example.sol b/examples/multi-chain/avalanche-example.sol new file mode 100644 index 000000000..4e95434b6 --- /dev/null +++ b/examples/multi-chain/avalanche-example.sol @@ -0,0 +1,72 @@ +// SPDX-License-Identifier: UNLICENSED +pragma solidity ^0.8.28; + +import {console2} from "forge-std/console2.sol"; +import {Script} from "forge-std/script.sol"; +import {IUniswapV3Factory} from "lib/v3-core/contracts/interfaces/IUniswapV3Factory.sol"; + +interface IUniswapGovernorBravoDelegator { + // function to create a proposal on Ethereum L1 using the Uniswap Governor Bravo Delegator contract + function propose( + address[] memory targets, + uint256[] memory values, + string[] memory signatures, + bytes[] memory calldatas, + string memory description + ) external returns (uint256); +} + +contract EthereumToAvaxSender is Script { + // target address for UniSwap DAO governor bravo delegate + address uniswapGovernorBravoDelegatorAddress = 0x408ED6354d4973f66138C91495F2f2FCbd8724C3; + + // Address of the LayerZero:EndpointV2 contract on Ethereum (L1) + address constant LAYERZERO_ENDPOINT_ADDRESS = 0x1a44076050125825900e736c501f859c50fE728c; + + // Address of the OmniChainExecutor contract on AVAX + address constant OMNICHAIN_EXECUTOR_ADDRESS = 0x341c1511141022cf8eE20824Ae0fFA3491F1302b; + + // target addresses on AVAX + address v3FactoryTargetAddress = 0x740b1c1de25031C31FF4fC9A62f554A55cdC1baD; + + // AVAX chainId + uint256 constant CHAIN_ID = 43114; + + // function to create a proposal on Ethereum L1 to enable a fee amount on the Uniswap V3 Factory on AVAX. + // Upon execution of the proposal, the WormholeMessageSender contract function sendMessage is called, containing calldata for the + // uniswap v3 factory to enable a fee amount on Uniswap V3 Factory on AVAX. + function proposeForExecutionOnAvax() external payable { + // setup calldata for the AVAX uniswap v3Factory.enableFeeAmount call + bytes memory _v3FactoryEnableFeeAmounCalldata = + abi.encodeWithSelector(IUniswapV3Factory.enableFeeAmount.selector, 10000, 205); + + // encode the calldata for the LayerZeroEndpoint.send call + bytes memory _sendCalldata = abi.encode( + CHAIN_ID, // Destination chain ID + OMNICHAIN_EXECUTOR_ADDRESS, // Address on the destination chain + _v3FactoryEnableFeeAmounCalldata, // Message payload + address(this), // Refund address for unused gas + address(0), // Address for paying fees in ZRO (if used) + 0 // Parameters for adapter services (e.g., gas limits) + ); + + // make the proposal on the L1 side + IUniswapGovernorBravoDelegator governor = IUniswapGovernorBravoDelegator(uniswapGovernorBravoDelegatorAddress); + address[] memory _targets = new address[](1); + uint256[] memory _values = new uint256[](1); + string[] memory _signatures = new string[](1); + bytes[] memory _calldatas = new bytes[](1); + _targets[0] = LAYERZERO_ENDPOINT_ADDRESS; + _values[0] = 0; + _signatures[0] = "send(uint16,bytes,bytes,address,address,bytes)"; + _calldatas[0] = _sendCalldata; + uint256 _proposalId = governor.propose( + _targets, + _values, + _signatures, + _calldatas, + "Proposal to enable 10000 fee amount of 205 on Uniswap V3 Factory on AVAX" + ); + console2.log("Proposal ID: %d", _proposalId); + } +} diff --git a/examples/multi-chain/opstack-example.sol b/examples/multi-chain/opstack-example.sol new file mode 100644 index 000000000..b5002b43e --- /dev/null +++ b/examples/multi-chain/opstack-example.sol @@ -0,0 +1,67 @@ +// SPDX-License-Identifier: UNLICENSED +pragma solidity ^0.8.28; + +import {console2} from "forge-std/console2.sol"; +import {Script} from "forge-std/script.sol"; +import {IUniswapV3Factory} from "lib/v3-core/contracts/interfaces/IUniswapV3Factory.sol"; + +interface ICrossChainAccount { + function forward(address target, bytes memory data) external; +} + +interface IUniswapGovernorBravoDelegator { + function propose( + address[] memory targets, + uint256[] memory values, + string[] memory signatures, + bytes[] memory calldatas, + string memory description + ) external returns (uint256); +} + +contract OptimismExample is Script { + // target address for the cross domain messenger on Ethereum L1 responsible for sending messages to Optimism + // (this address would be different for Base, Blast, or Zora) + address constant l1CrossDomainMessengerAddress = 0x25ace71c97B33Cc4729CF772ae268934F7ab5fA1; + + // target addresses on Optimism (these addresses would be different for various target chains) + address constant v3FactoryTargetAddress = 0x1F98431c8aD98523631AE4a59f267346ea31F984; + address constant crossChainAccountTargetAddress = 0xa1dD330d602c32622AA270Ea73d078B803Cb3518; + + // target address for UniSwap DAO governor bravo delegate contract on Ethereum L1 + address uniswapGovernorBravoDelegatorAddress = 0x408ED6354d4973f66138C91495F2f2FCbd8724C3; + + // function to create a proposal on Ethereum L1 to enable a fee amount on the Uniswap V3 Factory on Optimism. + // Upon execution of the proposal, the L1CrossDomainMessenger function sendMessage is called containing calldata for the + // CrossChainAccount.forward function on Optimism that will be called containing calldata for the uniswap v3 factory + // to enable a fee amount on Uniswap V3 Factory on Optimism + function proposeForExecutionOnOptimism() public { + // setup calldata for the Optimism uniswap v3Factory.enableFeeAmount call + bytes memory _v3FactoryEnableFeeAmounCalldata = + abi.encodeWithSelector(IUniswapV3Factory.enableFeeAmount.selector, 10000, 205); + + // encode the calldata for the CrossChainAccount.forward call + bytes memory _messageForwardCalldata = abi.encodeWithSelector( + ICrossChainAccount.forward.selector, v3FactoryTargetAddress, _v3FactoryEnableFeeAmounCalldata + ); + + // make the proposal on the L1 side + IUniswapGovernorBravoDelegator governor = IUniswapGovernorBravoDelegator(uniswapGovernorBravoDelegatorAddress); + address[] memory _targets = new address[](1); + uint256[] memory _values = new uint256[](1); + string[] memory _signatures = new string[](1); + bytes[] memory _calldatas = new bytes[](1); + _targets[0] = l1CrossDomainMessengerAddress; + _values[0] = 0; + _signatures[0] = "sendMessage(address,bytes,uint256)"; + _calldatas[0] = abi.encode(address(crossChainAccountTargetAddress), _messageForwardCalldata, 500_000); + uint256 _proposalId = governor.propose( + _targets, + _values, + _signatures, + _calldatas, + "Proposal to enable 10000 fee amount of 205 on Uniswap V3 Factory on Optimism" + ); + console2.log("Proposal ID: %d", _proposalId); + } +} diff --git a/examples/multi-chain/polygon-example.sol b/examples/multi-chain/polygon-example.sol new file mode 100644 index 000000000..7d201a5c0 --- /dev/null +++ b/examples/multi-chain/polygon-example.sol @@ -0,0 +1,58 @@ +// SPDX-License-Identifier: UNLICENSED +pragma solidity ^0.8.28; + +import {console2} from "forge-std/console2.sol"; +import {Script} from "forge-std/script.sol"; +import {IUniswapV3Factory} from "lib/v3-core/contracts/interfaces/IUniswapV3Factory.sol"; + +interface IUniswapGovernorBravoDelegator { + function propose( + address[] memory targets, + uint256[] memory values, + string[] memory signatures, + bytes[] memory calldatas, + string memory description + ) external returns (uint256); +} + +contract PolygonExample is Script { + // target address for UniSwap DAO governor bravo delegate + address uniswapGovernorBravoDelegatorAddress = 0x408ED6354d4973f66138C91495F2f2FCbd8724C3; + + // Address of the FxRoot contract Ethereum (L1) + address constant FXROOT_ADDRESS = 0xfe5e5D361b2ad62c541bAb87C45a0B9B018389a2; + + // target addresses on Polygon + address v3FactoryTargetAddress = 0x1F98431c8aD98523631AE4a59f267346ea31F984; + + // function to create a proposal on Ethereum L1 to enable a fee amount on the Uniswap V3 Factory on Polygon. + // Upon execution of the proposal, the FxRoot contract function sendMessageToChild is called, containing calldata for the + // uniswap v3 factory to enable a fee amount on Uniswap V3 Factory on Polygon. + function proposeForExecutionOnPolygon() external payable { + // setup calldata for the uniswap v3Factory.enableFeeAmount call + bytes memory _v3FactoryEnableFeeAmounCalldata = + abi.encodeWithSelector(IUniswapV3Factory.enableFeeAmount.selector, 10000, 205); + + // setup calldata for calling FxRoot.sendMessageToChild + bytes memory _proposalCalldata = abi.encode(v3FactoryTargetAddress, _v3FactoryEnableFeeAmounCalldata); + + // make the proposal on the L1 side + IUniswapGovernorBravoDelegator governor = IUniswapGovernorBravoDelegator(uniswapGovernorBravoDelegatorAddress); + address[] memory _targets = new address[](1); + uint256[] memory _values = new uint256[](1); + string[] memory _signatures = new string[](1); + bytes[] memory _calldatas = new bytes[](1); + _targets[0] = FXROOT_ADDRESS; + _values[0] = 0; + _signatures[0] = "sendMessageToChild(address,bytes)"; + _calldatas[0] = _proposalCalldata; + uint256 _proposalId = governor.propose( + _targets, + _values, + _signatures, + _calldatas, + "Proposal to enable 10000 fee amount of 205 on Uniswap V3 Factory on Polygon" + ); + console2.log("Proposal ID: %d", _proposalId); + } +} diff --git a/examples/multi-chain/scroll-example.sol b/examples/multi-chain/scroll-example.sol new file mode 100644 index 000000000..29f196a91 --- /dev/null +++ b/examples/multi-chain/scroll-example.sol @@ -0,0 +1,66 @@ +// SPDX-License-Identifier: UNLICENSED +pragma solidity ^0.8.28; + +import {console2} from "forge-std/console2.sol"; +import {Script} from "forge-std/script.sol"; +import {IUniswapV3Factory} from "lib/v3-core/contracts/interfaces/IUniswapV3Factory.sol"; + +interface ICrossChainAccount { + function forward(address target, bytes memory data) external; +} + +interface IUniswapGovernorBravoDelegator { + function propose( + address[] memory targets, + uint256[] memory values, + string[] memory signatures, + bytes[] memory calldatas, + string memory description + ) external returns (uint256); +} + +contract ScrollExample is Script { + // target address for the cross domain messenger on Ethereum L1 responsible for sending messages to Scroll + address constant L1_SCROLL_MESSENGER_ADDRESS = 0x6774Bcbd5ceCeF1336b5300fb5186a12DDD8b367; + + // target addresses on Scroll + address constant v3FactoryTargetAddress = 0x70C62C8b8e801124A4Aa81ce07b637A3e83cb919; + address constant crossChainAccountTargetAddress = 0x3b441EEa8e042dcB9517b0FC1afFb0d8CBa0f388; + + // target address for UniSwap DAO governor bravo delegate contract on Ethereum L1 + address uniswapGovernorBravoDelegatorAddress = 0x408ED6354d4973f66138C91495F2f2FCbd8724C3; + + // function to create a proposal on Ethereum L1 to enable a fee amount on the Uniswap V3 Factory on Scroll. + // Upon execution of the proposal, the L1ScrollMessenger function sendMessage is called containing calldata for the + // CrossChainAccount.forward function on Scroll that will be called containing calldata for the uniswap v3 factory + // to enable a fee amount on Uniswap V3 Factory on Scroll + function proposeForExecutionOnScroll() public { + // setup calldata for the Scroll uniswap v3Factory.enableFeeAmount call + bytes memory _v3FactoryEnableFeeAmounCalldata = + abi.encodeWithSelector(IUniswapV3Factory.enableFeeAmount.selector, 10000, 205); + + // encode the calldata for the CrossChainAccount.forward call + bytes memory _messageForwardCalldata = abi.encodeWithSelector( + ICrossChainAccount.forward.selector, v3FactoryTargetAddress, _v3FactoryEnableFeeAmounCalldata + ); + + // make the proposal on the L1 side + IUniswapGovernorBravoDelegator governor = IUniswapGovernorBravoDelegator(uniswapGovernorBravoDelegatorAddress); + address[] memory _targets = new address[](1); + uint256[] memory _values = new uint256[](1); + string[] memory _signatures = new string[](1); + bytes[] memory _calldatas = new bytes[](1); + _targets[0] = L1_SCROLL_MESSENGER_ADDRESS; + _values[0] = 0; + _signatures[0] = "sendMessage(address,uint256,bytes,uint256)"; + _calldatas[0] = abi.encode(address(crossChainAccountTargetAddress), 0, _messageForwardCalldata, 500_000); + uint256 _proposalId = governor.propose( + _targets, + _values, + _signatures, + _calldatas, + "Proposal to enable 10000 fee amount of 205 on Uniswap V3 Factory on Scroll" + ); + console2.log("Proposal ID: %d", _proposalId); + } +} diff --git a/examples/multi-chain/taiko-example.sol b/examples/multi-chain/taiko-example.sol new file mode 100644 index 000000000..3288ee89c --- /dev/null +++ b/examples/multi-chain/taiko-example.sol @@ -0,0 +1,54 @@ +// SPDX-License-Identifier: UNLICENSED +pragma solidity ^0.8.28; + +import {console2} from "forge-std/console2.sol"; +import {Script} from "forge-std/script.sol"; +import {IUniswapV3Factory} from "lib/v3-core/contracts/interfaces/IUniswapV3Factory.sol"; + +interface IUniswapGovernorBravoDelegator { + function propose( + address[] memory targets, + uint256[] memory values, + string[] memory signatures, + bytes[] memory calldatas, + string memory description + ) external returns (uint256); +} + +contract TaikoExample is Script { + // target address for the Taiko Signal Service contract on Ethereum L1 responsible for sending messages to Taiko + address constant TAIKO_SIGNAL_SERVICE_ADDRESS = 0x9e0a24964e5397B566c1ed39258e21aB5E35C77C; + + // target addresses on Taiko + address constant v3FactoryTargetAddress = 0x75FC67473A91335B5b8F8821277262a13B38c9b3; + + // target address for UniSwap DAO governor bravo delegate contract on Ethereum L1 + address uniswapGovernorBravoDelegatorAddress = 0x408ED6354d4973f66138C91495F2f2FCbd8724C3; + + // function to create a proposal on Ethereum L1 to enable a fee amount on the Uniswap V3 Factory on Taiko. + // Upon execution of the proposal, the Signal Service function "emitSignal" is called containing calldata for the + // uniswap v3 factory to enable a fee amount on Uniswap V3 Factory on Taiko. + function proposeForExecutionOnTaiko() public { + // setup calldata for the Taiko uniswap v3Factory.enableFeeAmount call + bytes memory _v3FactoryEnableFeeAmounCalldata = abi.encode(int24(10000), int24(205)); + + // make the proposal on the L1 side + IUniswapGovernorBravoDelegator governor = IUniswapGovernorBravoDelegator(uniswapGovernorBravoDelegatorAddress); + address[] memory _targets = new address[](1); + uint256[] memory _values = new uint256[](1); + string[] memory _signatures = new string[](1); + bytes[] memory _calldatas = new bytes[](1); + _targets[0] = TAIKO_SIGNAL_SERVICE_ADDRESS; + _values[0] = 0; + _signatures[0] = "emitSignal(bytes)"; + _calldatas[0] = abi.encode(address(v3FactoryTargetAddress), _v3FactoryEnableFeeAmounCalldata); + uint256 _proposalId = governor.propose( + _targets, + _values, + _signatures, + _calldatas, + "Proposal to enable 10000 fee amount of 205 on Uniswap V3 Factory on Taiko" + ); + console2.log("Proposal ID: %d", _proposalId); + } +} diff --git a/examples/multi-chain/wormhole-example.sol b/examples/multi-chain/wormhole-example.sol new file mode 100644 index 000000000..0ab00dc51 --- /dev/null +++ b/examples/multi-chain/wormhole-example.sol @@ -0,0 +1,73 @@ +// SPDX-License-Identifier: UNLICENSED +pragma solidity ^0.8.28; + +import {console2} from "forge-std/console2.sol"; +import {Script} from "forge-std/script.sol"; +import {IUniswapV3Factory} from "lib/v3-core/contracts/interfaces/IUniswapV3Factory.sol"; + +interface IUniswapGovernorBravoDelegator { + // function to create a proposal on Ethereum L1 using the Uniswap Governor Bravo Delegator contract + function propose( + address[] memory targets, + uint256[] memory values, + string[] memory signatures, + bytes[] memory calldatas, + string memory description + ) external returns (uint256); +} + +contract EthereumToBnbChainSender is Script { + // Address of the WormholeMessageReceiver contract on BNB Chain + address constant WORMHOLE_MESSAGE_RECEIVER_ADDRESS = 0x341c1511141022cf8eE20824Ae0fFA3491F1302b; + + // BNB Chain chainId + uint256 constant CHAIN_ID = 56; + + // target addresses on BNB Chain + address constant v3FactoryTargetAddress = 0xdB1d10011AD0Ff90774D0C6Bb92e5C5c8b4461F7; + + // target address for UniSwap DAO governor bravo delegate + address uniswapGovernorBravoDelegatorAddress = 0x408ED6354d4973f66138C91495F2f2FCbd8724C3; + + // Address of the WormholdMessageSender contract on Ethereum (L1) + address constant WORMHOLE_MESSAGE_SENDER_ADDRESS = 0xf5F4496219F31CDCBa6130B5402873624585615a; + + // function to create a proposal on Ethereum L1 to enable a fee amount on the Uniswap V3 Factory on BNB Chain. + // Upon execution of the proposal, the WormholeMessageSender contract function sendMessage is called, containing calldata for the + // uniswap v3 factory to enable a fee amount on Uniswap V3 Factory on BNB Chain. + function proposeForExecutionOnBnbChain() external payable { + // setup calldata for the destination chain uniswap v3Factory.enableFeeAmount call + bytes memory _v3FactoryEnableFeeAmounCalldata = + abi.encodeWithSelector(IUniswapV3Factory.enableFeeAmount.selector, 10000, 205); + + // setup calldata for calling WormholeMessageSender sendMessage + address[] memory _sendMessageTargets = new address[](1); + uint256[] memory _sendMessageValues = new uint256[](1); + bytes[] memory _sendMessageCalldatas = new bytes[](1); + _sendMessageTargets[0] = v3FactoryTargetAddress; + _sendMessageValues[0] = 0; + _sendMessageCalldatas[0] = _v3FactoryEnableFeeAmounCalldata; + bytes memory _sendMessageCalldata = abi.encode( + _sendMessageTargets, _sendMessageValues, _sendMessageCalldatas, WORMHOLE_MESSAGE_RECEIVER_ADDRESS, CHAIN_ID + ); + + // make the proposal on the L1 side + IUniswapGovernorBravoDelegator governor = IUniswapGovernorBravoDelegator(uniswapGovernorBravoDelegatorAddress); + address[] memory _targets = new address[](1); + uint256[] memory _values = new uint256[](1); + string[] memory _signatures = new string[](1); + bytes[] memory _calldatas = new bytes[](1); + _targets[0] = WORMHOLE_MESSAGE_SENDER_ADDRESS; + _values[0] = 0; + _signatures[0] = "sendMessage(address[],uint256[],bytes[],address,uint16)"; + _calldatas[0] = _sendMessageCalldata; + uint256 _proposalId = governor.propose( + _targets, + _values, + _signatures, + _calldatas, + "Proposal to enable 10000 fee amount of 205 on Uniswap V3 Factory on BNB Chain" + ); + console2.log("Proposal ID: %d", _proposalId); + } +} diff --git a/examples/multi-chain/zksync-example.sol b/examples/multi-chain/zksync-example.sol new file mode 100644 index 000000000..204c9bd2f --- /dev/null +++ b/examples/multi-chain/zksync-example.sol @@ -0,0 +1,77 @@ +// SPDX-License-Identifier: UNLICENSED +pragma solidity ^0.8.28; + +import {console2} from "forge-std/console2.sol"; +import {Script} from "forge-std/script.sol"; +import {IUniswapV3Factory} from "lib/v3-core/contracts/interfaces/IUniswapV3Factory.sol"; + +interface IUniswapGovernorBravoDelegator { + function propose( + address[] memory targets, + uint256[] memory values, + string[] memory signatures, + bytes[] memory calldatas, + string memory description + ) external returns (uint256); +} + +contract EthereumToZkSyncSender is Script { + // target address for UniSwap DAO governor bravo delegate + address uniswapGovernorBravoDelegatorAddress = 0x408ED6354d4973f66138C91495F2f2FCbd8724C3; + + // Address of the MailboxFacet contract on Ethereum (L1) + address constant MAILBOX_FACET_ADDRESS = 0x63b5EC36B09384fFA7106A80Ec7cfdFCa521fD08; + + // target addresses on ZkSync Era + address v3FactoryTargetAddress = 0x8FdA5a7a8dCA67BBcDd10F02Fa0649A937215422; + + // function to create a proposal on Ethereum L1 to enable a fee amount on the Uniswap V3 Factory on ZkSync Era. + // Upon execution of the proposal, the MailboxFacet contract function requestL2Transaction is called, containing calldata for the + // uniswap v3 factory to enable a fee amount on Uniswap V3 Factory on ZkSync Era. + function proposeForExecutionOnZkSync() external payable { + // setup calldata for the ZkSync Era uniswap v3Factory.enableFeeAmount call + bytes memory _v3FactoryEnableFeeAmounCalldata = + abi.encodeWithSelector(IUniswapV3Factory.enableFeeAmount.selector, 10000, 205); + + // setup calldata for the creating retryable ticket + uint256 callValue = 0; // Amount of ETH to send with the call on ZkSync Era + uint256 maxSubmissionCost = 0.01 ether; // Estimated cost for submission + uint256 maxGas = 1_000_000; // Maximum gas for ZkSync Era execution + + // Define refund addresses + address refundRecipientAddress = msg.sender; + + // Define known L2 dependency addresses + bytes[] memory knownL2DependentAddresses = new bytes[](1); + + // setup calldata for the proposal + bytes memory _proposalCalldata = abi.encode( + v3FactoryTargetAddress, + callValue, + _v3FactoryEnableFeeAmounCalldata, + maxGas, + maxSubmissionCost, + knownL2DependentAddresses, + refundRecipientAddress + ); + + // make the proposal on the L1 side + IUniswapGovernorBravoDelegator governor = IUniswapGovernorBravoDelegator(uniswapGovernorBravoDelegatorAddress); + address[] memory _targets = new address[](1); + uint256[] memory _values = new uint256[](1); + string[] memory _signatures = new string[](1); + bytes[] memory _calldatas = new bytes[](1); + _targets[0] = MAILBOX_FACET_ADDRESS; + _values[0] = 0; + _signatures[0] = "requestL2Transaction(address,uint256,bytes,uint256,uint256,bytes[],address)"; + _calldatas[0] = _proposalCalldata; + uint256 _proposalId = governor.propose( + _targets, + _values, + _signatures, + _calldatas, + "Proposal to enable 10000 fee amount of 205 on Uniswap V3 Factory on ZkSync Era" + ); + console2.log("Proposal ID: %d", _proposalId); + } +} From 8474ffc2795ae8cd151117d1f769b7ce22d12f19 Mon Sep 17 00:00:00 2001 From: John Feras Date: Mon, 3 Feb 2025 15:28:50 -0500 Subject: [PATCH 13/16] Added links to example files --- .../governance/05-multi-chain-proposals.md | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/docs/concepts/governance/05-multi-chain-proposals.md b/docs/concepts/governance/05-multi-chain-proposals.md index 0879828b5..d33dddcfe 100644 --- a/docs/concepts/governance/05-multi-chain-proposals.md +++ b/docs/concepts/governance/05-multi-chain-proposals.md @@ -54,7 +54,7 @@ The table below contains links to the various proposal methods for each chain. Arbitrum uses an approach where the owner of the V3Factory is a special aliased address (offset by the value `0x1111000000000000000000000000000000001111`) that (when the offset is subtracted away) is the L1 address of the Uniswap DAO Timelock contract. A Solidity code example for sending a proposal to Arbitrum is shown below. Proposal calldata on mainnet intended for Arbitrum should be forwarded through a call to `createRetryableTicket` on the Arbitrum Inbox contract. -The call would have wrapped calldata for a Uniswap contract function call that would effect the change. An example: +The call would have wrapped calldata for a Uniswap contract function call that would effect the change. An example (see on github [here](https://github.com/ScopeLift/uniswap-docs-fork/blob/b0da9f6596b3fcc9578adcb32f89a6b0472c0a1a/examples/multi-chain/arbitrum-example.sol) ): ``` // SPDX-License-Identifier: UNLICENSED @@ -141,7 +141,7 @@ contract EthereumToArbitrumSender is Script { Avalanche makes use of a contract called `OmnichainGovernanceExecutor` to receive proposals from Ethereum mainnet. Proposal calldata on mainnet intended for Avalanche should be forwarded through a call to `send` on the contract called `LayerZero:EndpointV2`. -A Solidity code example for sending a proposal to AVAX is below. +A Solidity code example for sending a proposal to AVAX is below. See on github [here](https://github.com/ScopeLift/uniswap-docs-fork/blob/b0da9f6596b3fcc9578adcb32f89a6b0472c0a1a/examples/multi-chain/avalanche-example.sol): ``` // SPDX-License-Identifier: UNLICENSED @@ -225,7 +225,7 @@ Filecoin EVM ?? TODO: Add details ## OP Stack Proposal calldata on mainnet meant for an OP stack chain should be forwarded through a call to sendMessage on the chain's corresponding mainnet `L1CrossDomainMessenger` contract. -The call would have doubly-wrapped calldata for the `CrossChainAccount:forward` function as well as the ultimate Uniswap contract function call that would effect the change. An example for Optimism: +The call would have doubly-wrapped calldata for the `CrossChainAccount:forward` function as well as the ultimate Uniswap contract function call that would effect the change. An example for Optimism (see on github [here](https://github.com/ScopeLift/uniswap-docs-fork/blob/b0da9f6596b3fcc9578adcb32f89a6b0472c0a1a/examples/multi-chain/opstack-example.sol)): ``` // SPDX-License-Identifier: UNLICENSED @@ -431,7 +431,7 @@ The relevant OP Stack contract addresses are as follows: Polygon makes use of an L1 contract called FxRoot for sending messages (in the case of Uniswap, executable proposals), and contracts called FxChild and EthereumProxy on the Polygon chain for forwarding the executable proposals to Uniswap V3 on Polygon. Proposal calldata on mainnet meant for Polygon should be forwarded through a call to `sendMessageToChild` on the chain's corresponding mainnet `FxRoot` contract. -The call would have wrapped calldata for a Uniswap contract function call that would effect the change. The FxRoot/FxChild tunnel would bridge that message to Polygon where FxChild would route the message the EthereumProxy parent contract of UniSwap V3. An example: +The call would have wrapped calldata for a Uniswap contract function call that would effect the change. The FxRoot/FxChild tunnel would bridge that message to Polygon where FxChild would route the message the EthereumProxy parent contract of UniSwap V3. An example (see on github [here](https://github.com/ScopeLift/uniswap-docs-fork/blob/b0da9f6596b3fcc9578adcb32f89a6b0472c0a1a/examples/multi-chain/polygon-example.sol)): ``` // SPDX-License-Identifier: UNLICENSED @@ -501,7 +501,7 @@ Polygon zkEVM uses a different approach than Polygon TODO: Add details ## Scroll Scroll also makes use of the CrossChainAccount contract for receiving proposals from Ethereum mainnet, but the way that messages are sent is different from the other OP Stack approaches. -Proposal calldata on mainnet meant for the Scroll chain should be forwarded through a call to `sendMessage` on the chain's corresponding mainnet `L1ScrollMessenger` contract. +Proposal calldata on mainnet meant for the Scroll chain should be forwarded through a call to `sendMessage` on the chain's corresponding mainnet `L1ScrollMessenger` contract. An example (see on github [here](https://github.com/ScopeLift/uniswap-docs-fork/blob/b0da9f6596b3fcc9578adcb32f89a6b0472c0a1a/examples/multi-chain/scroll-example.sol)): ``` // SPDX-License-Identifier: UNLICENSED @@ -576,7 +576,7 @@ contract ScrollExample is Script { Taiko makes use of a contract called `InvokableAccount`. Proposal calldata on mainnet meant for the Taiko chain should be forwarded through a call to`emitSignal`on the chain's corresponding mainnet`SignalService` contract. -The call would have wrapped calldata for the Uniswap contract function call that would effect the change. An example: +The call would have wrapped calldata for the Uniswap contract function call that would effect the change. An example (see on github [here](https://github.com/ScopeLift/uniswap-docs-fork/blob/b0da9f6596b3fcc9578adcb32f89a6b0472c0a1a/examples/multi-chain/taiko-example.sol)): ``` // SPDX-License-Identifier: UNLICENSED @@ -641,7 +641,7 @@ There are 5 chains where Uniswap V3 is deployed that use the same Ethereum mainn Proposal calldata on mainnet meant for a target chain using Wormhole should be forwarded through a call to the `sendMessage` function of that contract. The function takes both a target address for the message receiving contract on the destination chain, as well as the Chain ID of the destination chain as parameters, allowing the function to be used for sending to multiple destination chains. -A Solidity code example for sending a proposal to one these chains (BNB Chain) is provided below. +A Solidity code example for sending a proposal to one these chains (BNB Chain) is provided below. See on github [here](https://github.com/ScopeLift/uniswap-docs-fork/blob/b0da9f6596b3fcc9578adcb32f89a6b0472c0a1a/examples/multi-chain/wormhole-example.sol): ``` // SPDX-License-Identifier: UNLICENSED @@ -733,7 +733,7 @@ Because 5 networks all use the same Wormhole-based approach for communication, t ZkSync Era uses the Arbitrum-style approach where the parent of the V3Factory contract is an aliased address, but the method of sending the proposal is different than the one used for Arbitrum. Proposal calldata on mainnet intended for ZkSync Era should be forwarded through a call to `requestL2Transaction` on the `MailBoxFacet` contract. -The call would have wrapped calldata for a Uniswap contract function call that would effect the change. An example: +The call would have wrapped calldata for a Uniswap contract function call that would effect the change. An example (see on github [here](https://github.com/ScopeLift/uniswap-docs-fork/blob/b0da9f6596b3fcc9578adcb32f89a6b0472c0a1a/examples/multi-chain/zksync-example.sol)): ``` // SPDX-License-Identifier: UNLICENSED From e538fe42607e9242a9610a593e8f8740c4a3d370 Mon Sep 17 00:00:00 2001 From: wildmolasses Date: Mon, 24 Feb 2025 14:32:08 -0500 Subject: [PATCH 14/16] table formatting, multichain, directory rename --- docs/concepts/governance/02-process.md | 2 +- ...roposals.md => 05-multichain-proposals.md} | 243 ++++++++++-------- .../arbitrum-example.sol | 2 +- .../avalanche-example.sol | 2 +- .../opstack-example.sol | 2 +- .../polygon-example.sol | 2 +- .../scroll-example.sol | 2 +- .../taiko-example.sol | 2 +- .../wormhole-example.sol | 2 +- .../zksync-example.sol | 2 +- 10 files changed, 141 insertions(+), 120 deletions(-) rename docs/concepts/governance/{05-multi-chain-proposals.md => 05-multichain-proposals.md} (85%) rename examples/{multi-chain => multichain-proposals}/arbitrum-example.sol (98%) rename examples/{multi-chain => multichain-proposals}/avalanche-example.sol (98%) rename examples/{multi-chain => multichain-proposals}/opstack-example.sol (98%) rename examples/{multi-chain => multichain-proposals}/polygon-example.sol (98%) rename examples/{multi-chain => multichain-proposals}/scroll-example.sol (98%) rename examples/{multi-chain => multichain-proposals}/taiko-example.sol (98%) rename examples/{multi-chain => multichain-proposals}/wormhole-example.sol (98%) rename examples/{multi-chain => multichain-proposals}/zksync-example.sol (98%) diff --git a/docs/concepts/governance/02-process.md b/docs/concepts/governance/02-process.md index 78e2c1bb1..822e590bf 100644 --- a/docs/concepts/governance/02-process.md +++ b/docs/concepts/governance/02-process.md @@ -77,7 +77,7 @@ To create an onchain Governance Proposal: 2. Create a topic in the [Governance Forum](https://gov.uniswap.org/) titled "Governance Proposal — [Your Title Here]" and link to previous forum posts and the Temperature Check Snapshot poll. -3. Create your proposal. This can be done either through an interface (e.g. [Tally](https://tally.xyz/gov/uniswap)) or through writing the calldata for more complicated proposal logic. If the proposal passed, this calldata will execute. If writing the calldata yourself, please review the logic with a qualified Uniswap community member prior to posting the proposal. For more information on developing and advancing multi-chain Uniswap Governance Proposals, see [Multi-Chain Governance](./05-multi-chain-proposals.md). +3. Create your proposal. This can be done either through an interface (e.g. [Tally](https://tally.xyz/gov/uniswap)) or through writing the calldata for more complicated proposal logic. If the proposal passed, this calldata will execute. If writing the calldata yourself, please review the logic with a qualified Uniswap community member prior to posting the proposal. For more information on developing and advancing multichain Uniswap Governance Proposals, see [multichain proposals](./05-multichain-proposals.md). 4. Ensure that at least 1 million UNI is delegated to your address in order to submit a proposal, or find a delegate who has enough delegated UNI to meet the proposal threshold to propose on your behalf. diff --git a/docs/concepts/governance/05-multi-chain-proposals.md b/docs/concepts/governance/05-multichain-proposals.md similarity index 85% rename from docs/concepts/governance/05-multi-chain-proposals.md rename to docs/concepts/governance/05-multichain-proposals.md index d33dddcfe..aeec97873 100644 --- a/docs/concepts/governance/05-multi-chain-proposals.md +++ b/docs/concepts/governance/05-multichain-proposals.md @@ -1,9 +1,9 @@ --- -id: multi-chain-proposals -title: Multi-Chain Proposals +id: multichain-proposals +title: Multichain Proposals --- -This is a living document which represents the current process guidelines for developing and advancing multi-chain Uniswap Governance Proposals. It was last updated January 2025. +This is a living document which represents the current process guidelines for developing and advancing multichain Uniswap Governance Proposals. It was last updated March 2025. ## Introduction @@ -54,10 +54,10 @@ The table below contains links to the various proposal methods for each chain. Arbitrum uses an approach where the owner of the V3Factory is a special aliased address (offset by the value `0x1111000000000000000000000000000000001111`) that (when the offset is subtracted away) is the L1 address of the Uniswap DAO Timelock contract. A Solidity code example for sending a proposal to Arbitrum is shown below. Proposal calldata on mainnet intended for Arbitrum should be forwarded through a call to `createRetryableTicket` on the Arbitrum Inbox contract. -The call would have wrapped calldata for a Uniswap contract function call that would effect the change. An example (see on github [here](https://github.com/ScopeLift/uniswap-docs-fork/blob/b0da9f6596b3fcc9578adcb32f89a6b0472c0a1a/examples/multi-chain/arbitrum-example.sol) ): +The call would have wrapped calldata for a Uniswap contract function call that would effect the change. An example (see on github [here](https://github.com/ScopeLift/uniswap-docs-fork/blob/b0da9f6596b3fcc9578adcb32f89a6b0472c0a1a/examples/multichain-proposals/arbitrum-example.sol) ): ``` -// SPDX-License-Identifier: UNLICENSED +// SPDX-License-Identifier: MIT pragma solidity ^0.8.28; import {console2} from "forge-std/console2.sol"; @@ -141,10 +141,10 @@ contract EthereumToArbitrumSender is Script { Avalanche makes use of a contract called `OmnichainGovernanceExecutor` to receive proposals from Ethereum mainnet. Proposal calldata on mainnet intended for Avalanche should be forwarded through a call to `send` on the contract called `LayerZero:EndpointV2`. -A Solidity code example for sending a proposal to AVAX is below. See on github [here](https://github.com/ScopeLift/uniswap-docs-fork/blob/b0da9f6596b3fcc9578adcb32f89a6b0472c0a1a/examples/multi-chain/avalanche-example.sol): +A Solidity code example for sending a proposal to AVAX is below. See on github [here](https://github.com/ScopeLift/uniswap-docs-fork/blob/b0da9f6596b3fcc9578adcb32f89a6b0472c0a1a/examples/multichain-proposals/avalanche-example.sol): ``` -// SPDX-License-Identifier: UNLICENSED +// SPDX-License-Identifier: MIT pragma solidity ^0.8.28; import {console2} from "forge-std/console2.sol"; @@ -225,10 +225,10 @@ Filecoin EVM ?? TODO: Add details ## OP Stack Proposal calldata on mainnet meant for an OP stack chain should be forwarded through a call to sendMessage on the chain's corresponding mainnet `L1CrossDomainMessenger` contract. -The call would have doubly-wrapped calldata for the `CrossChainAccount:forward` function as well as the ultimate Uniswap contract function call that would effect the change. An example for Optimism (see on github [here](https://github.com/ScopeLift/uniswap-docs-fork/blob/b0da9f6596b3fcc9578adcb32f89a6b0472c0a1a/examples/multi-chain/opstack-example.sol)): +The call would have doubly-wrapped calldata for the `CrossChainAccount:forward` function as well as the ultimate Uniswap contract function call that would effect the change. An example for Optimism (see on github [here](https://github.com/ScopeLift/uniswap-docs-fork/blob/b0da9f6596b3fcc9578adcb32f89a6b0472c0a1a/examples/multichain-proposals/opstack-example.sol)): ``` -// SPDX-License-Identifier: UNLICENSED +// SPDX-License-Identifier: MIT pragma solidity ^0.8.28; import {console2} from "forge-std/console2.sol"; @@ -297,7 +297,7 @@ contract OptimismExample is Script { } ``` -Because 10 networks all use the same CrossChainAccount approach for communication, the only modifications to the above code snippet for those networks would be to change the first 3 address constant definitions. The definitions for all of the OP Stack based networks are below: +Because 10 networks all use the same CrossChainAccount approach for communication, the only modifications to the above code snippet for those networks would be to change the first 3 address constant definitions. :::warning **Linea**'s `sendMessage` reverses the order of the `fee` and `message` parameters. @@ -309,119 +309,140 @@ The relevant OP Stack contract addresses are as follows: Network - Contract Addresses + Contract + Address - Base - - l1CrossDomainMessengerAddress:
- 0x866E82a600A1414e583f7F13623F1aC5d58b0Afa
- v3FactoryTargetAddress:
- 0x33128a8fC17869897dcE68Ed026d694621f6FDfD
- crossChainAccountTarget:
- 0x31FAfd4889FA1269F7a13A66eE0fB458f27D72A9
- + Base + l1CrossDomainMessenger + 0x866E82a600A1414e583f7F13623F1aC5d58b0Afa - Blast - - l1CrossDomainMessengerAddress:
- 0x5D4472f31Bd9385709ec61305AFc749F0fA8e9d0
- v3FactoryTargetAddress:
- 0x792edAdE80af5fC680d96a2eD80A44247D2Cf6Fd
- crossChainAccountTarget:
- 0x2339C0d23b60739B3E5ABF201F05903D24A26C77
- + v3FactoryTargetAddress + 0x33128a8fC17869897dcE68Ed026d694621f6FDfD - Boba - - l1CrossDomainMessengerAddress:
- 0x6D4528d192dB72E282265D6092F4B872f9Dff69e
- v3FactoryTargetAddress:
- 0xFFCd7Aed9C627E82A765c3247d562239507f6f1B
- crossChainAccountTarget:
- 0x53163235746CeB81Da32293bb0932e1A599256B4
- + crossChainAccountTarget + 0x31FAfd4889FA1269F7a13A66eE0fB458f27D72A9 - Linea (see warning) - - l1CrossDomainMessengerAddress:
- 0xd19d4B5d358258f05D7B411E21A1460D11B0876F
- v3FactoryTargetAddress:
- 0x1111??
- crossChainAccountTarget:
- 0x581F86Da293A1D5Cd087a10E7227a75d2d2201A8
- + Blast + l1CrossDomainMessenger + 0x5D4472f31Bd9385709ec61305AFc749F0fA8e9d0 - Manta Pacific - - l1CrossDomainMessengerAddress:
- 0x635ba609680c55C3bDd0B3627b4c5dB21b13c310
- v3FactoryTargetAddress:
- 0x06D830e15081f65923674268121FF57Cc54e4e23
- crossChainAccountTarget:
- 0x683553d74D9779955a15d57D208234C956B6Eae6
- + v3FactoryTargetAddress + 0x792edAdE80af5fC680d96a2eD80A44247D2Cf6Fd - Mantle - - l1CrossDomainMessengerAddress:
- 0x676A795fe6E43C17c668de16730c3F690FEB7120
- v3FactoryTargetAddress:
- 0x0d922Fb1Bc191F64970ac40376643808b4B74Df9
- crossChainAccountTarget:
- 0x9b7aC6735b23578E81260acD34E3668D0cc6000A
- + crossChainAccountTarget + 0x2339C0d23b60739B3E5ABF201F05903D24A26C77 - Optimism - - l1CrossDomainMessengerAddress:
- 0x25ace71c97B33Cc4729CF772ae268934F7ab5fA1
- v3FactoryTargetAddress:
- 0x1F98431c8aD98523631AE4a59f267346ea31F984
- crossChainAccountTarget:
- 0xa1dD330d602c32622AA270Ea73d078B803Cb3518
- + Boba + l1CrossDomainMessenger + 0x6D4528d192dB72E282265D6092F4B872f9Dff69e - Redstone - - l1CrossDomainMessengerAddress:
- 0x592C1299e0F8331D81A28C0FC7352Da24eDB444a
- v3FactoryTargetAddress:
- 0xece75613Aa9b1680f0421E5B2eF376DF68aa83Bb
- crossChainAccountTarget:
- 0x2d00e94d78Fc307FC5E6195BBe2fB6aFC2FC07d4
- + v3FactoryTargetAddress + 0xFFCd7Aed9C627E82A765c3247d562239507f6f1B - Worldcoin - - l1CrossDomainMessengerAddress:
- 0xf931a81D18B1766d15695ffc7c1920a62b7e710a
- v3FactoryTargetAddress:
- 0x7a5028BDa40e7B173C278C5342087826455ea25a
- crossChainAccountTarget:
- 0xcb2436774C3e191c85056d248EF4260ce5f27A9D
- + crossChainAccountTarget + 0x53163235746CeB81Da32293bb0932e1A599256B4 - Worldcoin - - l1CrossDomainMessengerAddress:
- 0xdC40a14d9abd6F410226f1E6de71aE03441ca506
- v3FactoryTargetAddress:
- 0x7145F8aeef1f6510E92164038E1B6F8cB2c42Cbb
- crossChainAccountTarget:
- 0x36eEC182D0B24Df3DC23115D64DB521A93D5154f
- + Linea + l1CrossDomainMessenger + 0xd19d4B5d358258f05D7B411E21A1460D11B0876F + + + v3FactoryTargetAddress + 0x1111?? + + + crossChainAccountTarget + 0x581F86Da293A1D5Cd087a10E7227a75d2d2201A8 + + + Manta Pacific + l1CrossDomainMessenger + 0x635ba609680c55C3bDd0B3627b4c5dB21b13c310 + + + v3FactoryTargetAddress + 0x06D830e15081f65923674268121FF57Cc54e4e23 + + + crossChainAccountTarget + 0x683553d74D9779955a15d57D208234C956B6Eae6 + + + Mantle + l1CrossDomainMessenger + 0x676A795fe6E43C17c668de16730c3F690FEB7120 + + + v3FactoryTargetAddress + 0x0d922Fb1Bc191F64970ac40376643808b4B74Df9 + + + crossChainAccountTarget + 0x9b7aC6735b23578E81260acD34E3668D0cc6000A + + + Optimism + l1CrossDomainMessenger + 0x25ace71c97B33Cc4729CF772ae268934F7ab5fA1 + + + v3FactoryTargetAddress + 0x1F98431c8aD98523631AE4a59f267346ea31F984 + + + crossChainAccountTarget + 0xa1dD330d602c32622AA270Ea73d078B803Cb3518 + + + Redstone + l1CrossDomainMessenger + 0x592C1299e0F8331D81A28C0FC7352Da24eDB444a + + + v3FactoryTargetAddress + 0xece75613Aa9b1680f0421E5B2eF376DF68aa83Bb + + + crossChainAccountTarget + 0x2d00e94d78Fc307FC5E6195BBe2fB6aFC2FC07d4 + + + Worldcoin + l1CrossDomainMessenger + 0x7a5028BDa40e7B173C278C5342087826455ea25a + + + v3FactoryTargetAddress + 0xcb2436774C3e191c85056d248EF4260ce5f27A9D + + + crossChainAccountTarget + 0xcb2436774C3e191c85056d248EF4260ce5f27A9D + + + Worldcoin + l1CrossDomainMessenger + 0xdC40a14d9abd6F410226f1E6de71aE03441ca506 + + + v3FactoryTargetAddress + 0x7145F8aeef1f6510E92164038E1B6F8cB2c42Cbb + + + crossChainAccountTarget + 0x36eEC182D0B24Df3DC23115D64DB521A93D5154f @@ -431,10 +452,10 @@ The relevant OP Stack contract addresses are as follows: Polygon makes use of an L1 contract called FxRoot for sending messages (in the case of Uniswap, executable proposals), and contracts called FxChild and EthereumProxy on the Polygon chain for forwarding the executable proposals to Uniswap V3 on Polygon. Proposal calldata on mainnet meant for Polygon should be forwarded through a call to `sendMessageToChild` on the chain's corresponding mainnet `FxRoot` contract. -The call would have wrapped calldata for a Uniswap contract function call that would effect the change. The FxRoot/FxChild tunnel would bridge that message to Polygon where FxChild would route the message the EthereumProxy parent contract of UniSwap V3. An example (see on github [here](https://github.com/ScopeLift/uniswap-docs-fork/blob/b0da9f6596b3fcc9578adcb32f89a6b0472c0a1a/examples/multi-chain/polygon-example.sol)): +The call would have wrapped calldata for a Uniswap contract function call that would effect the change. The FxRoot/FxChild tunnel would bridge that message to Polygon where FxChild would route the message the EthereumProxy parent contract of UniSwap V3. An example (see on github [here](https://github.com/ScopeLift/uniswap-docs-fork/blob/b0da9f6596b3fcc9578adcb32f89a6b0472c0a1a/examples/multichain-proposals/polygon-example.sol)): ``` -// SPDX-License-Identifier: UNLICENSED +// SPDX-License-Identifier: MIT pragma solidity ^0.8.28; import {console2} from "forge-std/console2.sol"; @@ -501,10 +522,10 @@ Polygon zkEVM uses a different approach than Polygon TODO: Add details ## Scroll Scroll also makes use of the CrossChainAccount contract for receiving proposals from Ethereum mainnet, but the way that messages are sent is different from the other OP Stack approaches. -Proposal calldata on mainnet meant for the Scroll chain should be forwarded through a call to `sendMessage` on the chain's corresponding mainnet `L1ScrollMessenger` contract. An example (see on github [here](https://github.com/ScopeLift/uniswap-docs-fork/blob/b0da9f6596b3fcc9578adcb32f89a6b0472c0a1a/examples/multi-chain/scroll-example.sol)): +Proposal calldata on mainnet meant for the Scroll chain should be forwarded through a call to `sendMessage` on the chain's corresponding mainnet `L1ScrollMessenger` contract. An example (see on github [here](https://github.com/ScopeLift/uniswap-docs-fork/blob/b0da9f6596b3fcc9578adcb32f89a6b0472c0a1a/examples/multichain-proposals/scroll-example.sol)): ``` -// SPDX-License-Identifier: UNLICENSED +// SPDX-License-Identifier: MIT pragma solidity ^0.8.28; import {console2} from "forge-std/console2.sol"; @@ -576,10 +597,10 @@ contract ScrollExample is Script { Taiko makes use of a contract called `InvokableAccount`. Proposal calldata on mainnet meant for the Taiko chain should be forwarded through a call to`emitSignal`on the chain's corresponding mainnet`SignalService` contract. -The call would have wrapped calldata for the Uniswap contract function call that would effect the change. An example (see on github [here](https://github.com/ScopeLift/uniswap-docs-fork/blob/b0da9f6596b3fcc9578adcb32f89a6b0472c0a1a/examples/multi-chain/taiko-example.sol)): +The call would have wrapped calldata for the Uniswap contract function call that would effect the change. An example (see on github [here](https://github.com/ScopeLift/uniswap-docs-fork/blob/b0da9f6596b3fcc9578adcb32f89a6b0472c0a1a/examples/multichain-proposals/taiko-example.sol)): ``` -// SPDX-License-Identifier: UNLICENSED +// SPDX-License-Identifier: MIT pragma solidity ^0.8.28; import {console2} from "forge-std/console2.sol"; @@ -641,10 +662,10 @@ There are 5 chains where Uniswap V3 is deployed that use the same Ethereum mainn Proposal calldata on mainnet meant for a target chain using Wormhole should be forwarded through a call to the `sendMessage` function of that contract. The function takes both a target address for the message receiving contract on the destination chain, as well as the Chain ID of the destination chain as parameters, allowing the function to be used for sending to multiple destination chains. -A Solidity code example for sending a proposal to one these chains (BNB Chain) is provided below. See on github [here](https://github.com/ScopeLift/uniswap-docs-fork/blob/b0da9f6596b3fcc9578adcb32f89a6b0472c0a1a/examples/multi-chain/wormhole-example.sol): +A Solidity code example for sending a proposal to one these chains (BNB Chain) is provided below. See on github [here](https://github.com/ScopeLift/uniswap-docs-fork/blob/b0da9f6596b3fcc9578adcb32f89a6b0472c0a1a/examples/multichain-proposals/wormhole-example.sol): ``` -// SPDX-License-Identifier: UNLICENSED +// SPDX-License-Identifier: MIT pragma solidity ^0.8.28; import {console2} from "forge-std/console2.sol"; @@ -733,10 +754,10 @@ Because 5 networks all use the same Wormhole-based approach for communication, t ZkSync Era uses the Arbitrum-style approach where the parent of the V3Factory contract is an aliased address, but the method of sending the proposal is different than the one used for Arbitrum. Proposal calldata on mainnet intended for ZkSync Era should be forwarded through a call to `requestL2Transaction` on the `MailBoxFacet` contract. -The call would have wrapped calldata for a Uniswap contract function call that would effect the change. An example (see on github [here](https://github.com/ScopeLift/uniswap-docs-fork/blob/b0da9f6596b3fcc9578adcb32f89a6b0472c0a1a/examples/multi-chain/zksync-example.sol)): +The call would have wrapped calldata for a Uniswap contract function call that would effect the change. An example (see on github [here](https://github.com/ScopeLift/uniswap-docs-fork/blob/b0da9f6596b3fcc9578adcb32f89a6b0472c0a1a/examples/multichain-proposals/zksync-example.sol)): ``` -// SPDX-License-Identifier: UNLICENSED +// SPDX-License-Identifier: MIT pragma solidity ^0.8.28; import {console2} from "forge-std/console2.sol"; diff --git a/examples/multi-chain/arbitrum-example.sol b/examples/multichain-proposals/arbitrum-example.sol similarity index 98% rename from examples/multi-chain/arbitrum-example.sol rename to examples/multichain-proposals/arbitrum-example.sol index f7796eef8..bea348f0d 100644 --- a/examples/multi-chain/arbitrum-example.sol +++ b/examples/multichain-proposals/arbitrum-example.sol @@ -1,4 +1,4 @@ -// SPDX-License-Identifier: UNLICENSED +// SPDX-License-Identifier: MIT pragma solidity ^0.8.28; import {console2} from "forge-std/console2.sol"; diff --git a/examples/multi-chain/avalanche-example.sol b/examples/multichain-proposals/avalanche-example.sol similarity index 98% rename from examples/multi-chain/avalanche-example.sol rename to examples/multichain-proposals/avalanche-example.sol index 4e95434b6..0d6d6ecb7 100644 --- a/examples/multi-chain/avalanche-example.sol +++ b/examples/multichain-proposals/avalanche-example.sol @@ -1,4 +1,4 @@ -// SPDX-License-Identifier: UNLICENSED +// SPDX-License-Identifier: MIT pragma solidity ^0.8.28; import {console2} from "forge-std/console2.sol"; diff --git a/examples/multi-chain/opstack-example.sol b/examples/multichain-proposals/opstack-example.sol similarity index 98% rename from examples/multi-chain/opstack-example.sol rename to examples/multichain-proposals/opstack-example.sol index b5002b43e..ac5bc6cc9 100644 --- a/examples/multi-chain/opstack-example.sol +++ b/examples/multichain-proposals/opstack-example.sol @@ -1,4 +1,4 @@ -// SPDX-License-Identifier: UNLICENSED +// SPDX-License-Identifier: MIT pragma solidity ^0.8.28; import {console2} from "forge-std/console2.sol"; diff --git a/examples/multi-chain/polygon-example.sol b/examples/multichain-proposals/polygon-example.sol similarity index 98% rename from examples/multi-chain/polygon-example.sol rename to examples/multichain-proposals/polygon-example.sol index 7d201a5c0..751cb943a 100644 --- a/examples/multi-chain/polygon-example.sol +++ b/examples/multichain-proposals/polygon-example.sol @@ -1,4 +1,4 @@ -// SPDX-License-Identifier: UNLICENSED +// SPDX-License-Identifier: MIT pragma solidity ^0.8.28; import {console2} from "forge-std/console2.sol"; diff --git a/examples/multi-chain/scroll-example.sol b/examples/multichain-proposals/scroll-example.sol similarity index 98% rename from examples/multi-chain/scroll-example.sol rename to examples/multichain-proposals/scroll-example.sol index 29f196a91..65c761947 100644 --- a/examples/multi-chain/scroll-example.sol +++ b/examples/multichain-proposals/scroll-example.sol @@ -1,4 +1,4 @@ -// SPDX-License-Identifier: UNLICENSED +// SPDX-License-Identifier: MIT pragma solidity ^0.8.28; import {console2} from "forge-std/console2.sol"; diff --git a/examples/multi-chain/taiko-example.sol b/examples/multichain-proposals/taiko-example.sol similarity index 98% rename from examples/multi-chain/taiko-example.sol rename to examples/multichain-proposals/taiko-example.sol index 3288ee89c..e1e596917 100644 --- a/examples/multi-chain/taiko-example.sol +++ b/examples/multichain-proposals/taiko-example.sol @@ -1,4 +1,4 @@ -// SPDX-License-Identifier: UNLICENSED +// SPDX-License-Identifier: MIT pragma solidity ^0.8.28; import {console2} from "forge-std/console2.sol"; diff --git a/examples/multi-chain/wormhole-example.sol b/examples/multichain-proposals/wormhole-example.sol similarity index 98% rename from examples/multi-chain/wormhole-example.sol rename to examples/multichain-proposals/wormhole-example.sol index 0ab00dc51..1136b8d92 100644 --- a/examples/multi-chain/wormhole-example.sol +++ b/examples/multichain-proposals/wormhole-example.sol @@ -1,4 +1,4 @@ -// SPDX-License-Identifier: UNLICENSED +// SPDX-License-Identifier: MIT pragma solidity ^0.8.28; import {console2} from "forge-std/console2.sol"; diff --git a/examples/multi-chain/zksync-example.sol b/examples/multichain-proposals/zksync-example.sol similarity index 98% rename from examples/multi-chain/zksync-example.sol rename to examples/multichain-proposals/zksync-example.sol index 204c9bd2f..4f50859b3 100644 --- a/examples/multi-chain/zksync-example.sol +++ b/examples/multichain-proposals/zksync-example.sol @@ -1,4 +1,4 @@ -// SPDX-License-Identifier: UNLICENSED +// SPDX-License-Identifier: MIT pragma solidity ^0.8.28; import {console2} from "forge-std/console2.sol"; From 606d4a00ce2d5e1c9046fdf651fa30cbfc76e074 Mon Sep 17 00:00:00 2001 From: wildmolasses Date: Mon, 24 Feb 2025 15:02:29 -0500 Subject: [PATCH 15/16] copy changes, dir rename --- .../governance/05-multichain-proposals.md | 35 +++++++++---------- .../arbitrum-example.sol | 0 .../avalanche-example.sol | 0 .../opstack-example.sol | 0 .../polygon-example.sol | 0 .../scroll-example.sol | 0 .../taiko-example.sol | 0 .../wormhole-example.sol | 0 .../zksync-example.sol | 0 9 files changed, 16 insertions(+), 19 deletions(-) rename examples/{multichain-proposals => governance-multichain-proposals}/arbitrum-example.sol (100%) rename examples/{multichain-proposals => governance-multichain-proposals}/avalanche-example.sol (100%) rename examples/{multichain-proposals => governance-multichain-proposals}/opstack-example.sol (100%) rename examples/{multichain-proposals => governance-multichain-proposals}/polygon-example.sol (100%) rename examples/{multichain-proposals => governance-multichain-proposals}/scroll-example.sol (100%) rename examples/{multichain-proposals => governance-multichain-proposals}/taiko-example.sol (100%) rename examples/{multichain-proposals => governance-multichain-proposals}/wormhole-example.sol (100%) rename examples/{multichain-proposals => governance-multichain-proposals}/zksync-example.sol (100%) diff --git a/docs/concepts/governance/05-multichain-proposals.md b/docs/concepts/governance/05-multichain-proposals.md index aeec97873..bee1a88ac 100644 --- a/docs/concepts/governance/05-multichain-proposals.md +++ b/docs/concepts/governance/05-multichain-proposals.md @@ -3,24 +3,21 @@ id: multichain-proposals title: Multichain Proposals --- -This is a living document which represents the current process guidelines for developing and advancing multichain Uniswap Governance Proposals. It was last updated March 2025. +This is a living document which represents the current process guidelines for developing and advancing multichain governance proposals for Uniswap. It was last updated March 2025. ## Introduction -Uniswap V3 has now been deployed across 24 blockchain networks, including many Layer 2 chains. -However, because Uniswap governance (using GovernorBravo and its associated Timelock contract) executes approved proposals on Ethereum mainnet, implementing changes on these other blockchains requires some extra steps in the proposals' executable code. +Uniswap governance is deployed on mainnet and executes proposals on mainnet. However, Uniswap V3 has been deployed to 24 blockchain networks and counting, and there exists a need for governance to create and execute proposals on these chains. -For a proposal to successfully enact changes on a non-mainnet chain, the proposal's code must transmit the necessary code to effect the change on the specific target chain for execution. -A variety of mechanisms are used on the individual non-mainnet chains to receive the proposal code, although in some cases, similar or even identical communication methods and contracts are used to bridge proposals from Ethereum mainnet to the target chain. +This document will serve as a guide on constructing such proposals for various non-mainnet chains. It includes code snippets written as Foundry scripts, so delegates with experience can install dependencies (forge-std, uniswap-v3-core, and uniswap-v3-periphery) and run the code via `forge script`. -This document provides a detailed guide on constructing such proposals for each target non-mainnet chain, including Solidity code snippets for each general approach. -The code snippets are written as Forge/Foundry scripts, and assume they are being run on the Forge platform, in a code repository with the necessary libraries (forge-std, uniswap-v3-core, and uniswap-v3-periphery) imported. It is important to note that the code provided here is a general template and may require modification to suit the specific requirements of the target chain. -Of the 24 chains where Uniswap V3 is deployed there are 10 chains that are OP Stack based and use an `L1CrossDomainMessenger` contract for bridging proposals. -There are 6 chains that use Wormhole for bridging proposals. -The remaining 8 chains use a variety of other methods for bridging proposals, all described below. -The table below contains links to the various proposal methods for each chain. +Of the 24 chains where Uniswap V3 is deployed: + +- 10 OP Stack chains bridge proposals via an `L1CrossDomainMessenger` contract. +- 6 chains bridge proposals using Wormhole. +- The remaining chains use a variety of other methods for bridging proposals, described below. | Chain(s) | Proposal Method | | ------------- | ----------------------------------------------------- | @@ -54,7 +51,7 @@ The table below contains links to the various proposal methods for each chain. Arbitrum uses an approach where the owner of the V3Factory is a special aliased address (offset by the value `0x1111000000000000000000000000000000001111`) that (when the offset is subtracted away) is the L1 address of the Uniswap DAO Timelock contract. A Solidity code example for sending a proposal to Arbitrum is shown below. Proposal calldata on mainnet intended for Arbitrum should be forwarded through a call to `createRetryableTicket` on the Arbitrum Inbox contract. -The call would have wrapped calldata for a Uniswap contract function call that would effect the change. An example (see on github [here](https://github.com/ScopeLift/uniswap-docs-fork/blob/b0da9f6596b3fcc9578adcb32f89a6b0472c0a1a/examples/multichain-proposals/arbitrum-example.sol) ): +The call would have wrapped calldata for a Uniswap contract function call that would effect the change. An example (see on github [here](https://github.com/ScopeLift/uniswap-docs-fork/blob/b0da9f6596b3fcc9578adcb32f89a6b0472c0a1a/examples/governance-multichain-proposals/arbitrum-example.sol) ): ``` // SPDX-License-Identifier: MIT @@ -141,7 +138,7 @@ contract EthereumToArbitrumSender is Script { Avalanche makes use of a contract called `OmnichainGovernanceExecutor` to receive proposals from Ethereum mainnet. Proposal calldata on mainnet intended for Avalanche should be forwarded through a call to `send` on the contract called `LayerZero:EndpointV2`. -A Solidity code example for sending a proposal to AVAX is below. See on github [here](https://github.com/ScopeLift/uniswap-docs-fork/blob/b0da9f6596b3fcc9578adcb32f89a6b0472c0a1a/examples/multichain-proposals/avalanche-example.sol): +A Solidity code example for sending a proposal to AVAX is below. See on github [here](https://github.com/ScopeLift/uniswap-docs-fork/blob/b0da9f6596b3fcc9578adcb32f89a6b0472c0a1a/examples/governance-multichain-proposals/avalanche-example.sol): ``` // SPDX-License-Identifier: MIT @@ -225,7 +222,7 @@ Filecoin EVM ?? TODO: Add details ## OP Stack Proposal calldata on mainnet meant for an OP stack chain should be forwarded through a call to sendMessage on the chain's corresponding mainnet `L1CrossDomainMessenger` contract. -The call would have doubly-wrapped calldata for the `CrossChainAccount:forward` function as well as the ultimate Uniswap contract function call that would effect the change. An example for Optimism (see on github [here](https://github.com/ScopeLift/uniswap-docs-fork/blob/b0da9f6596b3fcc9578adcb32f89a6b0472c0a1a/examples/multichain-proposals/opstack-example.sol)): +The call would have doubly-wrapped calldata for the `CrossChainAccount:forward` function as well as the ultimate Uniswap contract function call that would effect the change. An example for Optimism (see on github [here](https://github.com/ScopeLift/uniswap-docs-fork/blob/b0da9f6596b3fcc9578adcb32f89a6b0472c0a1a/examples/governance-multichain-proposals/opstack-example.sol)): ``` // SPDX-License-Identifier: MIT @@ -452,7 +449,7 @@ The relevant OP Stack contract addresses are as follows: Polygon makes use of an L1 contract called FxRoot for sending messages (in the case of Uniswap, executable proposals), and contracts called FxChild and EthereumProxy on the Polygon chain for forwarding the executable proposals to Uniswap V3 on Polygon. Proposal calldata on mainnet meant for Polygon should be forwarded through a call to `sendMessageToChild` on the chain's corresponding mainnet `FxRoot` contract. -The call would have wrapped calldata for a Uniswap contract function call that would effect the change. The FxRoot/FxChild tunnel would bridge that message to Polygon where FxChild would route the message the EthereumProxy parent contract of UniSwap V3. An example (see on github [here](https://github.com/ScopeLift/uniswap-docs-fork/blob/b0da9f6596b3fcc9578adcb32f89a6b0472c0a1a/examples/multichain-proposals/polygon-example.sol)): +The call would have wrapped calldata for a Uniswap contract function call that would effect the change. The FxRoot/FxChild tunnel would bridge that message to Polygon where FxChild would route the message the EthereumProxy parent contract of UniSwap V3. An example (see on github [here](https://github.com/ScopeLift/uniswap-docs-fork/blob/b0da9f6596b3fcc9578adcb32f89a6b0472c0a1a/examples/governance-multichain-proposals/polygon-example.sol)): ``` // SPDX-License-Identifier: MIT @@ -522,7 +519,7 @@ Polygon zkEVM uses a different approach than Polygon TODO: Add details ## Scroll Scroll also makes use of the CrossChainAccount contract for receiving proposals from Ethereum mainnet, but the way that messages are sent is different from the other OP Stack approaches. -Proposal calldata on mainnet meant for the Scroll chain should be forwarded through a call to `sendMessage` on the chain's corresponding mainnet `L1ScrollMessenger` contract. An example (see on github [here](https://github.com/ScopeLift/uniswap-docs-fork/blob/b0da9f6596b3fcc9578adcb32f89a6b0472c0a1a/examples/multichain-proposals/scroll-example.sol)): +Proposal calldata on mainnet meant for the Scroll chain should be forwarded through a call to `sendMessage` on the chain's corresponding mainnet `L1ScrollMessenger` contract. An example (see on github [here](https://github.com/ScopeLift/uniswap-docs-fork/blob/b0da9f6596b3fcc9578adcb32f89a6b0472c0a1a/examples/governance-multichain-proposals/scroll-example.sol)): ``` // SPDX-License-Identifier: MIT @@ -597,7 +594,7 @@ contract ScrollExample is Script { Taiko makes use of a contract called `InvokableAccount`. Proposal calldata on mainnet meant for the Taiko chain should be forwarded through a call to`emitSignal`on the chain's corresponding mainnet`SignalService` contract. -The call would have wrapped calldata for the Uniswap contract function call that would effect the change. An example (see on github [here](https://github.com/ScopeLift/uniswap-docs-fork/blob/b0da9f6596b3fcc9578adcb32f89a6b0472c0a1a/examples/multichain-proposals/taiko-example.sol)): +The call would have wrapped calldata for the Uniswap contract function call that would effect the change. An example (see on github [here](https://github.com/ScopeLift/uniswap-docs-fork/blob/b0da9f6596b3fcc9578adcb32f89a6b0472c0a1a/examples/governance-multichain-proposals/taiko-example.sol)): ``` // SPDX-License-Identifier: MIT @@ -662,7 +659,7 @@ There are 5 chains where Uniswap V3 is deployed that use the same Ethereum mainn Proposal calldata on mainnet meant for a target chain using Wormhole should be forwarded through a call to the `sendMessage` function of that contract. The function takes both a target address for the message receiving contract on the destination chain, as well as the Chain ID of the destination chain as parameters, allowing the function to be used for sending to multiple destination chains. -A Solidity code example for sending a proposal to one these chains (BNB Chain) is provided below. See on github [here](https://github.com/ScopeLift/uniswap-docs-fork/blob/b0da9f6596b3fcc9578adcb32f89a6b0472c0a1a/examples/multichain-proposals/wormhole-example.sol): +A Solidity code example for sending a proposal to one these chains (BNB Chain) is provided below. See on github [here](https://github.com/ScopeLift/uniswap-docs-fork/blob/b0da9f6596b3fcc9578adcb32f89a6b0472c0a1a/examples/governance-multichain-proposals/wormhole-example.sol): ``` // SPDX-License-Identifier: MIT @@ -754,7 +751,7 @@ Because 5 networks all use the same Wormhole-based approach for communication, t ZkSync Era uses the Arbitrum-style approach where the parent of the V3Factory contract is an aliased address, but the method of sending the proposal is different than the one used for Arbitrum. Proposal calldata on mainnet intended for ZkSync Era should be forwarded through a call to `requestL2Transaction` on the `MailBoxFacet` contract. -The call would have wrapped calldata for a Uniswap contract function call that would effect the change. An example (see on github [here](https://github.com/ScopeLift/uniswap-docs-fork/blob/b0da9f6596b3fcc9578adcb32f89a6b0472c0a1a/examples/multichain-proposals/zksync-example.sol)): +The call would have wrapped calldata for a Uniswap contract function call that would effect the change. An example (see on github [here](https://github.com/ScopeLift/uniswap-docs-fork/blob/b0da9f6596b3fcc9578adcb32f89a6b0472c0a1a/examples/governance-multichain-proposals/zksync-example.sol)): ``` // SPDX-License-Identifier: MIT diff --git a/examples/multichain-proposals/arbitrum-example.sol b/examples/governance-multichain-proposals/arbitrum-example.sol similarity index 100% rename from examples/multichain-proposals/arbitrum-example.sol rename to examples/governance-multichain-proposals/arbitrum-example.sol diff --git a/examples/multichain-proposals/avalanche-example.sol b/examples/governance-multichain-proposals/avalanche-example.sol similarity index 100% rename from examples/multichain-proposals/avalanche-example.sol rename to examples/governance-multichain-proposals/avalanche-example.sol diff --git a/examples/multichain-proposals/opstack-example.sol b/examples/governance-multichain-proposals/opstack-example.sol similarity index 100% rename from examples/multichain-proposals/opstack-example.sol rename to examples/governance-multichain-proposals/opstack-example.sol diff --git a/examples/multichain-proposals/polygon-example.sol b/examples/governance-multichain-proposals/polygon-example.sol similarity index 100% rename from examples/multichain-proposals/polygon-example.sol rename to examples/governance-multichain-proposals/polygon-example.sol diff --git a/examples/multichain-proposals/scroll-example.sol b/examples/governance-multichain-proposals/scroll-example.sol similarity index 100% rename from examples/multichain-proposals/scroll-example.sol rename to examples/governance-multichain-proposals/scroll-example.sol diff --git a/examples/multichain-proposals/taiko-example.sol b/examples/governance-multichain-proposals/taiko-example.sol similarity index 100% rename from examples/multichain-proposals/taiko-example.sol rename to examples/governance-multichain-proposals/taiko-example.sol diff --git a/examples/multichain-proposals/wormhole-example.sol b/examples/governance-multichain-proposals/wormhole-example.sol similarity index 100% rename from examples/multichain-proposals/wormhole-example.sol rename to examples/governance-multichain-proposals/wormhole-example.sol diff --git a/examples/multichain-proposals/zksync-example.sol b/examples/governance-multichain-proposals/zksync-example.sol similarity index 100% rename from examples/multichain-proposals/zksync-example.sol rename to examples/governance-multichain-proposals/zksync-example.sol From 643e8ce50ebe9ff141eef779a4f4c5256584f73b Mon Sep 17 00:00:00 2001 From: wildmolasses Date: Mon, 24 Feb 2025 15:08:31 -0500 Subject: [PATCH 16/16] example proposal script headings --- .../governance/05-multichain-proposals.md | 55 +++++++++++++++---- 1 file changed, 44 insertions(+), 11 deletions(-) diff --git a/docs/concepts/governance/05-multichain-proposals.md b/docs/concepts/governance/05-multichain-proposals.md index bee1a88ac..990006dd1 100644 --- a/docs/concepts/governance/05-multichain-proposals.md +++ b/docs/concepts/governance/05-multichain-proposals.md @@ -51,7 +51,11 @@ Of the 24 chains where Uniswap V3 is deployed: Arbitrum uses an approach where the owner of the V3Factory is a special aliased address (offset by the value `0x1111000000000000000000000000000000001111`) that (when the offset is subtracted away) is the L1 address of the Uniswap DAO Timelock contract. A Solidity code example for sending a proposal to Arbitrum is shown below. Proposal calldata on mainnet intended for Arbitrum should be forwarded through a call to `createRetryableTicket` on the Arbitrum Inbox contract. -The call would have wrapped calldata for a Uniswap contract function call that would effect the change. An example (see on github [here](https://github.com/ScopeLift/uniswap-docs-fork/blob/b0da9f6596b3fcc9578adcb32f89a6b0472c0a1a/examples/governance-multichain-proposals/arbitrum-example.sol) ): +The call would have wrapped calldata for a Uniswap contract function call that would effect the change. + +#### Example proposal script + +[Source](https://github.com/ScopeLift/uniswap-docs-fork/blob/b0da9f6596b3fcc9578adcb32f89a6b0472c0a1a/examples/governance-multichain-proposals/arbitrum-example.sol) ``` // SPDX-License-Identifier: MIT @@ -138,7 +142,10 @@ contract EthereumToArbitrumSender is Script { Avalanche makes use of a contract called `OmnichainGovernanceExecutor` to receive proposals from Ethereum mainnet. Proposal calldata on mainnet intended for Avalanche should be forwarded through a call to `send` on the contract called `LayerZero:EndpointV2`. -A Solidity code example for sending a proposal to AVAX is below. See on github [here](https://github.com/ScopeLift/uniswap-docs-fork/blob/b0da9f6596b3fcc9578adcb32f89a6b0472c0a1a/examples/governance-multichain-proposals/avalanche-example.sol): + +#### Example proposal script + +[Source](https://github.com/ScopeLift/uniswap-docs-fork/blob/b0da9f6596b3fcc9578adcb32f89a6b0472c0a1a/examples/governance-multichain-proposals/avalanche-example.sol) ``` // SPDX-License-Identifier: MIT @@ -222,7 +229,11 @@ Filecoin EVM ?? TODO: Add details ## OP Stack Proposal calldata on mainnet meant for an OP stack chain should be forwarded through a call to sendMessage on the chain's corresponding mainnet `L1CrossDomainMessenger` contract. -The call would have doubly-wrapped calldata for the `CrossChainAccount:forward` function as well as the ultimate Uniswap contract function call that would effect the change. An example for Optimism (see on github [here](https://github.com/ScopeLift/uniswap-docs-fork/blob/b0da9f6596b3fcc9578adcb32f89a6b0472c0a1a/examples/governance-multichain-proposals/opstack-example.sol)): +The call would have doubly-wrapped calldata for the `CrossChainAccount:forward` function as well as the ultimate Uniswap contract function call that would effect the change. + +#### Example proposal script + +[Source](https://github.com/ScopeLift/uniswap-docs-fork/blob/b0da9f6596b3fcc9578adcb32f89a6b0472c0a1a/examples/governance-multichain-proposals/opstack-example.sol) ``` // SPDX-License-Identifier: MIT @@ -294,7 +305,7 @@ contract OptimismExample is Script { } ``` -Because 10 networks all use the same CrossChainAccount approach for communication, the only modifications to the above code snippet for those networks would be to change the first 3 address constant definitions. +Because 10 networks all use the same CrossChainAccount approach for communication, the only modifications to the above code snippet for those networks would be to change the first 3 address constant definitions. The exception to this is Linea: :::warning **Linea**'s `sendMessage` reverses the order of the `fee` and `message` parameters. @@ -429,7 +440,7 @@ The relevant OP Stack contract addresses are as follows: 0xcb2436774C3e191c85056d248EF4260ce5f27A9D - Worldcoin + Zora l1CrossDomainMessenger 0xdC40a14d9abd6F410226f1E6de71aE03441ca506 @@ -449,7 +460,11 @@ The relevant OP Stack contract addresses are as follows: Polygon makes use of an L1 contract called FxRoot for sending messages (in the case of Uniswap, executable proposals), and contracts called FxChild and EthereumProxy on the Polygon chain for forwarding the executable proposals to Uniswap V3 on Polygon. Proposal calldata on mainnet meant for Polygon should be forwarded through a call to `sendMessageToChild` on the chain's corresponding mainnet `FxRoot` contract. -The call would have wrapped calldata for a Uniswap contract function call that would effect the change. The FxRoot/FxChild tunnel would bridge that message to Polygon where FxChild would route the message the EthereumProxy parent contract of UniSwap V3. An example (see on github [here](https://github.com/ScopeLift/uniswap-docs-fork/blob/b0da9f6596b3fcc9578adcb32f89a6b0472c0a1a/examples/governance-multichain-proposals/polygon-example.sol)): +The call would have wrapped calldata for a Uniswap contract function call that would effect the change. The FxRoot/FxChild tunnel would bridge that message to Polygon where FxChild would route the message the EthereumProxy parent contract of UniSwap V3. + +#### Example proposal script + +[Source](https://github.com/ScopeLift/uniswap-docs-fork/blob/b0da9f6596b3fcc9578adcb32f89a6b0472c0a1a/examples/governance-multichain-proposals/polygon-example.sol) ``` // SPDX-License-Identifier: MIT @@ -514,12 +529,18 @@ contract PolygonExample is Script { ## Polygon zkEVM -Polygon zkEVM uses a different approach than Polygon TODO: Add details +Polygon zkEVM uses an approach different from Polygon. + +TODO: Add details ## Scroll Scroll also makes use of the CrossChainAccount contract for receiving proposals from Ethereum mainnet, but the way that messages are sent is different from the other OP Stack approaches. -Proposal calldata on mainnet meant for the Scroll chain should be forwarded through a call to `sendMessage` on the chain's corresponding mainnet `L1ScrollMessenger` contract. An example (see on github [here](https://github.com/ScopeLift/uniswap-docs-fork/blob/b0da9f6596b3fcc9578adcb32f89a6b0472c0a1a/examples/governance-multichain-proposals/scroll-example.sol)): +Proposal calldata on mainnet meant for the Scroll chain should be forwarded through a call to `sendMessage` on the chain's corresponding mainnet `L1ScrollMessenger` contract. + +#### Example proposal script + +[Source](https://github.com/ScopeLift/uniswap-docs-fork/blob/b0da9f6596b3fcc9578adcb32f89a6b0472c0a1a/examples/governance-multichain-proposals/scroll-example.sol) ``` // SPDX-License-Identifier: MIT @@ -594,7 +615,11 @@ contract ScrollExample is Script { Taiko makes use of a contract called `InvokableAccount`. Proposal calldata on mainnet meant for the Taiko chain should be forwarded through a call to`emitSignal`on the chain's corresponding mainnet`SignalService` contract. -The call would have wrapped calldata for the Uniswap contract function call that would effect the change. An example (see on github [here](https://github.com/ScopeLift/uniswap-docs-fork/blob/b0da9f6596b3fcc9578adcb32f89a6b0472c0a1a/examples/governance-multichain-proposals/taiko-example.sol)): +The call would have wrapped calldata for the Uniswap contract function call that would effect the change. + +#### Example proposal script + +[Source](https://github.com/ScopeLift/uniswap-docs-fork/blob/b0da9f6596b3fcc9578adcb32f89a6b0472c0a1a/examples/governance-multichain-proposals/taiko-example.sol) ``` // SPDX-License-Identifier: MIT @@ -659,7 +684,11 @@ There are 5 chains where Uniswap V3 is deployed that use the same Ethereum mainn Proposal calldata on mainnet meant for a target chain using Wormhole should be forwarded through a call to the `sendMessage` function of that contract. The function takes both a target address for the message receiving contract on the destination chain, as well as the Chain ID of the destination chain as parameters, allowing the function to be used for sending to multiple destination chains. -A Solidity code example for sending a proposal to one these chains (BNB Chain) is provided below. See on github [here](https://github.com/ScopeLift/uniswap-docs-fork/blob/b0da9f6596b3fcc9578adcb32f89a6b0472c0a1a/examples/governance-multichain-proposals/wormhole-example.sol): +A Solidity code example for sending a proposal to one these chains (BNB Chain) is provided below. + +#### Example proposal script + +[Source](https://github.com/ScopeLift/uniswap-docs-fork/blob/b0da9f6596b3fcc9578adcb32f89a6b0472c0a1a/examples/governance-multichain-proposals/wormhole-example.sol) ``` // SPDX-License-Identifier: MIT @@ -751,7 +780,11 @@ Because 5 networks all use the same Wormhole-based approach for communication, t ZkSync Era uses the Arbitrum-style approach where the parent of the V3Factory contract is an aliased address, but the method of sending the proposal is different than the one used for Arbitrum. Proposal calldata on mainnet intended for ZkSync Era should be forwarded through a call to `requestL2Transaction` on the `MailBoxFacet` contract. -The call would have wrapped calldata for a Uniswap contract function call that would effect the change. An example (see on github [here](https://github.com/ScopeLift/uniswap-docs-fork/blob/b0da9f6596b3fcc9578adcb32f89a6b0472c0a1a/examples/governance-multichain-proposals/zksync-example.sol)): +The call would have wrapped calldata for a Uniswap contract function call that would effect the change. + +#### Example proposal script + +[Source](https://github.com/ScopeLift/uniswap-docs-fork/blob/b0da9f6596b3fcc9578adcb32f89a6b0472c0a1a/examples/governance-multichain-proposals/zksync-example.sol) ``` // SPDX-License-Identifier: MIT