Skip to content

Adding documentation about bridging proposals to destination chains. #1

New issue

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

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

Already on GitHub? Sign in to your account

Open
wants to merge 16 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 4 additions & 5 deletions docs/concepts/governance/02-process.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,15 +3,15 @@ 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

Uniswap Governance takes place in several venues. Each serves its own particular purpose.

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)

Expand All @@ -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.
Expand Down Expand Up @@ -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

Expand All @@ -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.
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.

Expand Down
867 changes: 867 additions & 0 deletions docs/concepts/governance/05-multichain-proposals.md

Large diffs are not rendered by default.

78 changes: 78 additions & 0 deletions examples/governance-multichain-proposals/arbitrum-example.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
// SPDX-License-Identifier: MIT
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);
}
}
72 changes: 72 additions & 0 deletions examples/governance-multichain-proposals/avalanche-example.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
// SPDX-License-Identifier: MIT
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);
}
}
67 changes: 67 additions & 0 deletions examples/governance-multichain-proposals/opstack-example.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
// SPDX-License-Identifier: MIT
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);
}
}
58 changes: 58 additions & 0 deletions examples/governance-multichain-proposals/polygon-example.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
// SPDX-License-Identifier: MIT
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);
}
}
66 changes: 66 additions & 0 deletions examples/governance-multichain-proposals/scroll-example.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
// SPDX-License-Identifier: MIT
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);
}
}
Loading