Skip to content
Merged
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
10 changes: 10 additions & 0 deletions helpers/SeamlessAddressBook.sol
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ library SeamlessAddressBook {
address constant SEAM = 0x1C7a460413dD4e964f96D8dFC56E7223cE88CD85;
address constant USDC = 0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913;
address constant ESSEAM = 0x998e44232BEF4F8B033e5A5175BDC97F2B10d5e5;
address constant BRETT = 0x532f27101965dd16442E59d40670FaF5eBB142E4;
address constant CBETH = 0x2Ae3F1Ec7F1F5012CFEab0185bfc7aa3cf0DEc22;
address constant CBBTC = 0xcbB7C0000aB88B473b1f5aFd9ef808440eed33Bf;
address constant EURC = 0x60a3E35Cc302bFA44Cb288Bc5a4F316Fdb1adb42;
Expand Down Expand Up @@ -71,4 +72,13 @@ library SeamlessAddressBook {
address constant SEAMLESS_WETH_MORPHO_VAULT_FEE_SPLITTER = 0xF070598338defd70068732290617c98CDb8adD30;

address constant SEAM_GOVERNOR_V2_IMPLEMENTATION = 0xC3A36d72bE57866EC4751D709b5bEF67efA9bAef;

address constant SEAM_TRANSFER_STRATEGY =
0x2b1bdeFCe33f34128759f71076eBd62637FD154C;
address constant ESSEAM_TRANSFER_STRATEGY =
0x2181be388ced00754E7c1Ee33DBcF78397DD89aC;
address constant USDC_TRANSFER_STRATEGY =
0x003D47ddDdb070822B35ae5cc4F0066Cf9E89753;
address constant BRETT_TRANSFER_STRATEGY =
0xD90EaC90f5f067283954b96BBc3d28E34ebE55Bb;
}
30 changes: 30 additions & 0 deletions proposals/sip_48/DeployProposal.s.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.25;

import { Script, console } from "forge-std/Script.sol";
import { Proposal } from "./Proposal.sol";
import { IGovernor } from "@openzeppelin/contracts/governance/IGovernor.sol";
import { SeamlessAddressBook } from "../../helpers/SeamlessAddressBook.sol";

contract DeployProposal is Script {
function setUp() public { }

function run(string memory descriptionPath) public {
Proposal proposal = new Proposal();

// Change this to GOVERNOR_LONG if you want to make proposal on the long governor
IGovernor governance = IGovernor(SeamlessAddressBook.GOVERNOR_SHORT);

string memory description = vm.readFile(descriptionPath);

address proposerAddress = vm.envAddress("PROPOSER_ADDRESS");
vm.startBroadcast(proposerAddress);
governance.propose(
proposal.getTargets(),
proposal.getValues(),
proposal.getCalldatas(),
description
);
vm.stopBroadcast();
}
}
76 changes: 76 additions & 0 deletions proposals/sip_48/Proposal.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.25;

import {
SeamlessGovProposal,
SeamlessAddressBook
} from "../../helpers/SeamlessGovProposal.sol";
import { ITransferStrategyBase } from
"@seamless-governance/interfaces/ITransferStrategyBase.sol";
import { IERC20 } from "@openzeppelin/contracts/interfaces/IERC20.sol";

contract Proposal is SeamlessGovProposal {
constructor() {
_makeProposal();
}

/// @dev This contract is not deployed onchain, do not make transactions to other contracts
/// or deploy a contract. Only the view/pure functions of deployed contracts can be called.
function _makeProposal() internal virtual override {
uint256 seamTransferStrategySeamBalance = IERC20(
SeamlessAddressBook.SEAM
).balanceOf(SeamlessAddressBook.SEAM_TRANSFER_STRATEGY);

_addAction(
SeamlessAddressBook.SEAM_TRANSFER_STRATEGY,
abi.encodeWithSelector(
ITransferStrategyBase.emergencyWithdrawal.selector,
SeamlessAddressBook.SEAM,
SeamlessAddressBook.TIMELOCK_SHORT,
seamTransferStrategySeamBalance
)
);

uint256 esSeamTransferStrategySeamBalance = IERC20(
SeamlessAddressBook.SEAM
).balanceOf(SeamlessAddressBook.ESSEAM_TRANSFER_STRATEGY);

_addAction(
SeamlessAddressBook.ESSEAM_TRANSFER_STRATEGY,
abi.encodeWithSelector(
ITransferStrategyBase.emergencyWithdrawal.selector,
SeamlessAddressBook.SEAM,
SeamlessAddressBook.TIMELOCK_SHORT,
esSeamTransferStrategySeamBalance
)
);

uint256 usdcTransferStrategyUsdcBalance = IERC20(
SeamlessAddressBook.USDC
).balanceOf(SeamlessAddressBook.USDC_TRANSFER_STRATEGY);

_addAction(
SeamlessAddressBook.USDC_TRANSFER_STRATEGY,
abi.encodeWithSelector(
ITransferStrategyBase.emergencyWithdrawal.selector,
SeamlessAddressBook.USDC,
SeamlessAddressBook.TIMELOCK_SHORT,
usdcTransferStrategyUsdcBalance
)
);

uint256 brettTransferStrategySeamBalance = IERC20(
SeamlessAddressBook.BRETT
).balanceOf(SeamlessAddressBook.BRETT_TRANSFER_STRATEGY);

_addAction(
SeamlessAddressBook.BRETT_TRANSFER_STRATEGY,
abi.encodeWithSelector(
ITransferStrategyBase.emergencyWithdrawal.selector,
SeamlessAddressBook.BRETT,
SeamlessAddressBook.TIMELOCK_SHORT,
brettTransferStrategySeamBalance
)
);
}
}
159 changes: 159 additions & 0 deletions proposals/sip_48/TenderlySimulation.s.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,159 @@
// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.21;

import { Script, console } from "forge-std/Script.sol";
import { Strings } from "@openzeppelin/contracts/utils/Strings.sol";
import { IVotes } from "@openzeppelin/contracts/governance/utils/IVotes.sol";
import { Proposal } from "./Proposal.sol";
import { IGovernor } from "@openzeppelin/contracts/governance/IGovernor.sol";
import { SeamlessAddressBook } from "../../helpers/SeamlessAddressBook.sol";
import { IVotes } from "@openzeppelin/contracts/governance/utils/IVotes.sol";

contract TenderlySimulation is Script {
// Change this to GOVERNOR_LONG if the proposal is made on the long governor
IGovernor governance = IGovernor(SeamlessAddressBook.GOVERNOR_SHORT);
IVotes seam = IVotes(SeamlessAddressBook.SEAM);

Proposal proposal = new Proposal();

address proposerAddress = 0x67b6dB42115d94Cc3FE27E92a3d12bB224041ac0;
uint256 proposerPk =
0x82fe25cccae9752b856c8857de74671320277f92e737b2116a5d9739dec59a26;

function _getProposalData(string memory descriptionPath)
internal
view
returns (uint256 proposalId, bytes32 descriptionHash)
{
string memory description = vm.readFile(descriptionPath);
descriptionHash = keccak256(bytes(description));

proposalId = governance.hashProposal(
proposal.getTargets(),
proposal.getValues(),
proposal.getCalldatas(),
descriptionHash
);
}

function setupProposer() public {
console.log("Setting up proposer");

_fundETH();
_fundSEAM();

moveOneBlockForwardOneSecond();
}

function delegateToProposer() public {
vm.startBroadcast(proposerPk);
seam.delegate(proposerAddress);
vm.stopBroadcast();
}

function moveOneBlockForwardOneSecond() public {
vm.rpc("evm_increaseTime", "[\"0x1\"]");
}

function _fundSEAM() public {
string memory params = string.concat(
"[\"",
Strings.toHexString(address(seam)),
"\",[\"",
Strings.toHexString(proposerAddress),
"\"],\"0x13DA329B6336471800000\"]" // 1.5M SEAM which is quorum
);
vm.rpc("tenderly_setErc20Balance", params);
}

function _fundETH() public {
string memory params = string.concat(
"[[\"",
Strings.toHexString(proposerAddress),
"\"],\"0xDE0B6B3A7640000\"]" // 1 ETH
);
vm.rpc("tenderly_setBalance", params);
}

function createProposal(string memory descriptionPath) public {
string memory description = vm.readFile(descriptionPath);

vm.startBroadcast(proposerPk);
governance.propose(
proposal.getTargets(),
proposal.getValues(),
proposal.getCalldatas(),
description
);
vm.stopBroadcast();
}

function increaseTimeVotingDelay() public {
console.log("Increasing voting delay");
string memory params = string.concat(
"[",
Strings.toString(block.timestamp + governance.votingDelay() + 1),
"]"
);
vm.rpc("evm_setNextBlockTimestamp", params);
vm.rpc("evm_mine", "[]");
}

function castVote(string memory descriptionPath) public {
console.log("Casting vote");
(uint256 proposalId,) = _getProposalData(descriptionPath);

vm.startBroadcast(proposerPk);
governance.castVote(proposalId, 1);
vm.stopBroadcast();
}

function increaseTimeVotingPeriod() public {
console.log("Increasing voting period");
string memory params = string.concat(
"[",
Strings.toString(block.timestamp + governance.votingPeriod() + 1),
"]"
);
vm.rpc("evm_setNextBlockTimestamp", params);
vm.rpc("evm_mine", "[]");
}

function queueProposal(string memory descriptionPath) public {
console.log("Queueing proposal");
(, bytes32 descriptionHash) = _getProposalData(descriptionPath);

vm.startBroadcast(proposerPk);
governance.queue(
proposal.getTargets(),
proposal.getValues(),
proposal.getCalldatas(),
descriptionHash
);
vm.stopBroadcast();
}

function setTimeToProposalEta(string memory descriptionPath) public {
console.log("Setting time to proposal eta");
(uint256 proposalId,) = _getProposalData(descriptionPath);
string memory params = string.concat(
"[", Strings.toString(governance.proposalEta(proposalId) + 1), "]"
);
vm.rpc("evm_setNextBlockTimestamp", params);
vm.rpc("evm_mine", "[]");
}

function executeProposal(string memory descriptionPath) public {
console.log("Executing proposal");
(, bytes32 descriptionHash) = _getProposalData(descriptionPath);

vm.startBroadcast(proposerPk);
governance.execute(
proposal.getTargets(),
proposal.getValues(),
proposal.getCalldatas(),
descriptionHash
);
vm.stopBroadcast();
}
}
97 changes: 97 additions & 0 deletions proposals/sip_48/TestProposal.t.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.25;

import { GovTestHelper } from "../../helpers/GovTestHelper.sol";
import { Proposal } from "./Proposal.sol";
import { IERC20 } from "@openzeppelin/contracts/interfaces/IERC20.sol";
import { SeamlessAddressBook } from "../../helpers/SeamlessGovProposal.sol";

contract TestProposal is GovTestHelper {
Proposal public proposal;

function setUp() public {
vm.rollFork(32430994);
proposal = new Proposal();
}

function test_seamAndEsseamClaimedAndTransferedToTimelock_afterPassingProposal(
) public {
IERC20 seam = IERC20(SeamlessAddressBook.SEAM);

uint256 seamTransferStrategySeamBalance = IERC20(
SeamlessAddressBook.SEAM
).balanceOf(SeamlessAddressBook.SEAM_TRANSFER_STRATEGY);

uint256 esseamTransferStrategyEsseamBalance = IERC20(
SeamlessAddressBook.SEAM
).balanceOf(SeamlessAddressBook.ESSEAM_TRANSFER_STRATEGY);

uint256 timelockBalanceBefore =
seam.balanceOf(SeamlessAddressBook.TIMELOCK_SHORT);

_passProposalShortGov(proposal);

uint256 timelockBalanceAfter =
seam.balanceOf(SeamlessAddressBook.TIMELOCK_SHORT);

assertEq(
timelockBalanceAfter,
timelockBalanceBefore + seamTransferStrategySeamBalance
+ esseamTransferStrategyEsseamBalance
);
assertEq(seam.balanceOf(SeamlessAddressBook.SEAM_TRANSFER_STRATEGY), 0);
assertEq(
seam.balanceOf(SeamlessAddressBook.ESSEAM_TRANSFER_STRATEGY), 0
);
}

function test_usdcClaimedAndTransferedToTimelock_afterPassingProposal()
public
{
IERC20 usdc = IERC20(SeamlessAddressBook.USDC);

uint256 usdcTransferStrategyUsdcBalance = IERC20(
SeamlessAddressBook.USDC
).balanceOf(SeamlessAddressBook.USDC_TRANSFER_STRATEGY);

uint256 timelockBalanceBefore =
usdc.balanceOf(SeamlessAddressBook.TIMELOCK_SHORT);

_passProposalShortGov(proposal);

uint256 timelockBalanceAfter =
usdc.balanceOf(SeamlessAddressBook.TIMELOCK_SHORT);

assertEq(
timelockBalanceAfter,
timelockBalanceBefore + usdcTransferStrategyUsdcBalance
);
assertEq(usdc.balanceOf(SeamlessAddressBook.USDC_TRANSFER_STRATEGY), 0);
}

function test_brettClaimedAndTransferedToTimelock_afterPassingProposal()
public
{
IERC20 brett = IERC20(SeamlessAddressBook.BRETT);

uint256 brettTransferStrategyBrettBalance = IERC20(
SeamlessAddressBook.BRETT
).balanceOf(SeamlessAddressBook.BRETT_TRANSFER_STRATEGY);

uint256 timelockBalanceBefore =
brett.balanceOf(SeamlessAddressBook.TIMELOCK_SHORT);

_passProposalShortGov(proposal);

uint256 timelockBalanceAfter =
brett.balanceOf(SeamlessAddressBook.TIMELOCK_SHORT);

assertEq(
timelockBalanceAfter,
timelockBalanceBefore + brettTransferStrategyBrettBalance
);
assertEq(
brett.balanceOf(SeamlessAddressBook.BRETT_TRANSFER_STRATEGY), 0
);
}
}
Loading