-
Notifications
You must be signed in to change notification settings - Fork 7
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
Op Farms #1
Open
oldchili
wants to merge
35
commits into
master
Choose a base branch
from
dev
base: master
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
+2,398
−1
Open
Op Farms #1
Changes from 1 commit
Commits
Show all changes
35 commits
Select commit
Hold shift + click to select a range
0f1676f
chore: forge init
oldchili fb88fda
forge install: forge-std
oldchili 6bc3351
forge install: dss-test
oldchili 98153df
forge remove forge-std
oldchili d861e89
Remove Counter files
oldchili 2d00826
Initial implementation
oldchili 51f07cf
Gateway => Bridge
oldchili 1e57f53
forge install: endgame-toolkit
oldchili 1c4f38f
forge install: op-token-bridge
oldchili 8f37ad4
Add init libs and integration test
oldchili 5aab2da
Add scripts - untested
oldchili a3a6e93
Small changes
oldchili fecbab3
Script fixes and configs
oldchili ab03847
Remove unused interface, fix typo
oldchili 73cbd87
Apply suggestions from code review
oldchili 3db60d6
Bond cfg.minGasLimit and cfg.initMinGasLimit by 500M
oldchili fa068a6
Notes that gas limits need to be tighter in production
oldchili 8902409
Add 20% gas buffer in Distribute.s.sol instructions
oldchili b08732f
Update dss-test
oldchili e665e0e
New line at EOF
oldchili 74f503e
Update op-token-bridge
oldchili b5a90d0
Apply suggestions from code review
oldchili 2212693
Remove redundant line
oldchili 94fc23f
Inlinve some vars in script/Init.s.sol
oldchili 29bc92a
More inlining
oldchili 7aad28b
Update CI
oldchili 632c17f
Minor changes in env example
oldchili 4950996
Change TODO to clarification message
oldchili f71a30e
Update lib/op-token-bridge
oldchili 119313b
Cantina audit (#2)
oldchili 525f923
Add Cantina Report (#3)
oldchili 6ce7a1e
Readme clarification about distribution rate (#4)
oldchili 62c2dea
Add ChainSecurity Report (#5)
oldchili a78ed4d
Upgrade bridge dependency
sunbreak1211 ba2f7c7
Upgrade bridge dependency
sunbreak1211 File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Add init libs and integration test
commit 8f37ad4b7a7dee564ea97229393b8cae6119147a
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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/ |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,13 @@ | ||
{ | ||
"domains": { | ||
"mainnet": { | ||
"chainlog": "0xdA0Ab1e0017DEbCd72Be8599041a2aa3bA7e740F", | ||
"tokens": [] | ||
}, | ||
"base": { | ||
"l1Messenger": "0x866E82a600A1414e583f7F13623F1aC5d58b0Afa", | ||
"l2Messenger": "0x4200000000000000000000000000000000000007", | ||
"tokens": [] | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,9 @@ | ||
{ | ||
"domains": { | ||
"sepolia": {}, | ||
"base_sepolia": { | ||
"l1Messenger": "0xC34855F4De64F1840e5686e64278da901e261f20", | ||
"l2Messenger": "0x4200000000000000000000000000000000000007" | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,256 @@ | ||
// SPDX-License-Identifier: AGPL-3.0-or-later | ||
|
||
// Copyright (C) 2024 Dai Foundation | ||
// | ||
// 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.21; | ||
|
||
import "dss-test/DssTest.sol"; | ||
|
||
import { Domain } from "dss-test/domains/Domain.sol"; | ||
import { OptimismDomain } from "dss-test/domains/OptimismDomain.sol"; | ||
|
||
import { TokenBridgeDeploy } from "lib/op-token-bridge/deploy/TokenBridgeDeploy.sol"; | ||
import { L2TokenBridgeSpell } from "lib/op-token-bridge/deploy/L2TokenBridgeSpell.sol"; | ||
import { L1TokenBridgeInstance } from "lib/op-token-bridge/deploy/L1TokenBridgeInstance.sol"; | ||
import { L2TokenBridgeInstance } from "lib/op-token-bridge/deploy/L2TokenBridgeInstance.sol"; | ||
import { TokenBridgeInit, BridgesConfig } from "lib/op-token-bridge/deploy/TokenBridgeInit.sol"; | ||
import { StakingRewards, StakingRewardsDeploy, StakingRewardsDeployParams } from "lib/endgame-toolkit/script/dependencies/StakingRewardsDeploy.sol"; | ||
import { VestedRewardsDistributionDeploy, VestedRewardsDistributionDeployParams } from "lib/endgame-toolkit/script/dependencies/VestedRewardsDistributionDeploy.sol"; | ||
import { VestedRewardsDistribution } from "lib/endgame-toolkit/src/VestedRewardsDistribution.sol"; | ||
import { GemMock } from "test/mocks/GemMock.sol"; | ||
import { DssVestMintableMock } from "test/mocks/DssVestMock.sol"; | ||
import { FarmProxyDeploy } from "deploy/FarmProxyDeploy.sol"; | ||
import { L2FarmProxySpell } from "deploy/L2FarmProxySpell.sol"; | ||
import { FarmProxyInit, ProxiesConfig } from "deploy/FarmProxyInit.sol"; | ||
import { L1FarmProxy } from "src/L1FarmProxy.sol"; | ||
import { L2FarmProxy } from "src/L2FarmProxy.sol"; | ||
|
||
interface L1RelayLike { | ||
function l2GovernanceRelay() external view returns (address); | ||
} | ||
|
||
contract IntegrationTest is DssTest { | ||
string config; | ||
Domain l1Domain; | ||
OptimismDomain l2Domain; | ||
|
||
// L1-side | ||
DssInstance dss; | ||
address PAUSE_PROXY; | ||
address escrow; | ||
address l1GovRelay; | ||
address l1Messenger; | ||
GemMock l1Token; | ||
address l1Bridge; | ||
L1FarmProxy l1Proxy; | ||
DssVestMintableMock vest; | ||
uint256 vestId; | ||
VestedRewardsDistribution vestedRewardsDistribution; | ||
|
||
// L2-side | ||
address l2GovRelay; | ||
address l2Messenger; | ||
GemMock l2Token; | ||
address l2Bridge; | ||
L2FarmProxy l2Proxy; | ||
StakingRewards farm; | ||
|
||
constructor() { | ||
vm.setEnv("FOUNDRY_ROOT_CHAINID", "1"); // used by ScriptTools to determine config path | ||
// Note: need to set the domains here instead of in setUp() to make sure their storages are actually persistent | ||
config = ScriptTools.loadConfig("config"); | ||
|
||
l1Domain = new Domain(config, getChain("mainnet")); | ||
l2Domain = new OptimismDomain(config, getChain("base"), l1Domain); | ||
} | ||
|
||
function setupGateways() internal { | ||
l1Messenger = address(l2Domain.l1Messenger()); | ||
l2Messenger = address(l2Domain.l2Messenger()); | ||
|
||
l1GovRelay = vm.computeCreateAddress(address(this), vm.getNonce(address(this)) + 3); // foundry increments a global nonce across domains | ||
l1Bridge = vm.computeCreateAddress(address(this), vm.getNonce(address(this)) + 5); | ||
|
||
l2Domain.selectFork(); | ||
L2TokenBridgeInstance memory l2BridgeInstance = TokenBridgeDeploy.deployL2Bridge({ | ||
deployer: address(this), | ||
l1GovRelay: l1GovRelay, | ||
l1Bridge: l1Bridge, | ||
l2Messenger: l2Messenger | ||
}); | ||
l2Bridge = l2BridgeInstance.bridge; | ||
l2GovRelay = l2BridgeInstance.govRelay; | ||
|
||
assertEq(address(L2TokenBridgeSpell(l2BridgeInstance.spell).l2Bridge()), address(l2Bridge)); | ||
|
||
l1Domain.selectFork(); | ||
L1TokenBridgeInstance memory l1BridgeInstance = TokenBridgeDeploy.deployL1Bridge({ | ||
deployer: address(this), | ||
owner: PAUSE_PROXY, | ||
l2GovRelay: l2GovRelay, | ||
l2Bridge: address(l2Bridge), | ||
l1Messenger: l1Messenger | ||
}); | ||
assertEq(l1BridgeInstance.bridge, l1Bridge); | ||
assertEq(l1BridgeInstance.govRelay, l1GovRelay); | ||
escrow = l1BridgeInstance.escrow; | ||
|
||
l2Domain.selectFork(); | ||
l2Token = new GemMock(0); | ||
l2Token.rely(l2GovRelay); | ||
l2Token.deny(address(this)); | ||
|
||
address[] memory l1Tokens = new address[](1); | ||
l1Tokens[0] = address(l1Token); | ||
address[] memory l2Tokens = new address[](1); | ||
l2Tokens[0] = address(l2Token); | ||
|
||
BridgesConfig memory cfg = BridgesConfig({ | ||
l1Messenger: l1Messenger, | ||
l2Messenger: l2Messenger, | ||
l1Tokens: l1Tokens, | ||
l2Tokens: l2Tokens, | ||
minGasLimit: 1_000_000, | ||
govRelayCLKey: "BASE_GOV_RELAY", | ||
escrowCLKey: "BASE_ESCROW", | ||
l1BridgeCLKey: "BASE_TOKEN_BRIDGE" | ||
}); | ||
|
||
l1Domain.selectFork(); | ||
vm.startPrank(PAUSE_PROXY); | ||
TokenBridgeInit.initBridges(dss, l1BridgeInstance, l2BridgeInstance, cfg); | ||
vm.stopPrank(); | ||
|
||
} | ||
|
||
function setUp() public { | ||
l1Domain.selectFork(); | ||
l1Domain.loadDssFromChainlog(); | ||
dss = l1Domain.dss(); | ||
PAUSE_PROXY = dss.chainlog.getAddress("MCD_PAUSE_PROXY"); | ||
|
||
vm.startPrank(PAUSE_PROXY); | ||
l1Token = new GemMock(100 ether); | ||
vest = new DssVestMintableMock(address(l1Token)); | ||
l1Token.rely(address(vest)); | ||
vest.file("cap", type(uint256).max); | ||
vm.stopPrank(); | ||
|
||
setupGateways(); | ||
|
||
l2Domain.selectFork(); | ||
|
||
address stakingToken = address(new GemMock(100 ether)); | ||
StakingRewardsDeployParams memory farmParams = StakingRewardsDeployParams({ | ||
owner: l2GovRelay, | ||
stakingToken: stakingToken, | ||
rewardsToken: address(l2Token) | ||
}); | ||
farm = StakingRewards(StakingRewardsDeploy.deploy(farmParams)); | ||
|
||
l2Proxy = L2FarmProxy(FarmProxyDeploy.deployL2Proxy({ | ||
deployer: address(this), | ||
owner: l2GovRelay, | ||
farm: address(farm) | ||
})); | ||
address l2Spell = FarmProxyDeploy.deployL2ProxySpell(); | ||
|
||
l1Domain.selectFork(); | ||
l1Proxy = L1FarmProxy(FarmProxyDeploy.deployL1Proxy({ | ||
deployer: address(this), | ||
owner: PAUSE_PROXY, | ||
rewardsToken: address(l1Token), | ||
remoteToken: address(l2Token), | ||
l2Proxy: address(l2Proxy), | ||
l1Bridge: l1Bridge | ||
})); | ||
|
||
VestedRewardsDistributionDeployParams memory distributionParams = VestedRewardsDistributionDeployParams({ | ||
deployer: address(this), | ||
owner: PAUSE_PROXY, | ||
vest: address(vest), | ||
rewards: address(l1Proxy) | ||
}); | ||
vestedRewardsDistribution = VestedRewardsDistribution(VestedRewardsDistributionDeploy.deploy(distributionParams)); | ||
|
||
ProxiesConfig memory cfg = ProxiesConfig({ | ||
vest: address(vest), | ||
vestTot: 100 * 1e18, | ||
vestBgn: block.timestamp, | ||
vestTau: 100 days, | ||
vestedRewardsDistribution: address(vestedRewardsDistribution), | ||
l1RewardsToken: address(l1Token), | ||
l2RewardsToken: address(l2Token), | ||
stakingToken: stakingToken, | ||
l1Bridge: l1Bridge, | ||
minGasLimit: 1_000_000, // determined by running deploy/Estimate.s.sol and adding some margin // TODO: leave this comment? | ||
rewardThreshold: 1 ether, | ||
farm: address(farm), | ||
rewardsDuration: 1 days, | ||
relayMinGasLimit: 1_000_000, | ||
proxyChainlogKey: "FARM_PROXY_TKA_TKB_ARB", | ||
distrChainlogKey: "REWARDS_DISTRIBUTION_TKA_TKB_ARB" | ||
}); | ||
|
||
vm.startPrank(PAUSE_PROXY); | ||
FarmProxyInit.initProxies(dss, l1GovRelay, address(l1Proxy), address(l2Proxy), l2Spell, cfg); | ||
vm.stopPrank(); | ||
|
||
// test L1 side of initProxies | ||
vestId = vestedRewardsDistribution.vestId(); | ||
assertEq(vest.usr(vestId), cfg.vestedRewardsDistribution); | ||
assertEq(vest.bgn(vestId), cfg.vestBgn); | ||
assertEq(vest.clf(vestId), cfg.vestBgn); | ||
assertEq(vest.fin(vestId), cfg.vestBgn + cfg.vestTau); | ||
assertEq(vest.tot(vestId), cfg.vestTot); | ||
assertEq(vest.mgr(vestId), address(0)); | ||
assertEq(vest.res(vestId), 1); | ||
assertEq(l1Proxy.minGasLimit(), cfg.minGasLimit); | ||
assertEq(l1Proxy.rewardThreshold(), cfg.rewardThreshold); | ||
assertEq(dss.chainlog.getAddress("FARM_PROXY_TKA_TKB_ARB"), address(l1Proxy)); | ||
assertEq(dss.chainlog.getAddress("REWARDS_DISTRIBUTION_TKA_TKB_ARB"), cfg.vestedRewardsDistribution); | ||
|
||
l2Domain.relayFromHost(true); | ||
|
||
// test L2 side of initProxies | ||
assertEq(l2Proxy.rewardThreshold(), cfg.rewardThreshold); | ||
assertEq(farm.rewardsDistribution(), address(l2Proxy)); | ||
assertEq(farm.rewardsDuration(), cfg.rewardsDuration); | ||
} | ||
|
||
function testDistribution() public { | ||
l1Domain.selectFork(); | ||
uint256 rewardThreshold = l1Proxy.rewardThreshold(); | ||
vm.warp(vest.bgn(vestId) + rewardThreshold * (vest.fin(vestId) - vest.bgn(vestId)) / vest.tot(vestId) + 1); | ||
uint256 amount = vest.unpaid(vestId); | ||
assertGt(amount, rewardThreshold); | ||
assertEq(l1Token.balanceOf(escrow), 0); | ||
|
||
vestedRewardsDistribution.distribute(); | ||
|
||
assertEq(l1Token.balanceOf(escrow), amount); | ||
|
||
l2Domain.relayFromHost(true); | ||
|
||
assertEq(l2Token.balanceOf(address(l2Proxy)), amount); | ||
|
||
l2Proxy.forwardReward(); | ||
|
||
assertEq(l2Token.balanceOf(address(l2Proxy)), 0); | ||
assertEq(l2Token.balanceOf(address(farm)), amount); | ||
assertEq(farm.rewardRate(), amount / farm.rewardsDuration()); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Just consider this
relayFromHost
would also be executing the L2 side of the spell for the Bridge set up. Nothing really wrong with that but doesn't get isolated in thesetupGateways
function as was probably the intention.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This code is the same as in
arbitrum-farms
.For some reason when adding
l2Domain.relayFromHost(false);
at the end ofsetupGateways
the test fails with a message of "revert: CrossDomainMessenger: message has already been relayed".I think @telome saw something similar, but not sure.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ok yeah let's leave this open for now. It doesn't affect the audit as not in the scope anyway.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Still not sure what exactly is the root cause of the issue but there seems to be some bug in foundry that causes the exit from
setUp()
to incorrectly restore the storage ofOptimismDomain
. For now a simple workaround could be to avoid usingsetUp()
. For example, renamingsetUp()
tosetUpFarm()
and callingsetUpFarm()
at the beginning ofdistribute()
avoids the issue.