diff --git a/.gitignore b/.gitignore index 8d64507..b48895d 100644 --- a/.gitignore +++ b/.gitignore @@ -6,9 +6,9 @@ out/ /broadcast # Ignores script config -/script/input/*/*.json -!/script/input/*/template-*.json -/script/output/*/*.json +/script/input/**/*.json +!/script/input/**/template-*.json +/script/output/**/*.json # Docs docs/ diff --git a/foundry.toml b/foundry.toml index 7729d0e..6f4b513 100644 --- a/foundry.toml +++ b/foundry.toml @@ -3,7 +3,7 @@ src = 'src' out = 'out' script = 'script' libs = ['lib'] -solc_version = '0.8.19' +solc_version = '0.8.16' optimizer = true fs_permissions = [ { access = "read", path = "./out/" }, diff --git a/lib/dss-test b/lib/dss-test index 9d75578..28000a0 160000 --- a/lib/dss-test +++ b/lib/dss-test @@ -1 +1 @@ -Subproject commit 9d7557817af849273ec1d952cc953b212f19025f +Subproject commit 28000a0ea3602c887f2fdc22dea591789d1b4971 diff --git a/script/02-StakingRewardsInit.s.sol b/script/02-StakingRewardsInit.s.sol deleted file mode 100644 index 6ac23a6..0000000 --- a/script/02-StakingRewardsInit.s.sol +++ /dev/null @@ -1,98 +0,0 @@ -// SPDX-FileCopyrightText: © 2023 Dai Foundation -// 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 . -pragma solidity ^0.8.0; - -import {Script} from "forge-std/Script.sol"; -import {stdJson} from "forge-std/StdJson.sol"; -import {ScriptTools} from "dss-test/ScriptTools.sol"; - -import {Reader} from "./helpers/Reader.sol"; -import {StakingRewardsInit, StakingRewardsInitParams} from "./dependencies/StakingRewardsInit.sol"; -import { - VestedRewardsDistributionInit, - VestedRewardsDistributionInitParams -} from "./dependencies/VestedRewardsDistributionInit.sol"; -import {VestInit, VestInitParams, VestCreateParams} from "./dependencies/VestInit.sol"; - -interface RelyLike { - function rely(address who) external; -} - -interface WithGemLike { - function gem() external view returns (address); -} - -interface StakingRewardsLike { - function rewardsToken() external view returns (address); -} - -contract Phase0StakingRewardsInitScript is Script { - using stdJson for string; - using ScriptTools for string; - - string internal constant NAME = "Phase0StakingRewardsInit"; - - function run() external { - Reader deps = new Reader(ScriptTools.loadDependencies()); - - address ngt = deps.envOrReadAddress(".ngt", "FOUNDRY_NGT"); - address dist = deps.envOrReadAddress(".dist", "FOUNDRY_DIST"); - address farm = deps.envOrReadAddress(".farm", "FOUNDRY_FARM"); - address vest = deps.envOrReadAddress(".vest", "FOUNDRY_VEST"); - - Reader config = new Reader(ScriptTools.loadConfig()); - - uint256 vestCap = config.readUint(".vestCap"); - uint256 vestTot = config.readUint(".vestTot"); - uint256 vestBgn = config.readUint(".vestBgn"); - uint256 vestTau = config.readUint(".vestTau"); - - require(WithGemLike(dist).gem() == ngt, "VestedRewardsDistribution/invalid-gem"); - require(WithGemLike(vest).gem() == ngt, "DssVest/invalid-gem"); - require(StakingRewardsLike(farm).rewardsToken() == ngt, "StakingRewards/invalid-rewards-token"); - - vm.startBroadcast(); - - // Grant minting rights on `ngt` to `vest`. - RelyLike(ngt).rely(vest); - - // Define global max vesting ratio on `vest`. - VestInit.init(vest, VestInitParams({cap: vestCap})); - - // Set `dist` with `rewardsDistribution` role in `farm`. - StakingRewardsInit.init(farm, StakingRewardsInitParams({dist: dist})); - - // Create the proper vesting stream for rewards distribution. - uint256 vestId = VestInit.create( - vest, - VestCreateParams({usr: dist, tot: vestTot, bgn: vestBgn, tau: vestTau, eta: 0}) - ); - - // Set the `vestId` in `dist` - VestedRewardsDistributionInit.init(dist, VestedRewardsDistributionInitParams({vestId: vestId})); - - vm.stopBroadcast(); - - ScriptTools.exportContract(NAME, "ngt", ngt); - ScriptTools.exportContract(NAME, "dist", dist); - ScriptTools.exportContract(NAME, "farm", farm); - ScriptTools.exportContract(NAME, "vest", vest); - ScriptTools.exportContract(NAME, "vestId", address(uint160(vestId))); - ScriptTools.exportContract(NAME, "vestTot", address(uint160(vestTot))); - ScriptTools.exportContract(NAME, "vestBgn", address(uint160(vestBgn))); - ScriptTools.exportContract(NAME, "vestTau", address(uint160(vestTau))); - } -} diff --git a/script/dependencies/SDAODeploy.sol b/script/dependencies/SDAODeploy.sol index 551e392..99ce344 100644 --- a/script/dependencies/SDAODeploy.sol +++ b/script/dependencies/SDAODeploy.sol @@ -13,7 +13,7 @@ // You should have received a copy of the GNU Affero General Public License // along with this program. If not, see . -pragma solidity ^0.8.0; +pragma solidity ^0.8.16; import {ScriptTools} from "dss-test/ScriptTools.sol"; import {SDAO} from "../../src/SDAO.sol"; diff --git a/script/dependencies/StakingRewardsDeploy.sol b/script/dependencies/StakingRewardsDeploy.sol index 9e8c605..712bfa8 100644 --- a/script/dependencies/StakingRewardsDeploy.sol +++ b/script/dependencies/StakingRewardsDeploy.sol @@ -13,7 +13,7 @@ // You should have received a copy of the GNU Affero General Public License // along with this program. If not, see . -pragma solidity ^0.8.0; +pragma solidity ^0.8.16; import {StakingRewards} from "../../src/synthetix/StakingRewards.sol"; @@ -24,7 +24,7 @@ struct StakingRewardsDeployParams { } library StakingRewardsDeploy { - function deploy(StakingRewardsDeployParams memory p) internal returns (address farm) { - farm = address(new StakingRewards(p.owner, address(0), p.rewardsToken, p.stakingToken)); + function deploy(StakingRewardsDeployParams memory p) internal returns (address rewards) { + rewards = address(new StakingRewards(p.owner, address(0), p.rewardsToken, p.stakingToken)); } } diff --git a/script/dependencies/StakingRewardsInit.sol b/script/dependencies/StakingRewardsInit.sol index fc457b3..3e10a28 100644 --- a/script/dependencies/StakingRewardsInit.sol +++ b/script/dependencies/StakingRewardsInit.sol @@ -13,15 +13,7 @@ // You should have received a copy of the GNU Affero General Public License // along with this program. If not, see . -pragma solidity ^0.8.0; - -interface StakingRewardsLike { - function setRewardsDistribution(address _rewardsDistribution) external; - - function acceptOwnership() external; - - function nominateNewOwner(address _owner) external; -} +pragma solidity ^0.8.16; struct StakingRewardsInitParams { address dist; @@ -32,17 +24,25 @@ struct StakingRewardsNominateNewOwnerParams { } library StakingRewardsInit { - function init(address farm, StakingRewardsInitParams memory p) internal { - StakingRewardsLike(farm).setRewardsDistribution(p.dist); + function init(address rewards, StakingRewardsInitParams memory p) internal { + StakingRewardsLike(rewards).setRewardsDistribution(p.dist); } /// @dev `StakingRewards` ownership transfer is a 2-step process: nominate + acceptance. - function nominateNewOwner(address farm, StakingRewardsNominateNewOwnerParams memory p) internal { - StakingRewardsLike(farm).nominateNewOwner(p.newOwner); + function nominateNewOwner(address rewards, StakingRewardsNominateNewOwnerParams memory p) internal { + StakingRewardsLike(rewards).nominateNewOwner(p.newOwner); } /// @dev `StakingRewards` ownership transfer requires the new owner to explicitly accept it. - function acceptOwnership(address farm) internal { - StakingRewardsLike(farm).acceptOwnership(); + function acceptOwnership(address rewards) internal { + StakingRewardsLike(rewards).acceptOwnership(); } } + +interface StakingRewardsLike { + function setRewardsDistribution(address _rewardsDistribution) external; + + function acceptOwnership() external; + + function nominateNewOwner(address _owner) external; +} diff --git a/script/dependencies/SubProxyDeploy.sol b/script/dependencies/SubProxyDeploy.sol index e5ff0dd..57a089e 100644 --- a/script/dependencies/SubProxyDeploy.sol +++ b/script/dependencies/SubProxyDeploy.sol @@ -13,7 +13,7 @@ // You should have received a copy of the GNU Affero General Public License // along with this program. If not, see . -pragma solidity ^0.8.0; +pragma solidity ^0.8.16; import {ScriptTools} from "dss-test/ScriptTools.sol"; import {SubProxy} from "../../src/SubProxy.sol"; diff --git a/script/dependencies/SubProxyInit.sol b/script/dependencies/SubProxyInit.sol index 4505484..5c9b487 100644 --- a/script/dependencies/SubProxyInit.sol +++ b/script/dependencies/SubProxyInit.sol @@ -13,15 +13,11 @@ // You should have received a copy of the GNU Affero General Public License // along with this program. If not, see . -pragma solidity ^0.8.0; +pragma solidity ^0.8.16; import {ScriptTools} from "dss-test/ScriptTools.sol"; import {DssInstance, MCD} from "dss-test/MCD.sol"; -interface SubProxyLike { - function rely(address who) external; -} - struct SubProxyInitParams { address chainlog; string name; @@ -42,3 +38,7 @@ library SubProxyInit { mcd.chainlog.setAddress(string.concat("SUBPROXY_", name).stringToBytes32(), subProxy); } } + +interface SubProxyLike { + function rely(address who) external; +} diff --git a/script/dependencies/VestInit.sol b/script/dependencies/VestInit.sol index c09dd5e..0a5b0ae 100644 --- a/script/dependencies/VestInit.sol +++ b/script/dependencies/VestInit.sol @@ -13,24 +13,7 @@ // You should have received a copy of the GNU Affero General Public License // along with this program. If not, see . -pragma solidity ^0.8.0; - -import {ScriptTools} from "dss-test/ScriptTools.sol"; - -interface DssVestLike { - function file(bytes32 _what, uint256 _data) external; - - function create( - address _usr, - uint256 _tot, - uint256 _bgn, - uint256 _tau, - uint256 _eta, - address _mgr - ) external returns (uint256 id); - - function restrict(uint256 _id) external; -} +pragma solidity ^0.8.16; struct VestInitParams { uint256 cap; @@ -45,8 +28,6 @@ struct VestCreateParams { } library VestInit { - using ScriptTools for string; - function init(address vest, VestInitParams memory p) internal { DssVestLike(vest).file("cap", p.cap); } @@ -64,3 +45,18 @@ library VestInit { DssVestLike(vest).restrict(vestId); } } + +interface DssVestLike { + function file(bytes32 _what, uint256 _data) external; + + function create( + address _usr, + uint256 _tot, + uint256 _bgn, + uint256 _tau, + uint256 _eta, + address _mgr + ) external returns (uint256 id); + + function restrict(uint256 _id) external; +} diff --git a/script/dependencies/VestedRewardsDistributionDeploy.sol b/script/dependencies/VestedRewardsDistributionDeploy.sol index 8a69ba4..2d33c1e 100644 --- a/script/dependencies/VestedRewardsDistributionDeploy.sol +++ b/script/dependencies/VestedRewardsDistributionDeploy.sol @@ -13,7 +13,7 @@ // You should have received a copy of the GNU Affero General Public License // along with this program. If not, see . -pragma solidity ^0.8.0; +pragma solidity ^0.8.16; import {ScriptTools} from "dss-test/ScriptTools.sol"; import {VestedRewardsDistribution} from "../../src/VestedRewardsDistribution.sol"; @@ -22,12 +22,12 @@ struct VestedRewardsDistributionDeployParams { address deployer; address owner; address vest; - address farm; + address rewards; } library VestedRewardsDistributionDeploy { function deploy(VestedRewardsDistributionDeployParams memory p) internal returns (address dist) { - dist = address(new VestedRewardsDistribution(p.vest, p.farm)); + dist = address(new VestedRewardsDistribution(p.vest, p.rewards)); ScriptTools.switchOwner(dist, p.deployer, p.owner); } diff --git a/script/dependencies/VestedRewardsDistributionInit.sol b/script/dependencies/VestedRewardsDistributionInit.sol index 8f45797..2026b97 100644 --- a/script/dependencies/VestedRewardsDistributionInit.sol +++ b/script/dependencies/VestedRewardsDistributionInit.sol @@ -13,11 +13,7 @@ // You should have received a copy of the GNU Affero General Public License // along with this program. If not, see . -pragma solidity ^0.8.0; - -interface VestedRewardsDistributionLike { - function file(bytes32 what, uint256 data) external; -} +pragma solidity ^0.8.16; struct VestedRewardsDistributionInitParams { uint256 vestId; @@ -28,3 +24,7 @@ library VestedRewardsDistributionInit { VestedRewardsDistributionLike(dist).file("vestId", p.vestId); } } + +interface VestedRewardsDistributionLike { + function file(bytes32 what, uint256 data) external; +} diff --git a/script/dependencies/phase-0-alpha/FarmingInit.sol b/script/dependencies/phase-0-alpha/FarmingInit.sol new file mode 100644 index 0000000..dc89d8f --- /dev/null +++ b/script/dependencies/phase-0-alpha/FarmingInit.sol @@ -0,0 +1,97 @@ +// 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 . + +pragma solidity ^0.8.16; + +import {StakingRewardsInit, StakingRewardsInitParams} from "../StakingRewardsInit.sol"; +import {VestedRewardsDistributionInit, VestedRewardsDistributionInitParams} from "../VestedRewardsDistributionInit.sol"; + +struct FarmingInitParams { + address nst; + address ngt; + address rewards; + address dist; + address vest; + uint256 vestId; +} + +library FarmingInit { + function init(FarmingInitParams memory p) internal { + require(DssVestWithGemLike(p.vest).gem() == p.ngt, "FarmingInit/vest-gem-mismatch"); + require(DssVestWithGemLike(p.vest).valid(p.vestId), "FarmingInit/vest-invalid-id"); + require(DssVestWithGemLike(p.vest).usr(p.vestId) == p.dist, "FarmingInit/vest-invalid-usr"); + require(DssVestWithGemLike(p.vest).res(p.vestId) == 1, "FarmingInit/vest-not-restricted"); + require(DssVestWithGemLike(p.vest).mgr(p.vestId) == address(0), "FarmingInit/vest-invalid-mgr"); + require( + DssVestWithGemLike(p.vest).bgn(p.vestId) == DssVestWithGemLike(p.vest).clf(p.vestId), + "FarmingInit/vest-bgn-clf-mismatch" + ); + + require(StakingRewardsLike(p.rewards).rewardsToken() == p.ngt, "FarmingInit/rewards-rewards-token-mismatch"); + require(StakingRewardsLike(p.rewards).stakingToken() == p.nst, "FarmingInit/rewards-staking-token-mismatch"); + require(StakingRewardsLike(p.rewards).lastUpdateTime() == 0, "FarmingInit/rewards-last-update-time-invalid"); + + require(VestedRewardsDistributionLike(p.dist).gem() == p.ngt, "FarmingInit/dist-gem-mismatch"); + require(VestedRewardsDistributionLike(p.dist).dssVest() == p.vest, "FarmingInit/dist-dss-vest-mismatch"); + require( + VestedRewardsDistributionLike(p.dist).stakingRewards() == p.rewards, + "FarmingInit/dist-staking-rewards-mismatch" + ); + + require(WardsLike(p.ngt).wards(p.vest) == 1, "FarmingInit/missing-ngt-rely-vest"); + + // Set `dist` with `rewardsDistribution` role in `rewards`. + StakingRewardsInit.init(p.rewards, StakingRewardsInitParams({dist: p.dist})); + + // Set the `vestId` in `dist` + VestedRewardsDistributionInit.init(p.dist, VestedRewardsDistributionInitParams({vestId: p.vestId})); + } +} + +interface WardsLike { + function wards(address usr) external view returns (uint256); +} + +interface DssVestWithGemLike { + function gem() external view returns (address); + + function bgn(uint256 vestId) external view returns (uint256); + + function clf(uint256 vestId) external view returns (uint256); + + function mgr(uint256 vestId) external view returns (address); + + function res(uint256 vestId) external view returns (uint256); + + function usr(uint256 vestId) external view returns (address); + + function valid(uint256 vestId) external view returns (bool); +} + +interface StakingRewardsLike { + function lastUpdateTime() external view returns (uint256); + + function rewardsToken() external view returns (address); + + function stakingToken() external view returns (address); +} + +interface VestedRewardsDistributionLike { + function dssVest() external view returns (address); + + function gem() external view returns (address); + + function stakingRewards() external view returns (address); +} diff --git a/script/dependencies/phase-0/FarmingInit.sol b/script/dependencies/phase-0/FarmingInit.sol new file mode 100644 index 0000000..ccd9e3c --- /dev/null +++ b/script/dependencies/phase-0/FarmingInit.sol @@ -0,0 +1,94 @@ +// 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 . + +pragma solidity ^0.8.16; + +import {StakingRewardsInit, StakingRewardsInitParams} from "../StakingRewardsInit.sol"; +import {VestedRewardsDistributionInit, VestedRewardsDistributionInitParams} from "../VestedRewardsDistributionInit.sol"; +import {VestInit, VestCreateParams} from "../VestInit.sol"; + +struct FarmingInitParams { + address nst; + address ngt; + address rewards; + address dist; + address vest; + uint256 vestTot; + uint256 vestBgn; + uint256 vestTau; +} + +struct FarmingInitResult { + uint256 vestId; +} + +library FarmingInit { + function init(FarmingInitParams memory p) internal returns (FarmingInitResult memory r) { + require(DssVestWithGemLike(p.vest).gem() == p.ngt, "FarmingInit/vest-gem-mismatch"); + + require(StakingRewardsLike(p.rewards).rewardsToken() == p.ngt, "FarmingInit/rewards-rewards-token-mismatch"); + require(StakingRewardsLike(p.rewards).stakingToken() == p.nst, "FarmingInit/rewards-staking-token-mismatch"); + require(StakingRewardsLike(p.rewards).lastUpdateTime() == 0, "FarmingInit/rewards-last-update-time-invalid"); + + require(VestedRewardsDistributionLike(p.dist).gem() == p.ngt, "FarmingInit/dist-gem-mismatch"); + require(VestedRewardsDistributionLike(p.dist).dssVest() == p.vest, "FarmingInit/dist-dss-vest-mismatch"); + require( + VestedRewardsDistributionLike(p.dist).stakingRewards() == p.rewards, + "FarmingInit/dist-staking-rewards-mismatch" + ); + + // `vest` is expected to be an instance of `DssVestMintable`. + // Check if minting rights on `ngt` were granted to `vest`. + require(WardsLike(p.ngt).wards(p.vest) == 1, "FarmingInit/missing-ngt-rely-vest"); + + // Set `dist` with `rewardsDistribution` role in `rewards`. + StakingRewardsInit.init(p.rewards, StakingRewardsInitParams({dist: p.dist})); + + // Create the proper vesting stream for rewards distribution. + uint256 vestId = VestInit.create( + p.vest, + VestCreateParams({usr: p.dist, tot: p.vestTot, bgn: p.vestBgn, tau: p.vestTau, eta: 0}) + ); + + // Set the `vestId` in `dist` + VestedRewardsDistributionInit.init(p.dist, VestedRewardsDistributionInitParams({vestId: vestId})); + + r.vestId = vestId; + } +} + +interface WardsLike { + function wards(address who) external view returns (uint256); +} + +interface DssVestWithGemLike { + function gem() external view returns (address); +} + +interface StakingRewardsLike { + function lastUpdateTime() external view returns (uint256); + + function rewardsToken() external view returns (address); + + function stakingToken() external view returns (address); +} + +interface VestedRewardsDistributionLike { + function dssVest() external view returns (address); + + function gem() external view returns (address); + + function stakingRewards() external view returns (address); +} diff --git a/script/helpers/Reader.sol b/script/helpers/Reader.sol index 6ccd0ae..1d90ecc 100644 --- a/script/helpers/Reader.sol +++ b/script/helpers/Reader.sol @@ -13,7 +13,7 @@ // // You should have received a copy of the GNU Affero General Public License // along with this program. If not, see . -pragma solidity ^0.8.0; +pragma solidity ^0.8.16; import {Vm} from "forge-std/Vm.sol"; import {stdJson} from "forge-std/StdJson.sol"; @@ -79,7 +79,7 @@ contract Reader { } } - function envOrReadAddress(string memory key, string memory envKey) external returns (address) { + function envOrReadAddress(string memory envKey, string memory key) external returns (address) { try vm.envAddress(envKey) returns (address addr) { return addr; } catch { @@ -87,7 +87,7 @@ contract Reader { } } - function envOrReadUint(string memory key, string memory envKey) external returns (uint256) { + function envOrReadUint(string memory envKey, string memory key) external returns (uint256) { try vm.envUint(envKey) returns (uint256 n) { return n; } catch { diff --git a/script/input/1/phase-0/README.md b/script/input/1/phase-0/README.md new file mode 100644 index 0000000..fe6baf3 --- /dev/null +++ b/script/input/1/phase-0/README.md @@ -0,0 +1 @@ +Script inputs for Mainnet for `phase-0`. diff --git a/script/input/1/phase-0/template-farming-deploy.json b/script/input/1/phase-0/template-farming-deploy.json new file mode 100644 index 0000000..a4d49b0 --- /dev/null +++ b/script/input/1/phase-0/template-farming-deploy.json @@ -0,0 +1,6 @@ +{ + "changelog": "address", + "ngt": "address", + "nst": "address", + "vest": "address" +} diff --git a/script/input/5/phase-0-alpha/README.md b/script/input/5/phase-0-alpha/README.md new file mode 100644 index 0000000..e18baf5 --- /dev/null +++ b/script/input/5/phase-0-alpha/README.md @@ -0,0 +1 @@ +Script inputs for Goerli for `phase-0-alpha`. diff --git a/script/input/5/template-check-staking-rewards.json b/script/input/5/phase-0-alpha/template-farming-check.json similarity index 93% rename from script/input/5/template-check-staking-rewards.json rename to script/input/5/phase-0-alpha/template-farming-check.json index 1ccda8e..bc76afd 100644 --- a/script/input/5/template-check-staking-rewards.json +++ b/script/input/5/phase-0-alpha/template-farming-check.json @@ -3,7 +3,7 @@ "ngt": "address", "nst": "address", "dist": "address", - "farm": "address", + "rewards": "address", "vest": "address", "vestId": "uint256", "vestTot": "uint256: total amount of the vesting stream", diff --git a/script/input/5/template-staking-rewards-deploy.json b/script/input/5/phase-0-alpha/template-farming-deploy.json similarity index 85% rename from script/input/5/template-staking-rewards-deploy.json rename to script/input/5/phase-0-alpha/template-farming-deploy.json index df73510..47cc195 100644 --- a/script/input/5/template-staking-rewards-deploy.json +++ b/script/input/5/phase-0-alpha/template-farming-deploy.json @@ -3,6 +3,6 @@ "ngt": "address", "nst": "address", "dist": "address", - "farm": "address", + "rewards": "address", "vest": "address" } diff --git a/script/input/5/phase-0-alpha/template-farming-init.json b/script/input/5/phase-0-alpha/template-farming-init.json new file mode 100644 index 0000000..8fa9acf --- /dev/null +++ b/script/input/5/phase-0-alpha/template-farming-init.json @@ -0,0 +1,3 @@ +{ + "vestId": "uint256: the ID of the vesting stream" +} diff --git a/script/input/5/phase-0/README.md b/script/input/5/phase-0/README.md new file mode 100644 index 0000000..2f1dfa5 --- /dev/null +++ b/script/input/5/phase-0/README.md @@ -0,0 +1 @@ +Script inputs for Goerli for `phase-0`. diff --git a/script/input/5/phase-0/template-farming-deploy.json b/script/input/5/phase-0/template-farming-deploy.json new file mode 100644 index 0000000..292e86f --- /dev/null +++ b/script/input/5/phase-0/template-farming-deploy.json @@ -0,0 +1,8 @@ +{ + "changelog": "address", + "ngt": "address", + "nst": "address", + "dist": "address", + "rewards": "address", + "vest": "address" +} diff --git a/script/input/5/template-staking-rewards-init.json b/script/input/5/template-staking-rewards-init.json deleted file mode 100644 index daaf29b..0000000 --- a/script/input/5/template-staking-rewards-init.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "vestCap": "uint256: global max issuance rate per token", - "vestTot": "uint256: total amount of the vesting stream", - "vestBgn": "uint256: timestamp of the beginning of the vesting stream", - "vestTau": "uint256: duration of the vesting stream in seconds" -} diff --git a/script/output/1/phase-0/README.md b/script/output/1/phase-0/README.md new file mode 100644 index 0000000..c5f9fc3 --- /dev/null +++ b/script/output/1/phase-0/README.md @@ -0,0 +1 @@ +Script outputs for Mainnet for `phase-0`. diff --git a/script/output/5/phase-0-alpha/README.md b/script/output/5/phase-0-alpha/README.md new file mode 100644 index 0000000..bc96443 --- /dev/null +++ b/script/output/5/phase-0-alpha/README.md @@ -0,0 +1 @@ +Script outputs for Goerli for `phase-0-alpha`. diff --git a/script/output/5/phase-0/README.md b/script/output/5/phase-0/README.md new file mode 100644 index 0000000..0253251 --- /dev/null +++ b/script/output/5/phase-0/README.md @@ -0,0 +1 @@ +Script outputs for Goerli for `phase-0`. diff --git a/script/01-StakingRewardsDeploy.s.sol b/script/phase-0-alpha/01-FarmingDeploy.s.sol similarity index 60% rename from script/01-StakingRewardsDeploy.s.sol rename to script/phase-0-alpha/01-FarmingDeploy.s.sol index bfdbb77..05b57fa 100644 --- a/script/01-StakingRewardsDeploy.s.sol +++ b/script/phase-0-alpha/01-FarmingDeploy.s.sol @@ -13,51 +13,47 @@ // // You should have received a copy of the GNU Affero General Public License // along with this program. If not, see . -pragma solidity ^0.8.0; +pragma solidity ^0.8.16; import {Script} from "forge-std/Script.sol"; -import {stdJson} from "forge-std/StdJson.sol"; import {ScriptTools} from "dss-test/ScriptTools.sol"; -import {Reader} from "./helpers/Reader.sol"; -import {StakingRewardsDeploy, StakingRewardsDeployParams} from "./dependencies/StakingRewardsDeploy.sol"; +import {Reader} from "../helpers/Reader.sol"; +import {StakingRewardsDeploy, StakingRewardsDeployParams} from "../dependencies/StakingRewardsDeploy.sol"; import { VestedRewardsDistributionDeploy, VestedRewardsDistributionDeployParams -} from "./dependencies/VestedRewardsDistributionDeploy.sol"; +} from "../dependencies/VestedRewardsDistributionDeploy.sol"; -contract Phase0StakingRewardsDeployScript is Script { - using stdJson for string; - using ScriptTools for string; - - string internal constant NAME = "Phase0StakingRewardsDeploy"; +contract Phase0Alpha_FarmingDeployScript is Script { + string internal constant NAME = "phase-0-alpha/farming-deploy"; function run() external { Reader reader = new Reader(ScriptTools.loadConfig()); - address admin = reader.envOrReadAddress(".admin", "FOUNDRY_ADMIN"); - address ngt = reader.envOrReadAddress(".ngt", "FOUNDRY_NGT"); - address nst = reader.envOrReadAddress(".nst", "FOUNDRY_NST"); + address admin = reader.envOrReadAddress("FOUNDRY_ADMIN", ".admin"); + address ngt = reader.envOrReadAddress("FOUNDRY_NGT", ".ngt"); + address nst = reader.envOrReadAddress("FOUNDRY_NST", ".nst"); + address vest = reader.envOrReadAddress("FOUNDRY_VEST", ".vest"); address dist = reader.readAddressOptional(".dist"); - address farm = reader.readAddressOptional(".farm"); - address vest = reader.readAddressOptional(".vest"); + address rewards = reader.readAddressOptional(".rewards"); vm.startBroadcast(); - if (vest == address(0)) { - vest = deployCode("DssVest.sol:DssVestMintable", abi.encode(ngt)); - ScriptTools.switchOwner(vest, msg.sender, admin); - } - - if (farm == address(0)) { - farm = StakingRewardsDeploy.deploy( + if (rewards == address(0)) { + rewards = StakingRewardsDeploy.deploy( StakingRewardsDeployParams({owner: admin, stakingToken: nst, rewardsToken: ngt}) ); } if (dist == address(0)) { dist = VestedRewardsDistributionDeploy.deploy( - VestedRewardsDistributionDeployParams({deployer: msg.sender, owner: admin, vest: vest, farm: farm}) + VestedRewardsDistributionDeployParams({ + deployer: msg.sender, + owner: admin, + vest: vest, + rewards: rewards + }) ); } @@ -67,7 +63,7 @@ contract Phase0StakingRewardsDeployScript is Script { ScriptTools.exportContract(NAME, "ngt", ngt); ScriptTools.exportContract(NAME, "nst", nst); ScriptTools.exportContract(NAME, "dist", dist); - ScriptTools.exportContract(NAME, "farm", farm); + ScriptTools.exportContract(NAME, "rewards", rewards); ScriptTools.exportContract(NAME, "vest", vest); } } diff --git a/script/phase-0-alpha/02-FarmingInit.s.sol b/script/phase-0-alpha/02-FarmingInit.s.sol new file mode 100644 index 0000000..6e4572d --- /dev/null +++ b/script/phase-0-alpha/02-FarmingInit.s.sol @@ -0,0 +1,54 @@ +// SPDX-FileCopyrightText: © 2023 Dai Foundation +// 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 . +pragma solidity ^0.8.16; + +import {Script} from "forge-std/Script.sol"; +import {ScriptTools} from "dss-test/ScriptTools.sol"; + +import {Reader} from "../helpers/Reader.sol"; +import {FarmingInit, FarmingInitParams} from "../dependencies/phase-0-alpha/FarmingInit.sol"; + +contract Phase0Alpha_FarmingInitScript is Script { + string internal constant NAME = "phase-0-alpha/farming-init"; + + function run() external { + Reader deps = new Reader(ScriptTools.loadDependencies()); + + address ngt = deps.envOrReadAddress("FOUNDRY_NGT", ".ngt"); + address nst = deps.envOrReadAddress("FOUNDRY_NST", ".nst"); + address dist = deps.envOrReadAddress("FOUNDRY_DIST", ".dist"); + address rewards = deps.envOrReadAddress("FOUNDRY_FARM", ".rewards"); + address vest = deps.envOrReadAddress("FOUNDRY_VEST", ".vest"); + + Reader config = new Reader(ScriptTools.loadConfig()); + + uint256 vestId = config.readUint(".vestId"); + + vm.startBroadcast(); + + FarmingInit.init( + FarmingInitParams({ngt: ngt, nst: nst, dist: dist, rewards: rewards, vest: vest, vestId: vestId}) + ); + + vm.stopBroadcast(); + + ScriptTools.exportContract(NAME, "ngt", ngt); + ScriptTools.exportContract(NAME, "dist", dist); + ScriptTools.exportContract(NAME, "rewards", rewards); + ScriptTools.exportContract(NAME, "vest", vest); + ScriptTools.exportValue(NAME, "vestId", vestId); + } +} diff --git a/script/09-CheckStakingRewardsDeployment.s.sol b/script/phase-0-alpha/03-FarmingCheck.s.sol similarity index 66% rename from script/09-CheckStakingRewardsDeployment.s.sol rename to script/phase-0-alpha/03-FarmingCheck.s.sol index 7f567f6..4b148f1 100644 --- a/script/09-CheckStakingRewardsDeployment.s.sol +++ b/script/phase-0-alpha/03-FarmingCheck.s.sol @@ -13,13 +13,51 @@ // // You should have received a copy of the GNU Affero General Public License // along with this program. If not, see . -pragma solidity ^0.8.0; +pragma solidity ^0.8.16; import {Script} from "forge-std/Script.sol"; -import {stdJson} from "forge-std/StdJson.sol"; -import {ScriptTools} from "dss-test/ScriptTools.sol"; +import {Reader} from "../helpers/Reader.sol"; -import {Reader} from "./helpers/Reader.sol"; +contract Phase0Alpha_FarmingCheckScript is Script { + function run() external returns (bool) { + Reader deps = new Reader(""); + deps.loadDependenciesOrConfig(); + + address admin = deps.envOrReadAddress("FOUNDRY_ADMIN", ".admin"); + address ngt = deps.envOrReadAddress("FOUNDRY_NGT", ".ngt"); + address nst = deps.envOrReadAddress("FOUNDRY_NST", ".nst"); + address dist = deps.readAddress(".dist"); + address rewards = deps.readAddress(".rewards"); + address vest = deps.readAddress(".vest"); + uint256 vestId = deps.readUint(".vestId"); + + require(VestedRewardsDistributionLike(dist).dssVest() == vest, "VestedRewardsDistribution/invalid-vest"); + require(VestedRewardsDistributionLike(dist).vestId() == vestId, "VestedRewardsDistribution/invalid-vest-id"); + require(VestedRewardsDistributionLike(dist).gem() == ngt, "VestedRewardsDistribution/invalid-gem"); + require( + VestedRewardsDistributionLike(dist).stakingRewards() == rewards, + "VestedRewardsDistribution/invalid-staking-rewards" + ); + + require(StakingRewardsLike(rewards).owner() == admin, "StakingRewards/admin-not-owner"); + require(StakingRewardsLike(rewards).rewardsToken() == ngt, "StakingRewards/invalid-rewards-token"); + require(StakingRewardsLike(rewards).stakingToken() == nst, "StakingRewards/invalid-rewards-token"); + require( + StakingRewardsLike(rewards).rewardsDistribution() == dist, + "StakingRewards/invalid-rewards-distribution" + ); + + require(WardsLike(ngt).wards(vest) == 1, "Ngt/dss-vest-not-ward"); + + require(DssVestWithGemLike(vest).gem() == ngt, "DssVest/invalid-gem"); + require(DssVestWithGemLike(vest).valid(vestId), "DssVest/invalid-vest-id"); + require(DssVestWithGemLike(vest).res(vestId) == 1, "DssVest/invalid-vest-res"); + require(DssVestWithGemLike(vest).usr(vestId) == dist, "DssVest/wrong-dist"); + require(DssVestWithGemLike(vest).mgr(vestId) == address(0), "DssVest/mgr-should-not-be-set"); + + return true; + } +} interface WardsLike { function wards(address who) external view returns (uint256); @@ -64,53 +102,3 @@ interface DssVestWithGemLike { function valid(uint256 _id) external view returns (bool); } - -contract Phase0CheckStakingRewardsDeploymentScript is Script { - using stdJson for string; - using ScriptTools for string; - - function run() external returns (bool) { - Reader deps = new Reader(""); - deps.loadDependenciesOrConfig(); - - address admin = deps.envOrReadAddress(".admin", "FOUNDRY_ADMIN"); - address ngt = deps.envOrReadAddress(".ngt", "FOUNDRY_NGT"); - address nst = deps.envOrReadAddress(".nst", "FOUNDRY_NST"); - address dist = deps.readAddress(".dist"); - address farm = deps.readAddress(".farm"); - address vest = deps.readAddress(".vest"); - uint256 vestId = deps.readUint(".vestId"); - uint256 vestTot = deps.readUint(".vestTot"); - uint256 vestBgn = deps.readUint(".vestBgn"); - uint256 vestTau = deps.readUint(".vestTau"); - - require(WardsLike(dist).wards(admin) == 1, "VestedRewardsDistribution/pause-proxy-not-relied"); - require(VestedRewardsDistributionLike(dist).dssVest() == vest, "VestedRewardsDistribution/invalid-vest"); - require(VestedRewardsDistributionLike(dist).vestId() == vestId, "VestedRewardsDistribution/invalid-vest-id"); - require(VestedRewardsDistributionLike(dist).gem() == ngt, "VestedRewardsDistribution/invalid-gem"); - require( - VestedRewardsDistributionLike(dist).stakingRewards() == farm, - "VestedRewardsDistribution/invalid-staking-rewards" - ); - - require(StakingRewardsLike(farm).owner() == admin, "StakingRewards/pause-proxy-not-owner"); - require(StakingRewardsLike(farm).rewardsToken() == ngt, "StakingRewards/invalid-rewards-token"); - require(StakingRewardsLike(farm).stakingToken() == nst, "StakingRewards/invalid-rewards-token"); - require(StakingRewardsLike(farm).rewardsDistribution() == dist, "StakingRewards/invalid-rewards-distribution"); - - require(WardsLike(ngt).wards(vest) == 1, "Ngt/dss-vest-not-ward"); - - require(WardsLike(vest).wards(admin) == 1, "DssVest/pause-proxy-not-relied"); - require(DssVestWithGemLike(vest).gem() == ngt, "DssVest/invalid-gem"); - require(DssVestWithGemLike(vest).valid(vestId), "DssVest/invalid-vest-id"); - require(DssVestWithGemLike(vest).res(vestId) == 1, "DssVest/invalid-vest-res"); - require(DssVestWithGemLike(vest).usr(vestId) == dist, "DssVest/wrong-dist"); - require(DssVestWithGemLike(vest).tot(vestId) == vestTot, "DssVest/invalid-tot"); - require(DssVestWithGemLike(vest).bgn(vestId) == vestBgn, "DssVest/invalid-bgn"); - require(DssVestWithGemLike(vest).clf(vestId) == vestBgn, "DssVest/eta-not-zero"); - require(DssVestWithGemLike(vest).fin(vestId) == vestBgn + vestTau, "DssVest/invalid-tau"); - require(DssVestWithGemLike(vest).mgr(vestId) == address(0), "DssVest/mgr-should-not-be-set"); - - return true; - } -} diff --git a/script/phase-0/01-FarmingDeploy.s.sol b/script/phase-0/01-FarmingDeploy.s.sol new file mode 100644 index 0000000..fe5e328 --- /dev/null +++ b/script/phase-0/01-FarmingDeploy.s.sol @@ -0,0 +1,75 @@ +// SPDX-FileCopyrightText: © 2023 Dai Foundation +// 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 . +pragma solidity ^0.8.16; + +import {Script} from "forge-std/Script.sol"; +import {ScriptTools} from "dss-test/ScriptTools.sol"; + +import {Reader} from "../helpers/Reader.sol"; +import {StakingRewardsDeploy, StakingRewardsDeployParams} from "../dependencies/StakingRewardsDeploy.sol"; +import { + VestedRewardsDistributionDeploy, + VestedRewardsDistributionDeployParams +} from "../dependencies/VestedRewardsDistributionDeploy.sol"; + +contract Phase0_FarmingDeployScript is Script { + string internal constant NAME = "phase-0/farming-deploy"; + + function run() external { + Reader reader = new Reader(ScriptTools.loadConfig()); + ChangelogLike changelog = ChangelogLike(reader.envOrReadAddress("FOUNDRY_CHANGELOG", ".changelog")); + + address admin = changelog.getAddress("MCD_PAUSE_PROXY"); + + address ngt = reader.envOrReadAddress("FOUNDRY_NGT", ".ngt"); + address nst = reader.envOrReadAddress("FOUNDRY_NST", ".nst"); + address vest = reader.envOrReadAddress("FOUNDRY_VEST", ".vest"); + address dist = reader.readAddressOptional(".dist"); + address rewards = reader.readAddressOptional(".rewards"); + + vm.startBroadcast(); + + if (rewards == address(0)) { + rewards = StakingRewardsDeploy.deploy( + StakingRewardsDeployParams({owner: admin, stakingToken: nst, rewardsToken: ngt}) + ); + } + + if (dist == address(0)) { + dist = VestedRewardsDistributionDeploy.deploy( + VestedRewardsDistributionDeployParams({ + deployer: msg.sender, + owner: admin, + vest: vest, + rewards: rewards + }) + ); + } + + vm.stopBroadcast(); + + ScriptTools.exportContract(NAME, "admin", admin); + ScriptTools.exportContract(NAME, "ngt", ngt); + ScriptTools.exportContract(NAME, "nst", nst); + ScriptTools.exportContract(NAME, "dist", dist); + ScriptTools.exportContract(NAME, "rewards", rewards); + ScriptTools.exportContract(NAME, "vest", vest); + } +} + +interface ChangelogLike { + function getAddress(bytes32 _key) external view returns (address addr); +} diff --git a/src/SDAO.sol b/src/SDAO.sol index c3e1d1f..c52c52f 100644 --- a/src/SDAO.sol +++ b/src/SDAO.sol @@ -14,7 +14,7 @@ // // You should have received a copy of the GNU Affero General Public License // along with this program. If not, see . -pragma solidity 0.8.19; +pragma solidity ^0.8.16; /// @dev Smart Contract signature validation interface. interface IERC1271 { diff --git a/src/SDAO.t.sol b/src/SDAO.t.sol index 43f2c65..85ce46a 100644 --- a/src/SDAO.t.sol +++ b/src/SDAO.t.sol @@ -13,7 +13,7 @@ // // You should have received a copy of the GNU Affero General Public License // along with this program. If not, see . -pragma solidity 0.8.19; +pragma solidity ^0.8.16; import {TokenFuzzTests} from "token-tests/TokenFuzzTests.sol"; import {DssTest} from "dss-test/DssTest.sol"; diff --git a/src/SubProxy.sol b/src/SubProxy.sol index 328afd6..6ba0c0f 100644 --- a/src/SubProxy.sol +++ b/src/SubProxy.sol @@ -13,7 +13,7 @@ // // You should have received a copy of the GNU Affero General Public License // along with this program. If not, see . -pragma solidity 0.8.19; +pragma solidity ^0.8.16; /** * @title SubProxy: the SubDAO-level `PauseProxy`. diff --git a/src/SubProxy.t.sol b/src/SubProxy.t.sol index 5ec2ffe..03441ca 100644 --- a/src/SubProxy.t.sol +++ b/src/SubProxy.t.sol @@ -13,7 +13,7 @@ // // You should have received a copy of the GNU Affero General Public License // along with this program. If not, see . -pragma solidity 0.8.19; +pragma solidity ^0.8.16; import {DssTest} from "dss-test/DssTest.sol"; import {SubProxy} from "./SubProxy.sol"; diff --git a/src/VestedRewardsDistribution.sol b/src/VestedRewardsDistribution.sol index 6305dc1..6460d74 100644 --- a/src/VestedRewardsDistribution.sol +++ b/src/VestedRewardsDistribution.sol @@ -13,7 +13,7 @@ // // You should have received a copy of the GNU Affero General Public License // along with this program. If not, see . -pragma solidity 0.8.19; +pragma solidity ^0.8.16; import {IStakingRewards} from "./synthetix/interfaces/IStakingRewards.sol"; import {DssVestWithGemLike} from "./interfaces/DssVestWithGemLike.sol"; diff --git a/src/VestedRewardsDistribution.t.sol b/src/VestedRewardsDistribution.t.sol index 4ba65e9..7ea4421 100644 --- a/src/VestedRewardsDistribution.t.sol +++ b/src/VestedRewardsDistribution.t.sol @@ -13,7 +13,7 @@ // // You should have received a copy of the GNU Affero General Public License // along with this program. If not, see . -pragma solidity 0.8.19; +pragma solidity ^0.8.16; import {DssTest, stdStorage, StdStorage} from "dss-test/DssTest.sol"; import {IERC20} from "openzeppelin-contracts/token/ERC20/IERC20.sol"; @@ -37,7 +37,7 @@ contract VestedRewardsDistributionTest is DssTest { struct DistributionParams { VestedRewardsDistribution dist; DssVestWithGemLike vest; - IStakingRewards farm; + IStakingRewards rewards; IERC20Mintable rewardsToken; uint256 vestId; VestParams vestParams; @@ -61,7 +61,7 @@ contract VestedRewardsDistributionTest is DssTest { DistributionParams({ dist: VestedRewardsDistribution(address(0)), vest: DssVestWithGemLike(address(0)), - farm: IStakingRewards(address(0)), + rewards: IStakingRewards(address(0)), rewardsToken: IERC20Mintable(address(new SDAO("K Token", "K"))), vestId: 0, vestParams: _makeVestParams() @@ -73,13 +73,17 @@ contract VestedRewardsDistributionTest is DssTest { // 1st distribution skip(k.vestParams.tau / 3); - assertEq(k.rewardsToken.balanceOf(address(k.farm)), 0, "Bad initial balance"); + assertEq(k.rewardsToken.balanceOf(address(k.rewards)), 0, "Bad initial balance"); vm.expectEmit(false, false, false, true, address(k.dist)); emit Distribute(k.vestParams.tot / 3); k.dist.distribute(); - assertEq(k.rewardsToken.balanceOf(address(k.farm)), k.vestParams.tot / 3, "Bad balance after 1st distribution"); + assertEq( + k.rewardsToken.balanceOf(address(k.rewards)), + k.vestParams.tot / 3, + "Bad balance after 1st distribution" + ); // 2nd distribution skip(k.vestParams.tau / 3); @@ -92,7 +96,7 @@ contract VestedRewardsDistributionTest is DssTest { uint256 tolerance = 0.0001e18; assertApproxEqRel( - k.rewardsToken.balanceOf(address(k.farm)), + k.rewardsToken.balanceOf(address(k.rewards)), (2 * k.vestParams.tot) / 3, tolerance, "Bad balance after 2nd distribution" @@ -106,7 +110,7 @@ contract VestedRewardsDistributionTest is DssTest { k.dist.distribute(); assertApproxEqRel( - k.rewardsToken.balanceOf(address(k.farm)), + k.rewardsToken.balanceOf(address(k.rewards)), k.vestParams.tot, tolerance, "Bad balance after 3rd distribution" @@ -124,7 +128,7 @@ contract VestedRewardsDistributionTest is DssTest { for (uint256 i = 0; i < totalDistributions; i++) { skip(timeSkip); k.dist.distribute(); - balances[i] = k.rewardsToken.balanceOf(address(k.farm)); + balances[i] = k.rewardsToken.balanceOf(address(k.rewards)); } uint256[] memory deltas = new uint256[](totalDistributions - 1); @@ -145,7 +149,7 @@ contract VestedRewardsDistributionTest is DssTest { ); } - assertApproxEqRel(k.rewardsToken.balanceOf(address(k.farm)), k.vestParams.tot, tolerance); + assertApproxEqRel(k.rewardsToken.balanceOf(address(k.rewards)), k.vestParams.tot, tolerance); skip(365 days); // Check if the amount undistributed is less than 0.001% of the total @@ -212,13 +216,13 @@ contract VestedRewardsDistributionTest is DssTest { // 1st vest skip(k.vestParams.tau); - assertEq(k.rewardsToken.balanceOf(address(k.farm)), 0, "Bad initial balance"); + assertEq(k.rewardsToken.balanceOf(address(k.rewards)), 0, "Bad initial balance"); vm.expectEmit(false, false, false, true, address(k.dist)); emit Distribute(k.vestParams.tot); k.dist.distribute(); - assertEq(k.rewardsToken.balanceOf(address(k.farm)), k.vestParams.tot, "Bad balance after 1st distribution"); + assertEq(k.rewardsToken.balanceOf(address(k.rewards)), k.vestParams.tot, "Bad balance after 1st distribution"); // 2nd vest @@ -241,7 +245,7 @@ contract VestedRewardsDistributionTest is DssTest { k.dist.distribute(); assertEq( - k.rewardsToken.balanceOf(address(k.farm)), + k.rewardsToken.balanceOf(address(k.rewards)), k.vestParams.tot + vestParams2.tot, "Bad balance after 2nd distribution" ); @@ -276,7 +280,7 @@ contract VestedRewardsDistributionTest is DssTest { k.dist.distribute(); assertEq( - k.rewardsToken.balanceOf(address(k.farm)), + k.rewardsToken.balanceOf(address(k.rewards)), k.vestParams.tot + vestParams2.tot + vestParams3.tot, "Bad balance after 3rd distribution" ); @@ -288,12 +292,12 @@ contract VestedRewardsDistributionTest is DssTest { skip(k.vestParams.tau / 3); - assertEq(k.rewardsToken.balanceOf(address(k.farm)), 0, "Bad initial balance"); + assertEq(k.rewardsToken.balanceOf(address(k.rewards)), 0, "Bad initial balance"); uint256 amount = k.dist.distribute(); assertLt(amount, 1_000_000_000 * WAD, "Dangling tokens distributed"); - assertEq(k.rewardsToken.balanceOf(address(k.farm)), amount, "Bad final balance"); + assertEq(k.rewardsToken.balanceOf(address(k.rewards)), amount, "Bad final balance"); } function testRevertWithReasonWhenDistributeBeforeCliffRegression() public { @@ -348,15 +352,15 @@ contract VestedRewardsDistributionTest is DssTest { result.vest.file("cap", type(uint256).max); } - if (address(result.farm) == address(0)) { - result.farm = new StakingRewards(address(this), address(0), address(result.rewardsToken), address(0)); + if (address(result.rewards) == address(0)) { + result.rewards = new StakingRewards(address(this), address(0), address(result.rewardsToken), address(0)); } if (address(result.dist) == address(0)) { - result.dist = new VestedRewardsDistribution(address(result.vest), address(result.farm)); + result.dist = new VestedRewardsDistribution(address(result.vest), address(result.rewards)); } - result.farm.setRewardsDistribution(address(result.dist)); + result.rewards.setRewardsDistribution(address(result.dist)); _distParams.vestParams.usr = address(result.dist); (result.vestId, result.vestParams) = _setUpVest(result.vest, _distParams.vestParams); diff --git a/src/interfaces/DssVestWithGemLike.sol b/src/interfaces/DssVestWithGemLike.sol index c15374a..f717489 100644 --- a/src/interfaces/DssVestWithGemLike.sol +++ b/src/interfaces/DssVestWithGemLike.sol @@ -14,7 +14,7 @@ // // You should have received a copy of the GNU Affero General Public License // along with this program. If not, see . -pragma solidity >=0.8.8; +pragma solidity ^0.8.16; interface DssVestWithGemLike { function valid(uint256 _id) external view returns (bool); diff --git a/src/synthetix/StakingRewards.sol b/src/synthetix/StakingRewards.sol index b1db131..e8989cb 100644 --- a/src/synthetix/StakingRewards.sol +++ b/src/synthetix/StakingRewards.sol @@ -23,7 +23,7 @@ // // You should have received a copy of the GNU Affero General Public License // along with this program. If not, see . -pragma solidity 0.8.19; +pragma solidity ^0.8.16; import {SafeERC20, IERC20} from "openzeppelin-contracts/token/ERC20/utils/SafeERC20.sol"; import {ReentrancyGuard} from "openzeppelin-contracts/security/ReentrancyGuard.sol"; diff --git a/src/synthetix/StakingRewards.t.sol b/src/synthetix/StakingRewards.t.sol index 64e877a..83f591c 100644 --- a/src/synthetix/StakingRewards.t.sol +++ b/src/synthetix/StakingRewards.t.sol @@ -14,7 +14,7 @@ // // You should have received a copy of the GNU Affero General Public License // along with this program. If not, see . -pragma solidity 0.8.19; +pragma solidity ^0.8.16; import "forge-std/Test.sol"; import {ERC20} from "openzeppelin-contracts/token/ERC20/ERC20.sol"; @@ -25,24 +25,24 @@ contract StakingRewardsTest is Test { TestToken rewardGem; TestToken gem; - StakingRewards farm; + StakingRewards rewards; function setupReward(uint256 amt) internal { rewardGem.mint(amt); - rewardGem.transfer(address(farm), amt); - farm.notifyRewardAmount(amt); + rewardGem.transfer(address(rewards), amt); + rewards.notifyRewardAmount(amt); } function setupStakingToken(uint256 amt) internal { gem.mint(amt); - gem.approve(address(farm), amt); + gem.approve(address(rewards), amt); } function setUp() public { rewardGem = new TestToken("SubDaoT", 18); gem = new TestToken("MKR", 18); - farm = new StakingRewards(address(this), address(this), address(rewardGem), address(gem)); + rewards = new StakingRewards(address(this), address(this), address(rewardGem), address(gem)); } function testConstructor() public { @@ -54,105 +54,105 @@ contract StakingRewardsTest is Test { } function testSetRewardsDistribution() public { - farm.setRewardsDistribution(address(0)); - assertEq(farm.rewardsDistribution(), address(0)); + rewards.setRewardsDistribution(address(0)); + assertEq(rewards.rewardsDistribution(), address(0)); } function testSetRewardsDistributionEvent() public { - vm.expectEmit(false, false, false, true, address(farm)); + vm.expectEmit(false, false, false, true, address(rewards)); emit RewardsDistributionUpdated(address(0)); - farm.setRewardsDistribution(address(0)); + rewards.setRewardsDistribution(address(0)); } function testRevertOnUnauthorizedMethods() public { vm.startPrank(address(0)); vm.expectRevert("Only the contract owner may perform this action"); - farm.setRewardsDistribution(address(0)); + rewards.setRewardsDistribution(address(0)); vm.expectRevert("Only the contract owner may perform this action"); - farm.setRewardsDuration(1 days); + rewards.setRewardsDuration(1 days); vm.expectRevert("Only the contract owner may perform this action"); - farm.setPaused(true); + rewards.setPaused(true); vm.expectRevert("Only the contract owner may perform this action"); - farm.recoverERC20(address(0), 1); + rewards.recoverERC20(address(0), 1); vm.expectRevert("Caller is not RewardsDistribution contract"); - farm.notifyRewardAmount(1); + rewards.notifyRewardAmount(1); } function testRevertStakeWhenPaused() public { - farm.setPaused(true); + rewards.setPaused(true); vm.expectRevert("This action cannot be performed while the contract is paused"); - farm.stake(1); + rewards.stake(1); } function testPauseUnpause() public { - farm.setPaused(true); + rewards.setPaused(true); vm.expectRevert("This action cannot be performed while the contract is paused"); - farm.stake(1); + rewards.stake(1); - farm.setPaused(false); + rewards.setPaused(false); setupStakingToken(1); - farm.stake(1); + rewards.stake(1); } function testRevertOnRecoverStakingToken() public { vm.expectRevert("Cannot withdraw the staking token"); - farm.recoverERC20(address(gem), 1); + rewards.recoverERC20(address(gem), 1); } function testRecoverERC20() public { TestToken t = new TestToken("TT", 18); t.mint(10); - t.transfer(address(farm), 10); + t.transfer(address(rewards), 10); - assertEq(t.balanceOf(address(farm)), 10); + assertEq(t.balanceOf(address(rewards)), 10); vm.expectEmit(true, true, true, true); emit Recovered(address(t), 10); - farm.recoverERC20(address(t), 10); + rewards.recoverERC20(address(t), 10); - assertEq(t.balanceOf(address(farm)), 0); + assertEq(t.balanceOf(address(rewards)), 0); assertEq(t.balanceOf(address(this)), 10); } function testLastTimeRewardApplicable() public { - assertEq(farm.lastTimeRewardApplicable(), 0); + assertEq(rewards.lastTimeRewardApplicable(), 0); setupReward(10 * WAD); - assertEq(farm.lastTimeRewardApplicable(), block.timestamp); + assertEq(rewards.lastTimeRewardApplicable(), block.timestamp); } function testRewardPerToken() public { - assertEq(farm.rewardPerToken(), 0); + assertEq(rewards.rewardPerToken(), 0); setupStakingToken(100 * WAD); - farm.stake(100 * WAD); + rewards.stake(100 * WAD); - assertEq(farm.totalSupply(), 100 * WAD); + assertEq(rewards.totalSupply(), 100 * WAD); setupReward(5000 * WAD); skip(1 days); - assert(farm.rewardPerToken() > 0); + assert(rewards.rewardPerToken() > 0); } function testStakeEmitEvent() public { setupStakingToken(100 * WAD); - vm.expectEmit(false, false, false, true, address(farm)); + vm.expectEmit(false, false, false, true, address(rewards)); emit Staked(address(this), 100 * WAD); - farm.stake(100 * WAD); + rewards.stake(100 * WAD); } function testStakeWithReferralEmitEvent() public { @@ -165,7 +165,7 @@ contract StakingRewardsTest is Test { vm.expectEmit(true, false, false, false); emit Referral(1, address(this), 100 * WAD); - farm.stake(100 * WAD, referralCode); + rewards.stake(100 * WAD, referralCode); } function testStaking() public { @@ -173,133 +173,133 @@ contract StakingRewardsTest is Test { uint256 gemBalance = gem.balanceOf(address(this)); - farm.stake(100 * WAD); + rewards.stake(100 * WAD); - assertEq(farm.balanceOf(address(this)), 100 * WAD); + assertEq(rewards.balanceOf(address(this)), 100 * WAD); assertEq(gem.balanceOf(address(this)), gemBalance - 100 * WAD); - assertEq(gem.balanceOf(address(farm)), 100 * WAD); + assertEq(gem.balanceOf(address(rewards)), 100 * WAD); } function testRevertOnZeroStake() public { vm.expectRevert("Cannot stake 0"); - farm.stake(0); + rewards.stake(0); } function testEarned() public { - assertEq(farm.earned(address(this)), 0); + assertEq(rewards.earned(address(this)), 0); setupStakingToken(100 * WAD); - farm.stake(100 * WAD); + rewards.stake(100 * WAD); setupReward(5000 * WAD); skip(1 days); - assert(farm.earned(address(this)) > 0); + assert(rewards.earned(address(this)) > 0); } function testRewardRateIncreaseOnNewRewardBeforeDurationEnd() public { setupReward(5000 * WAD); - uint256 rewardRate = farm.rewardRate(); + uint256 rewardRate = rewards.rewardRate(); setupReward(5000 * WAD); assert(rewardRate > 0); - assert(farm.rewardRate() > rewardRate); + assert(rewards.rewardRate() > rewardRate); } function earnedShouldIncreaseAfterDuration() public { setupStakingToken(100 * WAD); - farm.stake(100 * WAD); + rewards.stake(100 * WAD); setupStakingToken(5000 * WAD); skip(7 days); - uint256 earned = farm.earned(address(this)); + uint256 earned = rewards.earned(address(this)); setupStakingToken(5000 * WAD); skip(7 days); - assertEq(farm.earned(address(this)), earned + earned); + assertEq(rewards.earned(address(this)), earned + earned); } function testGetRewardEvent() public { setupStakingToken(100 * WAD); - farm.stake(100 * WAD); + rewards.stake(100 * WAD); setupReward(5000 * WAD); skip(1 days); vm.expectEmit(true, true, true, true); - emit RewardPaid(address(this), farm.rewardRate() * 1 days); - farm.getReward(); + emit RewardPaid(address(this), rewards.rewardRate() * 1 days); + rewards.getReward(); } function testGetReward() public { setupStakingToken(100 * WAD); - farm.stake(100 * WAD); + rewards.stake(100 * WAD); setupReward(5000 * WAD); skip(1 days); uint256 rewardBalance = rewardGem.balanceOf(address(this)); - uint256 earned = farm.earned(address(this)); + uint256 earned = rewards.earned(address(this)); - farm.getReward(); + rewards.getReward(); - assert(farm.earned(address(this)) < earned); + assert(rewards.earned(address(this)) < earned); assert(rewardGem.balanceOf(address(this)) > rewardBalance); } function testsetRewardsDurationEvent() public { vm.expectEmit(true, true, true, true); emit RewardsDurationUpdated(70 days); - farm.setRewardsDuration(70 days); + rewards.setRewardsDuration(70 days); } function testSetRewardsDurationBeforeDistribution() public { - assertEq(farm.rewardsDuration(), 7 days); + assertEq(rewards.rewardsDuration(), 7 days); - farm.setRewardsDuration(70 days); + rewards.setRewardsDuration(70 days); - assertEq(farm.rewardsDuration(), 70 days); + assertEq(rewards.rewardsDuration(), 70 days); } function testRevertSetRewardsDurationOnActiveDistribution() public { setupStakingToken(100 * WAD); - farm.stake(100 * WAD); + rewards.stake(100 * WAD); setupReward(100 * WAD); skip(1 days); vm.expectRevert("Previous rewards period must be complete before changing the duration for the new period"); - farm.setRewardsDuration(70 days); + rewards.setRewardsDuration(70 days); } function testSetRewardsDurationAfterDistributionPeriod() public { setupStakingToken(100 * WAD); - farm.stake(100 * WAD); + rewards.stake(100 * WAD); setupReward(100 * WAD); skip(8 days); - farm.setRewardsDuration(70 days); - assertEq(farm.rewardsDuration(), 70 days); + rewards.setRewardsDuration(70 days); + assertEq(rewards.rewardsDuration(), 70 days); } function testGetRewardForDuration() public { setupReward(5000 * WAD); - uint256 rewardForDuration = farm.getRewardForDuration(); - uint256 rewardDuration = farm.rewardsDuration(); - uint256 rewardRate = farm.rewardRate(); + uint256 rewardForDuration = rewards.getRewardForDuration(); + uint256 rewardDuration = rewards.rewardsDuration(); + uint256 rewardRate = rewards.rewardRate(); assert(rewardForDuration > 0); assertEq(rewardForDuration, rewardRate * rewardDuration); @@ -307,115 +307,115 @@ contract StakingRewardsTest is Test { function testWithdrawalEvent() public { setupStakingToken(100 * WAD); - farm.stake(100 * WAD); + rewards.stake(100 * WAD); vm.expectEmit(true, true, true, true); emit Withdrawn(address(this), 1 * WAD); - farm.withdraw(1 * WAD); + rewards.withdraw(1 * WAD); } function testFailtIfNothingToWithdraw() public { - farm.withdraw(1); + rewards.withdraw(1); } function testRevertOnZeroWithdraw() public { vm.expectRevert("Cannot withdraw 0"); - farm.withdraw(0); + rewards.withdraw(0); } function testWithdrwal() public { setupStakingToken(100 * WAD); - farm.stake(100 * WAD); + rewards.stake(100 * WAD); - uint256 initialStakeBalance = farm.balanceOf(address(this)); + uint256 initialStakeBalance = rewards.balanceOf(address(this)); - farm.withdraw(100 * WAD); + rewards.withdraw(100 * WAD); - assertEq(initialStakeBalance, farm.balanceOf(address(this)) + 100 * WAD); + assertEq(initialStakeBalance, rewards.balanceOf(address(this)) + 100 * WAD); assertEq(gem.balanceOf(address(this)), 100 * WAD); } function testExit() public { setupStakingToken(100 * WAD); - farm.stake(100 * WAD); + rewards.stake(100 * WAD); setupReward(500 * WAD); skip(1 days); - farm.exit(); + rewards.exit(); - assertEq(farm.earned(address(this)), 0); + assertEq(rewards.earned(address(this)), 0); assertEq(gem.balanceOf(address(this)), 100 * WAD); - assertEq(rewardGem.balanceOf(address(this)), farm.rewardRate() * 1 days); + assertEq(rewardGem.balanceOf(address(this)), rewards.rewardRate() * 1 days); } function testNotifyRewardEvent() public { uint256 amt = 1 * WAD; rewardGem.mint(amt); - rewardGem.transfer(address(farm), amt); + rewardGem.transfer(address(rewards), amt); - vm.expectEmit(false, false, false, true, address(farm)); + vm.expectEmit(false, false, false, true, address(rewards)); emit RewardAdded(amt); - farm.notifyRewardAmount(amt); + rewards.notifyRewardAmount(amt); } function testRevertOnNotBeingRewardDistributor() public { vm.prank(address(0)); vm.expectRevert("Caller is not RewardsDistribution contract"); - farm.notifyRewardAmount(1); + rewards.notifyRewardAmount(1); } function testRevertOnRewardGreaterThenBalance() public { rewardGem.mint(100 * WAD); - rewardGem.transfer(address(farm), 100 * WAD); + rewardGem.transfer(address(rewards), 100 * WAD); vm.expectRevert("Provided reward too high"); - farm.notifyRewardAmount(101 * WAD); + rewards.notifyRewardAmount(101 * WAD); } function testRevertOnRewardGreaterThenBalancePlusRollOverBalance() public { setupReward(100 * WAD); rewardGem.mint(100 * WAD); - rewardGem.transfer(address(farm), 100 * WAD); + rewardGem.transfer(address(rewards), 100 * WAD); vm.expectRevert("Provided reward too high"); - farm.notifyRewardAmount(101 * WAD); + rewards.notifyRewardAmount(101 * WAD); } function testFarm() public { uint256 staked = 100 * WAD; setupStakingToken(staked); - farm.stake(staked); + rewards.stake(staked); setupReward(5000 * WAD); // Period finish should be 7 days from now - assertEq(farm.periodFinish(), block.timestamp + 7 days); + assertEq(rewards.periodFinish(), block.timestamp + 7 days); // Reward duration is 7 days, so we'll // skip by 6 days to prevent expiration skip(6 days); // Make sure we earned in proportion to reward per token - assertEq(farm.earned(address(this)), (farm.rewardPerToken() * staked) / WAD); + assertEq(rewards.earned(address(this)), (rewards.rewardPerToken() * staked) / WAD); // Make sure we get staking token after withdrawal and we still have the same amount earned - farm.withdraw(20 * WAD); + rewards.withdraw(20 * WAD); assertEq(gem.balanceOf(address(this)), 20 * WAD); - assertEq(farm.earned(address(this)), (farm.rewardPerToken() * staked) / WAD); + assertEq(rewards.earned(address(this)), (rewards.rewardPerToken() * staked) / WAD); // Get rewards - farm.getReward(); - assertEq(rewardGem.balanceOf(address(this)), (farm.rewardPerToken() * staked) / WAD); - assertEq(farm.earned(address(this)), 0); + rewards.getReward(); + assertEq(rewardGem.balanceOf(address(this)), (rewards.rewardPerToken() * staked) / WAD); + assertEq(rewards.earned(address(this)), 0); // exit - farm.exit(); + rewards.exit(); assertEq(gem.balanceOf(address(this)), staked); } diff --git a/src/synthetix/interfaces/IStakingRewards.sol b/src/synthetix/interfaces/IStakingRewards.sol index 85cc7d3..60f10ad 100644 --- a/src/synthetix/interfaces/IStakingRewards.sol +++ b/src/synthetix/interfaces/IStakingRewards.sol @@ -14,7 +14,7 @@ // // You should have received a copy of the GNU Affero General Public License // along with this program. If not, see . -pragma solidity 0.8.19; +pragma solidity ^0.8.16; import {IERC20} from "openzeppelin-contracts/token/ERC20/utils/SafeERC20.sol"; diff --git a/src/synthetix/utils/Owned.sol b/src/synthetix/utils/Owned.sol index 365089f..455019b 100644 --- a/src/synthetix/utils/Owned.sol +++ b/src/synthetix/utils/Owned.sol @@ -22,7 +22,7 @@ // // You should have received a copy of the GNU Affero General Public License // along with this program. If not, see . -pragma solidity 0.8.19; +pragma solidity ^0.8.16; // https://developer.synthetix.io/contracts/source/contracts/Owned/ contract Owned { diff --git a/src/synthetix/utils/Pausable.sol b/src/synthetix/utils/Pausable.sol index 1018516..a9aed4f 100644 --- a/src/synthetix/utils/Pausable.sol +++ b/src/synthetix/utils/Pausable.sol @@ -22,7 +22,7 @@ // // You should have received a copy of the GNU Affero General Public License // along with this program. If not, see . -pragma solidity 0.8.19; +pragma solidity ^0.8.16; // Inheritance import {Owned} from "./Owned.sol";