From 4a36b5a19c466a4b81e44cdea24007f1f049e7b3 Mon Sep 17 00:00:00 2001 From: wildmolasses Date: Tue, 18 Nov 2025 14:53:05 -0500 Subject: [PATCH 1/4] add attestations and calldata generation to proposal script --- script/03_UnificationProposal.s.sol | 182 ++++++++++++++++++++++++++-- 1 file changed, 169 insertions(+), 13 deletions(-) diff --git a/script/03_UnificationProposal.s.sol b/script/03_UnificationProposal.s.sol index 45a08bc..9deeb86 100644 --- a/script/03_UnificationProposal.s.sol +++ b/script/03_UnificationProposal.s.sol @@ -8,6 +8,15 @@ import {IUniswapV3Factory} from "briefcase/protocols/v3-core/interfaces/IUniswap import {IERC20} from "forge-std/interfaces/IERC20.sol"; contract UnificationProposal is Script { + // TODO: Fill in these values + address public constant AGREEMENT_ANCHOR_1 = address(0); + bytes32 public constant CONTENT_HASH_1 = bytes32(0); + address public constant AGREEMENT_ANCHOR_2 = address(0); + bytes32 public constant CONTENT_HASH_2 = bytes32(0); + string public constant PROPOSAL_DESCRIPTION = ""; + + IGovernorBravo internal constant GOVERNOR_BRAVO = + IGovernorBravo(0x408ED6354d4973f66138C91495F2f2FCbd8724C3); IERC20 UNI = IERC20(0x1f9840a85d5aF5bf1D1762F925BDADdC4201F984); IUniswapV2Factory public V2_FACTORY = IUniswapV2Factory(0x5C69bEe701ef814a2B6a3EDD4B1652CB9cc5aA6f); @@ -15,11 +24,45 @@ contract UnificationProposal is Script { IUniswapV3Factory(0x1F98431c8aD98523631AE4a59f267346ea31F984); address public constant OLD_FEE_TO_SETTER = 0x18e433c7Bf8A2E1d0197CE5d8f9AFAda1A771360; + // EAS Constants + IEAS internal constant EAS = IEAS(0xA1207F3BBa224E2c9c3c6D5aF63D0eb1582Ce587); + bytes32 public constant AGREEMENT_SCHEMA_UID = + 0x504f10498bcdb19d4960412dbade6fa1530b8eed65c319f15cbe20fadafe56bd; + function setUp() public {} function run(MainnetDeployer deployer) public { vm.startBroadcast(); - _run(deployer); + ( + address[] memory targets, + uint256[] memory values, + string[] memory signatures, + bytes[] memory calldatas + ) = _run(deployer); + console.log("Calldata details:"); + for (uint256 i = 0; i < calldatas.length; i++) { + console.log("Target", i); + console.log(targets[i]); + console.log("Value", i); + console.log(values[i]); + console.log("Signature", i); + console.log(signatures[i]); + console.log("Calldata", i); + console.logBytes(calldatas[i]); + console.log("--------------------------------"); + } + + console.log("Description:"); + console.log(PROPOSAL_DESCRIPTION); + + bytes memory proposalCalldata = abi.encodeCall( + IGovernorBravo.propose, (targets, values, signatures, calldatas, PROPOSAL_DESCRIPTION) + ); + console.log("GovernorBravo.propose() Calldata:"); + console.logBytes(proposalCalldata); + + GOVERNOR_BRAVO.propose(targets, values, signatures, calldatas, PROPOSAL_DESCRIPTION); + vm.stopBroadcast(); } @@ -31,24 +74,109 @@ contract UnificationProposal is Script { function runPranked(MainnetDeployer deployer) public { vm.startPrank(V3_FACTORY.owner()); - _run(deployer); + ( + address[] memory targets, + uint256[] memory values, + string[] memory signatures, + bytes[] memory calldatas + ) = _run(deployer); + for (uint256 i = 0; i < targets.length; i++) { + targets[i].call{value: values[i]}(calldatas[i]); + } vm.stopPrank(); } - function _run(MainnetDeployer deployer) public { + function _run(MainnetDeployer deployer) + public + returns ( + address[] memory targets, + uint256[] memory values, + string[] memory signatures, + bytes[] memory calldatas + ) + { address timelock = deployer.V3_FACTORY().owner(); + // --- Proposal Actions Setup --- + address[] memory targets = new address[](7); + uint256[] memory values = new uint256[](7); + string[] memory signatures = new string[](7); + bytes[] memory calldatas = new bytes[](7); + // Burn 100M UNI - UNI.transfer(address(0xdead), 100_000_000 ether); - /// Set the owner of the v3 factory to the configured fee controller - V3_FACTORY.setOwner(address(deployer.V3_FEE_ADAPTER())); - /// Update the v2 fee to setter to the timelock - IFeeToSetter(OLD_FEE_TO_SETTER).setFeeToSetter(timelock); - /// Set the recipient of v2 protocol fees to the token jar - V2_FACTORY.setFeeTo(address(deployer.TOKEN_JAR())); - /// Approve two years of vesting to the UNIvester smart contract UNI stays in treasury until - /// vested and unvested UNI can be cancelled by setting approve back to 0 - UNI.approve(address(deployer.UNI_VESTING()), 40_000_000 ether); + targets[0] = address(UNI); + values[0] = 0; + signatures[0] = ""; + calldatas[0] = abi.encodeCall(UNI.transfer, (address(0xdead), 100_000_000 ether)); + + // Set the owner of the v3 factory to the configured fee controller + targets[1] = address(V3_FACTORY); + values[1] = 0; + signatures[1] = ""; + calldatas[1] = abi.encodeCall(V3_FACTORY.setOwner, (address(deployer.V3_FEE_ADAPTER()))); + + // Update the v2 fee to setter to the timelock + targets[2] = address(OLD_FEE_TO_SETTER); + values[2] = 0; + signatures[2] = ""; + calldatas[2] = abi.encodeCall(IFeeToSetter.setFeeToSetter, (timelock)); + + // Set the recipient of v2 protocol fees to the token jar + targets[3] = address(V2_FACTORY); + values[3] = 0; + signatures[3] = ""; + calldatas[3] = abi.encodeCall(V2_FACTORY.setFeeTo, (address(deployer.TOKEN_JAR()))); + + // Approve two years of vesting to the UNIvester smart contract + // UNI stays in treasury until vested and unvested UNI can be cancelled by setting approve back to 0 + targets[4] = address(UNI); + values[4] = 0; + signatures[4] = ""; + calldatas[4] = abi.encodeCall(UNI.approve, (address(deployer.UNI_VESTING()), 40_000_000 ether)); + + // DAO attests to Agreement 1 + if (AGREEMENT_ANCHOR_1 != address(0)) { + targets[5] = address(EAS); + values[5] = 0; + signatures[5] = ""; + calldatas[5] = abi.encodeCall( + EAS.attest, + (AttestationRequest({ + schema: AGREEMENT_SCHEMA_UID, + data: AttestationRequestData({ + recipient: AGREEMENT_ANCHOR_1, + expirationTime: 0, + revocable: false, + refUID: bytes32(0), + data: abi.encode(CONTENT_HASH_1), + value: 0 + }) + })) + ); + } + + // DAO attests to Agreement 2 + if (AGREEMENT_ANCHOR_2 != address(0)) { + targets[6] = address(EAS); + values[6] = 0; + signatures[6] = ""; + calldatas[6] = abi.encodeCall( + EAS.attest, + (AttestationRequest({ + schema: AGREEMENT_SCHEMA_UID, + data: AttestationRequestData({ + recipient: AGREEMENT_ANCHOR_2, + expirationTime: 0, + revocable: false, + refUID: bytes32(0), + data: abi.encode(CONTENT_HASH_2), + value: 0 + }) + })) + ); + } + + return (targets, values, signatures, calldatas); } } @@ -58,3 +186,31 @@ contract UnificationProposal is Script { interface IFeeToSetter { function setFeeToSetter(address) external; } + +struct AttestationRequestData { + address recipient; + uint64 expirationTime; + bool revocable; + bytes32 refUID; + bytes data; + uint256 value; +} + +struct AttestationRequest { + bytes32 schema; + AttestationRequestData data; +} + +interface IEAS { + function attest(AttestationRequest calldata request) external payable returns (bytes32); +} + +interface IGovernorBravo { + function propose( + address[] memory targets, + uint256[] memory values, + string[] memory signatures, + bytes[] memory calldatas, + string memory description + ) external returns (uint256); +} From 0a1fcc6e618d24161f679a984526abc06a20ec4b Mon Sep 17 00:00:00 2001 From: Ed Mazurek Date: Fri, 21 Nov 2025 09:21:07 -0500 Subject: [PATCH 2/4] add agreement anchor creation script (#2) --- script/03_CreateAgreementAnchorsMainnet.s.sol | 36 +++++++++++++++++++ ...sal.s.sol => 04_UnificationProposal.s.sol} | 11 ++++-- test/ProtocolFees.fork.t.sol | 2 +- test/UNIVesting.fork.t.sol | 3 +- 4 files changed, 47 insertions(+), 5 deletions(-) create mode 100644 script/03_CreateAgreementAnchorsMainnet.s.sol rename script/{03_UnificationProposal.s.sol => 04_UnificationProposal.s.sol} (94%) diff --git a/script/03_CreateAgreementAnchorsMainnet.s.sol b/script/03_CreateAgreementAnchorsMainnet.s.sol new file mode 100644 index 0000000..ed67c89 --- /dev/null +++ b/script/03_CreateAgreementAnchorsMainnet.s.sol @@ -0,0 +1,36 @@ +// SPDX-License-Identifier: AGPL-3.0-only +pragma solidity ^0.8.29; + +import {Script, console2} from "forge-std/Script.sol"; + +contract CreateAgreementAnchors is Script { + IAgreementAnchorFactory public constant AGREEMENT_ANCHOR_FACTORY = + IAgreementAnchorFactory(0x5Ef3cCf9eC7E0af61E1767b2EEbB50e052b5Df47); + + // TODO: set content hashes and counterparty addresses for DUNI agreements + bytes32 public constant AGREEMENT_ANCHOR_1_CONTENT_HASH = ""; + address public constant AGREEMENT_ANCHOR_1_COUNTER_SIGNER = address(0); + bytes32 public constant AGREEMENT_ANCHOR_2_CONTENT_HASH = ""; + address public constant AGREEMENT_ANCHOR_2_COUNTER_SIGNER = address(0); + + function run() public { + require(block.chainid == 1, "Not mainnet"); + vm.startBroadcast(); + address agreementAnchor1 = AGREEMENT_ANCHOR_FACTORY.createAgreementAnchor( + AGREEMENT_ANCHOR_1_CONTENT_HASH, AGREEMENT_ANCHOR_1_COUNTER_SIGNER + ); + + address agreementAnchor2 = AGREEMENT_ANCHOR_FACTORY.createAgreementAnchor( + AGREEMENT_ANCHOR_2_CONTENT_HASH, AGREEMENT_ANCHOR_2_COUNTER_SIGNER + ); + console2.log("Agreement Anchor 1:", agreementAnchor1); + console2.log("Agreement Anchor 2:", agreementAnchor2); + vm.stopBroadcast(); + } +} + +interface IAgreementAnchorFactory { + function createAgreementAnchor(bytes32 contentHash, address counterSigner) + external + returns (address); +} diff --git a/script/03_UnificationProposal.s.sol b/script/04_UnificationProposal.s.sol similarity index 94% rename from script/03_UnificationProposal.s.sol rename to script/04_UnificationProposal.s.sol index 9deeb86..2b6dc53 100644 --- a/script/03_UnificationProposal.s.sol +++ b/script/04_UnificationProposal.s.sol @@ -2,12 +2,13 @@ pragma solidity ^0.8.20; import "forge-std/Script.sol"; +import "forge-std/StdAssertions.sol"; import {MainnetDeployer} from "./deployers/MainnetDeployer.sol"; import {IUniswapV2Factory} from "briefcase/protocols/v2-core/interfaces/IUniswapV2Factory.sol"; import {IUniswapV3Factory} from "briefcase/protocols/v3-core/interfaces/IUniswapV3Factory.sol"; import {IERC20} from "forge-std/interfaces/IERC20.sol"; -contract UnificationProposal is Script { +contract UnificationProposal is Script, StdAssertions { // TODO: Fill in these values address public constant AGREEMENT_ANCHOR_1 = address(0); bytes32 public constant CONTENT_HASH_1 = bytes32(0); @@ -41,6 +42,7 @@ contract UnificationProposal is Script { ) = _run(deployer); console.log("Calldata details:"); for (uint256 i = 0; i < calldatas.length; i++) { + assertTrue(targets[i] != address(0)); console.log("Target", i); console.log(targets[i]); console.log("Value", i); @@ -62,7 +64,6 @@ contract UnificationProposal is Script { console.logBytes(proposalCalldata); GOVERNOR_BRAVO.propose(targets, values, signatures, calldatas, PROPOSAL_DESCRIPTION); - vm.stopBroadcast(); } @@ -136,6 +137,7 @@ contract UnificationProposal is Script { // DAO attests to Agreement 1 if (AGREEMENT_ANCHOR_1 != address(0)) { + assertEq(CONTENT_HASH_1, IAgreementAnchor(AGREEMENT_ANCHOR_1).CONTENT_HASH()); targets[5] = address(EAS); values[5] = 0; signatures[5] = ""; @@ -157,6 +159,7 @@ contract UnificationProposal is Script { // DAO attests to Agreement 2 if (AGREEMENT_ANCHOR_2 != address(0)) { + assertEq(CONTENT_HASH_2, IAgreementAnchor(AGREEMENT_ANCHOR_2).CONTENT_HASH()); targets[6] = address(EAS); values[6] = 0; signatures[6] = ""; @@ -214,3 +217,7 @@ interface IGovernorBravo { string memory description ) external returns (uint256); } + +interface IAgreementAnchor { + function CONTENT_HASH() external returns (bytes32); +} diff --git a/test/ProtocolFees.fork.t.sol b/test/ProtocolFees.fork.t.sol index 4d9346b..388e661 100644 --- a/test/ProtocolFees.fork.t.sol +++ b/test/ProtocolFees.fork.t.sol @@ -19,7 +19,7 @@ import {Currency} from "v4-core/types/Currency.sol"; import {IUniswapV2Factory} from "./interfaces/IUniswapV2Factory.sol"; import {IUniswapV2Pair} from "./interfaces/IUniswapV2Pair.sol"; import {IUniswapV2Router02} from "./interfaces/IUniswapV2Router02.sol"; -import {UnificationProposal} from "../script/03_UnificationProposal.s.sol"; +import {UnificationProposal} from "../script/04_UnificationProposal.s.sol"; contract ProtocolFeesForkTest is Test { using FixedPointMathLib for uint256; diff --git a/test/UNIVesting.fork.t.sol b/test/UNIVesting.fork.t.sol index bf3214f..6753d25 100644 --- a/test/UNIVesting.fork.t.sol +++ b/test/UNIVesting.fork.t.sol @@ -5,7 +5,7 @@ import {Test} from "forge-std/Test.sol"; import {IERC20} from "forge-std/interfaces/IERC20.sol"; import {MainnetDeployer} from "../script/deployers/MainnetDeployer.sol"; import {IUNIVesting} from "../src/interfaces/IUNIVesting.sol"; -import {UnificationProposal} from "../script/03_UnificationProposal.s.sol"; +import {UnificationProposal} from "../script/04_UnificationProposal.s.sol"; import {IOwned} from "../src/interfaces/base/IOwned.sol"; import {IUniswapV3Factory} from "v3-core/contracts/interfaces/IUniswapV3Factory.sol"; @@ -340,4 +340,3 @@ contract UNIVestingForkTest is Test { ); } } - From 8d2832d431c0d10dbaeb6e2613ce412a9312f0e5 Mon Sep 17 00:00:00 2001 From: wildmolasses Date: Fri, 21 Nov 2025 10:58:03 -0500 Subject: [PATCH 3/4] test script --- script/03_CreateAgreementAnchorsMainnet.s.sol | 3 +- script/04_UnificationProposal.s.sol | 3 +- test/CreateAgreementAnchorsMainnet.fork.t.sol | 49 +++++++++++++++++++ 3 files changed, 53 insertions(+), 2 deletions(-) create mode 100644 test/CreateAgreementAnchorsMainnet.fork.t.sol diff --git a/script/03_CreateAgreementAnchorsMainnet.s.sol b/script/03_CreateAgreementAnchorsMainnet.s.sol index ed67c89..3db8504 100644 --- a/script/03_CreateAgreementAnchorsMainnet.s.sol +++ b/script/03_CreateAgreementAnchorsMainnet.s.sol @@ -13,7 +13,7 @@ contract CreateAgreementAnchors is Script { bytes32 public constant AGREEMENT_ANCHOR_2_CONTENT_HASH = ""; address public constant AGREEMENT_ANCHOR_2_COUNTER_SIGNER = address(0); - function run() public { + function run() public returns (address, address) { require(block.chainid == 1, "Not mainnet"); vm.startBroadcast(); address agreementAnchor1 = AGREEMENT_ANCHOR_FACTORY.createAgreementAnchor( @@ -26,6 +26,7 @@ contract CreateAgreementAnchors is Script { console2.log("Agreement Anchor 1:", agreementAnchor1); console2.log("Agreement Anchor 2:", agreementAnchor2); vm.stopBroadcast(); + return (agreementAnchor1, agreementAnchor2); } } diff --git a/script/04_UnificationProposal.s.sol b/script/04_UnificationProposal.s.sol index 2b6dc53..5c751c0 100644 --- a/script/04_UnificationProposal.s.sol +++ b/script/04_UnificationProposal.s.sol @@ -129,7 +129,8 @@ contract UnificationProposal is Script, StdAssertions { calldatas[3] = abi.encodeCall(V2_FACTORY.setFeeTo, (address(deployer.TOKEN_JAR()))); // Approve two years of vesting to the UNIvester smart contract - // UNI stays in treasury until vested and unvested UNI can be cancelled by setting approve back to 0 + // UNI stays in treasury until vested and unvested UNI can be cancelled by setting approve back + // to 0 targets[4] = address(UNI); values[4] = 0; signatures[4] = ""; diff --git a/test/CreateAgreementAnchorsMainnet.fork.t.sol b/test/CreateAgreementAnchorsMainnet.fork.t.sol new file mode 100644 index 0000000..fd3327a --- /dev/null +++ b/test/CreateAgreementAnchorsMainnet.fork.t.sol @@ -0,0 +1,49 @@ +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.20; + +import "forge-std/Test.sol"; +import { + CreateAgreementAnchors, + IAgreementAnchorFactory +} from "../script/03_CreateAgreementAnchorsMainnet.s.sol"; + +contract CreateAgreementAnchorsMainnetForkTest is Test { + CreateAgreementAnchors public script; + + function setUp() public { + vm.createSelectFork("mainnet"); + script = new CreateAgreementAnchors(); + } + + function test_run() public { + script.run(); + } + + function test_revert_wrongChainId() public { + vm.chainId(31_337); + vm.expectRevert("Not mainnet"); + script.run(); + } + + function test_createsAgreementAnchors() public { + (address agreementAnchor1, address agreementAnchor2) = script.run(); + assertEq( + IAgreementAnchor(agreementAnchor1).CONTENT_HASH(), script.AGREEMENT_ANCHOR_1_CONTENT_HASH() + ); + assertEq( + IAgreementAnchor(agreementAnchor2).CONTENT_HASH(), script.AGREEMENT_ANCHOR_2_CONTENT_HASH() + ); + assertEq( + IAgreementAnchor(agreementAnchor2).PARTY_B(), script.AGREEMENT_ANCHOR_1_COUNTER_SIGNER() + ); + assertEq( + IAgreementAnchor(agreementAnchor2).PARTY_B(), script.AGREEMENT_ANCHOR_2_COUNTER_SIGNER() + ); + } +} + +interface IAgreementAnchor { + function PARTY_A() external view returns (address); + function PARTY_B() external view returns (address); + function CONTENT_HASH() external view returns (bytes32); +} From ffe24196b5b92771b1e9248acdcfb50da40ef048 Mon Sep 17 00:00:00 2001 From: wildmolasses Date: Fri, 21 Nov 2025 11:03:03 -0500 Subject: [PATCH 4/4] rename scripts for cleaner diff --- ...nnet.s.sol => 02B_CreateAgreementAnchorsMainnet.s.sol} | 0 ...icationProposal.s.sol => 03_UnificationProposal.s.sol} | 0 test/CreateAgreementAnchorsMainnet.fork.t.sol | 8 ++------ test/ProtocolFees.fork.t.sol | 2 +- test/UNIVesting.fork.t.sol | 3 ++- 5 files changed, 5 insertions(+), 8 deletions(-) rename script/{03_CreateAgreementAnchorsMainnet.s.sol => 02B_CreateAgreementAnchorsMainnet.s.sol} (100%) rename script/{04_UnificationProposal.s.sol => 03_UnificationProposal.s.sol} (100%) diff --git a/script/03_CreateAgreementAnchorsMainnet.s.sol b/script/02B_CreateAgreementAnchorsMainnet.s.sol similarity index 100% rename from script/03_CreateAgreementAnchorsMainnet.s.sol rename to script/02B_CreateAgreementAnchorsMainnet.s.sol diff --git a/script/04_UnificationProposal.s.sol b/script/03_UnificationProposal.s.sol similarity index 100% rename from script/04_UnificationProposal.s.sol rename to script/03_UnificationProposal.s.sol diff --git a/test/CreateAgreementAnchorsMainnet.fork.t.sol b/test/CreateAgreementAnchorsMainnet.fork.t.sol index fd3327a..b972f26 100644 --- a/test/CreateAgreementAnchorsMainnet.fork.t.sol +++ b/test/CreateAgreementAnchorsMainnet.fork.t.sol @@ -5,7 +5,7 @@ import "forge-std/Test.sol"; import { CreateAgreementAnchors, IAgreementAnchorFactory -} from "../script/03_CreateAgreementAnchorsMainnet.s.sol"; +} from "script/02B_CreateAgreementAnchorsMainnet.s.sol"; contract CreateAgreementAnchorsMainnetForkTest is Test { CreateAgreementAnchors public script; @@ -15,11 +15,7 @@ contract CreateAgreementAnchorsMainnetForkTest is Test { script = new CreateAgreementAnchors(); } - function test_run() public { - script.run(); - } - - function test_revert_wrongChainId() public { + function test_RevertIf_wrongChainId() public { vm.chainId(31_337); vm.expectRevert("Not mainnet"); script.run(); diff --git a/test/ProtocolFees.fork.t.sol b/test/ProtocolFees.fork.t.sol index 388e661..4d9346b 100644 --- a/test/ProtocolFees.fork.t.sol +++ b/test/ProtocolFees.fork.t.sol @@ -19,7 +19,7 @@ import {Currency} from "v4-core/types/Currency.sol"; import {IUniswapV2Factory} from "./interfaces/IUniswapV2Factory.sol"; import {IUniswapV2Pair} from "./interfaces/IUniswapV2Pair.sol"; import {IUniswapV2Router02} from "./interfaces/IUniswapV2Router02.sol"; -import {UnificationProposal} from "../script/04_UnificationProposal.s.sol"; +import {UnificationProposal} from "../script/03_UnificationProposal.s.sol"; contract ProtocolFeesForkTest is Test { using FixedPointMathLib for uint256; diff --git a/test/UNIVesting.fork.t.sol b/test/UNIVesting.fork.t.sol index 6753d25..bf3214f 100644 --- a/test/UNIVesting.fork.t.sol +++ b/test/UNIVesting.fork.t.sol @@ -5,7 +5,7 @@ import {Test} from "forge-std/Test.sol"; import {IERC20} from "forge-std/interfaces/IERC20.sol"; import {MainnetDeployer} from "../script/deployers/MainnetDeployer.sol"; import {IUNIVesting} from "../src/interfaces/IUNIVesting.sol"; -import {UnificationProposal} from "../script/04_UnificationProposal.s.sol"; +import {UnificationProposal} from "../script/03_UnificationProposal.s.sol"; import {IOwned} from "../src/interfaces/base/IOwned.sol"; import {IUniswapV3Factory} from "v3-core/contracts/interfaces/IUniswapV3Factory.sol"; @@ -340,3 +340,4 @@ contract UNIVestingForkTest is Test { ); } } +