Skip to content
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

Add L1 & L2 Farm Proxies #1

Open
wants to merge 58 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 17 commits
Commits
Show all changes
58 commits
Select commit Hold shift + click to select a range
21f73d7
Add farm proxies
May 22, 2024
c8febe2
Add missing approval
May 22, 2024
afb473c
Add e2e test & deploy scripts
May 24, 2024
2a65e8c
Update new-bridge
May 24, 2024
79246a0
Add default maxGas estimate
May 31, 2024
45669be
Add unit tests
May 31, 2024
4ef9be8
Add vest setup to init script
May 31, 2024
5f42034
Add maxGas estimation script
Jun 5, 2024
4cede7b
Add minReward to L1Proxy
Jun 13, 2024
f88061e
Revert when reward is too small on L1
Jun 14, 2024
d98c65a
Add estimateDepositCost view func
Jun 14, 2024
e1c107b
Apply suggestions from code review
telome Jun 19, 2024
b498cd9
Update arbitrum-token-bridge
Jun 24, 2024
ad7048c
Apply suggestions from code review
telome Jun 24, 2024
c86e45e
File gas params in spell
Jun 24, 2024
c2eb5ab
Update arbitrum-token-bridge
Jun 25, 2024
f371a1a
Add more sanity checks
Jun 25, 2024
9b4847b
Assume dss-vest already inited
Jun 25, 2024
63e4ca2
Update chainlog
Jun 25, 2024
84e2b6a
Make L2 spell reusable across L2 proxies
Jun 25, 2024
6044525
Add farm sanity checks
Jun 25, 2024
87a2147
Add more farm checks
Jun 25, 2024
cf292ac
Fix nits
Jun 25, 2024
8fb5adf
Apply suggestions from code review
telome Jun 25, 2024
71829f9
Add Deploy.s.sol
Jun 26, 2024
214a78a
Add Init.s.sol
Jun 26, 2024
9e2b461
Fix tests and apply correct alias to feeRecipient
Jun 27, 2024
6ba7328
Move feeRecipient aliasing to init lib
Jun 27, 2024
2229c3a
Move dealiasing to L1FarmProxy
Jun 27, 2024
2093296
Add deploy test scripts and fix nits
Jun 27, 2024
1441c87
Update script/output/11155111/deployed-latest.json
telome Jun 27, 2024
8202396
Fix Estimate script
Jul 1, 2024
7777054
Use minThreshold
Jul 2, 2024
84680b8
emit RewardAdded from L1 proxy
Jul 5, 2024
1376638
Complete README
Jul 9, 2024
a8c1570
Remove vestMgr
Jul 9, 2024
d356c1d
Update arbitrum-token-bridge
Jul 9, 2024
e36873e
Check vest mgr is still 0 after spell
Jul 10, 2024
25c8104
Add token recovery funcs
Jul 10, 2024
c4d9f08
Recommend L2FarmProxy.rewardThreshold <= L1FarmProxy.rewardThreshold
Jul 10, 2024
99af088
Use single rewardThreshold in init lib
Jul 10, 2024
fbe197f
Fix L2 spell and add L2 spell tests
Jul 10, 2024
2960565
Fix alignment
Jul 10, 2024
f74d47a
Fix spell revert msg
Jul 10, 2024
7226d12
Fix nits
Jul 11, 2024
78aa99d
Fix excess fee collection logic
Jul 13, 2024
a45c297
Remove old Deploy.s.sol
Jul 13, 2024
880c283
Remove extra space
Jul 15, 2024
3e6f5c7
Merge pull request #2 from telome/fee-forwarder
telome Jul 16, 2024
d8204ad
Update README for env vars to be exported
Jul 16, 2024
09ce9d8
Use single deploy proxy script with multiple deployers
Jul 18, 2024
702e425
Remove unnecessary command
Jul 18, 2024
fa18c20
Update deps
Jul 18, 2024
38476c1
Remove outdated files
Jul 18, 2024
69b539f
Add CS report (#3)
telome Aug 23, 2024
8cae0ca
Update deps (#4)
telome Aug 23, 2024
b879549
Update CI
Aug 23, 2024
370f3da
Upgrade bridge dependency + minor adjustment (#5)
sunbreak1211 Sep 19, 2024
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
14 changes: 14 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
# Compiler files
cache/
out/

# Ignores development broadcast logs
!/broadcast
/broadcast/*/31337/
/broadcast/**/dry-run/

# Docs
docs/

# Dotenv file
.env
telome marked this conversation as resolved.
Show resolved Hide resolved
9 changes: 9 additions & 0 deletions .gitmodules
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
[submodule "lib/dss-test"]
path = lib/dss-test
url = https://github.com/makerdao/dss-test
[submodule "lib/arbitrum-token-bridge"]
path = lib/arbitrum-token-bridge
url = https://github.com/makerdao/arbitrum-token-bridge
[submodule "lib/endgame-toolkit"]
path = lib/endgame-toolkit
url = https://github.com/makerdao/endgame-toolkit
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
# Endgame L2 Farms
49 changes: 49 additions & 0 deletions deploy/FarmProxyDeploy.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
// SPDX-FileCopyrightText: © 2024 Dai Foundation <www.daifoundation.org>
// SPDX-License-Identifier: AGPL-3.0-or-later
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.

pragma solidity >=0.8.0;

import { ScriptTools } from "dss-test/ScriptTools.sol";

import { L2FarmProxyInstance } from "./L2FarmProxyInstance.sol";
import { L2FarmProxySpell } from "./L2FarmProxySpell.sol";
import { L1FarmProxy } from "src/L1FarmProxy.sol";
import { L2FarmProxy } from "src/L2FarmProxy.sol";

library FarmProxyDeploy {
function deployL1Proxy(
address deployer,
address owner,
address gem,
address l2Proxy,
address feeRecipient,
address inbox,
address l1Gateway
) internal returns (address l1Proxy) {
l1Proxy = address(new L1FarmProxy(gem, l2Proxy, feeRecipient, inbox, l1Gateway));
ScriptTools.switchOwner(l1Proxy, deployer, owner);
}

function deployL2Proxy(
address deployer,
address owner,
address farm
) internal returns (L2FarmProxyInstance memory l2ProxyInstance) {
l2ProxyInstance.proxy = address(new L2FarmProxy(farm));
l2ProxyInstance.spell = address(new L2FarmProxySpell(l2ProxyInstance.proxy));
ScriptTools.switchOwner(l2ProxyInstance.proxy, deployer, owner);
}
}
160 changes: 160 additions & 0 deletions deploy/FarmProxyInit.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,160 @@
// SPDX-FileCopyrightText: © 2024 Dai Foundation <www.daifoundation.org>
// SPDX-License-Identifier: AGPL-3.0-or-later
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.

pragma solidity >=0.8.0;

import { DssInstance } from "dss-test/MCD.sol";
import { L2FarmProxyInstance } from "./L2FarmProxyInstance.sol";
import { L2FarmProxySpell } from "./L2FarmProxySpell.sol";

interface AuthLike {
function rely(address usr) external;
}

interface DssVestLike {
function gem() external view returns (address);
function create(address _usr, uint256 _tot, uint256 _bgn, uint256 _tau, uint256 _eta, address _mgr) external returns (uint256 id);
function restrict(uint256 _id) external;
}

interface VestedRewardsDistributionLike {
function dssVest() external view returns (address);
function stakingRewards() external view returns (address);
function gem() external view returns (address);
function file(bytes32 what, uint256 data) external;
}

interface L1FarmProxyLike {
function rewardsToken() external view returns (address);
function l2Proxy() external view returns (address);
function feeRecipient() external view returns (address);
function inbox() external view returns (address);
function l1Gateway() external view returns (address);
function file(bytes32 what, uint256 data) external;
}

interface L1RelayLike {
function relay(
address target,
bytes calldata targetData,
uint256 l1CallValue,
uint256 maxGas,
uint256 gasPriceBid,
uint256 maxSubmissionCost
) external payable;
}

struct MessageParams {
uint256 maxGas;
uint256 gasPriceBid;
uint256 maxSubmissionCost;
}

struct ProxiesConfig {
address vest;
address vestedRewardDistribution;
uint256 vestTot;
uint256 vestBgn;
uint256 vestTau;
address vestMgr;
address rewardsToken;
address l2Proxy;
address feeRecipient;
address inbox;
address l1Gateway;
uint256 maxGas; // For the L1 proxy
uint256 gasPriceBid; // For the L1 proxy
uint256 l1MinReward; // For the L1 proxy
uint256 l2MinReward; // For the L2 proxy
uint256 rewardsDuration; // For the farm on L2
MessageParams xchainMsg; // For the xchain message executing the L2 spell
}

library FarmProxyInit {
function initProxies(
DssInstance memory dss,
address l1Proxy_,
L2FarmProxyInstance memory l2ProxyInstance,
ProxiesConfig memory cfg
) internal {
L1FarmProxyLike l1Proxy = L1FarmProxyLike(l1Proxy_);
DssVestLike vest = DssVestLike(cfg.vest);
VestedRewardsDistributionLike distribution = VestedRewardsDistributionLike(cfg.vestedRewardDistribution);

// sanity checks

require(vest.gem() == cfg.rewardsToken, "FarmProxyInit/vest-gem-mismatch");
require(distribution.gem() == cfg.rewardsToken, "FarmProxyInit/distribution-gem-mismatch");
require(distribution.stakingRewards() == l1Proxy_, "FarmProxyInit/distribution-farm-mismatch");
require(distribution.dssVest() == cfg.vest, "FarmProxyInit/distribution-vest-mismatch");
require(l1Proxy.rewardsToken() == cfg.rewardsToken, "FarmProxyInit/rewards-token-mismatch");
require(l1Proxy.l2Proxy() == cfg.l2Proxy, "FarmProxyInit/l2-proxy-mismatch");
require(l1Proxy.feeRecipient() == cfg.feeRecipient, "FarmProxyInit/fee-recipient-mismatch");
require(l1Proxy.inbox() == cfg.inbox, "FarmProxyInit/inbox-mismatch");
require(l1Proxy.l1Gateway() == cfg.l1Gateway, "FarmProxyInit/l1-gateway-mismatch");
require(cfg.maxGas <= 10_000_000_000, "FarmProxyInit/max-gas-out-of-bounds");
require(cfg.gasPriceBid <= 10_000 gwei, "FarmProxyInit/gas-price-bid-out-of-bounds");
require(cfg.l1MinReward <= type(uint128).max, "FarmProxyInit/l1-min-reward-out-of-bounds");
require(cfg.l2MinReward > 0, "FarmProxyInit/l2-min-reward-out-of-bounds");

// setup vest

if (cfg.rewardsToken == dss.chainlog.getAddress("NST")) {
// TODO: NST isn't currently planned as an L2 rewardsToken, so do we want to handle this case at all?
AuthLike(dss.chainlog.getAddress("MCD_VAT")).rely(cfg.vest);
} else {
AuthLike(cfg.rewardsToken).rely(cfg.vest);
}
oldchili marked this conversation as resolved.
Show resolved Hide resolved
uint256 vestId = vest.create({
_usr: cfg.vestedRewardDistribution,
_tot: cfg.vestTot,
_bgn: cfg.vestBgn,
_tau: cfg.vestTau,
_eta: 0,
_mgr: cfg.vestMgr
sunbreak1211 marked this conversation as resolved.
Show resolved Hide resolved
});
vest.restrict(vestId);
distribution.file("vestId", vestId);

// setup L1 proxy

l1Proxy.file("maxGas", cfg.maxGas);
l1Proxy.file("gasPriceBid", cfg.gasPriceBid);
l1Proxy.file("minReward", cfg.l1MinReward);

oldchili marked this conversation as resolved.
Show resolved Hide resolved
// setup L2 proxy

L1RelayLike l1GovRelay = L1RelayLike(dss.chainlog.getAddress("ARBITRUM_GOV_RELAY"));
uint256 l1CallValue = cfg.xchainMsg.maxSubmissionCost + cfg.xchainMsg.maxGas * cfg.xchainMsg.gasPriceBid;

// not strictly necessary (as the retryable ticket creation would otherwise fail)
// but makes the eth balance requirement more explicit
require(address(l1GovRelay).balance >= l1CallValue, "FarmProxyInit/insufficient-relay-balance");

l1GovRelay.relay({
target: l2ProxyInstance.spell,
targetData: abi.encodeCall(L2FarmProxySpell.init, (cfg.l2MinReward, cfg.rewardsDuration)),
l1CallValue: l1CallValue,
maxGas: cfg.xchainMsg.maxGas,
gasPriceBid: cfg.xchainMsg.gasPriceBid,
maxSubmissionCost: cfg.xchainMsg.maxSubmissionCost
});

// update chainlog

dss.chainlog.setAddress("ARBITRUM_L1_FARM_PROXY", l1Proxy_);
}
oldchili marked this conversation as resolved.
Show resolved Hide resolved
}
22 changes: 22 additions & 0 deletions deploy/L2FarmProxyInstance.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
// SPDX-FileCopyrightText: © 2024 Dai Foundation <www.daifoundation.org>
// SPDX-License-Identifier: AGPL-3.0-or-later
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.

pragma solidity >=0.8.0;

struct L2FarmProxyInstance {
address proxy;
address spell;
}
46 changes: 46 additions & 0 deletions deploy/L2FarmProxySpell.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
// SPDX-FileCopyrightText: © 2024 Dai Foundation <www.daifoundation.org>
// SPDX-License-Identifier: AGPL-3.0-or-later
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program. If not, see <https://www.gnu.org/licenses/>.

pragma solidity >=0.8.0;

import { L2FarmProxy } from "src/L2FarmProxy.sol";

interface FarmLike {
function setRewardsDistribution(address) external;
function setRewardsDuration(uint256) external;
}

// A reusable L2 spell to be used by the L2GovernanceRelay to exert admin control over L2FarmProxy
contract L2FarmProxySpell {
L2FarmProxy public immutable l2Proxy;
oldchili marked this conversation as resolved.
Show resolved Hide resolved
constructor(address l2Proxy_) {
l2Proxy = L2FarmProxy(l2Proxy_);
}

function rely(address usr) external { l2Proxy.rely(usr); }
function deny(address usr) external { l2Proxy.deny(usr); }
function file(bytes32 what, uint256 data) external { l2Proxy.file(what, data); }
oldchili marked this conversation as resolved.
Show resolved Hide resolved

oldchili marked this conversation as resolved.
Show resolved Hide resolved
function init(uint256 minReward, uint256 rewardsDuration) external {
// TODO: add L2-side sanity checks

l2Proxy.file("minReward", minReward);

FarmLike farm = FarmLike(address(l2Proxy.farm()));
farm.setRewardsDistribution(address(l2Proxy));
farm.setRewardsDuration(rewardsDuration);
}
}
14 changes: 14 additions & 0 deletions foundry.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
[profile.default]
src = "src"
out = "out"
libs = ["lib"]
solc = "0.8.21"
fs_permissions = [
{ access = "read", path = "./script/input/"}
]

[etherscan]
mainnet = { key = "${ETHERSCAN_KEY}" }
sepolia = { key = "${ETHERSCAN_KEY}", chain = 11155111 }
arbitrum_one = { key = "${ARBISCAN_KEY}", chain = 42161, url = "https://api.arbiscan.io/api" }
arbitrum_one_sepolia = { key = "${ARBISCAN_KEY}", chain = 421614, url = "https://api-sepolia.arbiscan.io/api" }
telome marked this conversation as resolved.
Show resolved Hide resolved
1 change: 1 addition & 0 deletions lib/arbitrum-token-bridge
Submodule arbitrum-token-bridge added at 247105
1 change: 1 addition & 0 deletions lib/dss-test
Submodule dss-test added at 41066f
1 change: 1 addition & 0 deletions lib/endgame-toolkit
Submodule endgame-toolkit added at 8c879a
2 changes: 2 additions & 0 deletions remappings.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
forge-std/=lib/dss-test/lib/forge-std/src/
openzeppelin-contracts/=lib/endgame-toolkit/lib/openzeppelin-contracts/contracts/
Loading