From fa362245ae4441c69fa38487e01496bbeae90211 Mon Sep 17 00:00:00 2001 From: Keno <Clearwood@users.noreply.github.com> Date: Tue, 28 Sep 2021 17:48:04 -0400 Subject: [PATCH 1/2] Initial Implementation ALCXStrategy --- .../implementations/ALCXStrategy.sol | 43 +++++++++++++++++++ 1 file changed, 43 insertions(+) create mode 100644 contracts/strategies/implementations/ALCXStrategy.sol diff --git a/contracts/strategies/implementations/ALCXStrategy.sol b/contracts/strategies/implementations/ALCXStrategy.sol new file mode 100644 index 00000000..e6098abb --- /dev/null +++ b/contracts/strategies/implementations/ALCXStrategy.sol @@ -0,0 +1,43 @@ +// SPDX-License-Identifier: MIT + +pragma solidity 0.6.12; +pragma experimental ABIEncoderV2; + +import "../BaseStrategy.sol"; + +interface IStakingPool { + function deposit(uint256 _poolId, uint256 _depositAmount) external; + function claim(uint256 _poolId) external; + function withdraw(uint256 _poolId, uint256 _withdrawAmount) external; + function getStakeTotalUnclaimed(address _account, uint256 _poolId) external view returns (uint256); + function exit(uint256 _poolId) external; +} + +contract ALCXStrategy is BaseStrategy { + IStakingPool public constant STAKING_POOL = IStakingPool(0xAB8e74017a8Cc7c15FFcCd726603790d26d7DeCa); + uint256 private constant POOL_ID = 1; + + constructor(BaseStrategyParams memory baseStrategyParams) public BaseStrategy(baseStrategyParams) { + IERC20(baseStrategyParams.token).approve(address(STAKING_POOL), type(uint256).max); + } + + function _skim(uint256 amount) internal override { + STAKING_POOL.deposit(POOL_ID, amount); + } + + function _harvest(uint256 balance) internal override returns (int256) { + STAKING_POOL.claim(POOL_ID); + // ALCX Staking Pool can't report a loss so no need to check for keep < total case + return int256(0); + } + + function _withdraw(uint256 amount) internal override { + uint256 unclaimedRewards = STAKING_POOL.getStakeTotalUnclaimed(address(this), POOL_ID); + uint256 withdrawAmount = amount.sub(unclaimedRewards); + STAKING_POOL.withdraw(withdrawAmount); + } + + function _exit() internal override { + STAKING_POOL.exit(POOL_ID); + } +} From 0be1a85d567a1dd95f7fb3ec396273ab3455ad01 Mon Sep 17 00:00:00 2001 From: Keno <Clearwood@users.noreply.github.com> Date: Thu, 30 Sep 2021 19:08:06 -0400 Subject: [PATCH 2/2] Added Test file --- test/ALCXStrategy.js | 100 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 100 insertions(+) create mode 100644 test/ALCXStrategy.js diff --git a/test/ALCXStrategy.js b/test/ALCXStrategy.js new file mode 100644 index 00000000..17403385 --- /dev/null +++ b/test/ALCXStrategy.js @@ -0,0 +1,100 @@ +const { ADDRESS_ZERO, setMasterContractApproval, createFixture, getBigNumber, advanceTime, advanceTimeAndBlock } = require("./utilities") +const { expect } = require("chai") +const { ethers, network } = require("hardhat") + +let cmd, fixture + +describe.only("ALCXStrategy", async function () { + const _alcx = "0xdBdb4d16EdA451D0503b854CF79D55697F90c8DF" + const factory = "0xc35DADB65012eC5796536bD9864eD8773aBc74C4" + + before(async function () { + this.timeout(30000) + + await network.provider.request({ + method: "hardhat_reset", + params: [ + { + forking: { + jsonRpcUrl: `https://eth-mainnet.alchemyapi.io/v2/${process.env.ALCHEMY_API_KEY}`, + blockNumber: 13316576, + }, + }, + ], + }) + + console.log("hey") + + fixture = await createFixture(deployments, this, async (cmd) => { + await cmd.deploy("weth9", "WETH9Mock") + await cmd.deploy("bentoBox", "BentoBoxMock", this.weth9.address) + await cmd.deploy("alcxStrategy", "ALCXStrategy", [ + _alcx, + this.bentoBox.address, + this.alice.address, + factory, + this.weth9.address, + ]) + }) + cmd = await fixture() + + const tokenFactory = await ethers.getContractFactory("RevertingERC20Mock") + this.alcx = tokenFactory.attach(_alcx) + + const alcxWhale = "0x000000000000000000000000000000000000dEaD" + await network.provider.request({ method: "hardhat_impersonateAccount", params: [alcxWhale] }) + const signer = await ethers.getSigner(alcxWhale) + await this.alcx.connect(signer).transfer(this.alice.address, getBigNumber(10000, 18)) // 10k ALCX + await this.alcx.approve(this.bentoBox.address, getBigNumber(10000, 18)) + }) + + it("allows to set strategy", async function () { + expect((await this.alcx.balanceOf(this.alice.address)).gt(0), "Polygon not forked") + await this.bentoBox.setStrategy(_alcx, this.alcxStrategy.address) + expect(await this.bentoBox.pendingStrategy(_alcx)).to.be.equal(this.alcxStrategy.address) + await advanceTime(1209600, ethers) + await this.bentoBox.setStrategy(_alcx, this.alcxStrategy.address) + expect(await this.bentoBox.strategy(_alcx)).to.be.equal(this.alcxStrategy.address) + await this.bentoBox.setStrategyTargetPercentage(_alcx, 80) + expect((await this.bentoBox.strategyData(_alcx)).targetPercentage).to.be.equal(80) + await this.bentoBox.deposit(_alcx, this.alice.address, this.alice.address, getBigNumber(10000, 18), 0) // 5 mil + expect((await this.bentoBox.strategyData(_alcx)).balance).to.be.equal(0) + await this.bentoBox.harvest(_alcx, true, 0) + expect((await this.bentoBox.strategyData(_alcx)).balance).to.be.equal(getBigNumber(8000, 18)) + }).timeout(30000) + + it("reports a profit", async function () { + const oldBalance = (await this.bentoBox.totals(_alcx)).elastic + await advanceTimeAndBlock(1209600, ethers) + await this.alcxStrategy.safeHarvest(getBigNumber(10000000, 18), false, 0, false) + const newBalance = (await this.bentoBox.totals(_alcx)).elastic + expect(oldBalance.lt(newBalance)) + }) + + it("rebalances", async function () { + await this.bentoBox.withdraw(_alcx, this.alice.address, this.alice.address, getBigNumber(2000, 18), 0) // withdraw a mil + const oldBalance = await this.alcx.balanceOf(this.bentoBox.address) + const oldStrategyAllocation = (await this.bentoBox.strategyData(_alcx)).balance + await this.alcxStrategy.safeHarvest(0, true, 0, false) + const newBalance = await this.alcx.balanceOf(this.bentoBox.address) + const newStrategyAllocation = (await this.bentoBox.strategyData(_alcx)).balance + expect(oldBalance.lt(newBalance)) + expect(oldStrategyAllocation.gt(newStrategyAllocation)) + }) + + it("exits", async function () { + await cmd.deploy("alcxStrategy2", "ALCXStrategy", [ + _alcx, + this.bentoBox.address, + this.alice.address, + factory, + this.weth9.address, + ]) + await this.bentoBox.setStrategy(_alcx, this.alcxStrategy2.address) + await advanceTime(1209600, ethers) + await this.bentoBox.setStrategy(_alcx, this.alcxStrategy2.address) + const balance = await this.alcx.balanceOf(this.bentoBox.address) + const elastic = (await this.bentoBox.totals(_alcx)).elastic + expect(balance.eq(elastic)) + }) +})