From 8c9ed3b6817eed37987572f43d68a0227e2f95be Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment?= Date: Tue, 21 Jan 2025 22:33:22 +0100 Subject: [PATCH 01/13] feat: add closeCampaign function to CurvePoolBooster. --- .../interfaces/ICampaignRemoteManager.sol | 4 ++++ .../contracts/strategies/CurvePoolBooster.sol | 15 ++++++++++++++- 2 files changed, 18 insertions(+), 1 deletion(-) diff --git a/contracts/contracts/interfaces/ICampaignRemoteManager.sol b/contracts/contracts/interfaces/ICampaignRemoteManager.sol index f2db979ea8..74526c9435 100644 --- a/contracts/contracts/interfaces/ICampaignRemoteManager.sol +++ b/contracts/contracts/interfaces/ICampaignRemoteManager.sol @@ -35,3 +35,7 @@ interface ICampaignRemoteManager { uint256 maxRewardPerVote; } } + +interface VotemarketV2 { + function closeCampaign(uint256 campaignId) external; +} diff --git a/contracts/contracts/strategies/CurvePoolBooster.sol b/contracts/contracts/strategies/CurvePoolBooster.sol index 4ed6a3c129..1783450086 100644 --- a/contracts/contracts/strategies/CurvePoolBooster.sol +++ b/contracts/contracts/strategies/CurvePoolBooster.sol @@ -5,7 +5,7 @@ import { IERC20 } from "@openzeppelin/contracts/token/ERC20/IERC20.sol"; import { SafeERC20 } from "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol"; import { Initializable } from "../utils/Initializable.sol"; import { Strategizable } from "../governance/Strategizable.sol"; -import { ICampaignRemoteManager } from "../interfaces/ICampaignRemoteManager.sol"; +import { ICampaignRemoteManager, VotemarketV2 } from "../interfaces/ICampaignRemoteManager.sol"; /// @title CurvePoolBooster /// @author Origin Protocol @@ -60,6 +60,7 @@ contract CurvePoolBooster is Initializable, Strategizable { event NumberOfPeriodsUpdated(uint8 extraNumberOfPeriods); event RewardPerVoteUpdated(uint256 newMaxRewardPerVote); event TokensRescued(address token, uint256 amount, address receiver); + event BribeClosed(uint256 campaignId); //////////////////////////////////////////////////// /// --- CONSTRUCTOR && INITIALIZATION @@ -240,6 +241,18 @@ contract CurvePoolBooster is Initializable, Strategizable { emit RewardPerVoteUpdated(newMaxRewardPerVote); } + /// @notice Close the campaign. + /// @dev This function only work on the L2 chain. Not on mainnet. + /// @dev The _campaignId parameter is not related to the campaignId of this contract, allowing greater flexibility. + /// @param _campaignId Id of the campaign to close + function closeCampaign(uint256 _campaignId) + external + onlyGovernorOrStrategist + { + VotemarketV2(campaignRemoteManager).closeCampaign(_campaignId); + emit BribeClosed(_campaignId); + } + /// @notice calculate the fee amount and transfer it to the feeCollector /// @return Balance after fee function _handleFee() internal returns (uint256) { From 1ebd1f1bde4a291a1e22f6e325b1b782675e1401 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment?= Date: Wed, 22 Jan 2025 12:46:11 +0100 Subject: [PATCH 02/13] fix: remove immutable from campaignRemoteManager. --- .../interfaces/ICampaignRemoteManager.sol | 6 +- .../contracts/strategies/CurvePoolBooster.sol | 41 +++++++++++--- .../deploy/mainnet/119_pool_booster_curve.js | 10 ++-- .../curvePoolBooster.mainnet.fork-test.js | 55 ++++++++++--------- 4 files changed, 68 insertions(+), 44 deletions(-) diff --git a/contracts/contracts/interfaces/ICampaignRemoteManager.sol b/contracts/contracts/interfaces/ICampaignRemoteManager.sol index 74526c9435..a7e0d4e0db 100644 --- a/contracts/contracts/interfaces/ICampaignRemoteManager.sol +++ b/contracts/contracts/interfaces/ICampaignRemoteManager.sol @@ -14,6 +14,8 @@ interface ICampaignRemoteManager { uint256 additionalGasLimit ) external payable; + function closeCampaign(uint256 campaignId) external; + struct CampaignCreationParams { uint256 chainId; address gauge; @@ -35,7 +37,3 @@ interface ICampaignRemoteManager { uint256 maxRewardPerVote; } } - -interface VotemarketV2 { - function closeCampaign(uint256 campaignId) external; -} diff --git a/contracts/contracts/strategies/CurvePoolBooster.sol b/contracts/contracts/strategies/CurvePoolBooster.sol index 1783450086..2a1694a4a5 100644 --- a/contracts/contracts/strategies/CurvePoolBooster.sol +++ b/contracts/contracts/strategies/CurvePoolBooster.sol @@ -5,7 +5,7 @@ import { IERC20 } from "@openzeppelin/contracts/token/ERC20/IERC20.sol"; import { SafeERC20 } from "@openzeppelin/contracts/token/ERC20/utils/SafeERC20.sol"; import { Initializable } from "../utils/Initializable.sol"; import { Strategizable } from "../governance/Strategizable.sol"; -import { ICampaignRemoteManager, VotemarketV2 } from "../interfaces/ICampaignRemoteManager.sol"; +import { ICampaignRemoteManager } from "../interfaces/ICampaignRemoteManager.sol"; /// @title CurvePoolBooster /// @author Origin Protocol @@ -25,9 +25,6 @@ contract CurvePoolBooster is Initializable, Strategizable { /// @notice Address of the reward token address public immutable rewardToken; - /// @notice Address of the campaignRemoteManager linked to VotemarketV2 - address public immutable campaignRemoteManager; - /// @notice Chain id of the target chain uint256 public immutable targetChainId; @@ -40,6 +37,9 @@ contract CurvePoolBooster is Initializable, Strategizable { /// @notice Address of the fee collector address public feeCollector; + /// @notice Address of the campaignRemoteManager linked to VotemarketV2 + address public campaignRemoteManager; + /// @notice Id of the campaign created uint256 public campaignId; @@ -61,18 +61,17 @@ contract CurvePoolBooster is Initializable, Strategizable { event RewardPerVoteUpdated(uint256 newMaxRewardPerVote); event TokensRescued(address token, uint256 amount, address receiver); event BribeClosed(uint256 campaignId); + event CampaignRemoteManagerUpdated(address newCampaignRemoteManager); //////////////////////////////////////////////////// /// --- CONSTRUCTOR && INITIALIZATION //////////////////////////////////////////////////// constructor( uint256 _targetChainId, - address _campaignRemoteManager, address _rewardToken, address _gauge ) { targetChainId = _targetChainId; - campaignRemoteManager = _campaignRemoteManager; rewardToken = _rewardToken; gauge = _gauge; @@ -87,11 +86,13 @@ contract CurvePoolBooster is Initializable, Strategizable { function initialize( address _strategist, uint16 _fee, - address _feeCollector + address _feeCollector, + address _campaignRemoteManager ) external onlyGovernor initializer { _setStrategistAddr(_strategist); _setFee(_fee); _setFeeCollector(_feeCollector); + _setCampaignRemoteManager(_campaignRemoteManager); } //////////////////////////////////////////////////// @@ -249,7 +250,9 @@ contract CurvePoolBooster is Initializable, Strategizable { external onlyGovernorOrStrategist { - VotemarketV2(campaignRemoteManager).closeCampaign(_campaignId); + ICampaignRemoteManager(campaignRemoteManager).closeCampaign( + _campaignId + ); emit BribeClosed(_campaignId); } @@ -343,5 +346,27 @@ contract CurvePoolBooster is Initializable, Strategizable { emit FeeCollectorUpdated(_feeCollector); } + /// @notice Set the campaignRemoteManager + /// @param _campaignRemoteManager New campaignRemoteManager address + function setCampaignRemoteManager(address _campaignRemoteManager) + external + onlyGovernor + { + _setCampaignRemoteManager(_campaignRemoteManager); + } + + /// @notice Internal logic to set the campaignRemoteManager + /// @param _campaignRemoteManager New campaignRemoteManager address + function _setCampaignRemoteManager(address _campaignRemoteManager) + internal + { + require( + _campaignRemoteManager != address(0), + "Invalid campaignRemoteManager" + ); + campaignRemoteManager = _campaignRemoteManager; + emit CampaignRemoteManagerUpdated(_campaignRemoteManager); + } + receive() external payable {} } diff --git a/contracts/deploy/mainnet/119_pool_booster_curve.js b/contracts/deploy/mainnet/119_pool_booster_curve.js index 69650d1668..3aa1696fe6 100644 --- a/contracts/deploy/mainnet/119_pool_booster_curve.js +++ b/contracts/deploy/mainnet/119_pool_booster_curve.js @@ -27,7 +27,6 @@ module.exports = deploymentWithGovernanceProposal( "CurvePoolBooster", [ 42161, // Arbitrum chain id - addresses.mainnet.CampaignRemoteManager, // Campaign Remote Manager (VotemarketV2 entry point) addresses.mainnet.OUSDProxy, // Bribe token addresses.mainnet.CurveOUSDUSDTGauge, // Gauge ] @@ -40,11 +39,12 @@ module.exports = deploymentWithGovernanceProposal( // 3. Initialize const initData = cCurvePoolBooster.interface.encodeFunctionData( - "initialize(address,uint16,address)", + "initialize(address,uint16,address,address)", [ - addresses.base.multichainStrategist, - 0, - addresses.base.multichainStrategist, + addresses.base.multichainStrategist, // Strategist + 0, // Fee + addresses.base.multichainStrategist, // Fee collector + addresses.mainnet.CampaignRemoteManager // Campaign Remote Manager (VotemarketV2 entry point) ] ); diff --git a/contracts/test/strategies/curvePoolBooster.mainnet.fork-test.js b/contracts/test/strategies/curvePoolBooster.mainnet.fork-test.js index d56253b1d8..8ecc58fa8b 100644 --- a/contracts/test/strategies/curvePoolBooster.mainnet.fork-test.js +++ b/contracts/test/strategies/curvePoolBooster.mainnet.fork-test.js @@ -20,7 +20,7 @@ describe("ForkTest: CurvePoolBooster", function () { it("Should have correct params", async () => { const { curvePoolBooster } = fixture; - const { strategistAddr } = await getNamedAccounts(); + const { multichainStrategistAddr } = await getNamedAccounts(); expect(await curvePoolBooster.gauge()).to.equal( addresses.mainnet.CurveOUSDUSDTGauge ); @@ -31,17 +31,18 @@ describe("ForkTest: CurvePoolBooster", function () { addresses.mainnet.OUSDProxy ); expect(await curvePoolBooster.targetChainId()).to.equal(42161); - expect(await curvePoolBooster.strategistAddr()).to.equal(strategistAddr); + expect(await curvePoolBooster.strategistAddr()).to.equal(multichainStrategistAddr); expect(await curvePoolBooster.governor()).to.equal( addresses.mainnet.Timelock ); + expect(await curvePoolBooster.campaignRemoteManager()).to.equal(addresses.mainnet.CampaignRemoteManager); }); it("Should Create a campaign", async () => { const { curvePoolBooster, ousd, wousd } = fixture; - const { strategistAddr } = await getNamedAccounts(); + const { multichainStrategistAddr } = await getNamedAccounts(); const woethSigner = await impersonateAndFund(wousd.address); - const sStrategist = await ethers.provider.getSigner(strategistAddr); + const sStrategist = await ethers.provider.getSigner(multichainStrategistAddr); // Deal OETH and ETH to pool booster await ousd @@ -72,9 +73,9 @@ describe("ForkTest: CurvePoolBooster", function () { it("Should Create a campaign with fee", async () => { const { curvePoolBooster, ousd, wousd, josh } = fixture; - const { strategistAddr } = await getNamedAccounts(); + const { multichainStrategistAddr } = await getNamedAccounts(); const woethSigner = await impersonateAndFund(wousd.address); - const sStrategist = await ethers.provider.getSigner(strategistAddr); + const sStrategist = await ethers.provider.getSigner(multichainStrategistAddr); const gov = await curvePoolBooster.governor(); const sGov = await ethers.provider.getSigner(gov); @@ -112,8 +113,8 @@ describe("ForkTest: CurvePoolBooster", function () { it("Should set campaign id", async () => { const { curvePoolBooster } = fixture; - const { strategistAddr } = await getNamedAccounts(); - const sStrategist = await ethers.provider.getSigner(strategistAddr); + const { multichainStrategistAddr } = await getNamedAccounts(); + const sStrategist = await ethers.provider.getSigner(multichainStrategistAddr); expect(await curvePoolBooster.campaignId()).to.equal(0); await curvePoolBooster.connect(sStrategist).setCampaignId(12); @@ -136,9 +137,9 @@ describe("ForkTest: CurvePoolBooster", function () { it("Should manage total rewards", async () => { const { curvePoolBooster, ousd, wousd } = fixture; - const { strategistAddr } = await getNamedAccounts(); + const { multichainStrategistAddr } = await getNamedAccounts(); const woethSigner = await impersonateAndFund(wousd.address); - const sStrategist = await ethers.provider.getSigner(strategistAddr); + const sStrategist = await ethers.provider.getSigner(multichainStrategistAddr); // Deal OETH and ETH to pool booster await ousd @@ -185,9 +186,9 @@ describe("ForkTest: CurvePoolBooster", function () { it("Should manage number of periods", async () => { const { curvePoolBooster, ousd, wousd } = fixture; - const { strategistAddr } = await getNamedAccounts(); + const { multichainStrategistAddr } = await getNamedAccounts(); const woethSigner = await impersonateAndFund(wousd.address); - const sStrategist = await ethers.provider.getSigner(strategistAddr); + const sStrategist = await ethers.provider.getSigner(multichainStrategistAddr); // Deal OETH and ETH to pool booster await ousd @@ -231,9 +232,9 @@ describe("ForkTest: CurvePoolBooster", function () { it("Should manage reward per voter", async () => { const { curvePoolBooster, ousd, wousd } = fixture; - const { strategistAddr } = await getNamedAccounts(); + const { multichainStrategistAddr } = await getNamedAccounts(); const woethSigner = await impersonateAndFund(wousd.address); - const sStrategist = await ethers.provider.getSigner(strategistAddr); + const sStrategist = await ethers.provider.getSigner(multichainStrategistAddr); // Deal OETH and ETH to pool booster await ousd @@ -277,8 +278,8 @@ describe("ForkTest: CurvePoolBooster", function () { it("Should rescue ETH", async () => { const { curvePoolBooster } = fixture; - const { strategistAddr } = await getNamedAccounts(); - const sStrategist = await ethers.provider.getSigner(strategistAddr); + const { multichainStrategistAddr } = await getNamedAccounts(); + const sStrategist = await ethers.provider.getSigner(multichainStrategistAddr); // Deal ETH to pool booster await sStrategist.sendTransaction({ @@ -289,7 +290,7 @@ describe("ForkTest: CurvePoolBooster", function () { const balanceBefore = await ethers.provider.getBalance( curvePoolBooster.address ); - await curvePoolBooster.connect(sStrategist).rescueETH(strategistAddr); + await curvePoolBooster.connect(sStrategist).rescueETH(multichainStrategistAddr); const balanceAfter = await ethers.provider.getBalance( curvePoolBooster.address ); @@ -324,8 +325,8 @@ describe("ForkTest: CurvePoolBooster", function () { it("Should revert if campaign is already created", async () => { const { curvePoolBooster } = fixture; - const { strategistAddr } = await getNamedAccounts(); - const sStrategist = await ethers.provider.getSigner(strategistAddr); + const { multichainStrategistAddr } = await getNamedAccounts(); + const sStrategist = await ethers.provider.getSigner(multichainStrategistAddr); await curvePoolBooster.connect(sStrategist).setCampaignId(12); await expect( @@ -343,8 +344,8 @@ describe("ForkTest: CurvePoolBooster", function () { it("Should revert if campaign is not created", async () => { const { curvePoolBooster } = fixture; - const { strategistAddr } = await getNamedAccounts(); - const sStrategist = await ethers.provider.getSigner(strategistAddr); + const { multichainStrategistAddr } = await getNamedAccounts(); + const sStrategist = await ethers.provider.getSigner(multichainStrategistAddr); await expect( curvePoolBooster @@ -365,8 +366,8 @@ describe("ForkTest: CurvePoolBooster", function () { it("Should revert if Invalid number of periods", async () => { const { curvePoolBooster } = fixture; - const { strategistAddr } = await getNamedAccounts(); - const sStrategist = await ethers.provider.getSigner(strategistAddr); + const { multichainStrategistAddr } = await getNamedAccounts(); + const sStrategist = await ethers.provider.getSigner(multichainStrategistAddr); await curvePoolBooster.connect(sStrategist).setCampaignId(12); @@ -379,8 +380,8 @@ describe("ForkTest: CurvePoolBooster", function () { it("Should revert if Invalid reward per vote", async () => { const { curvePoolBooster } = fixture; - const { strategistAddr } = await getNamedAccounts(); - const sStrategist = await ethers.provider.getSigner(strategistAddr); + const { multichainStrategistAddr } = await getNamedAccounts(); + const sStrategist = await ethers.provider.getSigner(multichainStrategistAddr); await curvePoolBooster.connect(sStrategist).setCampaignId(12); @@ -393,8 +394,8 @@ describe("ForkTest: CurvePoolBooster", function () { it("Should revert if No reward to manage", async () => { const { curvePoolBooster } = fixture; - const { strategistAddr } = await getNamedAccounts(); - const sStrategist = await ethers.provider.getSigner(strategistAddr); + const { multichainStrategistAddr } = await getNamedAccounts(); + const sStrategist = await ethers.provider.getSigner(multichainStrategistAddr); await expect( curvePoolBooster From fc4140274a95dfcd6a535a53ae26f6aa2e5f0b82 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment?= Date: Wed, 22 Jan 2025 12:57:40 +0100 Subject: [PATCH 03/13] fix: adjust naming and prettier. --- .../contracts/strategies/CurvePoolBooster.sol | 23 ++++--- .../deploy/mainnet/119_pool_booster_curve.js | 2 +- .../curvePoolBooster.mainnet.fork-test.js | 60 ++++++++++++++----- 3 files changed, 60 insertions(+), 25 deletions(-) diff --git a/contracts/contracts/strategies/CurvePoolBooster.sol b/contracts/contracts/strategies/CurvePoolBooster.sol index 2a1694a4a5..7c3a6f983f 100644 --- a/contracts/contracts/strategies/CurvePoolBooster.sol +++ b/contracts/contracts/strategies/CurvePoolBooster.sol @@ -46,22 +46,22 @@ contract CurvePoolBooster is Initializable, Strategizable { //////////////////////////////////////////////////// /// --- EVENTS //////////////////////////////////////////////////// - event FeeUpdated(uint16 _newFee); - event FeeCollected(address _feeCollector, uint256 _feeAmount); - event FeeCollectorUpdated(address _newFeeCollector); - event CampaignIdUpdated(uint256 _newId); - event BribeCreated( + event FeeUpdated(uint16 newFee); + event FeeCollected(address feeCollector, uint256 feeAmount); + event FeeCollectorUpdated(address newFeeCollector); + event CampaignRemoteManagerUpdated(address newCampaignRemoteManager); + event CampaignCreated( address gauge, address rewardToken, uint256 maxRewardPerVote, uint256 totalRewardAmount ); + event CampaignIdUpdated(uint256 newId); + event CampaignClosed(uint256 campaignId); event TotalRewardAmountUpdated(uint256 extraTotalRewardAmount); event NumberOfPeriodsUpdated(uint8 extraNumberOfPeriods); event RewardPerVoteUpdated(uint256 newMaxRewardPerVote); event TokensRescued(address token, uint256 amount, address receiver); - event BribeClosed(uint256 campaignId); - event CampaignRemoteManagerUpdated(address newCampaignRemoteManager); //////////////////////////////////////////////////// /// --- CONSTRUCTOR && INITIALIZATION @@ -142,7 +142,12 @@ contract CurvePoolBooster is Initializable, Strategizable { additionalGasLimit ); - emit BribeCreated(gauge, rewardToken, maxRewardPerVote, balanceSubFee); + emit CampaignCreated( + gauge, + rewardToken, + maxRewardPerVote, + balanceSubFee + ); } /// @notice Manage the total reward amount of the campaign @@ -253,7 +258,7 @@ contract CurvePoolBooster is Initializable, Strategizable { ICampaignRemoteManager(campaignRemoteManager).closeCampaign( _campaignId ); - emit BribeClosed(_campaignId); + emit CampaignClosed(_campaignId); } /// @notice calculate the fee amount and transfer it to the feeCollector diff --git a/contracts/deploy/mainnet/119_pool_booster_curve.js b/contracts/deploy/mainnet/119_pool_booster_curve.js index 3aa1696fe6..be0e50a2ff 100644 --- a/contracts/deploy/mainnet/119_pool_booster_curve.js +++ b/contracts/deploy/mainnet/119_pool_booster_curve.js @@ -44,7 +44,7 @@ module.exports = deploymentWithGovernanceProposal( addresses.base.multichainStrategist, // Strategist 0, // Fee addresses.base.multichainStrategist, // Fee collector - addresses.mainnet.CampaignRemoteManager // Campaign Remote Manager (VotemarketV2 entry point) + addresses.mainnet.CampaignRemoteManager, // Campaign Remote Manager (VotemarketV2 entry point) ] ); diff --git a/contracts/test/strategies/curvePoolBooster.mainnet.fork-test.js b/contracts/test/strategies/curvePoolBooster.mainnet.fork-test.js index 8ecc58fa8b..58a94a1789 100644 --- a/contracts/test/strategies/curvePoolBooster.mainnet.fork-test.js +++ b/contracts/test/strategies/curvePoolBooster.mainnet.fork-test.js @@ -31,18 +31,24 @@ describe("ForkTest: CurvePoolBooster", function () { addresses.mainnet.OUSDProxy ); expect(await curvePoolBooster.targetChainId()).to.equal(42161); - expect(await curvePoolBooster.strategistAddr()).to.equal(multichainStrategistAddr); + expect(await curvePoolBooster.strategistAddr()).to.equal( + multichainStrategistAddr + ); expect(await curvePoolBooster.governor()).to.equal( addresses.mainnet.Timelock ); - expect(await curvePoolBooster.campaignRemoteManager()).to.equal(addresses.mainnet.CampaignRemoteManager); + expect(await curvePoolBooster.campaignRemoteManager()).to.equal( + addresses.mainnet.CampaignRemoteManager + ); }); it("Should Create a campaign", async () => { const { curvePoolBooster, ousd, wousd } = fixture; const { multichainStrategistAddr } = await getNamedAccounts(); const woethSigner = await impersonateAndFund(wousd.address); - const sStrategist = await ethers.provider.getSigner(multichainStrategistAddr); + const sStrategist = await ethers.provider.getSigner( + multichainStrategistAddr + ); // Deal OETH and ETH to pool booster await ousd @@ -75,7 +81,9 @@ describe("ForkTest: CurvePoolBooster", function () { const { curvePoolBooster, ousd, wousd, josh } = fixture; const { multichainStrategistAddr } = await getNamedAccounts(); const woethSigner = await impersonateAndFund(wousd.address); - const sStrategist = await ethers.provider.getSigner(multichainStrategistAddr); + const sStrategist = await ethers.provider.getSigner( + multichainStrategistAddr + ); const gov = await curvePoolBooster.governor(); const sGov = await ethers.provider.getSigner(gov); @@ -114,7 +122,9 @@ describe("ForkTest: CurvePoolBooster", function () { it("Should set campaign id", async () => { const { curvePoolBooster } = fixture; const { multichainStrategistAddr } = await getNamedAccounts(); - const sStrategist = await ethers.provider.getSigner(multichainStrategistAddr); + const sStrategist = await ethers.provider.getSigner( + multichainStrategistAddr + ); expect(await curvePoolBooster.campaignId()).to.equal(0); await curvePoolBooster.connect(sStrategist).setCampaignId(12); @@ -139,7 +149,9 @@ describe("ForkTest: CurvePoolBooster", function () { const { curvePoolBooster, ousd, wousd } = fixture; const { multichainStrategistAddr } = await getNamedAccounts(); const woethSigner = await impersonateAndFund(wousd.address); - const sStrategist = await ethers.provider.getSigner(multichainStrategistAddr); + const sStrategist = await ethers.provider.getSigner( + multichainStrategistAddr + ); // Deal OETH and ETH to pool booster await ousd @@ -188,7 +200,9 @@ describe("ForkTest: CurvePoolBooster", function () { const { curvePoolBooster, ousd, wousd } = fixture; const { multichainStrategistAddr } = await getNamedAccounts(); const woethSigner = await impersonateAndFund(wousd.address); - const sStrategist = await ethers.provider.getSigner(multichainStrategistAddr); + const sStrategist = await ethers.provider.getSigner( + multichainStrategistAddr + ); // Deal OETH and ETH to pool booster await ousd @@ -234,7 +248,9 @@ describe("ForkTest: CurvePoolBooster", function () { const { curvePoolBooster, ousd, wousd } = fixture; const { multichainStrategistAddr } = await getNamedAccounts(); const woethSigner = await impersonateAndFund(wousd.address); - const sStrategist = await ethers.provider.getSigner(multichainStrategistAddr); + const sStrategist = await ethers.provider.getSigner( + multichainStrategistAddr + ); // Deal OETH and ETH to pool booster await ousd @@ -279,7 +295,9 @@ describe("ForkTest: CurvePoolBooster", function () { it("Should rescue ETH", async () => { const { curvePoolBooster } = fixture; const { multichainStrategistAddr } = await getNamedAccounts(); - const sStrategist = await ethers.provider.getSigner(multichainStrategistAddr); + const sStrategist = await ethers.provider.getSigner( + multichainStrategistAddr + ); // Deal ETH to pool booster await sStrategist.sendTransaction({ @@ -290,7 +308,9 @@ describe("ForkTest: CurvePoolBooster", function () { const balanceBefore = await ethers.provider.getBalance( curvePoolBooster.address ); - await curvePoolBooster.connect(sStrategist).rescueETH(multichainStrategistAddr); + await curvePoolBooster + .connect(sStrategist) + .rescueETH(multichainStrategistAddr); const balanceAfter = await ethers.provider.getBalance( curvePoolBooster.address ); @@ -326,7 +346,9 @@ describe("ForkTest: CurvePoolBooster", function () { it("Should revert if campaign is already created", async () => { const { curvePoolBooster } = fixture; const { multichainStrategistAddr } = await getNamedAccounts(); - const sStrategist = await ethers.provider.getSigner(multichainStrategistAddr); + const sStrategist = await ethers.provider.getSigner( + multichainStrategistAddr + ); await curvePoolBooster.connect(sStrategist).setCampaignId(12); await expect( @@ -345,7 +367,9 @@ describe("ForkTest: CurvePoolBooster", function () { it("Should revert if campaign is not created", async () => { const { curvePoolBooster } = fixture; const { multichainStrategistAddr } = await getNamedAccounts(); - const sStrategist = await ethers.provider.getSigner(multichainStrategistAddr); + const sStrategist = await ethers.provider.getSigner( + multichainStrategistAddr + ); await expect( curvePoolBooster @@ -367,7 +391,9 @@ describe("ForkTest: CurvePoolBooster", function () { it("Should revert if Invalid number of periods", async () => { const { curvePoolBooster } = fixture; const { multichainStrategistAddr } = await getNamedAccounts(); - const sStrategist = await ethers.provider.getSigner(multichainStrategistAddr); + const sStrategist = await ethers.provider.getSigner( + multichainStrategistAddr + ); await curvePoolBooster.connect(sStrategist).setCampaignId(12); @@ -381,7 +407,9 @@ describe("ForkTest: CurvePoolBooster", function () { it("Should revert if Invalid reward per vote", async () => { const { curvePoolBooster } = fixture; const { multichainStrategistAddr } = await getNamedAccounts(); - const sStrategist = await ethers.provider.getSigner(multichainStrategistAddr); + const sStrategist = await ethers.provider.getSigner( + multichainStrategistAddr + ); await curvePoolBooster.connect(sStrategist).setCampaignId(12); @@ -395,7 +423,9 @@ describe("ForkTest: CurvePoolBooster", function () { it("Should revert if No reward to manage", async () => { const { curvePoolBooster } = fixture; const { multichainStrategistAddr } = await getNamedAccounts(); - const sStrategist = await ethers.provider.getSigner(multichainStrategistAddr); + const sStrategist = await ethers.provider.getSigner( + multichainStrategistAddr + ); await expect( curvePoolBooster From 83548783d248e371cd5d950c17f9375a386c21e4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment?= Date: Wed, 22 Jan 2025 23:36:51 +0100 Subject: [PATCH 04/13] feat: configure deploymment with CreateX. --- contracts/abi/createx.json | 77 ++++++++++ .../deploy/mainnet/119_pool_booster_curve.js | 142 +++++++++++++----- contracts/utils/addresses.js | 3 +- 3 files changed, 182 insertions(+), 40 deletions(-) create mode 100644 contracts/abi/createx.json diff --git a/contracts/abi/createx.json b/contracts/abi/createx.json new file mode 100644 index 0000000000..9e30b0e694 --- /dev/null +++ b/contracts/abi/createx.json @@ -0,0 +1,77 @@ +[ + { + "inputs": [ + { + "internalType": "bytes32", + "name": "salt", + "type": "bytes32" + }, + { + "internalType": "bytes", + "name": "initCode", + "type": "bytes" + } + ], + "name": "deployCreate2", + "outputs": [ + { + "internalType": "address", + "name": "newContract", + "type": "address" + } + ], + "stateMutability": "payable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes32", + "name": "salt", + "type": "bytes32" + }, + { + "internalType": "bytes", + "name": "initCode", + "type": "bytes" + }, + { + "internalType": "bytes", + "name": "data", + "type": "bytes" + }, + { + "components": [ + { + "internalType": "uint256", + "name": "constructorAmount", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "initCallAmount", + "type": "uint256" + } + ], + "internalType": "struct CreateX.Values", + "name": "values", + "type": "tuple" + }, + { + "internalType": "address", + "name": "refundAddress", + "type": "address" + } + ], + "name": "deployCreate2AndInit", + "outputs": [ + { + "internalType": "address", + "name": "newContract", + "type": "address" + } + ], + "stateMutability": "payable", + "type": "function" + } +] \ No newline at end of file diff --git a/contracts/deploy/mainnet/119_pool_booster_curve.js b/contracts/deploy/mainnet/119_pool_booster_curve.js index be0e50a2ff..6e2171caa2 100644 --- a/contracts/deploy/mainnet/119_pool_booster_curve.js +++ b/contracts/deploy/mainnet/119_pool_booster_curve.js @@ -1,5 +1,8 @@ const addresses = require("../../utils/addresses"); const { deploymentWithGovernanceProposal } = require("../../utils/deploy"); +const createxAbi = require("../../abi/createx.json"); +const PoolBoosterBytecode = require("../../artifacts/contracts/strategies/CurvePoolBooster.sol/CurvePoolBooster.json"); +const ProxyBytecode = require("../../artifacts/contracts/proxies/Proxies.sol/CurvePoolBoosterProxy.json"); module.exports = deploymentWithGovernanceProposal( { @@ -10,54 +13,115 @@ module.exports = deploymentWithGovernanceProposal( deployerIsProposer: false, proposalId: "", }, - async ({ deployWithConfirmation, withConfirmation }) => { + async ({ withConfirmation }) => { const { deployerAddr } = await getNamedAccounts(); const sDeployer = await ethers.provider.getSigner(deployerAddr); - console.log(`Using deployer account: ${deployerAddr}`); - // 1. Deploy proxy - const dCurvePoolBoosterProxy = await deployWithConfirmation( - "CurvePoolBoosterProxy" + console.log(`Deployer address: ${deployerAddr}`); + + console.log("\nStarting deployment using CreateX"); + const rewardToken = addresses.mainnet.OUSDProxy; + const gauge = addresses.mainnet.CurveOUSDUSDTGauge; + const targetedChainId = 42161; // arbitrum + const fee = 0; + + // Get CreateX contract + const cCreateX = await ethers.getContractAt(createxAbi, addresses.createX); + + // Generate encoded salt (deployer address || crossChainProtectionFlag || bytes11(keccak256(rewardToken, gauge))) + const addressDeployerBytes20 = ethers.utils.hexlify( + ethers.utils.zeroPad(deployerAddr, 20) ); - const cCurvePoolBoosterProxy = await ethers.getContract( - "CurvePoolBoosterProxy" + const crossChainProtectioFlagBytes1 = ethers.utils.hexlify( + ethers.utils.zeroPad(0, 1) ); - - // 2. Deploy implementation - const dCurvePoolBoosterImpl = await deployWithConfirmation( - "CurvePoolBooster", - [ - 42161, // Arbitrum chain id - addresses.mainnet.OUSDProxy, // Bribe token - addresses.mainnet.CurveOUSDUSDTGauge, // Gauge - ] - ); - console.log("dCurvePoolBoosterImpl: ", dCurvePoolBoosterImpl.address); - const cCurvePoolBooster = await ethers.getContractAt( - "CurvePoolBooster", - dCurvePoolBoosterProxy.address + const saltBytes11 = + "0x" + + ethers.utils + .keccak256( + ethers.utils.concat([ + ethers.utils.arrayify(rewardToken), + ethers.utils.arrayify(gauge), + ]) + ) + .slice(2, 24); + const encodedSalt = ethers.utils.hexlify( + ethers.utils.concat([ + addressDeployerBytes20, + crossChainProtectioFlagBytes1, + saltBytes11, + ]) ); + console.log(`Encoded salt: ${encodedSalt}`); - // 3. Initialize - const initData = cCurvePoolBooster.interface.encodeFunctionData( - "initialize(address,uint16,address,address)", - [ - addresses.base.multichainStrategist, // Strategist - 0, // Fee - addresses.base.multichainStrategist, // Fee collector - addresses.mainnet.CampaignRemoteManager, // Campaign Remote Manager (VotemarketV2 entry point) - ] + // --- Deploy implementation --- // + const cachedInitCodeImpl = ethers.utils.concat([ + PoolBoosterBytecode.bytecode, + ethers.utils.defaultAbiCoder.encode( + ["uint256", "address", "address"], + [targetedChainId, rewardToken, gauge] + ), + ]); + const txResponse = await withConfirmation( + cCreateX.connect(sDeployer).deployCreate2(encodedSalt, cachedInitCodeImpl) ); - - // 4. Initialize proxy - const initFunction = "initialize(address,address,bytes)"; - await withConfirmation( - cCurvePoolBoosterProxy.connect(sDeployer)[initFunction]( - dCurvePoolBoosterImpl.address, - addresses.mainnet.Timelock, // governor - initData // data for delegate call to the initialize function on the strategy - ) + const txReceipt = await txResponse.wait(); + // event 0 is GovernorshipTransferred + // event 1 is ContractCreation, topics[1] is the address of the deployed contract, topics[2] is the salt + const implementationAddress = ethers.utils.getAddress( + `0x${txReceipt.events[1].topics[1].slice(26)}` + ); + console.log( + `Curve Booster Implementation deployed at: ${implementationAddress}` ); + // --- Deploy and init proxy --- // + const cachedInitCodeProxy = ProxyBytecode.bytecode; // No constructor arguments + const initializeImplem = ethers.utils.hexlify( + ethers.utils.concat([ + "0x67abbc82", + ethers.utils.defaultAbiCoder.encode( + ["address", "uint16", "address", "address"], + [ + addresses.base.multichainStrategist, // strategist + fee, // fee + addresses.base.multichainStrategist, // feeCollector + addresses.mainnet.CampaignRemoteManager, // campaignRemoteManager + ] + ), + ]) + ); + const initializeProxy = ethers.utils.hexlify( + ethers.utils.concat([ + "0xcf7a1d77", + ethers.utils.defaultAbiCoder.encode( + ["address", "address", "bytes"], + [implementationAddress, addresses.mainnet.Timelock, initializeImplem] // implementation, governor, init data + ), + ]) + ); + const txResponseProxy = await withConfirmation( + cCreateX + .connect(sDeployer) + .deployCreate2AndInit( + encodedSalt, + cachedInitCodeProxy, + initializeProxy, + ["0x00", "0x00"], + deployerAddr + ) + ); + const txReceiptProxy = await txResponseProxy.wait(); + // event 0 is GovernorshipTransferred + // event 1 is ContractCreation, topics[1] is the address of the deployed contract, topics[2] is the salt + // event 2 is StrategistUpdated + // event 3 is FeeUpdated + // event 4 is FeeCollectorUpdated + // event 5 is CampaignRemoteManagerUpdated + // event 6 is GovernorshipTransferred + const proxyAddress = ethers.utils.getAddress( + `0x${txReceiptProxy.events[1].topics[1].slice(26)}` + ); + console.log(`Curve Booster Proxy deployed at: ${proxyAddress}`); return {}; } ); diff --git a/contracts/utils/addresses.js b/contracts/utils/addresses.js index 1c34b79937..5a18af30ae 100644 --- a/contracts/utils/addresses.js +++ b/contracts/utils/addresses.js @@ -4,6 +4,7 @@ const addresses = {}; addresses.zero = "0x0000000000000000000000000000000000000000"; addresses.dead = "0x0000000000000000000000000000000000000001"; addresses.ETH = "0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE"; +addresses.createX = "0xba5Ed099633D3B313e4D5F7bdc1305d3c28ba5Ed"; addresses.mainnet = {}; addresses.holesky = {}; @@ -172,7 +173,7 @@ addresses.mainnet.CVXETHRewardsPool = // Votemarket - StakeDAO addresses.mainnet.CampaignRemoteManager = - "0xd1f0101Df22Cb7447F486Da5784237AB7a55eB4e"; + "0x000000009dF57105d76B059178989E01356e4b45"; // Morpho addresses.mainnet.MorphoStrategyProxy = From 4f1edda23db2f54b79697c3ac7e52b868c785d49 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment?= Date: Thu, 23 Jan 2025 11:23:20 +0100 Subject: [PATCH 05/13] fix: use encodeFunctionData to encodeWithSignature. --- .../deploy/mainnet/119_pool_booster_curve.js | 45 ++++++++++--------- 1 file changed, 24 insertions(+), 21 deletions(-) diff --git a/contracts/deploy/mainnet/119_pool_booster_curve.js b/contracts/deploy/mainnet/119_pool_booster_curve.js index 6e2171caa2..b5f35b6c7a 100644 --- a/contracts/deploy/mainnet/119_pool_booster_curve.js +++ b/contracts/deploy/mainnet/119_pool_booster_curve.js @@ -18,6 +18,15 @@ module.exports = deploymentWithGovernanceProposal( const sDeployer = await ethers.provider.getSigner(deployerAddr); console.log(`Deployer address: ${deployerAddr}`); + const cCurvePoolBooster = await ethers.getContractAt( + "CurvePoolBooster", + addresses.zero + ); + const cCurvePoolBoosterProxy = await ethers.getContractAt( + "CurvePoolBoosterProxy", + addresses.zero + ); + console.log("\nStarting deployment using CreateX"); const rewardToken = addresses.mainnet.OUSDProxy; const gauge = addresses.mainnet.CurveOUSDUSDTGauge; @@ -76,28 +85,22 @@ module.exports = deploymentWithGovernanceProposal( // --- Deploy and init proxy --- // const cachedInitCodeProxy = ProxyBytecode.bytecode; // No constructor arguments - const initializeImplem = ethers.utils.hexlify( - ethers.utils.concat([ - "0x67abbc82", - ethers.utils.defaultAbiCoder.encode( - ["address", "uint16", "address", "address"], - [ - addresses.base.multichainStrategist, // strategist - fee, // fee - addresses.base.multichainStrategist, // feeCollector - addresses.mainnet.CampaignRemoteManager, // campaignRemoteManager - ] - ), - ]) + const initializeImplem = cCurvePoolBooster.interface.encodeFunctionData( + "initialize(address,uint16,address,address)", + [ + addresses.base.multichainStrategist, // strategist + fee, // fee + addresses.base.multichainStrategist, // feeCollector + addresses.mainnet.CampaignRemoteManager, // campaignRemoteManager + ] ); - const initializeProxy = ethers.utils.hexlify( - ethers.utils.concat([ - "0xcf7a1d77", - ethers.utils.defaultAbiCoder.encode( - ["address", "address", "bytes"], - [implementationAddress, addresses.mainnet.Timelock, initializeImplem] // implementation, governor, init data - ), - ]) + const initializeProxy = cCurvePoolBoosterProxy.interface.encodeFunctionData( + "initialize(address,address,bytes)", + [ + implementationAddress, // implementation + addresses.mainnet.Timelock, // governor + initializeImplem, // init data + ] ); const txResponseProxy = await withConfirmation( cCreateX From 1f5d5b77e95282e527185c38103f9353f1b94b26 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment?= Date: Thu, 23 Jan 2025 11:34:32 +0100 Subject: [PATCH 06/13] feat: add arbitrum deployment file for CurvePoolBooster. --- .../mainnet/120_pool_booster_curve_arb.js | 137 ++++++++++++++++++ 1 file changed, 137 insertions(+) create mode 100644 contracts/deploy/mainnet/120_pool_booster_curve_arb.js diff --git a/contracts/deploy/mainnet/120_pool_booster_curve_arb.js b/contracts/deploy/mainnet/120_pool_booster_curve_arb.js new file mode 100644 index 0000000000..75b785a4ed --- /dev/null +++ b/contracts/deploy/mainnet/120_pool_booster_curve_arb.js @@ -0,0 +1,137 @@ +const addresses = require("../../utils/addresses"); +const { deployOnArb } = require("../../utils/deploy-l2"); +const createxAbi = require("../../abi/createx.json"); +const PoolBoosterBytecode = require("../../artifacts/contracts/strategies/CurvePoolBooster.sol/CurvePoolBooster.json"); +const ProxyBytecode = require("../../artifacts/contracts/proxies/Proxies.sol/CurvePoolBoosterProxy.json"); + +// --------------------------------!!! / WARNING \ !!!----------------------------------------- +// +// This deployment contract shoudl be EXACTLY the same as the 119 !!! +// It is using createX to deploy contract at the SAME address as the one deployed in 119. +// +// -------------------------------------------------------------------------------------------- + +module.exports = deployOnArb( + { + deployName: "120_pool_booster_curve", + forceDeploy: false, + //forceSkip: true, + reduceQueueTime: true, + deployerIsProposer: false, + proposalId: "", + }, + async ({ withConfirmation }) => { + const { deployerAddr } = await getNamedAccounts(); + const sDeployer = await ethers.provider.getSigner(deployerAddr); + console.log(`Deployer address: ${deployerAddr}`); + + const cCurvePoolBooster = await ethers.getContractAt( + "CurvePoolBooster", + addresses.zero + ); + const cCurvePoolBoosterProxy = await ethers.getContractAt( + "CurvePoolBoosterProxy", + addresses.zero + ); + + console.log("\nStarting deployment using CreateX"); + const rewardToken = addresses.mainnet.OUSDProxy; + const gauge = addresses.mainnet.CurveOUSDUSDTGauge; + const targetedChainId = 42161; // arbitrum + const fee = 0; + + // Get CreateX contract + const cCreateX = await ethers.getContractAt(createxAbi, addresses.createX); + + // Generate encoded salt (deployer address || crossChainProtectionFlag || bytes11(keccak256(rewardToken, gauge))) + const addressDeployerBytes20 = ethers.utils.hexlify( + ethers.utils.zeroPad(deployerAddr, 20) + ); + const crossChainProtectioFlagBytes1 = ethers.utils.hexlify( + ethers.utils.zeroPad(0, 1) + ); + const saltBytes11 = + "0x" + + ethers.utils + .keccak256( + ethers.utils.concat([ + ethers.utils.arrayify(rewardToken), + ethers.utils.arrayify(gauge), + ]) + ) + .slice(2, 24); + const encodedSalt = ethers.utils.hexlify( + ethers.utils.concat([ + addressDeployerBytes20, + crossChainProtectioFlagBytes1, + saltBytes11, + ]) + ); + console.log(`Encoded salt: ${encodedSalt}`); + + // --- Deploy implementation --- // + const cachedInitCodeImpl = ethers.utils.concat([ + PoolBoosterBytecode.bytecode, + ethers.utils.defaultAbiCoder.encode( + ["uint256", "address", "address"], + [targetedChainId, rewardToken, gauge] + ), + ]); + const txResponse = await withConfirmation( + cCreateX.connect(sDeployer).deployCreate2(encodedSalt, cachedInitCodeImpl) + ); + const txReceipt = await txResponse.wait(); + // event 0 is GovernorshipTransferred + // event 1 is ContractCreation, topics[1] is the address of the deployed contract, topics[2] is the salt + const implementationAddress = ethers.utils.getAddress( + `0x${txReceipt.events[1].topics[1].slice(26)}` + ); + console.log( + `Curve Booster Implementation deployed at: ${implementationAddress}` + ); + + // --- Deploy and init proxy --- // + const cachedInitCodeProxy = ProxyBytecode.bytecode; // No constructor arguments + const initializeImplem = cCurvePoolBooster.interface.encodeFunctionData( + "initialize(address,uint16,address,address)", + [ + addresses.base.multichainStrategist, // strategist + fee, // fee + addresses.base.multichainStrategist, // feeCollector + addresses.mainnet.CampaignRemoteManager, // campaignRemoteManager + ] + ); + const initializeProxy = cCurvePoolBoosterProxy.interface.encodeFunctionData( + "initialize(address,address,bytes)", + [ + implementationAddress, // implementation + addresses.mainnet.Timelock, // governor + initializeImplem, // init data + ] + ); + const txResponseProxy = await withConfirmation( + cCreateX + .connect(sDeployer) + .deployCreate2AndInit( + encodedSalt, + cachedInitCodeProxy, + initializeProxy, + ["0x00", "0x00"], + deployerAddr + ) + ); + const txReceiptProxy = await txResponseProxy.wait(); + // event 0 is GovernorshipTransferred + // event 1 is ContractCreation, topics[1] is the address of the deployed contract, topics[2] is the salt + // event 2 is StrategistUpdated + // event 3 is FeeUpdated + // event 4 is FeeCollectorUpdated + // event 5 is CampaignRemoteManagerUpdated + // event 6 is GovernorshipTransferred + const proxyAddress = ethers.utils.getAddress( + `0x${txReceiptProxy.events[1].topics[1].slice(26)}` + ); + console.log(`Curve Booster Proxy deployed at: ${proxyAddress}`); + return {}; + } +); From f2bf316b218ef98eb93cfbe10bf13a599169ffdf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment?= Date: Thu, 23 Jan 2025 14:22:20 +0100 Subject: [PATCH 07/13] feat: create a funciton for encodedSalt. --- .../deploy/mainnet/119_pool_booster_curve.js | 32 ++++++------------ .../mainnet/120_pool_booster_curve_arb.js | 33 ++++++------------- contracts/utils/deploy.js | 29 ++++++++++++++++ 3 files changed, 49 insertions(+), 45 deletions(-) diff --git a/contracts/deploy/mainnet/119_pool_booster_curve.js b/contracts/deploy/mainnet/119_pool_booster_curve.js index b5f35b6c7a..054bbfcf9a 100644 --- a/contracts/deploy/mainnet/119_pool_booster_curve.js +++ b/contracts/deploy/mainnet/119_pool_booster_curve.js @@ -1,5 +1,8 @@ const addresses = require("../../utils/addresses"); -const { deploymentWithGovernanceProposal } = require("../../utils/deploy"); +const { + deploymentWithGovernanceProposal, + encodeSaltForCreateX, +} = require("../../utils/deploy"); const createxAbi = require("../../abi/createx.json"); const PoolBoosterBytecode = require("../../artifacts/contracts/strategies/CurvePoolBooster.sol/CurvePoolBooster.json"); const ProxyBytecode = require("../../artifacts/contracts/proxies/Proxies.sol/CurvePoolBoosterProxy.json"); @@ -36,30 +39,15 @@ module.exports = deploymentWithGovernanceProposal( // Get CreateX contract const cCreateX = await ethers.getContractAt(createxAbi, addresses.createX); - // Generate encoded salt (deployer address || crossChainProtectionFlag || bytes11(keccak256(rewardToken, gauge))) - const addressDeployerBytes20 = ethers.utils.hexlify( - ethers.utils.zeroPad(deployerAddr, 20) - ); - const crossChainProtectioFlagBytes1 = ethers.utils.hexlify( - ethers.utils.zeroPad(0, 1) - ); - const saltBytes11 = - "0x" + - ethers.utils - .keccak256( - ethers.utils.concat([ - ethers.utils.arrayify(rewardToken), - ethers.utils.arrayify(gauge), - ]) - ) - .slice(2, 24); - const encodedSalt = ethers.utils.hexlify( + // Generate salt + const salt = ethers.utils.keccak256( ethers.utils.concat([ - addressDeployerBytes20, - crossChainProtectioFlagBytes1, - saltBytes11, + ethers.utils.arrayify(rewardToken), + ethers.utils.arrayify(gauge), ]) ); + + const encodedSalt = encodeSaltForCreateX(deployerAddr, false, salt); console.log(`Encoded salt: ${encodedSalt}`); // --- Deploy implementation --- // diff --git a/contracts/deploy/mainnet/120_pool_booster_curve_arb.js b/contracts/deploy/mainnet/120_pool_booster_curve_arb.js index 75b785a4ed..352e08f233 100644 --- a/contracts/deploy/mainnet/120_pool_booster_curve_arb.js +++ b/contracts/deploy/mainnet/120_pool_booster_curve_arb.js @@ -1,19 +1,21 @@ const addresses = require("../../utils/addresses"); const { deployOnArb } = require("../../utils/deploy-l2"); +const { encodeSaltForCreateX } = require("../../utils/deploy"); + const createxAbi = require("../../abi/createx.json"); const PoolBoosterBytecode = require("../../artifacts/contracts/strategies/CurvePoolBooster.sol/CurvePoolBooster.json"); const ProxyBytecode = require("../../artifacts/contracts/proxies/Proxies.sol/CurvePoolBoosterProxy.json"); // --------------------------------!!! / WARNING \ !!!----------------------------------------- // -// This deployment contract shoudl be EXACTLY the same as the 119 !!! +// `encodedSalt`, ProxyBytecode and PoolBoosterBytecode shoudl be EXACTLY the same as the 119 !!! // It is using createX to deploy contract at the SAME address as the one deployed in 119. // // -------------------------------------------------------------------------------------------- module.exports = deployOnArb( { - deployName: "120_pool_booster_curve", + deployName: "119_pool_booster_curve", forceDeploy: false, //forceSkip: true, reduceQueueTime: true, @@ -43,30 +45,15 @@ module.exports = deployOnArb( // Get CreateX contract const cCreateX = await ethers.getContractAt(createxAbi, addresses.createX); - // Generate encoded salt (deployer address || crossChainProtectionFlag || bytes11(keccak256(rewardToken, gauge))) - const addressDeployerBytes20 = ethers.utils.hexlify( - ethers.utils.zeroPad(deployerAddr, 20) - ); - const crossChainProtectioFlagBytes1 = ethers.utils.hexlify( - ethers.utils.zeroPad(0, 1) - ); - const saltBytes11 = - "0x" + - ethers.utils - .keccak256( - ethers.utils.concat([ - ethers.utils.arrayify(rewardToken), - ethers.utils.arrayify(gauge), - ]) - ) - .slice(2, 24); - const encodedSalt = ethers.utils.hexlify( + // Generate salt + const salt = ethers.utils.keccak256( ethers.utils.concat([ - addressDeployerBytes20, - crossChainProtectioFlagBytes1, - saltBytes11, + ethers.utils.arrayify(rewardToken), + ethers.utils.arrayify(gauge), ]) ); + + const encodedSalt = encodeSaltForCreateX(deployerAddr, false, salt); console.log(`Encoded salt: ${encodedSalt}`); // --- Deploy implementation --- // diff --git a/contracts/utils/deploy.js b/contracts/utils/deploy.js index 057c68d77c..504ad83cff 100644 --- a/contracts/utils/deploy.js +++ b/contracts/utils/deploy.js @@ -1599,6 +1599,34 @@ function deploymentWithGuardianGovernor(opts, fn) { return main; } +function encodeSaltForCreateX(deployer, crossChainProtectionFlag, salt) { + // Generate encoded salt (deployer address || crossChainProtectionFlag || bytes11(keccak256(rewardToken, gauge))) + + // convert deployer address to bytes20 + const addressDeployerBytes20 = ethers.utils.hexlify( + ethers.utils.zeroPad(deployer, 20) + ); + + // convert crossChainProtectionFlag to bytes1 + const crossChainProtectionFlagBytes1 = crossChainProtectionFlag + ? "0x01" + : "0x00"; + + // convert salt to bytes11 + const saltBytes11 = "0x" + salt.slice(2, 24); + + // concat all bytes into a bytes32 + const encodedSalt = ethers.utils.hexlify( + ethers.utils.concat([ + addressDeployerBytes20, + crossChainProtectionFlagBytes1, + saltBytes11, + ]) + ); + + return encodedSalt; +} + module.exports = { log, sleep, @@ -1613,4 +1641,5 @@ module.exports = { deploymentWithGuardianGovernor, handleTransitionGovernance, + encodeSaltForCreateX, }; From 72dbf2b86ba7640be7d8438c87d960a20aaf00cf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment?= Date: Thu, 23 Jan 2025 14:26:42 +0100 Subject: [PATCH 08/13] feat: create multichainStrategist variable. --- contracts/deploy/mainnet/119_pool_booster_curve.js | 4 ++-- contracts/deploy/mainnet/120_pool_booster_curve_arb.js | 4 ++-- contracts/utils/addresses.js | 1 + 3 files changed, 5 insertions(+), 4 deletions(-) diff --git a/contracts/deploy/mainnet/119_pool_booster_curve.js b/contracts/deploy/mainnet/119_pool_booster_curve.js index 054bbfcf9a..e181a6e588 100644 --- a/contracts/deploy/mainnet/119_pool_booster_curve.js +++ b/contracts/deploy/mainnet/119_pool_booster_curve.js @@ -76,9 +76,9 @@ module.exports = deploymentWithGovernanceProposal( const initializeImplem = cCurvePoolBooster.interface.encodeFunctionData( "initialize(address,uint16,address,address)", [ - addresses.base.multichainStrategist, // strategist + addresses.multichainStrategist, // strategist fee, // fee - addresses.base.multichainStrategist, // feeCollector + addresses.multichainStrategist, // feeCollector addresses.mainnet.CampaignRemoteManager, // campaignRemoteManager ] ); diff --git a/contracts/deploy/mainnet/120_pool_booster_curve_arb.js b/contracts/deploy/mainnet/120_pool_booster_curve_arb.js index 352e08f233..23e391ce29 100644 --- a/contracts/deploy/mainnet/120_pool_booster_curve_arb.js +++ b/contracts/deploy/mainnet/120_pool_booster_curve_arb.js @@ -82,9 +82,9 @@ module.exports = deployOnArb( const initializeImplem = cCurvePoolBooster.interface.encodeFunctionData( "initialize(address,uint16,address,address)", [ - addresses.base.multichainStrategist, // strategist + addresses.multichainStrategist, // strategist fee, // fee - addresses.base.multichainStrategist, // feeCollector + addresses.multichainStrategist, // feeCollector addresses.mainnet.CampaignRemoteManager, // campaignRemoteManager ] ); diff --git a/contracts/utils/addresses.js b/contracts/utils/addresses.js index 5a18af30ae..2a60fcfcef 100644 --- a/contracts/utils/addresses.js +++ b/contracts/utils/addresses.js @@ -5,6 +5,7 @@ addresses.zero = "0x0000000000000000000000000000000000000000"; addresses.dead = "0x0000000000000000000000000000000000000001"; addresses.ETH = "0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE"; addresses.createX = "0xba5Ed099633D3B313e4D5F7bdc1305d3c28ba5Ed"; +addresses.multichainStrategist = "0x4FF1b9D9ba8558F5EAfCec096318eA0d8b541971"; addresses.mainnet = {}; addresses.holesky = {}; From 1456572d70e48a97b7e114d24e9c7a64c5a19bf9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment?= Date: Thu, 23 Jan 2025 14:29:22 +0100 Subject: [PATCH 09/13] fix: change proxy owner. --- contracts/deploy/mainnet/120_pool_booster_curve_arb.js | 2 +- contracts/utils/addresses.js | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/contracts/deploy/mainnet/120_pool_booster_curve_arb.js b/contracts/deploy/mainnet/120_pool_booster_curve_arb.js index 23e391ce29..9bb57f0163 100644 --- a/contracts/deploy/mainnet/120_pool_booster_curve_arb.js +++ b/contracts/deploy/mainnet/120_pool_booster_curve_arb.js @@ -92,7 +92,7 @@ module.exports = deployOnArb( "initialize(address,address,bytes)", [ implementationAddress, // implementation - addresses.mainnet.Timelock, // governor + addresses.arbitrumOne.admin, // governor initializeImplem, // init data ] ); diff --git a/contracts/utils/addresses.js b/contracts/utils/addresses.js index 2a60fcfcef..e481013fed 100644 --- a/contracts/utils/addresses.js +++ b/contracts/utils/addresses.js @@ -298,6 +298,7 @@ addresses.mainnet.LidoWithdrawalQueue = // Arbitrum One addresses.arbitrumOne = {}; addresses.arbitrumOne.WOETHProxy = "0xD8724322f44E5c58D7A815F542036fb17DbbF839"; +addresses.arbitrumOne.admin = "0xfD1383fb4eE74ED9D83F2cbC67507bA6Eac2896a"; // Base addresses.base = {}; From 5ee48a45efb4e8894308470d385027b5220ab1d9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment?= Date: Thu, 23 Jan 2025 14:51:19 +0100 Subject: [PATCH 10/13] try something. --- contracts/deploy/mainnet/119_pool_booster_curve.js | 3 +++ 1 file changed, 3 insertions(+) diff --git a/contracts/deploy/mainnet/119_pool_booster_curve.js b/contracts/deploy/mainnet/119_pool_booster_curve.js index e181a6e588..8e65ea6ce9 100644 --- a/contracts/deploy/mainnet/119_pool_booster_curve.js +++ b/contracts/deploy/mainnet/119_pool_booster_curve.js @@ -113,6 +113,9 @@ module.exports = deploymentWithGovernanceProposal( `0x${txReceiptProxy.events[1].topics[1].slice(26)}` ); console.log(`Curve Booster Proxy deployed at: ${proxyAddress}`); + + await ethers.getContractAt("CurvePoolBooster", proxyAddress); + await ethers.getContractAt("CurvePoolBoosterProxy", proxyAddress); return {}; } ); From c28106f2307470064b4d86fa89ea2359f5e3a313 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment?= Date: Thu, 23 Jan 2025 16:08:55 +0100 Subject: [PATCH 11/13] fix: adjust with new CampaignRemoteManager. --- .../interfaces/ICampaignRemoteManager.sol | 17 ++++-- .../contracts/strategies/CurvePoolBooster.sol | 53 +++++++++++++++---- .../deploy/mainnet/119_pool_booster_curve.js | 3 +- .../mainnet/120_pool_booster_curve_arb.js | 3 +- contracts/test/_fixture.js | 6 +-- contracts/utils/addresses.js | 1 + 6 files changed, 62 insertions(+), 21 deletions(-) diff --git a/contracts/contracts/interfaces/ICampaignRemoteManager.sol b/contracts/contracts/interfaces/ICampaignRemoteManager.sol index a7e0d4e0db..d4823f3692 100644 --- a/contracts/contracts/interfaces/ICampaignRemoteManager.sol +++ b/contracts/contracts/interfaces/ICampaignRemoteManager.sol @@ -5,16 +5,23 @@ interface ICampaignRemoteManager { function createCampaign( CampaignCreationParams memory params, uint256 destinationChainId, - uint256 additionalGasLimit + uint256 additionalGasLimit, + address votemarket ) external payable; function manageCampaign( CampaignManagementParams memory params, uint256 destinationChainId, - uint256 additionalGasLimit + uint256 additionalGasLimit, + address votemarket ) external payable; - function closeCampaign(uint256 campaignId) external; + function closeCampaign( + CampaignClosingParams memory params, + uint256 destinationChainId, + uint256 additionalGasLimit, + address votemarket + ) external payable; struct CampaignCreationParams { uint256 chainId; @@ -36,4 +43,8 @@ interface ICampaignRemoteManager { uint256 totalRewardAmount; uint256 maxRewardPerVote; } + + struct CampaignClosingParams { + uint256 campaignId; + } } diff --git a/contracts/contracts/strategies/CurvePoolBooster.sol b/contracts/contracts/strategies/CurvePoolBooster.sol index 7c3a6f983f..774f631eb4 100644 --- a/contracts/contracts/strategies/CurvePoolBooster.sol +++ b/contracts/contracts/strategies/CurvePoolBooster.sol @@ -40,6 +40,9 @@ contract CurvePoolBooster is Initializable, Strategizable { /// @notice Address of the campaignRemoteManager linked to VotemarketV2 address public campaignRemoteManager; + /// @notice Address of votemarket in L2 + address public votemarket; + /// @notice Id of the campaign created uint256 public campaignId; @@ -49,6 +52,7 @@ contract CurvePoolBooster is Initializable, Strategizable { event FeeUpdated(uint16 newFee); event FeeCollected(address feeCollector, uint256 feeAmount); event FeeCollectorUpdated(address newFeeCollector); + event VotemarketUpdated(address newVotemarket); event CampaignRemoteManagerUpdated(address newCampaignRemoteManager); event CampaignCreated( address gauge, @@ -87,12 +91,14 @@ contract CurvePoolBooster is Initializable, Strategizable { address _strategist, uint16 _fee, address _feeCollector, - address _campaignRemoteManager + address _campaignRemoteManager, + address _votemarket ) external onlyGovernor initializer { _setStrategistAddr(_strategist); _setFee(_fee); _setFeeCollector(_feeCollector); _setCampaignRemoteManager(_campaignRemoteManager); + _setVotemarket(_votemarket); } //////////////////////////////////////////////////// @@ -139,7 +145,8 @@ contract CurvePoolBooster is Initializable, Strategizable { isWhitelist: false }), targetChainId, - additionalGasLimit + additionalGasLimit, + votemarket ); emit CampaignCreated( @@ -179,7 +186,8 @@ contract CurvePoolBooster is Initializable, Strategizable { maxRewardPerVote: 0 }), targetChainId, - additionalGasLimit + additionalGasLimit, + votemarket ); emit TotalRewardAmountUpdated(balanceSubFee); @@ -210,7 +218,8 @@ contract CurvePoolBooster is Initializable, Strategizable { maxRewardPerVote: 0 }), targetChainId, - additionalGasLimit + additionalGasLimit, + votemarket ); emit NumberOfPeriodsUpdated(extraNumberOfPeriods); @@ -241,7 +250,8 @@ contract CurvePoolBooster is Initializable, Strategizable { maxRewardPerVote: newMaxRewardPerVote }), targetChainId, - additionalGasLimit + additionalGasLimit, + votemarket ); emit RewardPerVoteUpdated(newMaxRewardPerVote); @@ -251,12 +261,20 @@ contract CurvePoolBooster is Initializable, Strategizable { /// @dev This function only work on the L2 chain. Not on mainnet. /// @dev The _campaignId parameter is not related to the campaignId of this contract, allowing greater flexibility. /// @param _campaignId Id of the campaign to close - function closeCampaign(uint256 _campaignId) - external - onlyGovernorOrStrategist - { - ICampaignRemoteManager(campaignRemoteManager).closeCampaign( - _campaignId + function closeCampaign( + uint256 _campaignId, + uint256 bridgeFee, + uint256 additionalGasLimit + ) external onlyGovernorOrStrategist { + ICampaignRemoteManager(campaignRemoteManager).closeCampaign{ + value: bridgeFee + }( + ICampaignRemoteManager.CampaignClosingParams({ + campaignId: campaignId + }), + targetChainId, + additionalGasLimit, + votemarket ); emit CampaignClosed(_campaignId); } @@ -373,5 +391,18 @@ contract CurvePoolBooster is Initializable, Strategizable { emit CampaignRemoteManagerUpdated(_campaignRemoteManager); } + /// @notice Set the votemarket address + /// @param _votemarket New votemarket address + function setVotemarket(address _votemarket) external onlyGovernor { + _setVotemarket(_votemarket); + } + + /// @notice Internal logic to set the votemarket address + function _setVotemarket(address _votemarket) internal onlyGovernor { + require(_votemarket != address(0), "Invalid votemarket"); + votemarket = _votemarket; + emit VotemarketUpdated(_votemarket); + } + receive() external payable {} } diff --git a/contracts/deploy/mainnet/119_pool_booster_curve.js b/contracts/deploy/mainnet/119_pool_booster_curve.js index 8e65ea6ce9..90f194a2cb 100644 --- a/contracts/deploy/mainnet/119_pool_booster_curve.js +++ b/contracts/deploy/mainnet/119_pool_booster_curve.js @@ -74,12 +74,13 @@ module.exports = deploymentWithGovernanceProposal( // --- Deploy and init proxy --- // const cachedInitCodeProxy = ProxyBytecode.bytecode; // No constructor arguments const initializeImplem = cCurvePoolBooster.interface.encodeFunctionData( - "initialize(address,uint16,address,address)", + "initialize(address,uint16,address,address,address)", [ addresses.multichainStrategist, // strategist fee, // fee addresses.multichainStrategist, // feeCollector addresses.mainnet.CampaignRemoteManager, // campaignRemoteManager + addresses.votemarket, // votemarket ] ); const initializeProxy = cCurvePoolBoosterProxy.interface.encodeFunctionData( diff --git a/contracts/deploy/mainnet/120_pool_booster_curve_arb.js b/contracts/deploy/mainnet/120_pool_booster_curve_arb.js index 9bb57f0163..87b14c1716 100644 --- a/contracts/deploy/mainnet/120_pool_booster_curve_arb.js +++ b/contracts/deploy/mainnet/120_pool_booster_curve_arb.js @@ -80,12 +80,13 @@ module.exports = deployOnArb( // --- Deploy and init proxy --- // const cachedInitCodeProxy = ProxyBytecode.bytecode; // No constructor arguments const initializeImplem = cCurvePoolBooster.interface.encodeFunctionData( - "initialize(address,uint16,address,address)", + "initialize(address,uint16,address,address,address)", [ addresses.multichainStrategist, // strategist fee, // fee addresses.multichainStrategist, // feeCollector addresses.mainnet.CampaignRemoteManager, // campaignRemoteManager + addresses.votemarket, // votemarket ] ); const initializeProxy = cCurvePoolBoosterProxy.interface.encodeFunctionData( diff --git a/contracts/test/_fixture.js b/contracts/test/_fixture.js index ef083049b8..ffdd81144e 100644 --- a/contracts/test/_fixture.js +++ b/contracts/test/_fixture.js @@ -646,14 +646,10 @@ const defaultFixture = deployments.createFixture(async () => { morphoGauntletPrimeUSDTStrategyProxy.address ); - const curvePoolBoosterProxy = isFork - ? await ethers.getContract("CurvePoolBoosterProxy") - : undefined; - const curvePoolBooster = isFork ? await ethers.getContractAt( "CurvePoolBooster", - curvePoolBoosterProxy.address + "0x6ff396064cA32F663fc893B06A3cBc04D775FE02" // hardcoded as generated with CreateX. ) : undefined; diff --git a/contracts/utils/addresses.js b/contracts/utils/addresses.js index e481013fed..5008f4243c 100644 --- a/contracts/utils/addresses.js +++ b/contracts/utils/addresses.js @@ -6,6 +6,7 @@ addresses.dead = "0x0000000000000000000000000000000000000001"; addresses.ETH = "0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE"; addresses.createX = "0xba5Ed099633D3B313e4D5F7bdc1305d3c28ba5Ed"; addresses.multichainStrategist = "0x4FF1b9D9ba8558F5EAfCec096318eA0d8b541971"; +addresses.votemarket = "0x5e5C922a5Eeab508486eB906ebE7bDFFB05D81e5"; addresses.mainnet = {}; addresses.holesky = {}; From 4cd4a6f43b40894b10e5c6f7eca4c3ad9126449c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment?= Date: Thu, 23 Jan 2025 17:20:15 +0100 Subject: [PATCH 12/13] fix merge conflit. --- contracts/contracts/strategies/CurvePoolBooster.sol | 6 ------ 1 file changed, 6 deletions(-) diff --git a/contracts/contracts/strategies/CurvePoolBooster.sol b/contracts/contracts/strategies/CurvePoolBooster.sol index 664daaf02e..6e21d6b5fc 100644 --- a/contracts/contracts/strategies/CurvePoolBooster.sol +++ b/contracts/contracts/strategies/CurvePoolBooster.sol @@ -259,7 +259,6 @@ contract CurvePoolBooster is Initializable, Strategizable { emit RewardPerVoteUpdated(newMaxRewardPerVote); } -<<<<<<< HEAD /// @notice Close the campaign. /// @dev This function only work on the L2 chain. Not on mainnet. /// @dev The _campaignId parameter is not related to the campaignId of this contract, allowing greater flexibility. @@ -284,11 +283,6 @@ contract CurvePoolBooster is Initializable, Strategizable { /// @notice calculate the fee amount and transfer it to the feeCollector /// @return Balance after fee -======= - /// @notice Take the balance of rewards tokens owned by this contract and calculate the fee amount. - /// Transfer the fee to the feeCollector. - /// @return balance remaining balance of reward token ->>>>>>> b7baf9d433e7120d6fa4c5aef448940e4cf95d8d function _handleFee() internal returns (uint256) { // Cache current rewardToken balance uint256 balance = IERC20(rewardToken).balanceOf(address(this)); From ec53c6d6713f443cbe03b623ef2d6f1a6e3d8974 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cl=C3=A9ment?= Date: Thu, 23 Jan 2025 20:37:45 +0100 Subject: [PATCH 13/13] fix: add more tests. --- contracts/test/_fixture.js | 2 +- .../curvePoolBooster.mainnet.fork-test.js | 206 ++++++------------ 2 files changed, 69 insertions(+), 139 deletions(-) diff --git a/contracts/test/_fixture.js b/contracts/test/_fixture.js index ffdd81144e..86ae0aef26 100644 --- a/contracts/test/_fixture.js +++ b/contracts/test/_fixture.js @@ -649,7 +649,7 @@ const defaultFixture = deployments.createFixture(async () => { const curvePoolBooster = isFork ? await ethers.getContractAt( "CurvePoolBooster", - "0x6ff396064cA32F663fc893B06A3cBc04D775FE02" // hardcoded as generated with CreateX. + "0xb8221A6271fc9eB8cEdd02955e4CB9557E902af1" // hardcoded as generated with CreateX. ) : undefined; diff --git a/contracts/test/strategies/curvePoolBooster.mainnet.fork-test.js b/contracts/test/strategies/curvePoolBooster.mainnet.fork-test.js index 58a94a1789..4b929db688 100644 --- a/contracts/test/strategies/curvePoolBooster.mainnet.fork-test.js +++ b/contracts/test/strategies/curvePoolBooster.mainnet.fork-test.js @@ -18,31 +18,7 @@ describe("ForkTest: CurvePoolBooster", function () { fixture = await loadDefaultFixture(); }); - it("Should have correct params", async () => { - const { curvePoolBooster } = fixture; - const { multichainStrategistAddr } = await getNamedAccounts(); - expect(await curvePoolBooster.gauge()).to.equal( - addresses.mainnet.CurveOUSDUSDTGauge - ); - expect(await curvePoolBooster.campaignRemoteManager()).to.equal( - addresses.mainnet.CampaignRemoteManager - ); - expect(await curvePoolBooster.rewardToken()).to.equal( - addresses.mainnet.OUSDProxy - ); - expect(await curvePoolBooster.targetChainId()).to.equal(42161); - expect(await curvePoolBooster.strategistAddr()).to.equal( - multichainStrategistAddr - ); - expect(await curvePoolBooster.governor()).to.equal( - addresses.mainnet.Timelock - ); - expect(await curvePoolBooster.campaignRemoteManager()).to.equal( - addresses.mainnet.CampaignRemoteManager - ); - }); - - it("Should Create a campaign", async () => { + async function dealOETHAndCreateCampaign() { const { curvePoolBooster, ousd, wousd } = fixture; const { multichainStrategistAddr } = await getNamedAccounts(); const woethSigner = await impersonateAndFund(wousd.address); @@ -50,18 +26,21 @@ describe("ForkTest: CurvePoolBooster", function () { multichainStrategistAddr ); - // Deal OETH and ETH to pool booster + // Deal OETH to pool booster await ousd .connect(woethSigner) .transfer(curvePoolBooster.address, parseUnits("10")); expect(await ousd.balanceOf(curvePoolBooster.address)).to.equal( parseUnits("10") ); + + // Deal ETH to pool booster await sStrategist.sendTransaction({ to: curvePoolBooster.address, value: parseUnits("1"), }); + // Create campaign await curvePoolBooster .connect(sStrategist) .createCampaign( @@ -71,6 +50,36 @@ describe("ForkTest: CurvePoolBooster", function () { parseUnits("0.1"), 0 ); + } + + it("Should have correct params", async () => { + const { curvePoolBooster } = fixture; + const { multichainStrategistAddr } = await getNamedAccounts(); + expect(await curvePoolBooster.gauge()).to.equal( + addresses.mainnet.CurveOUSDUSDTGauge + ); + expect(await curvePoolBooster.campaignRemoteManager()).to.equal( + addresses.mainnet.CampaignRemoteManager + ); + expect(await curvePoolBooster.rewardToken()).to.equal( + addresses.mainnet.OUSDProxy + ); + expect(await curvePoolBooster.targetChainId()).to.equal(42161); + expect(await curvePoolBooster.strategistAddr()).to.equal( + multichainStrategistAddr + ); + expect(await curvePoolBooster.governor()).to.equal( + addresses.mainnet.Timelock + ); + expect(await curvePoolBooster.campaignRemoteManager()).to.equal( + addresses.mainnet.CampaignRemoteManager + ); + }); + + it("Should Create a campaign", async () => { + const { curvePoolBooster, ousd } = fixture; + + await dealOETHAndCreateCampaign(); expect(await ousd.balanceOf(curvePoolBooster.address)).to.equal( parseUnits("0") @@ -78,42 +87,17 @@ describe("ForkTest: CurvePoolBooster", function () { }); it("Should Create a campaign with fee", async () => { - const { curvePoolBooster, ousd, wousd, josh } = fixture; - const { multichainStrategistAddr } = await getNamedAccounts(); - const woethSigner = await impersonateAndFund(wousd.address); - const sStrategist = await ethers.provider.getSigner( - multichainStrategistAddr - ); + const { curvePoolBooster, ousd, josh } = fixture; const gov = await curvePoolBooster.governor(); const sGov = await ethers.provider.getSigner(gov); - // Deal OETH and ETH to pool booster - await ousd - .connect(woethSigner) - .transfer(curvePoolBooster.address, parseUnits("10")); - expect(await ousd.balanceOf(curvePoolBooster.address)).to.equal( - parseUnits("10") - ); - await sStrategist.sendTransaction({ - to: curvePoolBooster.address, - value: parseUnits("1"), - }); - // Set fee and fee collector await curvePoolBooster.connect(sGov).setFee(1000); // 10% await curvePoolBooster.connect(sGov).setFeeCollector(josh.address); expect(await ousd.balanceOf(josh.address)).to.equal(0); - // Create campaign - await curvePoolBooster - .connect(sStrategist) - .createCampaign( - 4, - 10, - [addresses.mainnet.ConvexVoter], - parseUnits("0.1"), - 100 - ); + // Deal OETH and create campaign + await dealOETHAndCreateCampaign(); // Ensure fee is collected expect(await ousd.balanceOf(josh.address)).to.equal(parseUnits("1")); @@ -153,27 +137,7 @@ describe("ForkTest: CurvePoolBooster", function () { multichainStrategistAddr ); - // Deal OETH and ETH to pool booster - await ousd - .connect(woethSigner) - .transfer(curvePoolBooster.address, parseUnits("10")); - expect(await ousd.balanceOf(curvePoolBooster.address)).to.equal( - parseUnits("10") - ); - await sStrategist.sendTransaction({ - to: curvePoolBooster.address, - value: parseUnits("1"), - }); - - await curvePoolBooster - .connect(sStrategist) - .createCampaign( - 4, - 10, - [addresses.mainnet.ConvexVoter], - parseUnits("0.1"), - 0 - ); + await dealOETHAndCreateCampaign(); // Deal new OETH to pool booster await ousd @@ -182,9 +146,6 @@ describe("ForkTest: CurvePoolBooster", function () { expect(await ousd.balanceOf(curvePoolBooster.address)).to.equal( parseUnits("13") ); - expect(await ousd.balanceOf(curvePoolBooster.address)).to.equal( - parseUnits("13") - ); await curvePoolBooster.connect(sStrategist).setCampaignId(12); @@ -197,45 +158,12 @@ describe("ForkTest: CurvePoolBooster", function () { }); it("Should manage number of periods", async () => { - const { curvePoolBooster, ousd, wousd } = fixture; + const { curvePoolBooster } = fixture; const { multichainStrategistAddr } = await getNamedAccounts(); - const woethSigner = await impersonateAndFund(wousd.address); const sStrategist = await ethers.provider.getSigner( multichainStrategistAddr ); - - // Deal OETH and ETH to pool booster - await ousd - .connect(woethSigner) - .transfer(curvePoolBooster.address, parseUnits("10")); - expect(await ousd.balanceOf(curvePoolBooster.address)).to.equal( - parseUnits("10") - ); - await sStrategist.sendTransaction({ - to: curvePoolBooster.address, - value: parseUnits("1"), - }); - - await curvePoolBooster - .connect(sStrategist) - .createCampaign( - 4, - 10, - [addresses.mainnet.ConvexVoter], - parseUnits("0.1"), - 0 - ); - - // Deal new OETH to pool booster - await ousd - .connect(woethSigner) - .transfer(curvePoolBooster.address, parseUnits("13")); - expect(await ousd.balanceOf(curvePoolBooster.address)).to.equal( - parseUnits("13") - ); - expect(await ousd.balanceOf(curvePoolBooster.address)).to.equal( - parseUnits("13") - ); + await dealOETHAndCreateCampaign(); await curvePoolBooster.connect(sStrategist).setCampaignId(12); @@ -244,6 +172,8 @@ describe("ForkTest: CurvePoolBooster", function () { .manageNumberOfPeriods(2, parseUnits("0.1"), 0); }); + // J'en suis ici! + it("Should manage reward per voter", async () => { const { curvePoolBooster, ousd, wousd } = fixture; const { multichainStrategistAddr } = await getNamedAccounts(); @@ -251,28 +181,7 @@ describe("ForkTest: CurvePoolBooster", function () { const sStrategist = await ethers.provider.getSigner( multichainStrategistAddr ); - - // Deal OETH and ETH to pool booster - await ousd - .connect(woethSigner) - .transfer(curvePoolBooster.address, parseUnits("10")); - expect(await ousd.balanceOf(curvePoolBooster.address)).to.equal( - parseUnits("10") - ); - await sStrategist.sendTransaction({ - to: curvePoolBooster.address, - value: parseUnits("1"), - }); - - await curvePoolBooster - .connect(sStrategist) - .createCampaign( - 4, - 10, - [addresses.mainnet.ConvexVoter], - parseUnits("0.1"), - 0 - ); + await dealOETHAndCreateCampaign(); // Deal new OETH to pool booster await ousd @@ -281,9 +190,6 @@ describe("ForkTest: CurvePoolBooster", function () { expect(await ousd.balanceOf(curvePoolBooster.address)).to.equal( parseUnits("13") ); - expect(await ousd.balanceOf(curvePoolBooster.address)).to.equal( - parseUnits("13") - ); await curvePoolBooster.connect(sStrategist).setCampaignId(12); @@ -395,6 +301,18 @@ describe("ForkTest: CurvePoolBooster", function () { multichainStrategistAddr ); + await expect( + curvePoolBooster + .connect(sStrategist) + .createCampaign( + 0, + 10, + [addresses.mainnet.ConvexVoter], + parseUnits("0.1"), + 0 + ) + ).to.be.revertedWith("Invalid number of periods"); + await curvePoolBooster.connect(sStrategist).setCampaignId(12); await expect( @@ -411,6 +329,18 @@ describe("ForkTest: CurvePoolBooster", function () { multichainStrategistAddr ); + await expect( + curvePoolBooster + .connect(sStrategist) + .createCampaign( + 4, + 0, + [addresses.mainnet.ConvexVoter], + parseUnits("0.1"), + 0 + ) + ).to.be.revertedWith("Invalid reward per vote"); + await curvePoolBooster.connect(sStrategist).setCampaignId(12); await expect(