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))
+    })
+})