From cd57f104aa9af4caef467378d0d6be82dccd52a2 Mon Sep 17 00:00:00 2001 From: Daniel Graczer Date: Wed, 21 Sep 2022 17:11:26 +0700 Subject: [PATCH 1/7] Fixed mint bug and small improvements Details: 1. Fixed mint bug 2. Changed swap path in reapReward to omit WAvax for Stargate 3. Changed ERC20 methods to their safe version 4. Fixed up failing testcases 5. Fixed some grammar mistakes, auto-ordered imports --- .../common/bases/PortfolioBaseUpgradeable.sol | 49 ++-- .../common/bases/StrategyBaseUpgradeable.sol | 41 +++- contracts/common/interfaces/IInvestable.sol | 1 + .../PercentageAllocation.sol | 4 +- contracts/strategies/cash/Cash.sol | 4 +- contracts/strategies/stargate/Readme.md | 4 +- contracts/strategies/stargate/Stargate.sol | 9 +- contracts/strategies/traderjoe/TraderJoe.sol | 8 +- .../tests/strategies/stargate/StargateV2.sol | 4 +- .../strategies/traderjoe/TraderJoeV2.sol | 5 +- hardhat.config.ts | 3 + test/portfolio/Unified.test.ts | 2 +- test/portfolio/UnifiedAllocations.test.ts | 2 +- test/portfolio/UnifiedDeposit.test.ts | 166 +++++++++---- test/portfolio/UnifiedERC165.test.ts | 2 +- test/portfolio/UnifiedInvestable.test.ts | 6 +- test/portfolio/UnifiedOwnable.test.ts | 14 +- test/portfolio/UnifiedRebalance.test.ts | 114 ++++++--- test/portfolio/UnifiedUpgradeable.test.ts | 6 +- test/portfolio/UnifiedWithdraw.test.ts | 219 ++++++++++-------- .../PercentageAllocation.test.ts | 12 +- test/strategy/Unified.test.ts | 6 +- test/strategy/UnifiedDeposit.test.ts | 100 ++++++-- test/strategy/UnifiedERC165.test.ts | 2 +- test/strategy/UnifiedFee.test.ts | 24 +- test/strategy/UnifiedOwnable.test.ts | 18 +- test/strategy/UnifiedReapReward.test.ts | 6 +- test/strategy/UnifiedUpgradeable.test.ts | 6 +- test/strategy/UnifiedWithdraw.test.ts | 130 +++++++---- test/strategy/cash/Cash.test.ts | 6 +- test/strategy/stargate/Stargate.test.ts | 14 +- test/strategy/traderjoe/TraderJoe.test.ts | 8 +- 32 files changed, 647 insertions(+), 348 deletions(-) diff --git a/contracts/common/bases/PortfolioBaseUpgradeable.sol b/contracts/common/bases/PortfolioBaseUpgradeable.sol index 34ed02b6..6c36cb68 100644 --- a/contracts/common/bases/PortfolioBaseUpgradeable.sol +++ b/contracts/common/bases/PortfolioBaseUpgradeable.sol @@ -216,34 +216,31 @@ abstract contract PortfolioBaseUpgradeable is if (amount == 0) revert ZeroAmountDeposited(); // check investment limits - uint256 totalEquity; + // the underlying defi protocols might take fees, but for limit check we can safely ignore it + uint256 equityValuationBeforeInvestment = getEquityValuation( + true, + false + ); uint256 userEquity; uint256 investmentTokenSupply = getInvestmentTokenSupply(); if (investmentTokenSupply != 0) { - totalEquity = getEquityValuation(true, false); - uint256 investmentTokenBalance = getInvestmentTokenBalanceOf( investmentTokenReceiver ); userEquity = - (totalEquity * investmentTokenBalance) / + (equityValuationBeforeInvestment * investmentTokenBalance) / investmentTokenSupply; } - checkTotalInvestmentLimit(amount, totalEquity); + checkTotalInvestmentLimit(amount, equityValuationBeforeInvestment); checkInvestmentLimitPerAddress(amount, userEquity); + // transfering deposit tokens from the user depositToken.safeTransferFrom(_msgSender(), address(this), amount); - uint256 equity = getEquityValuation(true, false); - uint256 investmentTokenTotalSupply = getInvestmentTokenSupply(); - investmentToken.mint( - investmentTokenReceiver, - InvestableLib.calculateMintAmount( - equity, - amount, - investmentTokenTotalSupply - ) - ); + + // 1. emitting event for portfolios at the higher level first + // 2. emitting the deposit amount versus the actual invested amount emit Deposit(_msgSender(), investmentTokenReceiver, amount); + for (uint256 i = 0; i < investableDescs.length; i++) { uint256 embeddedAmount = (amount * investableDescs[i].allocationPercentage) / @@ -260,6 +257,26 @@ abstract contract PortfolioBaseUpgradeable is params ); } + + // calculating the actual amount invested into the defi protocol + uint256 equityValuationAfterInvestment = getEquityValuation( + true, + false + ); + uint256 actualInvested = equityValuationAfterInvestment - + equityValuationBeforeInvestment; + if (actualInvested == 0) revert ZeroAmountInvested(); + + // minting should be based on the actual amount invested versus the deposited amount + // to take defi fees and losses into consideration + investmentToken.mint( + investmentTokenReceiver, + InvestableLib.calculateMintAmount( + equityValuationBeforeInvestment, + actualInvested, + investmentTokenSupply + ) + ); } function withdraw( @@ -375,7 +392,7 @@ abstract contract PortfolioBaseUpgradeable is ); if (depositAmount != 0) { - depositToken.approve( + depositToken.safeApprove( address(embeddedInvestable), depositAmount ); diff --git a/contracts/common/bases/StrategyBaseUpgradeable.sol b/contracts/common/bases/StrategyBaseUpgradeable.sol index 3116d239..413d7c5f 100644 --- a/contracts/common/bases/StrategyBaseUpgradeable.sol +++ b/contracts/common/bases/StrategyBaseUpgradeable.sol @@ -104,35 +104,52 @@ abstract contract StrategyBaseUpgradeable is if (amount == 0) revert ZeroAmountDeposited(); // check investment limits - uint256 totalEquity; + // the underlying defi protocols might take fees, but for limit check we can safely ignore it + uint256 equityValuationBeforeInvestment = getEquityValuation( + true, + false + ); uint256 userEquity; uint256 investmentTokenSupply = getInvestmentTokenSupply(); if (investmentTokenSupply != 0) { - totalEquity = getEquityValuation(true, false); - uint256 investmentTokenBalance = getInvestmentTokenBalanceOf( investmentTokenReceiver ); userEquity = - (totalEquity * investmentTokenBalance) / + (equityValuationBeforeInvestment * investmentTokenBalance) / investmentTokenSupply; } - checkTotalInvestmentLimit(amount, totalEquity); + checkTotalInvestmentLimit(amount, equityValuationBeforeInvestment); checkInvestmentLimitPerAddress(amount, userEquity); + // transfering deposit tokens from the user depositToken.safeTransferFrom(_msgSender(), address(this), amount); - uint256 equity = getEquityValuation(true, false); - uint256 investmentTokenTotalSupply = getInvestmentTokenSupply(); + // investing into the underlying defi protocol + _deposit(amount, params); + + // calculating the actual amount invested into the defi protocol + uint256 equityValuationAfterInvestment = getEquityValuation( + true, + false + ); + + uint256 actualInvested = equityValuationAfterInvestment - + equityValuationBeforeInvestment; + if (actualInvested == 0) revert ZeroAmountInvested(); + + // minting should be based on the actual amount invested versus the deposited amount + // to take defi fees and losses into consideration investmentToken.mint( investmentTokenReceiver, InvestableLib.calculateMintAmount( - equity, - amount, - investmentTokenTotalSupply + equityValuationBeforeInvestment, + actualInvested, + investmentTokenSupply ) ); - _deposit(amount, params); + + // emitting the deposit amount versus the actual invested amount emit Deposit(_msgSender(), investmentTokenReceiver, amount); } @@ -365,7 +382,7 @@ abstract contract StrategyBaseUpgradeable is swapService_.router ); - IERC20Upgradeable(path[0]).approve( + IERC20Upgradeable(path[0]).safeApprove( address(traderjoeRouter), amountIn ); diff --git a/contracts/common/interfaces/IInvestable.sol b/contracts/common/interfaces/IInvestable.sol index 0094b2a0..00e08c22 100644 --- a/contracts/common/interfaces/IInvestable.sol +++ b/contracts/common/interfaces/IInvestable.sol @@ -8,6 +8,7 @@ import "./IInvestmentToken.sol"; interface IInvestable is IAum, IFee { error ZeroAmountDeposited(); + error ZeroAmountInvested(); error ZeroAmountWithdrawn(); event Deposit( diff --git a/contracts/portfolios/percentageAllocation/PercentageAllocation.sol b/contracts/portfolios/percentageAllocation/PercentageAllocation.sol index a1c740e0..83a6f768 100644 --- a/contracts/portfolios/percentageAllocation/PercentageAllocation.sol +++ b/contracts/portfolios/percentageAllocation/PercentageAllocation.sol @@ -11,12 +11,12 @@ contract PercentageAllocation is { // solhint-disable-next-line const-name-snakecase string public constant name = - "block42.percentage_allocation_portfolio.percentage_allocation_portfolio_v1.0.0"; + "block42.percentage_allocation_portfolio.percentage_allocation_portfolio_v1.0.1"; // solhint-disable-next-line const-name-snakecase string public constant humanReadableName = "Percentage allocation portfolio"; // solhint-disable-next-line const-name-snakecase - string public constant version = "1.0.0"; + string public constant version = "1.0.1"; /// @custom:oz-upgrades-unsafe-allow constructor constructor() { diff --git a/contracts/strategies/cash/Cash.sol b/contracts/strategies/cash/Cash.sol index fc712e1d..3490174e 100644 --- a/contracts/strategies/cash/Cash.sol +++ b/contracts/strategies/cash/Cash.sol @@ -8,11 +8,11 @@ import "@openzeppelin/contracts-upgradeable/proxy/utils/UUPSUpgradeable.sol"; contract Cash is UUPSUpgradeable, StrategyOwnablePausableBaseUpgradeable { // solhint-disable-next-line const-name-snakecase - string public constant name = "block42.cash_strategy.cash_strategy_v1.0.0"; + string public constant name = "block42.cash_strategy.cash_strategy_v1.0.1"; // solhint-disable-next-line const-name-snakecase string public constant humanReadableName = "Cash strategy"; // solhint-disable-next-line const-name-snakecase - string public constant version = "1.0.0"; + string public constant version = "1.0.1"; /// @custom:oz-upgrades-unsafe-allow constructor constructor() { diff --git a/contracts/strategies/stargate/Readme.md b/contracts/strategies/stargate/Readme.md index efa119a0..6f43911d 100644 --- a/contracts/strategies/stargate/Readme.md +++ b/contracts/strategies/stargate/Readme.md @@ -8,8 +8,10 @@ This strategy invests USDC into USDC or USDT pool of Stargate on Avalanche. ## High priority -1. solve the delta credit/liqidity issue +1. Solve the delta credit/liqidity issue. ## Medium priority +1. Expose 2 methods to be able to change the swapping path for deposit and reap reward. + ## Low priority diff --git a/contracts/strategies/stargate/Stargate.sol b/contracts/strategies/stargate/Stargate.sol index e7350cb0..ee34de86 100644 --- a/contracts/strategies/stargate/Stargate.sol +++ b/contracts/strategies/stargate/Stargate.sol @@ -18,11 +18,11 @@ contract Stargate is UUPSUpgradeable, StrategyOwnablePausableBaseUpgradeable { // solhint-disable-next-line const-name-snakecase string public constant name = - "brokkr.stargate_strategy.stargate_strategy_v1.0.0"; + "brokkr.stargate_strategy.stargate_strategy_v1.0.1"; // solhint-disable-next-line const-name-snakecase string public constant humanReadableName = "Stargate Strategy"; // solhint-disable-next-line const-name-snakecase - string public constant version = "1.0.0"; + string public constant version = "1.0.1"; /// @custom:oz-upgrades-unsafe-allow constructor constructor() { @@ -90,7 +90,7 @@ contract Stargate is UUPSUpgradeable, StrategyOwnablePausableBaseUpgradeable { uint256 lpBalanceBefore = strategyStorage.lpToken.balanceOf( address(this) ); - strategyStorage.poolDepositToken.approve( + strategyStorage.poolDepositToken.safeApprove( address(strategyStorage.router), amount ); @@ -105,7 +105,7 @@ contract Stargate is UUPSUpgradeable, StrategyOwnablePausableBaseUpgradeable { uint256 lpBalanceIncrement = lpBalanceAfter - lpBalanceBefore; - strategyStorage.lpToken.approve( + strategyStorage.lpToken.safeApprove( address(strategyStorage.lpStaking), lpBalanceIncrement ); @@ -170,7 +170,6 @@ contract Stargate is UUPSUpgradeable, StrategyOwnablePausableBaseUpgradeable { address[] memory path = new address[](3); path[0] = address(strategyStorage.stgToken); - path[1] = address(InvestableLib.WAVAX); path[2] = address(depositToken); swapExactTokensForTokens( diff --git a/contracts/strategies/traderjoe/TraderJoe.sol b/contracts/strategies/traderjoe/TraderJoe.sol index 9e250deb..76bf1bdd 100644 --- a/contracts/strategies/traderjoe/TraderJoe.sol +++ b/contracts/strategies/traderjoe/TraderJoe.sol @@ -17,11 +17,11 @@ contract TraderJoe is UUPSUpgradeable, StrategyOwnablePausableBaseUpgradeable { // solhint-disable-next-line const-name-snakecase string public constant name = - "brokkr.traderjoe_strategy.traderjoe_strategy_v1.0.0"; + "brokkr.traderjoe_strategy.traderjoe_strategy_v1.0.1"; // solhint-disable-next-line const-name-snakecase string public constant humanReadableName = "TraderJoe Strategy"; // solhint-disable-next-line const-name-snakecase - string public constant version = "1.0.0"; + string public constant version = "1.0.1"; /// @custom:oz-upgrades-unsafe-allow constructor constructor() { @@ -94,11 +94,11 @@ contract TraderJoe is UUPSUpgradeable, StrategyOwnablePausableBaseUpgradeable { ); uint256 depositTokenDesired = amount - swapAmount; - strategyStorage.pairDepositToken.approve( + strategyStorage.pairDepositToken.safeApprove( address(strategyStorage.router), pairDepositTokenDesired ); - depositToken.approve( + depositToken.safeApprove( address(strategyStorage.router), depositTokenDesired ); diff --git a/contracts/tests/strategies/stargate/StargateV2.sol b/contracts/tests/strategies/stargate/StargateV2.sol index 92866ffc..ff73b72f 100644 --- a/contracts/tests/strategies/stargate/StargateV2.sol +++ b/contracts/tests/strategies/stargate/StargateV2.sol @@ -90,7 +90,7 @@ contract StargateV2 is UUPSUpgradeable, StrategyOwnablePausableBaseUpgradeable { uint256 lpBalanceBefore = strategyStorage.lpToken.balanceOf( address(this) ); - strategyStorage.poolDepositToken.approve( + strategyStorage.poolDepositToken.safeApprove( address(strategyStorage.router), amount ); @@ -105,7 +105,7 @@ contract StargateV2 is UUPSUpgradeable, StrategyOwnablePausableBaseUpgradeable { uint256 lpBalanceIncrement = lpBalanceAfter - lpBalanceBefore; - strategyStorage.lpToken.approve( + strategyStorage.lpToken.safeApprove( address(strategyStorage.lpStaking), lpBalanceIncrement ); diff --git a/contracts/tests/strategies/traderjoe/TraderJoeV2.sol b/contracts/tests/strategies/traderjoe/TraderJoeV2.sol index d66cc747..33fb8a7b 100644 --- a/contracts/tests/strategies/traderjoe/TraderJoeV2.sol +++ b/contracts/tests/strategies/traderjoe/TraderJoeV2.sol @@ -15,6 +15,7 @@ contract TraderJoeV2 is { using SafeERC20Upgradeable for IInvestmentToken; using SafeERC20Upgradeable for IERC20Upgradeable; + using SafeERC20Upgradeable for ITraderJoePair; error InvalidTraderJoeLpToken(); @@ -97,11 +98,11 @@ contract TraderJoeV2 is ); uint256 depositTokenDesired = amount - swapAmount; - strategyStorage.pairDepositToken.approve( + strategyStorage.pairDepositToken.safeApprove( address(strategyStorage.router), pairDepositTokenDesired ); - depositToken.approve( + depositToken.safeApprove( address(strategyStorage.router), depositTokenDesired ); diff --git a/hardhat.config.ts b/hardhat.config.ts index 04f2c72d..459f3217 100644 --- a/hardhat.config.ts +++ b/hardhat.config.ts @@ -63,6 +63,9 @@ const config: HardhatUserConfig = { enabled: process.env.REPORT_GAS !== undefined, currency: "USD", }, + mocha: { + timeout: 90000, + }, // contractSizer: { // alphaSort: true, // runOnCompile: true, diff --git a/test/portfolio/Unified.test.ts b/test/portfolio/Unified.test.ts index eca39373..8e8110e6 100644 --- a/test/portfolio/Unified.test.ts +++ b/test/portfolio/Unified.test.ts @@ -1,5 +1,5 @@ -import { ethers, network } from "hardhat" import { takeSnapshot } from "@nomicfoundation/hardhat-network-helpers" +import { ethers, network } from "hardhat" import { TokenAddrs, WhaleAddrs } from "../shared/addresses" import { getTokenContract } from "../shared/contracts" import { testAllocations } from "./UnifiedAllocations.test" diff --git a/test/portfolio/UnifiedAllocations.test.ts b/test/portfolio/UnifiedAllocations.test.ts index 9ae896fd..3fab25ae 100644 --- a/test/portfolio/UnifiedAllocations.test.ts +++ b/test/portfolio/UnifiedAllocations.test.ts @@ -54,7 +54,7 @@ export function testAllocations() { ) }) - it("should success when the sum of target investable allocations equals to 100% and the length of target investable allocations equals to the length of investables", async function () { + it("should succeed when the sum of target investable allocations equals to 100% and the length of target investable allocations equals to the length of investables", async function () { const investableLength = (await this.portfolio.getInvestables()).length let allocations: number[] = [100000] for (let i = 1; i < investableLength; i++) { diff --git a/test/portfolio/UnifiedDeposit.test.ts b/test/portfolio/UnifiedDeposit.test.ts index 8ba84444..6cc3c2fc 100644 --- a/test/portfolio/UnifiedDeposit.test.ts +++ b/test/portfolio/UnifiedDeposit.test.ts @@ -6,7 +6,7 @@ import { getErrorRange } from "../shared/utils" export function testDeposit() { describe("Deposit", async function () { - it("should success when a single user deposits USDC that he/she has - 0", async function () { + it("should succeed when a single user deposits USDC that he/she has - integer amount", async function () { await this.usdc.connect(this.user0).approve(this.portfolio.address, ethers.utils.parseUnits("3000", 6)) await expect( this.portfolio.connect(this.user0).deposit(ethers.utils.parseUnits("3000", 6), this.user0.address, []) @@ -15,15 +15,21 @@ export function testDeposit() { .withArgs(this.user0.address, this.user0.address, ethers.utils.parseUnits("3000", 6)) expect(await this.usdc.balanceOf(this.user0.address)).to.equal(ethers.utils.parseUnits("7000", 6)) - expect(await this.investmentToken.balanceOf(this.user0.address)).to.equal(ethers.utils.parseUnits("3000", 6)) - expect(await this.portfolio.getInvestmentTokenSupply()).to.equal(ethers.utils.parseUnits("3000", 6)) + expect(await this.investmentToken.balanceOf(this.user0.address)).to.be.approximately( + ethers.utils.parseUnits("3000", 6), + getErrorRange(ethers.utils.parseUnits("3000", 6)) + ) + expect(await this.portfolio.getInvestmentTokenSupply()).to.be.approximately( + ethers.utils.parseUnits("3000", 6), + getErrorRange(ethers.utils.parseUnits("3000", 6)) + ) expect(await this.portfolio.getEquityValuation(true, false)).to.be.approximately( ethers.utils.parseUnits("3000", 6), getErrorRange(ethers.utils.parseUnits("3000", 6)) ) }) - it("should success when a single user deposits USDC that he/she has - 1", async function () { + it("should succeed when a single user deposits USDC that he/she has - fractional amount", async function () { await this.usdc.connect(this.user0).approve(this.portfolio.address, ethers.utils.parseUnits("3701.810393", 6)) await expect( this.portfolio.connect(this.user0).deposit(ethers.utils.parseUnits("3701.810393", 6), this.user0.address, []) @@ -32,10 +38,14 @@ export function testDeposit() { .withArgs(this.user0.address, this.user0.address, ethers.utils.parseUnits("3701.810393", 6)) expect(await this.usdc.balanceOf(this.user0.address)).to.equal(ethers.utils.parseUnits("6298.189607", 6)) - expect(await this.investmentToken.balanceOf(this.user0.address)).to.equal( - ethers.utils.parseUnits("3701.810393", 6) + expect(await this.investmentToken.balanceOf(this.user0.address)).to.be.approximately( + ethers.utils.parseUnits("3701.810393", 6), + getErrorRange(ethers.utils.parseUnits("3701.810393", 6)) + ) + expect(await this.portfolio.getInvestmentTokenSupply()).to.be.approximately( + ethers.utils.parseUnits("3701.810393", 6), + getErrorRange(ethers.utils.parseUnits("3701.810393", 6)) ) - expect(await this.portfolio.getInvestmentTokenSupply()).to.equal(ethers.utils.parseUnits("3701.810393", 6)) expect(await this.portfolio.getEquityValuation(true, false)).to.be.approximately( ethers.utils.parseUnits("3701.810393", 6), getErrorRange(ethers.utils.parseUnits("3701.810393", 6)) @@ -95,7 +105,7 @@ export function testDeposit() { expect(await this.portfolio.getEquityValuation(true, false)).to.equal(0) }) - it("should success when a single user deposits USDC that he/she has and another user deposited into investable directly before that", async function () { + it("should succeed when a single user deposits USDC that he/she has and another user deposited into investable directly before that", async function () { const investables = await this.portfolio.getInvestables() const investable = await ethers.getContractAt(investableAbi, await investables[0].investable) const investableInvestmentToken = await ethers.getContractAt(erc20Abi, await investable.getInvestmentToken()) @@ -116,11 +126,21 @@ export function testDeposit() { expect(await this.usdc.balanceOf(this.user0.address)).to.equal(ethers.utils.parseUnits("7000", 6)) expect(await this.usdc.balanceOf(this.user1.address)).to.equal(ethers.utils.parseUnits("7000", 6)) - expect(await this.investmentToken.balanceOf(this.user0.address)).to.equal(ethers.utils.parseUnits("3000", 6)) - expect(await investableInvestmentToken.balanceOf(this.user1.address)).to.equal(ethers.utils.parseUnits("3000", 6)) - expect(await this.portfolio.getInvestmentTokenSupply()).to.equal(ethers.utils.parseUnits("3000", 6)) - expect(await investable.getInvestmentTokenSupply()).to.equal( - ethers.utils.parseUnits("3000", 6).add(investableDepositAmount) + expect(await this.investmentToken.balanceOf(this.user0.address)).to.be.approximately( + ethers.utils.parseUnits("3000", 6), + getErrorRange(ethers.utils.parseUnits("3000", 6)) + ) + expect(await investableInvestmentToken.balanceOf(this.user1.address)).to.be.approximately( + ethers.utils.parseUnits("3000", 6), + getErrorRange(ethers.utils.parseUnits("3000", 6)) + ) + expect(await this.portfolio.getInvestmentTokenSupply()).to.be.approximately( + ethers.utils.parseUnits("3000", 6), + getErrorRange(ethers.utils.parseUnits("3000", 6)) + ) + expect(await investable.getInvestmentTokenSupply()).to.be.approximately( + ethers.utils.parseUnits("3000", 6).add(investableDepositAmount), + getErrorRange(ethers.utils.parseUnits("3000", 6)) ) expect(await this.portfolio.getEquityValuation(true, false)).to.be.approximately( ethers.utils.parseUnits("3000", 6), @@ -132,7 +152,7 @@ export function testDeposit() { ) }) - it("should success when a single user deposits USDC that he/she has and another user deposits into investable directly after that", async function () { + it("should succeed when a single user deposits USDC that he/she has and another user deposits into investable directly after that", async function () { await this.usdc.connect(this.user0).approve(this.portfolio.address, ethers.utils.parseUnits("3000", 6)) await expect( this.portfolio.connect(this.user0).deposit(ethers.utils.parseUnits("3000", 6), this.user0.address, []) @@ -153,12 +173,18 @@ export function testDeposit() { expect(await this.usdc.balanceOf(this.user0.address)).to.equal(ethers.utils.parseUnits("7000", 6)) expect(await this.usdc.balanceOf(this.user1.address)).to.equal(ethers.utils.parseUnits("7000", 6)) - expect(await this.investmentToken.balanceOf(this.user0.address)).to.equal(ethers.utils.parseUnits("3000", 6)) + expect(await this.investmentToken.balanceOf(this.user0.address)).to.be.approximately( + ethers.utils.parseUnits("3000", 6), + getErrorRange(ethers.utils.parseUnits("3000", 6)) + ) expect(await investableInvestmentToken.balanceOf(this.user1.address)).to.be.approximately( ethers.utils.parseUnits("3000", 6), getErrorRange(ethers.utils.parseUnits("3000", 6)) ) - expect(await this.portfolio.getInvestmentTokenSupply()).to.equal(ethers.utils.parseUnits("3000", 6)) + expect(await this.portfolio.getInvestmentTokenSupply()).to.be.approximately( + ethers.utils.parseUnits("3000", 6), + getErrorRange(ethers.utils.parseUnits("3000", 6)) + ) expect(await investable.getInvestmentTokenSupply()).to.be.approximately( ethers.utils.parseUnits("3000", 6).add(investableDepositAmount), getErrorRange(ethers.utils.parseUnits("3000", 6).add(investableDepositAmount)) @@ -173,7 +199,7 @@ export function testDeposit() { ) }) - it("should success when multiple users deposit USDC that they have - 0", async function () { + it("should succeed when multiple users deposit USDC that they have - integer amount", async function () { // The first user. await this.usdc.connect(this.user0).approve(this.portfolio.address, ethers.utils.parseUnits("30", 6)) await expect(this.portfolio.connect(this.user0).deposit(ethers.utils.parseUnits("30", 6), this.user0.address, [])) @@ -181,8 +207,14 @@ export function testDeposit() { .withArgs(this.user0.address, this.user0.address, ethers.utils.parseUnits("30", 6)) expect(await this.usdc.balanceOf(this.user0.address)).to.equal(ethers.utils.parseUnits("9970", 6)) - expect(await this.investmentToken.balanceOf(this.user0.address)).to.equal(ethers.utils.parseUnits("30", 6)) - expect(await this.portfolio.getInvestmentTokenSupply()).to.equal(ethers.utils.parseUnits("30", 6)) + expect(await this.investmentToken.balanceOf(this.user0.address)).to.be.approximately( + ethers.utils.parseUnits("30", 6), + getErrorRange(ethers.utils.parseUnits("30", 6)) + ) + expect(await this.portfolio.getInvestmentTokenSupply()).to.be.approximately( + ethers.utils.parseUnits("30", 6), + getErrorRange(ethers.utils.parseUnits("30", 6)) + ) expect(await this.portfolio.getEquityValuation(true, false)).to.be.approximately( ethers.utils.parseUnits("30", 6), getErrorRange(ethers.utils.parseUnits("30", 6)) @@ -229,7 +261,7 @@ export function testDeposit() { ) }) - it("should success when multiple users deposit USDC that they have - 1", async function () { + it("should succeed when multiple users deposit USDC that they have - fractional amount", async function () { // The first user. await this.usdc.connect(this.user0).approve(this.portfolio.address, ethers.utils.parseUnits("3701.810393", 6)) await expect( @@ -239,10 +271,14 @@ export function testDeposit() { .withArgs(this.user0.address, this.user0.address, ethers.utils.parseUnits("3701.810393", 6)) expect(await this.usdc.balanceOf(this.user0.address)).to.equal(ethers.utils.parseUnits("6298.189607", 6)) - expect(await this.investmentToken.balanceOf(this.user0.address)).to.equal( - ethers.utils.parseUnits("3701.810393", 6) + expect(await this.investmentToken.balanceOf(this.user0.address)).to.be.approximately( + ethers.utils.parseUnits("3701.810393", 6), + getErrorRange(ethers.utils.parseUnits("3701.810393", 6)) + ) + expect(await this.portfolio.getInvestmentTokenSupply()).to.be.approximately( + ethers.utils.parseUnits("3701.810393", 6), + getErrorRange(ethers.utils.parseUnits("3701.810393", 6)) ) - expect(await this.portfolio.getInvestmentTokenSupply()).to.equal(ethers.utils.parseUnits("3701.810393", 6)) expect(await this.portfolio.getEquityValuation(true, false)).to.be.approximately( ethers.utils.parseUnits("3701.810393", 6), getErrorRange(ethers.utils.parseUnits("3701.810393", 6)) @@ -319,7 +355,10 @@ export function testDeposit() { ethers.utils.parseUnits("3000", 6), getErrorRange(ethers.utils.parseUnits("3000", 6)) ) - expect(await this.portfolio.getInvestmentTokenSupply()).to.equal(ethers.utils.parseUnits("3000", 6)) + expect(await this.portfolio.getInvestmentTokenSupply()).to.be.approximately( + ethers.utils.parseUnits("3000", 6), + getErrorRange(ethers.utils.parseUnits("3000", 6)) + ) expect(await this.portfolio.getEquityValuation(true, false)).to.be.approximately( ethers.utils.parseUnits("3000", 6), getErrorRange(ethers.utils.parseUnits("3000", 6)) @@ -334,7 +373,10 @@ export function testDeposit() { expect(await this.usdc.balanceOf(this.user2.address)).to.equal(ethers.utils.parseUnits("10000", 6)) expect(await this.investmentToken.balanceOf(this.user2.address)).to.equal(0) - expect(await this.portfolio.getInvestmentTokenSupply()).to.equal(ethers.utils.parseUnits("3000", 6)) + expect(await this.portfolio.getInvestmentTokenSupply()).to.be.approximately( + ethers.utils.parseUnits("3000", 6), + getErrorRange(ethers.utils.parseUnits("3000", 6)) + ) expect(await this.portfolio.getEquityValuation(true, false)).to.be.approximately( ethers.utils.parseUnits("3000", 6), getErrorRange(ethers.utils.parseUnits("3000", 6)) @@ -351,8 +393,14 @@ export function testDeposit() { .withArgs(this.user0.address, this.user0.address, ethers.utils.parseUnits("3000", 6)) expect(await this.usdc.balanceOf(this.user0.address)).to.equal(ethers.utils.parseUnits("7000", 6)) - expect(await this.investmentToken.balanceOf(this.user0.address)).to.equal(ethers.utils.parseUnits("3000", 6)) - expect(await this.portfolio.getInvestmentTokenSupply()).to.equal(ethers.utils.parseUnits("3000", 6)) + expect(await this.investmentToken.balanceOf(this.user0.address)).to.be.approximately( + ethers.utils.parseUnits("3000", 6), + getErrorRange(ethers.utils.parseUnits("3000", 6)) + ) + expect(await this.portfolio.getInvestmentTokenSupply()).to.be.approximately( + ethers.utils.parseUnits("3000", 6), + getErrorRange(ethers.utils.parseUnits("3000", 6)) + ) expect(await this.portfolio.getEquityValuation(true, false)).to.be.approximately( ethers.utils.parseUnits("3000", 6), getErrorRange(ethers.utils.parseUnits("3000", 6)) @@ -366,7 +414,10 @@ export function testDeposit() { expect(await this.usdc.balanceOf(this.user1.address)).to.equal(ethers.utils.parseUnits("10000", 6)) expect(await this.investmentToken.balanceOf(this.user1.address)).to.equal(0) - expect(await this.portfolio.getInvestmentTokenSupply()).to.equal(ethers.utils.parseUnits("3000", 6)) + expect(await this.portfolio.getInvestmentTokenSupply()).to.be.approximately( + ethers.utils.parseUnits("3000", 6), + getErrorRange(ethers.utils.parseUnits("3000", 6)) + ) expect(await this.portfolio.getEquityValuation(true, false)).to.be.approximately( ethers.utils.parseUnits("3000", 6), getErrorRange(ethers.utils.parseUnits("3000", 6)) @@ -394,8 +445,14 @@ export function testDeposit() { .withArgs(this.user1.address, this.user1.address, ethers.utils.parseUnits("30", 6)) expect(await this.usdc.balanceOf(this.user1.address)).to.equal(ethers.utils.parseUnits("9970", 6)) - expect(await this.investmentToken.balanceOf(this.user1.address)).to.equal(ethers.utils.parseUnits("30", 6)) - expect(await this.portfolio.getInvestmentTokenSupply()).to.equal(ethers.utils.parseUnits("30", 6)) + expect(await this.investmentToken.balanceOf(this.user1.address)).to.be.approximately( + ethers.utils.parseUnits("30", 6), + getErrorRange(ethers.utils.parseUnits("30", 6)) + ) + expect(await this.portfolio.getInvestmentTokenSupply()).to.be.approximately( + ethers.utils.parseUnits("30", 6), + getErrorRange(ethers.utils.parseUnits("30", 6)) + ) expect(await this.portfolio.getEquityValuation(true, false)).to.be.approximately( ethers.utils.parseUnits("30", 6), getErrorRange(ethers.utils.parseUnits("30", 6)) @@ -409,7 +466,10 @@ export function testDeposit() { expect(await this.usdc.balanceOf(this.user2.address)).to.equal(ethers.utils.parseUnits("10000", 6)) expect(await this.investmentToken.balanceOf(this.user2.address)).to.equal(0) - expect(await this.portfolio.getInvestmentTokenSupply()).to.equal(ethers.utils.parseUnits("30", 6)) + expect(await this.portfolio.getInvestmentTokenSupply()).to.be.approximately( + ethers.utils.parseUnits("30", 6), + getErrorRange(ethers.utils.parseUnits("30", 6)) + ) expect(await this.portfolio.getEquityValuation(true, false)).to.be.approximately( ethers.utils.parseUnits("30", 6), getErrorRange(ethers.utils.parseUnits("30", 6)) @@ -426,8 +486,14 @@ export function testDeposit() { .withArgs(this.user0.address, this.user0.address, ethers.utils.parseUnits("30", 6)) expect(await this.usdc.balanceOf(this.user0.address)).to.equal(ethers.utils.parseUnits("9970", 6)) - expect(await this.investmentToken.balanceOf(this.user0.address)).to.equal(ethers.utils.parseUnits("30", 6)) - expect(await this.portfolio.getInvestmentTokenSupply()).to.equal(ethers.utils.parseUnits("30", 6)) + expect(await this.investmentToken.balanceOf(this.user0.address)).to.be.approximately( + ethers.utils.parseUnits("30", 6), + getErrorRange(ethers.utils.parseUnits("30", 6)) + ) + expect(await this.portfolio.getInvestmentTokenSupply()).to.be.approximately( + ethers.utils.parseUnits("30", 6), + getErrorRange(ethers.utils.parseUnits("30", 6)) + ) expect(await this.portfolio.getEquityValuation(true, false)).to.be.approximately( ethers.utils.parseUnits("30", 6), getErrorRange(ethers.utils.parseUnits("30", 6)) @@ -471,7 +537,7 @@ export function testDeposit() { ) }) - it("should success when multiple users deposit USDC that they have and another user deposited into investable directly before that", async function () { + it("should succeed when multiple users deposit USDC that they have and another user deposited into investable directly before that", async function () { const investables = await this.portfolio.getInvestables() const investable = await ethers.getContractAt(investableAbi, await investables[0].investable) const investableInvestmentToken = await ethers.getContractAt(erc20Abi, await investable.getInvestmentToken()) @@ -493,9 +559,18 @@ export function testDeposit() { expect(await this.usdc.balanceOf(this.user0.address)).to.equal(ethers.utils.parseUnits("7000", 6)) expect(await this.usdc.balanceOf(this.user2.address)).to.equal(ethers.utils.parseUnits("7000", 6)) - expect(await this.investmentToken.balanceOf(this.user0.address)).to.equal(ethers.utils.parseUnits("3000", 6)) - expect(await investableInvestmentToken.balanceOf(this.user2.address)).to.equal(ethers.utils.parseUnits("3000", 6)) - expect(await this.portfolio.getInvestmentTokenSupply()).to.equal(ethers.utils.parseUnits("3000", 6)) + expect(await this.investmentToken.balanceOf(this.user0.address)).to.be.approximately( + ethers.utils.parseUnits("3000", 6), + getErrorRange(ethers.utils.parseUnits("3000", 6)) + ) + expect(await investableInvestmentToken.balanceOf(this.user2.address)).to.be.approximately( + ethers.utils.parseUnits("3000", 6), + getErrorRange(ethers.utils.parseUnits("3000", 6)) + ) + expect(await this.portfolio.getInvestmentTokenSupply()).to.be.approximately( + ethers.utils.parseUnits("3000", 6), + getErrorRange(ethers.utils.parseUnits("3000", 6)) + ) expect(await investable.getInvestmentTokenSupply()).to.be.approximately( ethers.utils.parseUnits("3000", 6).add(investableDepositAmount), getErrorRange(ethers.utils.parseUnits("3000", 6).add(investableDepositAmount)) @@ -525,7 +600,10 @@ export function testDeposit() { ethers.utils.parseUnits("3000", 6), getErrorRange(ethers.utils.parseUnits("3000", 6)) ) - expect(await investableInvestmentToken.balanceOf(this.user2.address)).to.equal(ethers.utils.parseUnits("3000", 6)) + expect(await investableInvestmentToken.balanceOf(this.user2.address)).to.be.approximately( + ethers.utils.parseUnits("3000", 6), + getErrorRange(ethers.utils.parseUnits("3000", 6)) + ) expect(await this.portfolio.getInvestmentTokenSupply()).to.be.approximately( ethers.utils.parseUnits("6000", 6), getErrorRange(ethers.utils.parseUnits("6000", 6)) @@ -544,7 +622,7 @@ export function testDeposit() { ) }) - it("should success when multiple users deposit USDC that they have and another user deposits into investable directly after that", async function () { + it("should succeed when multiple users deposit USDC that they have and another user deposits into investable directly after that", async function () { // The first user. await this.usdc.connect(this.user0).approve(this.portfolio.address, ethers.utils.parseUnits("3000", 6)) await expect( @@ -554,8 +632,14 @@ export function testDeposit() { .withArgs(this.user0.address, this.user0.address, ethers.utils.parseUnits("3000", 6)) expect(await this.usdc.balanceOf(this.user0.address)).to.equal(ethers.utils.parseUnits("7000", 6)) - expect(await this.investmentToken.balanceOf(this.user0.address)).to.equal(ethers.utils.parseUnits("3000", 6)) - expect(await this.portfolio.getInvestmentTokenSupply()).to.equal(ethers.utils.parseUnits("3000", 6)) + expect(await this.investmentToken.balanceOf(this.user0.address)).to.be.approximately( + ethers.utils.parseUnits("3000", 6), + getErrorRange(ethers.utils.parseUnits("3000", 6)) + ) + expect(await this.portfolio.getInvestmentTokenSupply()).to.be.approximately( + ethers.utils.parseUnits("3000", 6), + getErrorRange(ethers.utils.parseUnits("3000", 6)) + ) expect(await this.portfolio.getEquityValuation(true, false)).to.be.approximately( ethers.utils.parseUnits("3000", 6), getErrorRange(ethers.utils.parseUnits("3000", 6)) diff --git a/test/portfolio/UnifiedERC165.test.ts b/test/portfolio/UnifiedERC165.test.ts index a109cde3..b3f5e136 100644 --- a/test/portfolio/UnifiedERC165.test.ts +++ b/test/portfolio/UnifiedERC165.test.ts @@ -2,7 +2,7 @@ import { expect } from "chai" export function testERC165() { describe("ERC165", async function () { - it("should success to support all interfaces that portfolio implements", async function () { + it("should succeed to support all interfaces that portfolio implements", async function () { expect(await this.portfolio.supportsInterface("0x49147370")).to.equal(true) // IAum expect(await this.portfolio.supportsInterface("0x52f3b8ca")).to.equal(true) // IFee expect(await this.portfolio.supportsInterface("0xa9233731")).to.equal(true) // IInvestable diff --git a/test/portfolio/UnifiedInvestable.test.ts b/test/portfolio/UnifiedInvestable.test.ts index 2ee89e1d..54d49b66 100644 --- a/test/portfolio/UnifiedInvestable.test.ts +++ b/test/portfolio/UnifiedInvestable.test.ts @@ -18,7 +18,7 @@ export function testInvestable() { ) }) - it("should success to add when the investable not exists", async function () { + it("should succeed to add when the investable not exists", async function () { const investableLength = (await this.portfolio.getInvestables()).length let allocations: number[] = [100000] for (let i = 1; i < investableLength + 1; i++) { @@ -65,7 +65,7 @@ export function testInvestable() { ) }) - it("should success to remove when the investable exists and has zero allocation", async function () { + it("should succeed to remove when the investable exists and has zero allocation", async function () { const investableLength = (await this.portfolio.getInvestables()).length if (investableLength <= 1) { @@ -110,7 +110,7 @@ export function testInvestable() { ) }) - it("should success to change when the investable exists", async function () { + it("should succeed to change when the investable exists", async function () { const investablesBefore = await this.portfolio.getInvestables() const investableAddr = await investablesBefore[0].investable diff --git a/test/portfolio/UnifiedOwnable.test.ts b/test/portfolio/UnifiedOwnable.test.ts index f323a4b1..d8a89f3b 100644 --- a/test/portfolio/UnifiedOwnable.test.ts +++ b/test/portfolio/UnifiedOwnable.test.ts @@ -74,7 +74,7 @@ export function testOwnable() { ) }) - it("should success when the owner user sets withdrawal fee to 30%", async function () { + it("should succeed when the owner user sets withdrawal fee to 30%", async function () { expect(await this.portfolio.setDepositFee(30000, [])) .to.emit(this.portfolio, "DepositFeeChange") .withArgs(30000, []) @@ -82,7 +82,7 @@ export function testOwnable() { expect(await this.portfolio.getDepositFee([])).to.equal(30000) }) - it("should success when the owner user sets deposit fee to 30%", async function () { + it("should succeed when the owner user sets deposit fee to 30%", async function () { expect(await this.portfolio.setWithdrawalFee(30000, [])) .to.emit(this.portfolio, "WithdrawalFeeChange") .withArgs(30000, []) @@ -90,7 +90,7 @@ export function testOwnable() { expect(await this.portfolio.getWithdrawalFee([])).to.equal(30000) }) - it("should success when the owner user sets performance fee to 30%", async function () { + it("should succeed when the owner user sets performance fee to 30%", async function () { expect(await this.portfolio.setPerformanceFee(30000, [])) .to.emit(this.portfolio, "PerformanceFeeChange") .withArgs(30000, []) @@ -98,7 +98,7 @@ export function testOwnable() { expect(await this.portfolio.getPerformanceFee([])).to.equal(30000) }) - it("should success when the owner user sets fee receiver", async function () { + it("should succeed when the owner user sets fee receiver", async function () { expect(await this.portfolio.setFeeReceiver(this.user0.address, [])) .to.emit(this.portfolio, "FeeReceiverChange") .withArgs(this.user0.address, []) @@ -106,19 +106,19 @@ export function testOwnable() { expect(await this.portfolio.getFeeReceiver([])).to.equal(this.user0.address) }) - it("should success when the owner user sets investment token", async function () { + it("should succeed when the owner user sets investment token", async function () { expect(await this.portfolio.setInvestmentToken(this.usdc.address)).not.to.be.reverted expect(await this.portfolio.getInvestmentToken()).to.equal(this.usdc.address) }) - it("should success when the owner user sets total investment limit", async function () { + it("should succeed when the owner user sets total investment limit", async function () { expect(await this.portfolio.setTotalInvestmentLimit(0)).not.to.be.reverted expect(await this.portfolio.getTotalInvestmentLimit()).to.equal(0) }) - it("should success when the owner user sets investment limit per address", async function () { + it("should succeed when the owner user sets investment limit per address", async function () { expect(await this.portfolio.setInvestmentLimitPerAddress(0)).not.to.be.reverted expect(await this.portfolio.getInvestmentLimitPerAddress()).to.equal(0) diff --git a/test/portfolio/UnifiedRebalance.test.ts b/test/portfolio/UnifiedRebalance.test.ts index 00ba74bf..897165bb 100644 --- a/test/portfolio/UnifiedRebalance.test.ts +++ b/test/portfolio/UnifiedRebalance.test.ts @@ -7,14 +7,14 @@ import { getErrorRange } from "../shared/utils" export function testRebalance() { describe("Rebalance", async function () { - it("should success when the owner user rebalances - 0", async function () { + it("should succeed when the owner user rebalances - [100%, 0%, 0%] -> [33%, 33%, 34%]", async function () { const investableLength = (await this.portfolio.getInvestables()).length if (investableLength <= 1) { return } - // Set target allocations 100% to the first investable and 0% to the others. e.g. [100%, 0%, 0%] + // Set target allocations 100% to the first investable and 0% to the others. [100%, 0%, 0%] let allocations: number[] = [100000] for (let i = 1; i < investableLength; i++) { allocations.push(0) @@ -27,14 +27,20 @@ export function testRebalance() { await this.portfolio.connect(this.user0).deposit(ethers.utils.parseUnits("10000", 6), this.user0.address, []) expect(await this.usdc.balanceOf(this.user0.address)).to.equal(0) - expect(await this.investmentToken.balanceOf(this.user0.address)).to.equal(ethers.utils.parseUnits("10000", 6)) - expect(await this.portfolio.getInvestmentTokenSupply()).to.equal(ethers.utils.parseUnits("10000", 6)) + expect(await this.investmentToken.balanceOf(this.user0.address)).to.be.approximately( + ethers.utils.parseUnits("10000", 6), + getErrorRange(ethers.utils.parseUnits("10000", 6)) + ) + expect(await this.portfolio.getInvestmentTokenSupply()).to.be.approximately( + ethers.utils.parseUnits("10000", 6), + getErrorRange(ethers.utils.parseUnits("10000", 6)) + ) expect(await this.portfolio.getEquityValuation(true, false)).to.be.approximately( ethers.utils.parseUnits("10000", 6), getErrorRange(ethers.utils.parseUnits("10000", 6)) ) - // Set target allocations approximately equally. e.g. [33%, 33%, 34%] + // Set target allocations approximately equally. [33%, 33%, 34%] allocations = [] const equalAllocation = Math.floor(100 / investableLength - 1) for (let i = 0; i < investableLength - 1; i++) { @@ -56,8 +62,14 @@ export function testRebalance() { expect(await this.portfolio.rebalance(depositParams, withdrawParams)).to.emit(this.portfolio, "Rebalance") expect(await this.usdc.balanceOf(this.user0.address)).to.equal(0) - expect(await this.investmentToken.balanceOf(this.user0.address)).to.equal(ethers.utils.parseUnits("10000", 6)) - expect(await this.portfolio.getInvestmentTokenSupply()).to.equal(ethers.utils.parseUnits("10000", 6)) + expect(await this.investmentToken.balanceOf(this.user0.address)).to.be.approximately( + ethers.utils.parseUnits("10000", 6), + getErrorRange(ethers.utils.parseUnits("10000", 6)) + ) + expect(await this.portfolio.getInvestmentTokenSupply()).to.be.approximately( + ethers.utils.parseUnits("10000", 6), + getErrorRange(ethers.utils.parseUnits("10000", 6)) + ) expect(await this.portfolio.getEquityValuation(true, false)).to.be.approximately( ethers.utils.parseUnits("10000", 6), getErrorRange(ethers.utils.parseUnits("10000", 6)) @@ -81,14 +93,14 @@ export function testRebalance() { } }) - it("should success when the owner user rebalances - 1", async function () { + it("should succeed when the owner user rebalances - [50%, 50%, 0%] -> [33%, 33%, 34%]", async function () { const investableLength = (await this.portfolio.getInvestables()).length if (investableLength <= 1) { return } - // Set target allocations 50% to the first and second investable and 0% to the others. e.g. [50%, 50%, 0%] + // Set target allocations 50% to the first and second investable and 0% to the others. [50%, 50%, 0%] let allocations: number[] = [50000, 50000] for (let i = 2; i < investableLength; i++) { allocations.push(0) @@ -101,14 +113,20 @@ export function testRebalance() { await this.portfolio.connect(this.user0).deposit(ethers.utils.parseUnits("10000", 6), this.user0.address, []) expect(await this.usdc.balanceOf(this.user0.address)).to.equal(0) - expect(await this.investmentToken.balanceOf(this.user0.address)).to.equal(ethers.utils.parseUnits("10000", 6)) - expect(await this.portfolio.getInvestmentTokenSupply()).to.equal(ethers.utils.parseUnits("10000", 6)) + expect(await this.investmentToken.balanceOf(this.user0.address)).to.be.approximately( + ethers.utils.parseUnits("10000", 6), + getErrorRange(ethers.utils.parseUnits("10000", 6)) + ) + expect(await this.portfolio.getInvestmentTokenSupply()).to.be.approximately( + ethers.utils.parseUnits("10000", 6), + getErrorRange(ethers.utils.parseUnits("10000", 6)) + ) expect(await this.portfolio.getEquityValuation(true, false)).to.be.approximately( ethers.utils.parseUnits("10000", 6), getErrorRange(ethers.utils.parseUnits("10000", 6)) ) - // Set target allocations approximately equally. e.g. [33%, 33%, 34%] + // Set target allocations approximately equally. [33%, 33%, 34%] allocations = [] const equalAllocation = Math.floor(100 / investableLength - 1) for (let i = 0; i < investableLength - 1; i++) { @@ -130,8 +148,14 @@ export function testRebalance() { expect(await this.portfolio.rebalance(depositParams, withdrawParams)).to.emit(this.portfolio, "Rebalance") expect(await this.usdc.balanceOf(this.user0.address)).to.equal(0) - expect(await this.investmentToken.balanceOf(this.user0.address)).to.equal(ethers.utils.parseUnits("10000", 6)) - expect(await this.portfolio.getInvestmentTokenSupply()).to.equal(ethers.utils.parseUnits("10000", 6)) + expect(await this.investmentToken.balanceOf(this.user0.address)).to.be.approximately( + ethers.utils.parseUnits("10000", 6), + getErrorRange(ethers.utils.parseUnits("10000", 6)) + ) + expect(await this.portfolio.getInvestmentTokenSupply()).to.be.approximately( + ethers.utils.parseUnits("10000", 6), + getErrorRange(ethers.utils.parseUnits("10000", 6)) + ) expect(await this.portfolio.getEquityValuation(true, false)).to.be.approximately( ethers.utils.parseUnits("10000", 6), getErrorRange(ethers.utils.parseUnits("10000", 6)) @@ -155,7 +179,7 @@ export function testRebalance() { } }) - it("should success when the owner user rebalances and another user deposits into investable directly", async function () { + it("should succeed when the owner user rebalances and another user deposits into investable directly - [50%, 50%, 0%] -> [33%, 33%, 34%]", async function () { const investableLength = (await this.portfolio.getInvestables()).length if (investableLength <= 1) { @@ -170,7 +194,7 @@ export function testRebalance() { await expect(investable.connect(this.user2).deposit(ethers.utils.parseUnits("3000", 6), this.user2.address, [])) .not.to.be.reverted - // Set target allocations 50% to the first and second investable and 0% to the others. e.g. [50%, 50%, 0%] + // Set target allocations 50% to the first and second investable and 0% to the others. [50%, 50%, 0%] let allocations: number[] = [50000, 50000] for (let i = 2; i < investableLength; i++) { allocations.push(0) @@ -183,14 +207,20 @@ export function testRebalance() { await this.portfolio.connect(this.user0).deposit(ethers.utils.parseUnits("10000", 6), this.user0.address, []) expect(await this.usdc.balanceOf(this.user0.address)).to.equal(0) - expect(await this.investmentToken.balanceOf(this.user0.address)).to.equal(ethers.utils.parseUnits("10000", 6)) - expect(await this.portfolio.getInvestmentTokenSupply()).to.equal(ethers.utils.parseUnits("10000", 6)) + expect(await this.investmentToken.balanceOf(this.user0.address)).to.be.approximately( + ethers.utils.parseUnits("10000", 6), + getErrorRange(ethers.utils.parseUnits("10000", 6)) + ) + expect(await this.portfolio.getInvestmentTokenSupply()).to.be.approximately( + ethers.utils.parseUnits("10000", 6), + getErrorRange(ethers.utils.parseUnits("10000", 6)) + ) expect(await this.portfolio.getEquityValuation(true, false)).to.be.approximately( ethers.utils.parseUnits("10000", 6), getErrorRange(ethers.utils.parseUnits("10000", 6)) ) - // Set target allocations approximately equally. e.g. [33%, 33%, 34%] + // Set target allocations approximately equally. [33%, 33%, 34%] allocations = [] const equalAllocation = Math.floor(100 / investableLength - 1) for (let i = 0; i < investableLength - 1; i++) { @@ -213,9 +243,18 @@ export function testRebalance() { expect(await this.usdc.balanceOf(this.user0.address)).to.equal(0) expect(await this.usdc.balanceOf(this.user2.address)).to.equal(ethers.utils.parseUnits("7000", 6)) - expect(await this.investmentToken.balanceOf(this.user0.address)).to.equal(ethers.utils.parseUnits("10000", 6)) - expect(await investableInvestmentToken.balanceOf(this.user2.address)).to.equal(ethers.utils.parseUnits("3000", 6)) - expect(await this.portfolio.getInvestmentTokenSupply()).to.equal(ethers.utils.parseUnits("10000", 6)) + expect(await this.investmentToken.balanceOf(this.user0.address)).to.be.approximately( + ethers.utils.parseUnits("10000", 6), + getErrorRange(ethers.utils.parseUnits("10000", 6)) + ) + expect(await investableInvestmentToken.balanceOf(this.user2.address)).to.be.approximately( + ethers.utils.parseUnits("3000", 6), + getErrorRange(ethers.utils.parseUnits("3000", 6)) + ) + expect(await this.portfolio.getInvestmentTokenSupply()).to.be.approximately( + ethers.utils.parseUnits("10000", 6), + getErrorRange(ethers.utils.parseUnits("10000", 6)) + ) expect(await this.portfolio.getEquityValuation(true, false)).to.be.approximately( ethers.utils.parseUnits("10000", 6), getErrorRange(ethers.utils.parseUnits("10000", 6)) @@ -240,7 +279,7 @@ export function testRebalance() { } }) - it("should success when the owner user rebalances and another user withdraws from investable directly", async function () { + it("should succeed when the owner user rebalances and another user withdraws from investable directly - [50%, 50%, 0%] ->[33%, 33%, 34%]", async function () { const investableLength = (await this.portfolio.getInvestables()).length if (investableLength <= 1) { @@ -255,7 +294,7 @@ export function testRebalance() { await expect(investable.connect(this.user2).deposit(ethers.utils.parseUnits("3000", 6), this.user2.address, [])) .not.to.be.reverted - // Set target allocations 50% to the first and second investable and 0% to the others. e.g. [50%, 50%, 0%] + // Set target allocations 50% to the first and second investable and 0% to the others. [50%, 50%, 0%] let allocations: number[] = [50000, 50000] for (let i = 2; i < investableLength; i++) { allocations.push(0) @@ -268,8 +307,14 @@ export function testRebalance() { await this.portfolio.connect(this.user0).deposit(ethers.utils.parseUnits("10000", 6), this.user0.address, []) expect(await this.usdc.balanceOf(this.user0.address)).to.equal(0) - expect(await this.investmentToken.balanceOf(this.user0.address)).to.equal(ethers.utils.parseUnits("10000", 6)) - expect(await this.portfolio.getInvestmentTokenSupply()).to.equal(ethers.utils.parseUnits("10000", 6)) + expect(await this.investmentToken.balanceOf(this.user0.address)).to.be.approximately( + ethers.utils.parseUnits("10000", 6), + getErrorRange(ethers.utils.parseUnits("10000", 6)) + ) + expect(await this.portfolio.getInvestmentTokenSupply()).to.be.approximately( + ethers.utils.parseUnits("10000", 6), + getErrorRange(ethers.utils.parseUnits("10000", 6)) + ) expect(await this.portfolio.getEquityValuation(true, false)).to.be.approximately( ethers.utils.parseUnits("10000", 6), getErrorRange(ethers.utils.parseUnits("10000", 6)) @@ -281,7 +326,7 @@ export function testRebalance() { await expect(investable.connect(this.user2).withdraw(ethers.utils.parseUnits("1500", 6), this.user2.address, [])) .not.to.be.reverted - // Set target allocations approximately equally. e.g. [33%, 33%, 34%] + // Set target allocations approximately equally. [33%, 33%, 34%] allocations = [] const equalAllocation = Math.floor(100 / investableLength - 1) for (let i = 0; i < investableLength - 1; i++) { @@ -307,9 +352,18 @@ export function testRebalance() { ethers.utils.parseUnits("8500", 6), getErrorRange(ethers.utils.parseUnits("8500", 6)) ) - expect(await this.investmentToken.balanceOf(this.user0.address)).to.equal(ethers.utils.parseUnits("10000", 6)) - expect(await investableInvestmentToken.balanceOf(this.user2.address)).to.equal(ethers.utils.parseUnits("1500", 6)) - expect(await this.portfolio.getInvestmentTokenSupply()).to.equal(ethers.utils.parseUnits("10000", 6)) + expect(await this.investmentToken.balanceOf(this.user0.address)).to.be.approximately( + ethers.utils.parseUnits("10000", 6), + getErrorRange(ethers.utils.parseUnits("10000", 6)) + ) + expect(await investableInvestmentToken.balanceOf(this.user2.address)).to.be.approximately( + ethers.utils.parseUnits("1500", 6), + getErrorRange(ethers.utils.parseUnits("1500", 6)) + ) + expect(await this.portfolio.getInvestmentTokenSupply()).to.be.approximately( + ethers.utils.parseUnits("10000", 6), + getErrorRange(ethers.utils.parseUnits("10000", 6)) + ) expect(await this.portfolio.getEquityValuation(true, false)).to.be.approximately( ethers.utils.parseUnits("10000", 6), getErrorRange(ethers.utils.parseUnits("10000", 6)) diff --git a/test/portfolio/UnifiedUpgradeable.test.ts b/test/portfolio/UnifiedUpgradeable.test.ts index 3f75ed9e..5ba99532 100644 --- a/test/portfolio/UnifiedUpgradeable.test.ts +++ b/test/portfolio/UnifiedUpgradeable.test.ts @@ -3,7 +3,7 @@ import { ethers, upgrades } from "hardhat" export function testUpgradeable() { describe("Upgradeable", async function () { - it("should success when the owner user upgrades", async function () { + it("should succeed when the owner user upgrades", async function () { const addr_before_upgrade = await upgrades.erc1967.getImplementationAddress(this.portfolio.address) const TestUpgradedPortfolio = await ethers.getContractFactory("TestUpgradedPortfolio") @@ -35,7 +35,7 @@ export function testUpgradeable() { expect(addr_before_upgrade != addr_after_upgrade).to.equal(true) }) - it("should success when the portfolio is paused", async function () { + it("should succeed when the portfolio is paused", async function () { expect(await this.portfolio.pause()).not.to.be.reverted const addr_before_upgrade = await upgrades.erc1967.getImplementationAddress(this.portfolio.address) @@ -96,7 +96,7 @@ export function testUpgradeable() { ).to.be.revertedWith("Ownable: caller is not the owner") }) - it("should success to leave all common state variables' value intact", async function () { + it("should succeed to leave all common state variables' value intact", async function () { // IAum const investmentTokenSupplyBefore = await this.portfolio.getInvestmentTokenSupply() // Don't check asset balances, liability balances, asset valuations, liability valuations diff --git a/test/portfolio/UnifiedWithdraw.test.ts b/test/portfolio/UnifiedWithdraw.test.ts index 5135db94..a4bcb31f 100644 --- a/test/portfolio/UnifiedWithdraw.test.ts +++ b/test/portfolio/UnifiedWithdraw.test.ts @@ -6,16 +6,15 @@ import { getErrorRange } from "../shared/utils" export function testWithdraw() { describe("Withdraw", async function () { - it("should success when a single user withdraws InvestmentToken that he/she has - 0", async function () { + it("should succeed when a single user withdraws InvestmentToken that he/she has - full withdrawal", async function () { await this.usdc.connect(this.user0).approve(this.portfolio.address, ethers.utils.parseUnits("3000", 6)) await this.portfolio.connect(this.user0).deposit(ethers.utils.parseUnits("3000", 6), this.user0.address, []) - await this.investmentToken.connect(this.user0).approve(this.portfolio.address, ethers.utils.parseUnits("3000", 6)) - await expect( - this.portfolio.connect(this.user0).withdraw(ethers.utils.parseUnits("3000", 6), this.user0.address, []) - ) + + const availableTokenBalance = await this.investmentToken.balanceOf(this.user0.address) + await expect(this.portfolio.connect(this.user0).withdraw(availableTokenBalance, this.user0.address, [])) .to.emit(this.portfolio, "Withdrawal") - .withArgs(this.user0.address, this.user0.address, ethers.utils.parseUnits("3000", 6)) + .withArgs(this.user0.address, this.user0.address, availableTokenBalance) expect(await this.usdc.balanceOf(this.user0.address)).to.be.approximately( ethers.utils.parseUnits("10000", 6), @@ -26,7 +25,7 @@ export function testWithdraw() { expect(await this.portfolio.getEquityValuation(true, false)).to.equal(0) }) - it("should success when a single user withdraws InvestmentToken that he/she has - 1", async function () { + it("should succeed when a single user withdraws InvestmentToken that he/she has - partial withdrawal", async function () { await this.usdc.connect(this.user0).approve(this.portfolio.address, ethers.utils.parseUnits("10000", 6)) await this.portfolio.connect(this.user0).deposit(ethers.utils.parseUnits("10000", 6), this.user0.address, []) @@ -41,21 +40,27 @@ export function testWithdraw() { ethers.utils.parseUnits("3000", 6), getErrorRange(ethers.utils.parseUnits("3000", 6)) ) - expect(await this.investmentToken.balanceOf(this.user0.address)).to.equal(ethers.utils.parseUnits("7000", 6)) - expect(await this.portfolio.getInvestmentTokenSupply()).to.equal(ethers.utils.parseUnits("7000", 6)) + expect(await this.investmentToken.balanceOf(this.user0.address)).to.be.approximately( + ethers.utils.parseUnits("7000", 6), + getErrorRange(ethers.utils.parseUnits("7000", 6)) + ) + expect(await this.portfolio.getInvestmentTokenSupply()).to.be.approximately( + ethers.utils.parseUnits("7000", 6), + getErrorRange(ethers.utils.parseUnits("7000", 6)) + ) expect(await this.portfolio.getEquityValuation(true, false)).to.be.approximately( ethers.utils.parseUnits("7000", 6), getErrorRange(ethers.utils.parseUnits("7000", 6)) ) }) - it("should success when a single user withdraws InvestmentToken that he/she has - 2", async function () { + it("should succeed when a single user withdraws InvestmentToken that he/she has - partial withdrawal on behalf of", async function () { await this.usdc.connect(this.user0).approve(this.portfolio.address, ethers.utils.parseUnits("5000", 6)) await this.portfolio.connect(this.user0).deposit(ethers.utils.parseUnits("5000", 6), this.user0.address, []) const usdcBalanceBefore = await this.usdc.balanceOf(this.user1.address) - await this.investmentToken.connect(this.user0).approve(this.portfolio.address, ethers.utils.parseUnits("3000", 6)) + await expect( this.portfolio.connect(this.user0).withdraw(ethers.utils.parseUnits("3000", 6), this.user1.address, []) ) @@ -70,28 +75,31 @@ export function testWithdraw() { ethers.utils.parseUnits("3000", 6).add(usdcBalanceBefore), getErrorRange(ethers.utils.parseUnits("3000", 6).add(usdcBalanceBefore)) ) - expect(await this.investmentToken.balanceOf(this.user0.address)).to.equal(ethers.utils.parseUnits("2000", 6)) - expect(await this.portfolio.getInvestmentTokenSupply()).to.equal(ethers.utils.parseUnits("2000", 6)) + expect(await this.investmentToken.balanceOf(this.user0.address)).to.be.approximately( + ethers.utils.parseUnits("2000", 6), + getErrorRange(ethers.utils.parseUnits("2000", 6)) + ) + expect(await this.portfolio.getInvestmentTokenSupply()).to.be.approximately( + ethers.utils.parseUnits("2000", 6), + getErrorRange(ethers.utils.parseUnits("2000", 6)) + ) expect(await this.portfolio.getEquityValuation(true, false)).to.be.approximately( ethers.utils.parseUnits("2000", 6), getErrorRange(ethers.utils.parseUnits("2000", 6)) ) }) - it("should success when a single user withdraws InvestmentToken that he/she has - 3", async function () { + it("should succeed when a single user withdraws InvestmentToken that he/she has - full withdrawal on behalf", async function () { await this.usdc.connect(this.user0).approve(this.portfolio.address, ethers.utils.parseUnits("10000", 6)) await this.portfolio.connect(this.user0).deposit(ethers.utils.parseUnits("10000", 6), this.user1.address, []) const usdcBalanceBefore = await this.usdc.balanceOf(this.user1.address) - await this.investmentToken - .connect(this.user1) - .approve(this.portfolio.address, ethers.utils.parseUnits("10000", 6)) - await expect( - this.portfolio.connect(this.user1).withdraw(ethers.utils.parseUnits("10000", 6), this.user1.address, []) - ) + const availableTokenBalance = await this.investmentToken.balanceOf(this.user1.address) + await this.investmentToken.connect(this.user1).approve(this.portfolio.address, availableTokenBalance) + await expect(this.portfolio.connect(this.user1).withdraw(availableTokenBalance, this.user1.address, [])) .to.emit(this.portfolio, "Withdrawal") - .withArgs(this.user1.address, this.user1.address, ethers.utils.parseUnits("10000", 6)) + .withArgs(this.user1.address, this.user1.address, availableTokenBalance) expect(await this.usdc.balanceOf(this.user0.address)).to.equal(0) expect(await this.usdc.balanceOf(this.user1.address)).to.be.approximately( @@ -117,8 +125,14 @@ export function testWithdraw() { ethers.utils.parseUnits("7000", 6), getErrorRange(ethers.utils.parseUnits("7000", 6)) ) - expect(await this.investmentToken.balanceOf(this.user0.address)).to.equal(ethers.utils.parseUnits("3000", 6)) - expect(await this.portfolio.getInvestmentTokenSupply()).to.equal(ethers.utils.parseUnits("3000", 6)) + expect(await this.investmentToken.balanceOf(this.user0.address)).to.be.approximately( + ethers.utils.parseUnits("3000", 6), + getErrorRange(ethers.utils.parseUnits("3000", 6)) + ) + expect(await this.portfolio.getInvestmentTokenSupply()).to.be.approximately( + ethers.utils.parseUnits("3000", 6), + getErrorRange(ethers.utils.parseUnits("3000", 6)) + ) expect(await this.portfolio.getEquityValuation(true, false)).to.be.approximately( ethers.utils.parseUnits("3000", 6), getErrorRange(ethers.utils.parseUnits("3000", 6)) @@ -138,15 +152,21 @@ export function testWithdraw() { ethers.utils.parseUnits("7000", 6), getErrorRange(ethers.utils.parseUnits("7000", 6)) ) - expect(await this.investmentToken.balanceOf(this.user0.address)).to.equal(ethers.utils.parseUnits("3000", 6)) - expect(await this.portfolio.getInvestmentTokenSupply()).to.equal(ethers.utils.parseUnits("3000", 6)) + expect(await this.investmentToken.balanceOf(this.user0.address)).to.be.approximately( + ethers.utils.parseUnits("3000", 6), + getErrorRange(ethers.utils.parseUnits("3000", 6)) + ) + expect(await this.portfolio.getInvestmentTokenSupply()).to.be.approximately( + ethers.utils.parseUnits("3000", 6), + getErrorRange(ethers.utils.parseUnits("3000", 6)) + ) expect(await this.portfolio.getEquityValuation(true, false)).to.be.approximately( ethers.utils.parseUnits("3000", 6), getErrorRange(ethers.utils.parseUnits("3000", 6)) ) }) - it("should success when a single user withdraws and another user withdrew from investable directly before that", async function () { + it("should succeed when a single user withdraws and another user withdrew from investable directly before that", async function () { const investables = await this.portfolio.getInvestables() const investable = await ethers.getContractAt(investableAbi, await investables[0].investable) const investableInvestmentToken = await ethers.getContractAt(erc20Abi, await investable.getInvestmentToken()) @@ -158,18 +178,16 @@ export function testWithdraw() { await this.usdc.connect(this.user0).approve(this.portfolio.address, ethers.utils.parseUnits("3000", 6)) await this.portfolio.connect(this.user0).deposit(ethers.utils.parseUnits("3000", 6), this.user0.address, []) - await investableInvestmentToken - .connect(this.user1) - .approve(investable.address, ethers.utils.parseUnits("3000", 6)) - await expect(investable.connect(this.user1).withdraw(ethers.utils.parseUnits("3000", 6), this.user1.address, [])) - .not.to.be.reverted + let availableTokenBalance = await investableInvestmentToken.balanceOf(this.user1.address) + await investableInvestmentToken.connect(this.user1).approve(investable.address, availableTokenBalance) + await expect(investable.connect(this.user1).withdraw(availableTokenBalance, this.user1.address, [])).not.to.be + .reverted - await this.investmentToken.connect(this.user0).approve(this.portfolio.address, ethers.utils.parseUnits("3000", 6)) - await expect( - this.portfolio.connect(this.user0).withdraw(ethers.utils.parseUnits("3000", 6), this.user0.address, []) - ) + availableTokenBalance = await this.investmentToken.balanceOf(this.user0.address) + await this.investmentToken.connect(this.user0).approve(this.portfolio.address, availableTokenBalance) + await expect(this.portfolio.connect(this.user0).withdraw(availableTokenBalance, this.user0.address, [])) .to.emit(this.portfolio, "Withdrawal") - .withArgs(this.user0.address, this.user0.address, ethers.utils.parseUnits("3000", 6)) + .withArgs(this.user0.address, this.user0.address, availableTokenBalance) expect(await this.usdc.balanceOf(this.user0.address)).to.be.approximately( ethers.utils.parseUnits("10000", 6), @@ -187,7 +205,7 @@ export function testWithdraw() { expect(await investable.getEquityValuation(true, false)).to.equal(0) }) - it("should success when a single user withdraws and another user withdrew from investable directly after that", async function () { + it("should succeed when a single user withdraws and another user withdrew from investable directly after that", async function () { await this.usdc.connect(this.user0).approve(this.portfolio.address, ethers.utils.parseUnits("3000", 6)) await this.portfolio.connect(this.user0).deposit(ethers.utils.parseUnits("3000", 6), this.user0.address, []) @@ -199,12 +217,11 @@ export function testWithdraw() { await expect(investable.connect(this.user1).deposit(ethers.utils.parseUnits("3000", 6), this.user1.address, [])) .not.to.be.reverted - await this.investmentToken.connect(this.user0).approve(this.portfolio.address, ethers.utils.parseUnits("3000", 6)) - await expect( - this.portfolio.connect(this.user0).withdraw(ethers.utils.parseUnits("3000", 6), this.user0.address, []) - ) + let availableTokenBalance = await this.investmentToken.balanceOf(this.user0.address) + await this.investmentToken.connect(this.user0).approve(this.portfolio.address, availableTokenBalance) + await expect(this.portfolio.connect(this.user0).withdraw(availableTokenBalance, this.user0.address, [])) .to.emit(this.portfolio, "Withdrawal") - .withArgs(this.user0.address, this.user0.address, ethers.utils.parseUnits("3000", 6)) + .withArgs(this.user0.address, this.user0.address, availableTokenBalance) const investableInvestmentTokenAmount = await investableInvestmentToken.balanceOf(this.user1.address) await investableInvestmentToken.connect(this.user1).approve(investable.address, investableInvestmentTokenAmount) @@ -227,7 +244,7 @@ export function testWithdraw() { expect(await investable.getEquityValuation(true, false)).to.equal(0) }) - it("should success when multiple users withdraw InvestmentTokens that they have - 0", async function () { + it("should succeed when multiple users withdraw InvestmentTokens that they have - full delayed withdraw", async function () { // The first user deposits. await this.usdc.connect(this.user0).approve(this.portfolio.address, ethers.utils.parseUnits("3000", 6)) await this.portfolio.connect(this.user0).deposit(ethers.utils.parseUnits("3000", 6), this.user0.address, []) @@ -237,19 +254,18 @@ export function testWithdraw() { await this.portfolio.connect(this.user1).deposit(ethers.utils.parseUnits("3000", 6), this.user1.address, []) // The first user withdraws. - await this.investmentToken.connect(this.user0).approve(this.portfolio.address, ethers.utils.parseUnits("3000", 6)) - await expect( - this.portfolio.connect(this.user0).withdraw(ethers.utils.parseUnits("3000", 6), this.user0.address, []) - ) + let availableTokenBalance = await this.investmentToken.balanceOf(this.user0.address) + await this.investmentToken.connect(this.user0).approve(this.portfolio.address, availableTokenBalance) + await expect(this.portfolio.connect(this.user0).withdraw(availableTokenBalance, this.user0.address, [])) .to.emit(this.portfolio, "Withdrawal") - .withArgs(this.user0.address, this.user0.address, ethers.utils.parseUnits("3000", 6)) + .withArgs(this.user0.address, this.user0.address, availableTokenBalance) // The second user withdraws. - const investmentTokenBalance = await this.investmentToken.balanceOf(this.user1.address) - await this.investmentToken.connect(this.user1).approve(this.portfolio.address, investmentTokenBalance) - await expect(this.portfolio.connect(this.user1).withdraw(investmentTokenBalance, this.user1.address, [])) + availableTokenBalance = await this.investmentToken.balanceOf(this.user1.address) + await this.investmentToken.connect(this.user1).approve(this.portfolio.address, availableTokenBalance) + await expect(this.portfolio.connect(this.user1).withdraw(availableTokenBalance, this.user1.address, [])) .to.emit(this.portfolio, "Withdrawal") - .withArgs(this.user1.address, this.user1.address, investmentTokenBalance) + .withArgs(this.user1.address, this.user1.address, availableTokenBalance) expect(await this.usdc.balanceOf(this.user0.address)).to.be.approximately( ethers.utils.parseUnits("10000", 6), @@ -265,7 +281,7 @@ export function testWithdraw() { expect(await this.portfolio.getEquityValuation(true, false)).to.equal(0) }) - it("should success when multiple users withdraw InvestmentTokens that they have - 1", async function () { + it("should succeed when multiple users withdraw InvestmentTokens that they have - partial delayed withdraw", async function () { // The first user deposits. await this.usdc.connect(this.user0).approve(this.portfolio.address, ethers.utils.parseUnits("3000", 6)) await this.portfolio.connect(this.user0).deposit(ethers.utils.parseUnits("3000", 6), this.user0.address, []) @@ -298,7 +314,10 @@ export function testWithdraw() { ethers.utils.parseUnits("8500", 6), getErrorRange(ethers.utils.parseUnits("8500", 6)) ) - expect(await this.investmentToken.balanceOf(this.user0.address)).to.equal(ethers.utils.parseUnits("1500", 6)) + expect(await this.investmentToken.balanceOf(this.user0.address)).to.be.approximately( + ethers.utils.parseUnits("1500", 6), + getErrorRange(ethers.utils.parseUnits("1500", 6)) + ) expect(await this.investmentToken.balanceOf(this.user1.address)).to.be.approximately( ethers.utils.parseUnits("1500", 6), getErrorRange(ethers.utils.parseUnits("1500", 6)) @@ -313,29 +332,28 @@ export function testWithdraw() { ) }) - it("should success when multiple users withdraw InvestmentTokens that they have - 2", async function () { + it("should succeed when multiple users withdraw InvestmentTokens that they have - full immediate withdrawal", async function () { // The first user deposits. await this.usdc.connect(this.user0).approve(this.portfolio.address, ethers.utils.parseUnits("3000", 6)) await this.portfolio.connect(this.user0).deposit(ethers.utils.parseUnits("3000", 6), this.user0.address, []) // The first user withdraws. - await this.investmentToken.connect(this.user0).approve(this.portfolio.address, ethers.utils.parseUnits("3000", 6)) - await expect( - this.portfolio.connect(this.user0).withdraw(ethers.utils.parseUnits("3000", 6), this.user0.address, []) - ) + let availableTokenBalance = await this.investmentToken.balanceOf(this.user0.address) + await this.investmentToken.connect(this.user0).approve(this.portfolio.address, availableTokenBalance) + await expect(this.portfolio.connect(this.user0).withdraw(availableTokenBalance, this.user0.address, [])) .to.emit(this.portfolio, "Withdrawal") - .withArgs(this.user0.address, this.user0.address, ethers.utils.parseUnits("3000", 6)) + .withArgs(this.user0.address, this.user0.address, availableTokenBalance) // The second user deposits. await this.usdc.connect(this.user1).approve(this.portfolio.address, ethers.utils.parseUnits("3000", 6)) await this.portfolio.connect(this.user1).deposit(ethers.utils.parseUnits("3000", 6), this.user1.address, []) // The second user withdraws. - const investmentTokenBalance = await this.investmentToken.balanceOf(this.user1.address) - await this.investmentToken.connect(this.user1).approve(this.portfolio.address, investmentTokenBalance) - await expect(this.portfolio.connect(this.user1).withdraw(investmentTokenBalance, this.user1.address, [])) + availableTokenBalance = await this.investmentToken.balanceOf(this.user1.address) + await this.investmentToken.connect(this.user1).approve(this.portfolio.address, availableTokenBalance) + await expect(this.portfolio.connect(this.user1).withdraw(availableTokenBalance, this.user1.address, [])) .to.emit(this.portfolio, "Withdrawal") - .withArgs(this.user1.address, this.user1.address, investmentTokenBalance) + .withArgs(this.user1.address, this.user1.address, availableTokenBalance) expect(await this.usdc.balanceOf(this.user0.address)).to.be.approximately( ethers.utils.parseUnits("10000", 6), @@ -381,9 +399,15 @@ export function testWithdraw() { ethers.utils.parseUnits("10000", 6), getErrorRange(ethers.utils.parseUnits("10000", 6)) ) - expect(await this.investmentToken.balanceOf(this.user0.address)).to.equal(ethers.utils.parseUnits("3000", 6)) + expect(await this.investmentToken.balanceOf(this.user0.address)).to.be.approximately( + ethers.utils.parseUnits("3000", 6), + getErrorRange(ethers.utils.parseUnits("3000", 6)) + ) expect(await this.investmentToken.balanceOf(this.user1.address)).to.equal(0) - expect(await this.portfolio.getInvestmentTokenSupply()).to.equal(ethers.utils.parseUnits("3000", 6)) + expect(await this.portfolio.getInvestmentTokenSupply()).to.be.approximately( + ethers.utils.parseUnits("3000", 6), + getErrorRange(ethers.utils.parseUnits("3000", 6)) + ) expect(await this.portfolio.getEquityValuation(true, false)).to.be.approximately( ethers.utils.parseUnits("3000", 6), getErrorRange(ethers.utils.parseUnits("3000", 6)) @@ -434,7 +458,10 @@ export function testWithdraw() { ethers.utils.parseUnits("8500", 6), getErrorRange(ethers.utils.parseUnits("8500", 6)) ) - expect(await this.investmentToken.balanceOf(this.user0.address)).to.equal(ethers.utils.parseUnits("1500", 6)) + expect(await this.investmentToken.balanceOf(this.user0.address)).to.be.approximately( + ethers.utils.parseUnits("1500", 6), + getErrorRange(ethers.utils.parseUnits("1500", 6)) + ) expect(await this.investmentToken.balanceOf(this.user1.address)).to.be.approximately( ethers.utils.parseUnits("3000", 6), getErrorRange(ethers.utils.parseUnits("3000", 6)) @@ -453,11 +480,12 @@ export function testWithdraw() { ) }) - it("should success when multiple user withdraws and another user withdrew from investable directly before that", async function () { + it("should succeed when multiple user withdraws and another user withdrew from investable directly before that", async function () { const investables = await this.portfolio.getInvestables() const investable = await ethers.getContractAt(investableAbi, await investables[0].investable) const investableInvestmentToken = await ethers.getContractAt(erc20Abi, await investable.getInvestmentToken()) + // The third user deposits. await this.usdc.connect(this.user2).approve(investable.address, ethers.utils.parseUnits("3000", 6)) await expect(investable.connect(this.user2).deposit(ethers.utils.parseUnits("3000", 6), this.user2.address, [])) .not.to.be.reverted @@ -470,26 +498,25 @@ export function testWithdraw() { await this.usdc.connect(this.user1).approve(this.portfolio.address, ethers.utils.parseUnits("3000", 6)) await this.portfolio.connect(this.user1).deposit(ethers.utils.parseUnits("3000", 6), this.user1.address, []) - await investableInvestmentToken - .connect(this.user2) - .approve(investable.address, ethers.utils.parseUnits("3000", 6)) - await expect(investable.connect(this.user2).withdraw(ethers.utils.parseUnits("3000", 6), this.user2.address, [])) - .not.to.be.reverted + // The third user withdraws. + let availableTokenBalance = await investableInvestmentToken.balanceOf(this.user2.address) + await investableInvestmentToken.connect(this.user2).approve(investable.address, availableTokenBalance) + await expect(investable.connect(this.user2).withdraw(availableTokenBalance, this.user2.address, [])).not.to.be + .reverted // The first user withdraws. - await this.investmentToken.connect(this.user0).approve(this.portfolio.address, ethers.utils.parseUnits("3000", 6)) - await expect( - this.portfolio.connect(this.user0).withdraw(ethers.utils.parseUnits("3000", 6), this.user0.address, []) - ) + availableTokenBalance = await this.investmentToken.balanceOf(this.user0.address) + await this.investmentToken.connect(this.user0).approve(this.portfolio.address, availableTokenBalance) + await expect(this.portfolio.connect(this.user0).withdraw(availableTokenBalance, this.user0.address, [])) .to.emit(this.portfolio, "Withdrawal") - .withArgs(this.user0.address, this.user0.address, ethers.utils.parseUnits("3000", 6)) + .withArgs(this.user0.address, this.user0.address, availableTokenBalance) // The second user withdraws. - const investmentTokenBalance = await this.investmentToken.balanceOf(this.user1.address) - await this.investmentToken.connect(this.user1).approve(this.portfolio.address, investmentTokenBalance) - await expect(this.portfolio.connect(this.user1).withdraw(investmentTokenBalance, this.user1.address, [])) + availableTokenBalance = await this.investmentToken.balanceOf(this.user1.address) + await this.investmentToken.connect(this.user1).approve(this.portfolio.address, availableTokenBalance) + await expect(this.portfolio.connect(this.user1).withdraw(availableTokenBalance, this.user1.address, [])) .to.emit(this.portfolio, "Withdrawal") - .withArgs(this.user1.address, this.user1.address, investmentTokenBalance) + .withArgs(this.user1.address, this.user1.address, availableTokenBalance) expect(await this.usdc.balanceOf(this.user0.address)).to.be.approximately( ethers.utils.parseUnits("10000", 6), @@ -512,7 +539,7 @@ export function testWithdraw() { expect(await investable.getEquityValuation(true, false)).to.equal(0) }) - it("should success when multiple user withdraws and another user withdrew from investable directly after that", async function () { + it("should succeed when multiple user withdraws and another user withdrew from investable directly after that", async function () { // The first user deposits. await this.usdc.connect(this.user0).approve(this.portfolio.address, ethers.utils.parseUnits("3000", 6)) await this.portfolio.connect(this.user0).deposit(ethers.utils.parseUnits("3000", 6), this.user0.address, []) @@ -525,29 +552,29 @@ export function testWithdraw() { const investable = await ethers.getContractAt(investableAbi, await investables[0].investable) const investableInvestmentToken = await ethers.getContractAt(erc20Abi, await investable.getInvestmentToken()) + // The third user deposits. await this.usdc.connect(this.user2).approve(investable.address, ethers.utils.parseUnits("3000", 6)) await expect(investable.connect(this.user2).deposit(ethers.utils.parseUnits("3000", 6), this.user2.address, [])) .not.to.be.reverted // The first user withdraws. - await this.investmentToken.connect(this.user0).approve(this.portfolio.address, ethers.utils.parseUnits("3000", 6)) - await expect( - this.portfolio.connect(this.user0).withdraw(ethers.utils.parseUnits("3000", 6), this.user0.address, []) - ) + let availableTokenBalance = await this.investmentToken.balanceOf(this.user0.address) + await this.investmentToken.connect(this.user0).approve(this.portfolio.address, availableTokenBalance) + await expect(this.portfolio.connect(this.user0).withdraw(availableTokenBalance, this.user0.address, [])) .to.emit(this.portfolio, "Withdrawal") - .withArgs(this.user0.address, this.user0.address, ethers.utils.parseUnits("3000", 6)) + .withArgs(this.user0.address, this.user0.address, availableTokenBalance) // The second user withdraws. - const investmentTokenBalance = await this.investmentToken.balanceOf(this.user1.address) - await this.investmentToken.connect(this.user1).approve(this.portfolio.address, investmentTokenBalance) - await expect(this.portfolio.connect(this.user1).withdraw(investmentTokenBalance, this.user1.address, [])) + availableTokenBalance = await this.investmentToken.balanceOf(this.user1.address) + await this.investmentToken.connect(this.user1).approve(this.portfolio.address, availableTokenBalance) + await expect(this.portfolio.connect(this.user1).withdraw(availableTokenBalance, this.user1.address, [])) .to.emit(this.portfolio, "Withdrawal") - .withArgs(this.user1.address, this.user1.address, investmentTokenBalance) + .withArgs(this.user1.address, this.user1.address, availableTokenBalance) - const investableInvestmentTokenAmount = await investableInvestmentToken.balanceOf(this.user2.address) - await investableInvestmentToken.connect(this.user2).approve(investable.address, investableInvestmentTokenAmount) - await expect(investable.connect(this.user2).withdraw(investableInvestmentTokenAmount, this.user2.address, [])).not - .to.be.reverted + availableTokenBalance = await investableInvestmentToken.balanceOf(this.user2.address) + await investableInvestmentToken.connect(this.user2).approve(investable.address, availableTokenBalance) + await expect(investable.connect(this.user2).withdraw(availableTokenBalance, this.user2.address, [])).not.to.be + .reverted expect(await this.usdc.balanceOf(this.user0.address)).to.be.approximately( ethers.utils.parseUnits("10000", 6), diff --git a/test/portfolio/percentageAllocation/PercentageAllocation.test.ts b/test/portfolio/percentageAllocation/PercentageAllocation.test.ts index c9e139d2..3a8d7306 100644 --- a/test/portfolio/percentageAllocation/PercentageAllocation.test.ts +++ b/test/portfolio/percentageAllocation/PercentageAllocation.test.ts @@ -1,15 +1,15 @@ import { expect } from "chai" import { ethers, upgrades } from "hardhat" import investableAbi from "../../shared/abi/investable.json" -import { TokenAddrs, StargateAddrs, TraderJoeAddrs } from "../../shared/addresses" +import { StargateAddrs, TokenAddrs, TraderJoeAddrs } from "../../shared/addresses" import { getUUPSUpgradeableContract, - getUUPSUpgradeableStrategy, getUUPSUpgradeablePortfolio, + getUUPSUpgradeableStrategy, } from "../../shared/contracts" import { Oracles } from "../../shared/oracles" -import { getErrorRange } from "../../shared/utils" import { SwapServices } from "../../shared/swaps" +import { getErrorRange } from "../../shared/utils" import { testPortfolio } from "../Unified.test" testPortfolio("PercentageAllocation Portfolio", deployPortfolio, [ @@ -258,7 +258,7 @@ async function deployPortfolio(context: Mocha.Context) { function testPercentageAllocationPortfolioAum() { describe("AUM - PercentageAllocation Portfolio Specific", async function () { - it("should success after a single deposit", async function () { + it("should succeed after a single deposit", async function () { await this.usdc.connect(this.user0).approve(this.portfolio.address, ethers.utils.parseUnits("3000", 6)) await this.portfolio.connect(this.user0).deposit(ethers.utils.parseUnits("3000", 6), this.user0.address, []) @@ -324,7 +324,7 @@ function testPercentageAllocationPortfolioAum() { ) }) - it("should success after multiple deposits and withdrawals", async function () { + it("should succeed after multiple deposits and withdrawals", async function () { await this.usdc.connect(this.user0).approve(this.portfolio.address, ethers.utils.parseUnits("5000", 6)) await this.portfolio.connect(this.user0).deposit(ethers.utils.parseUnits("5000", 6), this.user0.address, []) @@ -403,7 +403,7 @@ function testPercentageAllocationPortfolioAum() { function testPercentageAllocationPortfolioUpgradeable() { describe("Upgradeable - PercentageAllocation Portfolio Specific", async function () { - it("should success to leave all portfolio specific state variables' value intact", async function () { + it("should succeed to leave all portfolio specific state variables' value intact", async function () { // IAum. const assetBalancesBefore = await this.portfolio.getAssetBalances() const assetValuationsBefore = await this.portfolio.getAssetValuations(true, false) diff --git a/test/strategy/Unified.test.ts b/test/strategy/Unified.test.ts index 5b99eb74..ad257b33 100644 --- a/test/strategy/Unified.test.ts +++ b/test/strategy/Unified.test.ts @@ -1,7 +1,8 @@ -import { ethers, upgrades, network } from "hardhat" +import { takeSnapshot } from "@nomicfoundation/hardhat-network-helpers" +import { ethers, network, upgrades } from "hardhat" import { TokenAddrs, WhaleAddrs } from "../shared/addresses" -import { Oracle } from "../shared/oracles" import { getTokenContract } from "../shared/contracts" +import { Oracle } from "../shared/oracles" import { SwapServices } from "../shared/swaps" import { testDeposit } from "./UnifiedDeposit.test" import { testERC165 } from "./UnifiedERC165.test" @@ -11,7 +12,6 @@ import { testPausable } from "./UnifiedPausable.test" import { testReapReward } from "./UnifiedReapReward.test" import { testUpgradeable } from "./UnifiedUpgradeable.test" import { testWithdraw } from "./UnifiedWithdraw.test" -import { takeSnapshot } from "@nomicfoundation/hardhat-network-helpers" export function testStrategy( description: string, diff --git a/test/strategy/UnifiedDeposit.test.ts b/test/strategy/UnifiedDeposit.test.ts index 6fb75b1f..9601b82f 100644 --- a/test/strategy/UnifiedDeposit.test.ts +++ b/test/strategy/UnifiedDeposit.test.ts @@ -1,10 +1,10 @@ import { expect } from "chai" import { ethers } from "hardhat" -import { getErrorRange, airdropToken } from "../shared/utils" +import { airdropToken, getErrorRange } from "../shared/utils" export function testDeposit() { describe("Deposit", async function () { - it("should success when a single user deposits USDC that he/she has - 0", async function () { + it("should succeed when a single user deposits USDC that he/she has - integer amount", async function () { airdropToken(this.impersonatedSigner, this.user0, this.usdc, ethers.utils.parseUnits("100", 6)) await this.usdc.connect(this.user0).approve(this.strategy.address, ethers.utils.parseUnits("30", 6)) @@ -13,15 +13,21 @@ export function testDeposit() { .withArgs(this.user0.address, this.user0.address, ethers.utils.parseUnits("30", 6)) expect(await this.usdc.balanceOf(this.user0.address)).to.equal(ethers.utils.parseUnits("70", 6)) - expect(await this.investmentToken.balanceOf(this.user0.address)).to.equal(ethers.utils.parseUnits("30", 6)) - expect(await this.strategy.getInvestmentTokenSupply()).to.equal(ethers.utils.parseUnits("30", 6)) + expect(await this.investmentToken.balanceOf(this.user0.address)).to.be.approximately( + ethers.utils.parseUnits("30", 6), + getErrorRange(ethers.utils.parseUnits("30", 6)) + ) + expect(await this.strategy.getInvestmentTokenSupply()).to.be.approximately( + ethers.utils.parseUnits("30", 6), + getErrorRange(ethers.utils.parseUnits("30", 6)) + ) expect(await this.strategy.getEquityValuation(true, false)).to.be.approximately( ethers.utils.parseUnits("30", 6), getErrorRange(ethers.utils.parseUnits("30", 6)) ) }) - it("should success when a single user deposits USDC that he/she has - 1", async function () { + it("should succeed when a single user deposits USDC that he/she has - fractional amount", async function () { airdropToken(this.impersonatedSigner, this.user0, this.usdc, ethers.utils.parseUnits("473.782192", 6)) await this.usdc.connect(this.user0).approve(this.strategy.address, ethers.utils.parseUnits("37.810393", 6)) @@ -32,8 +38,14 @@ export function testDeposit() { .withArgs(this.user0.address, this.user0.address, ethers.utils.parseUnits("37.810393", 6)) expect(await this.usdc.balanceOf(this.user0.address)).to.equal(ethers.utils.parseUnits("435.971799", 6)) - expect(await this.investmentToken.balanceOf(this.user0.address)).to.equal(ethers.utils.parseUnits("37.810393", 6)) - expect(await this.strategy.getInvestmentTokenSupply()).to.equal(ethers.utils.parseUnits("37.810393", 6)) + expect(await this.investmentToken.balanceOf(this.user0.address)).to.be.approximately( + ethers.utils.parseUnits("37.810393", 6), + getErrorRange(ethers.utils.parseUnits("37.810393", 6)) + ) + expect(await this.strategy.getInvestmentTokenSupply()).to.be.approximately( + ethers.utils.parseUnits("37.810393", 6), + getErrorRange(ethers.utils.parseUnits("37.810393", 6)) + ) expect(await this.strategy.getEquityValuation(true, false)).to.be.approximately( ethers.utils.parseUnits("37.810393", 6), getErrorRange(ethers.utils.parseUnits("37.810393", 6)) @@ -101,7 +113,7 @@ export function testDeposit() { expect(await this.strategy.getEquityValuation(true, false)).to.equal(0) }) - it("should success when multiple users deposit USDC that they have - 0", async function () { + it("should succeed when multiple users deposit USDC that they have - integer amount", async function () { airdropToken(this.impersonatedSigner, this.user0, this.usdc, ethers.utils.parseUnits("100", 6)) airdropToken(this.impersonatedSigner, this.user1, this.usdc, ethers.utils.parseUnits("100", 6)) airdropToken(this.impersonatedSigner, this.user2, this.usdc, ethers.utils.parseUnits("100", 6)) @@ -113,8 +125,14 @@ export function testDeposit() { .withArgs(this.user0.address, this.user0.address, ethers.utils.parseUnits("30", 6)) expect(await this.usdc.balanceOf(this.user0.address)).to.equal(ethers.utils.parseUnits("70", 6)) - expect(await this.investmentToken.balanceOf(this.user0.address)).to.equal(ethers.utils.parseUnits("30", 6)) - expect(await this.strategy.getInvestmentTokenSupply()).to.equal(ethers.utils.parseUnits("30", 6)) + expect(await this.investmentToken.balanceOf(this.user0.address)).to.be.approximately( + ethers.utils.parseUnits("30", 6), + getErrorRange(ethers.utils.parseUnits("30", 6)) + ) + expect(await this.strategy.getInvestmentTokenSupply()).to.be.approximately( + ethers.utils.parseUnits("30", 6), + getErrorRange(ethers.utils.parseUnits("30", 6)) + ) expect(await this.strategy.getEquityValuation(true, false)).to.be.approximately( ethers.utils.parseUnits("30", 6), getErrorRange(ethers.utils.parseUnits("30", 6)) @@ -161,7 +179,7 @@ export function testDeposit() { ) }) - it("should success when multiple users deposit USDC that they have - 1", async function () { + it("should succeed when multiple users deposit USDC that they have - fractional amount", async function () { airdropToken(this.impersonatedSigner, this.user0, this.usdc, ethers.utils.parseUnits("473.782192", 6)) airdropToken(this.impersonatedSigner, this.user1, this.usdc, ethers.utils.parseUnits("473.782192", 6)) airdropToken(this.impersonatedSigner, this.user2, this.usdc, ethers.utils.parseUnits("473.782192", 6)) @@ -175,8 +193,14 @@ export function testDeposit() { .withArgs(this.user0.address, this.user0.address, ethers.utils.parseUnits("37.810393", 6)) expect(await this.usdc.balanceOf(this.user0.address)).to.equal(ethers.utils.parseUnits("435.971799", 6)) - expect(await this.investmentToken.balanceOf(this.user0.address)).to.equal(ethers.utils.parseUnits("37.810393", 6)) - expect(await this.strategy.getInvestmentTokenSupply()).to.equal(ethers.utils.parseUnits("37.810393", 6)) + expect(await this.investmentToken.balanceOf(this.user0.address)).to.be.approximately( + ethers.utils.parseUnits("37.810393", 6), + getErrorRange(ethers.utils.parseUnits("37.810393", 6)) + ) + expect(await this.strategy.getInvestmentTokenSupply()).to.be.approximately( + ethers.utils.parseUnits("37.810393", 6), + getErrorRange(ethers.utils.parseUnits("37.810393", 6)) + ) expect(await this.strategy.getEquityValuation(true, false)).to.be.approximately( ethers.utils.parseUnits("37.810393", 6), getErrorRange(ethers.utils.parseUnits("37.810393", 6)) @@ -255,7 +279,10 @@ export function testDeposit() { ethers.utils.parseUnits("30", 6), getErrorRange(ethers.utils.parseUnits("30", 6)) ) - expect(await this.strategy.getInvestmentTokenSupply()).to.equal(ethers.utils.parseUnits("30", 6)) + expect(await this.strategy.getInvestmentTokenSupply()).to.be.approximately( + ethers.utils.parseUnits("30", 6), + getErrorRange(ethers.utils.parseUnits("30", 6)) + ) expect(await this.strategy.getEquityValuation(true, false)).to.be.approximately( ethers.utils.parseUnits("30", 6), getErrorRange(ethers.utils.parseUnits("30", 6)) @@ -270,7 +297,10 @@ export function testDeposit() { expect(await this.usdc.balanceOf(this.user2.address)).to.equal(ethers.utils.parseUnits("100", 6)) expect(await this.investmentToken.balanceOf(this.user2.address)).to.equal(0) - expect(await this.strategy.getInvestmentTokenSupply()).to.equal(ethers.utils.parseUnits("30", 6)) + expect(await this.strategy.getInvestmentTokenSupply()).to.be.approximately( + ethers.utils.parseUnits("30", 6), + getErrorRange(ethers.utils.parseUnits("30", 6)) + ) expect(await this.strategy.getEquityValuation(true, false)).to.be.approximately( ethers.utils.parseUnits("30", 6), getErrorRange(ethers.utils.parseUnits("30", 6)) @@ -288,8 +318,14 @@ export function testDeposit() { .withArgs(this.user0.address, this.user0.address, ethers.utils.parseUnits("30", 6)) expect(await this.usdc.balanceOf(this.user0.address)).to.equal(ethers.utils.parseUnits("70", 6)) - expect(await this.investmentToken.balanceOf(this.user0.address)).to.equal(ethers.utils.parseUnits("30", 6)) - expect(await this.strategy.getInvestmentTokenSupply()).to.equal(ethers.utils.parseUnits("30", 6)) + expect(await this.investmentToken.balanceOf(this.user0.address)).to.be.approximately( + ethers.utils.parseUnits("30", 6), + getErrorRange(ethers.utils.parseUnits("30", 6)) + ) + expect(await this.strategy.getInvestmentTokenSupply()).to.be.approximately( + ethers.utils.parseUnits("30", 6), + getErrorRange(ethers.utils.parseUnits("30", 6)) + ) expect(await this.strategy.getEquityValuation(true, false)).to.be.approximately( ethers.utils.parseUnits("30", 6), getErrorRange(ethers.utils.parseUnits("30", 6)) @@ -303,7 +339,10 @@ export function testDeposit() { expect(await this.usdc.balanceOf(this.user1.address)).to.equal(ethers.utils.parseUnits("100", 6)) expect(await this.investmentToken.balanceOf(this.user1.address)).to.equal(0) - expect(await this.strategy.getInvestmentTokenSupply()).to.equal(ethers.utils.parseUnits("30", 6)) + expect(await this.strategy.getInvestmentTokenSupply()).to.be.approximately( + ethers.utils.parseUnits("30", 6), + getErrorRange(ethers.utils.parseUnits("30", 6)) + ) expect(await this.strategy.getEquityValuation(true, false)).to.be.approximately( ethers.utils.parseUnits("30", 6), getErrorRange(ethers.utils.parseUnits("30", 6)) @@ -335,8 +374,14 @@ export function testDeposit() { .withArgs(this.user1.address, this.user1.address, ethers.utils.parseUnits("30", 6)) expect(await this.usdc.balanceOf(this.user1.address)).to.equal(ethers.utils.parseUnits("70", 6)) - expect(await this.investmentToken.balanceOf(this.user1.address)).to.equal(ethers.utils.parseUnits("30", 6)) - expect(await this.strategy.getInvestmentTokenSupply()).to.equal(ethers.utils.parseUnits("30", 6)) + expect(await this.investmentToken.balanceOf(this.user1.address)).to.be.approximately( + ethers.utils.parseUnits("30", 6), + getErrorRange(ethers.utils.parseUnits("30", 6)) + ) + expect(await this.strategy.getInvestmentTokenSupply()).to.be.approximately( + ethers.utils.parseUnits("30", 6), + getErrorRange(ethers.utils.parseUnits("30", 6)) + ) expect(await this.strategy.getEquityValuation(true, false)).to.be.approximately( ethers.utils.parseUnits("30", 6), getErrorRange(ethers.utils.parseUnits("30", 6)) @@ -350,7 +395,10 @@ export function testDeposit() { expect(await this.usdc.balanceOf(this.user2.address)).to.equal(ethers.utils.parseUnits("100", 6)) expect(await this.investmentToken.balanceOf(this.user2.address)).to.equal(0) - expect(await this.strategy.getInvestmentTokenSupply()).to.equal(ethers.utils.parseUnits("30", 6)) + expect(await this.strategy.getInvestmentTokenSupply()).to.be.approximately( + ethers.utils.parseUnits("30", 6), + getErrorRange(ethers.utils.parseUnits("30", 6)) + ) expect(await this.strategy.getEquityValuation(true, false)).to.be.approximately( ethers.utils.parseUnits("30", 6), getErrorRange(ethers.utils.parseUnits("30", 6)) @@ -371,8 +419,14 @@ export function testDeposit() { .withArgs(this.user0.address, this.user0.address, ethers.utils.parseUnits("30", 6)) expect(await this.usdc.balanceOf(this.user0.address)).to.equal(ethers.utils.parseUnits("70", 6)) - expect(await this.investmentToken.balanceOf(this.user0.address)).to.equal(ethers.utils.parseUnits("30", 6)) - expect(await this.strategy.getInvestmentTokenSupply()).to.equal(ethers.utils.parseUnits("30", 6)) + expect(await this.investmentToken.balanceOf(this.user0.address)).to.be.approximately( + ethers.utils.parseUnits("30", 6), + getErrorRange(ethers.utils.parseUnits("30", 6)) + ) + expect(await this.strategy.getInvestmentTokenSupply()).to.be.approximately( + ethers.utils.parseUnits("30", 6), + getErrorRange(ethers.utils.parseUnits("30", 6)) + ) expect(await this.strategy.getEquityValuation(true, false)).to.be.approximately( ethers.utils.parseUnits("30", 6), getErrorRange(ethers.utils.parseUnits("30", 6)) diff --git a/test/strategy/UnifiedERC165.test.ts b/test/strategy/UnifiedERC165.test.ts index e50f978c..8a866ab1 100644 --- a/test/strategy/UnifiedERC165.test.ts +++ b/test/strategy/UnifiedERC165.test.ts @@ -2,7 +2,7 @@ import { expect } from "chai" export function testERC165() { describe("ERC165", async function () { - it("should success to support all interfaces that strategy implements", async function () { + it("should succeed to support all interfaces that strategy implements", async function () { expect(await this.strategy.supportsInterface("0x49147370")).to.equal(true) // IAum expect(await this.strategy.supportsInterface("0x52f3b8ca")).to.equal(true) // IFee expect(await this.strategy.supportsInterface("0xa9233731")).to.equal(true) // IInvestable diff --git a/test/strategy/UnifiedFee.test.ts b/test/strategy/UnifiedFee.test.ts index 7c42ca06..6fff38ea 100644 --- a/test/strategy/UnifiedFee.test.ts +++ b/test/strategy/UnifiedFee.test.ts @@ -1,11 +1,11 @@ +import { mine } from "@nomicfoundation/hardhat-network-helpers" import { expect } from "chai" import { ethers } from "hardhat" -import { getErrorRange, airdropToken, getMonthsInSeconds } from "../shared/utils" -import { mine } from "@nomicfoundation/hardhat-network-helpers" +import { airdropToken, getErrorRange, getMonthsInSeconds } from "../shared/utils" export function testFee() { describe("Fee", async function () { - it("should success when any user calls claim fee", async function () { + it("should succeed when any user calls claim fee", async function () { airdropToken(this.impersonatedSigner, this.user0, this.usdc, ethers.utils.parseUnits("10000", 6)) await this.usdc.connect(this.user0).approve(this.strategy.address, ethers.utils.parseUnits("10000", 6)) @@ -51,7 +51,7 @@ export function testFee() { ) }) - it("should success when a single user withdraws and withdrawal fee is 30%", async function () { + it("should succeed when a single user withdraws and withdrawal fee is 30%", async function () { airdropToken(this.impersonatedSigner, this.user0, this.usdc, ethers.utils.parseUnits("200", 6)) await this.strategy.connect(this.owner).setWithdrawalFee(30000, []) @@ -59,12 +59,11 @@ export function testFee() { await this.usdc.connect(this.user0).approve(this.strategy.address, ethers.utils.parseUnits("100", 6)) await this.strategy.connect(this.user0).deposit(ethers.utils.parseUnits("100", 6), this.user0.address, []) - await this.investmentToken.connect(this.user0).approve(this.strategy.address, ethers.utils.parseUnits("100", 6)) - await expect( - this.strategy.connect(this.user0).withdraw(ethers.utils.parseUnits("100", 6), this.user0.address, []) - ) + const availableTokenBalance = await this.investmentToken.connect(this.user0).balanceOf(this.user0.address) + await this.investmentToken.connect(this.user0).approve(this.strategy.address, availableTokenBalance) + await expect(this.strategy.connect(this.user0).withdraw(availableTokenBalance, this.user0.address, [])) .to.emit(this.strategy, "Withdrawal") - .withArgs(this.user0.address, this.user0.address, ethers.utils.parseUnits("100", 6)) + .withArgs(this.user0.address, this.user0.address, availableTokenBalance) expect(await this.usdc.balanceOf(this.user0.address)).to.be.approximately( ethers.utils.parseUnits("170", 6), @@ -75,7 +74,7 @@ export function testFee() { expect(await this.strategy.getEquityValuation(true, false)).to.equal(0) }) - it("should success when multiple users withdraw and withdrawal fee is 30%", async function () { + it("should succeed when multiple users withdraw and withdrawal fee is 30%", async function () { airdropToken(this.impersonatedSigner, this.user0, this.usdc, ethers.utils.parseUnits("100", 6)) airdropToken(this.impersonatedSigner, this.user1, this.usdc, ethers.utils.parseUnits("100", 6)) airdropToken(this.impersonatedSigner, this.user2, this.usdc, ethers.utils.parseUnits("100", 6)) @@ -124,7 +123,10 @@ export function testFee() { ethers.utils.parseUnits("84", 6), getErrorRange(ethers.utils.parseUnits("84", 6)) ) - expect(await this.investmentToken.balanceOf(this.user0.address)).to.equal(ethers.utils.parseUnits("10", 6)) + expect(await this.investmentToken.balanceOf(this.user0.address)).to.be.approximately( + ethers.utils.parseUnits("10", 6), + getErrorRange(ethers.utils.parseUnits("10", 6)) + ) expect(await this.investmentToken.balanceOf(this.user1.address)).to.be.approximately( ethers.utils.parseUnits("10", 6), getErrorRange(ethers.utils.parseUnits("10", 6)) diff --git a/test/strategy/UnifiedOwnable.test.ts b/test/strategy/UnifiedOwnable.test.ts index 4baf8e79..d406d3e7 100644 --- a/test/strategy/UnifiedOwnable.test.ts +++ b/test/strategy/UnifiedOwnable.test.ts @@ -56,7 +56,7 @@ export function testOwnable() { ) }) - it("should success when the owner user sets withdrawal fee to 30%", async function () { + it("should succeed when the owner user sets withdrawal fee to 30%", async function () { expect(await this.strategy.setDepositFee(30000, [])) .to.emit(this.strategy, "DepositFeeChange") .withArgs(30000, []) @@ -64,7 +64,7 @@ export function testOwnable() { expect(await this.strategy.getDepositFee([])).to.equal(30000) }) - it("should success when the owner user sets deposit fee to 30%", async function () { + it("should succeed when the owner user sets deposit fee to 30%", async function () { expect(await this.strategy.setWithdrawalFee(30000, [])) .to.emit(this.strategy, "WithdrawalFeeChange") .withArgs(30000, []) @@ -72,7 +72,7 @@ export function testOwnable() { expect(await this.strategy.getWithdrawalFee([])).to.equal(30000) }) - it("should success when the owner user sets performance fee to 30%", async function () { + it("should succeed when the owner user sets performance fee to 30%", async function () { expect(await this.strategy.setPerformanceFee(30000, [])) .to.emit(this.strategy, "PerformanceFeeChange") .withArgs(30000, []) @@ -80,7 +80,7 @@ export function testOwnable() { expect(await this.strategy.getPerformanceFee([])).to.equal(30000) }) - it("should success when the owner user sets fee receiver", async function () { + it("should succeed when the owner user sets fee receiver", async function () { expect(await this.strategy.setFeeReceiver(this.user0.address, [])) .to.emit(this.strategy, "FeeReceiverChange") .withArgs(this.user0.address, []) @@ -88,29 +88,29 @@ export function testOwnable() { expect(await this.strategy.getFeeReceiver([])).to.equal(this.user0.address) }) - it("should success when the owner user sets investment token", async function () { + it("should succeed when the owner user sets investment token", async function () { expect(await this.strategy.setInvestmentToken(this.usdc.address)).not.to.be.reverted expect(await this.strategy.getInvestmentToken()).to.equal(this.usdc.address) }) - it("should success when the owner user sets total investment limit", async function () { + it("should succeed when the owner user sets total investment limit", async function () { expect(await this.strategy.setTotalInvestmentLimit(0)).not.to.be.reverted expect(await this.strategy.getTotalInvestmentLimit()).to.equal(0) }) - it("should success when the owner user sets investment limit per address", async function () { + it("should succeed when the owner user sets investment limit per address", async function () { expect(await this.strategy.setInvestmentLimitPerAddress(0)).not.to.be.reverted expect(await this.strategy.getInvestmentLimitPerAddress()).to.equal(0) }) - it("should success when the owner user sets price oracle", async function () { + it("should succeed when the owner user sets price oracle", async function () { expect(await this.strategy.setPriceOracle(this.user0.address)).not.to.be.reverted }) - it("should success when the owner user sets swap service", async function () { + it("should succeed when the owner user sets swap service", async function () { expect(await this.strategy.setSwapService(0, this.user0.address)).not.to.be.reverted }) }) diff --git a/test/strategy/UnifiedReapReward.test.ts b/test/strategy/UnifiedReapReward.test.ts index 2a05177a..7260d252 100644 --- a/test/strategy/UnifiedReapReward.test.ts +++ b/test/strategy/UnifiedReapReward.test.ts @@ -1,11 +1,11 @@ +import { mine } from "@nomicfoundation/hardhat-network-helpers" import { expect } from "chai" import { ethers } from "hardhat" import { airdropToken, getDaysInSeconds, getMonthsInSeconds, getYearsInSeconds } from "../shared/utils" -import { mine } from "@nomicfoundation/hardhat-network-helpers" export function testReapReward() { describe("ReapReward", async function () { - it("should success when any user processes reward", async function () { + it("should succeed when any user processes reward", async function () { airdropToken(this.impersonatedSigner, this.user0, this.usdc, ethers.utils.parseUnits("10000", 6)) await this.usdc.connect(this.user0).approve(this.strategy.address, ethers.utils.parseUnits("10000", 6)) @@ -17,7 +17,7 @@ export function testReapReward() { await expect(this.strategy.connect(this.user1).processReward([], [])).to.emit(this.strategy, "RewardProcess") }) - it("should success to earn larger reward when reap after 1 year", async function () { + it("should succeed to earn larger reward when reap after 1 year", async function () { if ((await this.strategy.humanReadableName()) == "Cash strategy") { return } diff --git a/test/strategy/UnifiedUpgradeable.test.ts b/test/strategy/UnifiedUpgradeable.test.ts index b2637b16..93acc194 100644 --- a/test/strategy/UnifiedUpgradeable.test.ts +++ b/test/strategy/UnifiedUpgradeable.test.ts @@ -3,7 +3,7 @@ import { ethers, upgrades } from "hardhat" export function testUpgradeable() { describe("Upgradeable", async function () { - it("should success when the owner user upgrades", async function () { + it("should succeed when the owner user upgrades", async function () { const addr_before_upgrade = await upgrades.erc1967.getImplementationAddress(this.strategy.address) const TestUpgradedStrategy = await ethers.getContractFactory("TestUpgradedStrategy") @@ -38,7 +38,7 @@ export function testUpgradeable() { expect(addr_before_upgrade != addr_after_upgrade).to.equal(true) }) - it("should success when the strategy is paused", async function () { + it("should succeed when the strategy is paused", async function () { expect(await this.strategy.pause()).not.to.be.reverted const addr_before_upgrade = await upgrades.erc1967.getImplementationAddress(this.strategy.address) @@ -105,7 +105,7 @@ export function testUpgradeable() { ).to.be.revertedWith("Ownable: caller is not the owner") }) - it("should success to leave all common state variables' value intact", async function () { + it("should succeed to leave all common state variables' value intact", async function () { // IAum const investmentTokenSupplyBefore = await this.strategy.getInvestmentTokenSupply() // Don't check asset balances, liability balances, asset valuations, liability valuations diff --git a/test/strategy/UnifiedWithdraw.test.ts b/test/strategy/UnifiedWithdraw.test.ts index d9567a4f..7921c334 100644 --- a/test/strategy/UnifiedWithdraw.test.ts +++ b/test/strategy/UnifiedWithdraw.test.ts @@ -1,19 +1,20 @@ import { expect } from "chai" import { ethers } from "hardhat" -import { getErrorRange, airdropToken } from "../shared/utils" +import { airdropToken, getErrorRange } from "../shared/utils" export function testWithdraw() { describe("Withdraw", async function () { - it("should success when a single user withdraws InvestmentToken that he/she has - 0", async function () { + it("should succeed when a single user withdraws InvestmentToken that he/she has - full withdrawal", async function () { airdropToken(this.impersonatedSigner, this.user0, this.usdc, ethers.utils.parseUnits("100", 6)) await this.usdc.connect(this.user0).approve(this.strategy.address, ethers.utils.parseUnits("30", 6)) await this.strategy.connect(this.user0).deposit(ethers.utils.parseUnits("30", 6), this.user0.address, []) - await this.investmentToken.connect(this.user0).approve(this.strategy.address, ethers.utils.parseUnits("30", 6)) - await expect(this.strategy.connect(this.user0).withdraw(ethers.utils.parseUnits("30", 6), this.user0.address, [])) + const availableTokenBalance = await this.investmentToken.connect(this.user0).balanceOf(this.user0.address) + await this.investmentToken.connect(this.user0).approve(this.strategy.address, availableTokenBalance) + await expect(this.strategy.connect(this.user0).withdraw(availableTokenBalance, this.user0.address, [])) .to.emit(this.strategy, "Withdrawal") - .withArgs(this.user0.address, this.user0.address, ethers.utils.parseUnits("30", 6)) + .withArgs(this.user0.address, this.user0.address, availableTokenBalance) expect(await this.usdc.balanceOf(this.user0.address)).to.be.approximately( ethers.utils.parseUnits("100", 6), @@ -24,7 +25,7 @@ export function testWithdraw() { expect(await this.strategy.getEquityValuation(true, false)).to.equal(0) }) - it("should success when a single user withdraws InvestmentToken that he/she has - 1", async function () { + it("should succeed when a single user withdraws InvestmentToken that he/she has - partial withdrawal", async function () { airdropToken(this.impersonatedSigner, this.user0, this.usdc, ethers.utils.parseUnits("200", 6)) await this.usdc.connect(this.user0).approve(this.strategy.address, ethers.utils.parseUnits("100", 6)) @@ -39,15 +40,21 @@ export function testWithdraw() { ethers.utils.parseUnits("130", 6), getErrorRange(ethers.utils.parseUnits("130", 6)) ) - expect(await this.investmentToken.balanceOf(this.user0.address)).to.equal(ethers.utils.parseUnits("70", 6)) - expect(await this.strategy.getInvestmentTokenSupply()).to.equal(ethers.utils.parseUnits("70", 6)) + expect(await this.investmentToken.balanceOf(this.user0.address)).to.be.approximately( + ethers.utils.parseUnits("70", 6), + getErrorRange(ethers.utils.parseUnits("70", 6)) + ) + expect(await this.strategy.getInvestmentTokenSupply()).to.be.approximately( + ethers.utils.parseUnits("70", 6), + getErrorRange(ethers.utils.parseUnits("70", 6)) + ) expect(await this.strategy.getEquityValuation(true, false)).to.be.approximately( ethers.utils.parseUnits("70", 6), getErrorRange(ethers.utils.parseUnits("70", 6)) ) }) - it("should success when a single user withdraws InvestmentToken that he/she has - 2", async function () { + it("should succeed when a single user withdraws InvestmentToken that he/she has - partial withdraw on behalf", async function () { airdropToken(this.impersonatedSigner, this.user0, this.usdc, ethers.utils.parseUnits("200", 6)) await this.usdc.connect(this.user0).approve(this.strategy.address, ethers.utils.parseUnits("100", 6)) @@ -68,15 +75,21 @@ export function testWithdraw() { ethers.utils.parseUnits("30", 6).add(usdcBalanceBefore), getErrorRange(ethers.utils.parseUnits("30", 6).add(usdcBalanceBefore)) ) - expect(await this.investmentToken.balanceOf(this.user0.address)).to.equal(ethers.utils.parseUnits("70", 6)) - expect(await this.strategy.getInvestmentTokenSupply()).to.equal(ethers.utils.parseUnits("70", 6)) + expect(await this.investmentToken.balanceOf(this.user0.address)).to.be.approximately( + ethers.utils.parseUnits("70", 6), + getErrorRange(ethers.utils.parseUnits("70", 6)) + ) + expect(await this.strategy.getInvestmentTokenSupply()).to.be.approximately( + ethers.utils.parseUnits("70", 6), + getErrorRange(ethers.utils.parseUnits("70", 6)) + ) expect(await this.strategy.getEquityValuation(true, false)).to.be.approximately( ethers.utils.parseUnits("70", 6), getErrorRange(ethers.utils.parseUnits("70", 6)) ) }) - it("should success when a single user withdraws InvestmentToken that he/she has - 3", async function () { + it("should succeed when a single user withdraws InvestmentToken that he/she has - full withdrawal after deposit on behalf", async function () { airdropToken(this.impersonatedSigner, this.user0, this.usdc, ethers.utils.parseUnits("200", 6)) await this.usdc.connect(this.user0).approve(this.strategy.address, ethers.utils.parseUnits("100", 6)) @@ -84,12 +97,11 @@ export function testWithdraw() { const usdcBalanceBefore = await this.usdc.balanceOf(this.user1.address) - await this.investmentToken.connect(this.user1).approve(this.strategy.address, ethers.utils.parseUnits("100", 6)) - await expect( - this.strategy.connect(this.user1).withdraw(ethers.utils.parseUnits("100", 6), this.user1.address, []) - ) + const availableTokenBalance = await this.investmentToken.connect(this.user1).balanceOf(this.user1.address) + await this.investmentToken.connect(this.user1).approve(this.strategy.address, availableTokenBalance) + await expect(this.strategy.connect(this.user1).withdraw(availableTokenBalance, this.user1.address, [])) .to.emit(this.strategy, "Withdrawal") - .withArgs(this.user1.address, this.user1.address, ethers.utils.parseUnits("100", 6)) + .withArgs(this.user1.address, this.user1.address, availableTokenBalance) expect(await this.usdc.balanceOf(this.user0.address)).to.be.approximately( ethers.utils.parseUnits("100", 6), @@ -121,8 +133,14 @@ export function testWithdraw() { ethers.utils.parseUnits("70", 6), getErrorRange(ethers.utils.parseUnits("70", 6)) ) - expect(await this.investmentToken.balanceOf(this.user0.address)).to.equal(ethers.utils.parseUnits("30", 6)) - expect(await this.strategy.getInvestmentTokenSupply()).to.equal(ethers.utils.parseUnits("30", 6)) + expect(await this.investmentToken.balanceOf(this.user0.address)).to.be.approximately( + ethers.utils.parseUnits("30", 6), + getErrorRange(ethers.utils.parseUnits("30", 6)) + ) + expect(await this.strategy.getInvestmentTokenSupply()).to.be.approximately( + ethers.utils.parseUnits("30", 6), + getErrorRange(ethers.utils.parseUnits("30", 6)) + ) expect(await this.strategy.getEquityValuation(true, false)).to.be.approximately( ethers.utils.parseUnits("30", 6), getErrorRange(ethers.utils.parseUnits("30", 6)) @@ -143,15 +161,21 @@ export function testWithdraw() { ethers.utils.parseUnits("70", 6), getErrorRange(ethers.utils.parseUnits("70", 6)) ) - expect(await this.investmentToken.balanceOf(this.user0.address)).to.equal(ethers.utils.parseUnits("30", 6)) - expect(await this.strategy.getInvestmentTokenSupply()).to.equal(ethers.utils.parseUnits("30", 6)) + expect(await this.investmentToken.balanceOf(this.user0.address)).to.be.approximately( + ethers.utils.parseUnits("30", 6), + getErrorRange(ethers.utils.parseUnits("30", 6)) + ) + expect(await this.strategy.getInvestmentTokenSupply()).to.be.approximately( + ethers.utils.parseUnits("30", 6), + getErrorRange(ethers.utils.parseUnits("30", 6)) + ) expect(await this.strategy.getEquityValuation(true, false)).to.be.approximately( ethers.utils.parseUnits("30", 6), getErrorRange(ethers.utils.parseUnits("30", 6)) ) }) - it("should success when multiple users withdraw InvestmentTokens that they have - 0", async function () { + it("should succeed when multiple users withdraw InvestmentTokens that they have - full delayed withdraw", async function () { airdropToken(this.impersonatedSigner, this.user0, this.usdc, ethers.utils.parseUnits("100", 6)) airdropToken(this.impersonatedSigner, this.user1, this.usdc, ethers.utils.parseUnits("100", 6)) @@ -164,17 +188,18 @@ export function testWithdraw() { await this.strategy.connect(this.user1).deposit(ethers.utils.parseUnits("30", 6), this.user1.address, []) // The first user withdraws. - await this.investmentToken.connect(this.user0).approve(this.strategy.address, ethers.utils.parseUnits("30", 6)) - await expect(this.strategy.connect(this.user0).withdraw(ethers.utils.parseUnits("30", 6), this.user0.address, [])) + let availableTokenBalance = await this.investmentToken.connect(this.user0).balanceOf(this.user0.address) + await this.investmentToken.connect(this.user0).approve(this.strategy.address, availableTokenBalance) + await expect(this.strategy.connect(this.user0).withdraw(availableTokenBalance, this.user0.address, [])) .to.emit(this.strategy, "Withdrawal") - .withArgs(this.user0.address, this.user0.address, ethers.utils.parseUnits("30", 6)) + .withArgs(this.user0.address, this.user0.address, availableTokenBalance) // The second user withdraws. - const investmentTokenBalance = await this.investmentToken.balanceOf(this.user1.address) - await this.investmentToken.connect(this.user1).approve(this.strategy.address, investmentTokenBalance) - await expect(this.strategy.connect(this.user1).withdraw(investmentTokenBalance, this.user1.address, [])) + availableTokenBalance = await this.investmentToken.balanceOf(this.user1.address) + await this.investmentToken.connect(this.user1).approve(this.strategy.address, availableTokenBalance) + await expect(this.strategy.connect(this.user1).withdraw(availableTokenBalance, this.user1.address, [])) .to.emit(this.strategy, "Withdrawal") - .withArgs(this.user1.address, this.user1.address, investmentTokenBalance) + .withArgs(this.user1.address, this.user1.address, availableTokenBalance) expect(await this.usdc.balanceOf(this.user0.address)).to.be.approximately( ethers.utils.parseUnits("100", 6), @@ -190,7 +215,7 @@ export function testWithdraw() { expect(await this.strategy.getEquityValuation(true, false)).to.equal(0) }) - it("should success when multiple users withdraw InvestmentTokens that they have - 1", async function () { + it("should succeed when multiple users withdraw InvestmentTokens that they have - partial delayed withdraw", async function () { airdropToken(this.impersonatedSigner, this.user0, this.usdc, ethers.utils.parseUnits("100", 6)) airdropToken(this.impersonatedSigner, this.user1, this.usdc, ethers.utils.parseUnits("100", 6)) @@ -202,13 +227,13 @@ export function testWithdraw() { await this.usdc.connect(this.user1).approve(this.strategy.address, ethers.utils.parseUnits("30", 6)) await this.strategy.connect(this.user1).deposit(ethers.utils.parseUnits("30", 6), this.user1.address, []) - // The first user withdraws. + // The first user partial withdraws. await this.investmentToken.connect(this.user0).approve(this.strategy.address, ethers.utils.parseUnits("15", 6)) await expect(this.strategy.connect(this.user0).withdraw(ethers.utils.parseUnits("15", 6), this.user0.address, [])) .to.emit(this.strategy, "Withdrawal") .withArgs(this.user0.address, this.user0.address, ethers.utils.parseUnits("15", 6)) - // The second user withdraws. + // The second user partial withdraws. const investmentTokenBalance = await this.investmentToken.balanceOf(this.user1.address) const investmentTokenBalanceHalf = Math.floor(investmentTokenBalance / 2) await this.investmentToken.connect(this.user1).approve(this.strategy.address, investmentTokenBalanceHalf) @@ -224,7 +249,10 @@ export function testWithdraw() { ethers.utils.parseUnits("85", 6), getErrorRange(ethers.utils.parseUnits("85", 6)) ) - expect(await this.investmentToken.balanceOf(this.user0.address)).to.equal(ethers.utils.parseUnits("15", 6)) + expect(await this.investmentToken.balanceOf(this.user0.address)).to.be.approximately( + ethers.utils.parseUnits("15", 6), + getErrorRange(ethers.utils.parseUnits("15", 6)) + ) expect(await this.investmentToken.balanceOf(this.user1.address)).to.be.approximately( ethers.utils.parseUnits("15", 6), getErrorRange(ethers.utils.parseUnits("15", 6)) @@ -239,7 +267,7 @@ export function testWithdraw() { ) }) - it("should success when multiple users withdraw InvestmentTokens that they have - 2", async function () { + it("should succeed when multiple users withdraw InvestmentTokens that they have - full immediate withdrawal", async function () { airdropToken(this.impersonatedSigner, this.user0, this.usdc, ethers.utils.parseUnits("100", 6)) airdropToken(this.impersonatedSigner, this.user1, this.usdc, ethers.utils.parseUnits("100", 6)) @@ -247,18 +275,19 @@ export function testWithdraw() { await this.usdc.connect(this.user0).approve(this.strategy.address, ethers.utils.parseUnits("30", 6)) await this.strategy.connect(this.user0).deposit(ethers.utils.parseUnits("30", 6), this.user0.address, []) - // The first user withdraws. - await this.investmentToken.connect(this.user0).approve(this.strategy.address, ethers.utils.parseUnits("30", 6)) - await expect(this.strategy.connect(this.user0).withdraw(ethers.utils.parseUnits("30", 6), this.user0.address, [])) + // The first user fully withdraws. + let investmentTokenBalance = await this.investmentToken.balanceOf(this.user0.address) + await this.investmentToken.connect(this.user0).approve(this.strategy.address, investmentTokenBalance) + await expect(this.strategy.connect(this.user0).withdraw(investmentTokenBalance, this.user0.address, [])) .to.emit(this.strategy, "Withdrawal") - .withArgs(this.user0.address, this.user0.address, ethers.utils.parseUnits("30", 6)) + .withArgs(this.user0.address, this.user0.address, investmentTokenBalance) // The second user deposits. await this.usdc.connect(this.user1).approve(this.strategy.address, ethers.utils.parseUnits("30", 6)) await this.strategy.connect(this.user1).deposit(ethers.utils.parseUnits("30", 6), this.user1.address, []) - // The second user withdraws. - const investmentTokenBalance = await this.investmentToken.balanceOf(this.user1.address) + // The second user fully withdraws. + investmentTokenBalance = await this.investmentToken.balanceOf(this.user1.address) await this.investmentToken.connect(this.user1).approve(this.strategy.address, investmentTokenBalance) await expect(this.strategy.connect(this.user1).withdraw(investmentTokenBalance, this.user1.address, [])) .to.emit(this.strategy, "Withdrawal") @@ -312,9 +341,15 @@ export function testWithdraw() { ethers.utils.parseUnits("100", 6), getErrorRange(ethers.utils.parseUnits("100", 6)) ) - expect(await this.investmentToken.balanceOf(this.user0.address)).to.equal(ethers.utils.parseUnits("30", 6)) + expect(await this.investmentToken.balanceOf(this.user0.address)).to.be.approximately( + ethers.utils.parseUnits("30", 6), + getErrorRange(ethers.utils.parseUnits("30", 6)) + ) expect(await this.investmentToken.balanceOf(this.user1.address)).to.equal(0) - expect(await this.strategy.getInvestmentTokenSupply()).to.equal(ethers.utils.parseUnits("30", 6)) + expect(await this.strategy.getInvestmentTokenSupply()).to.be.approximately( + ethers.utils.parseUnits("30", 6), + getErrorRange(ethers.utils.parseUnits("30", 6)) + ) expect(await this.strategy.getEquityValuation(true, false)).to.be.approximately( ethers.utils.parseUnits("30", 6), getErrorRange(ethers.utils.parseUnits("30", 6)) @@ -334,13 +369,13 @@ export function testWithdraw() { await this.usdc.connect(this.user1).approve(this.strategy.address, ethers.utils.parseUnits("30", 6)) await this.strategy.connect(this.user1).deposit(ethers.utils.parseUnits("30", 6), this.user1.address, []) - // The first user withdraws. + // The first user withdraws half. await this.investmentToken.connect(this.user0).approve(this.strategy.address, ethers.utils.parseUnits("15", 6)) await expect(this.strategy.connect(this.user0).withdraw(ethers.utils.parseUnits("15", 6), this.user0.address, [])) .to.emit(this.strategy, "Withdrawal") .withArgs(this.user0.address, this.user0.address, ethers.utils.parseUnits("15", 6)) - // The second user withdraws. + // The second user withdraws too much. await this.investmentToken.connect(this.user1).approve(this.strategy.address, ethers.utils.parseUnits("50", 6)) await expect(this.strategy.connect(this.user1).withdraw(ethers.utils.parseUnits("50", 6), this.user1.address, [])) .to.be.reverted @@ -349,7 +384,7 @@ export function testWithdraw() { await this.usdc.connect(this.user2).approve(this.strategy.address, ethers.utils.parseUnits("30", 6)) await this.strategy.connect(this.user2).deposit(ethers.utils.parseUnits("30", 6), this.user2.address, []) - // The third user withdraws. + // The third user withdraws half. await this.investmentToken.connect(this.user2).approve(this.strategy.address, ethers.utils.parseUnits("15", 6)) await expect(this.strategy.connect(this.user2).withdraw(ethers.utils.parseUnits("15", 6), this.user2.address, [])) .to.emit(this.strategy, "Withdrawal") @@ -364,7 +399,10 @@ export function testWithdraw() { ethers.utils.parseUnits("85", 6), getErrorRange(ethers.utils.parseUnits("85", 6)) ) - expect(await this.investmentToken.balanceOf(this.user0.address)).to.equal(ethers.utils.parseUnits("15", 6)) + expect(await this.investmentToken.balanceOf(this.user0.address)).to.be.approximately( + ethers.utils.parseUnits("15", 6), + getErrorRange(ethers.utils.parseUnits("15", 6)) + ) expect(await this.investmentToken.balanceOf(this.user1.address)).to.be.approximately( ethers.utils.parseUnits("30", 6), getErrorRange(ethers.utils.parseUnits("30", 6)) diff --git a/test/strategy/cash/Cash.test.ts b/test/strategy/cash/Cash.test.ts index 1853b08b..cd06541d 100644 --- a/test/strategy/cash/Cash.test.ts +++ b/test/strategy/cash/Cash.test.ts @@ -8,7 +8,7 @@ testStrategy("Cash Strategy", "Cash", [], Oracles.gmx, [testCashAum, testCashUpg function testCashAum() { describe("AUM - Cash Strategy Specific", async function () { - it("should success after a single deposit", async function () { + it("should succeed after a single deposit", async function () { airdropToken(this.impersonatedSigner, this.user0, this.usdc, ethers.utils.parseUnits("100", 6)) await this.usdc.connect(this.user0).approve(this.strategy.address, ethers.utils.parseUnits("100", 6)) @@ -29,7 +29,7 @@ function testCashAum() { expect(await this.strategy.getEquityValuation(true, false)).to.equal(ethers.utils.parseUnits("100", 6)) }) - it("should success after multiple deposits and withdrawals", async function () { + it("should succeed after multiple deposits and withdrawals", async function () { airdropToken(this.impersonatedSigner, this.user0, this.usdc, ethers.utils.parseUnits("100", 6)) await this.usdc.connect(this.user0).approve(this.strategy.address, ethers.utils.parseUnits("50", 6)) @@ -63,7 +63,7 @@ function testCashAum() { function testCashUpgradeable() { describe("Upgradeable - Cash Strategy Specific", async function () { - it("should success to leave all strategy specific state variables' value intact", async function () { + it("should succeed to leave all strategy specific state variables' value intact", async function () { // IAum. const assetBalancesBefore = await this.strategy.getAssetBalances() const assetValuationsBefore = await this.strategy.getAssetValuations(true, false) diff --git a/test/strategy/stargate/Stargate.test.ts b/test/strategy/stargate/Stargate.test.ts index 3960b697..c8aced5f 100644 --- a/test/strategy/stargate/Stargate.test.ts +++ b/test/strategy/stargate/Stargate.test.ts @@ -2,7 +2,7 @@ import { expect } from "chai" import { ethers, upgrades } from "hardhat" import { StargateAddrs } from "../../shared/addresses" import { Oracles } from "../../shared/oracles" -import { getErrorRange, airdropToken } from "../../shared/utils" +import { airdropToken, getErrorRange } from "../../shared/utils" import { testStrategy } from "../Unified.test" testStrategy( @@ -35,7 +35,7 @@ testStrategy( function testStargateUsdcAum() { describe("AUM - Stargate USDC Strategy Specific", async function () { - it("should success after a single deposit", async function () { + it("should succeed after a single deposit", async function () { airdropToken(this.impersonatedSigner, this.user0, this.usdc, ethers.utils.parseUnits("100", 6)) await this.usdc.connect(this.user0).approve(this.strategy.address, ethers.utils.parseUnits("100", 6)) @@ -65,7 +65,7 @@ function testStargateUsdcAum() { ) }) - it("should success after multiple deposits and withdrawals", async function () { + it("should succeed after multiple deposits and withdrawals", async function () { airdropToken(this.impersonatedSigner, this.user0, this.usdc, ethers.utils.parseUnits("100", 6)) await this.usdc.connect(this.user0).approve(this.strategy.address, ethers.utils.parseUnits("50", 6)) @@ -145,7 +145,7 @@ function testStargateUsdcInitialize() { function testStargateUsdcUpgradeable() { describe("Upgradeable - Stargate USDC Strategy Specific", async function () { - it("should success to leave all strategy specific state variables' value intact", async function () { + it("should succeed to leave all strategy specific state variables' value intact", async function () { // IAum. const assetBalancesBefore = await this.strategy.getAssetBalances() const assetValuationsBefore = await this.strategy.getAssetValuations(true, false) @@ -211,7 +211,7 @@ function testStargateUsdcUpgradeable() { function testStargateUsdtAum() { describe("AUM - Stargate USDT Strategy Specific", async function () { - it("should success after a single deposit", async function () { + it("should succeed after a single deposit", async function () { airdropToken(this.impersonatedSigner, this.user0, this.usdc, ethers.utils.parseUnits("100", 6)) await this.usdc.connect(this.user0).approve(this.strategy.address, ethers.utils.parseUnits("100", 6)) @@ -241,7 +241,7 @@ function testStargateUsdtAum() { ) }) - it("should success after multiple deposits and withdrawals", async function () { + it("should succeed after multiple deposits and withdrawals", async function () { airdropToken(this.impersonatedSigner, this.user0, this.usdc, ethers.utils.parseUnits("100", 6)) await this.usdc.connect(this.user0).approve(this.strategy.address, ethers.utils.parseUnits("50", 6)) @@ -321,7 +321,7 @@ function testStargateUsdtInitialize() { function testStargateUsdtUpgradeable() { describe("Upgradeable - Stargate USDT Strategy Specific", async function () { - it("should success to leave all strategy specific state variables' value intact", async function () { + it("should succeed to leave all strategy specific state variables' value intact", async function () { // IAum. const assetBalancesBefore = await this.strategy.getAssetBalances() const assetValuationsBefore = await this.strategy.getAssetValuations(true, false) diff --git a/test/strategy/traderjoe/TraderJoe.test.ts b/test/strategy/traderjoe/TraderJoe.test.ts index 6d591cab..dc862f2f 100644 --- a/test/strategy/traderjoe/TraderJoe.test.ts +++ b/test/strategy/traderjoe/TraderJoe.test.ts @@ -3,7 +3,7 @@ import { ethers, upgrades } from "hardhat" import joePairAbi from "../../shared/abi/joePair.json" import { TraderJoeAddrs } from "../../shared/addresses" import { Oracles } from "../../shared/oracles" -import { getErrorRange, airdropToken } from "../../shared/utils" +import { airdropToken, getErrorRange } from "../../shared/utils" import { testStrategy } from "../Unified.test" testStrategy( @@ -16,7 +16,7 @@ testStrategy( function testTraderJoeAum() { describe("AUM - TraderJoe Strategy Specific", async function () { - it("should success after a single deposit", async function () { + it("should succeed after a single deposit", async function () { airdropToken(this.impersonatedSigner, this.user0, this.usdc, ethers.utils.parseUnits("100", 6)) await this.usdc.connect(this.user0).approve(this.strategy.address, ethers.utils.parseUnits("100", 6)) @@ -48,7 +48,7 @@ function testTraderJoeAum() { ) }) - it("should success after multiple deposits and withdrawals", async function () { + it("should succeed after multiple deposits and withdrawals", async function () { airdropToken(this.impersonatedSigner, this.user0, this.usdc, ethers.utils.parseUnits("100", 6)) await this.usdc.connect(this.user0).approve(this.strategy.address, ethers.utils.parseUnits("50", 6)) @@ -129,7 +129,7 @@ function testTraderJoeInitialize() { function testTraderJoeUpgradeable() { describe("Upgradeable - TraderJoe Strategy Specific", async function () { - it("should success to leave all strategy specific state variables' value intact", async function () { + it("should succeed to leave all strategy specific state variables' value intact", async function () { // IAum. const assetBalancesBefore = await this.strategy.getAssetBalances() const assetValuationsBefore = await this.strategy.getAssetValuations(true, false) From 3bf3632a2f2a9ae0b62ffeb340c9061f619d15a2 Mon Sep 17 00:00:00 2001 From: Daniel Graczer Date: Wed, 21 Sep 2022 17:16:22 +0700 Subject: [PATCH 2/7] Excluded typechain files from prettier --- .prettierignore | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.prettierignore b/.prettierignore index f268596e..6a406b7f 100644 --- a/.prettierignore +++ b/.prettierignore @@ -3,3 +3,5 @@ artifacts cache coverage* gasReporterOutput.json +typechain +typechain-types From 86315e45459ba94ceae2d706155526a46131460d Mon Sep 17 00:00:00 2001 From: Daniel Graczer Date: Wed, 21 Sep 2022 17:25:58 +0700 Subject: [PATCH 3/7] Updated Readme with post release tasks --- Readme.md | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) diff --git a/Readme.md b/Readme.md index a8cd1e61..47cfaa2d 100644 --- a/Readme.md +++ b/Readme.md @@ -37,12 +37,20 @@ 1. add comments to unified test scripts 1. improve unified test performance -## Releasing a new strategy +## Versioning of strategies The following section explains how the versioning of the strategies needs to be done in order to always be able to retrieve the source code of the deployed strategies, even if the strategy was not verified on the blockchain. Even if the strategy was verified, we would still need a way to easily find the git commit from which the deployed strategy was built. -### Git +### Version number + +Each strategy has a version number in semver format: https://semver.org/. This version number has to be part of the git tag. + +The most important thing is to bump the major version number at each breaking changes for example if the current verstion was 2.3.1, and there was a breaking change, then the new version should be 3.0.0 + +## Post strategy release tasks + +### Git tagging Currently we have a very simple git workflow. New strategies are released from the main branch and tagged by part of the 'name' of the strategy. For example if a new version of the cash strategy is released, and the current 'name' property in the source code of that strategy has the value of **block42.cash_strategy.cash_strategy_v1.0.2**, then the following git commands has to be executed. @@ -59,8 +67,6 @@ git push origin cash_strategy_v1.0.2 If you release multiple strategies at once, please create the tags for all strategies. -### Versioning of strategies - -Each strategy has a version number in semver format: https://semver.org/. This version number has to be part of the git tag. +### Verifying deployed contracts on Snowtrace -The most important thing is to bump the major version number at each breaking changes for example if the current verstion was 2.3.1, and there was a breaking change, then the new version should be 3.0.0 +Both the logic contract and the proxy contract should be verified after deploying them. From c09913ee70452d0aca6f71e2ed7d0213b3158e75 Mon Sep 17 00:00:00 2001 From: Daniel Graczer Date: Wed, 21 Sep 2022 17:42:46 +0700 Subject: [PATCH 4/7] Partially emoved buggy safeApprove --- Readme.md | 1 + contracts/common/bases/StrategyBaseUpgradeable.sol | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/Readme.md b/Readme.md index 47cfaa2d..c0bc46b8 100644 --- a/Readme.md +++ b/Readme.md @@ -36,6 +36,7 @@ 1. refactoring: scripts/helper.ts 1. add comments to unified test scripts 1. improve unified test performance +1. write our own version of safeApprove: https://github.com/OpenZeppelin/openzeppelin-contracts/issues/2219 ## Versioning of strategies diff --git a/contracts/common/bases/StrategyBaseUpgradeable.sol b/contracts/common/bases/StrategyBaseUpgradeable.sol index 413d7c5f..d30f24b0 100644 --- a/contracts/common/bases/StrategyBaseUpgradeable.sol +++ b/contracts/common/bases/StrategyBaseUpgradeable.sol @@ -382,7 +382,7 @@ abstract contract StrategyBaseUpgradeable is swapService_.router ); - IERC20Upgradeable(path[0]).safeApprove( + IERC20Upgradeable(path[0]).approve( address(traderjoeRouter), amountIn ); From 292131ffaa0b602b3b430019055be395edc771cc Mon Sep 17 00:00:00 2001 From: Daniel Graczer Date: Wed, 21 Sep 2022 18:46:32 +0700 Subject: [PATCH 5/7] Fixed Stargate reapReward swap route --- contracts/strategies/stargate/Stargate.sol | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/contracts/strategies/stargate/Stargate.sol b/contracts/strategies/stargate/Stargate.sol index ee34de86..d7df65e1 100644 --- a/contracts/strategies/stargate/Stargate.sol +++ b/contracts/strategies/stargate/Stargate.sol @@ -168,9 +168,9 @@ contract Stargate is UUPSUpgradeable, StrategyOwnablePausableBaseUpgradeable { strategyStorage.lpStaking.deposit(strategyStorage.farmId, 0); - address[] memory path = new address[](3); + address[] memory path = new address[](2); path[0] = address(strategyStorage.stgToken); - path[2] = address(depositToken); + path[1] = address(depositToken); swapExactTokensForTokens( swapService, From 7f08fc04d935760b92443eabd42996b4e2d6256b Mon Sep 17 00:00:00 2001 From: Daniel Graczer Date: Thu, 22 Sep 2022 15:04:01 +0700 Subject: [PATCH 6/7] Temporarily switch back 2 safeApproves to approve Openzeppelin has a bug in the safeApprove, we will need to write our own https://github.com/OpenZeppelin/openzeppelin-contracts/issues/2219 We still use safeApprove in cases where we are sure the subsequent transferFrom switches the approve balance back to 0. --- contracts/strategies/traderjoe/TraderJoe.sol | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/contracts/strategies/traderjoe/TraderJoe.sol b/contracts/strategies/traderjoe/TraderJoe.sol index 76bf1bdd..2036dbc5 100644 --- a/contracts/strategies/traderjoe/TraderJoe.sol +++ b/contracts/strategies/traderjoe/TraderJoe.sol @@ -94,11 +94,11 @@ contract TraderJoe is UUPSUpgradeable, StrategyOwnablePausableBaseUpgradeable { ); uint256 depositTokenDesired = amount - swapAmount; - strategyStorage.pairDepositToken.safeApprove( + strategyStorage.pairDepositToken.approve( address(strategyStorage.router), pairDepositTokenDesired ); - depositToken.safeApprove( + depositToken.approve( address(strategyStorage.router), depositTokenDesired ); From 19c19086cd00f3b6340296ecc5a6221f75a2af65 Mon Sep 17 00:00:00 2001 From: Daniel Graczer Date: Thu, 22 Sep 2022 18:26:20 +0700 Subject: [PATCH 7/7] changes after PR review --- Readme.md | 4 ++-- .../common/bases/PortfolioBaseUpgradeable.sol | 8 +++---- .../PercentageAllocation.sol | 2 +- contracts/strategies/cash/Cash.sol | 2 +- contracts/strategies/stargate/Stargate.sol | 4 ++-- contracts/strategies/template/Template.sol | 2 +- contracts/tests/MockPortfolio.sol | 2 +- contracts/tests/MockStrategy.sol | 2 +- .../PercentageAllocationV2.sol | 2 +- contracts/tests/strategies/cash/CashV2.sol | 2 +- .../tests/strategies/stargate/StargateV2.sol | 4 ++-- .../strategies/traderjoe/TraderJoeV2.sol | 5 ++-- package.json | 2 +- test/portfolio/UnifiedRebalance.test.ts | 24 +++++++++---------- test/portfolio/UnifiedWithdraw.test.ts | 2 +- .../PercentageAllocation.test.ts | 2 +- test/strategy/UnifiedWithdraw.test.ts | 20 ++++++++-------- test/strategy/cash/Cash.test.ts | 2 +- 18 files changed, 45 insertions(+), 46 deletions(-) diff --git a/Readme.md b/Readme.md index c0bc46b8..a7ba7363 100644 --- a/Readme.md +++ b/Readme.md @@ -54,13 +54,13 @@ The most important thing is to bump the major version number at each breaking ch ### Git tagging Currently we have a very simple git workflow. New strategies are released from the main branch -and tagged by part of the 'name' of the strategy. For example if a new version of the cash strategy is released, and the current 'name' property in the source code of that strategy has the value of **block42.cash_strategy.cash_strategy_v1.0.2**, then the following git commands has to be executed. +and tagged by part of the 'name' of the strategy. For example if a new version of the cash strategy is released, and the current 'name' property in the source code of that strategy has the value of **brokkr.cash_strategy.cash_strategy_v1.0.2**, then the following git commands has to be executed. ```bash git checkout main git pull # the tag name will be the the string following the last dot of the name, in the example the name was -# block42.cash_strategy.cash_strategy_v1.0.2 +# brokkr.cash_strategy.cash_strategy_v1.0.2 git tag -a cash_strategy_v1.0.2 # the command above will open a text editor, please write down how this version is different from the previuos one git push origin cash_strategy_v1.0.2 diff --git a/contracts/common/bases/PortfolioBaseUpgradeable.sol b/contracts/common/bases/PortfolioBaseUpgradeable.sol index 6c36cb68..5d7bd2e9 100644 --- a/contracts/common/bases/PortfolioBaseUpgradeable.sol +++ b/contracts/common/bases/PortfolioBaseUpgradeable.sol @@ -247,7 +247,7 @@ abstract contract PortfolioBaseUpgradeable is Math.SHORT_FIXED_DECIMAL_FACTOR / 100; if (embeddedAmount == 0) continue; - depositToken.safeApprove( + depositToken.approve( address(investableDescs[i].investable), embeddedAmount ); @@ -294,7 +294,7 @@ abstract contract PortfolioBaseUpgradeable is .getInvestmentTokenBalanceOf(address(this)) * amount) / investmentTokenSupply; if (embeddedTokenAmountToBurn == 0) continue; - embeddedInvestable.getInvestmentToken().safeApprove( + embeddedInvestable.getInvestmentToken().approve( address(embeddedInvestable), embeddedTokenAmountToBurn ); @@ -366,7 +366,7 @@ abstract contract PortfolioBaseUpgradeable is address(this) ) * targetInvestableEquities[i]) / currentInvestableEquities[i]; - embeddedInvestable.getInvestmentToken().safeApprove( + embeddedInvestable.getInvestmentToken().approve( address(embeddedInvestable), withdrawAmount ); @@ -392,7 +392,7 @@ abstract contract PortfolioBaseUpgradeable is ); if (depositAmount != 0) { - depositToken.safeApprove( + depositToken.approve( address(embeddedInvestable), depositAmount ); diff --git a/contracts/portfolios/percentageAllocation/PercentageAllocation.sol b/contracts/portfolios/percentageAllocation/PercentageAllocation.sol index 83a6f768..fb9e3e04 100644 --- a/contracts/portfolios/percentageAllocation/PercentageAllocation.sol +++ b/contracts/portfolios/percentageAllocation/PercentageAllocation.sol @@ -11,7 +11,7 @@ contract PercentageAllocation is { // solhint-disable-next-line const-name-snakecase string public constant name = - "block42.percentage_allocation_portfolio.percentage_allocation_portfolio_v1.0.1"; + "brokkr.percentage_allocation_portfolio.percentage_allocation_portfolio_v1.0.1"; // solhint-disable-next-line const-name-snakecase string public constant humanReadableName = "Percentage allocation portfolio"; diff --git a/contracts/strategies/cash/Cash.sol b/contracts/strategies/cash/Cash.sol index 3490174e..55d5f501 100644 --- a/contracts/strategies/cash/Cash.sol +++ b/contracts/strategies/cash/Cash.sol @@ -8,7 +8,7 @@ import "@openzeppelin/contracts-upgradeable/proxy/utils/UUPSUpgradeable.sol"; contract Cash is UUPSUpgradeable, StrategyOwnablePausableBaseUpgradeable { // solhint-disable-next-line const-name-snakecase - string public constant name = "block42.cash_strategy.cash_strategy_v1.0.1"; + string public constant name = "brokkr.cash_strategy.cash_strategy_v1.0.1"; // solhint-disable-next-line const-name-snakecase string public constant humanReadableName = "Cash strategy"; // solhint-disable-next-line const-name-snakecase diff --git a/contracts/strategies/stargate/Stargate.sol b/contracts/strategies/stargate/Stargate.sol index d7df65e1..670c3487 100644 --- a/contracts/strategies/stargate/Stargate.sol +++ b/contracts/strategies/stargate/Stargate.sol @@ -90,7 +90,7 @@ contract Stargate is UUPSUpgradeable, StrategyOwnablePausableBaseUpgradeable { uint256 lpBalanceBefore = strategyStorage.lpToken.balanceOf( address(this) ); - strategyStorage.poolDepositToken.safeApprove( + strategyStorage.poolDepositToken.approve( address(strategyStorage.router), amount ); @@ -105,7 +105,7 @@ contract Stargate is UUPSUpgradeable, StrategyOwnablePausableBaseUpgradeable { uint256 lpBalanceIncrement = lpBalanceAfter - lpBalanceBefore; - strategyStorage.lpToken.safeApprove( + strategyStorage.lpToken.approve( address(strategyStorage.lpStaking), lpBalanceIncrement ); diff --git a/contracts/strategies/template/Template.sol b/contracts/strategies/template/Template.sol index 920990c3..b1d47082 100644 --- a/contracts/strategies/template/Template.sol +++ b/contracts/strategies/template/Template.sol @@ -12,7 +12,7 @@ import "@openzeppelin/contracts-upgradeable/token/ERC20/utils/SafeERC20Upgradeab contract Template is StrategyOwnablePausableBaseUpgradeable { // solhint-disable-next-line const-name-snakecase string public constant name = - "block42.template_strategy."; + "brokkr.template_strategy."; // solhint-disable-next-line const-name-snakecase string public constant humanReadableName = "Template strategy"; // solhint-disable-next-line const-name-snakecase diff --git a/contracts/tests/MockPortfolio.sol b/contracts/tests/MockPortfolio.sol index bf70ca0d..3ea2e91b 100644 --- a/contracts/tests/MockPortfolio.sol +++ b/contracts/tests/MockPortfolio.sol @@ -6,7 +6,7 @@ import "../common/bases/PortfolioOwnableBaseUpgradeable.sol"; contract MockPortfolio is PortfolioOwnableBaseUpgradeable { // solhint-disable-next-line const-name-snakecase string public constant name = - "block42.mock_portfolio."; + "brokkr.mock_portfolio."; // solhint-disable-next-line const-name-snakecase string public constant humanReadableName = "Mock portfolio"; // solhint-disable-next-line const-name-snakecase diff --git a/contracts/tests/MockStrategy.sol b/contracts/tests/MockStrategy.sol index 6c8fb59e..041451d0 100644 --- a/contracts/tests/MockStrategy.sol +++ b/contracts/tests/MockStrategy.sol @@ -13,7 +13,7 @@ contract MockStrategy is { // solhint-disable-next-line const-name-snakecase string public constant name = - "block42.mock_strategy."; + "brokkr.mock_strategy."; // solhint-disable-next-line const-name-snakecase string public constant humanReadableName = "Mock strategy"; // solhint-disable-next-line const-name-snakecase diff --git a/contracts/tests/portfolios/percentageAllocation/PercentageAllocationV2.sol b/contracts/tests/portfolios/percentageAllocation/PercentageAllocationV2.sol index 9df1fb1b..89f031cc 100644 --- a/contracts/tests/portfolios/percentageAllocation/PercentageAllocationV2.sol +++ b/contracts/tests/portfolios/percentageAllocation/PercentageAllocationV2.sol @@ -11,7 +11,7 @@ contract PercentageAllocationV2 is { // solhint-disable-next-line const-name-snakecase string public constant name = - "block42.percentage_allocation_portfolio.percentage_allocation_portfolio_v2.0.0"; + "brokkr.percentage_allocation_portfolio.percentage_allocation_portfolio_v2.0.0"; // solhint-disable-next-line const-name-snakecase string public constant humanReadableName = "Percentage allocation portfolio"; diff --git a/contracts/tests/strategies/cash/CashV2.sol b/contracts/tests/strategies/cash/CashV2.sol index 20aa803d..f0fd9fdb 100644 --- a/contracts/tests/strategies/cash/CashV2.sol +++ b/contracts/tests/strategies/cash/CashV2.sol @@ -8,7 +8,7 @@ import "@openzeppelin/contracts-upgradeable/proxy/utils/UUPSUpgradeable.sol"; contract CashV2 is UUPSUpgradeable, StrategyOwnablePausableBaseUpgradeable { // solhint-disable-next-line const-name-snakecase - string public constant name = "block42.cash_strategy.cash_strategy_initial"; + string public constant name = "brokkr.cash_strategy.cash_strategy_initial"; // solhint-disable-next-line const-name-snakecase string public constant humanReadableName = "Cash strategy"; // solhint-disable-next-line const-name-snakecase diff --git a/contracts/tests/strategies/stargate/StargateV2.sol b/contracts/tests/strategies/stargate/StargateV2.sol index ff73b72f..92866ffc 100644 --- a/contracts/tests/strategies/stargate/StargateV2.sol +++ b/contracts/tests/strategies/stargate/StargateV2.sol @@ -90,7 +90,7 @@ contract StargateV2 is UUPSUpgradeable, StrategyOwnablePausableBaseUpgradeable { uint256 lpBalanceBefore = strategyStorage.lpToken.balanceOf( address(this) ); - strategyStorage.poolDepositToken.safeApprove( + strategyStorage.poolDepositToken.approve( address(strategyStorage.router), amount ); @@ -105,7 +105,7 @@ contract StargateV2 is UUPSUpgradeable, StrategyOwnablePausableBaseUpgradeable { uint256 lpBalanceIncrement = lpBalanceAfter - lpBalanceBefore; - strategyStorage.lpToken.safeApprove( + strategyStorage.lpToken.approve( address(strategyStorage.lpStaking), lpBalanceIncrement ); diff --git a/contracts/tests/strategies/traderjoe/TraderJoeV2.sol b/contracts/tests/strategies/traderjoe/TraderJoeV2.sol index 33fb8a7b..d66cc747 100644 --- a/contracts/tests/strategies/traderjoe/TraderJoeV2.sol +++ b/contracts/tests/strategies/traderjoe/TraderJoeV2.sol @@ -15,7 +15,6 @@ contract TraderJoeV2 is { using SafeERC20Upgradeable for IInvestmentToken; using SafeERC20Upgradeable for IERC20Upgradeable; - using SafeERC20Upgradeable for ITraderJoePair; error InvalidTraderJoeLpToken(); @@ -98,11 +97,11 @@ contract TraderJoeV2 is ); uint256 depositTokenDesired = amount - swapAmount; - strategyStorage.pairDepositToken.safeApprove( + strategyStorage.pairDepositToken.approve( address(strategyStorage.router), pairDepositTokenDesired ); - depositToken.safeApprove( + depositToken.approve( address(strategyStorage.router), depositTokenDesired ); diff --git a/package.json b/package.json index f54af28f..6a14dc83 100644 --- a/package.json +++ b/package.json @@ -3,7 +3,7 @@ "version": "1.0.0", "description": "template for project setup", "main": "index.js", - "author": "block42", + "author": "brokkr", "license": "MIT", "devDependencies": { "@aave/core-v3": "^1.13.1", diff --git a/test/portfolio/UnifiedRebalance.test.ts b/test/portfolio/UnifiedRebalance.test.ts index 897165bb..9cbb82ea 100644 --- a/test/portfolio/UnifiedRebalance.test.ts +++ b/test/portfolio/UnifiedRebalance.test.ts @@ -7,14 +7,14 @@ import { getErrorRange } from "../shared/utils" export function testRebalance() { describe("Rebalance", async function () { - it("should succeed when the owner user rebalances - [100%, 0%, 0%] -> [33%, 33%, 34%]", async function () { + it("should succeed when the owner user rebalances - for example [100%, 0%, 0%] -> [33%, 33%, 34%]", async function () { const investableLength = (await this.portfolio.getInvestables()).length if (investableLength <= 1) { return } - // Set target allocations 100% to the first investable and 0% to the others. [100%, 0%, 0%] + // Set target allocations 100% to the first investable and 0% to the others. for example [100%, 0%, 0%] let allocations: number[] = [100000] for (let i = 1; i < investableLength; i++) { allocations.push(0) @@ -40,7 +40,7 @@ export function testRebalance() { getErrorRange(ethers.utils.parseUnits("10000", 6)) ) - // Set target allocations approximately equally. [33%, 33%, 34%] + // Set target allocations approximately equally. for example [33%, 33%, 34%] allocations = [] const equalAllocation = Math.floor(100 / investableLength - 1) for (let i = 0; i < investableLength - 1; i++) { @@ -93,14 +93,14 @@ export function testRebalance() { } }) - it("should succeed when the owner user rebalances - [50%, 50%, 0%] -> [33%, 33%, 34%]", async function () { + it("should succeed when the owner user rebalances - for example [50%, 50%, 0%] -> [33%, 33%, 34%]", async function () { const investableLength = (await this.portfolio.getInvestables()).length if (investableLength <= 1) { return } - // Set target allocations 50% to the first and second investable and 0% to the others. [50%, 50%, 0%] + // Set target allocations 50% to the first and second investable and 0% to the others. for example [50%, 50%, 0%] let allocations: number[] = [50000, 50000] for (let i = 2; i < investableLength; i++) { allocations.push(0) @@ -126,7 +126,7 @@ export function testRebalance() { getErrorRange(ethers.utils.parseUnits("10000", 6)) ) - // Set target allocations approximately equally. [33%, 33%, 34%] + // Set target allocations approximately equally. for example [33%, 33%, 34%] allocations = [] const equalAllocation = Math.floor(100 / investableLength - 1) for (let i = 0; i < investableLength - 1; i++) { @@ -179,7 +179,7 @@ export function testRebalance() { } }) - it("should succeed when the owner user rebalances and another user deposits into investable directly - [50%, 50%, 0%] -> [33%, 33%, 34%]", async function () { + it("should succeed when the owner user rebalances and another user deposits into investable directly - for example [50%, 50%, 0%] -> [33%, 33%, 34%]", async function () { const investableLength = (await this.portfolio.getInvestables()).length if (investableLength <= 1) { @@ -194,7 +194,7 @@ export function testRebalance() { await expect(investable.connect(this.user2).deposit(ethers.utils.parseUnits("3000", 6), this.user2.address, [])) .not.to.be.reverted - // Set target allocations 50% to the first and second investable and 0% to the others. [50%, 50%, 0%] + // Set target allocations 50% to the first and second investable and 0% to the others. for example [50%, 50%, 0%] let allocations: number[] = [50000, 50000] for (let i = 2; i < investableLength; i++) { allocations.push(0) @@ -220,7 +220,7 @@ export function testRebalance() { getErrorRange(ethers.utils.parseUnits("10000", 6)) ) - // Set target allocations approximately equally. [33%, 33%, 34%] + // Set target allocations approximately equally. for example [33%, 33%, 34%] allocations = [] const equalAllocation = Math.floor(100 / investableLength - 1) for (let i = 0; i < investableLength - 1; i++) { @@ -279,7 +279,7 @@ export function testRebalance() { } }) - it("should succeed when the owner user rebalances and another user withdraws from investable directly - [50%, 50%, 0%] ->[33%, 33%, 34%]", async function () { + it("should succeed when the owner user rebalances and another user withdraws from investable directly - for example [50%, 50%, 0%] -> [33%, 33%, 34%]", async function () { const investableLength = (await this.portfolio.getInvestables()).length if (investableLength <= 1) { @@ -294,7 +294,7 @@ export function testRebalance() { await expect(investable.connect(this.user2).deposit(ethers.utils.parseUnits("3000", 6), this.user2.address, [])) .not.to.be.reverted - // Set target allocations 50% to the first and second investable and 0% to the others. [50%, 50%, 0%] + // Set target allocations 50% to the first and second investable and 0% to the others. for example [50%, 50%, 0%] let allocations: number[] = [50000, 50000] for (let i = 2; i < investableLength; i++) { allocations.push(0) @@ -326,7 +326,7 @@ export function testRebalance() { await expect(investable.connect(this.user2).withdraw(ethers.utils.parseUnits("1500", 6), this.user2.address, [])) .not.to.be.reverted - // Set target allocations approximately equally. [33%, 33%, 34%] + // Set target allocations approximately equally. for example [33%, 33%, 34%] allocations = [] const equalAllocation = Math.floor(100 / investableLength - 1) for (let i = 0; i < investableLength - 1; i++) { diff --git a/test/portfolio/UnifiedWithdraw.test.ts b/test/portfolio/UnifiedWithdraw.test.ts index a4bcb31f..083f8211 100644 --- a/test/portfolio/UnifiedWithdraw.test.ts +++ b/test/portfolio/UnifiedWithdraw.test.ts @@ -9,9 +9,9 @@ export function testWithdraw() { it("should succeed when a single user withdraws InvestmentToken that he/she has - full withdrawal", async function () { await this.usdc.connect(this.user0).approve(this.portfolio.address, ethers.utils.parseUnits("3000", 6)) await this.portfolio.connect(this.user0).deposit(ethers.utils.parseUnits("3000", 6), this.user0.address, []) - await this.investmentToken.connect(this.user0).approve(this.portfolio.address, ethers.utils.parseUnits("3000", 6)) const availableTokenBalance = await this.investmentToken.balanceOf(this.user0.address) + await this.investmentToken.connect(this.user0).approve(this.portfolio.address, availableTokenBalance) await expect(this.portfolio.connect(this.user0).withdraw(availableTokenBalance, this.user0.address, [])) .to.emit(this.portfolio, "Withdrawal") .withArgs(this.user0.address, this.user0.address, availableTokenBalance) diff --git a/test/portfolio/percentageAllocation/PercentageAllocation.test.ts b/test/portfolio/percentageAllocation/PercentageAllocation.test.ts index 3a8d7306..91d11c6e 100644 --- a/test/portfolio/percentageAllocation/PercentageAllocation.test.ts +++ b/test/portfolio/percentageAllocation/PercentageAllocation.test.ts @@ -453,7 +453,7 @@ function testPercentageAllocationPortfolioUpgradeable() { // IInvestable. expect(await this.portfolio.name()).to.equal( - "block42.percentage_allocation_portfolio.percentage_allocation_portfolio_v2.0.0" + "brokkr.percentage_allocation_portfolio.percentage_allocation_portfolio_v2.0.0" ) expect(await this.portfolio.humanReadableName()).to.equal("Percentage allocation portfolio") expect(await this.portfolio.version()).to.equal("2.0.0") diff --git a/test/strategy/UnifiedWithdraw.test.ts b/test/strategy/UnifiedWithdraw.test.ts index 7921c334..1e0d7c13 100644 --- a/test/strategy/UnifiedWithdraw.test.ts +++ b/test/strategy/UnifiedWithdraw.test.ts @@ -227,13 +227,13 @@ export function testWithdraw() { await this.usdc.connect(this.user1).approve(this.strategy.address, ethers.utils.parseUnits("30", 6)) await this.strategy.connect(this.user1).deposit(ethers.utils.parseUnits("30", 6), this.user1.address, []) - // The first user partial withdraws. + // The first user partially withdraws. await this.investmentToken.connect(this.user0).approve(this.strategy.address, ethers.utils.parseUnits("15", 6)) await expect(this.strategy.connect(this.user0).withdraw(ethers.utils.parseUnits("15", 6), this.user0.address, [])) .to.emit(this.strategy, "Withdrawal") .withArgs(this.user0.address, this.user0.address, ethers.utils.parseUnits("15", 6)) - // The second user partial withdraws. + // The second user partially withdraws. const investmentTokenBalance = await this.investmentToken.balanceOf(this.user1.address) const investmentTokenBalanceHalf = Math.floor(investmentTokenBalance / 2) await this.investmentToken.connect(this.user1).approve(this.strategy.address, investmentTokenBalanceHalf) @@ -276,22 +276,22 @@ export function testWithdraw() { await this.strategy.connect(this.user0).deposit(ethers.utils.parseUnits("30", 6), this.user0.address, []) // The first user fully withdraws. - let investmentTokenBalance = await this.investmentToken.balanceOf(this.user0.address) - await this.investmentToken.connect(this.user0).approve(this.strategy.address, investmentTokenBalance) - await expect(this.strategy.connect(this.user0).withdraw(investmentTokenBalance, this.user0.address, [])) + let availableTokenBalance = await this.investmentToken.balanceOf(this.user0.address) + await this.investmentToken.connect(this.user0).approve(this.strategy.address, availableTokenBalance) + await expect(this.strategy.connect(this.user0).withdraw(availableTokenBalance, this.user0.address, [])) .to.emit(this.strategy, "Withdrawal") - .withArgs(this.user0.address, this.user0.address, investmentTokenBalance) + .withArgs(this.user0.address, this.user0.address, availableTokenBalance) // The second user deposits. await this.usdc.connect(this.user1).approve(this.strategy.address, ethers.utils.parseUnits("30", 6)) await this.strategy.connect(this.user1).deposit(ethers.utils.parseUnits("30", 6), this.user1.address, []) // The second user fully withdraws. - investmentTokenBalance = await this.investmentToken.balanceOf(this.user1.address) - await this.investmentToken.connect(this.user1).approve(this.strategy.address, investmentTokenBalance) - await expect(this.strategy.connect(this.user1).withdraw(investmentTokenBalance, this.user1.address, [])) + availableTokenBalance = await this.investmentToken.balanceOf(this.user1.address) + await this.investmentToken.connect(this.user1).approve(this.strategy.address, availableTokenBalance) + await expect(this.strategy.connect(this.user1).withdraw(availableTokenBalance, this.user1.address, [])) .to.emit(this.strategy, "Withdrawal") - .withArgs(this.user1.address, this.user1.address, investmentTokenBalance) + .withArgs(this.user1.address, this.user1.address, availableTokenBalance) expect(await this.usdc.balanceOf(this.user0.address)).to.be.approximately( ethers.utils.parseUnits("100", 6), diff --git a/test/strategy/cash/Cash.test.ts b/test/strategy/cash/Cash.test.ts index cd06541d..bd113b50 100644 --- a/test/strategy/cash/Cash.test.ts +++ b/test/strategy/cash/Cash.test.ts @@ -115,7 +115,7 @@ function testCashUpgradeable() { expect(equityValuationBefore.eq(equityValuationAfter)).to.equal(true) // IInvestable. - expect(await this.strategy.name()).to.equal("block42.cash_strategy.cash_strategy_initial") + expect(await this.strategy.name()).to.equal("brokkr.cash_strategy.cash_strategy_initial") expect(await this.strategy.humanReadableName()).to.equal("Cash strategy") expect(await this.strategy.version()).to.equal("2.0.0") })