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
30 changes: 30 additions & 0 deletions proposals/sip_47/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();
}
}
31 changes: 31 additions & 0 deletions proposals/sip_47/Proposal.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.21;

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

contract Proposal is SeamlessGovProposal {
uint256 public constant budgetAmount = 2_250_000 * 1e18;

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 {
_addAction(
SeamlessAddressBook.SEAM,
abi.encodeWithSelector(
IERC20.transfer.selector,
SeamlessAddressBook.GUARDIAN_MULTISIG,
budgetAmount
)
);
}
}
159 changes: 159 additions & 0 deletions proposals/sip_47/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();
}
}
43 changes: 43 additions & 0 deletions proposals/sip_47/TestProposal.t.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
// SPDX-License-Identifier: UNLICENSED
pragma solidity ^0.8.25;

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

contract TestProposal is GovTestHelper {
Proposal public proposal;

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

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

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

_passProposalShortGov(proposal);

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

assertEq(
guardianBalanceAfter, guardianBalanceBefore + (2_250_000 * 1e18)
);
assertEq(
timelockBalanceAfter, timelockBalanceBefore - (2_250_000 * 1e18)
);
}
}
44 changes: 44 additions & 0 deletions proposals/sip_47/description.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
# [SIP-47] Protocol Rewards Refresh Q3 2025

## Summary
This proposal seeks to extend platform reward emissions beyond their current end date of June 30, 2025 through Q3 2025 (July 1 - September 30). The proposal maintains our quarterly cadence while preserving monthly flexibility for adjustments. The total budget of 2.25 million SEAM will be sourced from the short governor timelock and master emissions vesting contracts.

The proposed quarterly reward allocations are:
1. Seamless Vaults on Morpho (750,000 SEAM)
- Incentivize growth of Seamless Vaults on Morpho
- Maintain competitive yields for lenders and borrowers
- Ensure enough liquidity for Leverage Tokens strategies

2. Leverage Tokens (750,000 SEAM)
- Incentivize Leverage Tokens strategies

3. Safety Module (375,000 SEAM)
- Encourage protocol security through staking incentives
- Strengthen alignment between token holders and protocol safety

4. Strategic Growth Reserve (375,000 SEAM)
- Flexible budget for emerging opportunities


## Context and motivation
This proposal aligns with the broader token emissions strategy outlined in the [\[GP-7\] Expanding Protocol Reward Categories - Increasing Token Emission Budget for Seamless!](https://snapshot.box/#/s:seamlessprotocol.eth/proposal/0x113a9f75eb623fcccdc2b4400ecb0e125b95c0085bf0c8b44c5f136b27c29c2d) and [\[GP-10\] In preparation for the upcoming Leverage Tokens launch](https://snapshot.box/#/s:seamlessprotocol.eth/proposal/0xb83326a5d7a6a2e5fc282208d3b4e87cfa12b29cf5b948362b9959803733d986). As such, this current proposal will move straight to onchain governance.

The key drivers for continuing emissions are:
1. Market Timing: The current macro environment and crypto market sentiment create an opportune moment to accelerate growth and capture market share through strategic token emissions.
2. Product Evolution: Our expanding product suite - including new markets, recent Leverage Tokens launch, and innovative offerings - requires competitive incentives to drive initial adoption and sustainable usage.
3. Strategic Token Utilization: With approximately 44,5% of token supply [reserved for rewards](https://docs.seamlessprotocol.com/governance/seam-tokenomics), front-loading emissions during this growth phase allows us to:
- Maximize market presence and TVL capture
- Establish Seamless as the premier lending platform on Base
- Сapture a significant market share of Leverage Strategies on Base
- Build toward sustainable fee generation and token value accrual
- Create a framework for gradual emissions reduction over time

The reward schedules will be implemented based on recommendations from ecosystem partners and contributors. While this represents a maximum quarterly budget, actual emissions may be lower based on market conditions and performance metrics.

## Resources & References

- [\[GP-7\] Expanding Protocol Reward Categories - Increasing Token Emission Budget for Seamless!](https://snapshot.box/#/s:seamlessprotocol.eth/proposal/0x113a9f75eb623fcccdc2b4400ecb0e125b95c0085bf0c8b44c5f136b27c29c2d)
- [\[GP-10\] In preparation for the upcoming Leverage Tokens launch](https://snapshot.box/#/s:seamlessprotocol.eth/proposal/0xb83326a5d7a6a2e5fc282208d3b4e87cfa12b29cf5b948362b9959803733d986)
- [Proposal Implementation](https://github.com/seamless-protocol/gov-proposals/tree/main/proposals/sip_47)
- Built using [Seamless Governance Proposals tools](https://github.com/seamless-protocol/gov-proposals)