diff --git a/spot-contracts/.eslintrc.js b/spot-contracts/.eslintrc.js index 96cf592c..ce316247 100644 --- a/spot-contracts/.eslintrc.js +++ b/spot-contracts/.eslintrc.js @@ -5,7 +5,7 @@ module.exports = { mocha: true, node: true, }, - plugins: ["@typescript-eslint"], + plugins: ["@typescript-eslint", "no-only-tests"], extends: ["standard", "plugin:prettier/recommended", "plugin:node/recommended"], parser: "@typescript-eslint/parser", parserOptions: { @@ -26,5 +26,6 @@ module.exports = { allowModules: ["hardhat", "ethers", "@openzeppelin/upgrades-core"], }, ], + "no-only-tests/no-only-tests": "error", }, }; diff --git a/spot-contracts/contracts/BondIssuer.sol b/spot-contracts/contracts/BondIssuer.sol index 5af63620..7ceb1598 100644 --- a/spot-contracts/contracts/BondIssuer.sol +++ b/spot-contracts/contracts/BondIssuer.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: BUSL-1.1 -pragma solidity ^0.8.18; +pragma solidity ^0.8.19; import { OwnableUpgradeable } from "@openzeppelin/contracts-upgradeable/access/OwnableUpgradeable.sol"; import { EnumerableSetUpgradeable } from "@openzeppelin/contracts-upgradeable/utils/structs/EnumerableSetUpgradeable.sol"; diff --git a/spot-contracts/contracts/PerpetualTranche.sol b/spot-contracts/contracts/PerpetualTranche.sol index 32a81542..be948d60 100644 --- a/spot-contracts/contracts/PerpetualTranche.sol +++ b/spot-contracts/contracts/PerpetualTranche.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: BUSL-1.1 -pragma solidity ^0.8.18; +pragma solidity ^0.8.19; import { OwnableUpgradeable } from "@openzeppelin/contracts-upgradeable/access/OwnableUpgradeable.sol"; import { PausableUpgradeable } from "@openzeppelin/contracts-upgradeable/security/PausableUpgradeable.sol"; diff --git a/spot-contracts/contracts/RouterV1.sol b/spot-contracts/contracts/RouterV1.sol index 388630f6..1ec3216a 100644 --- a/spot-contracts/contracts/RouterV1.sol +++ b/spot-contracts/contracts/RouterV1.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: BUSL-1.1 -pragma solidity ^0.8.18; +pragma solidity ^0.8.19; import { SafeERC20Upgradeable } from "@openzeppelin/contracts-upgradeable/token/ERC20/utils/SafeERC20Upgradeable.sol"; import { SafeCastUpgradeable } from "@openzeppelin/contracts-upgradeable/utils/math/SafeCastUpgradeable.sol"; diff --git a/spot-contracts/contracts/_interfaces/IVault.sol b/spot-contracts/contracts/_interfaces/IVault.sol new file mode 100644 index 00000000..22339e40 --- /dev/null +++ b/spot-contracts/contracts/_interfaces/IVault.sol @@ -0,0 +1,96 @@ +// SPDX-License-Identifier: GPL-3.0-or-later +pragma solidity ^0.8.0; + +import { IERC20Upgradeable } from "@openzeppelin/contracts-upgradeable/token/ERC20/IERC20Upgradeable.sol"; + +/// @notice Expected asset to be a valid vault asset. +/// @param token Address of the token. +error UnexpectedAsset(IERC20Upgradeable token); + +/// @notice Expected transfer out asset to not be a vault asset. +/// @param token Address of the token transferred. +error UnauthorizedTransferOut(IERC20Upgradeable token); + +/// @notice Expected vault assets to be deployed. +error NoDeployment(); + +/* + * @title IVault + * + * @notice The standard interface for a generic vault as described by the "Vault Framework". + * http://thinking.farm/essays/2022-10-05-mechanical-finance/ + * + * Users deposit a "underlying" asset and mint "notes" (or vault shares). + * The vault "deploys" underlying asset in a rules-based fashion (through a hard-coded strategy) + * to "earn" income. It "recovers" deployed assets once the investment matures. + * + * The vault operates through two external poke functions which off-chain keepers can execute. + * 1) `deploy`: When executed, the vault "puts to work" the underlying assets it holds. The vault + * usually returns other ERC-20 tokens which act as receipts of the deployment. + * 2) `recover`: When executed, the vault turns in the receipts and retrieves the underlying asset and + * usually collects some yield for this work. + * + * The rules of the deployment and recovery are specific to the vault strategy. + * + * At any time the vault will hold multiple ERC20 tokens, together referred to as the vault's "assets". + * They can be a combination of the underlying asset, the earned asset and the deployed assets (receipts). + * + * On redemption users burn their "notes" to receive a proportional slice of all the vault's assets. + * + */ + +interface IVault { + /// @notice Recovers deployed funds and redeploys them. + function recoverAndRedeploy() external; + + /// @notice Deploys deposited funds. + function deploy() external; + + /// @notice Recovers deployed funds. + function recover() external; + + /// @notice Deposits the underlying asset from {msg.sender} into the vault and mints notes. + /// @param amount The amount tokens to be deposited into the vault. + /// @return The amount of notes. + function deposit(uint256 amount) external returns (uint256); + + struct TokenAmount { + /// @notice The asset token redeemed. + IERC20Upgradeable token; + /// @notice The amount redeemed. + uint256 amount; + } + + /// @notice Burns notes and sends a proportional share of vault's assets back to {msg.sender}. + /// @param notes The amount of notes to be burnt. + /// @return The list of asset tokens and amounts redeemed. + function redeem(uint256 notes) external returns (TokenAmount[] memory); + + /// @return The total value of assets currently held by the vault, denominated in a standard unit of account. + function getTVL() external returns (uint256); + + /// @notice The ERC20 token that can be deposited into this vault. + function underlying() external view returns (IERC20Upgradeable); + + /// @param token The address of the asset ERC-20 token held by the vault. + /// @return The vault's asset token balance. + function vaultAssetBalance(IERC20Upgradeable token) external view returns (uint256); + + /// @return Total count of deployed asset tokens held by the vault. + function deployedCount() external view returns (uint256); + + /// @param i The index of a token. + /// @return The token address from the deployed asset token list by index. + function deployedAt(uint256 i) external view returns (IERC20Upgradeable); + + /// @return Total count of earned income tokens held by the vault. + function earnedCount() external pure returns (uint256); + + /// @param i The index of a token. + /// @return The token address from the earned income token list by index. + function earnedAt(uint256 i) external view returns (IERC20Upgradeable); + + /// @param token The address of a token to check. + /// @return If the given token is held by the vault. + function isVaultAsset(IERC20Upgradeable token) external view returns (bool); +} diff --git a/spot-contracts/contracts/_utils/BondHelpers.sol b/spot-contracts/contracts/_utils/BondHelpers.sol index c39d5119..fb2ca600 100644 --- a/spot-contracts/contracts/_utils/BondHelpers.sol +++ b/spot-contracts/contracts/_utils/BondHelpers.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: BUSL-1.1 -pragma solidity ^0.8.18; +pragma solidity ^0.8.19; import { SafeCastUpgradeable } from "@openzeppelin/contracts-upgradeable/utils/math/SafeCastUpgradeable.sol"; import { MathUpgradeable } from "@openzeppelin/contracts-upgradeable/utils/math/MathUpgradeable.sol"; diff --git a/spot-contracts/contracts/_utils/SignedMathHelpers.sol b/spot-contracts/contracts/_utils/SignedMathHelpers.sol index c622c2eb..2b6f4f9e 100644 --- a/spot-contracts/contracts/_utils/SignedMathHelpers.sol +++ b/spot-contracts/contracts/_utils/SignedMathHelpers.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: BUSL-1.1 -pragma solidity ^0.8.18; +pragma solidity ^0.8.19; /** * @title SignedMathHelpers diff --git a/spot-contracts/contracts/strategies/BasicFeeStrategy.sol b/spot-contracts/contracts/strategies/BasicFeeStrategy.sol index 4352b6c6..423a1fdb 100644 --- a/spot-contracts/contracts/strategies/BasicFeeStrategy.sol +++ b/spot-contracts/contracts/strategies/BasicFeeStrategy.sol @@ -1,9 +1,10 @@ // SPDX-License-Identifier: BUSL-1.1 -pragma solidity ^0.8.18; +pragma solidity ^0.8.19; import { OwnableUpgradeable } from "@openzeppelin/contracts-upgradeable/access/OwnableUpgradeable.sol"; import { SafeCastUpgradeable } from "@openzeppelin/contracts-upgradeable/utils/math/SafeCastUpgradeable.sol"; import { SignedMathUpgradeable } from "@openzeppelin/contracts-upgradeable/utils/math/SignedMathUpgradeable.sol"; +import { MathUpgradeable } from "@openzeppelin/contracts-upgradeable/utils/math/MathUpgradeable.sol"; import { SignedMathHelpers } from "../_utils/SignedMathHelpers.sol"; import { IERC20Upgradeable } from "@openzeppelin/contracts-upgradeable/token/ERC20/IERC20Upgradeable.sol"; @@ -26,8 +27,8 @@ error UnacceptablePercValue(int256 perc); contract BasicFeeStrategy is IFeeStrategy, OwnableUpgradeable { using SignedMathUpgradeable for int256; using SignedMathHelpers for int256; - using SafeCastUpgradeable for uint256; using SafeCastUpgradeable for int256; + using SafeCastUpgradeable for uint256; /// @dev {10 ** PERC_DECIMALS} is considered 1% uint8 public constant PERC_DECIMALS = 6; @@ -38,6 +39,10 @@ contract BasicFeeStrategy is IFeeStrategy, OwnableUpgradeable { /// @custom:oz-upgrades-unsafe-allow state-variable-immutable IERC20Upgradeable public immutable override feeToken; + /// @notice The address of the fee reserve. + /// @custom:oz-upgrades-unsafe-allow state-variable-immutable + address public immutable feeReserve; + /// @notice Fixed percentage of the mint amount to be used as fee. int256 public mintFeePerc; @@ -47,6 +52,11 @@ contract BasicFeeStrategy is IFeeStrategy, OwnableUpgradeable { /// @notice Fixed percentage of the rollover amount to be used as fee. int256 public rolloverFeePerc; + /// @notice Allows debasement of perp supply when the fee reserve is empty. + /// @dev When the fee amount is negative, ie) paid from the reserve to the user + /// this flag stops paying out more than the reserve balance through perp supply inflation. + bool public allowDebase; + // EVENTS /// @notice Event emitted when the mint fee percentage is updated. @@ -61,10 +71,16 @@ contract BasicFeeStrategy is IFeeStrategy, OwnableUpgradeable { /// @param rolloverFeePerc Rollover fee percentage. event UpdatedRolloverPerc(int256 rolloverFeePerc); + /// @notice Event emitted when the debasement rule is updated. + /// @param allow If debasement is allowed or not. + event UpdatedDebasementRule(bool allow); + /// @notice Contract constructor. /// @param feeToken_ Address of the fee ERC-20 token contract. - constructor(IERC20Upgradeable feeToken_) { + /// @param feeReserve_ Address of the fee reserve. + constructor(IERC20Upgradeable feeToken_, address feeReserve_) { feeToken = feeToken_; + feeReserve = feeReserve_; } /// @notice Contract initializer. @@ -103,21 +119,41 @@ contract BasicFeeStrategy is IFeeStrategy, OwnableUpgradeable { emit UpdatedRolloverPerc(rolloverFeePerc_); } + /// @notice Update debasement rule. + /// @param allow If debasement is allowed or not. + function allowDebasement(bool allow) public onlyOwner { + allowDebase = allow; + emit UpdatedDebasementRule(allow); + } + /// @inheritdoc IFeeStrategy function computeMintFees(uint256 mintAmt) external view override returns (int256, uint256) { - uint256 absoluteFee = (mintFeePerc.abs() * mintAmt) / HUNDRED_PERC; - return (mintFeePerc.sign() * absoluteFee.toInt256(), 0); + return (_computeFeeAmt(mintAmt, mintFeePerc), 0); } /// @inheritdoc IFeeStrategy function computeBurnFees(uint256 burnAmt) external view override returns (int256, uint256) { - uint256 absoluteFee = (burnFeePerc.abs() * burnAmt) / HUNDRED_PERC; - return (burnFeePerc.sign() * absoluteFee.toInt256(), 0); + return (_computeFeeAmt(burnAmt, burnFeePerc), 0); } /// @inheritdoc IFeeStrategy function computeRolloverFees(uint256 rolloverAmt) external view override returns (int256, uint256) { - uint256 absoluteFee = (rolloverFeePerc.abs() * rolloverAmt) / HUNDRED_PERC; - return (rolloverFeePerc.sign() * absoluteFee.toInt256(), 0); + return (_computeFeeAmt(rolloverAmt, rolloverFeePerc), 0); + } + + /// @dev Given the token amount and fee percentage, computes the integer amount to be charged as fees. + function _computeFeeAmt(uint256 amount, int256 feePerc) private view returns (int256) { + uint256 absoluteFee = (feePerc.abs() * amount) / HUNDRED_PERC; + int256 feeAmt = feePerc.sign() * absoluteFee.toInt256(); + + // when fee is to be paid by the user or when debasement is allowed + // use the entire fee amount + if (feeAmt >= 0 || allowDebase) { + return feeAmt; + } + + // fee is to be paid to the user and debasement is not allowed + // pay out only till the reserve is depleted + return -1 * MathUpgradeable.min(feeToken.balanceOf(feeReserve), absoluteFee).toInt256(); } } diff --git a/spot-contracts/contracts/strategies/CDRPricingStrategy.sol b/spot-contracts/contracts/strategies/CDRPricingStrategy.sol index 96d2c29f..03a93583 100644 --- a/spot-contracts/contracts/strategies/CDRPricingStrategy.sol +++ b/spot-contracts/contracts/strategies/CDRPricingStrategy.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: BUSL-1.1 -pragma solidity ^0.8.18; +pragma solidity ^0.8.19; import { TrancheHelpers } from "../_utils/BondHelpers.sol"; diff --git a/spot-contracts/contracts/strategies/TrancheClassDiscountStrategy.sol b/spot-contracts/contracts/strategies/TrancheClassDiscountStrategy.sol index 9220086f..7536de28 100644 --- a/spot-contracts/contracts/strategies/TrancheClassDiscountStrategy.sol +++ b/spot-contracts/contracts/strategies/TrancheClassDiscountStrategy.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: BUSL-1.1 -pragma solidity ^0.8.18; +pragma solidity ^0.8.19; import { OwnableUpgradeable } from "@openzeppelin/contracts-upgradeable/access/OwnableUpgradeable.sol"; import { TrancheData, TrancheDataHelpers, BondHelpers } from "../_utils/BondHelpers.sol"; diff --git a/spot-contracts/contracts/strategies/UnitPricingStrategy.sol b/spot-contracts/contracts/strategies/UnitPricingStrategy.sol index 75d0ff44..521fbb71 100644 --- a/spot-contracts/contracts/strategies/UnitPricingStrategy.sol +++ b/spot-contracts/contracts/strategies/UnitPricingStrategy.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: BUSL-1.1 -pragma solidity ^0.8.18; +pragma solidity ^0.8.19; import { IERC20Upgradeable } from "@openzeppelin/contracts-upgradeable/token/ERC20/IERC20Upgradeable.sol"; import { ITranche } from "../_interfaces/buttonwood/ITranche.sol"; diff --git a/spot-contracts/contracts/test/BondHelpersTester.sol b/spot-contracts/contracts/test/BondHelpersTester.sol index e02af7b5..29a315d1 100644 --- a/spot-contracts/contracts/test/BondHelpersTester.sol +++ b/spot-contracts/contracts/test/BondHelpersTester.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: BUSL-1.1 -pragma solidity ^0.8.18; +pragma solidity ^0.8.19; import { BondHelpers, TrancheData, TrancheDataHelpers } from "../_utils/BondHelpers.sol"; import { IBondController } from "../_interfaces/buttonwood/IBondController.sol"; diff --git a/spot-contracts/contracts/test/MathTester.sol b/spot-contracts/contracts/test/MathTester.sol index 2c67ef8c..ce473f28 100644 --- a/spot-contracts/contracts/test/MathTester.sol +++ b/spot-contracts/contracts/test/MathTester.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: BUSL-1.1 -pragma solidity ^0.8.18; +pragma solidity ^0.8.19; import { SignedMathHelpers } from "../_utils/SignedMathHelpers.sol"; diff --git a/spot-contracts/contracts/test/mocks/MockBondIssuer.sol b/spot-contracts/contracts/test/mocks/MockBondIssuer.sol deleted file mode 100644 index 15a794ac..00000000 --- a/spot-contracts/contracts/test/mocks/MockBondIssuer.sol +++ /dev/null @@ -1,19 +0,0 @@ -// SPDX-License-Identifier: BUSL-1.1 -pragma solidity ^0.8.18; - -contract MockBondIssuer { - address private _bond; - address public collateral; - - constructor(address collateral_) { - collateral = collateral_; - } - - function setLatestBond(address b) external { - _bond = b; - } - - function getLatestBond() external view returns (address) { - return _bond; - } -} diff --git a/spot-contracts/contracts/test/mocks/MockDiscountStrategy.sol b/spot-contracts/contracts/test/mocks/MockDiscountStrategy.sol deleted file mode 100644 index db321d1f..00000000 --- a/spot-contracts/contracts/test/mocks/MockDiscountStrategy.sol +++ /dev/null @@ -1,28 +0,0 @@ -// SPDX-License-Identifier: BUSL-1.1 -pragma solidity ^0.8.18; - -contract MockDiscountStrategy { - uint8 private _decimals; - - mapping(address => uint256) private _discounts; - - constructor() { - _decimals = 18; - } - - function setDecimals(uint8 d) external { - _decimals = d; - } - - function setTrancheDiscount(address t, uint256 y) external { - _discounts[t] = y; - } - - function computeTrancheDiscount(address t) external view returns (uint256) { - return _discounts[t]; - } - - function decimals() external view returns (uint8) { - return _decimals; - } -} diff --git a/spot-contracts/contracts/test/mocks/MockERC20.sol b/spot-contracts/contracts/test/mocks/MockERC20.sol index 9b219060..384eb928 100644 --- a/spot-contracts/contracts/test/mocks/MockERC20.sol +++ b/spot-contracts/contracts/test/mocks/MockERC20.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: BUSL-1.1 -pragma solidity ^0.8.18; +pragma solidity ^0.8.19; import { ERC20Upgradeable } from "@openzeppelin/contracts-upgradeable/token/ERC20/ERC20Upgradeable.sol"; diff --git a/spot-contracts/contracts/test/mocks/MockFeeStrategy.sol b/spot-contracts/contracts/test/mocks/MockFeeStrategy.sol deleted file mode 100644 index 730c16a1..00000000 --- a/spot-contracts/contracts/test/mocks/MockFeeStrategy.sol +++ /dev/null @@ -1,52 +0,0 @@ -// SPDX-License-Identifier: BUSL-1.1 -pragma solidity ^0.8.18; - -contract MockFeeStrategy { - int256 private _mintFee; - int256 private _burnFee; - int256 private _rolloverFee; - uint256 private _protocolFee; - address private _feeToken; - - function setFeeToken(address t) external { - _feeToken = t; - } - - function setMintFee(int256 f) external { - _mintFee = f; - } - - function setBurnFee(int256 f) external { - _burnFee = f; - } - - function setRolloverFee(int256 f) external { - _rolloverFee = f; - } - - function setProtocolFee(uint256 f) external { - _protocolFee = f; - } - - function feeToken() external view returns (address) { - return _feeToken; - } - - function computeMintFees( - uint256 /* f */ - ) external view returns (int256, uint256) { - return (_mintFee, _protocolFee); - } - - function computeBurnFees( - uint256 /* f */ - ) external view returns (int256, uint256) { - return (_burnFee, _protocolFee); - } - - function computeRolloverFees( - uint256 /* f */ - ) external view returns (int256, uint256) { - return (_rolloverFee, _protocolFee); - } -} diff --git a/spot-contracts/contracts/test/mocks/MockOracle.sol b/spot-contracts/contracts/test/mocks/MockOracle.sol deleted file mode 100644 index a8d16f98..00000000 --- a/spot-contracts/contracts/test/mocks/MockOracle.sol +++ /dev/null @@ -1,16 +0,0 @@ -// SPDX-License-Identifier: BUSL-1.1 -pragma solidity ^0.8.18; - -contract MockOracle { - uint256 private data; - bool private success; - - function getData() external view returns (uint256, bool) { - return (data, success); - } - - function setData(uint256 dt, bool v) external { - data = dt; - success = v; - } -} diff --git a/spot-contracts/contracts/test/mocks/MockPerpetualTranche.sol b/spot-contracts/contracts/test/mocks/MockPerpetualTranche.sol deleted file mode 100644 index eb89a66a..00000000 --- a/spot-contracts/contracts/test/mocks/MockPerpetualTranche.sol +++ /dev/null @@ -1,32 +0,0 @@ -// SPDX-License-Identifier: BUSL-1.1 -pragma solidity ^0.8.18; - -import { MockERC20 } from "./MockERC20.sol"; - -contract MockPerpetualTranche is MockERC20 { - uint256 private _reserveTrancheBalance; - uint256 public matureTrancheBalance; - address public collateral; - - function protocolFeeCollector() public view returns (address) { - return address(this); - } - - function getReserveTrancheBalance( - address /* token */ - ) public view returns (uint256) { - return _reserveTrancheBalance; - } - - function setReserveTrancheBalance(uint256 b) external { - _reserveTrancheBalance = b; - } - - function setMatureTrancheBalance(uint256 b) external { - matureTrancheBalance = b; - } - - function setCollateral(address c) external { - collateral = c; - } -} diff --git a/spot-contracts/contracts/test/mocks/MockPricingStrategy.sol b/spot-contracts/contracts/test/mocks/MockPricingStrategy.sol deleted file mode 100644 index 18dc18ce..00000000 --- a/spot-contracts/contracts/test/mocks/MockPricingStrategy.sol +++ /dev/null @@ -1,44 +0,0 @@ -// SPDX-License-Identifier: BUSL-1.1 -pragma solidity ^0.8.18; - -contract MockPricingStrategy { - uint8 private _decimals; - uint256 private _price; - - mapping(address => uint256) private _tranchePrice; - mapping(address => bool) private _tranchePriceSet; - - constructor() { - _decimals = 8; - _price = 10**8; - } - - function setDecimals(uint8 d) external { - _decimals = d; - } - - function setPrice(uint256 p) external { - _price = p; - } - - function setTranchePrice(address t, uint256 p) external { - _tranchePrice[t] = p; - _tranchePriceSet[t] = true; - } - - function computeTranchePrice(address t) external view returns (uint256) { - return _tranchePriceSet[t] ? _tranchePrice[t] : _price; - } - - function computeMatureTranchePrice( - address t, - uint256, /* collateralBalance */ - uint256 /* debt */ - ) external view returns (uint256) { - return _tranchePriceSet[t] ? _tranchePrice[t] : _price; - } - - function decimals() external view returns (uint8) { - return _decimals; - } -} diff --git a/spot-contracts/contracts/vaults/RolloverVault.sol b/spot-contracts/contracts/vaults/RolloverVault.sol index 01f9f7b1..19bf7eff 100644 --- a/spot-contracts/contracts/vaults/RolloverVault.sol +++ b/spot-contracts/contracts/vaults/RolloverVault.sol @@ -12,22 +12,11 @@ import { SafeERC20Upgradeable } from "@openzeppelin/contracts-upgradeable/token/ import { TrancheData, TrancheHelpers, BondHelpers } from "../_utils/BondHelpers.sol"; import { IERC20Upgradeable, IPerpetualTranche, IBondIssuer, IBondController, ITranche } from "../_interfaces/IPerpetualTranche.sol"; +import { IVault, UnexpectedAsset, UnauthorizedTransferOut, NoDeployment } from "../_interfaces/IVault.sol"; -// TODO: create a IVault interface // TODO: add mint cap // TODO: limit size of vault assets -/// @notice Expected asset to be a valid vault asset. -/// @param token Address of the token. -error UnexpectedAsset(IERC20Upgradeable token); - -/// @notice Expected transfer out asset to not be a vault asset. -/// @param token Address of the token transferred. -error UnauthorizedTransferOut(IERC20Upgradeable token); - -/// @notice Expected vault assets to be deployed. -error NoDeployment(); - /// @notice Storage array access out of bounds. error OutOfBounds(); @@ -35,31 +24,24 @@ error OutOfBounds(); * @title RolloverVault * * @notice A vault which generates yield (from fees) by performing rollovers on PerpetualTranche (or perp). + * The vault takes in AMPL or any other rebasing collateral as the "underlying" asset. * - * Users deposit a "underlying" asset (like AMPL or any other rebasing collateral) and mint "notes". - * The vault "deploys" underlying asset in a rules-based fashion to "earn" income. - * It "recovers" deployed assets once the investment matures. - * - * The vault operates through two external poke functions which off-chain keepers can execute. - * 1) `deploy`: When executed, the vault deposits the underlying asset into perp's current deposit bond + * Vault strategy: + * 1) deploy: The vault deposits the underlying asset into perp's current deposit bond * to get tranche tokens in return, it then swaps these fresh tranche tokens for * older tranche tokens (ones mature or approaching maturity) from perp. * system through a rollover operation and earns an income in perp tokens. - * 2) `recover`: When executed, the vault redeems tranches for the underlying asset. + * 2) recover: The vault redeems tranches for the underlying asset. * NOTE: It performs both mature and immature redemption. Read more: https://bit.ly/3tuN6OC * - * At any time the vault will hold multiple ERC20 tokens, together referred to as the vault's "assets". - * They can be a combination of the underlying asset, the earned asset and multiple tranches (deployed assets). - * - * On redemption users burn their "notes" to receive a proportional slice of all the vault's assets. - * * */ contract RolloverVault is ERC20BurnableUpgradeable, OwnableUpgradeable, PausableUpgradeable, - ReentrancyGuardUpgradeable + ReentrancyGuardUpgradeable, + IVault { // data handling using EnumerableSetUpgradeable for EnumerableSetUpgradeable.AddressSet; @@ -135,7 +117,7 @@ contract RolloverVault is underlying = perp_.collateral(); _syncAsset(underlying); - assert(underlying != perp_); + require(underlying != perp_, "RolloverVault: unacceptable perp"); perp = perp_; } @@ -172,15 +154,16 @@ contract RolloverVault is //-------------------------------------------------------------------------- // External & Public write methods - /// @notice Recovers deployed funds and redeploys them. Reverts if there are no funds to deploy. - /// @dev Simply batches the `recover` and `deploy` functions. - function recoverAndRedeploy() external { + /// @inheritdoc IVault + /// @dev Simply batches the `recover` and `deploy` functions. Reverts if there are no funds to deploy. + function recoverAndRedeploy() external override { recover(); deploy(); } - /// @notice Deploys deposited funds. Reverts if there are no funds to deploy. + /// @inheritdoc IVault /// @dev Its safer to call `recover` before `deploy` so the full available balance can be deployed. + /// Reverts if there are no funds to deploy. function deploy() public nonReentrant whenNotPaused { TrancheData memory td = _tranche(perp.getDepositBond()); if (_rollover(perp, td) == 0) { @@ -188,21 +171,13 @@ contract RolloverVault is } } - /// @notice Recovers deployed funds. + /// @inheritdoc IVault function recover() public nonReentrant whenNotPaused { _redeemTranches(); } - /// @notice Deposits the provided asset from {msg.sender} into the vault and mints notes. - /// @param token The address of the asset to deposit. - /// @param amount The amount tokens to be deposited into the vault. - /// @return The amount of notes. - function deposit(IERC20Upgradeable token, uint256 amount) external nonReentrant whenNotPaused returns (uint256) { - // NOTE: The vault only accepts the underlying asset tokens. - if (token != underlying) { - revert UnexpectedAsset(token); - } - + /// @inheritdoc IVault + function deposit(uint256 amount) external override nonReentrant whenNotPaused returns (uint256) { uint256 totalSupply_ = totalSupply(); uint256 notes = (totalSupply_ > 0) ? amount.mulDiv(totalSupply_, getTVL()) : (amount * INITIAL_RATE); @@ -213,23 +188,14 @@ contract RolloverVault is return notes; } - struct TokenAmount { - /// @notice The asset token redeemed. - IERC20Upgradeable token; - /// @notice The amount redeemed. - uint256 amount; - } - - /// @notice Burns notes and sends a proportional share of vault's assets back to {msg.sender}. - /// @param notes The amount of notes to be burnt. - /// @return The list of asset tokens and amounts redeemed. - function redeem(uint256 notes) external nonReentrant whenNotPaused returns (TokenAmount[] memory) { + /// @inheritdoc IVault + function redeem(uint256 notes) external override nonReentrant whenNotPaused returns (IVault.TokenAmount[] memory) { uint256 totalNotes = totalSupply(); uint256 deployedCount_ = _deployed.length(); uint256 assetCount = 2 + deployedCount_; // aggregating vault assets to be redeemed - TokenAmount[] memory redemptions = new TokenAmount[](assetCount); + IVault.TokenAmount[] memory redemptions = new IVault.TokenAmount[](assetCount); redemptions[0].token = underlying; for (uint256 i = 0; i < deployedCount_; i++) { redemptions[i + 1].token = IERC20Upgradeable(_deployed.at(i)); @@ -249,8 +215,9 @@ contract RolloverVault is return redemptions; } - /// @return The total value of assets currently held by the vault, denominated in the underlying asset. - function getTVL() public returns (uint256) { + /// @inheritdoc IVault + /// @dev The total value is denominated in the underlying asset. + function getTVL() public override returns (uint256) { uint256 totalAssets = 0; // The underlying balance @@ -279,40 +246,36 @@ contract RolloverVault is //-------------------------------------------------------------------------- // External & Public read methods - /// @param token The address of the asset ERC-20 token held by the vault. - /// @return The vault's asset token balance. - function vaultAssetBalance(IERC20Upgradeable token) external view returns (uint256) { + /// @inheritdoc IVault + function vaultAssetBalance(IERC20Upgradeable token) external view override returns (uint256) { return isVaultAsset(token) ? token.balanceOf(address(this)) : 0; } - /// @return Total count of deployed asset tokens held by the vault. - function deployedCount() external view returns (uint256) { + /// @inheritdoc IVault + function deployedCount() external view override returns (uint256) { return _deployed.length(); } - /// @param i The index of a token. - /// @return The token address from the deployed asset token list by index. - function deployedAt(uint256 i) external view returns (IERC20Upgradeable) { + /// @inheritdoc IVault + function deployedAt(uint256 i) external view override returns (IERC20Upgradeable) { return IERC20Upgradeable(_deployed.at(i)); } - /// @return Total count of earned income tokens held by the vault. + /// @inheritdoc IVault function earnedCount() external pure returns (uint256) { return 1; } - /// @param i The index of a token. - /// @return The token address from the earned income token list by index. - function earnedAt(uint256 i) external view returns (IERC20Upgradeable) { + /// @inheritdoc IVault + function earnedAt(uint256 i) external view override returns (IERC20Upgradeable) { if (i > 0) { revert OutOfBounds(); } return IERC20Upgradeable(perp); } - /// @param token The address of a token to check. - /// @return If the given token is held by the vault. - function isVaultAsset(IERC20Upgradeable token) public view returns (bool) { + /// @inheritdoc IVault + function isVaultAsset(IERC20Upgradeable token) public view override returns (bool) { return (token == underlying) || _deployed.contains(address(token)) || (address(perp) == address(token)); } @@ -424,7 +387,12 @@ contract RolloverVault is /// @notice Redeems the deployed tranche tokens for the underlying asset. function _redeemTranches() private { - for (uint256 i = 0; i < _deployed.length(); i++) { + uint256 deployedCount_ = _deployed.length(); + if (deployedCount_ <= 0) { + return; + } + + for (uint256 i = 0; i < deployedCount_; i++) { ITranche tranche = ITranche(_deployed.at(i)); IBondController bond = IBondController(tranche.bond()); @@ -454,8 +422,8 @@ contract RolloverVault is // NOTE: We traverse the deployed set in the reverse order // as deletions involve swapping the deleted element to the // end of the set and removing the last element. - for (uint256 i = _deployed.length() - 1; i >= 0; i--) { - _syncDeployedAsset(IERC20Upgradeable(_deployed.at(i))); + for (uint256 i = deployedCount_; i > 0; i--) { + _syncDeployedAsset(IERC20Upgradeable(_deployed.at(i - 1))); } _syncAsset(underlying); } diff --git a/spot-contracts/external-artifacts/MedianOracle.json b/spot-contracts/external-artifacts/MedianOracle.json new file mode 100644 index 00000000..5c9f46bb --- /dev/null +++ b/spot-contracts/external-artifacts/MedianOracle.json @@ -0,0 +1,366 @@ +{ + "_format": "hh-sol-artifact-1", + "contractName": "MedianOracle", + "sourceName": "contracts/MedianOracle.sol", + "abi": [ + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "uint8", + "name": "version", + "type": "uint8" + } + ], + "name": "Initialized", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "previousOwner", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "newOwner", + "type": "address" + } + ], + "name": "OwnershipTransferred", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "address", + "name": "provider", + "type": "address" + } + ], + "name": "ProviderAdded", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "address", + "name": "provider", + "type": "address" + } + ], + "name": "ProviderRemoved", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "provider", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "payload", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "timestamp", + "type": "uint256" + } + ], + "name": "ProviderReportPushed", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "address", + "name": "provider", + "type": "address" + } + ], + "name": "ReportTimestampOutOfRange", + "type": "event" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "provider", + "type": "address" + } + ], + "name": "addProvider", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "getData", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + }, + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "reportExpirationTimeSec_", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "reportDelaySec_", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "minimumProviders_", + "type": "uint256" + } + ], + "name": "init", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "minimumProviders", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "owner", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + }, + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "name": "providerReports", + "outputs": [ + { + "internalType": "uint256", + "name": "timestamp", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "payload", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "name": "providers", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "providersSize", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "purgeReports", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "payload", + "type": "uint256" + } + ], + "name": "pushReport", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "provider", + "type": "address" + } + ], + "name": "removeProvider", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "renounceOwnership", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "reportDelaySec", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "reportExpirationTimeSec", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "minimumProviders_", + "type": "uint256" + } + ], + "name": "setMinimumProviders", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "reportDelaySec_", + "type": "uint256" + } + ], + "name": "setReportDelaySec", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "reportExpirationTimeSec_", + "type": "uint256" + } + ], + "name": "setReportExpirationTimeSec", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "newOwner", + "type": "address" + } + ], + "name": "transferOwnership", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + } + ], + "bytecode": "0x6080604052600160695534801561001557600080fd5b50611279806100256000396000f3fe608060405234801561001057600080fd5b50600436106101165760003560e01c8063b577c0c7116100a2578063df98298511610071578063df98298514610212578063ef35bcce1461021b578063f10864b61461022e578063f2fde38b14610256578063f68be5131461026957600080fd5b8063b577c0c7146101e6578063d13d5971146101ef578063da6b0eea146101f7578063dcbb82531461020a57600080fd5b806350f3fc81116100e957806350f3fc811461017c578063715018a6146101a75780638a355a57146101af5780638cd8db8a146101c25780638da5cb5b146101d557600080fd5b806312e800f11461011b5780631e20d14b146101375780633bc5de301461014c57806346e2577a14610169575b600080fd5b61012460685481565b6040519081526020015b60405180910390f35b61014a6101453660046110dd565b61027c565b005b6101546103d1565b6040805192835290151560208301520161012e565b61014a610177366004611093565b6107d4565b61018f61018a3660046110dd565b61089c565b6040516001600160a01b03909116815260200161012e565b61014a6108c6565b61014a6101bd366004611093565b6108da565b61014a6101d03660046110f5565b610aa6565b6033546001600160a01b031661018f565b61012460695481565b61014a610bec565b61014a6102053660046110dd565b610c29565b606554610124565b61012460675481565b61014a6102293660046110dd565b610c36565b61024161023c3660046110b4565b610c50565b6040805192835260208301919091520161012e565b61014a610264366004611093565b610c80565b61014a6102773660046110dd565b610cf9565b336000818152606660209081526040918290208251808401909352805480845260028201549284019290925291906102b357600080fd5b6020810151815160009111156102ca5760016102cd565b60005b905060006102dc8260016111ae565b905042606854848460ff166002811061030557634e487b7160e01b600052603260045260246000fd5b6020020151610314919061116b565b111561031f57600080fd5b42848260ff166002811061034357634e487b7160e01b600052603260045260246000fd5b600202016000018190555085848260ff166002811061037257634e487b7160e01b600052603260045260246000fd5b6002020160010181905550846001600160a01b03167f460fcc5a1888965d48c2cab000fe20da51b1297d995af79a1924e2312d0d82b387426040516103c1929190918252602082015260400190565b60405180910390a2505050505050565b6065546000908190818167ffffffffffffffff81111561040157634e487b7160e01b600052604160045260246000fd5b60405190808252806020026020018201604052801561042a578160200160208202803683370190505b5090506000806067544261043e9190611197565b90506000606854426104509190611197565b905060005b858110156107a25760006065828154811061048057634e487b7160e01b600052603260045260246000fd5b60009182526020808320909101546001600160a01b031680835260669091526040808320815180830190925291935090600283835b828210156104f15783826002020160405180604001604052908160008201548152602001600182015481525050815260200190600101906104b5565b50505050905060008160016002811061051a57634e487b7160e01b600052603260045260246000fd5b6020020151518251511015610530576001610533565b60005b905060006105428260016111ae565b90506000838360ff166002811061056957634e487b7160e01b600052603260045260246000fd5b6020020151519050868111156106c6576001600160a01b038516600090815260666020526040812060ff8416600281106105b357634e487b7160e01b600052603260045260246000fd5b6002020154905088811015610604576040516001600160a01b03871681527f71f61642cb57ac11764a2f35fb4edc5361ced458af35bbed8f5ebf708c10e341906020015b60405180910390a16106c0565b87811115610645576040516001600160a01b03871681527f71f61642cb57ac11764a2f35fb4edc5361ced458af35bbed8f5ebf708c10e341906020016105f7565b6001600160a01b038616600090815260666020526040902060ff84166002811061067f57634e487b7160e01b600052603260045260246000fd5b60020201600101548b8b80610693906111e8565b9c50815181106106b357634e487b7160e01b600052603260045260246000fd5b6020026020010181815250505b5061078a565b8781101561070f576040516001600160a01b03861681527f71f61642cb57ac11764a2f35fb4edc5361ced458af35bbed8f5ebf708c10e3419060200160405180910390a161078a565b6001600160a01b038516600090815260666020526040902060ff84166002811061074957634e487b7160e01b600052603260045260246000fd5b60020201600101548a8a8061075d906111e8565b9b508151811061077d57634e487b7160e01b600052603260045260246000fd5b6020026020010181815250505b5050505050808061079a906111e8565b915050610455565b506069548310156107bc5750600096879650945050505050565b6107c68484610d17565b976001975095505050505050565b6107dc610f71565b6001600160a01b038116600090815260666020526040902054156107ff57600080fd5b6065805460018082019092557f8ff97419363ffd7000167f130ef7168fbea05faf9251824ca5043f113cc6a7c70180546001600160a01b0319166001600160a01b03841690811790915560009081526066602052604081209060020201556040516001600160a01b03821681527fae9c2c6481964847714ce58f65a7f6dcc41d0d8394449bacdf161b5920c4744a9060200160405180910390a150565b606581815481106108ac57600080fd5b6000918252602090912001546001600160a01b0316905081565b6108ce610f71565b6108d86000610fcb565b565b6108e2610f71565b6001600160a01b038116600090815260666020526040812081815560018101829055600281018290556003015560005b606554811015610aa257816001600160a01b03166065828154811061094757634e487b7160e01b600052603260045260246000fd5b6000918252602090912001546001600160a01b03161415610a905760655461097082600161116b565b14610a0c576065805461098590600190611197565b815481106109a357634e487b7160e01b600052603260045260246000fd5b600091825260209091200154606580546001600160a01b0390921691839081106109dd57634e487b7160e01b600052603260045260246000fd5b9060005260206000200160006101000a8154816001600160a01b0302191690836001600160a01b031602179055505b6065805480610a2b57634e487b7160e01b600052603160045260246000fd5b6000828152602090819020600019908301810180546001600160a01b03191690559091019091556040516001600160a01b03841681527f1589f8555933761a3cff8aa925061be3b46e2dd43f621322ab611d300f62b1d9910160405180910390a15050565b80610a9a816111e8565b915050610912565b5050565b600054610100900460ff1615808015610ac65750600054600160ff909116105b80610ae05750303b158015610ae0575060005460ff166001145b610b485760405162461bcd60e51b815260206004820152602e60248201527f496e697469616c697a61626c653a20636f6e747261637420697320616c72656160448201526d191e481a5b9a5d1a585b1a5e995960921b60648201526084015b60405180910390fd5b6000805460ff191660011790558015610b6b576000805461ff0019166101001790555b6312bed400841115610b7c57600080fd5b60008211610b8957600080fd5b606784905560688390556069829055610ba061101d565b8015610be6576000805461ff0019169055604051600181527f7f26b83ff96e1f2b6a682f133852f6798a09c465da95921460cefb38474024989060200160405180910390a15b50505050565b33600081815260666020526040902054610c0557600080fd5b6001600160a01b031660009081526066602052604090206001808255600290910155565b610c31610f71565b606855565b610c3e610f71565b60008111610c4b57600080fd5b606955565b60666020528160005260406000208160028110610c6c57600080fd5b600202018054600190910154909250905082565b610c88610f71565b6001600160a01b038116610ced5760405162461bcd60e51b815260206004820152602660248201527f4f776e61626c653a206e6577206f776e657220697320746865207a65726f206160448201526564647265737360d01b6064820152608401610b3f565b610cf681610fcb565b50565b610d01610f71565b6312bed400811115610d1257600080fd5b606755565b60008082118015610d29575081835110155b610d3257600080fd5b60015b82811015610e9457805b600081118015610da85750848181518110610d6a57634e487b7160e01b600052603260045260246000fd5b602002602001015185600183610d809190611197565b81518110610d9e57634e487b7160e01b600052603260045260246000fd5b6020026020010151115b15610e81576000858281518110610dcf57634e487b7160e01b600052603260045260246000fd5b6020026020010151905085600183610de79190611197565b81518110610e0557634e487b7160e01b600052603260045260246000fd5b6020026020010151868381518110610e2d57634e487b7160e01b600052603260045260246000fd5b60209081029190910101528086610e45600185611197565b81518110610e6357634e487b7160e01b600052603260045260246000fd5b60209081029190910101525080610e79816111d1565b915050610d3f565b5080610e8c816111e8565b915050610d35565b50610ea0600283611203565b60011415610ee15782610eb4600284611183565b81518110610ed257634e487b7160e01b600052603260045260246000fd5b60200260200101519050610f6b565b6002836001610ef08386611183565b610efa9190611197565b81518110610f1857634e487b7160e01b600052603260045260246000fd5b602002602001015184600285610f2e9190611183565b81518110610f4c57634e487b7160e01b600052603260045260246000fd5b6020026020010151610f5e919061116b565b610f689190611183565b90505b92915050565b6033546001600160a01b031633146108d85760405162461bcd60e51b815260206004820181905260248201527f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e65726044820152606401610b3f565b603380546001600160a01b038381166001600160a01b0319831681179093556040519116919082907f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e090600090a35050565b600054610100900460ff166110445760405162461bcd60e51b8152600401610b3f90611120565b6108d8600054610100900460ff1661106e5760405162461bcd60e51b8152600401610b3f90611120565b6108d833610fcb565b80356001600160a01b038116811461108e57600080fd5b919050565b6000602082840312156110a4578081fd5b6110ad82611077565b9392505050565b600080604083850312156110c6578081fd5b6110cf83611077565b946020939093013593505050565b6000602082840312156110ee578081fd5b5035919050565b600080600060608486031215611109578081fd5b505081359360208301359350604090920135919050565b6020808252602b908201527f496e697469616c697a61626c653a20636f6e7472616374206973206e6f74206960408201526a6e697469616c697a696e6760a81b606082015260800190565b6000821982111561117e5761117e611217565b500190565b6000826111925761119261122d565b500490565b6000828210156111a9576111a9611217565b500390565b600060ff821660ff8416808210156111c8576111c8611217565b90039392505050565b6000816111e0576111e0611217565b506000190190565b60006000198214156111fc576111fc611217565b5060010190565b6000826112125761121261122d565b500690565b634e487b7160e01b600052601160045260246000fd5b634e487b7160e01b600052601260045260246000fdfea26469706673582212201830afdebbb0a773c1612d542049c26255d8c66ab8cd1c0486dd60877074415464736f6c63430008040033", + "deployedBytecode": "0x608060405234801561001057600080fd5b50600436106101165760003560e01c8063b577c0c7116100a2578063df98298511610071578063df98298514610212578063ef35bcce1461021b578063f10864b61461022e578063f2fde38b14610256578063f68be5131461026957600080fd5b8063b577c0c7146101e6578063d13d5971146101ef578063da6b0eea146101f7578063dcbb82531461020a57600080fd5b806350f3fc81116100e957806350f3fc811461017c578063715018a6146101a75780638a355a57146101af5780638cd8db8a146101c25780638da5cb5b146101d557600080fd5b806312e800f11461011b5780631e20d14b146101375780633bc5de301461014c57806346e2577a14610169575b600080fd5b61012460685481565b6040519081526020015b60405180910390f35b61014a6101453660046110dd565b61027c565b005b6101546103d1565b6040805192835290151560208301520161012e565b61014a610177366004611093565b6107d4565b61018f61018a3660046110dd565b61089c565b6040516001600160a01b03909116815260200161012e565b61014a6108c6565b61014a6101bd366004611093565b6108da565b61014a6101d03660046110f5565b610aa6565b6033546001600160a01b031661018f565b61012460695481565b61014a610bec565b61014a6102053660046110dd565b610c29565b606554610124565b61012460675481565b61014a6102293660046110dd565b610c36565b61024161023c3660046110b4565b610c50565b6040805192835260208301919091520161012e565b61014a610264366004611093565b610c80565b61014a6102773660046110dd565b610cf9565b336000818152606660209081526040918290208251808401909352805480845260028201549284019290925291906102b357600080fd5b6020810151815160009111156102ca5760016102cd565b60005b905060006102dc8260016111ae565b905042606854848460ff166002811061030557634e487b7160e01b600052603260045260246000fd5b6020020151610314919061116b565b111561031f57600080fd5b42848260ff166002811061034357634e487b7160e01b600052603260045260246000fd5b600202016000018190555085848260ff166002811061037257634e487b7160e01b600052603260045260246000fd5b6002020160010181905550846001600160a01b03167f460fcc5a1888965d48c2cab000fe20da51b1297d995af79a1924e2312d0d82b387426040516103c1929190918252602082015260400190565b60405180910390a2505050505050565b6065546000908190818167ffffffffffffffff81111561040157634e487b7160e01b600052604160045260246000fd5b60405190808252806020026020018201604052801561042a578160200160208202803683370190505b5090506000806067544261043e9190611197565b90506000606854426104509190611197565b905060005b858110156107a25760006065828154811061048057634e487b7160e01b600052603260045260246000fd5b60009182526020808320909101546001600160a01b031680835260669091526040808320815180830190925291935090600283835b828210156104f15783826002020160405180604001604052908160008201548152602001600182015481525050815260200190600101906104b5565b50505050905060008160016002811061051a57634e487b7160e01b600052603260045260246000fd5b6020020151518251511015610530576001610533565b60005b905060006105428260016111ae565b90506000838360ff166002811061056957634e487b7160e01b600052603260045260246000fd5b6020020151519050868111156106c6576001600160a01b038516600090815260666020526040812060ff8416600281106105b357634e487b7160e01b600052603260045260246000fd5b6002020154905088811015610604576040516001600160a01b03871681527f71f61642cb57ac11764a2f35fb4edc5361ced458af35bbed8f5ebf708c10e341906020015b60405180910390a16106c0565b87811115610645576040516001600160a01b03871681527f71f61642cb57ac11764a2f35fb4edc5361ced458af35bbed8f5ebf708c10e341906020016105f7565b6001600160a01b038616600090815260666020526040902060ff84166002811061067f57634e487b7160e01b600052603260045260246000fd5b60020201600101548b8b80610693906111e8565b9c50815181106106b357634e487b7160e01b600052603260045260246000fd5b6020026020010181815250505b5061078a565b8781101561070f576040516001600160a01b03861681527f71f61642cb57ac11764a2f35fb4edc5361ced458af35bbed8f5ebf708c10e3419060200160405180910390a161078a565b6001600160a01b038516600090815260666020526040902060ff84166002811061074957634e487b7160e01b600052603260045260246000fd5b60020201600101548a8a8061075d906111e8565b9b508151811061077d57634e487b7160e01b600052603260045260246000fd5b6020026020010181815250505b5050505050808061079a906111e8565b915050610455565b506069548310156107bc5750600096879650945050505050565b6107c68484610d17565b976001975095505050505050565b6107dc610f71565b6001600160a01b038116600090815260666020526040902054156107ff57600080fd5b6065805460018082019092557f8ff97419363ffd7000167f130ef7168fbea05faf9251824ca5043f113cc6a7c70180546001600160a01b0319166001600160a01b03841690811790915560009081526066602052604081209060020201556040516001600160a01b03821681527fae9c2c6481964847714ce58f65a7f6dcc41d0d8394449bacdf161b5920c4744a9060200160405180910390a150565b606581815481106108ac57600080fd5b6000918252602090912001546001600160a01b0316905081565b6108ce610f71565b6108d86000610fcb565b565b6108e2610f71565b6001600160a01b038116600090815260666020526040812081815560018101829055600281018290556003015560005b606554811015610aa257816001600160a01b03166065828154811061094757634e487b7160e01b600052603260045260246000fd5b6000918252602090912001546001600160a01b03161415610a905760655461097082600161116b565b14610a0c576065805461098590600190611197565b815481106109a357634e487b7160e01b600052603260045260246000fd5b600091825260209091200154606580546001600160a01b0390921691839081106109dd57634e487b7160e01b600052603260045260246000fd5b9060005260206000200160006101000a8154816001600160a01b0302191690836001600160a01b031602179055505b6065805480610a2b57634e487b7160e01b600052603160045260246000fd5b6000828152602090819020600019908301810180546001600160a01b03191690559091019091556040516001600160a01b03841681527f1589f8555933761a3cff8aa925061be3b46e2dd43f621322ab611d300f62b1d9910160405180910390a15050565b80610a9a816111e8565b915050610912565b5050565b600054610100900460ff1615808015610ac65750600054600160ff909116105b80610ae05750303b158015610ae0575060005460ff166001145b610b485760405162461bcd60e51b815260206004820152602e60248201527f496e697469616c697a61626c653a20636f6e747261637420697320616c72656160448201526d191e481a5b9a5d1a585b1a5e995960921b60648201526084015b60405180910390fd5b6000805460ff191660011790558015610b6b576000805461ff0019166101001790555b6312bed400841115610b7c57600080fd5b60008211610b8957600080fd5b606784905560688390556069829055610ba061101d565b8015610be6576000805461ff0019169055604051600181527f7f26b83ff96e1f2b6a682f133852f6798a09c465da95921460cefb38474024989060200160405180910390a15b50505050565b33600081815260666020526040902054610c0557600080fd5b6001600160a01b031660009081526066602052604090206001808255600290910155565b610c31610f71565b606855565b610c3e610f71565b60008111610c4b57600080fd5b606955565b60666020528160005260406000208160028110610c6c57600080fd5b600202018054600190910154909250905082565b610c88610f71565b6001600160a01b038116610ced5760405162461bcd60e51b815260206004820152602660248201527f4f776e61626c653a206e6577206f776e657220697320746865207a65726f206160448201526564647265737360d01b6064820152608401610b3f565b610cf681610fcb565b50565b610d01610f71565b6312bed400811115610d1257600080fd5b606755565b60008082118015610d29575081835110155b610d3257600080fd5b60015b82811015610e9457805b600081118015610da85750848181518110610d6a57634e487b7160e01b600052603260045260246000fd5b602002602001015185600183610d809190611197565b81518110610d9e57634e487b7160e01b600052603260045260246000fd5b6020026020010151115b15610e81576000858281518110610dcf57634e487b7160e01b600052603260045260246000fd5b6020026020010151905085600183610de79190611197565b81518110610e0557634e487b7160e01b600052603260045260246000fd5b6020026020010151868381518110610e2d57634e487b7160e01b600052603260045260246000fd5b60209081029190910101528086610e45600185611197565b81518110610e6357634e487b7160e01b600052603260045260246000fd5b60209081029190910101525080610e79816111d1565b915050610d3f565b5080610e8c816111e8565b915050610d35565b50610ea0600283611203565b60011415610ee15782610eb4600284611183565b81518110610ed257634e487b7160e01b600052603260045260246000fd5b60200260200101519050610f6b565b6002836001610ef08386611183565b610efa9190611197565b81518110610f1857634e487b7160e01b600052603260045260246000fd5b602002602001015184600285610f2e9190611183565b81518110610f4c57634e487b7160e01b600052603260045260246000fd5b6020026020010151610f5e919061116b565b610f689190611183565b90505b92915050565b6033546001600160a01b031633146108d85760405162461bcd60e51b815260206004820181905260248201527f4f776e61626c653a2063616c6c6572206973206e6f7420746865206f776e65726044820152606401610b3f565b603380546001600160a01b038381166001600160a01b0319831681179093556040519116919082907f8be0079c531659141344cd1fd0a4f28419497f9722a3daafe3b4186f6b6457e090600090a35050565b600054610100900460ff166110445760405162461bcd60e51b8152600401610b3f90611120565b6108d8600054610100900460ff1661106e5760405162461bcd60e51b8152600401610b3f90611120565b6108d833610fcb565b80356001600160a01b038116811461108e57600080fd5b919050565b6000602082840312156110a4578081fd5b6110ad82611077565b9392505050565b600080604083850312156110c6578081fd5b6110cf83611077565b946020939093013593505050565b6000602082840312156110ee578081fd5b5035919050565b600080600060608486031215611109578081fd5b505081359360208301359350604090920135919050565b6020808252602b908201527f496e697469616c697a61626c653a20636f6e7472616374206973206e6f74206960408201526a6e697469616c697a696e6760a81b606082015260800190565b6000821982111561117e5761117e611217565b500190565b6000826111925761119261122d565b500490565b6000828210156111a9576111a9611217565b500390565b600060ff821660ff8416808210156111c8576111c8611217565b90039392505050565b6000816111e0576111e0611217565b506000190190565b60006000198214156111fc576111fc611217565b5060010190565b6000826112125761121261122d565b500690565b634e487b7160e01b600052601160045260246000fd5b634e487b7160e01b600052601260045260246000fdfea26469706673582212201830afdebbb0a773c1612d542049c26255d8c66ab8cd1c0486dd60877074415464736f6c63430008040033", + "linkReferences": {}, + "deployedLinkReferences": {} +} diff --git a/spot-contracts/hardhat.config.ts b/spot-contracts/hardhat.config.ts index 47b9deed..f6718728 100644 --- a/spot-contracts/hardhat.config.ts +++ b/spot-contracts/hardhat.config.ts @@ -3,6 +3,7 @@ import { Wallet } from "ethers"; import "@nomiclabs/hardhat-ethers"; import "@nomiclabs/hardhat-waffle"; +import "@nomicfoundation/hardhat-chai-matchers"; import "@openzeppelin/hardhat-upgrades"; import "solidity-coverage"; import "hardhat-gas-reporter"; @@ -46,7 +47,7 @@ export default { solidity: { compilers: [ { - version: "0.8.18", + version: "0.8.19", settings: { optimizer: { enabled: true, diff --git a/spot-contracts/package.json b/spot-contracts/package.json index a3abc077..1b8ac53c 100644 --- a/spot-contracts/package.json +++ b/spot-contracts/package.json @@ -19,11 +19,13 @@ "@openzeppelin/contracts-upgradeable": "^4.7.3" }, "devDependencies": { + "@defi-wonderland/smock": "^2.3.4", "@ethersproject/abi": "^5.6.4", "@ethersproject/bytes": "^5.6.1", "@ethersproject/providers": "^5.6.8", - "@nomiclabs/hardhat-ethers": "^2.1.0", - "@nomiclabs/hardhat-etherscan": "^3.1.0", + "@nomicfoundation/hardhat-chai-matchers": "^1.0.6", + "@nomiclabs/hardhat-ethers": "^2.2.1", + "@nomiclabs/hardhat-etherscan": "^3.1.2", "@nomiclabs/hardhat-waffle": "^2.0.3", "@openzeppelin/hardhat-upgrades": "^1.19.0", "@openzeppelin/upgrades-core": "latest", @@ -41,19 +43,20 @@ "eslint-config-standard": "^17.0.0", "eslint-plugin-import": "^2.26.0", "eslint-plugin-n": "^15.2.4", + "eslint-plugin-no-only-tests": "^3.1.0", "eslint-plugin-node": "^11.1.0", "eslint-plugin-prettier": "^4.2.1", "eslint-plugin-promise": "^6.0.0", "ethereum-waffle": "^3.4.4", "ethers": "^5.6.9", "ganache-cli": "^6.12.2", - "hardhat": "=2.10.2", - "hardhat-gas-reporter": "^1.0.8", + "hardhat": "^2.12.6", + "hardhat-gas-reporter": "^1.0.9", "lodash": "^4.17.21", "prettier": "^2.7.1", "prettier-plugin-solidity": "^1.0.0-dev.23", "solhint": "^3.3.7", - "solidity-coverage": "^0.7.21", + "solidity-coverage": "^0.8.2", "ts-node": "^10.9.1", "typechain": "^8.1.0", "typescript": "^4.7.4" diff --git a/spot-contracts/test/PerpetualTranche.ts b/spot-contracts/test/PerpetualTranche.ts index 1292e39c..b5832150 100644 --- a/spot-contracts/test/PerpetualTranche.ts +++ b/spot-contracts/test/PerpetualTranche.ts @@ -1,7 +1,7 @@ -import { expect } from "chai"; +import { expect, use } from "chai"; import { network, ethers, upgrades } from "hardhat"; import { Contract, Transaction, Signer, constants } from "ethers"; - +import { smock } from "@defi-wonderland/smock"; import { setupCollateralToken, setupBondFactory, @@ -18,6 +18,7 @@ import { rebase, advancePerpQueueToRollover, } from "./helpers"; +use(smock.matchers); let perp: Contract, collateralToken: Contract, @@ -38,17 +39,23 @@ describe("PerpetualTranche", function () { ({ collateralToken, rebaseOracle } = await setupCollateralToken("Bitcoin", "BTC")); - const BondIssuer = await ethers.getContractFactory("MockBondIssuer"); - issuer = await BondIssuer.deploy(collateralToken.address); + const BondIssuer = await ethers.getContractFactory("BondIssuer"); + issuer = await smock.fake(BondIssuer); + await issuer.collateral.returns(collateralToken.address); - const FeeStrategy = await ethers.getContractFactory("MockFeeStrategy"); - feeStrategy = await FeeStrategy.deploy(); + const FeeStrategy = await ethers.getContractFactory("BasicFeeStrategy"); + feeStrategy = await smock.fake(FeeStrategy); - const PricingStrategy = await ethers.getContractFactory("MockPricingStrategy"); - pricingStrategy = await PricingStrategy.deploy(); + const PricingStrategy = await ethers.getContractFactory("UnitPricingStrategy"); + pricingStrategy = await smock.fake(PricingStrategy); + await pricingStrategy.decimals.returns(8); + await pricingStrategy.computeMatureTranchePrice.returns(toPriceFixedPtAmt("1")); + await pricingStrategy.computeTranchePrice.returns(toPriceFixedPtAmt("1")); - const DiscountStrategy = await ethers.getContractFactory("MockDiscountStrategy"); - discountStrategy = await DiscountStrategy.deploy(); + const DiscountStrategy = await ethers.getContractFactory("TrancheClassDiscountStrategy"); + discountStrategy = await smock.fake(DiscountStrategy); + await discountStrategy.decimals.returns(18); + await discountStrategy.computeTrancheDiscount.returns(toDiscountFixedPtAmt("1")); const PerpetualTranche = await ethers.getContractFactory("PerpetualTranche"); perp = await upgrades.deployProxy( @@ -134,7 +141,7 @@ describe("PerpetualTranche", function () { describe("when triggered by non-keeper", function () { it("should revert", async function () { - await expect(perp.connect(deployer).pause()).to.be.revertedWith("UnauthorizedCall"); + await expect(perp.connect(deployer).pause()).to.be.revertedWithCustomError(perp, "UnauthorizedCall"); }); }); @@ -175,7 +182,7 @@ describe("PerpetualTranche", function () { }); it("should revert", async function () { - await expect(perp.connect(deployer).unpause()).to.be.revertedWith("UnauthorizedCall"); + await expect(perp.connect(deployer).unpause()).to.be.revertedWithCustomError(perp, "UnauthorizedCall"); }); }); @@ -308,14 +315,18 @@ describe("PerpetualTranche", function () { describe("when set address is NOT valid", function () { it("should revert", async function () { - await expect(perp.updateBondIssuer(constants.AddressZero)).to.be.revertedWith("UnacceptableReference"); + await expect(perp.updateBondIssuer(constants.AddressZero)).to.be.revertedWithCustomError( + perp, + "UnacceptableReference", + ); }); }); describe("when set address is valid", function () { beforeEach(async function () { - const BondIssuer = await ethers.getContractFactory("MockBondIssuer"); - newIssuer = await BondIssuer.deploy(collateralToken.address); + const BondIssuer = await ethers.getContractFactory("BondIssuer"); + newIssuer = await smock.fake(BondIssuer); + await newIssuer.collateral.returns(collateralToken.address); tx = perp.updateBondIssuer(newIssuer.address); await tx; }); @@ -329,11 +340,12 @@ describe("PerpetualTranche", function () { describe("when collateral is NOT valid", function () { beforeEach(async function () { - const BondIssuer = await ethers.getContractFactory("MockBondIssuer"); - newIssuer = await BondIssuer.deploy(perp.address); + const BondIssuer = await ethers.getContractFactory("BondIssuer"); + newIssuer = await smock.fake(BondIssuer); + await newIssuer.collateral.returns(constants.AddressZero); }); it("should revert", async function () { - await expect(perp.updateBondIssuer(newIssuer.address)).to.be.revertedWith("InvalidCollateral"); + await expect(perp.updateBondIssuer(newIssuer.address)).to.be.revertedWithCustomError(perp, "InvalidCollateral"); }); }); }); @@ -351,14 +363,17 @@ describe("PerpetualTranche", function () { describe("when set address is NOT valid", function () { it("should revert", async function () { - await expect(perp.updateFeeStrategy(constants.AddressZero)).to.be.revertedWith("UnacceptableReference"); + await expect(perp.updateFeeStrategy(constants.AddressZero)).to.be.revertedWithCustomError( + perp, + "UnacceptableReference", + ); }); }); describe("when set address is valid", function () { beforeEach(async function () { - const FeeStrategy = await ethers.getContractFactory("MockFeeStrategy"); - newFeeStrategy = await FeeStrategy.deploy(); + const FeeStrategy = await ethers.getContractFactory("BasicFeeStrategy"); + newFeeStrategy = await smock.fake(FeeStrategy); tx = perp.updateFeeStrategy(newFeeStrategy.address); await tx; }); @@ -384,18 +399,22 @@ describe("PerpetualTranche", function () { describe("when set address is NOT valid", function () { it("should revert", async function () { - await expect(perp.updatePricingStrategy(constants.AddressZero)).to.be.revertedWith("UnacceptableReference"); + await expect(perp.updatePricingStrategy(constants.AddressZero)).to.be.revertedWithCustomError( + perp, + "UnacceptableReference", + ); }); }); describe("when new strategy has different decimals", function () { beforeEach(async function () { - const PricingStrategy = await ethers.getContractFactory("MockPricingStrategy"); - newPricingStrategy = await PricingStrategy.deploy(); - await newPricingStrategy.setDecimals(18); + const PricingStrategy = await ethers.getContractFactory("UnitPricingStrategy"); + newPricingStrategy = await smock.fake(PricingStrategy); + await newPricingStrategy.decimals.returns(18); }); it("should revert", async function () { - await expect(perp.updatePricingStrategy(newPricingStrategy.address)).to.be.revertedWith( + await expect(perp.updatePricingStrategy(newPricingStrategy.address)).to.be.revertedWithCustomError( + perp, "InvalidStrategyDecimals", ); }); @@ -403,8 +422,9 @@ describe("PerpetualTranche", function () { describe("when set address is valid", function () { beforeEach(async function () { - const PricingStrategy = await ethers.getContractFactory("MockPricingStrategy"); - newPricingStrategy = await PricingStrategy.deploy(); + const PricingStrategy = await ethers.getContractFactory("UnitPricingStrategy"); + newPricingStrategy = await smock.fake(PricingStrategy); + await newPricingStrategy.decimals.returns(8); tx = perp.updatePricingStrategy(newPricingStrategy.address); await tx; }); @@ -430,18 +450,22 @@ describe("PerpetualTranche", function () { describe("when set address is NOT valid", function () { it("should revert", async function () { - await expect(perp.updateDiscountStrategy(constants.AddressZero)).to.be.revertedWith("UnacceptableReference"); + await expect(perp.updateDiscountStrategy(constants.AddressZero)).to.be.revertedWithCustomError( + perp, + "UnacceptableReference", + ); }); }); describe("when new strategy has different decimals", function () { beforeEach(async function () { - const DiscountStrategy = await ethers.getContractFactory("MockDiscountStrategy"); - newDiscountStrategy = await DiscountStrategy.deploy(); - await newDiscountStrategy.setDecimals(8); + const DiscountStrategy = await ethers.getContractFactory("TrancheClassDiscountStrategy"); + newDiscountStrategy = await smock.fake(DiscountStrategy); + await newDiscountStrategy.decimals.returns(8); }); it("should revert", async function () { - await expect(perp.updateDiscountStrategy(newDiscountStrategy.address)).to.be.revertedWith( + await expect(perp.updateDiscountStrategy(newDiscountStrategy.address)).to.be.revertedWithCustomError( + perp, "InvalidStrategyDecimals", ); }); @@ -449,8 +473,9 @@ describe("PerpetualTranche", function () { describe("when set address is valid", function () { beforeEach(async function () { - const DiscountStrategy = await ethers.getContractFactory("MockDiscountStrategy"); - newDiscountStrategy = await DiscountStrategy.deploy(); + const DiscountStrategy = await ethers.getContractFactory("TrancheClassDiscountStrategy"); + newDiscountStrategy = await smock.fake(DiscountStrategy); + await newDiscountStrategy.decimals.returns(18); tx = perp.updateDiscountStrategy(newDiscountStrategy.address); await tx; }); @@ -476,7 +501,8 @@ describe("PerpetualTranche", function () { describe("when set values are not valid", function () { it("should revert", async function () { - await expect(perp.updateTolerableTrancheMaturity(86400, 3600)).to.be.revertedWith( + await expect(perp.updateTolerableTrancheMaturity(86400, 3600)).to.be.revertedWithCustomError( + perp, "InvalidTrancheMaturityBounds", ); }); @@ -536,7 +562,7 @@ describe("PerpetualTranche", function () { describe("when NOT valid", function () { it("should revert", async function () { - await expect(perp.updateMatureValueTargetPerc("100000001")).to.be.revertedWith("InvalidPerc"); + await expect(perp.updateMatureValueTargetPerc("100000001")).to.be.revertedWithCustomError(perp, "InvalidPerc"); }); }); @@ -586,17 +612,17 @@ describe("PerpetualTranche", function () { describe("when reserve asset", function () { it("should revert", async function () { expect(await perp.callStatic.inReserve(collateralToken.address)).to.eq(true); - await expect(perp.transferERC20(collateralToken.address, toAddress, toFixedPtAmt("100"))).to.be.revertedWith( - "UnauthorizedTransferOut", - ); + await expect( + perp.transferERC20(collateralToken.address, toAddress, toFixedPtAmt("100")), + ).to.be.revertedWithCustomError(perp, "UnauthorizedTransferOut"); }); }); describe("when fee token", function () { it("should revert", async function () { - await expect(perp.transferERC20(await perp.feeToken(), toAddress, toFixedPtAmt("100"))).to.be.revertedWith( - "UnauthorizedTransferOut", - ); + await expect( + perp.transferERC20(await perp.feeToken(), toAddress, toFixedPtAmt("100")), + ).to.be.revertedWithCustomError(perp, "UnauthorizedTransferOut"); }); }); }); @@ -605,12 +631,12 @@ describe("PerpetualTranche", function () { let bondFactory: Contract, bond: Contract, tranches: Contract[]; beforeEach(async function () { bondFactory = await setupBondFactory(); - bond = await createBondWithFactory(bondFactory, collateralToken, [200, 300, 500], 3600); tranches = await getTranches(bond); - await issuer.setLatestBond(bond.address); - - await discountStrategy.setTrancheDiscount(tranches[0].address, toDiscountFixedPtAmt("1")); + await issuer.getLatestBond.returns(bond.address); + await discountStrategy.computeTrancheDiscount + .whenCalledWith(tranches[0].address) + .returns(toDiscountFixedPtAmt("1")); }); describe("when tranche instance is not in the system", function () { @@ -618,6 +644,9 @@ describe("PerpetualTranche", function () { expect(await perp.computeDiscount(tranches[0].address)).to.eq(toDiscountFixedPtAmt("1")); }); describe("when not defined", function () { + beforeEach(async function () { + await discountStrategy.computeTrancheDiscount.returns(toDiscountFixedPtAmt("0")); + }); it("should return 0", async function () { expect(await perp.computeDiscount(tranches[1].address)).to.eq(toDiscountFixedPtAmt("0")); expect(await perp.computeDiscount(tranches[2].address)).to.eq(toDiscountFixedPtAmt("0")); @@ -626,7 +655,9 @@ describe("PerpetualTranche", function () { describe("when updated", function () { beforeEach(async function () { expect(await perp.computeDiscount(tranches[0].address)).to.eq(toDiscountFixedPtAmt("1")); - await discountStrategy.setTrancheDiscount(tranches[0].address, toDiscountFixedPtAmt("0.5")); + await discountStrategy.computeTrancheDiscount + .whenCalledWith(tranches[0].address) + .returns(toDiscountFixedPtAmt("0.5")); }); it("should return defined discount", async function () { expect(await perp.computeDiscount(tranches[0].address)).to.eq(toDiscountFixedPtAmt("0.5")); @@ -645,7 +676,9 @@ describe("PerpetualTranche", function () { }); describe("when updated", function () { beforeEach(async function () { - await discountStrategy.setTrancheDiscount(tranches[0].address, toDiscountFixedPtAmt("0.5")); + await discountStrategy.computeTrancheDiscount + .whenCalledWith(tranches[0].address) + .returns(toDiscountFixedPtAmt("0.5")); }); it("should return applied discount", async function () { expect(await perp.computeDiscount(tranches[0].address)).to.eq(toDiscountFixedPtAmt("1")); @@ -662,16 +695,20 @@ describe("PerpetualTranche", function () { const bondNext = await createBondWithFactory(bondFactory, collateralToken, [200, 300, 500], 3600); tranchesNext = await getTranches(bondNext); - await issuer.setLatestBond(bondNext.address); + await issuer.getLatestBond.returns(bondNext.address); - await discountStrategy.setTrancheDiscount(tranchesNext[0].address, toDiscountFixedPtAmt("1")); + await discountStrategy.computeTrancheDiscount + .whenCalledWith(tranchesNext[0].address) + .returns(toDiscountFixedPtAmt("1")); }); it("should return defined discount", async function () { expect(await perp.computeDiscount(tranchesNext[0].address)).to.eq(toDiscountFixedPtAmt("1")); }); describe("when updated", function () { beforeEach(async function () { - await discountStrategy.setTrancheDiscount(tranchesNext[0].address, toDiscountFixedPtAmt("0.5")); + await discountStrategy.computeTrancheDiscount + .whenCalledWith(tranchesNext[0].address) + .returns(toDiscountFixedPtAmt("0.5")); }); it("should return defined discount for new tranche", async function () { expect(await perp.computeDiscount(tranchesNext[0].address)).to.eq(toDiscountFixedPtAmt("0.5")); @@ -686,7 +723,7 @@ describe("PerpetualTranche", function () { describe("#computePrice", function () { beforeEach(async function () { expect(await perp.computePrice(constants.AddressZero)).not.to.eq(toPriceFixedPtAmt("0.33")); - await pricingStrategy.setPrice(toPriceFixedPtAmt("0.33")); + await pricingStrategy.computeTranchePrice.returns(toPriceFixedPtAmt("0.33")); }); it("should return the price from the strategy", async function () { expect(await perp.computePrice(constants.AddressZero)).to.eq(toPriceFixedPtAmt("0.33")); @@ -700,7 +737,7 @@ describe("PerpetualTranche", function () { feeToken = await ERC20.deploy(); await feeToken.init("Mock token", "MOCK"); expect(await perp.feeToken()).not.to.eq(feeToken.address); - await feeStrategy.setFeeToken(feeToken.address); + await feeStrategy.feeToken.returns(feeToken.address); }); it("should return the fee token from the strategy", async function () { @@ -727,9 +764,7 @@ describe("PerpetualTranche", function () { beforeEach(async function () { bond = await createBondWithFactory(bondFactory, collateralToken, [200, 300, 500], 3600); tranches = await getTranches(bond); - await issuer.setLatestBond(bond.address); - - await discountStrategy.setTrancheDiscount(tranches[0].address, toDiscountFixedPtAmt("1")); + await issuer.getLatestBond.returns(bond.address); await depositIntoBond(bond, toFixedPtAmt("1000"), deployer); await tranches[0].approve(perp.address, toFixedPtAmt("200")); @@ -748,9 +783,7 @@ describe("PerpetualTranche", function () { beforeEach(async function () { bond = await createBondWithFactory(bondFactory, collateralToken, [200, 300, 500], 3600); tranches = await getTranches(bond); - await issuer.setLatestBond(bond.address); - - await discountStrategy.setTrancheDiscount(tranches[0].address, toDiscountFixedPtAmt("1")); + await issuer.getLatestBond.returns(bond.address); await depositIntoBond(bond, toFixedPtAmt("1000"), deployer); await tranches[0].approve(perp.address, toFixedPtAmt("200")); @@ -758,10 +791,14 @@ describe("PerpetualTranche", function () { bondNext = await createBondWithFactory(bondFactory, collateralToken, [200, 300, 500], 3600); tranchesNext = await getTranches(bondNext); - await issuer.setLatestBond(bondNext.address); - - await discountStrategy.setTrancheDiscount(tranchesNext[0].address, toDiscountFixedPtAmt("0.5")); - await pricingStrategy.setTranchePrice(tranchesNext[0].address, toPriceFixedPtAmt("0.5")); + await issuer.getLatestBond.returns(bondNext.address); + + await discountStrategy.computeTrancheDiscount + .whenCalledWith(tranchesNext[0].address) + .returns(toDiscountFixedPtAmt("0.5")); + await pricingStrategy.computeTranchePrice + .whenCalledWith(tranchesNext[0].address) + .returns(toPriceFixedPtAmt("0.5")); await depositIntoBond(bondNext, toFixedPtAmt("1000"), deployer); await tranchesNext[0].approve(perp.address, toFixedPtAmt("100")); await perp.deposit(tranchesNext[0].address, toFixedPtAmt("100")); @@ -791,7 +828,6 @@ describe("PerpetualTranche", function () { bond = await bondAt(await perp.callStatic.getDepositBond()); tranches = await getTranches(bond); - await discountStrategy.setTrancheDiscount(tranches[0].address, toDiscountFixedPtAmt("1")); await depositIntoBond(bond, toFixedPtAmt("1000"), deployer); await tranches[0].approve(perp.address, toFixedPtAmt("200")); await perp.deposit(tranches[0].address, toFixedPtAmt("200")); @@ -800,8 +836,6 @@ describe("PerpetualTranche", function () { bondNext = await bondAt(await perp.callStatic.getDepositBond()); tranchesNext = await getTranches(bondNext); - await discountStrategy.setTrancheDiscount(tranchesNext[0].address, toDiscountFixedPtAmt("1")); - await pricingStrategy.setTranchePrice(tranchesNext[0].address, toPriceFixedPtAmt("1")); await depositIntoBond(bondNext, toFixedPtAmt("1000"), deployer); await tranchesNext[0].approve(perp.address, toFixedPtAmt("100")); await perp.deposit(tranchesNext[0].address, toFixedPtAmt("100")); @@ -829,7 +863,6 @@ describe("PerpetualTranche", function () { bond = await bondAt(await perp.callStatic.getDepositBond()); tranches = await getTranches(bond); - await discountStrategy.setTrancheDiscount(tranches[0].address, toDiscountFixedPtAmt("1")); await depositIntoBond(bond, toFixedPtAmt("1000"), deployer); await tranches[0].approve(perp.address, toFixedPtAmt("200")); await perp.deposit(tranches[0].address, toFixedPtAmt("200")); @@ -838,8 +871,6 @@ describe("PerpetualTranche", function () { bondNext = await bondAt(await perp.callStatic.getDepositBond()); tranchesNext = await getTranches(bondNext); - await discountStrategy.setTrancheDiscount(tranchesNext[0].address, toDiscountFixedPtAmt("1")); - await pricingStrategy.setTranchePrice(tranchesNext[0].address, toPriceFixedPtAmt("1")); await depositIntoBond(bondNext, toFixedPtAmt("1000"), deployer); await tranchesNext[0].approve(perp.address, toFixedPtAmt("100")); await perp.deposit(tranchesNext[0].address, toFixedPtAmt("100")); @@ -866,12 +897,10 @@ describe("PerpetualTranche", function () { issuer = await BondIssuer.deploy(bondFactory.address, collateralToken.address); await issuer.init(3600, [200, 300, 500], 1200, 0); await perp.updateBondIssuer(issuer.address); - await advancePerpQueue(perp, 3600); bond = await bondAt(await perp.callStatic.getDepositBond()); tranches = await getTranches(bond); - await discountStrategy.setTrancheDiscount(tranches[0].address, toDiscountFixedPtAmt("1")); await depositIntoBond(bond, toFixedPtAmt("1000"), deployer); await tranches[0].approve(perp.address, toFixedPtAmt("200")); await perp.deposit(tranches[0].address, toFixedPtAmt("200")); @@ -880,8 +909,6 @@ describe("PerpetualTranche", function () { bondNext = await bondAt(await perp.callStatic.getDepositBond()); tranchesNext = await getTranches(bondNext); - await discountStrategy.setTrancheDiscount(tranchesNext[0].address, toDiscountFixedPtAmt("1")); - await pricingStrategy.setTranchePrice(tranchesNext[0].address, toPriceFixedPtAmt("1")); await depositIntoBond(bondNext, toFixedPtAmt("1000"), deployer); await tranchesNext[0].approve(perp.address, toFixedPtAmt("100")); await perp.deposit(tranchesNext[0].address, toFixedPtAmt("100")); @@ -889,7 +916,7 @@ describe("PerpetualTranche", function () { await advancePerpQueue(perp, 2400); await rebase(collateralToken, rebaseOracle, 0.1); - await pricingStrategy.setTranchePrice(collateralToken.address, toPriceFixedPtAmt("1.1")); + await pricingStrategy.computeMatureTranchePrice.returns(toPriceFixedPtAmt("1.1")); }); it("should have expected reserve composition", async function () { @@ -916,7 +943,6 @@ describe("PerpetualTranche", function () { bond = await bondAt(await perp.callStatic.getDepositBond()); tranches = await getTranches(bond); - await discountStrategy.setTrancheDiscount(tranches[0].address, toDiscountFixedPtAmt("1")); await depositIntoBond(bond, toFixedPtAmt("1000"), deployer); await tranches[0].approve(perp.address, toFixedPtAmt("200")); await perp.deposit(tranches[0].address, toFixedPtAmt("200")); @@ -925,8 +951,6 @@ describe("PerpetualTranche", function () { bondNext = await bondAt(await perp.callStatic.getDepositBond()); tranchesNext = await getTranches(bondNext); - await discountStrategy.setTrancheDiscount(tranchesNext[0].address, toDiscountFixedPtAmt("1")); - await pricingStrategy.setTranchePrice(tranchesNext[0].address, toPriceFixedPtAmt("1")); await depositIntoBond(bondNext, toFixedPtAmt("1000"), deployer); await tranchesNext[0].approve(perp.address, toFixedPtAmt("100")); await perp.deposit(tranchesNext[0].address, toFixedPtAmt("100")); @@ -934,7 +958,7 @@ describe("PerpetualTranche", function () { await advancePerpQueue(perp, 2400); await rebase(collateralToken, rebaseOracle, -0.1); - await pricingStrategy.setTranchePrice(collateralToken.address, toPriceFixedPtAmt("0.9")); + await pricingStrategy.computeMatureTranchePrice.returns(toPriceFixedPtAmt("0.9")); }); it("should have expected reserve composition", async function () { @@ -962,7 +986,7 @@ describe("PerpetualTranche", function () { describe("when deposit bond matures too soon", async function () { beforeEach(async function () { bond = await createBondWithFactory(bondFactory, collateralToken, [200, 300, 500], 600); - await issuer.setLatestBond(bond.address); + await issuer.getLatestBond.returns(bond.address); await perp.updateState(); }); @@ -975,7 +999,7 @@ describe("PerpetualTranche", function () { beforeEach(async function () { await perp.updateTolerableTrancheMaturity(1200, 7200); bond = await createBondWithFactory(bondFactory, collateralToken, [200, 300, 500], 7210); - await issuer.setLatestBond(bond.address); + await issuer.getLatestBond.returns(bond.address); await perp.updateState(); }); @@ -989,7 +1013,7 @@ describe("PerpetualTranche", function () { await perp.updateTolerableTrancheMaturity(1200, 7200); const r = await setupCollateralToken("Ethereum", "ETH"); bond = await createBondWithFactory(bondFactory, r.collateralToken, [200, 300, 500], 3600); - await issuer.setLatestBond(bond.address); + await issuer.getLatestBond.returns(bond.address); await perp.updateState(); }); @@ -1003,7 +1027,7 @@ describe("PerpetualTranche", function () { beforeEach(async function () { await perp.updateTolerableTrancheMaturity(1200, 7200); bond = await createBondWithFactory(bondFactory, collateralToken, [200, 300, 500], 3600); - await issuer.setLatestBond(bond.address); + await issuer.getLatestBond.returns(bond.address); tx = perp.updateState(); await tx; }); @@ -1031,8 +1055,10 @@ describe("PerpetualTranche", function () { for (let i = 0; i < 5; i++) { const depositBond = await bondAt(await perp.callStatic.getDepositBond()); const tranches = await getTranches(depositBond); - await pricingStrategy.setTranchePrice(tranches[0].address, toPriceFixedPtAmt("1")); - await discountStrategy.setTrancheDiscount(tranches[0].address, toDiscountFixedPtAmt("1")); + await pricingStrategy.computeTranchePrice.whenCalledWith(tranches[0].address).returns(toPriceFixedPtAmt("1")); + await discountStrategy.computeTrancheDiscount + .whenCalledWith(tranches[0].address) + .returns(toDiscountFixedPtAmt("1")); await depositIntoBond(depositBond, toFixedPtAmt("1000"), deployer); await tranches[0].approve(perp.address, toFixedPtAmt("500")); await perp.deposit(tranches[0].address, toFixedPtAmt("500")); @@ -1108,8 +1134,10 @@ describe("PerpetualTranche", function () { for (let i = 0; i < 5; i++) { const depositBond = await bondAt(await perp.callStatic.getDepositBond()); const tranches = await getTranches(depositBond); - await pricingStrategy.setTranchePrice(tranches[0].address, toPriceFixedPtAmt("1")); - await discountStrategy.setTrancheDiscount(tranches[0].address, toDiscountFixedPtAmt("1")); + await pricingStrategy.computeTranchePrice.whenCalledWith(tranches[0].address).returns(toPriceFixedPtAmt("1")); + await discountStrategy.computeTrancheDiscount + .whenCalledWith(tranches[0].address) + .returns(toDiscountFixedPtAmt("1")); await depositIntoBond(depositBond, toFixedPtAmt("1000"), deployer); await tranches[0].approve(perp.address, toFixedPtAmt("500")); await perp.deposit(tranches[0].address, toFixedPtAmt("500")); @@ -1199,8 +1227,10 @@ describe("PerpetualTranche", function () { for (let i = 0; i < 5; i++) { const depositBond = await bondAt(await perp.callStatic.getDepositBond()); const tranches = await getTranches(depositBond); - await pricingStrategy.setTranchePrice(tranches[0].address, toPriceFixedPtAmt("1")); - await discountStrategy.setTrancheDiscount(tranches[0].address, toDiscountFixedPtAmt("1")); + await pricingStrategy.computeTranchePrice.whenCalledWith(tranches[0].address).returns(toPriceFixedPtAmt("1")); + await discountStrategy.computeTrancheDiscount + .whenCalledWith(tranches[0].address) + .returns(toDiscountFixedPtAmt("1")); await depositIntoBond(depositBond, toFixedPtAmt("1000"), deployer); await tranches[0].approve(perp.address, toFixedPtAmt("500")); await perp.deposit(tranches[0].address, toFixedPtAmt("500")); @@ -1287,11 +1317,15 @@ describe("PerpetualTranche", function () { for (let i = 0; i < 5; i++) { const depositBond = await bondAt(await perp.callStatic.getDepositBond()); const tranches = await getTranches(depositBond); - await pricingStrategy.setTranchePrice(tranches[0].address, toPriceFixedPtAmt("1")); + await pricingStrategy.computeTranchePrice.whenCalledWith(tranches[0].address).returns(toPriceFixedPtAmt("1")); if (i === 0) { - await discountStrategy.setTrancheDiscount(tranches[0].address, toDiscountFixedPtAmt("0.5")); + await discountStrategy.computeTrancheDiscount + .whenCalledWith(tranches[0].address) + .returns(toDiscountFixedPtAmt("0.5")); } else { - await discountStrategy.setTrancheDiscount(tranches[0].address, toDiscountFixedPtAmt("1")); + await discountStrategy.computeTrancheDiscount + .whenCalledWith(tranches[0].address) + .returns(toDiscountFixedPtAmt("1")); } await depositIntoBond(depositBond, toFixedPtAmt("1000"), deployer); await tranches[0].approve(perp.address, toFixedPtAmt("500")); @@ -1378,8 +1412,10 @@ describe("PerpetualTranche", function () { for (let i = 0; i < 5; i++) { const depositBond = await bondAt(await perp.callStatic.getDepositBond()); const tranches = await getTranches(depositBond); - await pricingStrategy.setTranchePrice(tranches[0].address, toPriceFixedPtAmt("1")); - await discountStrategy.setTrancheDiscount(tranches[0].address, toDiscountFixedPtAmt("1")); + await pricingStrategy.computeTranchePrice.whenCalledWith(tranches[0].address).returns(toPriceFixedPtAmt("1")); + await discountStrategy.computeTrancheDiscount + .whenCalledWith(tranches[0].address) + .returns(toDiscountFixedPtAmt("1")); await depositIntoBond(depositBond, toFixedPtAmt("1000"), deployer); await tranches[0].approve(perp.address, toFixedPtAmt("500")); await perp.deposit(tranches[0].address, toFixedPtAmt("500")); @@ -1413,8 +1449,10 @@ describe("PerpetualTranche", function () { for (let i = 0; i < 5; i++) { const depositBond = await bondAt(await perp.callStatic.getDepositBond()); const tranches = await getTranches(depositBond); - await pricingStrategy.setTranchePrice(tranches[0].address, toPriceFixedPtAmt("1")); - await discountStrategy.setTrancheDiscount(tranches[0].address, toDiscountFixedPtAmt("0.75")); + await pricingStrategy.computeTranchePrice.whenCalledWith(tranches[0].address).returns(toPriceFixedPtAmt("1")); + await discountStrategy.computeTrancheDiscount + .whenCalledWith(tranches[0].address) + .returns(toDiscountFixedPtAmt("0.75")); await depositIntoBond(depositBond, toFixedPtAmt("1000"), deployer); await tranches[0].approve(perp.address, toFixedPtAmt("500")); await perp.deposit(tranches[0].address, toFixedPtAmt("500")); @@ -1448,8 +1486,10 @@ describe("PerpetualTranche", function () { for (let i = 0; i < 5; i++) { const depositBond = await bondAt(await perp.callStatic.getDepositBond()); const tranches = await getTranches(depositBond); - await pricingStrategy.setTranchePrice(tranches[0].address, toPriceFixedPtAmt("0.9")); - await discountStrategy.setTrancheDiscount(tranches[0].address, toDiscountFixedPtAmt("0.8")); + await pricingStrategy.computeTranchePrice.whenCalledWith(tranches[0].address).returns(toPriceFixedPtAmt("0.9")); + await discountStrategy.computeTrancheDiscount + .whenCalledWith(tranches[0].address) + .returns(toDiscountFixedPtAmt("0.8")); await depositIntoBond(depositBond, toFixedPtAmt("1000"), deployer); await tranches[0].approve(perp.address, toFixedPtAmt("500")); await perp.deposit(tranches[0].address, toFixedPtAmt("500")); @@ -1457,7 +1497,7 @@ describe("PerpetualTranche", function () { await advancePerpQueue(perp, 1200); } await advancePerpQueueToRollover(perp, await bondAt(depositTranches[2].bond())); - await pricingStrategy.setPrice(toPriceFixedPtAmt("1.1")); + await pricingStrategy.computeMatureTranchePrice.returns(toPriceFixedPtAmt("1.1")); }); it("should return the tranche value", async function () { diff --git a/spot-contracts/test/PerpetualTranche_deposit.ts b/spot-contracts/test/PerpetualTranche_deposit.ts index 5b31e8f6..dff69cd4 100644 --- a/spot-contracts/test/PerpetualTranche_deposit.ts +++ b/spot-contracts/test/PerpetualTranche_deposit.ts @@ -1,7 +1,7 @@ -import { expect } from "chai"; +import { expect, use } from "chai"; import { network, ethers, upgrades } from "hardhat"; import { Contract, Transaction, Signer, constants } from "ethers"; - +import { smock } from "@defi-wonderland/smock"; import { setupCollateralToken, setupBondFactory, @@ -15,6 +15,7 @@ import { advancePerpQueue, checkReserveComposition, } from "./helpers"; +use(smock.matchers); let perp: Contract, bondFactory: Contract, @@ -44,14 +45,24 @@ describe("PerpetualTranche", function () { issuer = await BondIssuer.deploy(bondFactory.address, collateralToken.address); await issuer.init(3600, [500, 500], 1200, 0); - const FeeStrategy = await ethers.getContractFactory("MockFeeStrategy"); - feeStrategy = await FeeStrategy.deploy(); - - const PricingStrategy = await ethers.getContractFactory("MockPricingStrategy"); - pricingStrategy = await PricingStrategy.deploy(); - - const DiscountStrategy = await ethers.getContractFactory("MockDiscountStrategy"); - discountStrategy = await DiscountStrategy.deploy(); + const FeeStrategy = await ethers.getContractFactory("BasicFeeStrategy"); + feeStrategy = await smock.fake(FeeStrategy); + await feeStrategy.computeMintFees.returns(["0", "0"]); + await feeStrategy.computeBurnFees.returns(["0", "0"]); + await feeStrategy.computeRolloverFees.returns(["0", "0"]); + + const PricingStrategy = await ethers.getContractFactory("UnitPricingStrategy"); + pricingStrategy = await smock.fake(PricingStrategy); + await pricingStrategy.decimals.returns(8); + await pricingStrategy.computeMatureTranchePrice.returns(toPriceFixedPtAmt("1")); + await pricingStrategy.computeTranchePrice.returns(toPriceFixedPtAmt("1")); + + const DiscountStrategy = await ethers.getContractFactory("TrancheClassDiscountStrategy"); + discountStrategy = await smock.fake(DiscountStrategy); + await discountStrategy.decimals.returns(18); + await discountStrategy.computeTrancheDiscount + .whenCalledWith(collateralToken.address) + .returns(toDiscountFixedPtAmt("1")); const PerpetualTranche = await ethers.getContractFactory("PerpetualTranche"); perp = await upgrades.deployProxy( @@ -74,10 +85,12 @@ describe("PerpetualTranche", function () { depositBond = await bondAt(await perp.callStatic.getDepositBond()); [depositTrancheA, depositTrancheZ] = await getTranches(depositBond); - await feeStrategy.setFeeToken(perp.address); - await feeStrategy.setMintFee(toFixedPtAmt("0")); - await pricingStrategy.setTranchePrice(depositTrancheA.address, toPriceFixedPtAmt("1")); - await discountStrategy.setTrancheDiscount(depositTrancheA.address, toDiscountFixedPtAmt("1")); + await feeStrategy.feeToken.returns(perp.address); + + await pricingStrategy.computeTranchePrice.whenCalledWith(depositTrancheA.address).returns(toPriceFixedPtAmt("1")); + await discountStrategy.computeTrancheDiscount + .whenCalledWith(depositTrancheA.address) + .returns(toDiscountFixedPtAmt("1")); await depositIntoBond(depositBond, toFixedPtAmt("1000"), deployer); await depositTrancheA.approve(perp.address, toFixedPtAmt("500")); @@ -103,11 +116,12 @@ describe("PerpetualTranche", function () { describe("when bond issuer is NOT set correctly", function () { let bond: Contract; beforeEach(async function () { - const BondIssuer = await ethers.getContractFactory("MockBondIssuer"); - const newIssuer = await BondIssuer.deploy(collateralToken.address); + const BondIssuer = await ethers.getContractFactory("BondIssuer"); + const newIssuer = await smock.fake(BondIssuer); + await newIssuer.collateral.returns(collateralToken.address); await perp.updateBondIssuer(newIssuer.address); bond = await createBondWithFactory(bondFactory, perp, [200, 300, 500], 3600); - await newIssuer.setLatestBond(bond.address); + await newIssuer.getLatestBond.returns(bond.address); }); it("should not update the deposit bond", async function () { await depositTrancheA.approve(perp.address, toFixedPtAmt("500")); @@ -124,7 +138,8 @@ describe("PerpetualTranche", function () { }); it("should revert", async function () { await depositTrancheA.approve(perp.address, toFixedPtAmt("500")); - await expect(perp.deposit(depositTrancheA.address, toFixedPtAmt("500"))).to.revertedWith( + await expect(perp.deposit(depositTrancheA.address, toFixedPtAmt("500"))).to.revertedWithCustomError( + perp, "UnacceptableDepositTranche", ); }); @@ -138,7 +153,8 @@ describe("PerpetualTranche", function () { await maliciousTranche.mint(deployerAddress, toFixedPtAmt("500")); await maliciousTranche.setBond(await perp.callStatic.getDepositBond()); await maliciousTranche.approve(perp.address, toFixedPtAmt("500")); - await expect(perp.deposit(maliciousTranche.address, toFixedPtAmt("500"))).to.revertedWith( + await expect(perp.deposit(maliciousTranche.address, toFixedPtAmt("500"))).to.revertedWithCustomError( + perp, "UnacceptableDepositTranche", ); }); @@ -172,8 +188,11 @@ describe("PerpetualTranche", function () { await perp.updateMintingLimits(toFixedPtAmt("499"), toFixedPtAmt("1000")); }); - it("should mint the correct amount", async function () { - await expect(perp.deposit(depositTrancheA.address, toFixedPtAmt("500"))).to.revertedWith("ExceededMaxSupply"); + it("should revert", async function () { + await expect(perp.deposit(depositTrancheA.address, toFixedPtAmt("500"))).to.revertedWithCustomError( + perp, + "ExceededMaxSupply", + ); }); }); @@ -183,9 +202,10 @@ describe("PerpetualTranche", function () { await perp.updateMintingLimits(toFixedPtAmt("499"), toFixedPtAmt("1000")); }); - it("should mint the correct amount", async function () { - await expect(perp.deposit(depositTrancheA.address, toFixedPtAmt("100"))).to.revertedWith( - "ExceededMaxSupply(500000000000000000000, 499000000000000000000)", + it("should revert", async function () { + await expect(perp.deposit(depositTrancheA.address, toFixedPtAmt("100"))).to.revertedWithCustomError( + perp, + "ExceededMaxSupply", ); }); }); @@ -195,8 +215,9 @@ describe("PerpetualTranche", function () { await perp.updateMintingLimits(toFixedPtAmt("1000"), toFixedPtAmt("499")); }); - it("should mint the correct amount", async function () { - await expect(perp.deposit(depositTrancheA.address, toFixedPtAmt("500"))).to.revertedWith( + it("should revert", async function () { + await expect(perp.deposit(depositTrancheA.address, toFixedPtAmt("500"))).to.revertedWithCustomError( + perp, "ExceededMaxMintPerTranche", ); }); @@ -208,39 +229,53 @@ describe("PerpetualTranche", function () { await perp.updateMintingLimits(toFixedPtAmt("1000"), toFixedPtAmt("499")); }); - it("should mint the correct amount", async function () { - await expect(perp.deposit(depositTrancheA.address, toFixedPtAmt("100"))).to.revertedWith( - `ExceededMaxMintPerTranche("${depositTrancheA.address}", 500000000000000000000, 499000000000000000000)`, + it("should revert", async function () { + await expect(perp.deposit(depositTrancheA.address, toFixedPtAmt("100"))).to.revertedWithCustomError( + perp, + `ExceededMaxMintPerTranche`, ); }); }); describe("when tranche amount is zero", function () { it("should revert", async function () { - await expect(perp.deposit(depositTrancheA.address, toFixedPtAmt("0"))).to.revertedWith("UnacceptableMintAmt"); + await expect(perp.deposit(depositTrancheA.address, toFixedPtAmt("0"))).to.revertedWithCustomError( + perp, + "UnacceptableMintAmt", + ); }); }); describe("when tranche price is zero", function () { beforeEach(async function () { - await pricingStrategy.setTranchePrice(depositTrancheA.address, toPriceFixedPtAmt("0")); + await pricingStrategy.computeTranchePrice + .whenCalledWith(depositTrancheA.address) + .returns(toPriceFixedPtAmt("0")); }); it("should revert", async function () { - await expect(perp.deposit(depositTrancheA.address, toFixedPtAmt("500"))).to.revertedWith("UnacceptableMintAmt"); + await expect(perp.deposit(depositTrancheA.address, toFixedPtAmt("500"))).to.revertedWithCustomError( + perp, + "UnacceptableMintAmt", + ); }); }); describe("when tranche discount is zero", function () { it("should revert", async function () { - await expect(perp.deposit(depositTrancheZ.address, toFixedPtAmt("500"))).to.revertedWith("UnacceptableMintAmt"); + await expect(perp.deposit(depositTrancheZ.address, toFixedPtAmt("500"))).to.revertedWithCustomError( + perp, + "UnacceptableMintAmt", + ); }); }); describe("when total supply is zero", function () { describe("when tranche price is 0.5", function () { beforeEach(async function () { - await pricingStrategy.setTranchePrice(depositTrancheA.address, toPriceFixedPtAmt("0.5")); + await pricingStrategy.computeTranchePrice + .whenCalledWith(depositTrancheA.address) + .returns(toPriceFixedPtAmt("0.5")); }); it("should mint the correct amount", async function () { @@ -251,7 +286,9 @@ describe("PerpetualTranche", function () { describe("when tranche discount is 0.5", function () { beforeEach(async function () { - await discountStrategy.setTrancheDiscount(depositTrancheA.address, toDiscountFixedPtAmt("0.5")); + await discountStrategy.computeTrancheDiscount + .whenCalledWith(depositTrancheA.address) + .returns(toDiscountFixedPtAmt("0.5")); }); it("should mint the correct amount", async function () { @@ -262,8 +299,12 @@ describe("PerpetualTranche", function () { describe("when tranche discount is 0.5 and tranche price is 0.5", function () { beforeEach(async function () { - await pricingStrategy.setTranchePrice(depositTrancheA.address, toPriceFixedPtAmt("0.5")); - await discountStrategy.setTrancheDiscount(depositTrancheA.address, toDiscountFixedPtAmt("0.5")); + await pricingStrategy.computeTranchePrice + .whenCalledWith(depositTrancheA.address) + .returns(toPriceFixedPtAmt("0.5")); + await discountStrategy.computeTrancheDiscount + .whenCalledWith(depositTrancheA.address) + .returns(toDiscountFixedPtAmt("0.5")); }); it("should mint the correct amount", async function () { @@ -276,8 +317,12 @@ describe("PerpetualTranche", function () { describe("when total supply > zero", function () { let newBond: Contract, newTranche: Contract; beforeEach(async function () { - await discountStrategy.setTrancheDiscount(depositTrancheA.address, toDiscountFixedPtAmt("1")); - await pricingStrategy.setTranchePrice(depositTrancheA.address, toPriceFixedPtAmt("1")); + await discountStrategy.computeTrancheDiscount + .whenCalledWith(depositTrancheA.address) + .returns(toDiscountFixedPtAmt("1")); + await pricingStrategy.computeTranchePrice + .whenCalledWith(depositTrancheA.address) + .returns(toPriceFixedPtAmt("1")); await perp.deposit(depositTrancheA.address, toFixedPtAmt("200")); await advancePerpQueue(perp, 1200); @@ -291,8 +336,12 @@ describe("PerpetualTranche", function () { describe("when price is eql to avg reserve price", function () { describe("when discount is 1", function () { beforeEach(async function () { - await discountStrategy.setTrancheDiscount(newTranche.address, toDiscountFixedPtAmt("1")); - await pricingStrategy.setTranchePrice(newTranche.address, toPriceFixedPtAmt("1")); + await discountStrategy.computeTrancheDiscount + .whenCalledWith(newTranche.address) + .returns(toDiscountFixedPtAmt("1")); + await pricingStrategy.computeTranchePrice + .whenCalledWith(newTranche.address) + .returns(toPriceFixedPtAmt("1")); }); it("should mint the correct amount", async function () { @@ -303,8 +352,12 @@ describe("PerpetualTranche", function () { describe("when discount < 1", function () { beforeEach(async function () { - await discountStrategy.setTrancheDiscount(newTranche.address, toDiscountFixedPtAmt("0.5")); - await pricingStrategy.setTranchePrice(newTranche.address, toPriceFixedPtAmt("1")); + await discountStrategy.computeTrancheDiscount + .whenCalledWith(newTranche.address) + .returns(toDiscountFixedPtAmt("0.5")); + await pricingStrategy.computeTranchePrice + .whenCalledWith(newTranche.address) + .returns(toPriceFixedPtAmt("1")); }); it("should mint the correct amount", async function () { @@ -315,8 +368,12 @@ describe("PerpetualTranche", function () { describe("when discount > 1", function () { beforeEach(async function () { - await discountStrategy.setTrancheDiscount(newTranche.address, toDiscountFixedPtAmt("2")); - await pricingStrategy.setTranchePrice(newTranche.address, toPriceFixedPtAmt("1")); + await discountStrategy.computeTrancheDiscount + .whenCalledWith(newTranche.address) + .returns(toDiscountFixedPtAmt("2")); + await pricingStrategy.computeTranchePrice + .whenCalledWith(newTranche.address) + .returns(toPriceFixedPtAmt("1")); }); it("should mint the correct amount", async function () { @@ -328,9 +385,13 @@ describe("PerpetualTranche", function () { describe("when price is > avg reserve price", function () { beforeEach(async function () { - await pricingStrategy.setTranchePrice(depositTrancheA.address, toPriceFixedPtAmt("0.5")); - await discountStrategy.setTrancheDiscount(newTranche.address, toDiscountFixedPtAmt("1")); - await pricingStrategy.setTranchePrice(newTranche.address, toPriceFixedPtAmt("1")); + await pricingStrategy.computeTranchePrice + .whenCalledWith(depositTrancheA.address) + .returns(toPriceFixedPtAmt("0.5")); + await discountStrategy.computeTrancheDiscount + .whenCalledWith(newTranche.address) + .returns(toDiscountFixedPtAmt("1")); + await pricingStrategy.computeTranchePrice.whenCalledWith(newTranche.address).returns(toPriceFixedPtAmt("1")); }); it("should mint the correct amount", async function () { @@ -341,9 +402,13 @@ describe("PerpetualTranche", function () { describe("when price is < avg reserve price", function () { beforeEach(async function () { - await pricingStrategy.setTranchePrice(depositTrancheA.address, toPriceFixedPtAmt("2")); - await discountStrategy.setTrancheDiscount(newTranche.address, toDiscountFixedPtAmt("1")); - await pricingStrategy.setTranchePrice(newTranche.address, toPriceFixedPtAmt("1")); + await pricingStrategy.computeTranchePrice + .whenCalledWith(depositTrancheA.address) + .returns(toPriceFixedPtAmt("2")); + await discountStrategy.computeTrancheDiscount + .whenCalledWith(newTranche.address) + .returns(toDiscountFixedPtAmt("1")); + await pricingStrategy.computeTranchePrice.whenCalledWith(newTranche.address).returns(toPriceFixedPtAmt("1")); }); it("should mint the correct amount", async function () { @@ -383,7 +448,7 @@ describe("PerpetualTranche", function () { }); describe("when fee > 0", function () { beforeEach(async function () { - await feeStrategy.setMintFee(toFixedPtAmt("1")); + await feeStrategy.computeMintFees.returns([toFixedPtAmt("1"), "0"]); }); it("should mint perp tokens", async function () { await expect(() => perp.deposit(depositTrancheA.address, toFixedPtAmt("500"))).to.changeTokenBalance( @@ -417,7 +482,7 @@ describe("PerpetualTranche", function () { await depositTrancheA.increaseAllowance(perp.address, toFixedPtAmt("1")); await perp.deposit(depositTrancheA.address, toFixedPtAmt("1")); await perp.transfer(perp.address, toFixedPtAmt("1")); - await feeStrategy.setMintFee(toFixedPtAmt("-1")); + await feeStrategy.computeMintFees.returns([toFixedPtAmt("-1"), "0"]); }); it("should mint perp tokens", async function () { await expect(() => perp.deposit(depositTrancheA.address, toFixedPtAmt("500"))).to.changeTokenBalance( @@ -451,7 +516,7 @@ describe("PerpetualTranche", function () { await depositTrancheA.increaseAllowance(perp.address, toFixedPtAmt("1")); await perp.deposit(depositTrancheA.address, toFixedPtAmt("1")); await perp.transfer(perp.address, toFixedPtAmt("0.5")); - await feeStrategy.setMintFee(toFixedPtAmt("-1")); + await feeStrategy.computeMintFees.returns([toFixedPtAmt("-1"), "0"]); }); it("should mint perp tokens", async function () { await expect(() => perp.deposit(depositTrancheA.address, toFixedPtAmt("500"))).to.changeTokenBalance( @@ -487,8 +552,7 @@ describe("PerpetualTranche", function () { describe("when protocol fee > 0", function () { beforeEach(async function () { await perp.transferOwnership(await otherUser.getAddress()); - await feeStrategy.setMintFee(toFixedPtAmt("1")); - await feeStrategy.setProtocolFee(toFixedPtAmt("1")); + await feeStrategy.computeMintFees.returns([toFixedPtAmt("1"), toFixedPtAmt("1")]); }); it("should mint perp tokens", async function () { await expect(() => perp.deposit(depositTrancheA.address, toFixedPtAmt("500"))).to.changeTokenBalance( @@ -531,7 +595,7 @@ describe("PerpetualTranche", function () { const ERC20 = await ethers.getContractFactory("MockERC20"); feeToken = await ERC20.deploy(); await feeToken.init("Mock token", "MOCK"); - await feeStrategy.setFeeToken(feeToken.address); + await feeStrategy.feeToken.returns(feeToken.address); }); describe("when fee is zero", async function () { @@ -563,7 +627,7 @@ describe("PerpetualTranche", function () { }); describe("when fee > 0", async function () { beforeEach(async function () { - await feeStrategy.setMintFee(toFixedPtAmt("1")); + await feeStrategy.computeMintFees.returns([toFixedPtAmt("1"), "0"]); }); describe("with no approval", function () { @@ -588,7 +652,7 @@ describe("PerpetualTranche", function () { describe("with sufficient fee", async function () { beforeEach(async function () { - await feeStrategy.setMintFee(toFixedPtAmt("1")); + await feeStrategy.computeMintFees.returns([toFixedPtAmt("1"), "0"]); await feeToken.mint(deployerAddress, toFixedPtAmt("1")); await feeToken.approve(perp.address, toFixedPtAmt("1")); }); @@ -622,7 +686,7 @@ describe("PerpetualTranche", function () { }); describe("when fee < 0", async function () { beforeEach(async function () { - await feeStrategy.setMintFee(toFixedPtAmt("-1")); + await feeStrategy.computeMintFees.returns([toFixedPtAmt("-1"), "0"]); await feeToken.mint(perp.address, toFixedPtAmt("1")); }); @@ -655,8 +719,7 @@ describe("PerpetualTranche", function () { describe("when protocol fee > 0", async function () { beforeEach(async function () { await perp.transferOwnership(await otherUser.getAddress()); - await feeStrategy.setMintFee(toFixedPtAmt("1")); - await feeStrategy.setProtocolFee(toFixedPtAmt("0.5")); + await feeStrategy.computeMintFees.returns([toFixedPtAmt("1"), toFixedPtAmt("0.5")]); await feeToken.mint(deployerAddress, toFixedPtAmt("1.5")); await feeToken.approve(perp.address, toFixedPtAmt("1.5")); }); @@ -775,8 +838,12 @@ describe("PerpetualTranche", function () { await depositIntoBond(newBond, toFixedPtAmt("1000"), deployer); const tranches = await getTranches(newBond); newTranche = tranches[0]; - await discountStrategy.setTrancheDiscount(newTranche.address, toDiscountFixedPtAmt("0.5")); - await pricingStrategy.setTranchePrice(newTranche.address, toPriceFixedPtAmt("0.2")); + await discountStrategy.computeTrancheDiscount + .whenCalledWith(newTranche.address) + .returns(toDiscountFixedPtAmt("0.5")); + await pricingStrategy.computeTranchePrice + .whenCalledWith(newTranche.address) + .returns(toPriceFixedPtAmt("0.2")); await checkReserveComposition( perp, diff --git a/spot-contracts/test/PerpetualTranche_redeem.ts b/spot-contracts/test/PerpetualTranche_redeem.ts index bde81eb6..29e0d9de 100644 --- a/spot-contracts/test/PerpetualTranche_redeem.ts +++ b/spot-contracts/test/PerpetualTranche_redeem.ts @@ -1,7 +1,7 @@ -import { expect } from "chai"; +import { expect, use } from "chai"; import { network, ethers, upgrades } from "hardhat"; import { Contract, Transaction, Signer, constants } from "ethers"; - +import { smock } from "@defi-wonderland/smock"; import { setupCollateralToken, setupBondFactory, @@ -15,6 +15,7 @@ import { checkReserveComposition, rebase, } from "./helpers"; +use(smock.matchers); let perp: Contract, bondFactory: Contract, @@ -45,14 +46,24 @@ describe("PerpetualTranche", function () { issuer = await BondIssuer.deploy(bondFactory.address, collateralToken.address); await issuer.init(3600, [500, 500], 1200, 0); - const FeeStrategy = await ethers.getContractFactory("MockFeeStrategy"); - feeStrategy = await FeeStrategy.deploy(); - - const PricingStrategy = await ethers.getContractFactory("MockPricingStrategy"); - pricingStrategy = await PricingStrategy.deploy(); - - const DiscountStrategy = await ethers.getContractFactory("MockDiscountStrategy"); - discountStrategy = await DiscountStrategy.deploy(); + const FeeStrategy = await ethers.getContractFactory("BasicFeeStrategy"); + feeStrategy = await smock.fake(FeeStrategy); + await feeStrategy.computeMintFees.returns(["0", "0"]); + await feeStrategy.computeBurnFees.returns(["0", "0"]); + await feeStrategy.computeRolloverFees.returns(["0", "0"]); + + const PricingStrategy = await ethers.getContractFactory("UnitPricingStrategy"); + pricingStrategy = await smock.fake(PricingStrategy); + await pricingStrategy.decimals.returns(8); + await pricingStrategy.computeMatureTranchePrice.returns(toPriceFixedPtAmt("1")); + await pricingStrategy.computeTranchePrice.returns(toPriceFixedPtAmt("1")); + + const DiscountStrategy = await ethers.getContractFactory("TrancheClassDiscountStrategy"); + discountStrategy = await smock.fake(DiscountStrategy); + await discountStrategy.decimals.returns(18); + await discountStrategy.computeTrancheDiscount + .whenCalledWith(collateralToken.address) + .returns(toDiscountFixedPtAmt("1")); const PerpetualTranche = await ethers.getContractFactory("PerpetualTranche"); perp = await upgrades.deployProxy( @@ -75,10 +86,13 @@ describe("PerpetualTranche", function () { depositBond = await bondAt(await perp.callStatic.getDepositBond()); [initialDepositTranche] = await getTranches(depositBond); - await feeStrategy.setFeeToken(perp.address); - await feeStrategy.setMintFee(toFixedPtAmt("0")); - await pricingStrategy.setTranchePrice(initialDepositTranche.address, toPriceFixedPtAmt("1")); - await discountStrategy.setTrancheDiscount(initialDepositTranche.address, toDiscountFixedPtAmt("1")); + await feeStrategy.feeToken.returns(perp.address); + await pricingStrategy.computeTranchePrice + .whenCalledWith(initialDepositTranche.address) + .returns(toPriceFixedPtAmt("1")); + await discountStrategy.computeTrancheDiscount + .whenCalledWith(initialDepositTranche.address) + .returns(toDiscountFixedPtAmt("1")); await depositIntoBond(depositBond, toFixedPtAmt("1000"), deployer); await initialDepositTranche.approve(perp.address, toFixedPtAmt("500")); @@ -146,15 +160,13 @@ describe("PerpetualTranche", function () { it("should revert", async function () { expect(await perp.balanceOf(deployerAddress)).to.lte(toFixedPtAmt("500")); - await expect(perp.redeem(toFixedPtAmt("500"))).to.revertedWith( - "UnacceptableBurnAmt(500000000000000000000, 250000000000000000000)", - ); + await expect(perp.redeem(toFixedPtAmt("500"))).to.revertedWithCustomError(perp, "UnacceptableBurnAmt"); }); }); describe("when requested amount is zero", function () { it("should revert", async function () { - await expect(perp.redeem(toFixedPtAmt("0"))).to.revertedWith("UnacceptableBurnAmt(0, 500000000000000000000)"); + await expect(perp.redeem(toFixedPtAmt("0"))).to.revertedWithCustomError(perp, "UnacceptableBurnAmt"); }); }); @@ -164,7 +176,7 @@ describe("PerpetualTranche", function () { }); it("should revert", async function () { - await expect(perp.redeem(toFixedPtAmt("100"))).to.revertedWith("UnacceptableBurnAmt(100000000000000000000, 0)"); + await expect(perp.redeem(toFixedPtAmt("100"))).to.revertedWithCustomError(perp, "UnacceptableBurnAmt"); }); it("should return 0", async function () { @@ -176,11 +188,11 @@ describe("PerpetualTranche", function () { describe("when the supply increases", function () { beforeEach(async function () { - await feeStrategy.setBurnFee(toFixedPtAmt("-1")); + await feeStrategy.computeBurnFees.returns([toFixedPtAmt("-1"), "0"]); }); it("should revert", async function () { - await expect(perp.redeem(toFixedPtAmt("0.01"))).to.revertedWith("ExpectedSupplyReduction"); + await expect(perp.redeem(toFixedPtAmt("0.01"))).to.revertedWithCustomError(perp, "ExpectedSupplyReduction"); }); }); @@ -214,7 +226,7 @@ describe("PerpetualTranche", function () { await depositIntoBond(await bondAt(await perp.callStatic.getDepositBond()), toFixedPtAmt("2"), deployer); await initialDepositTranche.increaseAllowance(perp.address, toFixedPtAmt("1")); await perp.deposit(initialDepositTranche.address, toFixedPtAmt("1")); - await feeStrategy.setBurnFee(toFixedPtAmt("1")); + await feeStrategy.computeBurnFees.returns([toFixedPtAmt("1"), "0"]); }); it("should burn perp tokens", async function () { await expect(() => perp.redeem(toFixedPtAmt("500"))).to.changeTokenBalance( @@ -248,7 +260,7 @@ describe("PerpetualTranche", function () { await initialDepositTranche.increaseAllowance(perp.address, toFixedPtAmt("1")); await perp.deposit(initialDepositTranche.address, toFixedPtAmt("1")); await perp.transfer(perp.address, toFixedPtAmt("1")); - await feeStrategy.setBurnFee(toFixedPtAmt("-1")); + await feeStrategy.computeBurnFees.returns([toFixedPtAmt("-1"), "0"]); }); it("should burn perp tokens", async function () { await expect(() => perp.redeem(toFixedPtAmt("500"))).to.changeTokenBalance( @@ -282,7 +294,7 @@ describe("PerpetualTranche", function () { await initialDepositTranche.increaseAllowance(perp.address, toFixedPtAmt("1")); await perp.deposit(initialDepositTranche.address, toFixedPtAmt("1")); await perp.transfer(perp.address, toFixedPtAmt("0.5")); - await feeStrategy.setBurnFee(toFixedPtAmt("-1")); + await feeStrategy.computeBurnFees.returns([toFixedPtAmt("-1"), "0"]); }); it("should burn perp tokens", async function () { await expect(() => perp.redeem(toFixedPtAmt("500"))).to.changeTokenBalance( @@ -321,8 +333,7 @@ describe("PerpetualTranche", function () { await depositIntoBond(await bondAt(await perp.callStatic.getDepositBond()), toFixedPtAmt("3"), deployer); await initialDepositTranche.increaseAllowance(perp.address, toFixedPtAmt("1.5")); await perp.deposit(initialDepositTranche.address, toFixedPtAmt("1.5")); - await feeStrategy.setBurnFee(toFixedPtAmt("1")); - await feeStrategy.setProtocolFee(toFixedPtAmt("0.5")); + await feeStrategy.computeBurnFees.returns([toFixedPtAmt("1"), toFixedPtAmt("0.5")]); }); it("should burn perp tokens", async function () { await expect(() => perp.redeem(toFixedPtAmt("500"))).to.changeTokenBalance( @@ -364,7 +375,7 @@ describe("PerpetualTranche", function () { const ERC20 = await ethers.getContractFactory("MockERC20"); feeToken = await ERC20.deploy(); await feeToken.init("Mock token", "MOCK"); - await feeStrategy.setFeeToken(feeToken.address); + await feeStrategy.feeToken.returns(feeToken.address); }); describe("when fee is zero", async function () { @@ -400,7 +411,7 @@ describe("PerpetualTranche", function () { describe("when fee > 0", async function () { beforeEach(async function () { - await feeStrategy.setBurnFee(toFixedPtAmt("1")); + await feeStrategy.computeBurnFees.returns([toFixedPtAmt("1"), "0"]); }); describe("with no approval", function () { @@ -421,7 +432,7 @@ describe("PerpetualTranche", function () { describe("with sufficient fee", async function () { beforeEach(async function () { - await feeStrategy.setBurnFee(toFixedPtAmt("1")); + await feeStrategy.computeBurnFees.returns([toFixedPtAmt("1"), "0"]); await feeToken.mint(deployerAddress, toFixedPtAmt("1")); await feeToken.approve(perp.address, toFixedPtAmt("1")); }); @@ -459,7 +470,7 @@ describe("PerpetualTranche", function () { describe("when fee < 0", async function () { beforeEach(async function () { - await feeStrategy.setBurnFee(toFixedPtAmt("-1")); + await feeStrategy.computeBurnFees.returns([toFixedPtAmt("-1"), "0"]); await feeToken.mint(perp.address, toFixedPtAmt("1")); }); @@ -496,8 +507,7 @@ describe("PerpetualTranche", function () { describe("when protocol fee > 0", async function () { beforeEach(async function () { await perp.transferOwnership(await otherUser.getAddress()); - await feeStrategy.setBurnFee(toFixedPtAmt("1")); - await feeStrategy.setProtocolFee(toFixedPtAmt("0.5")); + await feeStrategy.computeBurnFees.returns([toFixedPtAmt("1"), toFixedPtAmt("0.5")]); await feeToken.mint(deployerAddress, toFixedPtAmt("1.5")); await feeToken.approve(perp.address, toFixedPtAmt("1.5")); }); @@ -560,8 +570,12 @@ describe("PerpetualTranche", function () { const tranches = await getTranches(newBond); newRedemptionTranche = tranches[0]; - await pricingStrategy.setTranchePrice(newRedemptionTranche.address, toPriceFixedPtAmt("1")); - await discountStrategy.setTrancheDiscount(newRedemptionTranche.address, toDiscountFixedPtAmt("0.5")); + await pricingStrategy.computeTranchePrice + .whenCalledWith(newRedemptionTranche.address) + .returns(toPriceFixedPtAmt("1")); + await discountStrategy.computeTrancheDiscount + .whenCalledWith(newRedemptionTranche.address) + .returns(toDiscountFixedPtAmt("0.5")); await newRedemptionTranche.approve(perp.address, toFixedPtAmt("500")); await perp.deposit(newRedemptionTranche.address, toFixedPtAmt("500")); @@ -629,8 +643,12 @@ describe("PerpetualTranche", function () { const tranches = await getTranches(newBond); newRedemptionTranche = tranches[0]; - await pricingStrategy.setTranchePrice(newRedemptionTranche.address, toPriceFixedPtAmt("1")); - await discountStrategy.setTrancheDiscount(newRedemptionTranche.address, toDiscountFixedPtAmt("0.5")); + await pricingStrategy.computeTranchePrice + .whenCalledWith(newRedemptionTranche.address) + .returns(toPriceFixedPtAmt("1")); + await discountStrategy.computeTrancheDiscount + .whenCalledWith(newRedemptionTranche.address) + .returns(toDiscountFixedPtAmt("0.5")); await newRedemptionTranche.approve(perp.address, toFixedPtAmt("500")); await perp.deposit(newRedemptionTranche.address, toFixedPtAmt("500")); @@ -693,8 +711,12 @@ describe("PerpetualTranche", function () { const tranches = await getTranches(newBond); newRedemptionTranche = tranches[0]; - await pricingStrategy.setTranchePrice(newRedemptionTranche.address, toPriceFixedPtAmt("1")); - await discountStrategy.setTrancheDiscount(newRedemptionTranche.address, toDiscountFixedPtAmt("0.5")); + await pricingStrategy.computeTranchePrice + .whenCalledWith(newRedemptionTranche.address) + .returns(toPriceFixedPtAmt("1")); + await discountStrategy.computeTrancheDiscount + .whenCalledWith(newRedemptionTranche.address) + .returns(toDiscountFixedPtAmt("0.5")); await newRedemptionTranche.approve(perp.address, toFixedPtAmt("500")); await perp.deposit(newRedemptionTranche.address, toFixedPtAmt("500")); @@ -764,8 +786,12 @@ describe("PerpetualTranche", function () { const tranches = await getTranches(newBond); newRedemptionTranche = tranches[0]; - await pricingStrategy.setTranchePrice(newRedemptionTranche.address, toPriceFixedPtAmt("1")); - await discountStrategy.setTrancheDiscount(newRedemptionTranche.address, toDiscountFixedPtAmt("0.5")); + await pricingStrategy.computeTranchePrice + .whenCalledWith(newRedemptionTranche.address) + .returns(toPriceFixedPtAmt("1")); + await discountStrategy.computeTrancheDiscount + .whenCalledWith(newRedemptionTranche.address) + .returns(toDiscountFixedPtAmt("0.5")); await newRedemptionTranche.approve(perp.address, toFixedPtAmt("500")); await perp.deposit(newRedemptionTranche.address, toFixedPtAmt("500")); @@ -829,8 +855,12 @@ describe("PerpetualTranche", function () { const tranches = await getTranches(newBond); newRedemptionTranche = tranches[0]; - await pricingStrategy.setTranchePrice(newRedemptionTranche.address, toPriceFixedPtAmt("1")); - await discountStrategy.setTrancheDiscount(newRedemptionTranche.address, toDiscountFixedPtAmt("0.5")); + await pricingStrategy.computeTranchePrice + .whenCalledWith(newRedemptionTranche.address) + .returns(toPriceFixedPtAmt("1")); + await discountStrategy.computeTrancheDiscount + .whenCalledWith(newRedemptionTranche.address) + .returns(toDiscountFixedPtAmt("0.5")); await newRedemptionTranche.approve(perp.address, toFixedPtAmt("500")); await perp.deposit(newRedemptionTranche.address, toFixedPtAmt("500")); @@ -887,11 +917,19 @@ describe("PerpetualTranche", function () { newRedemptionTranche1 = tranches[0]; newRedemptionTranche2 = tranches[1]; - await pricingStrategy.setTranchePrice(newRedemptionTranche1.address, toPriceFixedPtAmt("1")); - await discountStrategy.setTrancheDiscount(newRedemptionTranche1.address, toDiscountFixedPtAmt("1")); - - await pricingStrategy.setTranchePrice(newRedemptionTranche2.address, toPriceFixedPtAmt("1")); - await discountStrategy.setTrancheDiscount(newRedemptionTranche2.address, toDiscountFixedPtAmt("1")); + await pricingStrategy.computeTranchePrice + .whenCalledWith(newRedemptionTranche1.address) + .returns(toPriceFixedPtAmt("1")); + await discountStrategy.computeTrancheDiscount + .whenCalledWith(newRedemptionTranche1.address) + .returns(toDiscountFixedPtAmt("1")); + + await pricingStrategy.computeTranchePrice + .whenCalledWith(newRedemptionTranche2.address) + .returns(toPriceFixedPtAmt("1")); + await discountStrategy.computeTrancheDiscount + .whenCalledWith(newRedemptionTranche2.address) + .returns(toDiscountFixedPtAmt("1")); await newRedemptionTranche1.approve(perp.address, toFixedPtAmt("500")); await perp.deposit(newRedemptionTranche1.address, toFixedPtAmt("500")); diff --git a/spot-contracts/test/PerpetualTranche_rollover.ts b/spot-contracts/test/PerpetualTranche_rollover.ts index a3b90929..131be43d 100644 --- a/spot-contracts/test/PerpetualTranche_rollover.ts +++ b/spot-contracts/test/PerpetualTranche_rollover.ts @@ -1,7 +1,7 @@ -import { expect } from "chai"; +import { expect, use } from "chai"; import { network, ethers, upgrades } from "hardhat"; import { Contract, Transaction, Signer, constants } from "ethers"; - +import { smock } from "@defi-wonderland/smock"; import { setupCollateralToken, setupBondFactory, @@ -17,6 +17,7 @@ import { checkReserveComposition, rebase, } from "./helpers"; +use(smock.matchers); let perp: Contract, bondFactory: Contract, @@ -52,14 +53,24 @@ describe("PerpetualTranche", function () { issuer = await BondIssuer.deploy(bondFactory.address, collateralToken.address); await issuer.init(10800, [500, 500], 1200, 0); - const FeeStrategy = await ethers.getContractFactory("MockFeeStrategy"); - feeStrategy = await FeeStrategy.deploy(); - - const PricingStrategy = await ethers.getContractFactory("MockPricingStrategy"); - pricingStrategy = await PricingStrategy.deploy(); - - const DiscountStrategy = await ethers.getContractFactory("MockDiscountStrategy"); - discountStrategy = await DiscountStrategy.deploy(); + const FeeStrategy = await ethers.getContractFactory("BasicFeeStrategy"); + feeStrategy = await smock.fake(FeeStrategy); + await feeStrategy.computeMintFees.returns(["0", "0"]); + await feeStrategy.computeBurnFees.returns(["0", "0"]); + await feeStrategy.computeRolloverFees.returns(["0", "0"]); + + const PricingStrategy = await ethers.getContractFactory("UnitPricingStrategy"); + pricingStrategy = await smock.fake(PricingStrategy); + await pricingStrategy.decimals.returns(8); + await pricingStrategy.computeMatureTranchePrice.returns(toPriceFixedPtAmt("1")); + await pricingStrategy.computeTranchePrice.returns(toPriceFixedPtAmt("1")); + + const DiscountStrategy = await ethers.getContractFactory("TrancheClassDiscountStrategy"); + discountStrategy = await smock.fake(DiscountStrategy); + await discountStrategy.decimals.returns(18); + await discountStrategy.computeTrancheDiscount + .whenCalledWith(collateralToken.address) + .returns(toDiscountFixedPtAmt("1")); const PerpetualTranche = await ethers.getContractFactory("PerpetualTranche"); perp = await upgrades.deployProxy( @@ -78,10 +89,7 @@ describe("PerpetualTranche", function () { }, ); - await feeStrategy.setFeeToken(perp.address); - await feeStrategy.setMintFee(toFixedPtAmt("0")); - await feeStrategy.setBurnFee(toFixedPtAmt("0")); - await feeStrategy.setRolloverFee(toFixedPtAmt("0")); + await feeStrategy.feeToken.returns(perp.address); await perp.updateTolerableTrancheMaturity(1200, 10800); await advancePerpQueue(perp, 10900); @@ -91,8 +99,12 @@ describe("PerpetualTranche", function () { await depositIntoBond(holdingPenBond, toFixedPtAmt("2000"), deployer); - await pricingStrategy.setTranchePrice(holdingPenTranche1.address, toPriceFixedPtAmt("1")); - await discountStrategy.setTrancheDiscount(holdingPenTranche1.address, toDiscountFixedPtAmt("1")); + await pricingStrategy.computeTranchePrice + .whenCalledWith(holdingPenTranche1.address) + .returns(toPriceFixedPtAmt("1")); + await discountStrategy.computeTrancheDiscount + .whenCalledWith(holdingPenTranche1.address) + .returns(toDiscountFixedPtAmt("1")); await holdingPenTranche1.approve(perp.address, toFixedPtAmt("500")); await perp.deposit(holdingPenTranche1.address, toFixedPtAmt("500")); @@ -103,13 +115,17 @@ describe("PerpetualTranche", function () { await depositIntoBond(reserveBond, toFixedPtAmt("2000"), deployer); - await pricingStrategy.setTranchePrice(reserveTranche1.address, toPriceFixedPtAmt("1")); - await discountStrategy.setTrancheDiscount(reserveTranche1.address, toDiscountFixedPtAmt("1")); + await pricingStrategy.computeTranchePrice.whenCalledWith(reserveTranche1.address).returns(toPriceFixedPtAmt("1")); + await discountStrategy.computeTrancheDiscount + .whenCalledWith(reserveTranche1.address) + .returns(toDiscountFixedPtAmt("1")); await reserveTranche1.approve(perp.address, toFixedPtAmt("500")); await perp.deposit(reserveTranche1.address, toFixedPtAmt("500")); - await pricingStrategy.setTranchePrice(reserveTranche2.address, toPriceFixedPtAmt("1")); - await discountStrategy.setTrancheDiscount(reserveTranche2.address, toDiscountFixedPtAmt("0.5")); + await pricingStrategy.computeTranchePrice.whenCalledWith(reserveTranche2.address).returns(toPriceFixedPtAmt("1")); + await discountStrategy.computeTrancheDiscount + .whenCalledWith(reserveTranche2.address) + .returns(toDiscountFixedPtAmt("0.5")); await reserveTranche2.approve(perp.address, toFixedPtAmt("1000")); await perp.deposit(reserveTranche2.address, toFixedPtAmt("1000")); @@ -118,8 +134,10 @@ describe("PerpetualTranche", function () { rolloverInBond = await bondAt(await perp.callStatic.getDepositBond()); [rolloverInTranche] = await getTranches(rolloverInBond); - await pricingStrategy.setTranchePrice(rolloverInTranche.address, toPriceFixedPtAmt("1")); - await discountStrategy.setTrancheDiscount(rolloverInTranche.address, toDiscountFixedPtAmt("1")); + await pricingStrategy.computeTranchePrice.whenCalledWith(rolloverInTranche.address).returns(toPriceFixedPtAmt("1")); + await discountStrategy.computeTrancheDiscount + .whenCalledWith(rolloverInTranche.address) + .returns(toDiscountFixedPtAmt("1")); await depositIntoBond(rolloverInBond, toFixedPtAmt("5000"), deployer); await rolloverInTranche.approve(perp.address, toFixedPtAmt("5000")); @@ -158,7 +176,7 @@ describe("PerpetualTranche", function () { it("should revert when invoked from unauthorized roller ", async function () { await expect( perp.rollover(rolloverInTranche.address, reserveTranche2.address, toFixedPtAmt("500")), - ).to.revertedWith("UnauthorizedCall"); + ).to.revertedWithCustomError(perp, "UnauthorizedCall"); }); it("should NOT revert when invoked from authorized roller ", async function () { @@ -174,12 +192,14 @@ describe("PerpetualTranche", function () { let tranches: Contract[]; beforeEach(async function () { tranches = await getTranches(rolloverInBond); - await discountStrategy.setTrancheDiscount(tranches[1].address, toDiscountFixedPtAmt("1")); + await discountStrategy.computeTrancheDiscount + .whenCalledWith(tranches[1].address) + .returns(toDiscountFixedPtAmt("1")); }); it("should revert", async function () { await expect( perp.rollover(rolloverInTranche.address, tranches[1].address, toFixedPtAmt("500")), - ).to.revertedWith("UnacceptableRollover("); + ).to.revertedWithCustomError(perp, "UnacceptableRollover"); }); }); @@ -187,16 +207,16 @@ describe("PerpetualTranche", function () { it("should revert", async function () { await expect( perp.rollover(reserveTranche1.address, collateralToken.address, toFixedPtAmt("500")), - ).to.revertedWith("UnacceptableRollover("); + ).to.revertedWithCustomError(perp, "UnacceptableRollover"); await expect( perp.rollover(reserveTranche1.address, reserveTranche1.address, toFixedPtAmt("500")), - ).to.revertedWith("UnacceptableRollover("); + ).to.revertedWithCustomError(perp, "UnacceptableRollover"); await expect( perp.rollover(reserveTranche1.address, reserveTranche2.address, toFixedPtAmt("500")), - ).to.revertedWith("UnacceptableRollover("); + ).to.revertedWithCustomError(perp, "UnacceptableRollover"); await expect( perp.rollover(reserveTranche1.address, holdingPenTranche1.address, toFixedPtAmt("500")), - ).to.revertedWith("UnacceptableRollover("); + ).to.revertedWithCustomError(perp, "UnacceptableRollover"); }); }); @@ -209,7 +229,7 @@ describe("PerpetualTranche", function () { it("should revert", async function () { await expect( perp.rollover(rolloverInTranche.address, maliciousTranche.address, toFixedPtAmt("500")), - ).to.revertedWith("UnacceptableRollover("); + ).to.revertedWithCustomError(perp, "UnacceptableRollover"); }); }); @@ -223,7 +243,7 @@ describe("PerpetualTranche", function () { it("should revert", async function () { await expect( perp.rollover(newRotationInTranche.address, rolloverInTranche.address, toFixedPtAmt("500")), - ).to.revertedWith("UnacceptableRollover("); + ).to.revertedWithCustomError(perp, "UnacceptableRollover"); }); }); @@ -240,7 +260,7 @@ describe("PerpetualTranche", function () { it("should revert", async function () { await expect( perp.rollover(maliciousTranche.address, reserveTranche1.address, toFixedPtAmt("500")), - ).to.revertedWith("UnacceptableRollover("); + ).to.revertedWithCustomError(perp, "UnacceptableRollover"); }); }); @@ -274,7 +294,7 @@ describe("PerpetualTranche", function () { it("should revert", async function () { await expect( perp.rollover(rolloverInTranche.address, reserveTranche1.address, toFixedPtAmt("0")), - ).to.revertedWith("UnacceptableRolloverAmt"); + ).to.revertedWithCustomError(perp, "UnacceptableRolloverAmt"); }); }); @@ -288,20 +308,20 @@ describe("PerpetualTranche", function () { it("should be reverted", async function () { await expect( perp.rollover(newRotationInTranche.address, reserveTranche1.address, toFixedPtAmt("0")), - ).to.revertedWith("UnacceptableRolloverAmt"); + ).to.revertedWithCustomError(perp, "UnacceptableRolloverAmt"); }); }); describe("when the supply cap is exceeded", function () { beforeEach(async function () { - await feeStrategy.setRolloverFee(toFixedPtAmt("-100")); + await feeStrategy.computeRolloverFees.returns([toFixedPtAmt("-100"), "0"]); await perp.updateMintingLimits(toFixedPtAmt("100"), toFixedPtAmt("1")); }); it("should revert", async function () { await expect( perp.rollover(rolloverInTranche.address, reserveTranche1.address, toFixedPtAmt("100")), - ).to.revertedWith("ExceededMaxSupply"); + ).to.revertedWithCustomError(perp, "ExceededMaxSupply"); }); }); @@ -310,7 +330,9 @@ describe("PerpetualTranche", function () { beforeEach(async function () { const tranches = await getTranches(rolloverInBond); newRotationInTranche = tranches[1]; - await discountStrategy.setTrancheDiscount(newRotationInTranche.address, toDiscountFixedPtAmt("0.5")); + await discountStrategy.computeTrancheDiscount + .whenCalledWith(newRotationInTranche.address) + .returns(toDiscountFixedPtAmt("0.5")); }); it("should rollover the correct amount", async function () { @@ -346,8 +368,12 @@ describe("PerpetualTranche", function () { describe("when trancheIn price is zero", function () { beforeEach(async function () { - await pricingStrategy.setTranchePrice(rolloverInTranche.address, toPriceFixedPtAmt("0")); - await pricingStrategy.setTranchePrice(reserveTranche1.address, toPriceFixedPtAmt("1")); + await pricingStrategy.computeTranchePrice + .whenCalledWith(rolloverInTranche.address) + .returns(toPriceFixedPtAmt("0")); + await pricingStrategy.computeTranchePrice + .whenCalledWith(reserveTranche1.address) + .returns(toPriceFixedPtAmt("1")); }); it("should rollover the correct amount", async function () { @@ -367,8 +393,12 @@ describe("PerpetualTranche", function () { describe("when tokenOut price is zero", function () { beforeEach(async function () { - await pricingStrategy.setTranchePrice(rolloverInTranche.address, toPriceFixedPtAmt("1")); - await pricingStrategy.setTranchePrice(reserveTranche1.address, toPriceFixedPtAmt("0")); + await pricingStrategy.computeTranchePrice + .whenCalledWith(rolloverInTranche.address) + .returns(toPriceFixedPtAmt("1")); + await pricingStrategy.computeTranchePrice + .whenCalledWith(reserveTranche1.address) + .returns(toPriceFixedPtAmt("0")); }); it("should rollover the correct amount", async function () { @@ -388,8 +418,12 @@ describe("PerpetualTranche", function () { describe("when trancheIn price is 0.5", function () { beforeEach(async function () { - await pricingStrategy.setTranchePrice(rolloverInTranche.address, toPriceFixedPtAmt("0.5")); - await pricingStrategy.setTranchePrice(reserveTranche1.address, toPriceFixedPtAmt("1")); + await pricingStrategy.computeTranchePrice + .whenCalledWith(rolloverInTranche.address) + .returns(toPriceFixedPtAmt("0.5")); + await pricingStrategy.computeTranchePrice + .whenCalledWith(reserveTranche1.address) + .returns(toPriceFixedPtAmt("1")); }); it("should rollover the correct amount", async function () { @@ -410,8 +444,12 @@ describe("PerpetualTranche", function () { describe("tokenOut price is 0.5", function () { beforeEach(async function () { - await pricingStrategy.setTranchePrice(rolloverInTranche.address, toPriceFixedPtAmt("1")); - await pricingStrategy.setTranchePrice(reserveTranche1.address, toPriceFixedPtAmt("0.5")); + await pricingStrategy.computeTranchePrice + .whenCalledWith(rolloverInTranche.address) + .returns(toPriceFixedPtAmt("1")); + await pricingStrategy.computeTranchePrice + .whenCalledWith(reserveTranche1.address) + .returns(toPriceFixedPtAmt("0.5")); }); it("should rollover the correct amount", async function () { @@ -432,8 +470,10 @@ describe("PerpetualTranche", function () { describe("tokenOut is collateral which rebased up", function () { beforeEach(async function () { - await pricingStrategy.setTranchePrice(rolloverInTranche.address, toPriceFixedPtAmt("1")); - await pricingStrategy.setTranchePrice(collateralToken.address, toPriceFixedPtAmt("2")); + await pricingStrategy.computeTranchePrice + .whenCalledWith(rolloverInTranche.address) + .returns(toPriceFixedPtAmt("1")); + await pricingStrategy.computeMatureTranchePrice.returns(toPriceFixedPtAmt("2")); await rebase(collateralToken, rebaseOracle, +1); }); @@ -455,8 +495,10 @@ describe("PerpetualTranche", function () { describe("tokenOut is collateral which rebased down", function () { beforeEach(async function () { - await pricingStrategy.setTranchePrice(rolloverInTranche.address, toPriceFixedPtAmt("1")); - await pricingStrategy.setTranchePrice(collateralToken.address, toPriceFixedPtAmt("0.5")); + await pricingStrategy.computeTranchePrice + .whenCalledWith(rolloverInTranche.address) + .returns(toPriceFixedPtAmt("1")); + await pricingStrategy.computeMatureTranchePrice.returns(toPriceFixedPtAmt("0.5")); await rebase(collateralToken, rebaseOracle, -0.5); }); @@ -481,9 +523,13 @@ describe("PerpetualTranche", function () { beforeEach(async function () { const rolloverInTranches = await getTranches(rolloverInBond); rolloverInTranche2 = rolloverInTranches[1]; - await pricingStrategy.setTranchePrice(rolloverInTranche2.address, toPriceFixedPtAmt("1")); - await discountStrategy.setTrancheDiscount(rolloverInTranche2.address, toDiscountFixedPtAmt("0.5")); - await pricingStrategy.setTranchePrice(collateralToken.address, toPriceFixedPtAmt("2")); + await pricingStrategy.computeTranchePrice + .whenCalledWith(rolloverInTranche2.address) + .returns(toPriceFixedPtAmt("1")); + await discountStrategy.computeTrancheDiscount + .whenCalledWith(rolloverInTranche2.address) + .returns(toDiscountFixedPtAmt("0.5")); + await pricingStrategy.computeMatureTranchePrice.returns(toPriceFixedPtAmt("2")); await rebase(collateralToken, rebaseOracle, +1); }); @@ -508,9 +554,13 @@ describe("PerpetualTranche", function () { beforeEach(async function () { const rolloverInTranches = await getTranches(rolloverInBond); rolloverInTranche2 = rolloverInTranches[1]; - await pricingStrategy.setTranchePrice(rolloverInTranche2.address, toPriceFixedPtAmt("1")); - await discountStrategy.setTrancheDiscount(rolloverInTranche2.address, toDiscountFixedPtAmt("0.5")); - await pricingStrategy.setTranchePrice(collateralToken.address, toPriceFixedPtAmt("0.5")); + await pricingStrategy.computeTranchePrice + .whenCalledWith(rolloverInTranche2.address) + .returns(toPriceFixedPtAmt("1")); + await discountStrategy.computeTrancheDiscount + .whenCalledWith(rolloverInTranche2.address) + .returns(toDiscountFixedPtAmt("0.5")); + await pricingStrategy.computeMatureTranchePrice.returns(toPriceFixedPtAmt("0.5")); await rebase(collateralToken, rebaseOracle, -0.5); }); @@ -605,8 +655,12 @@ describe("PerpetualTranche", function () { beforeEach(async function () { const tranches = await getTranches(rolloverInBond); newRotationInTranche = tranches[1]; - await pricingStrategy.setTranchePrice(newRotationInTranche.address, toPriceFixedPtAmt("1")); - await discountStrategy.setTrancheDiscount(newRotationInTranche.address, toDiscountFixedPtAmt("0.5")); + await pricingStrategy.computeTranchePrice + .whenCalledWith(newRotationInTranche.address) + .returns(toPriceFixedPtAmt("1")); + await discountStrategy.computeTrancheDiscount + .whenCalledWith(newRotationInTranche.address) + .returns(toDiscountFixedPtAmt("0.5")); await rebase(collateralToken, rebaseOracle, -0.25); }); it("should rollover the correct amount", async function () { @@ -629,8 +683,12 @@ describe("PerpetualTranche", function () { beforeEach(async function () { const tranches = await getTranches(rolloverInBond); newRotationInTranche = tranches[1]; - await pricingStrategy.setTranchePrice(newRotationInTranche.address, toPriceFixedPtAmt("1")); - await discountStrategy.setTrancheDiscount(newRotationInTranche.address, toDiscountFixedPtAmt("0.5")); + await pricingStrategy.computeTranchePrice + .whenCalledWith(newRotationInTranche.address) + .returns(toPriceFixedPtAmt("1")); + await discountStrategy.computeTrancheDiscount + .whenCalledWith(newRotationInTranche.address) + .returns(toDiscountFixedPtAmt("0.5")); await rebase(collateralToken, rebaseOracle, 0.25); }); it("should rollover the correct amount", async function () { @@ -653,8 +711,12 @@ describe("PerpetualTranche", function () { beforeEach(async function () { const tranches = await getTranches(rolloverInBond); newRotationInTranche = tranches[1]; - await pricingStrategy.setTranchePrice(newRotationInTranche.address, toPriceFixedPtAmt("1")); - await discountStrategy.setTrancheDiscount(newRotationInTranche.address, toDiscountFixedPtAmt("0.5")); + await pricingStrategy.computeTranchePrice + .whenCalledWith(newRotationInTranche.address) + .returns(toPriceFixedPtAmt("1")); + await discountStrategy.computeTrancheDiscount + .whenCalledWith(newRotationInTranche.address) + .returns(toDiscountFixedPtAmt("0.5")); await rebase(collateralToken, rebaseOracle, -0.25); }); it("should rollover the correct amount", async function () { @@ -677,8 +739,12 @@ describe("PerpetualTranche", function () { beforeEach(async function () { const tranches = await getTranches(rolloverInBond); newRotationInTranche = tranches[1]; - await pricingStrategy.setTranchePrice(newRotationInTranche.address, toPriceFixedPtAmt("1")); - await discountStrategy.setTrancheDiscount(newRotationInTranche.address, toDiscountFixedPtAmt("0.5")); + await pricingStrategy.computeTranchePrice + .whenCalledWith(newRotationInTranche.address) + .returns(toPriceFixedPtAmt("1")); + await discountStrategy.computeTrancheDiscount + .whenCalledWith(newRotationInTranche.address) + .returns(toDiscountFixedPtAmt("0.5")); await rebase(collateralToken, rebaseOracle, 0.25); }); it("should rollover the correct amount", async function () { @@ -730,7 +796,7 @@ describe("PerpetualTranche", function () { describe("when fee > 0", function () { beforeEach(async function () { - await feeStrategy.setRolloverFee(toFixedPtAmt("1")); + await feeStrategy.computeRolloverFees.returns([toFixedPtAmt("1"), "0"]); }); it("should transfer the tranches in", async function () { await expect(() => @@ -765,7 +831,7 @@ describe("PerpetualTranche", function () { describe("when fee < 0", function () { beforeEach(async function () { await perp.transfer(perp.address, toFixedPtAmt("1")); - await feeStrategy.setRolloverFee(toFixedPtAmt("-1")); + await feeStrategy.computeRolloverFees.returns([toFixedPtAmt("-1"), "0"]); }); it("should transfer the tranches in", async function () { await expect(() => @@ -800,7 +866,7 @@ describe("PerpetualTranche", function () { describe("when fee < 0 and abs(fee) < balance", function () { beforeEach(async function () { await perp.transfer(perp.address, toFixedPtAmt("0.5")); - await feeStrategy.setRolloverFee(toFixedPtAmt("-1")); + await feeStrategy.computeRolloverFees.returns([toFixedPtAmt("-1"), "0"]); }); it("should transfer the tranches in", async function () { await expect(() => @@ -840,8 +906,7 @@ describe("PerpetualTranche", function () { describe("when protocol fee > 0", function () { beforeEach(async function () { await perp.transferOwnership(await otherUser.getAddress()); - await feeStrategy.setRolloverFee(toFixedPtAmt("1")); - await feeStrategy.setProtocolFee(toFixedPtAmt("0.5")); + await feeStrategy.computeRolloverFees.returns([toFixedPtAmt("1"), toFixedPtAmt("0.5")]); }); it("should transfer the tranches in", async function () { await expect(() => @@ -884,7 +949,7 @@ describe("PerpetualTranche", function () { const ERC20 = await ethers.getContractFactory("MockERC20"); feeToken = await ERC20.deploy(); await feeToken.init("Mock token", "MOCK"); - await feeStrategy.setFeeToken(feeToken.address); + await feeStrategy.feeToken.returns(feeToken.address); }); describe("when fee is zero", async function () { @@ -920,7 +985,7 @@ describe("PerpetualTranche", function () { describe("when fee > 0", async function () { beforeEach(async function () { - await feeStrategy.setRolloverFee(toFixedPtAmt("1")); + await feeStrategy.computeRolloverFees.returns([toFixedPtAmt("1"), "0"]); }); describe("with no approval", function () { @@ -945,7 +1010,7 @@ describe("PerpetualTranche", function () { describe("with sufficient fee", async function () { beforeEach(async function () { - await feeStrategy.setRolloverFee(toFixedPtAmt("1")); + await feeStrategy.computeRolloverFees.returns([toFixedPtAmt("1"), "0"]); await feeToken.mint(deployerAddress, toFixedPtAmt("1")); await feeToken.approve(perp.address, toFixedPtAmt("1")); }); @@ -983,7 +1048,7 @@ describe("PerpetualTranche", function () { describe("when fee < 0", async function () { beforeEach(async function () { - await feeStrategy.setRolloverFee(toFixedPtAmt("-1")); + await feeStrategy.computeRolloverFees.returns([toFixedPtAmt("-1"), "0"]); }); describe("with insufficient balance", function () { it("should revert", async function () { @@ -1032,8 +1097,7 @@ describe("PerpetualTranche", function () { describe("when protocol fee > 0", async function () { beforeEach(async function () { await perp.transferOwnership(await otherUser.getAddress()); - await feeStrategy.setRolloverFee(toFixedPtAmt("1")); - await feeStrategy.setProtocolFee(toFixedPtAmt("0.5")); + await feeStrategy.computeRolloverFees.returns([toFixedPtAmt("1"), toFixedPtAmt("0.5")]); await feeToken.mint(deployerAddress, toFixedPtAmt("1.5")); await feeToken.approve(perp.address, toFixedPtAmt("1.5")); }); @@ -1078,8 +1142,12 @@ describe("PerpetualTranche", function () { beforeEach(async function () { const tranches = await getTranches(rolloverInBond); newRotationInTranche = tranches[1]; - await pricingStrategy.setTranchePrice(newRotationInTranche.address, toPriceFixedPtAmt("1")); - await discountStrategy.setTrancheDiscount(newRotationInTranche.address, toDiscountFixedPtAmt("1")); + await pricingStrategy.computeTranchePrice + .whenCalledWith(newRotationInTranche.address) + .returns(toPriceFixedPtAmt("1")); + await discountStrategy.computeTrancheDiscount + .whenCalledWith(newRotationInTranche.address) + .returns(toDiscountFixedPtAmt("1")); expect(await perp.callStatic.getMatureTrancheBalance()).to.eq(toFixedPtAmt("500")); @@ -1450,7 +1518,8 @@ describe("PerpetualTranche", function () { let tx: Transaction, r: any; beforeEach(async function () { await checkReserveComposition(perp, [collateralToken, reserveTranche2, reserveTranche1, rolloverInTranche]); - await pricingStrategy.setTranchePrice(collateralToken.address, toPriceFixedPtAmt("2")); + await pricingStrategy.computeMatureTranchePrice.returns(toPriceFixedPtAmt("2")); + expect(await perp.callStatic.getMatureTrancheBalance()).to.eq(toFixedPtAmt("500")); r = await perp.callStatic.computeRolloverAmt( rolloverInTranche.address, @@ -1503,7 +1572,7 @@ describe("PerpetualTranche", function () { it("should NOT revert", async function () { await expect( perp.rollover(rolloverInTranche.address, collateralToken.address, toFixedPtAmt("100")), - ).not.to.be.revertedWith("BelowMatureValueTargetPerc"); + ).not.to.be.revertedWithCustomError(perp, "BelowMatureValueTargetPerc"); }); }); @@ -1511,7 +1580,7 @@ describe("PerpetualTranche", function () { it("should revert", async function () { await expect( perp.rollover(rolloverInTranche.address, collateralToken.address, toFixedPtAmt("100.000001")), - ).to.be.revertedWith("BelowMatureValueTargetPerc"); + ).to.be.revertedWithCustomError(perp, "BelowMatureValueTargetPerc"); }); }); }); diff --git a/spot-contracts/test/RouterV1.ts b/spot-contracts/test/RouterV1.ts index 66c48fb3..32d2a805 100644 --- a/spot-contracts/test/RouterV1.ts +++ b/spot-contracts/test/RouterV1.ts @@ -1,7 +1,7 @@ -import { expect } from "chai"; +import { expect, use } from "chai"; import { network, ethers, upgrades } from "hardhat"; import { constants, Contract, Signer } from "ethers"; - +import { smock } from "@defi-wonderland/smock"; import { setupCollateralToken, setupBondFactory, @@ -15,6 +15,7 @@ import { mintCollteralToken, advancePerpQueueToRollover, } from "./helpers"; +use(smock.matchers); let perp: Contract, bondFactory: Contract, @@ -44,14 +45,24 @@ describe("RouterV1", function () { issuer = await BondIssuer.deploy(bondFactory.address, collateralToken.address); await issuer.init(3600, [200, 300, 500], 1200, 0); - const FeeStrategy = await ethers.getContractFactory("MockFeeStrategy"); - feeStrategy = await FeeStrategy.deploy(); - - const PricingStrategy = await ethers.getContractFactory("MockPricingStrategy"); - pricingStrategy = await PricingStrategy.deploy(); - - const DiscountStrategy = await ethers.getContractFactory("MockDiscountStrategy"); - discountStrategy = await DiscountStrategy.deploy(); + const FeeStrategy = await ethers.getContractFactory("BasicFeeStrategy"); + feeStrategy = await smock.fake(FeeStrategy); + await feeStrategy.computeMintFees.returns(["0", "0"]); + await feeStrategy.computeBurnFees.returns(["0", "0"]); + await feeStrategy.computeRolloverFees.returns(["0", "0"]); + + const PricingStrategy = await ethers.getContractFactory("UnitPricingStrategy"); + pricingStrategy = await smock.fake(PricingStrategy); + await pricingStrategy.decimals.returns(8); + await pricingStrategy.computeMatureTranchePrice.returns(toPriceFixedPtAmt("1")); + await pricingStrategy.computeTranchePrice.returns(toPriceFixedPtAmt("1")); + + const DiscountStrategy = await ethers.getContractFactory("TrancheClassDiscountStrategy"); + discountStrategy = await smock.fake(DiscountStrategy); + await discountStrategy.decimals.returns(18); + await discountStrategy.computeTrancheDiscount + .whenCalledWith(collateralToken.address) + .returns(toDiscountFixedPtAmt("1")); const PerpetualTranche = await ethers.getContractFactory("PerpetualTranche"); perp = await upgrades.deployProxy( @@ -75,15 +86,23 @@ describe("RouterV1", function () { depositBond = await bondAt(await perp.callStatic.getDepositBond()); depositTranches = await getTranches(depositBond); - await pricingStrategy.setTranchePrice(depositTranches[0].address, toPriceFixedPtAmt("1")); - await discountStrategy.setTrancheDiscount(depositTranches[0].address, toDiscountFixedPtAmt("1")); + await pricingStrategy.computeTranchePrice + .whenCalledWith(depositTranches[0].address) + .returns(toPriceFixedPtAmt("1")); + await discountStrategy.computeTrancheDiscount + .whenCalledWith(depositTranches[0].address) + .returns(toDiscountFixedPtAmt("1")); - await pricingStrategy.setTranchePrice(depositTranches[1].address, toPriceFixedPtAmt("1")); - await discountStrategy.setTrancheDiscount(depositTranches[1].address, toDiscountFixedPtAmt("0.75")); + await pricingStrategy.computeTranchePrice + .whenCalledWith(depositTranches[1].address) + .returns(toPriceFixedPtAmt("1")); + await discountStrategy.computeTrancheDiscount + .whenCalledWith(depositTranches[1].address) + .returns(toDiscountFixedPtAmt("0.75")); - await feeStrategy.setFeeToken(perp.address); - await feeStrategy.setMintFee(toFixedPtAmt("0")); - await feeStrategy.setBurnFee(toFixedPtAmt("0")); + await feeStrategy.feeToken.returns(perp.address); + await feeStrategy.computeMintFees.returns([toFixedPtAmt("0"), "0"]); + await feeStrategy.computeBurnFees.returns([toFixedPtAmt("0"), "0"]); const Router = await ethers.getContractFactory("RouterV1"); router = await Router.deploy(); @@ -108,7 +127,7 @@ describe("RouterV1", function () { describe("#previewDeposit", function () { beforeEach(async function () { - await feeStrategy.setMintFee(toFixedPtAmt("10")); + await feeStrategy.computeMintFees.returns([toFixedPtAmt("10"), "0"]); }); describe("when fee token is the native token", async function () { @@ -126,7 +145,7 @@ describe("RouterV1", function () { const ERC20 = await ethers.getContractFactory("MockERC20"); feeToken = await ERC20.deploy(); await feeToken.init("Mock token", "MOCK"); - await feeStrategy.setFeeToken(feeToken.address); + await feeStrategy.feeToken.returns(feeToken.address); }); it("should compute the mint amount and fee", async function () { @@ -141,16 +160,24 @@ describe("RouterV1", function () { describe("#previewRedeem", function () { let depositTranches1: Contract[], depositTranches2: Contract[], depositTranches3: Contract[]; beforeEach(async function () { - await feeStrategy.setBurnFee(toFixedPtAmt("12.75")); + await feeStrategy.computeBurnFees.returns([toFixedPtAmt("12.75"), "0"]); const depositBond1 = await bondAt(await perp.callStatic.getDepositBond()); depositTranches1 = await getTranches(depositBond1); await depositIntoBond(depositBond1, toFixedPtAmt("1000"), deployer); - await pricingStrategy.setTranchePrice(depositTranches1[0].address, toPriceFixedPtAmt("1")); - await discountStrategy.setTrancheDiscount(depositTranches1[0].address, toDiscountFixedPtAmt("1")); - await pricingStrategy.setTranchePrice(depositTranches1[1].address, toPriceFixedPtAmt("1")); - await discountStrategy.setTrancheDiscount(depositTranches1[1].address, toDiscountFixedPtAmt("0.75")); + await pricingStrategy.computeTranchePrice + .whenCalledWith(depositTranches1[0].address) + .returns(toPriceFixedPtAmt("1")); + await discountStrategy.computeTrancheDiscount + .whenCalledWith(depositTranches1[0].address) + .returns(toDiscountFixedPtAmt("1")); + await pricingStrategy.computeTranchePrice + .whenCalledWith(depositTranches1[1].address) + .returns(toPriceFixedPtAmt("1")); + await discountStrategy.computeTrancheDiscount + .whenCalledWith(depositTranches1[1].address) + .returns(toDiscountFixedPtAmt("0.75")); await depositTranches1[0].approve(perp.address, toFixedPtAmt("200")); await perp.deposit(depositTranches1[0].address, toFixedPtAmt("200")); @@ -163,10 +190,18 @@ describe("RouterV1", function () { depositTranches2 = await getTranches(depositBond2); await depositIntoBond(depositBond2, toFixedPtAmt("1000"), deployer); - await pricingStrategy.setTranchePrice(depositTranches2[0].address, toPriceFixedPtAmt("1")); - await discountStrategy.setTrancheDiscount(depositTranches2[0].address, toDiscountFixedPtAmt("1")); - await pricingStrategy.setTranchePrice(depositTranches2[1].address, toPriceFixedPtAmt("1")); - await discountStrategy.setTrancheDiscount(depositTranches2[1].address, toDiscountFixedPtAmt("0.75")); + await pricingStrategy.computeTranchePrice + .whenCalledWith(depositTranches2[0].address) + .returns(toPriceFixedPtAmt("1")); + await discountStrategy.computeTrancheDiscount + .whenCalledWith(depositTranches2[0].address) + .returns(toDiscountFixedPtAmt("1")); + await pricingStrategy.computeTranchePrice + .whenCalledWith(depositTranches2[1].address) + .returns(toPriceFixedPtAmt("1")); + await discountStrategy.computeTrancheDiscount + .whenCalledWith(depositTranches2[1].address) + .returns(toDiscountFixedPtAmt("0.75")); await depositTranches2[0].approve(perp.address, toFixedPtAmt("200")); await perp.deposit(depositTranches2[0].address, toFixedPtAmt("200")); @@ -179,10 +214,18 @@ describe("RouterV1", function () { depositTranches3 = await getTranches(depositBond3); await depositIntoBond(depositBond3, toFixedPtAmt("1000"), deployer); - await pricingStrategy.setTranchePrice(depositTranches3[0].address, toPriceFixedPtAmt("1")); - await discountStrategy.setTrancheDiscount(depositTranches3[0].address, toDiscountFixedPtAmt("1")); - await pricingStrategy.setTranchePrice(depositTranches3[1].address, toPriceFixedPtAmt("1")); - await discountStrategy.setTrancheDiscount(depositTranches3[1].address, toDiscountFixedPtAmt("0.75")); + await pricingStrategy.computeTranchePrice + .whenCalledWith(depositTranches3[0].address) + .returns(toPriceFixedPtAmt("1")); + await discountStrategy.computeTrancheDiscount + .whenCalledWith(depositTranches3[0].address) + .returns(toDiscountFixedPtAmt("1")); + await pricingStrategy.computeTranchePrice + .whenCalledWith(depositTranches3[1].address) + .returns(toPriceFixedPtAmt("1")); + await discountStrategy.computeTrancheDiscount + .whenCalledWith(depositTranches3[1].address) + .returns(toDiscountFixedPtAmt("0.75")); await depositTranches3[0].approve(perp.address, toFixedPtAmt("200")); await perp.deposit(depositTranches3[0].address, toFixedPtAmt("200")); @@ -246,7 +289,7 @@ describe("RouterV1", function () { const ERC20 = await ethers.getContractFactory("MockERC20"); feeToken = await ERC20.deploy(); await feeToken.init("Mock token", "MOCK"); - await feeStrategy.setFeeToken(feeToken.address); + await feeStrategy.feeToken.returns(feeToken.address); }); it("should compute the burn amount and fee", async function () { @@ -277,16 +320,24 @@ describe("RouterV1", function () { describe("#previewRollover", function () { let holdingPenTranches: Contract[], reserveTranches: Contract[], depositTranches: Contract[]; beforeEach(async function () { - await feeStrategy.setBurnFee(toFixedPtAmt("10")); + await feeStrategy.computeBurnFees.returns([toFixedPtAmt("10"), "0"]); const holdingPenBond = await bondAt(await perp.callStatic.getDepositBond()); holdingPenTranches = await getTranches(holdingPenBond); await depositIntoBond(holdingPenBond, toFixedPtAmt("1000"), deployer); - await pricingStrategy.setTranchePrice(holdingPenTranches[0].address, toPriceFixedPtAmt("1")); - await discountStrategy.setTrancheDiscount(holdingPenTranches[0].address, toDiscountFixedPtAmt("1")); - await pricingStrategy.setTranchePrice(holdingPenTranches[1].address, toPriceFixedPtAmt("1")); - await discountStrategy.setTrancheDiscount(holdingPenTranches[1].address, toDiscountFixedPtAmt("0.75")); + await pricingStrategy.computeTranchePrice + .whenCalledWith(holdingPenTranches[0].address) + .returns(toPriceFixedPtAmt("1")); + await discountStrategy.computeTrancheDiscount + .whenCalledWith(holdingPenTranches[0].address) + .returns(toDiscountFixedPtAmt("1")); + await pricingStrategy.computeTranchePrice + .whenCalledWith(holdingPenTranches[1].address) + .returns(toPriceFixedPtAmt("1")); + await discountStrategy.computeTrancheDiscount + .whenCalledWith(holdingPenTranches[1].address) + .returns(toDiscountFixedPtAmt("0.75")); await holdingPenTranches[0].approve(perp.address, toFixedPtAmt("200")); await perp.deposit(holdingPenTranches[0].address, toFixedPtAmt("200")); @@ -299,10 +350,18 @@ describe("RouterV1", function () { reserveTranches = await getTranches(reserveBond); await depositIntoBond(reserveBond, toFixedPtAmt("1000"), deployer); - await pricingStrategy.setTranchePrice(reserveTranches[0].address, toPriceFixedPtAmt("1")); - await discountStrategy.setTrancheDiscount(reserveTranches[0].address, toDiscountFixedPtAmt("1")); - await pricingStrategy.setTranchePrice(reserveTranches[1].address, toPriceFixedPtAmt("1")); - await discountStrategy.setTrancheDiscount(reserveTranches[1].address, toDiscountFixedPtAmt("0.75")); + await pricingStrategy.computeTranchePrice + .whenCalledWith(reserveTranches[0].address) + .returns(toPriceFixedPtAmt("1")); + await discountStrategy.computeTrancheDiscount + .whenCalledWith(reserveTranches[0].address) + .returns(toDiscountFixedPtAmt("1")); + await pricingStrategy.computeTranchePrice + .whenCalledWith(reserveTranches[1].address) + .returns(toPriceFixedPtAmt("1")); + await discountStrategy.computeTrancheDiscount + .whenCalledWith(reserveTranches[1].address) + .returns(toDiscountFixedPtAmt("0.75")); await reserveTranches[0].approve(perp.address, toFixedPtAmt("200")); await perp.deposit(reserveTranches[0].address, toFixedPtAmt("200")); @@ -315,10 +374,18 @@ describe("RouterV1", function () { depositTranches = await getTranches(depositBond); await depositIntoBond(depositBond, toFixedPtAmt("1000"), deployer); - await pricingStrategy.setTranchePrice(depositTranches[0].address, toPriceFixedPtAmt("1")); - await discountStrategy.setTrancheDiscount(depositTranches[0].address, toDiscountFixedPtAmt("1")); - await pricingStrategy.setTranchePrice(depositTranches[1].address, toPriceFixedPtAmt("1")); - await discountStrategy.setTrancheDiscount(depositTranches[1].address, toDiscountFixedPtAmt("0.75")); + await pricingStrategy.computeTranchePrice + .whenCalledWith(depositTranches[0].address) + .returns(toPriceFixedPtAmt("1")); + await discountStrategy.computeTrancheDiscount + .whenCalledWith(depositTranches[0].address) + .returns(toDiscountFixedPtAmt("1")); + await pricingStrategy.computeTranchePrice + .whenCalledWith(depositTranches[1].address) + .returns(toPriceFixedPtAmt("1")); + await discountStrategy.computeTrancheDiscount + .whenCalledWith(depositTranches[1].address) + .returns(toDiscountFixedPtAmt("0.75")); await depositTranches[0].approve(perp.address, toFixedPtAmt("200")); await perp.deposit(depositTranches[0].address, toFixedPtAmt("200")); @@ -328,7 +395,7 @@ describe("RouterV1", function () { describe("when rollover is not acceptable", function () { beforeEach(async function () { - await await feeStrategy.setRolloverFee(toFixedPtAmt("1")); + await feeStrategy.computeRolloverFees.returns([toFixedPtAmt("1"), "0"]); }); it("should return 0", async function () { const r = await router.callStatic.previewRollover( @@ -349,7 +416,7 @@ describe("RouterV1", function () { describe("when tranche out balance is NOT covered", function () { beforeEach(async function () { - await await feeStrategy.setRolloverFee(toFixedPtAmt("1")); + await feeStrategy.computeRolloverFees.returns([toFixedPtAmt("1"), "0"]); }); it("should compute the rollover fees and amounts", async function () { const r = await router.callStatic.previewRollover( @@ -370,7 +437,7 @@ describe("RouterV1", function () { describe("when tranche in has different rate", function () { beforeEach(async function () { - await await feeStrategy.setRolloverFee(toFixedPtAmt("1")); + await feeStrategy.computeRolloverFees.returns([toFixedPtAmt("1"), "0"]); }); it("should compute the rollover fees and amounts", async function () { const r = await router.callStatic.previewRollover( @@ -391,7 +458,7 @@ describe("RouterV1", function () { describe("when tranche out has different rate", function () { beforeEach(async function () { - await await feeStrategy.setRolloverFee(toFixedPtAmt("1")); + await feeStrategy.computeRolloverFees.returns([toFixedPtAmt("1"), "0"]); }); it("should compute the rollover fees and amounts", async function () { const r = await router.callStatic.previewRollover( @@ -412,7 +479,7 @@ describe("RouterV1", function () { describe("when tranche out balance is covered exactly", function () { beforeEach(async function () { - await await feeStrategy.setRolloverFee(toFixedPtAmt("1")); + await feeStrategy.computeRolloverFees.returns([toFixedPtAmt("1"), "0"]); }); it("should compute the rollover fees and amounts", async function () { const r = await router.callStatic.previewRollover( @@ -433,7 +500,7 @@ describe("RouterV1", function () { describe("when tranche out used is less than the balance", function () { beforeEach(async function () { - await await feeStrategy.setRolloverFee(toFixedPtAmt("1")); + await feeStrategy.computeRolloverFees.returns([toFixedPtAmt("1"), "0"]); }); it("should compute the rollover fees and amounts", async function () { const r = await router.callStatic.previewRollover( @@ -454,7 +521,7 @@ describe("RouterV1", function () { describe("when tranche out balance is covered", function () { beforeEach(async function () { - await await feeStrategy.setRolloverFee(toFixedPtAmt("1")); + await feeStrategy.computeRolloverFees.returns([toFixedPtAmt("1"), "0"]); }); it("should compute the rollover fees and amounts", async function () { const r = await router.callStatic.previewRollover( @@ -475,7 +542,7 @@ describe("RouterV1", function () { describe("when collateral token is transferred out", function () { beforeEach(async function () { - await await feeStrategy.setRolloverFee(toFixedPtAmt("1")); + await feeStrategy.computeRolloverFees.returns([toFixedPtAmt("1"), "0"]); }); it("should compute the rollover fees and amounts", async function () { const r = await router.callStatic.previewRollover( @@ -496,7 +563,7 @@ describe("RouterV1", function () { describe("when fee is -ve", function () { beforeEach(async function () { - await await feeStrategy.setRolloverFee(toFixedPtAmt("-1")); + await feeStrategy.computeRolloverFees.returns([toFixedPtAmt("-1"), "0"]); }); it("should compute the rollover fees and amounts", async function () { const r = await router.callStatic.previewRollover( @@ -521,8 +588,8 @@ describe("RouterV1", function () { const ERC20 = await ethers.getContractFactory("MockERC20"); feeToken = await ERC20.deploy(); await feeToken.init("Mock token", "MOCK"); - await feeStrategy.setFeeToken(feeToken.address); - await await feeStrategy.setRolloverFee(toFixedPtAmt("1")); + await feeStrategy.feeToken.returns(feeToken.address); + await feeStrategy.computeRolloverFees.returns([toFixedPtAmt("1"), "0"]); }); it("should compute the rollover fees and amounts", async function () { const r = await router.callStatic.previewRollover( @@ -545,7 +612,7 @@ describe("RouterV1", function () { describe("#trancheAndDeposit", function () { beforeEach(async function () { await mintCollteralToken(collateralToken, toFixedPtAmt("2000"), deployer); - await feeStrategy.setMintFee(toFixedPtAmt("5")); + await feeStrategy.computeMintFees.returns([toFixedPtAmt("5"), "0"]); }); describe("when deposit bond is incorrect", function () { @@ -556,7 +623,7 @@ describe("RouterV1", function () { it("should revert", async function () { await expect( router.trancheAndDeposit(perp.address, depositBond.address, toFixedPtAmt("1000"), 0), - ).to.revertedWith("UnacceptableDepositTranche"); + ).to.revertedWithCustomError(perp, "UnacceptableDepositTranche"); }); }); @@ -587,7 +654,7 @@ describe("RouterV1", function () { const ERC20 = await ethers.getContractFactory("MockERC20"); feeToken = await ERC20.deploy(); await feeToken.init("Mock token", "MOCK"); - await feeStrategy.setFeeToken(feeToken.address); + await feeStrategy.feeToken.returns(feeToken.address); await feeToken.mint(deployerAddress, toFixedPtAmt("10")); await feeToken.approve(router.address, constants.MaxUint256); @@ -621,7 +688,7 @@ describe("RouterV1", function () { const ERC20 = await ethers.getContractFactory("MockERC20"); feeToken = await ERC20.deploy(); await feeToken.init("Mock token", "MOCK"); - await feeStrategy.setFeeToken(feeToken.address); + await feeStrategy.feeToken.returns(feeToken.address); await feeToken.mint(deployerAddress, toFixedPtAmt("25")); await feeToken.approve(router.address, constants.MaxUint256); @@ -660,16 +727,24 @@ describe("RouterV1", function () { describe("#trancheAndRollover", function () { let holdingPenTranches: Contract[], reserveTranches: Contract[], depositBond: Contract, depositTranches: Contract[]; beforeEach(async function () { - await feeStrategy.setBurnFee(toFixedPtAmt("10")); + await feeStrategy.computeBurnFees.returns([toFixedPtAmt("10"), "0"]); const holdingPenBond = await bondAt(await perp.callStatic.getDepositBond()); holdingPenTranches = await getTranches(holdingPenBond); await depositIntoBond(holdingPenBond, toFixedPtAmt("1000"), deployer); - await pricingStrategy.setTranchePrice(holdingPenTranches[0].address, toPriceFixedPtAmt("1")); - await discountStrategy.setTrancheDiscount(holdingPenTranches[0].address, toDiscountFixedPtAmt("1")); - await pricingStrategy.setTranchePrice(holdingPenTranches[1].address, toPriceFixedPtAmt("1")); - await discountStrategy.setTrancheDiscount(holdingPenTranches[1].address, toDiscountFixedPtAmt("1")); + await pricingStrategy.computeTranchePrice + .whenCalledWith(holdingPenTranches[0].address) + .returns(toPriceFixedPtAmt("1")); + await discountStrategy.computeTrancheDiscount + .whenCalledWith(holdingPenTranches[0].address) + .returns(toDiscountFixedPtAmt("1")); + await pricingStrategy.computeTranchePrice + .whenCalledWith(holdingPenTranches[1].address) + .returns(toPriceFixedPtAmt("1")); + await discountStrategy.computeTrancheDiscount + .whenCalledWith(holdingPenTranches[1].address) + .returns(toDiscountFixedPtAmt("1")); await holdingPenTranches[0].approve(perp.address, toFixedPtAmt("200")); await perp.deposit(holdingPenTranches[0].address, toFixedPtAmt("200")); @@ -682,10 +757,18 @@ describe("RouterV1", function () { reserveTranches = await getTranches(reserveBond); await depositIntoBond(reserveBond, toFixedPtAmt("1000"), deployer); - await pricingStrategy.setTranchePrice(reserveTranches[0].address, toPriceFixedPtAmt("1")); - await discountStrategy.setTrancheDiscount(reserveTranches[0].address, toDiscountFixedPtAmt("1")); - await pricingStrategy.setTranchePrice(reserveTranches[1].address, toPriceFixedPtAmt("1")); - await discountStrategy.setTrancheDiscount(reserveTranches[1].address, toDiscountFixedPtAmt("1")); + await pricingStrategy.computeTranchePrice + .whenCalledWith(reserveTranches[0].address) + .returns(toPriceFixedPtAmt("1")); + await discountStrategy.computeTrancheDiscount + .whenCalledWith(reserveTranches[0].address) + .returns(toDiscountFixedPtAmt("1")); + await pricingStrategy.computeTranchePrice + .whenCalledWith(reserveTranches[1].address) + .returns(toPriceFixedPtAmt("1")); + await discountStrategy.computeTrancheDiscount + .whenCalledWith(reserveTranches[1].address) + .returns(toDiscountFixedPtAmt("1")); await reserveTranches[0].approve(perp.address, toFixedPtAmt("200")); await perp.deposit(reserveTranches[0].address, toFixedPtAmt("200")); @@ -697,15 +780,23 @@ describe("RouterV1", function () { depositBond = await bondAt(await perp.callStatic.getDepositBond()); depositTranches = await getTranches(depositBond); - await pricingStrategy.setTranchePrice(depositTranches[0].address, toPriceFixedPtAmt("1")); - await discountStrategy.setTrancheDiscount(depositTranches[0].address, toDiscountFixedPtAmt("1")); - await pricingStrategy.setTranchePrice(depositTranches[1].address, toPriceFixedPtAmt("1")); - await discountStrategy.setTrancheDiscount(depositTranches[1].address, toDiscountFixedPtAmt("1")); + await pricingStrategy.computeTranchePrice + .whenCalledWith(depositTranches[0].address) + .returns(toPriceFixedPtAmt("1")); + await discountStrategy.computeTrancheDiscount + .whenCalledWith(depositTranches[0].address) + .returns(toDiscountFixedPtAmt("1")); + await pricingStrategy.computeTranchePrice + .whenCalledWith(depositTranches[1].address) + .returns(toPriceFixedPtAmt("1")); + await discountStrategy.computeTrancheDiscount + .whenCalledWith(depositTranches[1].address) + .returns(toDiscountFixedPtAmt("1")); }); describe("successful tranche & rollover and return the remainder", function () { beforeEach(async function () { - await await feeStrategy.setRolloverFee(toFixedPtAmt("1")); + await feeStrategy.computeRolloverFees.returns([toFixedPtAmt("1"), "0"]); await perp.approve(router.address, toFixedPtAmt("15")); await mintCollteralToken(collateralToken, toFixedPtAmt("2000"), deployer); @@ -761,7 +852,7 @@ describe("RouterV1", function () { describe("successful tranche & rollover and return the remainder (with no fees)", function () { beforeEach(async function () { - await await feeStrategy.setRolloverFee("0"); + await feeStrategy.computeRolloverFees.returns(["0", "0"]); await mintCollteralToken(collateralToken, toFixedPtAmt("2000"), deployer); await collateralToken.approve(router.address, toFixedPtAmt("2000")); diff --git a/spot-contracts/test/_utils/BondHelpers.ts b/spot-contracts/test/_utils/BondHelpers.ts index 988dfbcd..8dd142ba 100644 --- a/spot-contracts/test/_utils/BondHelpers.ts +++ b/spot-contracts/test/_utils/BondHelpers.ts @@ -10,6 +10,7 @@ import { toFixedPtAmt, rebase, depositIntoBond, + getTranches, getTrancheBalances, } from "../helpers"; @@ -19,12 +20,16 @@ let bondFactory: Contract, bondHelpers: Contract, accounts: Signer[], deployer: Signer, - deployerAddress: string; + deployerAddress: string, + user: Signer, + userAddress: string; async function setupContracts() { accounts = await ethers.getSigners(); deployer = accounts[0]; deployerAddress = await deployer.getAddress(); + user = accounts[1]; + userAddress = await user.getAddress(); bondFactory = await setupBondFactory(); ({ collateralToken, rebaseOracle } = await setupCollateralToken("Bitcoin", "BTC")); @@ -97,13 +102,16 @@ describe("BondHelpers", function () { expect(await bondHelpers.getTrancheIndex(bond.address, td.tranches[t])).to.eq(t); } - await expect(bondHelpers.getTrancheIndex(bond.address, bond.address)).to.be.revertedWith( + await expect(bondHelpers.getTrancheIndex(bond.address, bond.address)).to.be.revertedWithCustomError( + bondHelpers, "UnacceptableTrancheIndex", ); - await expect(bondHelpers.getTrancheIndex(bond.address, deployerAddress)).to.be.revertedWith( + await expect(bondHelpers.getTrancheIndex(bond.address, deployerAddress)).to.be.revertedWithCustomError( + bondHelpers, "UnacceptableTrancheIndex", ); - await expect(bondHelpers.getTrancheIndex(bond.address, constants.AddressZero)).to.be.revertedWith( + await expect(bondHelpers.getTrancheIndex(bond.address, constants.AddressZero)).to.be.revertedWithCustomError( + bondHelpers, "UnacceptableTrancheIndex", ); }); @@ -300,7 +308,7 @@ describe("BondHelpers", function () { }); }); - describe("#getTrancheCollateralization", function () { + describe("#getTrancheCollateralizations", function () { let bond: Contract, bondLength: number; beforeEach(async function () { bondLength = 86400; @@ -419,4 +427,110 @@ describe("BondHelpers", function () { }); }); }); + + describe("#computeRedeemableTrancheAmounts", function () { + let bond: Contract, bondLength: number; + beforeEach(async function () { + bondLength = 86400; + bond = await createBondWithFactory(bondFactory, collateralToken, [200, 300, 500], bondLength); + await depositIntoBond(bond, toFixedPtAmt("1000"), deployer); + }); + + describe("when the user has all the tranches in the right proportions", function () { + describe("when the user has the entire supply", function () { + it("should calculate the amounts", async function () { + const b = await bondHelpers.computeRedeemableTrancheAmounts(bond.address, deployerAddress); + expect(b[1][0]).to.eq(toFixedPtAmt("200")); + expect(b[1][1]).to.eq(toFixedPtAmt("300")); + expect(b[1][2]).to.eq(toFixedPtAmt("500")); + }); + }); + + describe("when the user does not have the entire supply", function () { + beforeEach(async function () { + const tranches = await getTranches(bond); + await tranches[0].transfer(userAddress, toFixedPtAmt("10")); + await tranches[1].transfer(userAddress, toFixedPtAmt("15")); + await tranches[2].transfer(userAddress, toFixedPtAmt("25")); + }); + it("should calculate the amounts", async function () { + const b1 = await bondHelpers.computeRedeemableTrancheAmounts(bond.address, userAddress); + expect(b1[1][0]).to.eq(toFixedPtAmt("10")); + expect(b1[1][1]).to.eq(toFixedPtAmt("15")); + expect(b1[1][2]).to.eq(toFixedPtAmt("25")); + + const b2 = await bondHelpers.computeRedeemableTrancheAmounts(bond.address, deployerAddress); + expect(b2[1][0]).to.eq(toFixedPtAmt("190")); + expect(b2[1][1]).to.eq(toFixedPtAmt("285")); + expect(b2[1][2]).to.eq(toFixedPtAmt("475")); + }); + }); + }); + + describe("when the user does not have tranches right proportions", function () { + async function checkRedeemableAmts( + bond: Contract, + userAddress: string, + amounts: string[] = [], + redemptionAmts: string[] = [], + ) { + const tranches = await getTranches(bond); + for (const a in amounts) { + await tranches[a].transfer(userAddress, toFixedPtAmt(amounts[a])); + } + const b = await bondHelpers.computeRedeemableTrancheAmounts(bond.address, userAddress); + for (const a in redemptionAmts) { + expect(b[1][a]).to.eq(toFixedPtAmt(redemptionAmts[a])); + } + } + + describe("[9, 15, 25]", async function () { + it("should calculate the amounts", async function () { + await checkRedeemableAmts(bond, userAddress, ["9", "15", "25"], ["9", "13.5", "22.5"]); + }); + }); + + describe("[10, 15, 250]", async function () { + it("should calculate the amounts", async function () { + await checkRedeemableAmts(bond, userAddress, ["10", "15", "250"], ["10", "15", "25"]); + }); + }); + + describe("[10, 12, 250]", async function () { + it("should calculate the amounts", async function () { + await checkRedeemableAmts(bond, userAddress, ["10", "12", "250"], ["8", "12", "20"]); + }); + }); + + describe("[10, 12, 5]", async function () { + it("should calculate the amounts", async function () { + await checkRedeemableAmts(bond, userAddress, ["10", "12", "5"], ["2", "3", "5"]); + }); + }); + + describe("[10, 12, 0.5]", async function () { + it("should calculate the amounts", async function () { + await checkRedeemableAmts(bond, userAddress, ["10", "12", "0.5"], ["0.2", "0.3", "0.5"]); + }); + }); + + describe("[10, 0, 25]", async function () { + it("should calculate the amounts", async function () { + await checkRedeemableAmts(bond, userAddress, ["10", "0", "25"], ["0", "0", "0"]); + }); + }); + + describe("[0, 15, 25]", async function () { + it("should calculate the amounts", async function () { + await checkRedeemableAmts(bond, userAddress, ["0", "15", "25"], ["0", "0", "0"]); + }); + }); + + describe("[10, 15, 0]", async function () { + it("should calculate the amounts", async function () { + await checkRedeemableAmts(bond, userAddress, ["10", "15", "0"], ["0", "0", "0"]); + }); + }); + }); + }); }); diff --git a/spot-contracts/test/helpers.ts b/spot-contracts/test/helpers.ts index 9e9bf6f6..073a5309 100644 --- a/spot-contracts/test/helpers.ts +++ b/spot-contracts/test/helpers.ts @@ -1,8 +1,10 @@ -import { expect } from "chai"; +import { expect, use } from "chai"; import hre, { ethers } from "hardhat"; import { Signer, Contract, BigNumber, ContractFactory, Transaction, utils } from "ethers"; import * as fs from "fs"; import * as path from "path"; +import { smock, FakeContract } from "@defi-wonderland/smock"; +use(smock.matchers); const TOKEN_DECIMALS = 18; const PRICE_DECIMALS = 8; @@ -41,7 +43,7 @@ export const TimeHelpers = { // Rebasing collateral token (button tokens) interface ButtonTokenContracts { underlyingToken: Contract; - rebaseOracle: Contract; + rebaseOracle: FakeContract; collateralToken: Contract; } export const setupCollateralToken = async (name: string, symbol: string): Promise => { @@ -49,10 +51,9 @@ export const setupCollateralToken = async (name: string, symbol: string): Promis const underlyingToken = await ERC20.deploy(); await underlyingToken.init(name, symbol); - const MockOracle = await ethers.getContractFactory("MockOracle"); - const rebaseOracle = await MockOracle.deploy(); - await rebaseOracle.deployed(); - await rebaseOracle.setData(ORACLE_BASE_PRICE, true); + const MedianOracle = await getContractFactoryFromExternalArtifacts("MedianOracle"); + const rebaseOracle = await smock.fake(MedianOracle); + await rebaseOracle.getData.returns([ORACLE_BASE_PRICE, true]); const ButtonToken = await getContractFactoryFromExternalArtifacts("ButtonToken"); const collateralToken = await ButtonToken.deploy(); @@ -75,10 +76,11 @@ export const mintCollteralToken = async (collateralToken: Contract, amount: BigN await collateralToken.connect(from).mint(amount); }; -export const rebase = async (token: Contract, oracle: Contract, perc: number) => { +export const rebase = async (token: Contract, oracle: FakeContract, perc: number) => { const p = await token.lastPrice(); const newPrice = p.mul(ORACLE_BASE_PRICE.add(ORACLE_BASE_PRICE.toNumber() * perc)).div(ORACLE_BASE_PRICE); - await oracle.setData(newPrice, true); + await oracle.getData.returns([newPrice, true]); + await token.rebase(); }; // Button tranche @@ -133,6 +135,7 @@ export interface BondDeposit { feeBps: BigNumber; from: string; } + export const depositIntoBond = async (bond: Contract, amount: BigNumber, from: Signer): Promise => { const ButtonToken = await getContractFactoryFromExternalArtifacts("ButtonToken"); const collateralToken = await ButtonToken.attach(await bond.collateralToken()); @@ -167,35 +170,47 @@ export const getTrancheBalances = async (bond: Contract, user: string): Promise< return balances; }; +export const getDepositBond = async (perp: Contract): Contract => { + return bondAt(await perp.callStatic.getDepositBond()); +}; + export const advancePerpQueue = async (perp: Contract, time: number): Promise => { await TimeHelpers.increaseTime(time); return perp.updateState(); }; export const advancePerpQueueToBondMaturity = async (perp: Contract, bond: Contract): Promise => { + await perp.updateState(); const matuirtyDate = await bond.maturityDate(); - await TimeHelpers.setNextBlockTimestamp(matuirtyDate.toNumber() + 1); + await TimeHelpers.setNextBlockTimestamp(matuirtyDate.toNumber()); + await perp.updateState(); + await TimeHelpers.increaseTime(1); return perp.updateState(); }; export const advancePerpQueueToRollover = async (perp: Contract, bond: Contract): Promise => { + await perp.updateState(); const bufferSec = await perp.minTrancheMaturitySec(); const matuirtyDate = await bond.maturityDate(); - await TimeHelpers.setNextBlockTimestamp(matuirtyDate.sub(bufferSec).toNumber() + 1); + await TimeHelpers.setNextBlockTimestamp(matuirtyDate.sub(bufferSec).toNumber()); + await perp.updateState(); + await TimeHelpers.increaseTime(1); return perp.updateState(); }; export const logReserveComposition = async (perp: Contract) => { + const ERC20 = await ethers.getContractFactory("MockERC20"); const count = await perp.callStatic.getReserveCount(); console.log("Reserve count", count); for (let i = 0; i < count; i++) { - const token = await perp.callStatic.getReserveAt(i); + const token = await ERC20.attach(await perp.callStatic.getReserveAt(i)); console.log( i, - token, - utils.formatUnits(await perp.callStatic.getReserveTrancheBalance(token), await perp.decimals()), - utils.formatUnits(await perp.computeDiscount(token), await perp.DISCOUNT_DECIMALS()), - utils.formatUnits(await perp.computePrice(token), await perp.PRICE_DECIMALS()), + token.address, + utils.formatUnits(await token.balanceOf(await perp.reserve()), await perp.decimals()), + utils.formatUnits(await perp.callStatic.getReserveTrancheBalance(token.address), await perp.decimals()), + utils.formatUnits(await perp.computeDiscount(token.address), await perp.DISCOUNT_DECIMALS()), + utils.formatUnits(await perp.computePrice(token.address), await perp.PRICE_DECIMALS()), ); } }; @@ -203,12 +218,83 @@ export const logReserveComposition = async (perp: Contract) => { export const checkReserveComposition = async (perp: Contract, tokens: Contract[], balances: BigNumber[] = []) => { const checkBalances = balances.length > 0; expect(await perp.callStatic.getReserveCount()).to.eq(tokens.length); + + const tokenMap = {}; + const tokenBalanceMap = {}; for (const i in tokens) { - expect(await perp.callStatic.inReserve(tokens[i].address)).to.eq(true); - expect(await perp.callStatic.getReserveAt(i)).to.eq(tokens[i].address); + tokenMap[tokens[i].address] = true; + if (checkBalances) { + tokenBalanceMap[tokens[i].address] = balances[i]; + } + } + + const ERC20 = await ethers.getContractFactory("MockERC20"); + for (let j = 0; j < tokens.length; j++) { + const reserveToken = ERC20.attach(await perp.callStatic.getReserveAt(j)); + expect(tokenMap[reserveToken.address]).to.eq(true); if (checkBalances) { - expect(await tokens[i].balanceOf(perp.reserve())).to.eq(balances[i]); + expect(await reserveToken.balanceOf(await perp.reserve())).to.eq(tokenBalanceMap[reserveToken.address]); } } await expect(perp.callStatic.getReserveAt(tokens.length)).to.be.reverted; }; + +export const getReserveTokens = async (perp: Contract) => { + const ERC20 = await ethers.getContractFactory("MockERC20"); + const reserves: Contract[] = []; + for (let i = 0; i < (await perp.callStatic.getReserveCount()); i++) { + reserves.push(await ERC20.attach(await perp.callStatic.getReserveAt(i))); + } + return reserves; +}; + +export const logVaultAssets = async (vault: Contract) => { + const ERC20 = await ethers.getContractFactory("MockERC20"); + const deployedCount = (await vault.deployedCount()).toNumber(); + const earnedCount = (await vault.earnedCount()).toNumber(); + const count = 1 + deployedCount + earnedCount; + console.log("Asset count", count); + + const underlying = await ERC20.attach(await vault.underlying()); + console.log( + 0, + underlying.address, + utils.formatUnits(await vault.vaultAssetBalance(underlying.address), await underlying.decimals()), + ); + for (let i = 0; i < deployedCount; i++) { + const token = await ERC20.attach(await vault.deployedAt(i)); + console.log( + i + 1, + token.address, + utils.formatUnits(await vault.vaultAssetBalance(token.address), await token.decimals()), + ); + } + for (let j = 0; j < earnedCount; j++) { + const token = await ERC20.attach(await vault.earnedAt(j)); + console.log( + j + 1 + deployedCount, + token.address, + utils.formatUnits(await vault.vaultAssetBalance(token.address), await token.decimals()), + ); + } +}; + +export const checkVaultAssetComposition = async (vault: Contract, tokens: Contract[], balances: BigNumber[] = []) => { + expect(1 + (await vault.deployedCount()).toNumber() + (await vault.earnedCount()).toNumber()).to.eq(tokens.length); + for (const i in tokens) { + expect(await vault.vaultAssetBalance(tokens[i].address)).to.eq(balances[i]); + } +}; + +export const getVaultAssets = async (vault: Contract) => { + const ERC20 = await ethers.getContractFactory("MockERC20"); + const assets: Contract[] = []; + assets.push(await ERC20.attach(await vault.underlying())); + for (let i = 0; i < (await vault.deployedCount()); i++) { + assets.push(await ERC20.attach(await vault.deployedAt(i))); + } + for (let i = 0; i < (await vault.earnedCount()); i++) { + assets.push(await ERC20.attach(await vault.earnedAt(i))); + } + return assets; +}; diff --git a/spot-contracts/test/strategies/BasicFeeStrategy.ts b/spot-contracts/test/strategies/BasicFeeStrategy.ts index a99396ed..f04e8d5e 100644 --- a/spot-contracts/test/strategies/BasicFeeStrategy.ts +++ b/spot-contracts/test/strategies/BasicFeeStrategy.ts @@ -1,12 +1,10 @@ import { expect } from "chai"; import { ethers } from "hardhat"; -import { Contract, ContractFactory, Transaction, Signer } from "ethers"; +import { Contract, Transaction, Signer } from "ethers"; import { toFixedPtAmt } from "../helpers"; -let factory: ContractFactory, feeStrategy: Contract, deployer: Signer, otherUser: Signer; - -const mockFeeTokenAddress = "0x000000000000000000000000000000000000dEaD"; +let feeStrategy: Contract, deployer: Signer, otherUser: Signer, feeToken: Contract; describe("BasicFeeStrategy", function () { beforeEach(async function () { @@ -14,16 +12,25 @@ describe("BasicFeeStrategy", function () { deployer = accounts[0]; otherUser = accounts[1]; - factory = await ethers.getContractFactory("BasicFeeStrategy"); - feeStrategy = await factory.deploy(mockFeeTokenAddress); + const ERC20 = await ethers.getContractFactory("MockERC20"); + feeToken = await ERC20.deploy(); + + const factory = await ethers.getContractFactory("BasicFeeStrategy"); + feeStrategy = await factory.deploy(feeToken.address, feeToken.address); }); - describe("#feeToken", function () { + describe("#init", function () { beforeEach(async function () { await feeStrategy.init("0", "0", "0"); }); it("should return the fee token", async function () { - expect(await feeStrategy.feeToken()).to.eq(mockFeeTokenAddress); + expect(await feeStrategy.feeToken()).to.eq(feeToken.address); + }); + it("should return the debasement flag", async function () { + expect(await feeStrategy.allowDebase()).to.eq(false); + }); + it("should return owner", async function () { + expect(await feeStrategy.owner()).to.eq(await deployer.getAddress()); }); }); @@ -111,89 +118,380 @@ describe("BasicFeeStrategy", function () { }); }); - describe("#computeMintFees", function () { - describe("when perc > 0", function () { - it("should return the mint fee", async function () { - await feeStrategy.init("1500000", "0", "0"); - const r = await feeStrategy.computeMintFees(toFixedPtAmt("1000")); - expect(r[0]).to.eq(toFixedPtAmt("15")); - expect(r[1]).to.eq("0"); + describe("#enableDebasement", function () { + let tx: Transaction; + beforeEach(async function () { + await feeStrategy.init("0", "0", "0"); + }); + + describe("when triggered by non-owner", function () { + it("should revert", async function () { + await expect(feeStrategy.connect(otherUser).allowDebasement(true)).to.be.revertedWith( + "Ownable: caller is not the owner", + ); }); }); - describe("when perc < 0", function () { - it("should return the mint fee", async function () { - await feeStrategy.init("-2500000", "0", "0"); - const r = await feeStrategy.computeMintFees(toFixedPtAmt("1000")); - expect(r[0]).to.eq(toFixedPtAmt("-25")); - expect(r[1]).to.eq("0"); + describe("when debasement is enabled", function () { + beforeEach(async function () { + tx = feeStrategy.connect(deployer).allowDebasement(true); + await tx; + }); + it("should update flag", async function () { + expect(await feeStrategy.allowDebase()).to.eq(true); + }); + it("should emit event", async function () { + await expect(tx).to.emit(feeStrategy, "UpdatedDebasementRule").withArgs(true); }); }); + }); - describe("when perc = 0", function () { - it("should return the mint fee", async function () { - await feeStrategy.init("0", "0", "0"); - const r = await feeStrategy.computeMintFees(toFixedPtAmt("1000")); - expect(r[0]).to.eq(toFixedPtAmt("0")); - expect(r[1]).to.eq("0"); + describe("#disableDebasement", function () { + let tx: Transaction; + beforeEach(async function () { + await feeStrategy.init("0", "0", "0"); + }); + + describe("when triggered by non-owner", function () { + it("should revert", async function () { + await expect(feeStrategy.connect(otherUser).allowDebasement(false)).to.be.revertedWith( + "Ownable: caller is not the owner", + ); + }); + }); + + describe("when debasement is disabled", function () { + beforeEach(async function () { + tx = feeStrategy.connect(deployer).allowDebasement(false); + await tx; + }); + it("should update flag", async function () { + expect(await feeStrategy.allowDebase()).to.eq(false); + }); + it("should emit event", async function () { + await expect(tx).to.emit(feeStrategy, "UpdatedDebasementRule").withArgs(false); }); }); }); - describe("#computeBurnFees", function () { - describe("when perc > 0", function () { - it("should return the burn fee", async function () { - await feeStrategy.init("0", "2500000", "0"); - const r = await feeStrategy.computeBurnFees(toFixedPtAmt("1000")); - expect(r[0]).to.eq(toFixedPtAmt("25")); - expect(r[1]).to.eq("0"); + describe("when debasement is enabled", function () { + describe("#computeMintFees", function () { + describe("when perc > 0", function () { + it("should return the mint fee", async function () { + await feeStrategy.init("1500000", "0", "0"); + await feeStrategy.allowDebasement(true); + + const r = await feeStrategy.computeMintFees(toFixedPtAmt("1000")); + expect(r[0]).to.eq(toFixedPtAmt("15")); + expect(r[1]).to.eq("0"); + }); + }); + + describe("when perc < 0", function () { + it("should return the mint fee", async function () { + await feeStrategy.init("-2500000", "0", "0"); + await feeStrategy.allowDebasement(true); + + const r = await feeStrategy.computeMintFees(toFixedPtAmt("1000")); + expect(r[0]).to.eq(toFixedPtAmt("-25")); + expect(r[1]).to.eq("0"); + }); + }); + + describe("when perc = 0", function () { + it("should return the mint fee", async function () { + await feeStrategy.init("0", "0", "0"); + await feeStrategy.allowDebasement(true); + + const r = await feeStrategy.computeMintFees(toFixedPtAmt("1000")); + expect(r[0]).to.eq("0"); + expect(r[1]).to.eq("0"); + }); }); }); - describe("when perc < 0", function () { - it("should return the burn fee", async function () { - await feeStrategy.init("0", "-1500000", "0"); - const r = await feeStrategy.computeBurnFees(toFixedPtAmt("1000")); - expect(r[0]).to.eq(toFixedPtAmt("-15")); - expect(r[1]).to.eq("0"); + describe("#computeBurnFees", function () { + describe("when perc > 0", function () { + it("should return the burn fee", async function () { + await feeStrategy.init("0", "2500000", "0"); + await feeStrategy.allowDebasement(true); + + const r = await feeStrategy.computeBurnFees(toFixedPtAmt("1000")); + expect(r[0]).to.eq(toFixedPtAmt("25")); + expect(r[1]).to.eq("0"); + }); + }); + + describe("when perc < 0", function () { + it("should return the burn fee", async function () { + await feeStrategy.init("0", "-1500000", "0"); + await feeStrategy.allowDebasement(true); + + const r = await feeStrategy.computeBurnFees(toFixedPtAmt("1000")); + expect(r[0]).to.eq(toFixedPtAmt("-15")); + expect(r[1]).to.eq("0"); + }); + }); + + describe("when perc = 0", function () { + it("should return the burn fee", async function () { + await feeStrategy.init("0", "0", "0"); + await feeStrategy.allowDebasement(true); + + const r = await feeStrategy.computeBurnFees(toFixedPtAmt("1000")); + expect(r[0]).to.eq("0"); + expect(r[1]).to.eq("0"); + }); }); }); - describe("when perc = 0", function () { - it("should return the burn fee", async function () { - await feeStrategy.init("0", "0", "0"); - const r = await feeStrategy.computeBurnFees(toFixedPtAmt("1000")); - expect(r[0]).to.eq(toFixedPtAmt("0")); - expect(r[1]).to.eq("0"); + describe("#computeRolloverFees", function () { + describe("when perc > 0", function () { + it("should return the rollover fee", async function () { + await feeStrategy.init("0", "0", "1000000"); + await feeStrategy.allowDebasement(true); + + const r = await feeStrategy.computeRolloverFees(toFixedPtAmt("100000")); + expect(r[0]).to.eq(toFixedPtAmt("1000")); + expect(r[1]).to.eq("0"); + }); + }); + + describe("when perc < 0", function () { + it("should return the rollover fee", async function () { + await feeStrategy.init("0", "0", "-5000000"); + await feeStrategy.allowDebasement(true); + + const r = await feeStrategy.computeRolloverFees(toFixedPtAmt("100000")); + expect(r[0]).to.eq(toFixedPtAmt("-5000")); + expect(r[1]).to.eq("0"); + }); + }); + + describe("when perc = 0", function () { + it("should return the rollover fee", async function () { + await feeStrategy.init("0", "0", "0"); + await feeStrategy.allowDebasement(true); + + const r = await feeStrategy.computeRolloverFees(toFixedPtAmt("100000")); + expect(r[0]).to.eq("0"); + expect(r[1]).to.eq("0"); + }); }); }); }); - describe("#computeRolloverFees", function () { - describe("when perc > 0", function () { - it("should return the rollover fee", async function () { - await feeStrategy.init("0", "0", "1000000"); - const r = await feeStrategy.computeRolloverFees(toFixedPtAmt("100000")); - expect(r[0]).to.eq(toFixedPtAmt("1000")); - expect(r[1]).to.eq("0"); + describe("when debasement is disabled", function () { + describe("#computeMintFees", function () { + describe("when perc > 0", function () { + it("should return the mint fee", async function () { + await feeStrategy.init("1500000", "0", "0"); + await feeStrategy.allowDebasement(false); + + const r = await feeStrategy.computeMintFees(toFixedPtAmt("1000")); + expect(r[0]).to.eq(toFixedPtAmt("15")); + expect(r[1]).to.eq("0"); + }); + }); + + describe("when perc < 0 && reserve balance = 0", function () { + it("should return the debasement-free mint fee", async function () { + await feeStrategy.init("-2500000", "0", "0"); + await feeStrategy.allowDebasement(false); + + const r = await feeStrategy.computeMintFees(toFixedPtAmt("1000")); + expect(r[0]).to.eq("0"); + expect(r[1]).to.eq("0"); + }); + }); + + describe("when perc < 0 && reserve balance > fee to be paid", function () { + it("should return the mint fee", async function () { + await feeToken.mint(feeToken.address, toFixedPtAmt("30")); + await feeStrategy.init("-2500000", "0", "0"); + await feeStrategy.allowDebasement(false); + + const r = await feeStrategy.computeMintFees(toFixedPtAmt("1000")); + expect(r[0]).to.eq(toFixedPtAmt("-25")); + expect(r[1]).to.eq("0"); + }); + }); + + describe("when perc < 0 && reserve balance = fee to be paid", function () { + it("should return the mint fee", async function () { + await feeToken.mint(feeToken.address, toFixedPtAmt("25")); + await feeStrategy.init("-2500000", "0", "0"); + await feeStrategy.allowDebasement(false); + + const r = await feeStrategy.computeMintFees(toFixedPtAmt("1000")); + expect(r[0]).to.eq(toFixedPtAmt("-25")); + expect(r[1]).to.eq("0"); + }); + }); + + describe("when perc < 0 && reserve balance < fee to be paid", function () { + it("should return the the debasement-free mint fee", async function () { + await feeToken.mint(feeToken.address, toFixedPtAmt("18")); + await feeStrategy.init("-2500000", "0", "0"); + await feeStrategy.allowDebasement(false); + + const r = await feeStrategy.computeMintFees(toFixedPtAmt("1000")); + expect(r[0]).to.eq(toFixedPtAmt("-18")); + expect(r[1]).to.eq("0"); + }); + }); + + describe("when perc = 0", function () { + it("should return the mint fee", async function () { + await feeStrategy.init("0", "0", "0"); + await feeStrategy.allowDebasement(false); + + const r = await feeStrategy.computeMintFees(toFixedPtAmt("1000")); + expect(r[0]).to.eq("0"); + expect(r[1]).to.eq("0"); + }); }); }); - describe("when perc < 0", function () { - it("should return the rollover fee", async function () { - await feeStrategy.init("0", "0", "-5000000"); - const r = await feeStrategy.computeRolloverFees(toFixedPtAmt("100000")); - expect(r[0]).to.eq(toFixedPtAmt("-5000")); - expect(r[1]).to.eq("0"); + describe("#computeBurnFees", function () { + describe("when perc > 0", function () { + it("should return the burn fee", async function () { + await feeStrategy.init("0", "2500000", "0"); + await feeStrategy.allowDebasement(false); + + const r = await feeStrategy.computeBurnFees(toFixedPtAmt("1000")); + expect(r[0]).to.eq(toFixedPtAmt("25")); + expect(r[1]).to.eq("0"); + }); + }); + + describe("when perc < 0 && reserve balance = 0", function () { + it("should return the the debasement-free burn fee", async function () { + await feeStrategy.init("0", "-1500000", "0"); + await feeStrategy.allowDebasement(false); + + const r = await feeStrategy.computeBurnFees(toFixedPtAmt("1000")); + expect(r[0]).to.eq("0"); + expect(r[1]).to.eq("0"); + }); + }); + + describe("when perc < 0 && reserve balance > fee to be paid", function () { + it("should return the burn fee", async function () { + await feeToken.mint(feeToken.address, toFixedPtAmt("20")); + await feeStrategy.init("0", "-1500000", "0"); + await feeStrategy.allowDebasement(false); + + const r = await feeStrategy.computeBurnFees(toFixedPtAmt("1000")); + expect(r[0]).to.eq(toFixedPtAmt("-15")); + expect(r[1]).to.eq("0"); + }); + }); + + describe("when perc < 0 && reserve balance = fee to be paid", function () { + it("should return the burn fee", async function () { + await feeToken.mint(feeToken.address, toFixedPtAmt("15")); + await feeStrategy.init("0", "-1500000", "0"); + await feeStrategy.allowDebasement(false); + + const r = await feeStrategy.computeBurnFees(toFixedPtAmt("1000")); + expect(r[0]).to.eq(toFixedPtAmt("-15")); + expect(r[1]).to.eq("0"); + }); + }); + + describe("when perc < 0 && reserve balance < fee to be paid", function () { + it("should return the the debasement-free burn fee", async function () { + await feeToken.mint(feeToken.address, toFixedPtAmt("11")); + await feeStrategy.init("0", "-1500000", "0"); + await feeStrategy.allowDebasement(false); + + const r = await feeStrategy.computeBurnFees(toFixedPtAmt("1000")); + expect(r[0]).to.eq(toFixedPtAmt("-11")); + expect(r[1]).to.eq("0"); + }); + }); + + describe("when perc = 0", function () { + it("should return the burn fee", async function () { + await feeStrategy.init("0", "0", "0"); + await feeStrategy.allowDebasement(false); + + const r = await feeStrategy.computeBurnFees(toFixedPtAmt("1000")); + expect(r[0]).to.eq("0"); + expect(r[1]).to.eq("0"); + }); }); }); - describe("when perc = 0", function () { - it("should return the rollover fee", async function () { - await feeStrategy.init("0", "0", "0"); - const r = await feeStrategy.computeRolloverFees(toFixedPtAmt("100000")); - expect(r[0]).to.eq(toFixedPtAmt("0")); - expect(r[1]).to.eq("0"); + describe("#computeRolloverFees", function () { + describe("when perc > 0", function () { + it("should return the rollover fee", async function () { + await feeStrategy.init("0", "0", "1000000"); + await feeStrategy.allowDebasement(false); + + const r = await feeStrategy.computeRolloverFees(toFixedPtAmt("100000")); + expect(r[0]).to.eq(toFixedPtAmt("1000")); + expect(r[1]).to.eq("0"); + }); + }); + + describe("when perc < 0 && reserve balance = 0", function () { + it("should return the the debasement-free rollover fee", async function () { + await feeStrategy.init("0", "0", "-5000000"); + await feeStrategy.allowDebasement(false); + + const r = await feeStrategy.computeRolloverFees(toFixedPtAmt("100000")); + expect(r[0]).to.eq("0"); + expect(r[1]).to.eq("0"); + }); + }); + + describe("when perc < 0 && reserve balance > fee to be paid", function () { + it("should return the rollover fee", async function () { + await feeToken.mint(feeToken.address, toFixedPtAmt("10000")); + await feeStrategy.init("0", "0", "-5000000"); + await feeStrategy.allowDebasement(false); + + const r = await feeStrategy.computeRolloverFees(toFixedPtAmt("100000")); + expect(r[0]).to.eq(toFixedPtAmt("-5000")); + expect(r[1]).to.eq("0"); + }); + }); + + describe("when perc < 0 && reserve balance = fee to be paid", function () { + it("should return the rollover fee", async function () { + await feeToken.mint(feeToken.address, toFixedPtAmt("5000")); + await feeStrategy.init("0", "0", "-5000000"); + await feeStrategy.allowDebasement(false); + + const r = await feeStrategy.computeRolloverFees(toFixedPtAmt("100000")); + expect(r[0]).to.eq(toFixedPtAmt("-5000")); + expect(r[1]).to.eq("0"); + }); + }); + + describe("when perc < 0 && reserve balance < fee to be paid", function () { + it("should return the the debasement-free rollover fee", async function () { + await feeToken.mint(feeToken.address, toFixedPtAmt("1000")); + await feeStrategy.init("0", "0", "-5000000"); + await feeStrategy.allowDebasement(false); + + const r = await feeStrategy.computeRolloverFees(toFixedPtAmt("100000")); + expect(r[0]).to.eq(toFixedPtAmt("-1000")); + expect(r[1]).to.eq("0"); + }); + }); + + describe("when perc = 0", function () { + it("should return the rollover fee", async function () { + await feeStrategy.init("0", "0", "0"); + await feeStrategy.allowDebasement(false); + + const r = await feeStrategy.computeRolloverFees(toFixedPtAmt("100000")); + expect(r[0]).to.eq("0"); + expect(r[1]).to.eq("0"); + }); }); }); }); diff --git a/spot-contracts/test/strategies/CDRPricingStrategy.ts b/spot-contracts/test/strategies/CDRPricingStrategy.ts index 85a21501..99eb4991 100644 --- a/spot-contracts/test/strategies/CDRPricingStrategy.ts +++ b/spot-contracts/test/strategies/CDRPricingStrategy.ts @@ -1,6 +1,7 @@ -import { expect } from "chai"; +import { expect, use } from "chai"; import { network, ethers } from "hardhat"; import { Contract, Signer } from "ethers"; +import { smock } from "@defi-wonderland/smock"; import { TimeHelpers, @@ -12,6 +13,7 @@ import { depositIntoBond, getTranches, } from "../helpers"; +use(smock.matchers); let bondFactory: Contract, collateralToken: Contract, @@ -27,9 +29,9 @@ async function setupContracts() { bondFactory = await setupBondFactory(); ({ collateralToken, rebaseOracle } = await setupCollateralToken("Bitcoin", "BTC")); - const PerpetualTranche = await ethers.getContractFactory("MockPerpetualTranche"); - perp = await PerpetualTranche.deploy(); - await perp.setCollateral(collateralToken.address); + const PerpetualTranche = await ethers.getContractFactory("PerpetualTranche"); + perp = await smock.fake(PerpetualTranche); + await perp.collateral.returns(collateralToken.address); const CDRPricingStrategy = await ethers.getContractFactory("CDRPricingStrategy"); pricingStrategy = await CDRPricingStrategy.deploy(); diff --git a/spot-contracts/test/vaults/RolloverVault.ts b/spot-contracts/test/vaults/RolloverVault.ts new file mode 100644 index 00000000..b707290f --- /dev/null +++ b/spot-contracts/test/vaults/RolloverVault.ts @@ -0,0 +1,278 @@ +import { expect, use } from "chai"; +import { network, ethers, upgrades } from "hardhat"; +import { Contract, Transaction, Signer } from "ethers"; +import { + setupCollateralToken, + mintCollteralToken, + toFixedPtAmt, + setupBondFactory, + depositIntoBond, + getTranches, + toDiscountFixedPtAmt, + toPriceFixedPtAmt, + getDepositBond, + advancePerpQueueToBondMaturity, +} from "../helpers"; +import { smock, FakeContract } from "@defi-wonderland/smock"; + +use(smock.matchers); + +let vault: Contract, perp: FakeContract, collateralToken: Contract, deployer: Signer, otherUser: Signer; +describe("RolloverVault", function () { + beforeEach(async function () { + await network.provider.send("hardhat_reset"); + + const accounts = await ethers.getSigners(); + deployer = accounts[0]; + otherUser = accounts[1]; + + ({ collateralToken } = await setupCollateralToken("Bitcoin", "BTC")); + await mintCollteralToken(collateralToken, toFixedPtAmt("1000"), deployer); + + const PerpetualTranche = await ethers.getContractFactory("PerpetualTranche"); + perp = await smock.fake(PerpetualTranche); + + await perp.collateral.returns(collateralToken.address); + await perp.feeToken.returns(perp.address); + + const RolloverVault = await ethers.getContractFactory("RolloverVault"); + vault = await upgrades.deployProxy(RolloverVault.connect(deployer)); + await collateralToken.approve(vault.address, toFixedPtAmt("1")); + await vault.init("RolloverVault", "VSHARE", perp.address); + }); + + afterEach(async function () { + await network.provider.send("hardhat_reset"); + }); + + describe("#init", function () { + it("should set erc20 parameters", async function () { + expect(await vault.name()).to.eq("RolloverVault"); + expect(await vault.symbol()).to.eq("VSHARE"); + expect(await vault.decimals()).to.eq(18); + }); + + it("should set owner", async function () { + expect(await vault.owner()).to.eq(await deployer.getAddress()); + }); + + it("should set ext service references", async function () { + expect(await vault.perp()).to.eq(perp.address); + }); + + it("should set deposit asset reference", async function () { + expect(await vault.underlying()).to.eq(collateralToken.address); + }); + + it("should initialize lists", async function () { + expect(await vault.deployedCount()).to.eq(0); + expect(await vault.earnedCount()).to.eq(1); + expect(await vault.earnedAt(0)).to.eq(perp.address); + await expect(vault.earnedAt(1)).to.be.revertedWithCustomError(vault, "OutOfBounds"); + expect(await vault.isVaultAsset(collateralToken.address)).to.eq(true); + expect(await vault.isVaultAsset(perp.address)).to.eq(true); + }); + + it("should NOT be paused", async function () { + expect(await vault.paused()).to.eq(false); + }); + }); + + describe("#pause", function () { + let tx: Transaction; + beforeEach(async function () { + await vault.connect(deployer).transferOwnership(await otherUser.getAddress()); + }); + + describe("when triggered by non-owner", function () { + it("should revert", async function () { + await expect(vault.connect(deployer).pause()).to.be.revertedWith("Ownable: caller is not the owner"); + }); + }); + + describe("when already paused", function () { + beforeEach(async function () { + await vault.connect(otherUser).pause(); + }); + it("should revert", async function () { + await expect(vault.connect(otherUser).pause()).to.be.revertedWith("Pausable: paused"); + }); + }); + + describe("when valid", function () { + beforeEach(async function () { + tx = await vault.connect(otherUser).pause(); + await tx; + }); + it("should pause", async function () { + expect(await vault.paused()).to.eq(true); + }); + it("should emit event", async function () { + await expect(tx) + .to.emit(vault, "Paused") + .withArgs(await otherUser.getAddress()); + }); + }); + }); + + describe("#unpause", function () { + let tx: Transaction; + beforeEach(async function () { + await vault.connect(deployer).transferOwnership(await otherUser.getAddress()); + }); + + describe("when triggered by non-owner", function () { + beforeEach(async function () { + await vault.connect(otherUser).pause(); + }); + + it("should revert", async function () { + await expect(vault.connect(deployer).unpause()).to.be.revertedWith("Ownable: caller is not the owner"); + }); + }); + + describe("when not paused", function () { + it("should revert", async function () { + await expect(vault.connect(otherUser).unpause()).to.be.revertedWith("Pausable: not paused"); + }); + }); + + describe("when valid", function () { + beforeEach(async function () { + tx = await vault.connect(otherUser).pause(); + await tx; + tx = await vault.connect(otherUser).unpause(); + await tx; + }); + it("should unpause", async function () { + expect(await vault.paused()).to.eq(false); + }); + it("should emit event", async function () { + await expect(tx) + .to.emit(vault, "Unpaused") + .withArgs(await otherUser.getAddress()); + }); + }); + }); + + describe("#transferERC20", function () { + let transferToken: Contract, toAddress: string; + + beforeEach(async function () { + const Token = await ethers.getContractFactory("MockERC20"); + transferToken = await Token.deploy(); + await transferToken.init("Mock Token", "MOCK"); + await transferToken.mint(vault.address, "100"); + toAddress = await deployer.getAddress(); + }); + + describe("when triggered by non-owner", function () { + it("should revert", async function () { + await expect( + vault.connect(otherUser).transferERC20(transferToken.address, toAddress, "100"), + ).to.be.revertedWith("Ownable: caller is not the owner"); + }); + }); + + describe("when non vault asset", function () { + it("should transfer", async function () { + await expect(() => vault.transferERC20(transferToken.address, toAddress, "100")).to.changeTokenBalance( + transferToken, + deployer, + "100", + ); + }); + }); + + describe("when underlying asset", function () { + it("should revert", async function () { + await expect( + vault.transferERC20(await vault.underlying(), toAddress, toFixedPtAmt("100")), + ).to.be.revertedWithCustomError(vault, "UnauthorizedTransferOut"); + }); + }); + + describe("when earned asset", function () { + it("should revert", async function () { + await expect( + vault.transferERC20(await vault.earnedAt(0), toAddress, toFixedPtAmt("100")), + ).to.be.revertedWithCustomError(vault, "UnauthorizedTransferOut"); + }); + }); + + describe("when deployed asset", function () { + beforeEach(async function () { + const bondFactory = await setupBondFactory(); + ({ collateralToken } = await setupCollateralToken("Bitcoin", "BTC")); + const BondIssuer = await ethers.getContractFactory("BondIssuer"); + const issuer = await BondIssuer.deploy(bondFactory.address, collateralToken.address); + await issuer.init(4800, [200, 300, 500], 1200, 0); + + const FeeStrategy = await ethers.getContractFactory("BasicFeeStrategy"); + const feeStrategy = await smock.fake(FeeStrategy); + await feeStrategy.computeMintFees.returns(["0", "0"]); + await feeStrategy.computeBurnFees.returns(["0", "0"]); + await feeStrategy.computeRolloverFees.returns(["0", "0"]); + + const PricingStrategy = await ethers.getContractFactory("UnitPricingStrategy"); + const pricingStrategy = await smock.fake(PricingStrategy); + await pricingStrategy.decimals.returns(8); + await pricingStrategy.computeMatureTranchePrice.returns(toPriceFixedPtAmt("1")); + await pricingStrategy.computeTranchePrice.returns(toPriceFixedPtAmt("1")); + + const DiscountStrategy = await ethers.getContractFactory("TrancheClassDiscountStrategy"); + const discountStrategy = await smock.fake(DiscountStrategy); + await discountStrategy.decimals.returns(18); + await discountStrategy.computeTrancheDiscount + .whenCalledWith(collateralToken.address) + .returns(toDiscountFixedPtAmt("1")); + + const PerpetualTranche = await ethers.getContractFactory("PerpetualTranche"); + perp = await upgrades.deployProxy( + PerpetualTranche.connect(deployer), + [ + "PerpetualTranche", + "PERP", + collateralToken.address, + issuer.address, + feeStrategy.address, + pricingStrategy.address, + discountStrategy.address, + ], + { + initializer: "init(string,string,address,address,address,address,address)", + }, + ); + + await feeStrategy.feeToken.returns(perp.address); + await perp.updateTolerableTrancheMaturity(1200, 4800); + await pricingStrategy.computeTranchePrice.returns(toPriceFixedPtAmt("1")); + await discountStrategy.computeTrancheDiscount.returns(toDiscountFixedPtAmt("1")); + await advancePerpQueueToBondMaturity(perp, await getDepositBond(perp)); + + const bond = await getDepositBond(perp); + const tranches = await getTranches(bond); + await depositIntoBond(bond, toFixedPtAmt("1000"), deployer); + await tranches[0].approve(perp.address, toFixedPtAmt("200")); + await perp.deposit(tranches[0].address, toFixedPtAmt("200")); + await advancePerpQueueToBondMaturity(perp, bond); + + await mintCollteralToken(collateralToken, toFixedPtAmt("100000"), deployer); + const RolloverVault = await ethers.getContractFactory("RolloverVault"); + vault = await upgrades.deployProxy(RolloverVault.connect(deployer)); + await vault.init("RolloverVault", "VSHARE", perp.address); + await collateralToken.transfer(vault.address, toFixedPtAmt("1000")); + await vault.deploy(); + expect(await vault.deployedCount()).to.eq(2); + }); + it("should revert", async function () { + await expect( + vault.transferERC20(await vault.deployedAt(0), toAddress, toFixedPtAmt("100")), + ).to.be.revertedWithCustomError(vault, "UnauthorizedTransferOut"); + await expect( + vault.transferERC20(await vault.deployedAt(1), toAddress, toFixedPtAmt("100")), + ).to.be.revertedWithCustomError(vault, "UnauthorizedTransferOut"); + }); + }); + }); +}); diff --git a/spot-contracts/test/vaults/RolloverVault_deploy.ts b/spot-contracts/test/vaults/RolloverVault_deploy.ts new file mode 100644 index 00000000..3bd54398 --- /dev/null +++ b/spot-contracts/test/vaults/RolloverVault_deploy.ts @@ -0,0 +1,1059 @@ +import { expect, use } from "chai"; +import { network, ethers, upgrades } from "hardhat"; +import { Contract, Signer, Transaction } from "ethers"; +import { smock } from "@defi-wonderland/smock"; + +import { + setupCollateralToken, + mintCollteralToken, + setupBondFactory, + depositIntoBond, + bondAt, + getTranches, + toFixedPtAmt, + toDiscountFixedPtAmt, + toPriceFixedPtAmt, + getDepositBond, + advancePerpQueue, + advancePerpQueueToBondMaturity, + advancePerpQueueToRollover, + checkReserveComposition, + checkVaultAssetComposition, +} from "../helpers"; +use(smock.matchers); + +let vault: Contract; +let perp: Contract; +let bondFactory: Contract; +let collateralToken: Contract; +let issuer: Contract; +let feeStrategy: Contract; +let pricingStrategy: Contract; +let discountStrategy: Contract; +let deployer: Signer; +let reserveTranches: Contract[][] = []; +let rolloverInBond: Contract; +let rolloverInTranches: Contract; + +describe("RolloverVault", function () { + beforeEach(async function () { + await network.provider.send("hardhat_reset"); + + const accounts = await ethers.getSigners(); + deployer = accounts[0]; + + bondFactory = await setupBondFactory(); + ({ collateralToken } = await setupCollateralToken("Bitcoin", "BTC")); + const BondIssuer = await ethers.getContractFactory("BondIssuer"); + issuer = await BondIssuer.deploy(bondFactory.address, collateralToken.address); + await issuer.init(4800, [200, 300, 500], 1200, 0); + + const FeeStrategy = await ethers.getContractFactory("BasicFeeStrategy"); + feeStrategy = await smock.fake(FeeStrategy); + await feeStrategy.computeMintFees.returns(["0", "0"]); + await feeStrategy.computeBurnFees.returns(["0", "0"]); + await feeStrategy.computeRolloverFees.returns(["0", "0"]); + + const PricingStrategy = await ethers.getContractFactory("UnitPricingStrategy"); + pricingStrategy = await smock.fake(PricingStrategy); + await pricingStrategy.decimals.returns(8); + await pricingStrategy.computeMatureTranchePrice.returns(toPriceFixedPtAmt("1")); + await pricingStrategy.computeTranchePrice.returns(toPriceFixedPtAmt("1")); + + const DiscountStrategy = await ethers.getContractFactory("TrancheClassDiscountStrategy"); + discountStrategy = await smock.fake(DiscountStrategy); + await discountStrategy.decimals.returns(18); + await discountStrategy.computeTrancheDiscount + .whenCalledWith(collateralToken.address) + .returns(toDiscountFixedPtAmt("1")); + + const PerpetualTranche = await ethers.getContractFactory("PerpetualTranche"); + perp = await upgrades.deployProxy( + PerpetualTranche.connect(deployer), + [ + "PerpetualTranche", + "PERP", + collateralToken.address, + issuer.address, + feeStrategy.address, + pricingStrategy.address, + discountStrategy.address, + ], + { + initializer: "init(string,string,address,address,address,address,address)", + }, + ); + + await feeStrategy.feeToken.returns(perp.address); + + await perp.updateTolerableTrancheMaturity(1200, 4800); + await advancePerpQueueToBondMaturity(perp, await getDepositBond(perp)); + + reserveTranches = []; + for (let i = 0; i < 4; i++) { + const bond = await getDepositBond(perp); + const tranches = await getTranches(bond); + await depositIntoBond(bond, toFixedPtAmt("1000"), deployer); + + await pricingStrategy.computeTranchePrice.whenCalledWith(tranches[0].address).returns(toPriceFixedPtAmt("1")); + await discountStrategy.computeTrancheDiscount + .whenCalledWith(tranches[0].address) + .returns(toDiscountFixedPtAmt("1")); + await tranches[0].approve(perp.address, toFixedPtAmt("200")); + await perp.deposit(tranches[0].address, toFixedPtAmt("200")); + + await pricingStrategy.computeTranchePrice.whenCalledWith(tranches[1].address).returns(toPriceFixedPtAmt("1")); + await discountStrategy.computeTrancheDiscount + .whenCalledWith(tranches[1].address) + .returns(toDiscountFixedPtAmt("1")); + await tranches[1].approve(perp.address, toFixedPtAmt("300")); + await perp.deposit(tranches[1].address, toFixedPtAmt("300")); + + reserveTranches.push(tranches[0]); + reserveTranches.push(tranches[1]); + await advancePerpQueue(perp, 1200); + } + + await checkReserveComposition( + perp, + [collateralToken, ...reserveTranches.slice(-6)], + [ + toFixedPtAmt("500"), + toFixedPtAmt("200"), + toFixedPtAmt("300"), + toFixedPtAmt("200"), + toFixedPtAmt("300"), + toFixedPtAmt("200"), + toFixedPtAmt("300"), + ], + ); + + rolloverInBond = await bondAt(await perp.callStatic.getDepositBond()); + rolloverInTranches = await getTranches(rolloverInBond); + await pricingStrategy.computeTranchePrice + .whenCalledWith(rolloverInTranches[0].address) + .returns(toPriceFixedPtAmt("1")); + await discountStrategy.computeTrancheDiscount + .whenCalledWith(rolloverInTranches[0].address) + .returns(toDiscountFixedPtAmt("1")); + await pricingStrategy.computeTranchePrice + .whenCalledWith(rolloverInTranches[1].address) + .returns(toPriceFixedPtAmt("1")); + await discountStrategy.computeTrancheDiscount + .whenCalledWith(rolloverInTranches[1].address) + .returns(toDiscountFixedPtAmt("0")); + await pricingStrategy.computeTranchePrice + .whenCalledWith(rolloverInTranches[2].address) + .returns(toPriceFixedPtAmt("1")); + await discountStrategy.computeTrancheDiscount + .whenCalledWith(rolloverInTranches[2].address) + .returns(toDiscountFixedPtAmt("0")); + + await mintCollteralToken(collateralToken, toFixedPtAmt("100000"), deployer); + const RolloverVault = await ethers.getContractFactory("RolloverVault"); + vault = await upgrades.deployProxy(RolloverVault.connect(deployer)); + await collateralToken.approve(vault.address, toFixedPtAmt("1")); + await vault.init("RolloverVault", "VSHARE", perp.address); + await checkVaultAssetComposition(vault, [collateralToken, perp], [toFixedPtAmt("0"), toFixedPtAmt("0")]); + expect(await vault.deployedCount()).to.eq(0); + }); + + afterEach(async function () { + await network.provider.send("hardhat_reset"); + }); + + describe("#deploy", function () { + describe("when usable balance is zero", function () { + it("should revert", async function () { + await expect(vault.deploy()).to.be.revertedWithCustomError(vault, "NoDeployment"); + }); + }); + + describe("when no trancheIn", function () { + beforeEach(async function () { + await pricingStrategy.computeTranchePrice + .whenCalledWith(rolloverInTranches[0].address) + .returns(toPriceFixedPtAmt("1")); + await discountStrategy.computeTrancheDiscount + .whenCalledWith(rolloverInTranches[0].address) + .returns(toDiscountFixedPtAmt("0")); + await collateralToken.transfer(vault.address, toFixedPtAmt("10")); + }); + it("should revert", async function () { + await expect(vault.deploy()).to.be.revertedWithCustomError(vault, "NoDeployment"); + }); + }); + + describe("when one trancheIn one tokenOut (mature tranche)", function () { + let newTranchesIn; + beforeEach(async function () { + await advancePerpQueueToBondMaturity(perp, rolloverInBond); + const newBondIn = await bondAt(await perp.callStatic.getDepositBond()); + newTranchesIn = await getTranches(newBondIn); + await pricingStrategy.computeTranchePrice + .whenCalledWith(newTranchesIn[0].address) + .returns(toPriceFixedPtAmt("1")); + await discountStrategy.computeTrancheDiscount + .whenCalledWith(newTranchesIn[0].address) + .returns(toDiscountFixedPtAmt("1")); + await checkReserveComposition(perp, [collateralToken], [toFixedPtAmt("2000")]); + }); + + describe("when balance covers just 1 token", function () { + beforeEach(async function () { + await collateralToken.transfer(vault.address, toFixedPtAmt("10")); + }); + it("should rollover", async function () { + await expect(vault.deploy()).not.to.be.reverted; + await checkVaultAssetComposition( + vault, + [collateralToken, newTranchesIn[1], newTranchesIn[2], perp], + [toFixedPtAmt("2"), toFixedPtAmt("3"), toFixedPtAmt("5"), toFixedPtAmt("0")], + ); + await checkReserveComposition( + perp, + [collateralToken, newTranchesIn[0]], + [toFixedPtAmt("1998"), toFixedPtAmt("2")], + ); + }); + }); + + describe("when balance covers just 1 token exactly", function () { + beforeEach(async function () { + await collateralToken.transfer(vault.address, toFixedPtAmt("10000")); + }); + it("should rollover", async function () { + await expect(vault.deploy()).not.to.be.reverted; + await checkVaultAssetComposition( + vault, + [collateralToken, newTranchesIn[1], newTranchesIn[2], perp], + [toFixedPtAmt("2000"), toFixedPtAmt("3000"), toFixedPtAmt("5000"), toFixedPtAmt("0")], + ); + await checkReserveComposition( + perp, + [collateralToken, newTranchesIn[0]], + [toFixedPtAmt("0"), toFixedPtAmt("2000")], + ); + }); + }); + }); + + describe("when many trancheIn one tokenOut (mature tranche)", function () { + let newTranchesIn; + beforeEach(async function () { + await advancePerpQueueToBondMaturity(perp, rolloverInBond); + const newBondIn = await bondAt(await perp.callStatic.getDepositBond()); + newTranchesIn = await getTranches(newBondIn); + await pricingStrategy.computeTranchePrice + .whenCalledWith(newTranchesIn[0].address) + .returns(toPriceFixedPtAmt("1")); + await discountStrategy.computeTrancheDiscount + .whenCalledWith(newTranchesIn[0].address) + .returns(toDiscountFixedPtAmt("1")); + await pricingStrategy.computeTranchePrice + .whenCalledWith(newTranchesIn[1].address) + .returns(toPriceFixedPtAmt("1")); + await discountStrategy.computeTrancheDiscount + .whenCalledWith(newTranchesIn[1].address) + .returns(toDiscountFixedPtAmt("1")); + await checkReserveComposition(perp, [collateralToken], [toFixedPtAmt("2000")]); + }); + + describe("when balance covers just 1 token", function () { + beforeEach(async function () { + await collateralToken.transfer(vault.address, toFixedPtAmt("10")); + }); + it("should rollover", async function () { + await expect(vault.deploy()).not.to.be.reverted; + await checkVaultAssetComposition( + vault, + [collateralToken, newTranchesIn[2], perp], + [toFixedPtAmt("5"), toFixedPtAmt("5"), toFixedPtAmt("0")], + ); + await checkReserveComposition( + perp, + [collateralToken, newTranchesIn[0], newTranchesIn[1]], + [toFixedPtAmt("1995"), toFixedPtAmt("2"), toFixedPtAmt("3")], + ); + }); + }); + + describe("when balance covers just 1 token exactly", function () { + beforeEach(async function () { + await collateralToken.transfer(vault.address, toFixedPtAmt("4000")); + }); + it("should rollover", async function () { + await expect(vault.deploy()).not.to.be.reverted; + await checkVaultAssetComposition( + vault, + [collateralToken, newTranchesIn[2], perp], + [toFixedPtAmt("2000"), toFixedPtAmt("2000"), toFixedPtAmt("0")], + ); + await checkReserveComposition( + perp, + [collateralToken, newTranchesIn[0], newTranchesIn[1]], + [toFixedPtAmt("0"), toFixedPtAmt("800"), toFixedPtAmt("1200")], + ); + }); + }); + }); + + describe("when one trancheIn one tokenOut (near mature tranche)", function () { + let curTranchesIn, newTranchesIn; + beforeEach(async function () { + await advancePerpQueueToBondMaturity(perp, await bondAt(reserveTranches[4].bond())); + const curBondIn = await bondAt(await perp.callStatic.getDepositBond()); + curTranchesIn = await getTranches(curBondIn); + await pricingStrategy.computeTranchePrice + .whenCalledWith(curTranchesIn[0].address) + .returns(toPriceFixedPtAmt("1")); + await discountStrategy.computeTrancheDiscount + .whenCalledWith(curTranchesIn[0].address) + .returns(toDiscountFixedPtAmt("1")); + + await collateralToken.transfer(vault.address, toFixedPtAmt("10000")); + await vault.deploy(); + + await advancePerpQueueToRollover(perp, curBondIn); + const newBondIn = await bondAt(await perp.callStatic.getDepositBond()); + + newTranchesIn = await getTranches(newBondIn); + await pricingStrategy.computeTranchePrice + .whenCalledWith(newTranchesIn[0].address) + .returns(toPriceFixedPtAmt("1")); + await discountStrategy.computeTrancheDiscount + .whenCalledWith(newTranchesIn[0].address) + .returns(toDiscountFixedPtAmt("1")); + + await checkVaultAssetComposition( + vault, + [collateralToken, reserveTranches[6], reserveTranches[7], curTranchesIn[1], curTranchesIn[2], perp], + [ + toFixedPtAmt("1500"), + toFixedPtAmt("200"), + toFixedPtAmt("300"), + toFixedPtAmt("3000"), + toFixedPtAmt("5000"), + toFixedPtAmt("0"), + ], + ); + await checkReserveComposition( + perp, + [collateralToken, curTranchesIn[0]], + [toFixedPtAmt("0"), toFixedPtAmt("2000")], + ); + }); + + describe("when balance covers just 1 token", function () { + it("should rollover", async function () { + await expect(vault.deploy()).not.to.be.reverted; + await checkVaultAssetComposition( + vault, + [ + collateralToken, + reserveTranches[6], + reserveTranches[7], + curTranchesIn[0], + curTranchesIn[1], + curTranchesIn[2], + newTranchesIn[1], + newTranchesIn[2], + perp, + ], + [ + toFixedPtAmt("0"), + toFixedPtAmt("200"), + toFixedPtAmt("300"), + toFixedPtAmt("300"), + toFixedPtAmt("3000"), + toFixedPtAmt("5000"), + toFixedPtAmt("450"), + toFixedPtAmt("750"), + toFixedPtAmt("0"), + ], + ); + await checkReserveComposition( + perp, + [collateralToken, curTranchesIn[0], newTranchesIn[0]], + [toFixedPtAmt("0"), toFixedPtAmt("1700"), toFixedPtAmt("300")], + ); + }); + }); + + describe("when balance covers just 1 token exactly", function () { + beforeEach(async function () { + await collateralToken.transfer(vault.address, toFixedPtAmt("8500")); + }); + it("should rollover", async function () { + await expect(vault.deploy()).not.to.be.reverted; + await checkVaultAssetComposition( + vault, + [ + collateralToken, + reserveTranches[6], + reserveTranches[7], + curTranchesIn[0], + curTranchesIn[1], + curTranchesIn[2], + newTranchesIn[1], + newTranchesIn[2], + perp, + ], + [ + toFixedPtAmt("0"), + toFixedPtAmt("200"), + toFixedPtAmt("300"), + toFixedPtAmt("2000"), + toFixedPtAmt("3000"), + toFixedPtAmt("5000"), + toFixedPtAmt("3000"), + toFixedPtAmt("5000"), + toFixedPtAmt("0"), + ], + ); + await checkReserveComposition( + perp, + [collateralToken, newTranchesIn[0]], + [toFixedPtAmt("0"), toFixedPtAmt("2000")], + ); + }); + }); + }); + + describe("when many trancheIn one tokenOut (near mature tranche)", function () { + let curTranchesIn, newTranchesIn; + beforeEach(async function () { + await advancePerpQueueToBondMaturity(perp, await bondAt(reserveTranches[4].bond())); + const curBondIn = await bondAt(await perp.callStatic.getDepositBond()); + curTranchesIn = await getTranches(curBondIn); + await pricingStrategy.computeTranchePrice + .whenCalledWith(curTranchesIn[0].address) + .returns(toPriceFixedPtAmt("1")); + await discountStrategy.computeTrancheDiscount + .whenCalledWith(curTranchesIn[0].address) + .returns(toDiscountFixedPtAmt("1")); + + await collateralToken.transfer(vault.address, toFixedPtAmt("10000")); + await vault.deploy(); + + await advancePerpQueueToRollover(perp, curBondIn); + const newBondIn = await bondAt(await perp.callStatic.getDepositBond()); + + newTranchesIn = await getTranches(newBondIn); + await pricingStrategy.computeTranchePrice + .whenCalledWith(newTranchesIn[0].address) + .returns(toPriceFixedPtAmt("1")); + await discountStrategy.computeTrancheDiscount + .whenCalledWith(newTranchesIn[0].address) + .returns(toDiscountFixedPtAmt("1")); + await pricingStrategy.computeTranchePrice + .whenCalledWith(newTranchesIn[1].address) + .returns(toPriceFixedPtAmt("1")); + await discountStrategy.computeTrancheDiscount + .whenCalledWith(newTranchesIn[1].address) + .returns(toDiscountFixedPtAmt("1")); + + await checkVaultAssetComposition( + vault, + [collateralToken, reserveTranches[6], reserveTranches[7], curTranchesIn[1], curTranchesIn[2], perp], + [ + toFixedPtAmt("1500"), + toFixedPtAmt("200"), + toFixedPtAmt("300"), + toFixedPtAmt("3000"), + toFixedPtAmt("5000"), + toFixedPtAmt("0"), + ], + ); + await checkReserveComposition( + perp, + [collateralToken, curTranchesIn[0]], + [toFixedPtAmt("0"), toFixedPtAmt("2000")], + ); + }); + + describe("when balance covers just 1 token", function () { + it("should rollover", async function () { + await expect(vault.deploy()).not.to.be.reverted; + await checkVaultAssetComposition( + vault, + [ + collateralToken, + reserveTranches[6], + reserveTranches[7], + curTranchesIn[0], + curTranchesIn[1], + curTranchesIn[2], + newTranchesIn[2], + perp, + ], + [ + toFixedPtAmt("0"), + toFixedPtAmt("200"), + toFixedPtAmt("300"), + toFixedPtAmt("750"), + toFixedPtAmt("3000"), + toFixedPtAmt("5000"), + toFixedPtAmt("750"), + toFixedPtAmt("0"), + ], + ); + await checkReserveComposition( + perp, + [collateralToken, curTranchesIn[0], newTranchesIn[0], newTranchesIn[1]], + [toFixedPtAmt("0"), toFixedPtAmt("1250"), toFixedPtAmt("300"), toFixedPtAmt("450")], + ); + }); + }); + + describe("when balance covers just 1 token exactly", function () { + beforeEach(async function () { + await collateralToken.transfer(vault.address, toFixedPtAmt("2500")); + }); + it("should rollover", async function () { + await expect(vault.deploy()).not.to.be.reverted; + await checkVaultAssetComposition( + vault, + [ + collateralToken, + reserveTranches[6], + reserveTranches[7], + curTranchesIn[0], + curTranchesIn[1], + curTranchesIn[2], + newTranchesIn[2], + perp, + ], + [ + toFixedPtAmt("0"), + toFixedPtAmt("200"), + toFixedPtAmt("300"), + toFixedPtAmt("2000"), + toFixedPtAmt("3000"), + toFixedPtAmt("5000"), + toFixedPtAmt("2000"), + toFixedPtAmt("0"), + ], + ); + await checkReserveComposition( + perp, + [collateralToken, newTranchesIn[0], newTranchesIn[1]], + [toFixedPtAmt("0"), toFixedPtAmt("800"), toFixedPtAmt("1200")], + ); + }); + }); + }); + + describe("when one trancheIn many tokenOut", function () { + describe("when balance covers just 1 token", function () { + beforeEach(async function () { + await collateralToken.transfer(vault.address, toFixedPtAmt("10")); + }); + it("should rollover", async function () { + await expect(vault.deploy()).not.to.be.reverted; + await checkVaultAssetComposition( + vault, + [collateralToken, rolloverInTranches[1], rolloverInTranches[2], perp], + [toFixedPtAmt("2"), toFixedPtAmt("3"), toFixedPtAmt("5"), toFixedPtAmt("0")], + ); + await checkReserveComposition( + perp, + [collateralToken, ...reserveTranches.slice(-6), rolloverInTranches[0]], + [ + toFixedPtAmt("498"), + toFixedPtAmt("200"), + toFixedPtAmt("300"), + toFixedPtAmt("200"), + toFixedPtAmt("300"), + toFixedPtAmt("200"), + toFixedPtAmt("300"), + toFixedPtAmt("2"), + ], + ); + }); + }); + + describe("when balance covers just 1 token exactly", function () { + beforeEach(async function () { + await collateralToken.transfer(vault.address, toFixedPtAmt("2500")); + }); + it("should rollover", async function () { + await expect(vault.deploy()).not.to.be.reverted; + await checkVaultAssetComposition( + vault, + [collateralToken, rolloverInTranches[1], rolloverInTranches[2], perp], + [toFixedPtAmt("500"), toFixedPtAmt("750"), toFixedPtAmt("1250"), toFixedPtAmt("0")], + ); + await checkReserveComposition( + perp, + [collateralToken, ...reserveTranches.slice(-6), rolloverInTranches[0]], + [ + toFixedPtAmt("0"), + toFixedPtAmt("200"), + toFixedPtAmt("300"), + toFixedPtAmt("200"), + toFixedPtAmt("300"), + toFixedPtAmt("200"), + toFixedPtAmt("300"), + toFixedPtAmt("500"), + ], + ); + }); + }); + + describe("when balance covers many tokens", function () { + beforeEach(async function () { + await collateralToken.transfer(vault.address, toFixedPtAmt("4000")); + }); + it("should rollover", async function () { + await expect(vault.deploy()).not.to.be.reverted; + await checkVaultAssetComposition( + vault, + [ + collateralToken, + reserveTranches[2], + reserveTranches[3], + rolloverInTranches[1], + rolloverInTranches[2], + perp, + ], + [ + toFixedPtAmt("500"), + toFixedPtAmt("200"), + toFixedPtAmt("100"), + toFixedPtAmt("1200"), + toFixedPtAmt("2000"), + toFixedPtAmt("0"), + ], + ); + await checkReserveComposition( + perp, + [collateralToken, ...reserveTranches.slice(-5), rolloverInTranches[0]], + [ + toFixedPtAmt("0"), + toFixedPtAmt("200"), + toFixedPtAmt("200"), + toFixedPtAmt("300"), + toFixedPtAmt("200"), + toFixedPtAmt("300"), + toFixedPtAmt("800"), + ], + ); + }); + }); + + describe("when balance covers all tokens", function () { + beforeEach(async function () { + await collateralToken.transfer(vault.address, toFixedPtAmt("5000")); + }); + it("should rollover", async function () { + await expect(vault.deploy()).not.to.be.reverted; + await checkVaultAssetComposition( + vault, + [ + collateralToken, + reserveTranches[2], + reserveTranches[3], + rolloverInTranches[1], + rolloverInTranches[2], + perp, + ], + [ + toFixedPtAmt("500"), + toFixedPtAmt("200"), + toFixedPtAmt("300"), + toFixedPtAmt("1500"), + toFixedPtAmt("2500"), + toFixedPtAmt("0"), + ], + ); + await checkReserveComposition( + perp, + [collateralToken, ...reserveTranches.slice(-4), rolloverInTranches[0]], + [ + toFixedPtAmt("0"), + toFixedPtAmt("200"), + toFixedPtAmt("300"), + toFixedPtAmt("200"), + toFixedPtAmt("300"), + toFixedPtAmt("1000"), + ], + ); + }); + }); + + describe("when balance exceeds all tokens", function () { + beforeEach(async function () { + await collateralToken.transfer(vault.address, toFixedPtAmt("6000")); + }); + it("should rollover", async function () { + await expect(vault.deploy()).not.to.be.reverted; + await checkVaultAssetComposition( + vault, + [ + collateralToken, + reserveTranches[2], + reserveTranches[3], + rolloverInTranches[0], + rolloverInTranches[1], + rolloverInTranches[2], + perp, + ], + [ + toFixedPtAmt("500"), + toFixedPtAmt("200"), + toFixedPtAmt("300"), + toFixedPtAmt("200"), + toFixedPtAmt("1800"), + toFixedPtAmt("3000"), + toFixedPtAmt("0"), + ], + ); + await checkReserveComposition( + perp, + [collateralToken, ...reserveTranches.slice(-4), rolloverInTranches[0]], + [ + toFixedPtAmt("0"), + toFixedPtAmt("200"), + toFixedPtAmt("300"), + toFixedPtAmt("200"), + toFixedPtAmt("300"), + toFixedPtAmt("1000"), + ], + ); + }); + }); + }); + + describe("when many trancheIn many tokenOut", function () { + beforeEach(async function () { + await pricingStrategy.computeTranchePrice + .whenCalledWith(rolloverInTranches[1].address) + .returns(toPriceFixedPtAmt("1")); + await discountStrategy.computeTrancheDiscount + .whenCalledWith(rolloverInTranches[1].address) + .returns(toDiscountFixedPtAmt("1")); + }); + + describe("when balance covers just 1 token", async function () { + beforeEach(async function () { + await collateralToken.transfer(vault.address, toFixedPtAmt("10")); + }); + it("should rollover", async function () { + await expect(vault.deploy()).not.to.be.reverted; + await checkVaultAssetComposition( + vault, + [collateralToken, rolloverInTranches[2], perp], + [toFixedPtAmt("5"), toFixedPtAmt("5"), toFixedPtAmt("0")], + ); + await checkReserveComposition( + perp, + [collateralToken, ...reserveTranches.slice(-6), rolloverInTranches[0], rolloverInTranches[1]], + [ + toFixedPtAmt("495"), + toFixedPtAmt("200"), + toFixedPtAmt("300"), + toFixedPtAmt("200"), + toFixedPtAmt("300"), + toFixedPtAmt("200"), + toFixedPtAmt("300"), + toFixedPtAmt("2"), + toFixedPtAmt("3"), + ], + ); + }); + }); + + describe("when balance covers just 1 token exactly", async function () { + beforeEach(async function () { + await collateralToken.transfer(vault.address, toFixedPtAmt("1000")); + }); + it("should rollover", async function () { + await expect(vault.deploy()).not.to.be.reverted; + await checkVaultAssetComposition( + vault, + [collateralToken, rolloverInTranches[2], perp], + [toFixedPtAmt("500"), toFixedPtAmt("500"), toFixedPtAmt("0")], + ); + await checkReserveComposition( + perp, + [collateralToken, ...reserveTranches.slice(-6), rolloverInTranches[0], rolloverInTranches[1]], + [ + toFixedPtAmt("0"), + toFixedPtAmt("200"), + toFixedPtAmt("300"), + toFixedPtAmt("200"), + toFixedPtAmt("300"), + toFixedPtAmt("200"), + toFixedPtAmt("300"), + toFixedPtAmt("200"), + toFixedPtAmt("300"), + ], + ); + }); + }); + + describe("when balance covers many tokens", async function () { + beforeEach(async function () { + await collateralToken.transfer(vault.address, toFixedPtAmt("1500")); + }); + it("should rollover", async function () { + await expect(vault.deploy()).not.to.be.reverted; + await checkVaultAssetComposition( + vault, + [collateralToken, reserveTranches[2], reserveTranches[3], rolloverInTranches[2], perp], + [toFixedPtAmt("500"), toFixedPtAmt("200"), toFixedPtAmt("50"), toFixedPtAmt("750"), toFixedPtAmt("0")], + ); + await checkReserveComposition( + perp, + [collateralToken, ...reserveTranches.slice(-5), rolloverInTranches[0], rolloverInTranches[1]], + [ + toFixedPtAmt("0"), + toFixedPtAmt("250"), + toFixedPtAmt("200"), + toFixedPtAmt("300"), + toFixedPtAmt("200"), + toFixedPtAmt("300"), + toFixedPtAmt("300"), + toFixedPtAmt("450"), + ], + ); + }); + }); + + describe("when balance covers all tokens", async function () { + beforeEach(async function () { + await collateralToken.transfer(vault.address, toFixedPtAmt("2000")); + }); + it("should rollover", async function () { + await expect(vault.deploy()).not.to.be.reverted; + await checkVaultAssetComposition( + vault, + [collateralToken, reserveTranches[2], reserveTranches[3], rolloverInTranches[2], perp], + [toFixedPtAmt("500"), toFixedPtAmt("200"), toFixedPtAmt("300"), toFixedPtAmt("1000"), toFixedPtAmt("0")], + ); + await checkReserveComposition( + perp, + [collateralToken, ...reserveTranches.slice(-4), rolloverInTranches[0], rolloverInTranches[1]], + [ + toFixedPtAmt("0"), + toFixedPtAmt("200"), + toFixedPtAmt("300"), + toFixedPtAmt("200"), + toFixedPtAmt("300"), + toFixedPtAmt("400"), + toFixedPtAmt("600"), + ], + ); + }); + }); + + describe("when balance exceeds all tokens", async function () { + beforeEach(async function () { + await collateralToken.transfer(vault.address, toFixedPtAmt("6000")); + }); + it("should rollover", async function () { + await expect(vault.deploy()).not.to.be.reverted; + await checkVaultAssetComposition( + vault, + [ + collateralToken, + reserveTranches[2], + reserveTranches[3], + rolloverInTranches[0], + rolloverInTranches[1], + rolloverInTranches[2], + perp, + ], + [ + toFixedPtAmt("500"), + toFixedPtAmt("200"), + toFixedPtAmt("300"), + toFixedPtAmt("200"), + toFixedPtAmt("1800"), + toFixedPtAmt("3000"), + toFixedPtAmt("0"), + ], + ); + await checkReserveComposition( + perp, + [collateralToken, ...reserveTranches.slice(-4), rolloverInTranches[0]], + [ + toFixedPtAmt("0"), + toFixedPtAmt("200"), + toFixedPtAmt("300"), + toFixedPtAmt("200"), + toFixedPtAmt("300"), + toFixedPtAmt("1000"), + ], + ); + }); + }); + }); + + describe("when many trancheIn many tokenOut with different yields", function () { + beforeEach(async function () { + await pricingStrategy.computeTranchePrice + .whenCalledWith(rolloverInTranches[1].address) + .returns(toPriceFixedPtAmt("1")); + await discountStrategy.computeTrancheDiscount + .whenCalledWith(rolloverInTranches[1].address) + .returns(toDiscountFixedPtAmt("0.75")); + }); + + describe("when balance covers many tokens", async function () { + beforeEach(async function () { + await collateralToken.transfer(vault.address, toFixedPtAmt("1500")); + }); + it("should rollover", async function () { + await expect(vault.deploy()).not.to.be.reverted; + await checkVaultAssetComposition( + vault, + [collateralToken, reserveTranches[2], rolloverInTranches[2], perp], + [toFixedPtAmt("500"), toFixedPtAmt("137.5"), toFixedPtAmt("750"), toFixedPtAmt("0")], + ); + await checkReserveComposition( + perp, + [collateralToken, ...reserveTranches.slice(-6), rolloverInTranches[0], rolloverInTranches[1]], + [ + toFixedPtAmt("0"), + toFixedPtAmt("62.5"), + toFixedPtAmt("300"), + toFixedPtAmt("200"), + toFixedPtAmt("300"), + toFixedPtAmt("200"), + toFixedPtAmt("300"), + toFixedPtAmt("300"), + toFixedPtAmt("450"), + ], + ); + }); + }); + + describe("when balance covers all tokens", async function () { + beforeEach(async function () { + await collateralToken.transfer(vault.address, toFixedPtAmt("3500")); + }); + it("should rollover", async function () { + await expect(vault.deploy()).not.to.be.reverted; + await checkVaultAssetComposition( + vault, + [ + collateralToken, + reserveTranches[2], + reserveTranches[3], + rolloverInTranches[1], + rolloverInTranches[2], + perp, + ], + [ + toFixedPtAmt("500"), + toFixedPtAmt("200"), + toFixedPtAmt("300"), + toFixedPtAmt("650"), + toFixedPtAmt("1750"), + toFixedPtAmt("0"), + ], + ); + await checkReserveComposition( + perp, + [collateralToken, ...reserveTranches.slice(-4), rolloverInTranches[0], rolloverInTranches[1]], + [ + toFixedPtAmt("0"), + toFixedPtAmt("200"), + toFixedPtAmt("300"), + toFixedPtAmt("200"), + toFixedPtAmt("300"), + toFixedPtAmt("700"), + toFixedPtAmt("400"), + ], + ); + }); + }); + }); + + describe("when rollover yield is rewarded", function () { + beforeEach(async function () { + await pricingStrategy.computeTranchePrice + .whenCalledWith(rolloverInTranches[1].address) + .returns(toPriceFixedPtAmt("1")); + await discountStrategy.computeTrancheDiscount + .whenCalledWith(rolloverInTranches[1].address) + .returns(toDiscountFixedPtAmt("1")); + + await feeStrategy.computeRolloverFees.returns([toFixedPtAmt("-25"), "0"]); + await collateralToken.transfer(vault.address, toFixedPtAmt("1500")); + }); + + it("should rollover", async function () { + await expect(vault.deploy()).not.to.be.reverted; + await checkVaultAssetComposition( + vault, + [collateralToken, reserveTranches[2], reserveTranches[3], rolloverInTranches[2], perp], + [toFixedPtAmt("500"), toFixedPtAmt("200"), toFixedPtAmt("50"), toFixedPtAmt("750"), toFixedPtAmt("100")], + ); + await checkReserveComposition( + perp, + [collateralToken, ...reserveTranches.slice(-5), rolloverInTranches[0], rolloverInTranches[1]], + [ + toFixedPtAmt("0"), + toFixedPtAmt("250"), + toFixedPtAmt("200"), + toFixedPtAmt("300"), + toFixedPtAmt("200"), + toFixedPtAmt("300"), + toFixedPtAmt("300"), + toFixedPtAmt("450"), + ], + ); + }); + }); + + describe("typical deploy", function () { + let tx: Transaction; + beforeEach(async function () { + await pricingStrategy.computeTranchePrice + .whenCalledWith(rolloverInTranches[1].address) + .returns(toPriceFixedPtAmt("1")); + await discountStrategy.computeTrancheDiscount + .whenCalledWith(rolloverInTranches[1].address) + .returns(toDiscountFixedPtAmt("0.75")); + + await feeStrategy.computeRolloverFees.returns([toFixedPtAmt("-5"), "0"]); + await collateralToken.transfer(vault.address, toFixedPtAmt("1500")); + tx = vault.deploy(); + await tx; + }); + + it("should tranche and rollover", async function () { + // Tranche + await expect(tx).to.emit(vault, "AssetSynced").withArgs(rolloverInTranches[0].address, toFixedPtAmt("300")); + await expect(tx).to.emit(vault, "AssetSynced").withArgs(rolloverInTranches[1].address, toFixedPtAmt("450")); + await expect(tx).to.emit(vault, "AssetSynced").withArgs(rolloverInTranches[2].address, toFixedPtAmt("750")); + + // Roll rollIn[0] for collateralToken + await expect(tx).to.emit(vault, "AssetSynced").withArgs(rolloverInTranches[0].address, toFixedPtAmt("0")); + await expect(tx).to.emit(vault, "AssetSynced").withArgs(collateralToken.address, toFixedPtAmt("300")); + + // Roll rollIn[1] for collateralToken + await expect(tx) + .to.emit(vault, "AssetSynced") + .withArgs(rolloverInTranches[1].address, toFixedPtAmt("183.333333333333333334")); + await expect(tx).to.emit(vault, "AssetSynced").withArgs(collateralToken.address, toFixedPtAmt("500")); + + // Roll rollIn[1] for reserve[2] + await expect(tx).to.emit(vault, "AssetSynced").withArgs(rolloverInTranches[1].address, toFixedPtAmt("0")); + await expect(tx).to.emit(vault, "AssetSynced").withArgs(reserveTranches[2].address, toFixedPtAmt("137.5")); + + // rewards + await expect(tx).to.emit(vault, "AssetSynced").withArgs(perp.address, toFixedPtAmt("15")); + }); + + it("should update the list of deployed assets", async function () { + expect(await vault.deployedCount()).to.eq(2); + expect(await vault.deployedAt(0)).to.eq(rolloverInTranches[2].address); + expect(await vault.deployedAt(1)).to.eq(reserveTranches[2].address); + }); + }); + }); +}); diff --git a/spot-contracts/test/vaults/RolloverVault_deposit_redeem.ts b/spot-contracts/test/vaults/RolloverVault_deposit_redeem.ts new file mode 100644 index 00000000..2bbae565 --- /dev/null +++ b/spot-contracts/test/vaults/RolloverVault_deposit_redeem.ts @@ -0,0 +1,691 @@ +import { expect, use } from "chai"; +import { network, ethers, upgrades } from "hardhat"; +import { Contract, Signer, Transaction, constants, BigNumber } from "ethers"; +import { smock } from "@defi-wonderland/smock"; + +import { + setupCollateralToken, + mintCollteralToken, + setupBondFactory, + depositIntoBond, + bondAt, + getTranches, + toFixedPtAmt, + toDiscountFixedPtAmt, + toPriceFixedPtAmt, + getDepositBond, + advancePerpQueue, + advancePerpQueueToBondMaturity, + checkReserveComposition, + rebase, +} from "../helpers"; +use(smock.matchers); + +let deployer: Signer; +let deployerAddress: string; +let otherUser: Signer; +let otherUserAddress: string; +let vault: Contract; +let perp: Contract; +let bondFactory: Contract; +let collateralToken: Contract; +let rebaseOracle: Contract; +let issuer: Contract; +let feeStrategy: Contract; +let pricingStrategy: Contract; +let discountStrategy: Contract; + +let reserveTranches: Contract[][] = []; +let rolloverInBond: Contract; +let rolloverInTranches: Contract; + +describe("RolloverVault", function () { + beforeEach(async function () { + await network.provider.send("hardhat_reset"); + + const accounts = await ethers.getSigners(); + deployer = accounts[0]; + deployerAddress = await deployer.getAddress(); + otherUser = accounts[1]; + otherUserAddress = await otherUser.getAddress(); + + bondFactory = await setupBondFactory(); + ({ collateralToken, rebaseOracle } = await setupCollateralToken("Bitcoin", "BTC")); + const BondIssuer = await ethers.getContractFactory("BondIssuer"); + issuer = await BondIssuer.deploy(bondFactory.address, collateralToken.address); + await issuer.init(4800, [200, 300, 500], 1200, 0); + + const FeeStrategy = await ethers.getContractFactory("BasicFeeStrategy"); + feeStrategy = await smock.fake(FeeStrategy); + await feeStrategy.computeMintFees.returns(["0", "0"]); + await feeStrategy.computeBurnFees.returns(["0", "0"]); + await feeStrategy.computeRolloverFees.returns(["0", "0"]); + + const PricingStrategy = await ethers.getContractFactory("UnitPricingStrategy"); + pricingStrategy = await smock.fake(PricingStrategy); + await pricingStrategy.decimals.returns(8); + await pricingStrategy.computeMatureTranchePrice.returns(toPriceFixedPtAmt("1")); + await pricingStrategy.computeTranchePrice.returns(toPriceFixedPtAmt("1")); + + const DiscountStrategy = await ethers.getContractFactory("TrancheClassDiscountStrategy"); + discountStrategy = await smock.fake(DiscountStrategy); + await discountStrategy.decimals.returns(18); + await discountStrategy.computeTrancheDiscount + .whenCalledWith(collateralToken.address) + .returns(toDiscountFixedPtAmt("1")); + + const PerpetualTranche = await ethers.getContractFactory("PerpetualTranche"); + perp = await upgrades.deployProxy( + PerpetualTranche.connect(deployer), + [ + "PerpetualTranche", + "PERP", + collateralToken.address, + issuer.address, + feeStrategy.address, + pricingStrategy.address, + discountStrategy.address, + ], + { + initializer: "init(string,string,address,address,address,address,address)", + }, + ); + + await feeStrategy.feeToken.returns(perp.address); + + await perp.updateTolerableTrancheMaturity(1200, 4800); + await advancePerpQueueToBondMaturity(perp, await getDepositBond(perp)); + + reserveTranches = []; + for (let i = 0; i < 3; i++) { + const bond = await getDepositBond(perp); + const tranches = await getTranches(bond); + await depositIntoBond(bond, toFixedPtAmt("1000"), deployer); + + await pricingStrategy.computeTranchePrice.whenCalledWith(tranches[0].address).returns(toPriceFixedPtAmt("1")); + await discountStrategy.computeTrancheDiscount + .whenCalledWith(tranches[0].address) + .returns(toDiscountFixedPtAmt("1")); + await tranches[0].approve(perp.address, toFixedPtAmt("200")); + await perp.deposit(tranches[0].address, toFixedPtAmt("200")); + + await pricingStrategy.computeTranchePrice.whenCalledWith(tranches[1].address).returns(toPriceFixedPtAmt("1")); + await discountStrategy.computeTrancheDiscount + .whenCalledWith(tranches[1].address) + .returns(toDiscountFixedPtAmt("1")); + await tranches[1].approve(perp.address, toFixedPtAmt("300")); + await perp.deposit(tranches[1].address, toFixedPtAmt("300")); + + reserveTranches.push(tranches[0]); + reserveTranches.push(tranches[1]); + await advancePerpQueue(perp, 1200); + } + + await checkReserveComposition( + perp, + [collateralToken, ...reserveTranches], + [ + toFixedPtAmt("0"), + toFixedPtAmt("200"), + toFixedPtAmt("300"), + toFixedPtAmt("200"), + toFixedPtAmt("300"), + toFixedPtAmt("200"), + toFixedPtAmt("300"), + ], + ); + + rolloverInBond = await bondAt(await perp.callStatic.getDepositBond()); + rolloverInTranches = await getTranches(rolloverInBond); + await pricingStrategy.computeTranchePrice + .whenCalledWith(rolloverInTranches[0].address) + .returns(toPriceFixedPtAmt("1")); + await discountStrategy.computeTrancheDiscount + .whenCalledWith(rolloverInTranches[0].address) + .returns(toDiscountFixedPtAmt("1")); + await pricingStrategy.computeTranchePrice + .whenCalledWith(rolloverInTranches[1].address) + .returns(toPriceFixedPtAmt("1")); + await discountStrategy.computeTrancheDiscount + .whenCalledWith(rolloverInTranches[1].address) + .returns(toDiscountFixedPtAmt("0")); + await pricingStrategy.computeTranchePrice + .whenCalledWith(rolloverInTranches[2].address) + .returns(toPriceFixedPtAmt("1")); + await discountStrategy.computeTrancheDiscount + .whenCalledWith(rolloverInTranches[2].address) + .returns(toDiscountFixedPtAmt("0")); + + await mintCollteralToken(collateralToken, toFixedPtAmt("100000"), deployer); + const RolloverVault = await ethers.getContractFactory("RolloverVault"); + vault = await upgrades.deployProxy(RolloverVault.connect(deployer)); + await collateralToken.approve(vault.address, toFixedPtAmt("1")); + await vault.init("RolloverVault", "VSHARE", perp.address); + + expect(await vault.deployedCount()).to.eq(0); + expect(await vault.vaultAssetBalance(await vault.underlying())).to.eq(0); + expect(await vault.vaultAssetBalance(await vault.earnedAt(0))).to.eq(0); + }); + + describe("#getTVL", function () { + describe("when vault is empty", function () { + it("should return 0", async function () { + expect(await vault.callStatic.getTVL()).to.eq(0); + }); + }); + + describe("when vault has only usable balance", function () { + beforeEach(async function () { + await collateralToken.transfer(vault.address, toFixedPtAmt("100")); + }); + it("should return tvl", async function () { + expect(await vault.callStatic.getTVL()).to.eq(toFixedPtAmt("100")); + }); + }); + + describe("when vault has only deployed balance", function () { + beforeEach(async function () { + await collateralToken.transfer(vault.address, toFixedPtAmt("100")); + await discountStrategy.computeTrancheDiscount + .whenCalledWith(rolloverInTranches[1].address) + .returns(toDiscountFixedPtAmt("1")); + await discountStrategy.computeTrancheDiscount + .whenCalledWith(rolloverInTranches[2].address) + .returns(toDiscountFixedPtAmt("1")); + await vault.deploy(); + expect(await vault.vaultAssetBalance(await vault.underlying())).to.eq(0); + expect(await vault.deployedCount()).to.eq(1); + }); + it("should return tvl", async function () { + expect(await vault.callStatic.getTVL()).to.eq(toFixedPtAmt("100")); + }); + }); + + describe("when vault has only earned balance", function () { + beforeEach(async function () { + await perp.transfer(vault.address, toFixedPtAmt("100")); + }); + it("should return tvl", async function () { + expect(await vault.callStatic.getTVL()).to.eq(toFixedPtAmt("100")); + }); + }); + + describe("when vault has many balances", function () { + beforeEach(async function () { + await perp.transfer(vault.address, toFixedPtAmt("100")); + await collateralToken.transfer(vault.address, toFixedPtAmt("2000")); + await vault.deploy(); + await collateralToken.transfer(vault.address, toFixedPtAmt("100")); + }); + it("should return tvl", async function () { + expect(await vault.callStatic.getTVL()).to.eq(toFixedPtAmt("2200")); + }); + }); + + describe("when vault has many balances and rebases up", function () { + beforeEach(async function () { + await perp.transfer(vault.address, toFixedPtAmt("100")); + await collateralToken.transfer(vault.address, toFixedPtAmt("2000")); + await vault.deploy(); + await collateralToken.transfer(vault.address, toFixedPtAmt("100")); + await rebase(collateralToken, rebaseOracle, 0.1); + }); + it("should return tvl", async function () { + expect(await vault.callStatic.getTVL()).to.eq(toFixedPtAmt("2410")); + }); + }); + + describe("when vault has many balances and rebases down", function () { + beforeEach(async function () { + await perp.transfer(vault.address, toFixedPtAmt("100")); + await collateralToken.transfer(vault.address, toFixedPtAmt("2000")); + await vault.deploy(); + await collateralToken.transfer(vault.address, toFixedPtAmt("100")); + await rebase(collateralToken, rebaseOracle, -0.1); + }); + it("should return tvl", async function () { + expect(await vault.callStatic.getTVL()).to.eq(toFixedPtAmt("1990")); + }); + }); + + describe("when vault has many balances and rebases down below threshold", function () { + beforeEach(async function () { + await perp.transfer(vault.address, toFixedPtAmt("100")); + await collateralToken.transfer(vault.address, toFixedPtAmt("5000")); + await vault.deploy(); + await collateralToken.transfer(vault.address, toFixedPtAmt("100")); + + await pricingStrategy.computeTranchePrice + .whenCalledWith(reserveTranches[0].address) + .returns(toPriceFixedPtAmt("0.5")); + await pricingStrategy.computeTranchePrice + .whenCalledWith(reserveTranches[1].address) + .returns(toPriceFixedPtAmt("0")); + await pricingStrategy.computeTranchePrice + .whenCalledWith(reserveTranches[2].address) + .returns(toPriceFixedPtAmt("0.5")); + await pricingStrategy.computeTranchePrice + .whenCalledWith(reserveTranches[3].address) + .returns(toPriceFixedPtAmt("0")); + await pricingStrategy.computeTranchePrice + .whenCalledWith(reserveTranches[4].address) + .returns(toPriceFixedPtAmt("0.5")); + await pricingStrategy.computeTranchePrice + .whenCalledWith(reserveTranches[5].address) + .returns(toPriceFixedPtAmt("0")); + await pricingStrategy.computeTranchePrice + .whenCalledWith(rolloverInTranches[0].address) + .returns(toPriceFixedPtAmt("0.5")); + await pricingStrategy.computeTranchePrice + .whenCalledWith(rolloverInTranches[1].address) + .returns(toPriceFixedPtAmt("0")); + await pricingStrategy.computeTranchePrice + .whenCalledWith(rolloverInTranches[2].address) + .returns(toPriceFixedPtAmt("0")); + await rebase(collateralToken, rebaseOracle, -0.9); + }); + it("should return tvl", async function () { + expect(await vault.callStatic.getTVL()).to.eq(toFixedPtAmt("390")); + }); + }); + }); + + describe("#deposit", function () { + let tx: Transaction, noteAmt: BigNumber; + + describe("when total supply = 0", async function () { + beforeEach(async function () { + await collateralToken.approve(vault.address, toFixedPtAmt("100")); + noteAmt = await vault.callStatic.deposit(toFixedPtAmt("100")); + tx = vault.deposit(toFixedPtAmt("100")); + await tx; + }); + it("should transfer underlying", async function () { + await expect(tx) + .to.emit(collateralToken, "Transfer") + .withArgs(deployerAddress, vault.address, toFixedPtAmt("100")); + await expect(tx).to.emit(vault, "AssetSynced").withArgs(collateralToken.address, toFixedPtAmt("100")); + }); + it("should mint notes", async function () { + await expect(tx) + .to.emit(vault, "Transfer") + .withArgs(constants.AddressZero, deployerAddress, toFixedPtAmt("100").mul("1000000")); + expect(await vault.balanceOf(deployerAddress)).to.eq(toFixedPtAmt("100").mul("1000000")); + }); + it("should return the note amount", async function () { + expect(noteAmt).to.eq(toFixedPtAmt("100").mul("1000000")); + }); + }); + + describe("when total supply > 0 and tvl = ts", async function () { + beforeEach(async function () { + await collateralToken.approve(vault.address, toFixedPtAmt("100")); + await vault.deposit(toFixedPtAmt("100")); + await collateralToken.transfer(otherUserAddress, toFixedPtAmt("100")); + await collateralToken.connect(otherUser).approve(vault.address, toFixedPtAmt("100")); + noteAmt = await vault.connect(otherUser).callStatic.deposit(toFixedPtAmt("100")); + tx = vault.connect(otherUser).deposit(toFixedPtAmt("100")); + await tx; + }); + it("should transfer underlying", async function () { + await expect(tx) + .to.emit(collateralToken, "Transfer") + .withArgs(otherUserAddress, vault.address, toFixedPtAmt("100")); + await expect(tx).to.emit(vault, "AssetSynced").withArgs(collateralToken.address, toFixedPtAmt("200")); + }); + it("should mint notes", async function () { + await expect(tx) + .to.emit(vault, "Transfer") + .withArgs(constants.AddressZero, otherUserAddress, toFixedPtAmt("100").mul("1000000")); + expect(await vault.balanceOf(deployerAddress)).to.eq(toFixedPtAmt("100").mul("1000000")); + expect(await vault.balanceOf(otherUserAddress)).to.eq(toFixedPtAmt("100").mul("1000000")); + }); + it("should return the note amount", async function () { + expect(noteAmt).to.eq(toFixedPtAmt("100").mul("1000000")); + }); + }); + + describe("when total supply > 0 and tvl > ts", async function () { + beforeEach(async function () { + await collateralToken.approve(vault.address, toFixedPtAmt("100")); + await vault.deposit(toFixedPtAmt("100")); + await collateralToken.transfer(vault.address, toFixedPtAmt("100")); + await collateralToken.transfer(otherUserAddress, toFixedPtAmt("100")); + await collateralToken.connect(otherUser).approve(vault.address, toFixedPtAmt("100")); + noteAmt = await vault.connect(otherUser).callStatic.deposit(toFixedPtAmt("100")); + tx = vault.connect(otherUser).deposit(toFixedPtAmt("100")); + await tx; + }); + it("should transfer underlying", async function () { + await expect(tx) + .to.emit(collateralToken, "Transfer") + .withArgs(otherUserAddress, vault.address, toFixedPtAmt("100")); + await expect(tx).to.emit(vault, "AssetSynced").withArgs(collateralToken.address, toFixedPtAmt("300")); + }); + it("should mint notes", async function () { + await expect(tx) + .to.emit(vault, "Transfer") + .withArgs(constants.AddressZero, otherUserAddress, toFixedPtAmt("100").mul("500000")); + expect(await vault.balanceOf(deployerAddress)).to.eq(toFixedPtAmt("100").mul("1000000")); + expect(await vault.balanceOf(otherUserAddress)).to.eq(toFixedPtAmt("100").mul("500000")); + }); + it("should return the note amount", async function () { + expect(noteAmt).to.eq(toFixedPtAmt("100").mul("500000")); + }); + }); + + describe("when total supply > 0 and tvl < ts", async function () { + beforeEach(async function () { + await collateralToken.approve(vault.address, toFixedPtAmt("100")); + await vault.deposit(toFixedPtAmt("100")); + await rebase(collateralToken, rebaseOracle, -0.5); + await collateralToken.transfer(otherUserAddress, toFixedPtAmt("100")); + await collateralToken.connect(otherUser).approve(vault.address, toFixedPtAmt("100")); + noteAmt = await vault.connect(otherUser).callStatic.deposit(toFixedPtAmt("100")); + tx = vault.connect(otherUser).deposit(toFixedPtAmt("100")); + await tx; + }); + it("should transfer underlying", async function () { + await expect(tx) + .to.emit(collateralToken, "Transfer") + .withArgs(otherUserAddress, vault.address, toFixedPtAmt("100")); + await expect(tx).to.emit(vault, "AssetSynced").withArgs(collateralToken.address, toFixedPtAmt("150")); + }); + it("should mint notes", async function () { + await expect(tx) + .to.emit(vault, "Transfer") + .withArgs(constants.AddressZero, otherUserAddress, toFixedPtAmt("100").mul("2000000")); + expect(await vault.balanceOf(deployerAddress)).to.eq(toFixedPtAmt("100").mul("1000000")); + expect(await vault.balanceOf(otherUserAddress)).to.eq(toFixedPtAmt("100").mul("2000000")); + }); + it("should return the note amount", async function () { + expect(noteAmt).to.eq(toFixedPtAmt("100").mul("2000000")); + }); + }); + }); + + describe("#redeem", function () { + let tx: Transaction, redemptionAmts: [string, BigNumber][]; + describe("when vault is empty", function () { + it("should revert", async function () { + await expect(vault.redeem("1")).to.be.reverted; + }); + }); + + describe("when burning more than balance", function () { + beforeEach(async function () { + await collateralToken.approve(vault.address, toFixedPtAmt("100")); + await vault.deposit(toFixedPtAmt("100")); + }); + + it("should revert", async function () { + await expect(vault.redeem((await vault.balanceOf(deployerAddress)).add("1"))).to.be.reverted; + await expect(vault.redeem(await vault.balanceOf(deployerAddress))).not.to.be.reverted; + }); + }); + + describe("when vault has only usable balance", function () { + beforeEach(async function () { + await collateralToken.approve(vault.address, toFixedPtAmt("100")); + await vault.deposit(toFixedPtAmt("100")); + + await collateralToken.transfer(otherUserAddress, toFixedPtAmt("100")); + await collateralToken.connect(otherUser).approve(vault.address, toFixedPtAmt("100")); + await vault.connect(otherUser).deposit(toFixedPtAmt("100")); + + redemptionAmts = await vault.callStatic.redeem(await vault.balanceOf(deployerAddress)); + tx = vault.redeem(await vault.balanceOf(deployerAddress)); + await tx; + }); + it("should transfer assets", async function () { + await expect(tx) + .to.emit(collateralToken, "Transfer") + .withArgs(vault.address, deployerAddress, toFixedPtAmt("100")); + await expect(tx).to.emit(vault, "AssetSynced").withArgs(collateralToken.address, toFixedPtAmt("100")); + }); + it("should burn notes", async function () { + await expect(tx) + .to.emit(vault, "Transfer") + .withArgs(deployerAddress, constants.AddressZero, toFixedPtAmt("100").mul("1000000")); + expect(await vault.balanceOf(deployerAddress)).to.eq(0); + expect(await vault.balanceOf(otherUserAddress)).to.eq(toFixedPtAmt("100").mul("1000000")); + }); + it("should return redemption amounts", async function () { + expect(redemptionAmts.length).to.eq(2); + expect(redemptionAmts[0].token).to.eq(collateralToken.address); + expect(redemptionAmts[0].amount).to.eq(toFixedPtAmt("100")); + expect(redemptionAmts[1].token).to.eq(perp.address); + expect(redemptionAmts[1].amount).to.eq(0); + }); + }); + + describe("when vault has only deployed balance", function () { + beforeEach(async function () { + await collateralToken.approve(vault.address, toFixedPtAmt("100")); + await vault.deposit(toFixedPtAmt("100")); + + await collateralToken.transfer(otherUserAddress, toFixedPtAmt("100")); + await collateralToken.connect(otherUser).approve(vault.address, toFixedPtAmt("100")); + await vault.connect(otherUser).deposit(toFixedPtAmt("100")); + + await discountStrategy.computeTrancheDiscount + .whenCalledWith(rolloverInTranches[1].address) + .returns(toDiscountFixedPtAmt("1")); + await discountStrategy.computeTrancheDiscount + .whenCalledWith(rolloverInTranches[2].address) + .returns(toDiscountFixedPtAmt("1")); + await vault.deploy(); + + expect(await vault.vaultAssetBalance(await vault.underlying())).to.eq(0); + expect(await vault.deployedCount()).to.eq(1); + + redemptionAmts = await vault.callStatic.redeem(await vault.balanceOf(deployerAddress)); + tx = vault.redeem(await vault.balanceOf(deployerAddress)); + await tx; + }); + it("should transfer assets", async function () { + await expect(tx) + .to.emit(reserveTranches[0], "Transfer") + .withArgs(vault.address, deployerAddress, toFixedPtAmt("100")); + await expect(tx).to.emit(vault, "AssetSynced").withArgs(reserveTranches[0].address, toFixedPtAmt("100")); + }); + it("should burn notes", async function () { + await expect(tx) + .to.emit(vault, "Transfer") + .withArgs(deployerAddress, constants.AddressZero, toFixedPtAmt("100").mul("1000000")); + expect(await vault.balanceOf(deployerAddress)).to.eq(0); + expect(await vault.balanceOf(otherUserAddress)).to.eq(toFixedPtAmt("100").mul("1000000")); + }); + it("should return redemption amounts", async function () { + expect(redemptionAmts.length).to.eq(3); + expect(redemptionAmts[0].token).to.eq(collateralToken.address); + expect(redemptionAmts[0].amount).to.eq(0); + expect(redemptionAmts[1].token).to.eq(reserveTranches[0].address); + expect(redemptionAmts[1].amount).to.eq(toFixedPtAmt("100")); + expect(redemptionAmts[2].token).to.eq(perp.address); + expect(redemptionAmts[2].amount).to.eq(0); + }); + }); + + describe("when vault has a combination of balances (full balance redemption)", function () { + let redemptionAmts: [string, BigNumber][]; + beforeEach(async function () { + await collateralToken.approve(vault.address, toFixedPtAmt("100")); + await vault.deposit(toFixedPtAmt("100")); + + await collateralToken.transfer(otherUserAddress, toFixedPtAmt("100")); + await collateralToken.connect(otherUser).approve(vault.address, toFixedPtAmt("100")); + await vault.connect(otherUser).deposit(toFixedPtAmt("100")); + + await vault.deploy(); + await collateralToken.transfer(vault.address, toFixedPtAmt("20")); + await perp.transfer(vault.address, toFixedPtAmt("20")); + + redemptionAmts = await vault.callStatic.redeem(await vault.balanceOf(deployerAddress)); + tx = vault.redeem(await vault.balanceOf(deployerAddress)); + await tx; + }); + it("should transfer assets", async function () { + await expect(tx) + .to.emit(collateralToken, "Transfer") + .withArgs(vault.address, deployerAddress, toFixedPtAmt("10")); + await expect(tx) + .to.emit(reserveTranches[0], "Transfer") + .withArgs(vault.address, deployerAddress, toFixedPtAmt("20")); + await expect(tx) + .to.emit(rolloverInTranches[1], "Transfer") + .withArgs(vault.address, deployerAddress, toFixedPtAmt("30")); + await expect(tx) + .to.emit(rolloverInTranches[2], "Transfer") + .withArgs(vault.address, deployerAddress, toFixedPtAmt("50")); + await expect(tx).to.emit(perp, "Transfer").withArgs(vault.address, deployerAddress, toFixedPtAmt("10")); + await expect(tx).to.emit(vault, "AssetSynced").withArgs(collateralToken.address, toFixedPtAmt("10")); + await expect(tx).to.emit(vault, "AssetSynced").withArgs(reserveTranches[0].address, toFixedPtAmt("20")); + await expect(tx).to.emit(vault, "AssetSynced").withArgs(rolloverInTranches[1].address, toFixedPtAmt("30")); + await expect(tx).to.emit(vault, "AssetSynced").withArgs(rolloverInTranches[2].address, toFixedPtAmt("50")); + await expect(tx).to.emit(vault, "AssetSynced").withArgs(perp.address, toFixedPtAmt("10")); + }); + it("should burn notes", async function () { + await expect(tx) + .to.emit(vault, "Transfer") + .withArgs(deployerAddress, constants.AddressZero, toFixedPtAmt("100").mul("1000000")); + expect(await vault.balanceOf(deployerAddress)).to.eq(0); + expect(await vault.balanceOf(otherUserAddress)).to.eq(toFixedPtAmt("100").mul("1000000")); + }); + it("should return redemption amounts", async function () { + expect(redemptionAmts.length).to.eq(5); + expect(redemptionAmts[0].token).to.eq(collateralToken.address); + expect(redemptionAmts[0].amount).to.eq(toFixedPtAmt("10")); + expect(redemptionAmts[1].token).to.eq(rolloverInTranches[2].address); + expect(redemptionAmts[1].amount).to.eq(toFixedPtAmt("50")); + expect(redemptionAmts[2].token).to.eq(rolloverInTranches[1].address); + expect(redemptionAmts[2].amount).to.eq(toFixedPtAmt("30")); + expect(redemptionAmts[3].token).to.eq(reserveTranches[0].address); + expect(redemptionAmts[3].amount).to.eq(toFixedPtAmt("20")); + expect(redemptionAmts[4].token).to.eq(perp.address); + expect(redemptionAmts[4].amount).to.eq(toFixedPtAmt("10")); + }); + }); + + describe("when vault has a combination of balances (partial balance redemption)", function () { + let redemptionAmts: [string, BigNumber][]; + beforeEach(async function () { + await collateralToken.approve(vault.address, toFixedPtAmt("100")); + await vault.deposit(toFixedPtAmt("100")); + + await collateralToken.transfer(otherUserAddress, toFixedPtAmt("100")); + await collateralToken.connect(otherUser).approve(vault.address, toFixedPtAmt("100")); + await vault.connect(otherUser).deposit(toFixedPtAmt("100")); + + await vault.deploy(); + await collateralToken.transfer(vault.address, toFixedPtAmt("20")); + await perp.transfer(vault.address, toFixedPtAmt("20")); + + redemptionAmts = await vault.callStatic.redeem(toFixedPtAmt("50").mul("1000000")); + tx = vault.redeem(toFixedPtAmt("50").mul("1000000")); + await tx; + }); + it("should transfer assets", async function () { + await expect(tx) + .to.emit(collateralToken, "Transfer") + .withArgs(vault.address, deployerAddress, toFixedPtAmt("5")); + await expect(tx) + .to.emit(reserveTranches[0], "Transfer") + .withArgs(vault.address, deployerAddress, toFixedPtAmt("10")); + await expect(tx) + .to.emit(rolloverInTranches[1], "Transfer") + .withArgs(vault.address, deployerAddress, toFixedPtAmt("15")); + await expect(tx) + .to.emit(rolloverInTranches[2], "Transfer") + .withArgs(vault.address, deployerAddress, toFixedPtAmt("25")); + await expect(tx).to.emit(perp, "Transfer").withArgs(vault.address, deployerAddress, toFixedPtAmt("5")); + await expect(tx).to.emit(vault, "AssetSynced").withArgs(collateralToken.address, toFixedPtAmt("15")); + await expect(tx).to.emit(vault, "AssetSynced").withArgs(reserveTranches[0].address, toFixedPtAmt("30")); + await expect(tx).to.emit(vault, "AssetSynced").withArgs(rolloverInTranches[1].address, toFixedPtAmt("45")); + await expect(tx).to.emit(vault, "AssetSynced").withArgs(rolloverInTranches[2].address, toFixedPtAmt("75")); + await expect(tx).to.emit(vault, "AssetSynced").withArgs(perp.address, toFixedPtAmt("15")); + }); + it("should burn notes", async function () { + await expect(tx) + .to.emit(vault, "Transfer") + .withArgs(deployerAddress, constants.AddressZero, toFixedPtAmt("50").mul("1000000")); + expect(await vault.balanceOf(deployerAddress)).to.eq(toFixedPtAmt("50").mul("1000000")); + expect(await vault.balanceOf(otherUserAddress)).to.eq(toFixedPtAmt("100").mul("1000000")); + }); + it("should return redemption amounts", async function () { + expect(redemptionAmts.length).to.eq(5); + expect(redemptionAmts[0].token).to.eq(collateralToken.address); + expect(redemptionAmts[0].amount).to.eq(toFixedPtAmt("5")); + expect(redemptionAmts[1].token).to.eq(rolloverInTranches[2].address); + expect(redemptionAmts[1].amount).to.eq(toFixedPtAmt("25")); + expect(redemptionAmts[2].token).to.eq(rolloverInTranches[1].address); + expect(redemptionAmts[2].amount).to.eq(toFixedPtAmt("15")); + expect(redemptionAmts[3].token).to.eq(reserveTranches[0].address); + expect(redemptionAmts[3].amount).to.eq(toFixedPtAmt("10")); + expect(redemptionAmts[4].token).to.eq(perp.address); + expect(redemptionAmts[4].amount).to.eq(toFixedPtAmt("5")); + }); + }); + + describe("when vault has a combination of balances (full supply redemption)", function () { + let redemptionAmts: [string, BigNumber][]; + beforeEach(async function () { + await collateralToken.approve(vault.address, toFixedPtAmt("100")); + await vault.deposit(toFixedPtAmt("100")); + + await collateralToken.transfer(otherUserAddress, toFixedPtAmt("100")); + await collateralToken.connect(otherUser).approve(vault.address, toFixedPtAmt("100")); + await vault.connect(otherUser).deposit(toFixedPtAmt("100")); + + await vault.deploy(); + await collateralToken.transfer(vault.address, toFixedPtAmt("20")); + await perp.transfer(vault.address, toFixedPtAmt("20")); + + await vault.connect(otherUser).redeem(await vault.balanceOf(otherUserAddress)); + redemptionAmts = await vault.callStatic.redeem(await vault.balanceOf(deployerAddress)); + tx = vault.redeem(await vault.balanceOf(deployerAddress)); + await tx; + }); + it("should transfer assets", async function () { + await expect(tx) + .to.emit(collateralToken, "Transfer") + .withArgs(vault.address, deployerAddress, toFixedPtAmt("10")); + await expect(tx) + .to.emit(reserveTranches[0], "Transfer") + .withArgs(vault.address, deployerAddress, toFixedPtAmt("20")); + await expect(tx) + .to.emit(rolloverInTranches[1], "Transfer") + .withArgs(vault.address, deployerAddress, toFixedPtAmt("30")); + await expect(tx) + .to.emit(rolloverInTranches[2], "Transfer") + .withArgs(vault.address, deployerAddress, toFixedPtAmt("50")); + await expect(tx).to.emit(perp, "Transfer").withArgs(vault.address, deployerAddress, toFixedPtAmt("10")); + await expect(tx).to.emit(vault, "AssetSynced").withArgs(collateralToken.address, "0"); + await expect(tx).to.emit(vault, "AssetSynced").withArgs(reserveTranches[0].address, "0"); + await expect(tx).to.emit(vault, "AssetSynced").withArgs(rolloverInTranches[1].address, "0"); + await expect(tx).to.emit(vault, "AssetSynced").withArgs(rolloverInTranches[2].address, "0"); + await expect(tx).to.emit(vault, "AssetSynced").withArgs(perp.address, "0"); + }); + it("should burn notes", async function () { + await expect(tx) + .to.emit(vault, "Transfer") + .withArgs(deployerAddress, constants.AddressZero, toFixedPtAmt("100").mul("1000000")); + expect(await vault.balanceOf(deployerAddress)).to.eq(0); + expect(await vault.balanceOf(otherUserAddress)).to.eq(0); + }); + it("should return redemption amounts", async function () { + expect(redemptionAmts.length).to.eq(5); + expect(redemptionAmts[0].token).to.eq(collateralToken.address); + expect(redemptionAmts[0].amount).to.eq(toFixedPtAmt("10")); + expect(redemptionAmts[1].token).to.eq(rolloverInTranches[2].address); + expect(redemptionAmts[1].amount).to.eq(toFixedPtAmt("50")); + expect(redemptionAmts[2].token).to.eq(rolloverInTranches[1].address); + expect(redemptionAmts[2].amount).to.eq(toFixedPtAmt("30")); + expect(redemptionAmts[3].token).to.eq(reserveTranches[0].address); + expect(redemptionAmts[3].amount).to.eq(toFixedPtAmt("20")); + expect(redemptionAmts[4].token).to.eq(perp.address); + expect(redemptionAmts[4].amount).to.eq(toFixedPtAmt("10")); + }); + }); + }); +}); diff --git a/spot-contracts/test/vaults/RolloverVault_recover.ts b/spot-contracts/test/vaults/RolloverVault_recover.ts new file mode 100644 index 00000000..c306a640 --- /dev/null +++ b/spot-contracts/test/vaults/RolloverVault_recover.ts @@ -0,0 +1,466 @@ +import { expect, use } from "chai"; +import { network, ethers, upgrades } from "hardhat"; +import { Contract, Signer } from "ethers"; +import { smock } from "@defi-wonderland/smock"; + +import { + setupCollateralToken, + mintCollteralToken, + setupBondFactory, + depositIntoBond, + bondAt, + getTranches, + toFixedPtAmt, + toDiscountFixedPtAmt, + toPriceFixedPtAmt, + getDepositBond, + advancePerpQueue, + advancePerpQueueToBondMaturity, + advancePerpQueueToRollover, + checkReserveComposition, + checkVaultAssetComposition, +} from "../helpers"; +use(smock.matchers); + +let vault: Contract; +let perp: Contract; +let bondFactory: Contract; +let collateralToken: Contract; +let issuer: Contract; +let feeStrategy: Contract; +let pricingStrategy: Contract; +let discountStrategy: Contract; +let deployer: Signer; +let reserveTranches: Contract[][] = []; +let rolloverInBond: Contract; +let rolloverInTranches: Contract; + +describe("RolloverVault", function () { + beforeEach(async function () { + await network.provider.send("hardhat_reset"); + + const accounts = await ethers.getSigners(); + deployer = accounts[0]; + + bondFactory = await setupBondFactory(); + ({ collateralToken } = await setupCollateralToken("Bitcoin", "BTC")); + const BondIssuer = await ethers.getContractFactory("BondIssuer"); + issuer = await BondIssuer.deploy(bondFactory.address, collateralToken.address); + await issuer.init(4800, [200, 300, 500], 1200, 0); + + const FeeStrategy = await ethers.getContractFactory("BasicFeeStrategy"); + feeStrategy = await smock.fake(FeeStrategy); + await feeStrategy.computeMintFees.returns(["0", "0"]); + await feeStrategy.computeBurnFees.returns(["0", "0"]); + await feeStrategy.computeRolloverFees.returns(["0", "0"]); + + const PricingStrategy = await ethers.getContractFactory("UnitPricingStrategy"); + pricingStrategy = await smock.fake(PricingStrategy); + await pricingStrategy.decimals.returns(8); + await pricingStrategy.computeMatureTranchePrice.returns(toPriceFixedPtAmt("1")); + await pricingStrategy.computeTranchePrice.returns(toPriceFixedPtAmt("1")); + + const DiscountStrategy = await ethers.getContractFactory("TrancheClassDiscountStrategy"); + discountStrategy = await smock.fake(DiscountStrategy); + await discountStrategy.decimals.returns(18); + await discountStrategy.computeTrancheDiscount + .whenCalledWith(collateralToken.address) + .returns(toDiscountFixedPtAmt("1")); + + const PerpetualTranche = await ethers.getContractFactory("PerpetualTranche"); + perp = await upgrades.deployProxy( + PerpetualTranche.connect(deployer), + [ + "PerpetualTranche", + "PERP", + collateralToken.address, + issuer.address, + feeStrategy.address, + pricingStrategy.address, + discountStrategy.address, + ], + { + initializer: "init(string,string,address,address,address,address,address)", + }, + ); + + await feeStrategy.feeToken.returns(perp.address); + + await perp.updateTolerableTrancheMaturity(1200, 4800); + await advancePerpQueueToBondMaturity(perp, await getDepositBond(perp)); + + reserveTranches = []; + for (let i = 0; i < 4; i++) { + const bond = await getDepositBond(perp); + const tranches = await getTranches(bond); + await depositIntoBond(bond, toFixedPtAmt("1000"), deployer); + + await pricingStrategy.computeTranchePrice.whenCalledWith(tranches[0].address).returns(toPriceFixedPtAmt("1")); + await discountStrategy.computeTrancheDiscount + .whenCalledWith(tranches[0].address) + .returns(toDiscountFixedPtAmt("1")); + await tranches[0].approve(perp.address, toFixedPtAmt("200")); + await perp.deposit(tranches[0].address, toFixedPtAmt("200")); + + await pricingStrategy.computeTranchePrice.whenCalledWith(tranches[1].address).returns(toPriceFixedPtAmt("1")); + await discountStrategy.computeTrancheDiscount + .whenCalledWith(tranches[1].address) + .returns(toDiscountFixedPtAmt("1")); + await tranches[1].approve(perp.address, toFixedPtAmt("300")); + await perp.deposit(tranches[1].address, toFixedPtAmt("300")); + + reserveTranches.push(tranches[0]); + reserveTranches.push(tranches[1]); + await advancePerpQueue(perp, 1200); + } + + await checkReserveComposition( + perp, + [collateralToken, ...reserveTranches.slice(-6)], + [ + toFixedPtAmt("500"), + toFixedPtAmt("200"), + toFixedPtAmt("300"), + toFixedPtAmt("200"), + toFixedPtAmt("300"), + toFixedPtAmt("200"), + toFixedPtAmt("300"), + ], + ); + + rolloverInBond = await bondAt(await perp.callStatic.getDepositBond()); + rolloverInTranches = await getTranches(rolloverInBond); + await pricingStrategy.computeTranchePrice + .whenCalledWith(rolloverInTranches[0].address) + .returns(toPriceFixedPtAmt("1")); + await discountStrategy.computeTrancheDiscount + .whenCalledWith(rolloverInTranches[0].address) + .returns(toDiscountFixedPtAmt("1")); + await pricingStrategy.computeTranchePrice + .whenCalledWith(rolloverInTranches[1].address) + .returns(toPriceFixedPtAmt("1")); + await discountStrategy.computeTrancheDiscount + .whenCalledWith(rolloverInTranches[1].address) + .returns(toDiscountFixedPtAmt("0")); + await pricingStrategy.computeTranchePrice + .whenCalledWith(rolloverInTranches[2].address) + .returns(toPriceFixedPtAmt("1")); + await discountStrategy.computeTrancheDiscount + .whenCalledWith(rolloverInTranches[2].address) + .returns(toDiscountFixedPtAmt("0")); + + await mintCollteralToken(collateralToken, toFixedPtAmt("100000"), deployer); + const RolloverVault = await ethers.getContractFactory("RolloverVault"); + vault = await upgrades.deployProxy(RolloverVault.connect(deployer)); + await collateralToken.approve(vault.address, toFixedPtAmt("1")); + await vault.init("RolloverVault", "VSHARE", perp.address); + await checkVaultAssetComposition(vault, [collateralToken, perp], [toFixedPtAmt("0"), toFixedPtAmt("0")]); + expect(await vault.deployedCount()).to.eq(0); + }); + + afterEach(async function () { + await network.provider.send("hardhat_reset"); + }); + + describe("#recover", function () { + describe("when no asset is deployed", function () { + it("should be a no-op", async function () { + await vault.recover(); + await expect(vault.recover()).not.to.be.reverted; + expect(await vault.deployedCount()).to.eq(0); + }); + }); + + describe("when one asset deployed", function () { + let currentBondIn: Contract, currentTranchesIn: Contract[]; + beforeEach(async function () { + await advancePerpQueueToBondMaturity(perp, rolloverInBond); + currentBondIn = await bondAt(await perp.callStatic.getDepositBond()); + currentTranchesIn = await getTranches(currentBondIn); + + await pricingStrategy.computeTranchePrice + .whenCalledWith(currentTranchesIn[0].address) + .returns(toPriceFixedPtAmt("1")); + await discountStrategy.computeTrancheDiscount + .whenCalledWith(currentTranchesIn[0].address) + .returns(toDiscountFixedPtAmt("1")); + + await pricingStrategy.computeTranchePrice + .whenCalledWith(currentTranchesIn[1].address) + .returns(toPriceFixedPtAmt("1")); + await discountStrategy.computeTrancheDiscount + .whenCalledWith(currentTranchesIn[1].address) + .returns(toDiscountFixedPtAmt("1")); + + await collateralToken.transfer(vault.address, toFixedPtAmt("10")); + + await vault.deploy(); + await checkVaultAssetComposition( + vault, + [collateralToken, currentTranchesIn[2], perp], + [toFixedPtAmt("5"), toFixedPtAmt("5"), toFixedPtAmt("0")], + ); + expect(await vault.deployedCount()).to.eq(1); + expect(await vault.deployedAt(0)).to.eq(currentTranchesIn[2].address); + }); + describe("when its not mature", function () { + it("should be a no-op", async function () { + await expect(vault.recover()).not.to.be.reverted; + await checkVaultAssetComposition( + vault, + [collateralToken, currentTranchesIn[2], perp], + [toFixedPtAmt("5"), toFixedPtAmt("5"), toFixedPtAmt("0")], + ); + expect(await vault.deployedCount()).to.eq(1); + expect(await vault.deployedAt(0)).to.eq(currentTranchesIn[2].address); + }); + }); + describe("when its mature", function () { + beforeEach(async function () { + await advancePerpQueueToBondMaturity(perp, currentBondIn); + }); + it("should recover", async function () { + await expect(vault.recover()).not.to.be.reverted; + expect(await vault.deployedCount()).to.eq(0); + await checkVaultAssetComposition(vault, [collateralToken, perp], [toFixedPtAmt("10"), toFixedPtAmt("0")]); + }); + it("should sync assets", async function () { + const tx = vault.recover(); + await expect(tx).to.emit(vault, "AssetSynced").withArgs(collateralToken.address, toFixedPtAmt("10")); + await expect(tx).to.emit(vault, "AssetSynced").withArgs(currentTranchesIn[2].address, toFixedPtAmt("0")); + }); + }); + }); + + describe("when many assets are deployed", function () { + let currentBondIn: Contract, currentTranchesIn: Contract[], newBondIn: Contract, newTranchesIn: Contract[]; + beforeEach(async function () { + await advancePerpQueueToBondMaturity(perp, rolloverInBond); + currentBondIn = await bondAt(await perp.callStatic.getDepositBond()); + currentTranchesIn = await getTranches(currentBondIn); + + await pricingStrategy.computeTranchePrice + .whenCalledWith(currentTranchesIn[0].address) + .returns(toPriceFixedPtAmt("1")); + await discountStrategy.computeTrancheDiscount + .whenCalledWith(currentTranchesIn[0].address) + .returns(toDiscountFixedPtAmt("1")); + + await collateralToken.transfer(vault.address, toFixedPtAmt("10")); + await vault.deploy(); + + await checkVaultAssetComposition( + vault, + [collateralToken, currentTranchesIn[1], currentTranchesIn[2], perp], + [toFixedPtAmt("2"), toFixedPtAmt("3"), toFixedPtAmt("5"), toFixedPtAmt("0")], + ); + expect(await vault.deployedCount()).to.eq(2); + expect(await vault.deployedAt(0)).to.eq(currentTranchesIn[2].address); + expect(await vault.deployedAt(1)).to.eq(currentTranchesIn[1].address); + }); + + describe("when no redemption", function () { + it("should be a no-op", async function () { + await expect(vault.recover()).not.to.be.reverted; + await checkVaultAssetComposition( + vault, + [collateralToken, currentTranchesIn[1], currentTranchesIn[2], perp], + [toFixedPtAmt("2"), toFixedPtAmt("3"), toFixedPtAmt("5"), toFixedPtAmt("0")], + ); + expect(await vault.deployedCount()).to.eq(2); + expect(await vault.deployedAt(0)).to.eq(currentTranchesIn[2].address); + expect(await vault.deployedAt(1)).to.eq(currentTranchesIn[1].address); + }); + }); + + describe("when mature redemption", function () { + beforeEach(async function () { + await advancePerpQueueToBondMaturity(perp, currentBondIn); + }); + it("should recover", async function () { + await expect(vault.recover()).not.to.be.reverted; + expect(await vault.deployedCount()).to.eq(0); + await checkVaultAssetComposition(vault, [collateralToken, perp], [toFixedPtAmt("10"), toFixedPtAmt("0")]); + }); + it("should sync assets", async function () { + const tx = vault.recover(); + await expect(tx).to.emit(vault, "AssetSynced").withArgs(collateralToken.address, toFixedPtAmt("10")); + await expect(tx).to.emit(vault, "AssetSynced").withArgs(currentTranchesIn[1].address, toFixedPtAmt("0")); + await expect(tx).to.emit(vault, "AssetSynced").withArgs(currentTranchesIn[2].address, toFixedPtAmt("0")); + }); + }); + + describe("when immature redemption", function () { + beforeEach(async function () { + await advancePerpQueueToRollover(perp, currentBondIn); + + newBondIn = await bondAt(await perp.callStatic.getDepositBond()); + newTranchesIn = await getTranches(newBondIn); + + await pricingStrategy.computeTranchePrice + .whenCalledWith(newTranchesIn[0].address) + .returns(toPriceFixedPtAmt("1")); + await discountStrategy.computeTrancheDiscount + .whenCalledWith(newTranchesIn[0].address) + .returns(toDiscountFixedPtAmt("1")); + + await collateralToken.transfer(vault.address, toFixedPtAmt("9998")); + await vault.deploy(); + + expect(await vault.deployedCount()).to.eq(5); + await checkVaultAssetComposition( + vault, + [ + collateralToken, + currentTranchesIn[0], + currentTranchesIn[1], + currentTranchesIn[2], + newTranchesIn[1], + newTranchesIn[2], + perp, + ], + [ + toFixedPtAmt("1998"), + toFixedPtAmt("2"), + toFixedPtAmt("3"), + toFixedPtAmt("5"), + toFixedPtAmt("3000"), + toFixedPtAmt("5000"), + toFixedPtAmt("0"), + ], + ); + }); + + describe("without reminder", function () { + it("should recover", async function () { + await expect(vault.recover()).not.to.be.reverted; + expect(await vault.deployedCount()).to.eq(2); + await checkVaultAssetComposition( + vault, + [collateralToken, newTranchesIn[1], newTranchesIn[2], perp], + [toFixedPtAmt("2008"), toFixedPtAmt("3000"), toFixedPtAmt("5000"), toFixedPtAmt("0")], + ); + }); + it("should sync assets", async function () { + const tx = vault.recover(); + await expect(tx).to.emit(vault, "AssetSynced").withArgs(collateralToken.address, toFixedPtAmt("2008")); + await expect(tx).to.emit(vault, "AssetSynced").withArgs(currentTranchesIn[0].address, toFixedPtAmt("0")); + await expect(tx).to.emit(vault, "AssetSynced").withArgs(currentTranchesIn[1].address, toFixedPtAmt("0")); + await expect(tx).to.emit(vault, "AssetSynced").withArgs(currentTranchesIn[2].address, toFixedPtAmt("0")); + await expect(tx).to.emit(vault, "AssetSynced").withArgs(newTranchesIn[1].address, toFixedPtAmt("3000")); + await expect(tx).to.emit(vault, "AssetSynced").withArgs(newTranchesIn[2].address, toFixedPtAmt("5000")); + }); + }); + + describe("with reminder", function () { + beforeEach(async function () { + await depositIntoBond(currentBondIn, toFixedPtAmt("1000"), deployer); + await currentTranchesIn[2].transfer(vault.address, toFixedPtAmt("1")); + expect(await vault.deployedCount()).to.eq(5); + await checkVaultAssetComposition( + vault, + [ + collateralToken, + currentTranchesIn[0], + currentTranchesIn[1], + currentTranchesIn[2], + newTranchesIn[1], + newTranchesIn[2], + perp, + ], + [ + toFixedPtAmt("1998"), + toFixedPtAmt("2"), + toFixedPtAmt("3"), + toFixedPtAmt("6"), + toFixedPtAmt("3000"), + toFixedPtAmt("5000"), + toFixedPtAmt("0"), + ], + ); + }); + it("should recover", async function () { + await expect(vault.recover()).not.to.be.reverted; + expect(await vault.deployedCount()).to.eq(3); + await checkVaultAssetComposition( + vault, + [collateralToken, currentTranchesIn[2], newTranchesIn[1], newTranchesIn[2], perp], + [toFixedPtAmt("2008"), toFixedPtAmt("1"), toFixedPtAmt("3000"), toFixedPtAmt("5000"), toFixedPtAmt("0")], + ); + }); + it("should sync assets", async function () { + const tx = vault.recover(); + await expect(tx).to.emit(vault, "AssetSynced").withArgs(collateralToken.address, toFixedPtAmt("2008")); + await expect(tx).to.emit(vault, "AssetSynced").withArgs(currentTranchesIn[0].address, toFixedPtAmt("0")); + await expect(tx).to.emit(vault, "AssetSynced").withArgs(currentTranchesIn[1].address, toFixedPtAmt("0")); + await expect(tx).to.emit(vault, "AssetSynced").withArgs(currentTranchesIn[2].address, toFixedPtAmt("1")); + await expect(tx).to.emit(vault, "AssetSynced").withArgs(newTranchesIn[1].address, toFixedPtAmt("3000")); + await expect(tx).to.emit(vault, "AssetSynced").withArgs(newTranchesIn[2].address, toFixedPtAmt("5000")); + }); + }); + }); + }); + }); + + describe("#recoverAndRedeploy", function () { + let currentBondIn: Contract, currentTranchesIn: Contract[], newBondIn: Contract, newTranchesIn: Contract[]; + beforeEach(async function () { + await advancePerpQueueToBondMaturity(perp, rolloverInBond); + currentBondIn = await bondAt(await perp.callStatic.getDepositBond()); + currentTranchesIn = await getTranches(currentBondIn); + + await pricingStrategy.computeTranchePrice + .whenCalledWith(currentTranchesIn[0].address) + .returns(toPriceFixedPtAmt("1")); + await discountStrategy.computeTrancheDiscount + .whenCalledWith(currentTranchesIn[0].address) + .returns(toDiscountFixedPtAmt("1")); + + await collateralToken.transfer(vault.address, toFixedPtAmt("10")); + await vault.deploy(); + + await checkVaultAssetComposition( + vault, + [collateralToken, currentTranchesIn[1], currentTranchesIn[2], perp], + [toFixedPtAmt("2"), toFixedPtAmt("3"), toFixedPtAmt("5"), toFixedPtAmt("0")], + ); + expect(await vault.deployedCount()).to.eq(2); + expect(await vault.deployedAt(0)).to.eq(currentTranchesIn[2].address); + expect(await vault.deployedAt(1)).to.eq(currentTranchesIn[1].address); + + await advancePerpQueueToBondMaturity(perp, currentBondIn); + + newBondIn = await bondAt(await perp.callStatic.getDepositBond()); + newTranchesIn = await getTranches(newBondIn); + + await pricingStrategy.computeTranchePrice + .whenCalledWith(newTranchesIn[0].address) + .returns(toPriceFixedPtAmt("1")); + await discountStrategy.computeTrancheDiscount + .whenCalledWith(newTranchesIn[0].address) + .returns(toDiscountFixedPtAmt("1")); + }); + + it("should recover", async function () { + await expect(vault.recoverAndRedeploy()).not.to.be.reverted; + expect(await vault.deployedCount()).to.eq(2); + await checkVaultAssetComposition( + vault, + [collateralToken, newTranchesIn[1], newTranchesIn[2], perp], + [toFixedPtAmt("2"), toFixedPtAmt("3"), toFixedPtAmt("5"), toFixedPtAmt("0")], + ); + }); + + it("should sync assets", async function () { + const tx = vault.recoverAndRedeploy(); + await expect(tx).to.emit(vault, "AssetSynced").withArgs(collateralToken.address, toFixedPtAmt("10")); + await expect(tx).to.emit(vault, "AssetSynced").withArgs(currentTranchesIn[1].address, toFixedPtAmt("0")); + await expect(tx).to.emit(vault, "AssetSynced").withArgs(currentTranchesIn[2].address, toFixedPtAmt("0")); + await expect(tx).to.emit(vault, "AssetSynced").withArgs(newTranchesIn[0].address, toFixedPtAmt("2")); + await expect(tx).to.emit(vault, "AssetSynced").withArgs(newTranchesIn[1].address, toFixedPtAmt("3")); + await expect(tx).to.emit(vault, "AssetSynced").withArgs(newTranchesIn[2].address, toFixedPtAmt("5")); + await expect(tx).to.emit(vault, "AssetSynced").withArgs(newTranchesIn[0].address, toFixedPtAmt("0")); + await expect(tx).to.emit(vault, "AssetSynced").withArgs(collateralToken.address, toFixedPtAmt("2")); + }); + }); +}); diff --git a/yarn.lock b/yarn.lock index ea3ae8e3..2f70a433 100644 --- a/yarn.lock +++ b/yarn.lock @@ -9,11 +9,13 @@ __metadata: version: 0.0.0-use.local resolution: "@ampleforthorg/spot-contracts@workspace:spot-contracts" dependencies: + "@defi-wonderland/smock": ^2.3.4 "@ethersproject/abi": ^5.6.4 "@ethersproject/bytes": ^5.6.1 "@ethersproject/providers": ^5.6.8 - "@nomiclabs/hardhat-ethers": ^2.1.0 - "@nomiclabs/hardhat-etherscan": ^3.1.0 + "@nomicfoundation/hardhat-chai-matchers": ^1.0.6 + "@nomiclabs/hardhat-ethers": ^2.2.1 + "@nomiclabs/hardhat-etherscan": ^3.1.2 "@nomiclabs/hardhat-waffle": ^2.0.3 "@openzeppelin/contracts-upgradeable": ^4.7.3 "@openzeppelin/hardhat-upgrades": ^1.19.0 @@ -32,19 +34,20 @@ __metadata: eslint-config-standard: ^17.0.0 eslint-plugin-import: ^2.26.0 eslint-plugin-n: ^15.2.4 + eslint-plugin-no-only-tests: ^3.1.0 eslint-plugin-node: ^11.1.0 eslint-plugin-prettier: ^4.2.1 eslint-plugin-promise: ^6.0.0 ethereum-waffle: ^3.4.4 ethers: ^5.6.9 ganache-cli: ^6.12.2 - hardhat: =2.10.2 - hardhat-gas-reporter: ^1.0.8 + hardhat: ^2.12.6 + hardhat-gas-reporter: ^1.0.9 lodash: ^4.17.21 prettier: ^2.7.1 prettier-plugin-solidity: ^1.0.0-dev.23 solhint: ^3.3.7 - solidity-coverage: ^0.7.21 + solidity-coverage: ^0.8.2 ts-node: ^10.9.1 typechain: ^8.1.0 typescript: ^4.7.4 @@ -93,6 +96,29 @@ __metadata: languageName: node linkType: hard +"@defi-wonderland/smock@npm:^2.3.4": + version: 2.3.4 + resolution: "@defi-wonderland/smock@npm:2.3.4" + dependencies: + "@nomicfoundation/ethereumjs-evm": ^1.0.0-rc.3 + "@nomicfoundation/ethereumjs-util": ^8.0.0-rc.3 + "@nomicfoundation/ethereumjs-vm": ^6.0.0-rc.3 + diff: ^5.0.0 + lodash.isequal: ^4.5.0 + lodash.isequalwith: ^4.4.0 + rxjs: ^7.2.0 + semver: ^7.3.5 + peerDependencies: + "@ethersproject/abi": ^5 + "@ethersproject/abstract-provider": ^5 + "@ethersproject/abstract-signer": ^5 + "@nomiclabs/hardhat-ethers": ^2 + ethers: ^5 + hardhat: ^2 + checksum: 316026d672364a02c5d83c15110b2d5df4358768a6f645e9fd0786fbb230c0c7983a39b928521ee7d0b917a478e07c454b7d7bdf22ff10ed140f520340c28267 + languageName: node + linkType: hard + "@ensdomains/ens@npm:^0.4.4": version: 0.4.5 resolution: "@ensdomains/ens@npm:0.4.5" @@ -193,87 +219,6 @@ __metadata: languageName: node linkType: hard -"@ethereumjs/block@npm:^3.5.0, @ethereumjs/block@npm:^3.6.2, @ethereumjs/block@npm:^3.6.3": - version: 3.6.3 - resolution: "@ethereumjs/block@npm:3.6.3" - dependencies: - "@ethereumjs/common": ^2.6.5 - "@ethereumjs/tx": ^3.5.2 - ethereumjs-util: ^7.1.5 - merkle-patricia-tree: ^4.2.4 - checksum: d08c78134d15bc09c08b9a355ab736faa0f6b04ab87d2962e60df9c8bf977ebc68fe10aec6ca50bc2486532f489d7968fb5046defcd839b3b5ce28ca9dbce40f - languageName: node - linkType: hard - -"@ethereumjs/blockchain@npm:^5.5.2, @ethereumjs/blockchain@npm:^5.5.3": - version: 5.5.3 - resolution: "@ethereumjs/blockchain@npm:5.5.3" - dependencies: - "@ethereumjs/block": ^3.6.2 - "@ethereumjs/common": ^2.6.4 - "@ethereumjs/ethash": ^1.1.0 - debug: ^4.3.3 - ethereumjs-util: ^7.1.5 - level-mem: ^5.0.1 - lru-cache: ^5.1.1 - semaphore-async-await: ^1.5.1 - checksum: eeefb4735ac06e6fe5ec5457eb9ac7aa26ced8651093d05067aee264f23704d79eacb1b2742e0651b73d2528aa8a9a40f3cc9e479f1837253c2dbb784a7a8e59 - languageName: node - linkType: hard - -"@ethereumjs/common@npm:^2.3.0, @ethereumjs/common@npm:^2.4.0, @ethereumjs/common@npm:^2.6.4, @ethereumjs/common@npm:^2.6.5": - version: 2.6.5 - resolution: "@ethereumjs/common@npm:2.6.5" - dependencies: - crc-32: ^1.2.0 - ethereumjs-util: ^7.1.5 - checksum: 0143386f267ef01b7a8bb1847596f964ad58643c084e5fd8e3a0271a7bf8428605dbf38cbb92c84f6622080ad095abeb765f178c02d86ec52abf9e8a4c0e4ecf - languageName: node - linkType: hard - -"@ethereumjs/ethash@npm:^1.1.0": - version: 1.1.0 - resolution: "@ethereumjs/ethash@npm:1.1.0" - dependencies: - "@ethereumjs/block": ^3.5.0 - "@types/levelup": ^4.3.0 - buffer-xor: ^2.0.1 - ethereumjs-util: ^7.1.1 - miller-rabin: ^4.0.0 - checksum: 152bc0850eeb0f2507383ca005418697b0a6a4487b120d7b3fadae4cb3b4781403c96c01f0c47149031431e518fb174c284ff38806b457f86f00c500eb213df3 - languageName: node - linkType: hard - -"@ethereumjs/tx@npm:^3.2.1, @ethereumjs/tx@npm:^3.5.1, @ethereumjs/tx@npm:^3.5.2": - version: 3.5.2 - resolution: "@ethereumjs/tx@npm:3.5.2" - dependencies: - "@ethereumjs/common": ^2.6.4 - ethereumjs-util: ^7.1.5 - checksum: a34a7228a623b40300484d15875b9f31f0a612cfeab64a845f6866cf0bfe439519e9455ac6396149f29bc527cf0ee277ace082ae013a1075dcbf7193220a0146 - languageName: node - linkType: hard - -"@ethereumjs/vm@npm:^5.9.0": - version: 5.9.3 - resolution: "@ethereumjs/vm@npm:5.9.3" - dependencies: - "@ethereumjs/block": ^3.6.3 - "@ethereumjs/blockchain": ^5.5.3 - "@ethereumjs/common": ^2.6.5 - "@ethereumjs/tx": ^3.5.2 - async-eventemitter: ^0.2.4 - core-js-pure: ^3.0.1 - debug: ^4.3.3 - ethereumjs-util: ^7.1.5 - functional-red-black-tree: ^1.0.1 - mcl-wasm: ^0.7.1 - merkle-patricia-tree: ^4.2.4 - rustbn.js: ~0.2.0 - checksum: c5b4f85044342072ca009d8a26085f33764637492618522307a699a19123a3e18d36ff67126f8ab382cacf91cc94f5cabb8978e2ba9c5b2bf2ffdf20fe641e47 - languageName: node - linkType: hard - "@ethersproject/abi@npm:5.0.0-beta.153": version: 5.0.0-beta.153 resolution: "@ethersproject/abi@npm:5.0.0-beta.153" @@ -291,23 +236,6 @@ __metadata: languageName: node linkType: hard -"@ethersproject/abi@npm:5.0.7": - version: 5.0.7 - resolution: "@ethersproject/abi@npm:5.0.7" - dependencies: - "@ethersproject/address": ^5.0.4 - "@ethersproject/bignumber": ^5.0.7 - "@ethersproject/bytes": ^5.0.4 - "@ethersproject/constants": ^5.0.4 - "@ethersproject/hash": ^5.0.4 - "@ethersproject/keccak256": ^5.0.3 - "@ethersproject/logger": ^5.0.5 - "@ethersproject/properties": ^5.0.3 - "@ethersproject/strings": ^5.0.4 - checksum: 47bce732782187ef0343662aa0ffdabb98be752d3ede57234205b118df511f35d8cddabd468f139e367d908ce7fbb0555f5af943f4b47cf3165c8fd61811183d - languageName: node - linkType: hard - "@ethersproject/abi@npm:5.6.4, @ethersproject/abi@npm:^5.0.0-beta.146, @ethersproject/abi@npm:^5.1.2, @ethersproject/abi@npm:^5.5.0, @ethersproject/abi@npm:^5.6.3, @ethersproject/abi@npm:^5.6.4": version: 5.6.4 resolution: "@ethersproject/abi@npm:5.6.4" @@ -325,6 +253,23 @@ __metadata: languageName: node linkType: hard +"@ethersproject/abi@npm:^5.0.9": + version: 5.7.0 + resolution: "@ethersproject/abi@npm:5.7.0" + dependencies: + "@ethersproject/address": ^5.7.0 + "@ethersproject/bignumber": ^5.7.0 + "@ethersproject/bytes": ^5.7.0 + "@ethersproject/constants": ^5.7.0 + "@ethersproject/hash": ^5.7.0 + "@ethersproject/keccak256": ^5.7.0 + "@ethersproject/logger": ^5.7.0 + "@ethersproject/properties": ^5.7.0 + "@ethersproject/strings": ^5.7.0 + checksum: bc6962bb6cb854e4d2a4d65b2c49c716477675b131b1363312234bdbb7e19badb7d9ce66f4ca2a70ae2ea84f7123dbc4e300a1bfe5d58864a7eafabc1466627e + languageName: node + linkType: hard + "@ethersproject/abstract-provider@npm:5.6.1, @ethersproject/abstract-provider@npm:^5.6.1": version: 5.6.1 resolution: "@ethersproject/abstract-provider@npm:5.6.1" @@ -340,6 +285,21 @@ __metadata: languageName: node linkType: hard +"@ethersproject/abstract-provider@npm:^5.7.0": + version: 5.7.0 + resolution: "@ethersproject/abstract-provider@npm:5.7.0" + dependencies: + "@ethersproject/bignumber": ^5.7.0 + "@ethersproject/bytes": ^5.7.0 + "@ethersproject/logger": ^5.7.0 + "@ethersproject/networks": ^5.7.0 + "@ethersproject/properties": ^5.7.0 + "@ethersproject/transactions": ^5.7.0 + "@ethersproject/web": ^5.7.0 + checksum: 74cf4696245cf03bb7cc5b6cbf7b4b89dd9a79a1c4688126d214153a938126d4972d42c93182198653ce1de35f2a2cad68be40337d4774b3698a39b28f0228a8 + languageName: node + linkType: hard + "@ethersproject/abstract-signer@npm:5.6.2, @ethersproject/abstract-signer@npm:^5.6.2": version: 5.6.2 resolution: "@ethersproject/abstract-signer@npm:5.6.2" @@ -353,7 +313,20 @@ __metadata: languageName: node linkType: hard -"@ethersproject/address@npm:5.6.1, @ethersproject/address@npm:>=5.0.0-beta.128, @ethersproject/address@npm:^5.0.2, @ethersproject/address@npm:^5.0.4, @ethersproject/address@npm:^5.6.1": +"@ethersproject/abstract-signer@npm:^5.7.0": + version: 5.7.0 + resolution: "@ethersproject/abstract-signer@npm:5.7.0" + dependencies: + "@ethersproject/abstract-provider": ^5.7.0 + "@ethersproject/bignumber": ^5.7.0 + "@ethersproject/bytes": ^5.7.0 + "@ethersproject/logger": ^5.7.0 + "@ethersproject/properties": ^5.7.0 + checksum: a823dac9cfb761e009851050ebebd5b229d1b1cc4a75b125c2da130ff37e8218208f7f9d1386f77407705b889b23d4a230ad67185f8872f083143e0073cbfbe3 + languageName: node + linkType: hard + +"@ethersproject/address@npm:5.6.1, @ethersproject/address@npm:>=5.0.0-beta.128, @ethersproject/address@npm:^5.0.2, @ethersproject/address@npm:^5.6.1": version: 5.6.1 resolution: "@ethersproject/address@npm:5.6.1" dependencies: @@ -366,6 +339,19 @@ __metadata: languageName: node linkType: hard +"@ethersproject/address@npm:^5.7.0": + version: 5.7.0 + resolution: "@ethersproject/address@npm:5.7.0" + dependencies: + "@ethersproject/bignumber": ^5.7.0 + "@ethersproject/bytes": ^5.7.0 + "@ethersproject/keccak256": ^5.7.0 + "@ethersproject/logger": ^5.7.0 + "@ethersproject/rlp": ^5.7.0 + checksum: 64ea5ebea9cc0e845c413e6cb1e54e157dd9fc0dffb98e239d3a3efc8177f2ff798cd4e3206cf3660ee8faeb7bef1a47dc0ebef0d7b132c32e61e550c7d4c843 + languageName: node + linkType: hard + "@ethersproject/base64@npm:5.6.1, @ethersproject/base64@npm:^5.6.1": version: 5.6.1 resolution: "@ethersproject/base64@npm:5.6.1" @@ -375,6 +361,15 @@ __metadata: languageName: node linkType: hard +"@ethersproject/base64@npm:^5.7.0": + version: 5.7.0 + resolution: "@ethersproject/base64@npm:5.7.0" + dependencies: + "@ethersproject/bytes": ^5.7.0 + checksum: 7dd5d734d623582f08f665434f53685041a3d3b334a0e96c0c8afa8bbcaab934d50e5b6b980e826a8fde8d353e0b18f11e61faf17468177274b8e7c69cd9742b + languageName: node + linkType: hard + "@ethersproject/basex@npm:5.6.1, @ethersproject/basex@npm:^5.6.1": version: 5.6.1 resolution: "@ethersproject/basex@npm:5.6.1" @@ -385,7 +380,7 @@ __metadata: languageName: node linkType: hard -"@ethersproject/bignumber@npm:5.6.2, @ethersproject/bignumber@npm:>=5.0.0-beta.130, @ethersproject/bignumber@npm:^5.0.7, @ethersproject/bignumber@npm:^5.6.2": +"@ethersproject/bignumber@npm:5.6.2, @ethersproject/bignumber@npm:>=5.0.0-beta.130, @ethersproject/bignumber@npm:^5.6.2": version: 5.6.2 resolution: "@ethersproject/bignumber@npm:5.6.2" dependencies: @@ -396,7 +391,18 @@ __metadata: languageName: node linkType: hard -"@ethersproject/bytes@npm:5.6.1, @ethersproject/bytes@npm:>=5.0.0-beta.129, @ethersproject/bytes@npm:^5.0.4, @ethersproject/bytes@npm:^5.6.1": +"@ethersproject/bignumber@npm:^5.7.0": + version: 5.7.0 + resolution: "@ethersproject/bignumber@npm:5.7.0" + dependencies: + "@ethersproject/bytes": ^5.7.0 + "@ethersproject/logger": ^5.7.0 + bn.js: ^5.2.1 + checksum: 8c9a134b76f3feb4ec26a5a27379efb4e156b8fb2de0678a67788a91c7f4e30abe9d948638458e4b20f2e42380da0adacc7c9389d05fce070692edc6ae9b4904 + languageName: node + linkType: hard + +"@ethersproject/bytes@npm:5.6.1, @ethersproject/bytes@npm:>=5.0.0-beta.129, @ethersproject/bytes@npm:^5.6.1": version: 5.6.1 resolution: "@ethersproject/bytes@npm:5.6.1" dependencies: @@ -405,7 +411,16 @@ __metadata: languageName: node linkType: hard -"@ethersproject/constants@npm:5.6.1, @ethersproject/constants@npm:>=5.0.0-beta.128, @ethersproject/constants@npm:^5.0.4, @ethersproject/constants@npm:^5.6.1": +"@ethersproject/bytes@npm:^5.7.0": + version: 5.7.0 + resolution: "@ethersproject/bytes@npm:5.7.0" + dependencies: + "@ethersproject/logger": ^5.7.0 + checksum: 66ad365ceaab5da1b23b72225c71dce472cf37737af5118181fa8ab7447d696bea15ca22e3a0e8836fdd8cfac161afe321a7c67d0dde96f9f645ddd759676621 + languageName: node + linkType: hard + +"@ethersproject/constants@npm:5.6.1, @ethersproject/constants@npm:>=5.0.0-beta.128, @ethersproject/constants@npm:^5.6.1": version: 5.6.1 resolution: "@ethersproject/constants@npm:5.6.1" dependencies: @@ -414,6 +429,15 @@ __metadata: languageName: node linkType: hard +"@ethersproject/constants@npm:^5.7.0": + version: 5.7.0 + resolution: "@ethersproject/constants@npm:5.7.0" + dependencies: + "@ethersproject/bignumber": ^5.7.0 + checksum: 6d4b1355747cce837b3e76ec3bde70e4732736f23b04f196f706ebfa5d4d9c2be50904a390d4d40ce77803b98d03d16a9b6898418e04ba63491933ce08c4ba8a + languageName: node + linkType: hard + "@ethersproject/contracts@npm:5.6.2": version: 5.6.2 resolution: "@ethersproject/contracts@npm:5.6.2" @@ -432,7 +456,7 @@ __metadata: languageName: node linkType: hard -"@ethersproject/hash@npm:5.6.1, @ethersproject/hash@npm:>=5.0.0-beta.128, @ethersproject/hash@npm:^5.0.4, @ethersproject/hash@npm:^5.6.1": +"@ethersproject/hash@npm:5.6.1, @ethersproject/hash@npm:>=5.0.0-beta.128, @ethersproject/hash@npm:^5.6.1": version: 5.6.1 resolution: "@ethersproject/hash@npm:5.6.1" dependencies: @@ -448,6 +472,23 @@ __metadata: languageName: node linkType: hard +"@ethersproject/hash@npm:^5.7.0": + version: 5.7.0 + resolution: "@ethersproject/hash@npm:5.7.0" + dependencies: + "@ethersproject/abstract-signer": ^5.7.0 + "@ethersproject/address": ^5.7.0 + "@ethersproject/base64": ^5.7.0 + "@ethersproject/bignumber": ^5.7.0 + "@ethersproject/bytes": ^5.7.0 + "@ethersproject/keccak256": ^5.7.0 + "@ethersproject/logger": ^5.7.0 + "@ethersproject/properties": ^5.7.0 + "@ethersproject/strings": ^5.7.0 + checksum: 6e9fa8d14eb08171cd32f17f98cc108ec2aeca74a427655f0d689c550fee0b22a83b3b400fad7fb3f41cf14d4111f87f170aa7905bcbcd1173a55f21b06262ef + languageName: node + linkType: hard + "@ethersproject/hdnode@npm:5.6.2, @ethersproject/hdnode@npm:^5.6.2": version: 5.6.2 resolution: "@ethersproject/hdnode@npm:5.6.2" @@ -489,7 +530,7 @@ __metadata: languageName: node linkType: hard -"@ethersproject/keccak256@npm:5.6.1, @ethersproject/keccak256@npm:>=5.0.0-beta.127, @ethersproject/keccak256@npm:^5.0.3, @ethersproject/keccak256@npm:^5.6.1": +"@ethersproject/keccak256@npm:5.6.1, @ethersproject/keccak256@npm:>=5.0.0-beta.127, @ethersproject/keccak256@npm:^5.6.1": version: 5.6.1 resolution: "@ethersproject/keccak256@npm:5.6.1" dependencies: @@ -499,13 +540,30 @@ __metadata: languageName: node linkType: hard -"@ethersproject/logger@npm:5.6.0, @ethersproject/logger@npm:>=5.0.0-beta.129, @ethersproject/logger@npm:^5.0.5, @ethersproject/logger@npm:^5.6.0": +"@ethersproject/keccak256@npm:^5.7.0": + version: 5.7.0 + resolution: "@ethersproject/keccak256@npm:5.7.0" + dependencies: + "@ethersproject/bytes": ^5.7.0 + js-sha3: 0.8.0 + checksum: ff70950d82203aab29ccda2553422cbac2e7a0c15c986bd20a69b13606ed8bb6e4fdd7b67b8d3b27d4f841e8222cbaccd33ed34be29f866fec7308f96ed244c6 + languageName: node + linkType: hard + +"@ethersproject/logger@npm:5.6.0, @ethersproject/logger@npm:>=5.0.0-beta.129, @ethersproject/logger@npm:^5.6.0": version: 5.6.0 resolution: "@ethersproject/logger@npm:5.6.0" checksum: 6eee38a973c7a458552278971c109a3e5df3c257e433cb959da9a287ea04628d1f510d41b83bd5f9da5ddc05d97d307ed2162a9ba1b4fcc50664e4f60061636c languageName: node linkType: hard +"@ethersproject/logger@npm:^5.7.0": + version: 5.7.0 + resolution: "@ethersproject/logger@npm:5.7.0" + checksum: 075ab2f605f1fd0813f2e39c3308f77b44a67732b36e712d9bc085f22a84aac4da4f71b39bee50fe78da3e1c812673fadc41180c9970fe5e486e91ea17befe0d + languageName: node + linkType: hard + "@ethersproject/networks@npm:5.6.4, @ethersproject/networks@npm:^5.6.3": version: 5.6.4 resolution: "@ethersproject/networks@npm:5.6.4" @@ -515,6 +573,15 @@ __metadata: languageName: node linkType: hard +"@ethersproject/networks@npm:^5.7.0": + version: 5.7.1 + resolution: "@ethersproject/networks@npm:5.7.1" + dependencies: + "@ethersproject/logger": ^5.7.0 + checksum: 0339f312304c17d9a0adce550edb825d4d2c8c9468c1634c44172c67a9ed256f594da62c4cda5c3837a0f28b7fabc03aca9b492f68ff1fdad337ee861b27bd5d + languageName: node + linkType: hard + "@ethersproject/pbkdf2@npm:5.6.1, @ethersproject/pbkdf2@npm:^5.6.1": version: 5.6.1 resolution: "@ethersproject/pbkdf2@npm:5.6.1" @@ -525,7 +592,7 @@ __metadata: languageName: node linkType: hard -"@ethersproject/properties@npm:5.6.0, @ethersproject/properties@npm:>=5.0.0-beta.131, @ethersproject/properties@npm:^5.0.3, @ethersproject/properties@npm:^5.6.0": +"@ethersproject/properties@npm:5.6.0, @ethersproject/properties@npm:>=5.0.0-beta.131, @ethersproject/properties@npm:^5.6.0": version: 5.6.0 resolution: "@ethersproject/properties@npm:5.6.0" dependencies: @@ -534,6 +601,15 @@ __metadata: languageName: node linkType: hard +"@ethersproject/properties@npm:^5.7.0": + version: 5.7.0 + resolution: "@ethersproject/properties@npm:5.7.0" + dependencies: + "@ethersproject/logger": ^5.7.0 + checksum: 6ab0ccf0c3aadc9221e0cdc5306ce6cd0df7f89f77d77bccdd1277182c9ead0202cd7521329ba3acde130820bf8af299e17cf567d0d497c736ee918207bbf59f + languageName: node + linkType: hard + "@ethersproject/providers@npm:5.6.8, @ethersproject/providers@npm:^5.6.8": version: 5.6.8 resolution: "@ethersproject/providers@npm:5.6.8" @@ -582,6 +658,16 @@ __metadata: languageName: node linkType: hard +"@ethersproject/rlp@npm:^5.7.0": + version: 5.7.0 + resolution: "@ethersproject/rlp@npm:5.7.0" + dependencies: + "@ethersproject/bytes": ^5.7.0 + "@ethersproject/logger": ^5.7.0 + checksum: bce165b0f7e68e4d091c9d3cf47b247cac33252df77a095ca4281d32d5eeaaa3695d9bc06b2b057c5015353a68df89f13a4a54a72e888e4beeabbe56b15dda6e + languageName: node + linkType: hard + "@ethersproject/sha2@npm:5.6.1, @ethersproject/sha2@npm:^5.6.1": version: 5.6.1 resolution: "@ethersproject/sha2@npm:5.6.1" @@ -607,6 +693,20 @@ __metadata: languageName: node linkType: hard +"@ethersproject/signing-key@npm:^5.7.0": + version: 5.7.0 + resolution: "@ethersproject/signing-key@npm:5.7.0" + dependencies: + "@ethersproject/bytes": ^5.7.0 + "@ethersproject/logger": ^5.7.0 + "@ethersproject/properties": ^5.7.0 + bn.js: ^5.2.1 + elliptic: 6.5.4 + hash.js: 1.1.7 + checksum: 8f8de09b0aac709683bbb49339bc0a4cd2f95598f3546436c65d6f3c3a847ffa98e06d35e9ed2b17d8030bd2f02db9b7bd2e11c5cf8a71aad4537487ab4cf03a + languageName: node + linkType: hard + "@ethersproject/solidity@npm:5.6.1": version: 5.6.1 resolution: "@ethersproject/solidity@npm:5.6.1" @@ -621,7 +721,7 @@ __metadata: languageName: node linkType: hard -"@ethersproject/strings@npm:5.6.1, @ethersproject/strings@npm:>=5.0.0-beta.130, @ethersproject/strings@npm:^5.0.4, @ethersproject/strings@npm:^5.6.1": +"@ethersproject/strings@npm:5.6.1, @ethersproject/strings@npm:>=5.0.0-beta.130, @ethersproject/strings@npm:^5.6.1": version: 5.6.1 resolution: "@ethersproject/strings@npm:5.6.1" dependencies: @@ -632,6 +732,17 @@ __metadata: languageName: node linkType: hard +"@ethersproject/strings@npm:^5.7.0": + version: 5.7.0 + resolution: "@ethersproject/strings@npm:5.7.0" + dependencies: + "@ethersproject/bytes": ^5.7.0 + "@ethersproject/constants": ^5.7.0 + "@ethersproject/logger": ^5.7.0 + checksum: 5ff78693ae3fdf3cf23e1f6dc047a61e44c8197d2408c42719fef8cb7b7b3613a4eec88ac0ed1f9f5558c74fe0de7ae3195a29ca91a239c74b9f444d8e8b50df + languageName: node + linkType: hard + "@ethersproject/transactions@npm:5.6.2, @ethersproject/transactions@npm:^5.0.0-beta.135, @ethersproject/transactions@npm:^5.6.2": version: 5.6.2 resolution: "@ethersproject/transactions@npm:5.6.2" @@ -649,6 +760,23 @@ __metadata: languageName: node linkType: hard +"@ethersproject/transactions@npm:^5.7.0": + version: 5.7.0 + resolution: "@ethersproject/transactions@npm:5.7.0" + dependencies: + "@ethersproject/address": ^5.7.0 + "@ethersproject/bignumber": ^5.7.0 + "@ethersproject/bytes": ^5.7.0 + "@ethersproject/constants": ^5.7.0 + "@ethersproject/keccak256": ^5.7.0 + "@ethersproject/logger": ^5.7.0 + "@ethersproject/properties": ^5.7.0 + "@ethersproject/rlp": ^5.7.0 + "@ethersproject/signing-key": ^5.7.0 + checksum: a31b71996d2b283f68486241bff0d3ea3f1ba0e8f1322a8fffc239ccc4f4a7eb2ea9994b8fd2f093283fd75f87bae68171e01b6265261f821369aca319884a79 + languageName: node + linkType: hard + "@ethersproject/units@npm:5.6.1": version: 5.6.1 resolution: "@ethersproject/units@npm:5.6.1" @@ -696,6 +824,19 @@ __metadata: languageName: node linkType: hard +"@ethersproject/web@npm:^5.7.0": + version: 5.7.1 + resolution: "@ethersproject/web@npm:5.7.1" + dependencies: + "@ethersproject/base64": ^5.7.0 + "@ethersproject/bytes": ^5.7.0 + "@ethersproject/logger": ^5.7.0 + "@ethersproject/properties": ^5.7.0 + "@ethersproject/strings": ^5.7.0 + checksum: 7028c47103f82fd2e2c197ce0eecfacaa9180ffeec7de7845b1f4f9b19d84081b7a48227aaddde05a4aaa526af574a9a0ce01cc0fc75e3e371f84b38b5b16b2b + languageName: node + linkType: hard + "@ethersproject/wordlists@npm:5.6.1, @ethersproject/wordlists@npm:^5.6.1": version: 5.6.1 resolution: "@ethersproject/wordlists@npm:5.6.1" @@ -819,19 +960,302 @@ __metadata: languageName: node linkType: hard -"@nomiclabs/hardhat-ethers@npm:^2.1.0": - version: 2.1.0 - resolution: "@nomiclabs/hardhat-ethers@npm:2.1.0" +"@nomicfoundation/ethereumjs-block@npm:^4.0.0": + version: 4.0.0 + resolution: "@nomicfoundation/ethereumjs-block@npm:4.0.0" + dependencies: + "@nomicfoundation/ethereumjs-common": ^3.0.0 + "@nomicfoundation/ethereumjs-rlp": ^4.0.0 + "@nomicfoundation/ethereumjs-trie": ^5.0.0 + "@nomicfoundation/ethereumjs-tx": ^4.0.0 + "@nomicfoundation/ethereumjs-util": ^8.0.0 + ethereum-cryptography: 0.1.3 + checksum: a57a33dda7724f0a46ef2e0ca0dbb1b427268f4135e8c23eee9ab5730a79369d52122faba7a010d71bca3046f7ce644ed95e4a34d5f2221ecaa5d94886d84b11 + languageName: node + linkType: hard + +"@nomicfoundation/ethereumjs-blockchain@npm:^6.0.0": + version: 6.0.0 + resolution: "@nomicfoundation/ethereumjs-blockchain@npm:6.0.0" + dependencies: + "@nomicfoundation/ethereumjs-block": ^4.0.0 + "@nomicfoundation/ethereumjs-common": ^3.0.0 + "@nomicfoundation/ethereumjs-ethash": ^2.0.0 + "@nomicfoundation/ethereumjs-rlp": ^4.0.0 + "@nomicfoundation/ethereumjs-trie": ^5.0.0 + "@nomicfoundation/ethereumjs-util": ^8.0.0 + abstract-level: ^1.0.3 + debug: ^4.3.3 + ethereum-cryptography: 0.1.3 + level: ^8.0.0 + lru-cache: ^5.1.1 + memory-level: ^1.0.0 + checksum: 5605c1d249924321de98c1728b5b832ee6488b690a42c829db21afa96f5c152c73afdec6aa4758cb9b24ec7ac19ec9f3146b63cf837e1b91d364e4c37b497881 + languageName: node + linkType: hard + +"@nomicfoundation/ethereumjs-common@npm:^3.0.0": + version: 3.0.0 + resolution: "@nomicfoundation/ethereumjs-common@npm:3.0.0" + dependencies: + "@nomicfoundation/ethereumjs-util": ^8.0.0 + crc-32: ^1.2.0 + checksum: 6a62908e5ccd8a4f56b841bd6ba9eef21dffafdd505f18b6b886d86ba4287cd12a2c632d521c5fddf2c6fca5a840f580d7601d89820098f6c1f8311db41e496b + languageName: node + linkType: hard + +"@nomicfoundation/ethereumjs-ethash@npm:^2.0.0": + version: 2.0.0 + resolution: "@nomicfoundation/ethereumjs-ethash@npm:2.0.0" + dependencies: + "@nomicfoundation/ethereumjs-block": ^4.0.0 + "@nomicfoundation/ethereumjs-rlp": ^4.0.0 + "@nomicfoundation/ethereumjs-util": ^8.0.0 + abstract-level: ^1.0.3 + bigint-crypto-utils: ^3.0.23 + ethereum-cryptography: 0.1.3 + checksum: 60133df2d450179f2ab26e8784b1bd79b37411bb047a7dace655499749893750f0f8d6d573f182ebcf4dba35f2da6301b0ad1b80dbe7637bb0d5155ccb189fda + languageName: node + linkType: hard + +"@nomicfoundation/ethereumjs-evm@npm:^1.0.0, @nomicfoundation/ethereumjs-evm@npm:^1.0.0-rc.3": + version: 1.0.0 + resolution: "@nomicfoundation/ethereumjs-evm@npm:1.0.0" + dependencies: + "@nomicfoundation/ethereumjs-common": ^3.0.0 + "@nomicfoundation/ethereumjs-util": ^8.0.0 + "@types/async-eventemitter": ^0.2.1 + async-eventemitter: ^0.2.4 + debug: ^4.3.3 + ethereum-cryptography: 0.1.3 + mcl-wasm: ^0.7.1 + rustbn.js: ~0.2.0 + checksum: d1ffaa1a02c1f78099a5cfe802f2738c498063e383a51ede4b7194c809d7bdb8d322edfea4d83090c8c1b83b42fa9febbd571c35f5cf27f18d47fb664f3ab61e + languageName: node + linkType: hard + +"@nomicfoundation/ethereumjs-rlp@npm:^4.0.0, @nomicfoundation/ethereumjs-rlp@npm:^4.0.0-beta.2": + version: 4.0.0 + resolution: "@nomicfoundation/ethereumjs-rlp@npm:4.0.0" + bin: + rlp: bin/rlp + checksum: b358d239e5a24884f0446d52159c8115b0eb1d6907179dc968df5054dccea7eff72f2d12522c911b6e08bb4b5d3f5f8e1d86a45cb1a24a4831cbb109743d4407 + languageName: node + linkType: hard + +"@nomicfoundation/ethereumjs-statemanager@npm:^1.0.0": + version: 1.0.0 + resolution: "@nomicfoundation/ethereumjs-statemanager@npm:1.0.0" + dependencies: + "@nomicfoundation/ethereumjs-common": ^3.0.0 + "@nomicfoundation/ethereumjs-rlp": ^4.0.0 + "@nomicfoundation/ethereumjs-trie": ^5.0.0 + "@nomicfoundation/ethereumjs-util": ^8.0.0 + debug: ^4.3.3 + ethereum-cryptography: 0.1.3 + functional-red-black-tree: ^1.0.1 + checksum: fad02ea922fbe25328186ea2eb43bdba63def57822f373ce213be26125ee8d3c90cf3b6f626e6876637cdb842e3c2b788fb8891fcf1aca3fd655e1c0d9a7e936 + languageName: node + linkType: hard + +"@nomicfoundation/ethereumjs-trie@npm:^5.0.0": + version: 5.0.0 + resolution: "@nomicfoundation/ethereumjs-trie@npm:5.0.0" + dependencies: + "@nomicfoundation/ethereumjs-rlp": ^4.0.0 + "@nomicfoundation/ethereumjs-util": ^8.0.0 + ethereum-cryptography: 0.1.3 + readable-stream: ^3.6.0 + checksum: 468de7ffe05473f0f05940e74bba01652dd9a4ff155a13e0a5395551e53557afde47d98f496f6323824bccfaeee8de4e22fef9b7f88d3bbd4e97cadc54e2e4f9 + languageName: node + linkType: hard + +"@nomicfoundation/ethereumjs-tx@npm:^4.0.0": + version: 4.0.0 + resolution: "@nomicfoundation/ethereumjs-tx@npm:4.0.0" + dependencies: + "@nomicfoundation/ethereumjs-common": ^3.0.0 + "@nomicfoundation/ethereumjs-rlp": ^4.0.0 + "@nomicfoundation/ethereumjs-util": ^8.0.0 + ethereum-cryptography: 0.1.3 + checksum: d2c0e3384aaa9f3b58232c531a4efd524be257e7257f23c3beed6ec9cf5fba6345cb632b3a464ae0a2aa99fd9e4a2d3e2d5c501593c5466e6ab629f05255791e + languageName: node + linkType: hard + +"@nomicfoundation/ethereumjs-util@npm:^8.0.0, @nomicfoundation/ethereumjs-util@npm:^8.0.0-rc.3": + version: 8.0.0 + resolution: "@nomicfoundation/ethereumjs-util@npm:8.0.0" + dependencies: + "@nomicfoundation/ethereumjs-rlp": ^4.0.0-beta.2 + ethereum-cryptography: 0.1.3 + checksum: a39be4c8d3dea4fae1e969b47138d718cac31bf248bb517766a42c97ca5850ca3ddf16c66d8e404fa0a0363fd6898ae2e716d75da2ed4113e610d26026e4cefb + languageName: node + linkType: hard + +"@nomicfoundation/ethereumjs-vm@npm:^6.0.0, @nomicfoundation/ethereumjs-vm@npm:^6.0.0-rc.3": + version: 6.0.0 + resolution: "@nomicfoundation/ethereumjs-vm@npm:6.0.0" + dependencies: + "@nomicfoundation/ethereumjs-block": ^4.0.0 + "@nomicfoundation/ethereumjs-blockchain": ^6.0.0 + "@nomicfoundation/ethereumjs-common": ^3.0.0 + "@nomicfoundation/ethereumjs-evm": ^1.0.0 + "@nomicfoundation/ethereumjs-rlp": ^4.0.0 + "@nomicfoundation/ethereumjs-statemanager": ^1.0.0 + "@nomicfoundation/ethereumjs-trie": ^5.0.0 + "@nomicfoundation/ethereumjs-tx": ^4.0.0 + "@nomicfoundation/ethereumjs-util": ^8.0.0 + "@types/async-eventemitter": ^0.2.1 + async-eventemitter: ^0.2.4 + debug: ^4.3.3 + ethereum-cryptography: 0.1.3 + functional-red-black-tree: ^1.0.1 + mcl-wasm: ^0.7.1 + rustbn.js: ~0.2.0 + checksum: 3c0e10b377579d74bfdcfd056d5545b605f767982e41038d036c8219a50fe3564c7f146fdd04385d64f48f94b9d95c378d7a37955c5100c46c568a29f54ea737 + languageName: node + linkType: hard + +"@nomicfoundation/hardhat-chai-matchers@npm:^1.0.6": + version: 1.0.6 + resolution: "@nomicfoundation/hardhat-chai-matchers@npm:1.0.6" + dependencies: + "@ethersproject/abi": ^5.1.2 + "@types/chai-as-promised": ^7.1.3 + chai-as-promised: ^7.1.1 + deep-eql: ^4.0.1 + ordinal: ^1.0.3 + peerDependencies: + "@nomiclabs/hardhat-ethers": ^2.0.0 + chai: ^4.2.0 + ethers: ^5.0.0 + hardhat: ^2.9.4 + checksum: c388e5ed9068f2ba7c227737ab7312dd03405d5fab195247b061f2fa52e700fbd0fb65359c2d4f2086f2905bfca642c19a9122d034533edd936f89aea65ac7f2 + languageName: node + linkType: hard + +"@nomicfoundation/solidity-analyzer-darwin-arm64@npm:0.1.0": + version: 0.1.0 + resolution: "@nomicfoundation/solidity-analyzer-darwin-arm64@npm:0.1.0" + conditions: os=darwin & cpu=arm64 + languageName: node + linkType: hard + +"@nomicfoundation/solidity-analyzer-darwin-x64@npm:0.1.0": + version: 0.1.0 + resolution: "@nomicfoundation/solidity-analyzer-darwin-x64@npm:0.1.0" + conditions: os=darwin & cpu=x64 + languageName: node + linkType: hard + +"@nomicfoundation/solidity-analyzer-freebsd-x64@npm:0.1.0": + version: 0.1.0 + resolution: "@nomicfoundation/solidity-analyzer-freebsd-x64@npm:0.1.0" + conditions: os=freebsd & cpu=x64 + languageName: node + linkType: hard + +"@nomicfoundation/solidity-analyzer-linux-arm64-gnu@npm:0.1.0": + version: 0.1.0 + resolution: "@nomicfoundation/solidity-analyzer-linux-arm64-gnu@npm:0.1.0" + conditions: os=linux & cpu=arm64 & libc=glibc + languageName: node + linkType: hard + +"@nomicfoundation/solidity-analyzer-linux-arm64-musl@npm:0.1.0": + version: 0.1.0 + resolution: "@nomicfoundation/solidity-analyzer-linux-arm64-musl@npm:0.1.0" + conditions: os=linux & cpu=arm64 & libc=musl + languageName: node + linkType: hard + +"@nomicfoundation/solidity-analyzer-linux-x64-gnu@npm:0.1.0": + version: 0.1.0 + resolution: "@nomicfoundation/solidity-analyzer-linux-x64-gnu@npm:0.1.0" + conditions: os=linux & cpu=x64 & libc=glibc + languageName: node + linkType: hard + +"@nomicfoundation/solidity-analyzer-linux-x64-musl@npm:0.1.0": + version: 0.1.0 + resolution: "@nomicfoundation/solidity-analyzer-linux-x64-musl@npm:0.1.0" + conditions: os=linux & cpu=x64 & libc=musl + languageName: node + linkType: hard + +"@nomicfoundation/solidity-analyzer-win32-arm64-msvc@npm:0.1.0": + version: 0.1.0 + resolution: "@nomicfoundation/solidity-analyzer-win32-arm64-msvc@npm:0.1.0" + conditions: os=win32 & cpu=arm64 + languageName: node + linkType: hard + +"@nomicfoundation/solidity-analyzer-win32-ia32-msvc@npm:0.1.0": + version: 0.1.0 + resolution: "@nomicfoundation/solidity-analyzer-win32-ia32-msvc@npm:0.1.0" + conditions: os=win32 & cpu=ia32 + languageName: node + linkType: hard + +"@nomicfoundation/solidity-analyzer-win32-x64-msvc@npm:0.1.0": + version: 0.1.0 + resolution: "@nomicfoundation/solidity-analyzer-win32-x64-msvc@npm:0.1.0" + conditions: os=win32 & cpu=x64 + languageName: node + linkType: hard + +"@nomicfoundation/solidity-analyzer@npm:^0.1.0": + version: 0.1.0 + resolution: "@nomicfoundation/solidity-analyzer@npm:0.1.0" + dependencies: + "@nomicfoundation/solidity-analyzer-darwin-arm64": 0.1.0 + "@nomicfoundation/solidity-analyzer-darwin-x64": 0.1.0 + "@nomicfoundation/solidity-analyzer-freebsd-x64": 0.1.0 + "@nomicfoundation/solidity-analyzer-linux-arm64-gnu": 0.1.0 + "@nomicfoundation/solidity-analyzer-linux-arm64-musl": 0.1.0 + "@nomicfoundation/solidity-analyzer-linux-x64-gnu": 0.1.0 + "@nomicfoundation/solidity-analyzer-linux-x64-musl": 0.1.0 + "@nomicfoundation/solidity-analyzer-win32-arm64-msvc": 0.1.0 + "@nomicfoundation/solidity-analyzer-win32-ia32-msvc": 0.1.0 + "@nomicfoundation/solidity-analyzer-win32-x64-msvc": 0.1.0 + dependenciesMeta: + "@nomicfoundation/solidity-analyzer-darwin-arm64": + optional: true + "@nomicfoundation/solidity-analyzer-darwin-x64": + optional: true + "@nomicfoundation/solidity-analyzer-freebsd-x64": + optional: true + "@nomicfoundation/solidity-analyzer-linux-arm64-gnu": + optional: true + "@nomicfoundation/solidity-analyzer-linux-arm64-musl": + optional: true + "@nomicfoundation/solidity-analyzer-linux-x64-gnu": + optional: true + "@nomicfoundation/solidity-analyzer-linux-x64-musl": + optional: true + "@nomicfoundation/solidity-analyzer-win32-arm64-msvc": + optional: true + "@nomicfoundation/solidity-analyzer-win32-ia32-msvc": + optional: true + "@nomicfoundation/solidity-analyzer-win32-x64-msvc": + optional: true + checksum: 42dc5ba40e76bf14945fb6a423554bbbc6c99596675065d7d6f3c9a49ec39e37f3f77ecfedcf906fdb1bb33b033a5d92a90c645c886d6ff23334c8af8b14ff67 + languageName: node + linkType: hard + +"@nomiclabs/hardhat-ethers@npm:^2.2.1": + version: 2.2.1 + resolution: "@nomiclabs/hardhat-ethers@npm:2.2.1" peerDependencies: ethers: ^5.0.0 hardhat: ^2.0.0 - checksum: 16fcde7fbce0b953daa3520148b0054d4951434d39d454b13dedb795d10b89893b199f1c3ed2856c196a229fc470c6cbd3ceff5895a94f52996b99a55515525d + checksum: 8cdbf7068f15ee993142ab600074938d05b42af73392a5b12c8eb607a5bca2fac977a6a85955e0b0285541415ad520626e7fb3d33ca7cc112d61ee928358e2f6 languageName: node linkType: hard -"@nomiclabs/hardhat-etherscan@npm:^3.1.0": - version: 3.1.0 - resolution: "@nomiclabs/hardhat-etherscan@npm:3.1.0" +"@nomiclabs/hardhat-etherscan@npm:^3.1.2": + version: 3.1.2 + resolution: "@nomiclabs/hardhat-etherscan@npm:3.1.2" dependencies: "@ethersproject/abi": ^5.1.2 "@ethersproject/address": ^5.0.2 @@ -845,7 +1269,7 @@ __metadata: undici: ^5.4.0 peerDependencies: hardhat: ^2.0.4 - checksum: 3f28abc39edce2936226b6d0087c3be78bffcba68b6935f3f60767f0e10233e940ddc74803dff91f7ddf9464a7199aab00fba08d8b3865dbc2f8936f53a7a5a5 + checksum: 7f225d05fe4d8549472bed2a5c8b8d02e1be652c6c1e00ac83416c048919a2a665d3ad7833c8d6f4513bb6f444e2a165d32a99051b179da7d35aaa1247c32947 languageName: node linkType: hard @@ -1113,7 +1537,7 @@ __metadata: languageName: node linkType: hard -"@solidity-parser/parser@npm:^0.14.2, @solidity-parser/parser@npm:^0.14.3": +"@solidity-parser/parser@npm:^0.14.3": version: 0.14.3 resolution: "@solidity-parser/parser@npm:0.14.3" dependencies: @@ -1138,35 +1562,6 @@ __metadata: languageName: node linkType: hard -"@truffle/error@npm:^0.1.0": - version: 0.1.0 - resolution: "@truffle/error@npm:0.1.0" - checksum: bb4765ec18b0e3cc0d1a4d879bbd1ed84e2354cc976a721909b52d7659eb8d0eadc40c5e1011b167896e3967cfaa6af8d9f74e28c8c17d66e4aef02ca821ec24 - languageName: node - linkType: hard - -"@truffle/interface-adapter@npm:^0.5.17": - version: 0.5.17 - resolution: "@truffle/interface-adapter@npm:0.5.17" - dependencies: - bn.js: ^5.1.3 - ethers: ^4.0.32 - web3: 1.5.3 - checksum: d56c649439a614231ebe1a92271919883f16853ba7475ec24a819f22debac212f7821a235b3d70d2d864a2fd55f674323f1a2845cc6dc113d87f91af7bc965b8 - languageName: node - linkType: hard - -"@truffle/provider@npm:^0.2.24": - version: 0.2.55 - resolution: "@truffle/provider@npm:0.2.55" - dependencies: - "@truffle/error": ^0.1.0 - "@truffle/interface-adapter": ^0.5.17 - web3: 1.5.3 - checksum: b698bdf5bd50d4c335f93a11feb73934ffef343fffb4fa8ed75871bc9f0008b9c00bab57366c6ffff8aa9f835f5b014ef41630530469418e72833ddd0c9308c0 - languageName: node - linkType: hard - "@tsconfig/node10@npm:^1.0.7": version: 1.0.9 resolution: "@tsconfig/node10@npm:1.0.9" @@ -1241,10 +1636,10 @@ __metadata: languageName: node linkType: hard -"@types/abstract-leveldown@npm:*": - version: 7.2.0 - resolution: "@types/abstract-leveldown@npm:7.2.0" - checksum: f719ce076f65e47386ad412dbc6c89a6833a812ee2b2361b89a97480c69180cf9686effdabd0c76819ee5eaefeb18b19ae7dc54c06855261db0070a849124d90 +"@types/async-eventemitter@npm:^0.2.1": + version: 0.2.1 + resolution: "@types/async-eventemitter@npm:0.2.1" + checksum: 36ba0a6f52082f76b19b9123a2fa0497f94fe15218fa54040cc45f0edff483ec3be93a38c177cd4dab79f5e32333fbdf3682d4dc94197438e86694b1fddd6896 languageName: node linkType: hard @@ -1266,6 +1661,15 @@ __metadata: languageName: node linkType: hard +"@types/chai-as-promised@npm:^7.1.3": + version: 7.1.5 + resolution: "@types/chai-as-promised@npm:7.1.5" + dependencies: + "@types/chai": "*" + checksum: 7c1345c6e32513d52d8e562ec173c23161648d6b792046525f18803a9932d7b3ad3dca8f0181e3c529ec42b106099f174e34edeb184d61dc93e32c98b5132fd4 + languageName: node + linkType: hard + "@types/chai@npm:*, @types/chai@npm:^4.3.1": version: 4.3.1 resolution: "@types/chai@npm:4.3.1" @@ -1324,24 +1728,6 @@ __metadata: languageName: node linkType: hard -"@types/level-errors@npm:*": - version: 3.0.0 - resolution: "@types/level-errors@npm:3.0.0" - checksum: ad9392663439306677ac9cb704f8fa0b64c300dfea4f3494369eb78a2e09c194156cbab2b52c71a361a09b735d54a2de65195dcadba0ec7db1d14a320198133e - languageName: node - linkType: hard - -"@types/levelup@npm:^4.3.0": - version: 4.3.3 - resolution: "@types/levelup@npm:4.3.3" - dependencies: - "@types/abstract-leveldown": "*" - "@types/level-errors": "*" - "@types/node": "*" - checksum: 04969bb805035960b8d6650e8f76893be7ba70267bb7012f6f00d67a0cf096ada552355629791b3f5925e9cdb6912d3fe08892c33c3c583e8fd02099b573bdd7 - languageName: node - linkType: hard - "@types/lru-cache@npm:^5.1.0": version: 5.1.1 resolution: "@types/lru-cache@npm:5.1.1" @@ -1664,6 +2050,21 @@ __metadata: languageName: node linkType: hard +"abstract-level@npm:^1.0.0, abstract-level@npm:^1.0.2, abstract-level@npm:^1.0.3": + version: 1.0.3 + resolution: "abstract-level@npm:1.0.3" + dependencies: + buffer: ^6.0.3 + catering: ^2.1.0 + is-buffer: ^2.0.5 + level-supports: ^4.0.0 + level-transcoder: ^1.0.1 + module-error: ^1.0.1 + queue-microtask: ^1.2.3 + checksum: 70d61a3924526ebc257b138992052f9ff571a6cee5a7660836e37a1cc7081273c3acf465dd2f5e1897b38dc743a6fd9dba14a5d8a2a9d39e5787cd3da99f301d + languageName: node + linkType: hard + "abstract-leveldown@npm:3.0.0": version: 3.0.0 resolution: "abstract-leveldown@npm:3.0.0" @@ -1691,19 +2092,6 @@ __metadata: languageName: node linkType: hard -"abstract-leveldown@npm:^6.2.1": - version: 6.3.0 - resolution: "abstract-leveldown@npm:6.3.0" - dependencies: - buffer: ^5.5.0 - immediate: ^3.2.3 - level-concat-iterator: ~2.0.0 - level-supports: ~1.0.0 - xtend: ~4.0.0 - checksum: 121a8509d8c6a540e656c2a69e5b8d853d4df71072011afefc868b98076991bb00120550e90643de9dc18889c675f62413409eeb4c8c204663124c7d215e4ec3 - languageName: node - linkType: hard - "abstract-leveldown@npm:~2.6.0": version: 2.6.3 resolution: "abstract-leveldown@npm:2.6.3" @@ -1713,19 +2101,6 @@ __metadata: languageName: node linkType: hard -"abstract-leveldown@npm:~6.2.1": - version: 6.2.3 - resolution: "abstract-leveldown@npm:6.2.3" - dependencies: - buffer: ^5.5.0 - immediate: ^3.2.3 - level-concat-iterator: ~2.0.0 - level-supports: ~1.0.0 - xtend: ~4.0.0 - checksum: 00202b2eb7955dd7bc04f3e44d225e60160cedb8f96fe6ae0e6dca9c356d57071f001ece8ae1d53f48095c4c036d92b3440f2bc7666730610ddea030f9fbde4a - languageName: node - linkType: hard - "accepts@npm:~1.3.8": version: 1.3.8 resolution: "accepts@npm:1.3.8" @@ -2277,13 +2652,6 @@ __metadata: languageName: node linkType: hard -"available-typed-arrays@npm:^1.0.5": - version: 1.0.5 - resolution: "available-typed-arrays@npm:1.0.5" - checksum: 20eb47b3cefd7db027b9bbb993c658abd36d4edd3fe1060e83699a03ee275b0c9b216cc076ff3f2db29073225fb70e7613987af14269ac1fe2a19803ccc97f1a - languageName: node - linkType: hard - "aws-sign2@npm:~0.7.0": version: 0.7.0 resolution: "aws-sign2@npm:0.7.0" @@ -2990,6 +3358,22 @@ __metadata: languageName: node linkType: hard +"bigint-crypto-utils@npm:^3.0.23": + version: 3.1.7 + resolution: "bigint-crypto-utils@npm:3.1.7" + dependencies: + bigint-mod-arith: ^3.1.0 + checksum: 10fa35d3e3d37639c8d501f45e0044c9062e7aa60783ae514e4d4ed3235ac24ac180e0dd0c77dad8cb5410ef24de42e1ea12527a997fec4c59f15fa83ea477ba + languageName: node + linkType: hard + +"bigint-mod-arith@npm:^3.1.0": + version: 3.1.2 + resolution: "bigint-mod-arith@npm:3.1.2" + checksum: badddd745f6e6c45674b22335d26a9ea83250e749abde20c5f84b24afbc747e259bc36798530953332349ed898f38ec39125b326cae8b8ee2dddfaea7ddf8448 + languageName: node + linkType: hard + "bignumber.js@npm:^9.0.0, bignumber.js@npm:^9.0.1": version: 9.0.2 resolution: "bignumber.js@npm:9.0.2" @@ -3045,7 +3429,7 @@ __metadata: languageName: node linkType: hard -"bn.js@npm:^5.0.0, bn.js@npm:^5.1.1, bn.js@npm:^5.1.2, bn.js@npm:^5.1.3, bn.js@npm:^5.2.0, bn.js@npm:^5.2.1": +"bn.js@npm:^5.0.0, bn.js@npm:^5.1.1, bn.js@npm:^5.1.2, bn.js@npm:^5.2.0, bn.js@npm:^5.2.1": version: 5.2.1 resolution: "bn.js@npm:5.2.1" checksum: 3dd8c8d38055fedfa95c1d5fc3c99f8dd547b36287b37768db0abab3c239711f88ff58d18d155dd8ad902b0b0cee973747b7ae20ea12a09473272b0201c9edd3 @@ -3125,6 +3509,18 @@ __metadata: languageName: node linkType: hard +"browser-level@npm:^1.0.1": + version: 1.0.1 + resolution: "browser-level@npm:1.0.1" + dependencies: + abstract-level: ^1.0.2 + catering: ^2.1.1 + module-error: ^1.0.2 + run-parallel-limit: ^1.1.0 + checksum: 67fbc77ce832940bfa25073eccff279f512ad56f545deb996a5b23b02316f5e76f4a79d381acc27eda983f5c9a2566aaf9c97e4fdd0748288c4407307537a29b + languageName: node + linkType: hard + "browser-stdout@npm:1.3.1": version: 1.3.1 resolution: "browser-stdout@npm:1.3.1" @@ -3268,6 +3664,16 @@ __metadata: languageName: node linkType: hard +"buffer@npm:^6.0.3": + version: 6.0.3 + resolution: "buffer@npm:6.0.3" + dependencies: + base64-js: ^1.3.1 + ieee754: ^1.2.1 + checksum: 5ad23293d9a731e4318e420025800b42bf0d264004c0286c8cc010af7a270c7a0f6522e84f54b9ad65cbd6db20b8badbfd8d2ebf4f80fa03dab093b89e68c3f9 + languageName: node + linkType: hard + "bufferutil@npm:^4.0.1": version: 4.0.6 resolution: "bufferutil@npm:4.0.6" @@ -3467,6 +3873,13 @@ __metadata: languageName: node linkType: hard +"catering@npm:^2.1.0, catering@npm:^2.1.1": + version: 2.1.1 + resolution: "catering@npm:2.1.1" + checksum: 205daefa69c935b0c19f3d8f2e0a520dd69aebe9bda55902958003f7c9cff8f967dfb90071b421bd6eb618576f657a89d2bc0986872c9bc04bbd66655e9d4bd6 + languageName: node + linkType: hard + "cbor@npm:^5.0.2": version: 5.2.0 resolution: "cbor@npm:5.2.0" @@ -3486,6 +3899,17 @@ __metadata: languageName: node linkType: hard +"chai-as-promised@npm:^7.1.1": + version: 7.1.1 + resolution: "chai-as-promised@npm:7.1.1" + dependencies: + check-error: ^1.0.2 + peerDependencies: + chai: ">= 2.1.2 < 5" + checksum: 7262868a5b51a12af4e432838ddf97a893109266a505808e1868ba63a12de7ee1166e9d43b5c501a190c377c1b11ecb9ff8e093c89f097ad96c397e8ec0f8d6a + languageName: node + linkType: hard + "chai@npm:^4.3.6": version: 4.3.6 resolution: "chai@npm:4.3.6" @@ -3666,6 +4090,20 @@ __metadata: languageName: node linkType: hard +"classic-level@npm:^1.2.0": + version: 1.2.0 + resolution: "classic-level@npm:1.2.0" + dependencies: + abstract-level: ^1.0.2 + catering: ^2.1.0 + module-error: ^1.0.1 + napi-macros: ~2.0.0 + node-gyp: latest + node-gyp-build: ^4.3.0 + checksum: 88ddd12f2192c2775107d5e462998ac01095cb0222ca01dc2be77d8dcbbf9883c4c0a0248529cceee40a2f1232c68027b1aca731da9f767ad8e9483cbd61dd37 + languageName: node + linkType: hard + "clean-stack@npm:^2.0.0": version: 2.2.0 resolution: "clean-stack@npm:2.2.0" @@ -4269,6 +4707,15 @@ __metadata: languageName: node linkType: hard +"deep-eql@npm:^4.0.1": + version: 4.1.3 + resolution: "deep-eql@npm:4.1.3" + dependencies: + type-detect: ^4.0.0 + checksum: 7f6d30cb41c713973dc07eaadded848b2ab0b835e518a88b91bea72f34e08c4c71d167a722a6f302d3a6108f05afd8e6d7650689a84d5d29ec7fe6220420397f + languageName: node + linkType: hard + "deep-equal@npm:~1.1.1": version: 1.1.1 resolution: "deep-equal@npm:1.1.1" @@ -4323,16 +4770,6 @@ __metadata: languageName: node linkType: hard -"deferred-leveldown@npm:~5.3.0": - version: 5.3.0 - resolution: "deferred-leveldown@npm:5.3.0" - dependencies: - abstract-leveldown: ~6.2.1 - inherits: ^2.0.3 - checksum: 5631e153528bb9de1aa60d59a5065d1a519374c5e4c1d486f2190dba4008dcf5c2ee8dd7f2f81396fc4d5a6bb6e7d0055e3dfe68afe00da02adaa3bf329addf7 - languageName: node - linkType: hard - "define-properties@npm:^1.1.2, define-properties@npm:^1.1.3, define-properties@npm:^1.1.4": version: 1.1.4 resolution: "define-properties@npm:1.1.4" @@ -4466,6 +4903,13 @@ __metadata: languageName: node linkType: hard +"diff@npm:^5.0.0": + version: 5.1.0 + resolution: "diff@npm:5.1.0" + checksum: c7bf0df7c9bfbe1cf8a678fd1b2137c4fb11be117a67bc18a0e03ae75105e8533dbfb1cda6b46beb3586ef5aed22143ef9d70713977d5fb1f9114e21455fba90 + languageName: node + linkType: hard + "diffie-hellman@npm:^5.0.0": version: 5.0.3 resolution: "diffie-hellman@npm:5.0.3" @@ -4477,6 +4921,15 @@ __metadata: languageName: node linkType: hard +"difflib@npm:^0.2.4": + version: 0.2.4 + resolution: "difflib@npm:0.2.4" + dependencies: + heap: ">= 0.2.0" + checksum: 4f4237b026263ce7471b77d9019b901c2f358a7da89401a80a84a8c3cdc1643a8e70b7495ccbe686cb4d95492eaf5dac119cd9ecbffe5f06bfc175fbe5c20a27 + languageName: node + linkType: hard + "dir-glob@npm:^3.0.1": version: 3.0.1 resolution: "dir-glob@npm:3.0.1" @@ -4616,18 +5069,6 @@ __metadata: languageName: node linkType: hard -"encoding-down@npm:^6.3.0": - version: 6.3.0 - resolution: "encoding-down@npm:6.3.0" - dependencies: - abstract-leveldown: ^6.2.1 - inherits: ^2.0.3 - level-codec: ^9.0.0 - level-errors: ^2.0.0 - checksum: 74043e6d9061a470614ff61d708c849259ab32932a428fd5ddfb0878719804f56a52f59b31cccd95fddc2e636c0fd22dc3e02481fb98d5bf1bdbbbc44ca09bdc - languageName: node - linkType: hard - "encoding@npm:^0.1.11, encoding@npm:^0.1.13": version: 0.1.13 resolution: "encoding@npm:0.1.13" @@ -4689,7 +5130,7 @@ __metadata: languageName: node linkType: hard -"es-abstract@npm:^1.19.0, es-abstract@npm:^1.19.1, es-abstract@npm:^1.19.2, es-abstract@npm:^1.19.5, es-abstract@npm:^1.20.0, es-abstract@npm:^1.20.1": +"es-abstract@npm:^1.19.0, es-abstract@npm:^1.19.1, es-abstract@npm:^1.19.2, es-abstract@npm:^1.19.5, es-abstract@npm:^1.20.1": version: 1.20.1 resolution: "es-abstract@npm:1.20.1" dependencies: @@ -4934,6 +5375,13 @@ __metadata: languageName: node linkType: hard +"eslint-plugin-no-only-tests@npm:^3.1.0": + version: 3.1.0 + resolution: "eslint-plugin-no-only-tests@npm:3.1.0" + checksum: 2a5de82f3a732dbd46792661dd0f8546cf819f76d8828968166dee35741e8039904ba473dafe1eed585f401a496d260c2c38354bb887c94bd4ced0ddca00fb62 + languageName: node + linkType: hard + "eslint-plugin-node@npm:^11.1.0": version: 11.1.0 resolution: "eslint-plugin-node@npm:11.1.0" @@ -5265,7 +5713,7 @@ __metadata: languageName: node linkType: hard -"eth-gas-reporter@npm:^0.2.24": +"eth-gas-reporter@npm:^0.2.25": version: 0.2.25 resolution: "eth-gas-reporter@npm:0.2.25" dependencies: @@ -5438,7 +5886,7 @@ __metadata: languageName: node linkType: hard -"ethereum-cryptography@npm:^0.1.3": +"ethereum-cryptography@npm:0.1.3, ethereum-cryptography@npm:^0.1.3": version: 0.1.3 resolution: "ethereum-cryptography@npm:0.1.3" dependencies: @@ -5661,7 +6109,7 @@ __metadata: languageName: node linkType: hard -"ethereumjs-util@npm:^7.0.10, ethereumjs-util@npm:^7.0.2, ethereumjs-util@npm:^7.0.3, ethereumjs-util@npm:^7.1.0, ethereumjs-util@npm:^7.1.1, ethereumjs-util@npm:^7.1.4, ethereumjs-util@npm:^7.1.5": +"ethereumjs-util@npm:^7.0.2, ethereumjs-util@npm:^7.0.3, ethereumjs-util@npm:^7.1.0": version: 7.1.5 resolution: "ethereumjs-util@npm:7.1.5" dependencies: @@ -5733,7 +6181,7 @@ __metadata: languageName: node linkType: hard -"ethers@npm:^4.0.32, ethers@npm:^4.0.40": +"ethers@npm:^4.0.40": version: 4.0.49 resolution: "ethers@npm:4.0.49" dependencies: @@ -6965,32 +7413,37 @@ __metadata: languageName: node linkType: hard -"hardhat-gas-reporter@npm:^1.0.8": - version: 1.0.8 - resolution: "hardhat-gas-reporter@npm:1.0.8" +"hardhat-gas-reporter@npm:^1.0.9": + version: 1.0.9 + resolution: "hardhat-gas-reporter@npm:1.0.9" dependencies: array-uniq: 1.0.3 - eth-gas-reporter: ^0.2.24 + eth-gas-reporter: ^0.2.25 sha1: ^1.1.1 peerDependencies: hardhat: ^2.0.2 - checksum: bf18aacd08e0bdef81b180f3c97f76fcab885de3e92ed2dc014712e671c83ee7f77755c0e6c0f923a95f8372714cfcb7cdaa019afc42984c159603f8a8d724cf + checksum: 77f8f8d085ff3d9d7787f0227e5355e1800f7d6707bc70171e0567bf69706703ae7f6f53dce1be1d409e7e71e3629a434c94b546bdbbc1e4c1af47cd5d0c6776 languageName: node linkType: hard -"hardhat@npm:=2.10.2": - version: 2.10.2 - resolution: "hardhat@npm:2.10.2" +"hardhat@npm:^2.12.6": + version: 2.12.7 + resolution: "hardhat@npm:2.12.7" dependencies: - "@ethereumjs/block": ^3.6.2 - "@ethereumjs/blockchain": ^5.5.2 - "@ethereumjs/common": ^2.6.4 - "@ethereumjs/tx": ^3.5.1 - "@ethereumjs/vm": ^5.9.0 "@ethersproject/abi": ^5.1.2 "@metamask/eth-sig-util": ^4.0.0 + "@nomicfoundation/ethereumjs-block": ^4.0.0 + "@nomicfoundation/ethereumjs-blockchain": ^6.0.0 + "@nomicfoundation/ethereumjs-common": ^3.0.0 + "@nomicfoundation/ethereumjs-evm": ^1.0.0 + "@nomicfoundation/ethereumjs-rlp": ^4.0.0 + "@nomicfoundation/ethereumjs-statemanager": ^1.0.0 + "@nomicfoundation/ethereumjs-trie": ^5.0.0 + "@nomicfoundation/ethereumjs-tx": ^4.0.0 + "@nomicfoundation/ethereumjs-util": ^8.0.0 + "@nomicfoundation/ethereumjs-vm": ^6.0.0 + "@nomicfoundation/solidity-analyzer": ^0.1.0 "@sentry/node": ^5.18.1 - "@solidity-parser/parser": ^0.14.2 "@types/bn.js": ^5.1.0 "@types/lru-cache": ^5.1.0 abort-controller: ^3.0.0 @@ -7005,15 +7458,14 @@ __metadata: env-paths: ^2.2.0 ethereum-cryptography: ^1.0.3 ethereumjs-abi: ^0.6.8 - ethereumjs-util: ^7.1.4 find-up: ^2.1.0 fp-ts: 1.19.3 fs-extra: ^7.0.1 glob: 7.2.0 immutable: ^4.0.0-rc.12 io-ts: 1.10.4 + keccak: ^3.0.2 lodash: ^4.17.11 - merkle-patricia-tree: ^4.2.4 mnemonist: ^0.38.0 mocha: ^10.0.0 p-map: ^4.0.0 @@ -7021,13 +7473,11 @@ __metadata: raw-body: ^2.4.1 resolve: 1.17.0 semver: ^6.3.0 - slash: ^3.0.0 solc: 0.7.3 source-map-support: ^0.5.13 stacktrace-parser: ^0.1.10 - true-case-path: ^2.2.1 tsort: 0.0.1 - undici: ^5.4.0 + undici: ^5.14.0 uuid: ^8.3.2 ws: ^7.4.6 peerDependencies: @@ -7040,7 +7490,7 @@ __metadata: optional: true bin: hardhat: internal/cli/cli.js - checksum: 3034a213d25d2727fc80e4b45a6e82dc0ae01ec7b9fb669ee3650fa8a1ba8d38f29f689440826b8747fb2647882da15efb524d077e18c7e9eb55ebd574b9052a + checksum: d1f86c09f3db1cc67a448214ebf67a15fc980304ad892ca90792bba679c887a23d8bc2006daf301f4106d638230229dc1ddbc5ba363b1c6aa6b361064f0d7aec languageName: node linkType: hard @@ -7224,6 +7674,13 @@ __metadata: languageName: node linkType: hard +"heap@npm:>= 0.2.0": + version: 0.2.7 + resolution: "heap@npm:0.2.7" + checksum: b0f3963a799e02173f994c452921a777f2b895b710119df999736bfed7477235c2860c423d9aea18a9f3b3d065cb1114d605c208cfcb8d0ac550f97ec5d28cb0 + languageName: node + linkType: hard + "hmac-drbg@npm:^1.0.1": version: 1.0.1 resolution: "hmac-drbg@npm:1.0.1" @@ -7368,7 +7825,7 @@ __metadata: languageName: node linkType: hard -"ieee754@npm:^1.1.13": +"ieee754@npm:^1.1.13, ieee754@npm:^1.2.1": version: 1.2.1 resolution: "ieee754@npm:1.2.1" checksum: 5144c0c9815e54ada181d80a0b810221a253562422e7c6c3a60b1901154184f49326ec239d618c416c1c5945a2e197107aee8d986a3dd836b53dffefd99b5e7e @@ -7630,7 +8087,7 @@ __metadata: languageName: node linkType: hard -"is-buffer@npm:~2.0.3": +"is-buffer@npm:^2.0.5, is-buffer@npm:~2.0.3": version: 2.0.5 resolution: "is-buffer@npm:2.0.5" checksum: 764c9ad8b523a9f5a32af29bdf772b08eb48c04d2ad0a7240916ac2688c983bf5f8504bf25b35e66240edeb9d9085461f9b5dae1f3d2861c6b06a65fe983de42 @@ -7796,15 +8253,6 @@ __metadata: languageName: node linkType: hard -"is-generator-function@npm:^1.0.7": - version: 1.0.10 - resolution: "is-generator-function@npm:1.0.10" - dependencies: - has-tostringtag: ^1.0.0 - checksum: d54644e7dbaccef15ceb1e5d91d680eb5068c9ee9f9eb0a9e04173eb5542c9b51b5ab52c5537f5703e48d5fddfd376817c1ca07a84a407b7115b769d4bdde72b - languageName: node - linkType: hard - "is-glob@npm:^4.0.0, is-glob@npm:^4.0.1, is-glob@npm:^4.0.3, is-glob@npm:~4.0.1": version: 4.0.3 resolution: "is-glob@npm:4.0.3" @@ -7941,19 +8389,6 @@ __metadata: languageName: node linkType: hard -"is-typed-array@npm:^1.1.3, is-typed-array@npm:^1.1.9": - version: 1.1.9 - resolution: "is-typed-array@npm:1.1.9" - dependencies: - available-typed-arrays: ^1.0.5 - call-bind: ^1.0.2 - es-abstract: ^1.20.0 - for-each: ^0.3.3 - has-tostringtag: ^1.0.0 - checksum: 11910f1e58755fef43bf0074e52fa5b932bf101ec65d613e0a83d40e8e4c6e3f2ee142d624ebc7624c091d3bbe921131f8db7d36ecbbb71909f2fe310c1faa65 - languageName: node - linkType: hard - "is-typedarray@npm:^1.0.0, is-typedarray@npm:~1.0.0": version: 1.0.0 resolution: "is-typedarray@npm:1.0.0" @@ -8343,6 +8778,18 @@ __metadata: languageName: node linkType: hard +"keccak@npm:^3.0.2": + version: 3.0.3 + resolution: "keccak@npm:3.0.3" + dependencies: + node-addon-api: ^2.0.0 + node-gyp: latest + node-gyp-build: ^4.2.0 + readable-stream: ^3.6.0 + checksum: f08f04f5cc87013a3fc9e87262f761daff38945c86dd09c01a7f7930a15ae3e14f93b310ef821dcc83675a7b814eb1c983222399a2f263ad980251201d1b9a99 + languageName: node + linkType: hard + "keyv@npm:^3.0.0": version: 3.1.0 resolution: "keyv@npm:3.1.0" @@ -8439,13 +8886,6 @@ __metadata: languageName: node linkType: hard -"level-concat-iterator@npm:~2.0.0": - version: 2.0.1 - resolution: "level-concat-iterator@npm:2.0.1" - checksum: 562583ef1292215f8e749c402510cb61c4d6fccf4541082b3d21dfa5ecde9fcccfe52bdcb5cfff9d2384e7ce5891f44df9439a6ddb39b0ffe31015600b4a828a - languageName: node - linkType: hard - "level-errors@npm:^1.0.3": version: 1.1.2 resolution: "level-errors@npm:1.1.2" @@ -8503,48 +8943,17 @@ __metadata: inherits: ^2.0.1 readable-stream: ^2.3.6 xtend: ^4.0.0 - checksum: f3348316907c70163ea15319ef7e28c21c6b4b948616e11dcbbb8e3dab9ec5b39f7bf13e0d53f7d23c69641b7a2985a4911c5c9a03bd57a07f1af469aba6e3a8 - languageName: node - linkType: hard - -"level-iterator-stream@npm:~4.0.0": - version: 4.0.2 - resolution: "level-iterator-stream@npm:4.0.2" - dependencies: - inherits: ^2.0.4 - readable-stream: ^3.4.0 - xtend: ^4.0.2 - checksum: 239e2c7e62bffb485ed696bcd3b98de7a2bc455d13be4fce175ae3544fe9cda81c2ed93d3e88b61380ae6d28cce02511862d77b86fb2ba5b5cf00471f3c1eccc - languageName: node - linkType: hard - -"level-mem@npm:^3.0.1": - version: 3.0.1 - resolution: "level-mem@npm:3.0.1" - dependencies: - level-packager: ~4.0.0 - memdown: ~3.0.0 - checksum: e4c680922afc3c8cd4502d761ab610c8aa7bcacde2550a0a463e1db069eeb55b6b7bec0bb7fda564cec82422944776f9909fe101b0d7746ad8f4f7446ec2a5cd - languageName: node - linkType: hard - -"level-mem@npm:^5.0.1": - version: 5.0.1 - resolution: "level-mem@npm:5.0.1" - dependencies: - level-packager: ^5.0.3 - memdown: ^5.0.0 - checksum: 37a38163b0c7cc55f64385fdff78438669f953bc08dc751739e2f1edd401472a89001a73a95cc8b81f38f989e46279797c11eb82e702690ea9a171e02bf31e84 + checksum: f3348316907c70163ea15319ef7e28c21c6b4b948616e11dcbbb8e3dab9ec5b39f7bf13e0d53f7d23c69641b7a2985a4911c5c9a03bd57a07f1af469aba6e3a8 languageName: node linkType: hard -"level-packager@npm:^5.0.3": - version: 5.1.1 - resolution: "level-packager@npm:5.1.1" +"level-mem@npm:^3.0.1": + version: 3.0.1 + resolution: "level-mem@npm:3.0.1" dependencies: - encoding-down: ^6.3.0 - levelup: ^4.3.2 - checksum: befe2aa54f2010a6ecf7ddce392c8dee225e1839205080a2704d75e560e28b01191b345494696196777b70d376e3eaae4c9e7c330cc70d3000839f5b18dd78f2 + level-packager: ~4.0.0 + memdown: ~3.0.0 + checksum: e4c680922afc3c8cd4502d761ab610c8aa7bcacde2550a0a463e1db069eeb55b6b7bec0bb7fda564cec82422944776f9909fe101b0d7746ad8f4f7446ec2a5cd languageName: node linkType: hard @@ -8585,12 +8994,20 @@ __metadata: languageName: node linkType: hard -"level-supports@npm:~1.0.0": +"level-supports@npm:^4.0.0": + version: 4.0.1 + resolution: "level-supports@npm:4.0.1" + checksum: d4552b42bb8cdeada07b0f6356c7a90fefe76279147331f291aceae26e3e56d5f927b09ce921647c0230bfe03ddfbdcef332be921e5c2194421ae2bfa3cf6368 + languageName: node + linkType: hard + +"level-transcoder@npm:^1.0.1": version: 1.0.1 - resolution: "level-supports@npm:1.0.1" + resolution: "level-transcoder@npm:1.0.1" dependencies: - xtend: ^4.0.2 - checksum: 5d6bdb88cf00c3d9adcde970db06a548c72c5a94bf42c72f998b58341a105bfe2ea30d313ce1e84396b98cc9ddbc0a9bd94574955a86e929f73c986e10fc0df0 + buffer: ^6.0.3 + module-error: ^1.0.1 + checksum: 304f08d802faf3491a533b6d87ad8be3cabfd27f2713bbe9d4c633bf50fcb9460eab5a6776bf015e101ead7ba1c1853e05e7f341112f17a9d0cb37ee5a421a25 languageName: node linkType: hard @@ -8615,14 +9032,13 @@ __metadata: languageName: node linkType: hard -"level-ws@npm:^2.0.0": - version: 2.0.0 - resolution: "level-ws@npm:2.0.0" +"level@npm:^8.0.0": + version: 8.0.0 + resolution: "level@npm:8.0.0" dependencies: - inherits: ^2.0.3 - readable-stream: ^3.1.0 - xtend: ^4.0.1 - checksum: 4e5cbf090a07367373f693c98ad5b4797e7e694ea801ce5cd4103e06837ec883bdce9588ac11e0b9963ca144b96c95c6401c9e43583028ba1e4f847e81ec9ad6 + browser-level: ^1.0.1 + classic-level: ^1.2.0 + checksum: 13eb25bd71bfdca6cd714d1233adf9da97de9a8a4bf9f28d62a390b5c96d0250abaf983eb90eb8c4e89c7a985bb330750683d106f12670e5ea8fba1d7e608a1f languageName: node linkType: hard @@ -8653,19 +9069,6 @@ __metadata: languageName: node linkType: hard -"levelup@npm:^4.3.2": - version: 4.4.0 - resolution: "levelup@npm:4.4.0" - dependencies: - deferred-leveldown: ~5.3.0 - level-errors: ~2.0.0 - level-iterator-stream: ~4.0.0 - level-supports: ~1.0.0 - xtend: ~4.0.0 - checksum: 5a09e34c78cd7c23f9f6cb73563f1ebe8121ffc5f9f5f232242529d4fbdd40e8d1ffb337d2defa0b842334e0dbd4028fbfe7a072eebfe2c4d07174f0aa4aabca - languageName: node - linkType: hard - "levn@npm:^0.3.0, levn@npm:~0.3.0": version: 0.3.0 resolution: "levn@npm:0.3.0" @@ -8742,6 +9145,20 @@ __metadata: languageName: node linkType: hard +"lodash.isequal@npm:^4.5.0": + version: 4.5.0 + resolution: "lodash.isequal@npm:4.5.0" + checksum: da27515dc5230eb1140ba65ff8de3613649620e8656b19a6270afe4866b7bd461d9ba2ac8a48dcc57f7adac4ee80e1de9f965d89d4d81a0ad52bb3eec2609644 + languageName: node + linkType: hard + +"lodash.isequalwith@npm:^4.4.0": + version: 4.4.0 + resolution: "lodash.isequalwith@npm:4.4.0" + checksum: 428ba7a57c47ec05e2dd18c03a4b4c45dac524a46af7ce3f412594bfc7be6a5acaa51acf9ea113d0002598e9aafc6e19ee8d20bc28363145fcb4d21808c9039f + languageName: node + linkType: hard + "lodash.merge@npm:^4.6.2": version: 4.6.2 resolution: "lodash.merge@npm:4.6.2" @@ -9005,20 +9422,6 @@ __metadata: languageName: node linkType: hard -"memdown@npm:^5.0.0": - version: 5.1.0 - resolution: "memdown@npm:5.1.0" - dependencies: - abstract-leveldown: ~6.2.1 - functional-red-black-tree: ~1.0.1 - immediate: ~3.2.3 - inherits: ~2.0.1 - ltgt: ~2.2.0 - safe-buffer: ~5.2.0 - checksum: 23e4414034e975eae1edd6864874bbe77501d41814fc27e8ead946c3379cb1cbea303d724083d08a6a269af9bf5d55073f1f767dfa7ad6e70465769f87e29794 - languageName: node - linkType: hard - "memdown@npm:~3.0.0": version: 3.0.0 resolution: "memdown@npm:3.0.0" @@ -9033,6 +9436,17 @@ __metadata: languageName: node linkType: hard +"memory-level@npm:^1.0.0": + version: 1.0.0 + resolution: "memory-level@npm:1.0.0" + dependencies: + abstract-level: ^1.0.0 + functional-red-black-tree: ^1.0.1 + module-error: ^1.0.1 + checksum: 80b1b7aedaf936e754adbcd7b9303018c3684fb32f9992fd967c448f145d177f16c724fbba9ed3c3590a9475fd563151eae664d69b83d2ad48714852e9fc5c72 + languageName: node + linkType: hard + "memorystream@npm:^0.3.1": version: 0.3.1 resolution: "memorystream@npm:0.3.1" @@ -9085,20 +9499,6 @@ __metadata: languageName: node linkType: hard -"merkle-patricia-tree@npm:^4.2.4": - version: 4.2.4 - resolution: "merkle-patricia-tree@npm:4.2.4" - dependencies: - "@types/levelup": ^4.3.0 - ethereumjs-util: ^7.1.4 - level-mem: ^5.0.1 - level-ws: ^2.0.0 - readable-stream: ^3.6.0 - semaphore-async-await: ^1.5.1 - checksum: acedc7eea7bb14b97da01e8e023406ed55742f8e82bdd28d1ed821e3bd0cfed9e92f18c7cb300aee0d38f319c960026fd4d4e601f61e2a8665b73c0786d9f799 - languageName: node - linkType: hard - "methods@npm:~1.1.2": version: 1.1.2 resolution: "methods@npm:1.1.2" @@ -9409,6 +9809,41 @@ __metadata: languageName: node linkType: hard +"mocha@npm:7.1.2": + version: 7.1.2 + resolution: "mocha@npm:7.1.2" + dependencies: + ansi-colors: 3.2.3 + browser-stdout: 1.3.1 + chokidar: 3.3.0 + debug: 3.2.6 + diff: 3.5.0 + escape-string-regexp: 1.0.5 + find-up: 3.0.0 + glob: 7.1.3 + growl: 1.10.5 + he: 1.2.0 + js-yaml: 3.13.1 + log-symbols: 3.0.0 + minimatch: 3.0.4 + mkdirp: 0.5.5 + ms: 2.1.1 + node-environment-flags: 1.0.6 + object.assign: 4.1.0 + strip-json-comments: 2.0.1 + supports-color: 6.0.0 + which: 1.3.1 + wide-align: 1.1.3 + yargs: 13.3.2 + yargs-parser: 13.1.2 + yargs-unparser: 1.6.0 + bin: + _mocha: bin/_mocha + mocha: bin/mocha + checksum: 0fc9ad0dd79e43a34de03441634f58e8a3d211af4cdbcd56de150ec99f7aff3b8678bd5aeb41f82115f7df4199a24f7bb372f65e5bcba133b41a5310dee908bd + languageName: node + linkType: hard + "mocha@npm:^10.0.0": version: 10.0.0 resolution: "mocha@npm:10.0.0" @@ -9484,6 +9919,13 @@ __metadata: languageName: node linkType: hard +"module-error@npm:^1.0.1, module-error@npm:^1.0.2": + version: 1.0.2 + resolution: "module-error@npm:1.0.2" + checksum: 5d653e35bd55b3e95f8aee2cdac108082ea892e71b8f651be92cde43e4ee86abee4fa8bd7fc3fe5e68b63926d42f63c54cd17b87a560c31f18739295575a3962 + languageName: node + linkType: hard + "ms@npm:2.0.0": version: 2.0.0 resolution: "ms@npm:2.0.0" @@ -9604,6 +10046,13 @@ __metadata: languageName: node linkType: hard +"napi-macros@npm:~2.0.0": + version: 2.0.0 + resolution: "napi-macros@npm:2.0.0" + checksum: 30384819386977c1f82034757014163fa60ab3c5a538094f778d38788bebb52534966279956f796a92ea771c7f8ae072b975df65de910d051ffbdc927f62320c + languageName: node + linkType: hard + "natural-compare@npm:^1.4.0": version: 1.4.0 resolution: "natural-compare@npm:1.4.0" @@ -9959,15 +10408,6 @@ __metadata: languageName: node linkType: hard -"oboe@npm:2.1.5": - version: 2.1.5 - resolution: "oboe@npm:2.1.5" - dependencies: - http-https: ^1.0.0 - checksum: e6171b33645ffc3559688a824a461952380d0b8f6a203b2daf6767647f277554a73fd7ad795629d88cd8eab68c0460aabb1e1b8b52ef80e3ff7621ac39f832ed - languageName: node - linkType: hard - "on-finished@npm:2.4.1": version: 2.4.1 resolution: "on-finished@npm:2.4.1" @@ -10033,6 +10473,13 @@ __metadata: languageName: node linkType: hard +"ordinal@npm:^1.0.3": + version: 1.0.3 + resolution: "ordinal@npm:1.0.3" + checksum: 6761c5b7606b6c4b0c22b4097dab4fe7ffcddacc49238eedf9c0ced877f5d4e4ad3f4fd43fefa1cc3f167cc54c7149267441b2ae85b81ccf13f45cf4b7947164 + languageName: node + linkType: hard + "os-homedir@npm:^1.0.0": version: 1.0.2 resolution: "os-homedir@npm:1.0.2" @@ -10804,7 +11251,7 @@ __metadata: languageName: node linkType: hard -"queue-microtask@npm:^1.2.2": +"queue-microtask@npm:^1.2.2, queue-microtask@npm:^1.2.3": version: 1.2.3 resolution: "queue-microtask@npm:1.2.3" checksum: b676f8c040cdc5b12723ad2f91414d267605b26419d5c821ff03befa817ddd10e238d22b25d604920340fd73efd8ba795465a0377c4adf45a4a41e4234e42dc4 @@ -10897,7 +11344,7 @@ __metadata: languageName: node linkType: hard -"readable-stream@npm:^3.0.6, readable-stream@npm:^3.1.0, readable-stream@npm:^3.4.0, readable-stream@npm:^3.6.0": +"readable-stream@npm:^3.0.6, readable-stream@npm:^3.6.0": version: 3.6.0 resolution: "readable-stream@npm:3.6.0" dependencies: @@ -11369,6 +11816,15 @@ __metadata: languageName: node linkType: hard +"run-parallel-limit@npm:^1.1.0": + version: 1.1.0 + resolution: "run-parallel-limit@npm:1.1.0" + dependencies: + queue-microtask: ^1.2.2 + checksum: 672c3b87e7f939c684b9965222b361421db0930223ed1e43ebf0e7e48ccc1a022ea4de080bef4d5468434e2577c33b7681e3f03b7593fdc49ad250a55381123c + languageName: node + linkType: hard + "run-parallel@npm:^1.1.9": version: 1.2.0 resolution: "run-parallel@npm:1.2.0" @@ -11394,6 +11850,15 @@ __metadata: languageName: node linkType: hard +"rxjs@npm:^7.2.0": + version: 7.5.7 + resolution: "rxjs@npm:7.5.7" + dependencies: + tslib: ^2.1.0 + checksum: edabcdb73b0f7e0f5f6e05c2077aff8c52222ac939069729704357d6406438acca831c24210db320aba269e86dbe1a400f3769c89101791885121a342fb15d9c + languageName: node + linkType: hard + "safe-buffer@npm:5.2.1, safe-buffer@npm:^5.0.1, safe-buffer@npm:^5.1.0, safe-buffer@npm:^5.1.1, safe-buffer@npm:^5.1.2, safe-buffer@npm:^5.2.0, safe-buffer@npm:^5.2.1, safe-buffer@npm:~5.2.0": version: 5.2.1 resolution: "safe-buffer@npm:5.2.1" @@ -11499,13 +11964,6 @@ __metadata: languageName: node linkType: hard -"semaphore-async-await@npm:^1.5.1": - version: 1.5.1 - resolution: "semaphore-async-await@npm:1.5.1" - checksum: 2dedf7c59ba5f2da860fed95a81017189de6257cbe06c9de0ff2e610a3ae427e9bde1ab7685a62b03ebc28982dee437110492215d75fd6dc8257ce7a38e66b74 - languageName: node - linkType: hard - "semaphore@npm:>=1.0.1, semaphore@npm:^1.0.3, semaphore@npm:^1.1.0": version: 1.1.0 resolution: "semaphore@npm:1.1.0" @@ -11957,31 +12415,35 @@ __metadata: languageName: node linkType: hard -"solidity-coverage@npm:^0.7.21": - version: 0.7.21 - resolution: "solidity-coverage@npm:0.7.21" +"solidity-coverage@npm:^0.8.2": + version: 0.8.2 + resolution: "solidity-coverage@npm:0.8.2" dependencies: - "@solidity-parser/parser": ^0.14.0 - "@truffle/provider": ^0.2.24 + "@ethersproject/abi": ^5.0.9 + "@solidity-parser/parser": ^0.14.1 chalk: ^2.4.2 death: ^1.1.0 detect-port: ^1.3.0 + difflib: ^0.2.4 fs-extra: ^8.1.0 ghost-testrpc: ^0.0.2 global-modules: ^2.0.0 globby: ^10.0.1 jsonschema: ^1.2.4 lodash: ^4.17.15 + mocha: 7.1.2 node-emoji: ^1.10.0 pify: ^4.0.1 recursive-readdir: ^2.2.2 sc-istanbul: ^0.4.5 semver: ^7.3.4 shelljs: ^0.8.3 - web3-utils: ^1.3.0 + web3-utils: ^1.3.6 + peerDependencies: + hardhat: ^2.11.0 bin: solidity-coverage: plugins/bin.js - checksum: a782a96bc2a1cb8120f3e401b09e7ba4b0a38e9198fc91b11c8dacac071f1142ecb2fc4c290675cf36ba80f52773065dd1a6225d0650dc8987a073072734984a + checksum: 489f73d56a1279f2394b7a14db315532884895baa00a4016e68a4e5be0eddca90a95cb3322e6a0b15e67f2d9003b9413ee24c1c61d78f558f5a2e1e233840825 languageName: node linkType: hard @@ -12743,13 +13205,6 @@ __metadata: languageName: node linkType: hard -"true-case-path@npm:^2.2.1": - version: 2.2.1 - resolution: "true-case-path@npm:2.2.1" - checksum: fd5f1c2a87a122a65ffb1f84b580366be08dac7f552ea0fa4b5a6ab0a013af950b0e752beddb1c6c1652e6d6a2b293b7b3fd86a5a1706242ad365b68f1b5c6f1 - languageName: node - linkType: hard - "ts-command-line-args@npm:^2.2.0": version: 2.3.1 resolution: "ts-command-line-args@npm:2.3.1" @@ -12865,6 +13320,13 @@ __metadata: languageName: node linkType: hard +"tslib@npm:^2.1.0": + version: 2.4.1 + resolution: "tslib@npm:2.4.1" + checksum: 19480d6e0313292bd6505d4efe096a6b31c70e21cf08b5febf4da62e95c265c8f571f7b36fcc3d1a17e068032f59c269fab3459d6cd3ed6949eafecf64315fca + languageName: node + linkType: hard + "tsort@npm:0.0.1": version: 0.0.1 resolution: "tsort@npm:0.0.1" @@ -13137,6 +13599,15 @@ __metadata: languageName: node linkType: hard +"undici@npm:^5.14.0": + version: 5.18.0 + resolution: "undici@npm:5.18.0" + dependencies: + busboy: ^1.6.0 + checksum: 74e0f357c376c745fcca612481a5742866ae36086ad387e626255f4c4a15fc5357d9d0fa4355271b6a633d50f5556c3e85720844680c44861c66e23afca7245f + languageName: node + linkType: hard + "undici@npm:^5.4.0": version: 5.19.1 resolution: "undici@npm:5.19.1" @@ -13316,20 +13787,6 @@ __metadata: languageName: node linkType: hard -"util@npm:^0.12.0": - version: 0.12.4 - resolution: "util@npm:0.12.4" - dependencies: - inherits: ^2.0.3 - is-arguments: ^1.0.4 - is-generator-function: ^1.0.7 - is-typed-array: ^1.1.3 - safe-buffer: ^5.1.2 - which-typed-array: ^1.1.2 - checksum: 8eac7a6e6b341c0f1b3eb73bbe5dfcae31a7e9699c8fc3266789f3e95f7637946a7700dcf1904dbd3749a58a36760ebf7acf4bb5b717f7468532a8a79f44eff0 - languageName: node - linkType: hard - "utils-merge@npm:1.0.1": version: 1.0.1 resolution: "utils-merge@npm:1.0.1" @@ -13432,17 +13889,6 @@ __metadata: languageName: node linkType: hard -"web3-bzz@npm:1.5.3": - version: 1.5.3 - resolution: "web3-bzz@npm:1.5.3" - dependencies: - "@types/node": ^12.12.6 - got: 9.6.0 - swarm-js: ^0.1.40 - checksum: 19588b430af9d35d8428255dbc03b86228679b98274e964de1b1404f2e348aa2c989d662903d8d3b0b83cd70658be99126515804745ce27e145beb817ac2b60c - languageName: node - linkType: hard - "web3-core-helpers@npm:1.2.11": version: 1.2.11 resolution: "web3-core-helpers@npm:1.2.11" @@ -13454,16 +13900,6 @@ __metadata: languageName: node linkType: hard -"web3-core-helpers@npm:1.5.3": - version: 1.5.3 - resolution: "web3-core-helpers@npm:1.5.3" - dependencies: - web3-eth-iban: 1.5.3 - web3-utils: 1.5.3 - checksum: 562e6d2f5a8d1e13244a2a85f1a98ff2e8f7340cd94534379b9cc6a4476bed1a899b778a2e4b537f962811e618448a8096833638ec31893fcb03770c4143bd54 - languageName: node - linkType: hard - "web3-core-method@npm:1.2.11": version: 1.2.11 resolution: "web3-core-method@npm:1.2.11" @@ -13478,20 +13914,6 @@ __metadata: languageName: node linkType: hard -"web3-core-method@npm:1.5.3": - version: 1.5.3 - resolution: "web3-core-method@npm:1.5.3" - dependencies: - "@ethereumjs/common": ^2.4.0 - "@ethersproject/transactions": ^5.0.0-beta.135 - web3-core-helpers: 1.5.3 - web3-core-promievent: 1.5.3 - web3-core-subscriptions: 1.5.3 - web3-utils: 1.5.3 - checksum: 7ff2eea36096e57f15da8ee65ab7cb0bd9e41ab0605af7826178d8b1e796af45495d28cd8301e14e0c3ee5ce05aa1bdc610b793ae7fbdc763dc3afafcf937135 - languageName: node - linkType: hard - "web3-core-promievent@npm:1.2.11": version: 1.2.11 resolution: "web3-core-promievent@npm:1.2.11" @@ -13501,15 +13923,6 @@ __metadata: languageName: node linkType: hard -"web3-core-promievent@npm:1.5.3": - version: 1.5.3 - resolution: "web3-core-promievent@npm:1.5.3" - dependencies: - eventemitter3: 4.0.4 - checksum: b31a8ba6ff1d9d3cfdcef26396e1a9dfc3e0c95d4679e8cd69b6892094f21e2ce5ffc916c2d9776f5b28f0a617a814ee80be91bd3a8b17128cc01a390f9056eb - languageName: node - linkType: hard - "web3-core-requestmanager@npm:1.2.11": version: 1.2.11 resolution: "web3-core-requestmanager@npm:1.2.11" @@ -13523,19 +13936,6 @@ __metadata: languageName: node linkType: hard -"web3-core-requestmanager@npm:1.5.3": - version: 1.5.3 - resolution: "web3-core-requestmanager@npm:1.5.3" - dependencies: - util: ^0.12.0 - web3-core-helpers: 1.5.3 - web3-providers-http: 1.5.3 - web3-providers-ipc: 1.5.3 - web3-providers-ws: 1.5.3 - checksum: 7258bb7373198078fce027ede2a508e1f4f86c75e9647c4b1438dc376a58ccb8a077f6e6647b26bf0d25382602eb12bcd7f9fc691177d0d3a1b692b55d1234e8 - languageName: node - linkType: hard - "web3-core-subscriptions@npm:1.2.11": version: 1.2.11 resolution: "web3-core-subscriptions@npm:1.2.11" @@ -13547,16 +13947,6 @@ __metadata: languageName: node linkType: hard -"web3-core-subscriptions@npm:1.5.3": - version: 1.5.3 - resolution: "web3-core-subscriptions@npm:1.5.3" - dependencies: - eventemitter3: 4.0.4 - web3-core-helpers: 1.5.3 - checksum: 87c08b4ad9160e18fcfc05cf832c58616150ce69cb4e396f58fc64ef8ce43b5731dc7fc19bd8b4e5c40cfccd69bafec3b17f9b80f3a36ffad86fa50f83d55fb7 - languageName: node - linkType: hard - "web3-core@npm:1.2.11": version: 1.2.11 resolution: "web3-core@npm:1.2.11" @@ -13572,21 +13962,6 @@ __metadata: languageName: node linkType: hard -"web3-core@npm:1.5.3": - version: 1.5.3 - resolution: "web3-core@npm:1.5.3" - dependencies: - "@types/bn.js": ^4.11.5 - "@types/node": ^12.12.6 - bignumber.js: ^9.0.0 - web3-core-helpers: 1.5.3 - web3-core-method: 1.5.3 - web3-core-requestmanager: 1.5.3 - web3-utils: 1.5.3 - checksum: 0a0ba056fd96d754aacc410c58458365745de1b055c3e3051599dbbb7c3ced0a8db87ac90b98e8ee7fc0b26a8c1500860dad24f8303fa0f6206a71456692ea59 - languageName: node - linkType: hard - "web3-eth-abi@npm:1.2.11": version: 1.2.11 resolution: "web3-eth-abi@npm:1.2.11" @@ -13598,16 +13973,6 @@ __metadata: languageName: node linkType: hard -"web3-eth-abi@npm:1.5.3": - version: 1.5.3 - resolution: "web3-eth-abi@npm:1.5.3" - dependencies: - "@ethersproject/abi": 5.0.7 - web3-utils: 1.5.3 - checksum: ce327542202c97b47e40ec3369e9847c38894b6f436cf4c2ee6450a9ed4869faa765f69bc3432f28b28f1300e04802cbe0fba2c74bb09d563c8ed9f539816c4b - languageName: node - linkType: hard - "web3-eth-accounts@npm:1.2.11": version: 1.2.11 resolution: "web3-eth-accounts@npm:1.2.11" @@ -13627,25 +13992,6 @@ __metadata: languageName: node linkType: hard -"web3-eth-accounts@npm:1.5.3": - version: 1.5.3 - resolution: "web3-eth-accounts@npm:1.5.3" - dependencies: - "@ethereumjs/common": ^2.3.0 - "@ethereumjs/tx": ^3.2.1 - crypto-browserify: 3.12.0 - eth-lib: 0.2.8 - ethereumjs-util: ^7.0.10 - scrypt-js: ^3.0.1 - uuid: 3.3.2 - web3-core: 1.5.3 - web3-core-helpers: 1.5.3 - web3-core-method: 1.5.3 - web3-utils: 1.5.3 - checksum: 6a412691b7f4521dd8688fbaf7f0f4a068e1c58715391d59135142b987fc73cb771a42c0a40b633a9a66e0b3166b38fa8c40d81100942f387fc8db07b3658597 - languageName: node - linkType: hard - "web3-eth-contract@npm:1.2.11": version: 1.2.11 resolution: "web3-eth-contract@npm:1.2.11" @@ -13663,22 +14009,6 @@ __metadata: languageName: node linkType: hard -"web3-eth-contract@npm:1.5.3": - version: 1.5.3 - resolution: "web3-eth-contract@npm:1.5.3" - dependencies: - "@types/bn.js": ^4.11.5 - web3-core: 1.5.3 - web3-core-helpers: 1.5.3 - web3-core-method: 1.5.3 - web3-core-promievent: 1.5.3 - web3-core-subscriptions: 1.5.3 - web3-eth-abi: 1.5.3 - web3-utils: 1.5.3 - checksum: 66403f6f8d31dcb1a7c254f8ad59643cc9706761639a5c03118a7dcecb57df34ce01fd097130130ff73de45aa6918fb9033fb286217311a87d1e9b6c082525f7 - languageName: node - linkType: hard - "web3-eth-ens@npm:1.2.11": version: 1.2.11 resolution: "web3-eth-ens@npm:1.2.11" @@ -13696,22 +14026,6 @@ __metadata: languageName: node linkType: hard -"web3-eth-ens@npm:1.5.3": - version: 1.5.3 - resolution: "web3-eth-ens@npm:1.5.3" - dependencies: - content-hash: ^2.5.2 - eth-ens-namehash: 2.0.8 - web3-core: 1.5.3 - web3-core-helpers: 1.5.3 - web3-core-promievent: 1.5.3 - web3-eth-abi: 1.5.3 - web3-eth-contract: 1.5.3 - web3-utils: 1.5.3 - checksum: a54e973efff6e3bcd5d035c1f73301d09afa2df57ae756ca4025995f31d6b9815df900c879a195f47647545f8c28b878dfb455c90e0058e5172ceee492124c66 - languageName: node - linkType: hard - "web3-eth-iban@npm:1.2.11": version: 1.2.11 resolution: "web3-eth-iban@npm:1.2.11" @@ -13722,16 +14036,6 @@ __metadata: languageName: node linkType: hard -"web3-eth-iban@npm:1.5.3": - version: 1.5.3 - resolution: "web3-eth-iban@npm:1.5.3" - dependencies: - bn.js: ^4.11.9 - web3-utils: 1.5.3 - checksum: ce14813b4cf0330a870e4339f92f11a8456dfdc222c983cc9bbc7a5363c2ce8e8f453f553664e97d55e73c63e51e067e324ba31f900d008df5ef4735951975b9 - languageName: node - linkType: hard - "web3-eth-personal@npm:1.2.11": version: 1.2.11 resolution: "web3-eth-personal@npm:1.2.11" @@ -13746,20 +14050,6 @@ __metadata: languageName: node linkType: hard -"web3-eth-personal@npm:1.5.3": - version: 1.5.3 - resolution: "web3-eth-personal@npm:1.5.3" - dependencies: - "@types/node": ^12.12.6 - web3-core: 1.5.3 - web3-core-helpers: 1.5.3 - web3-core-method: 1.5.3 - web3-net: 1.5.3 - web3-utils: 1.5.3 - checksum: 0d3fb453f43251478949ac253c3ecf6c7d145e5310ee790e4e2b7ae5a69e63cc4cbb34be39ddd9a05d2b7d47b1e41c6b9809ea585eb00deec905dd6f1b13b996 - languageName: node - linkType: hard - "web3-eth@npm:1.2.11": version: 1.2.11 resolution: "web3-eth@npm:1.2.11" @@ -13781,26 +14071,6 @@ __metadata: languageName: node linkType: hard -"web3-eth@npm:1.5.3": - version: 1.5.3 - resolution: "web3-eth@npm:1.5.3" - dependencies: - web3-core: 1.5.3 - web3-core-helpers: 1.5.3 - web3-core-method: 1.5.3 - web3-core-subscriptions: 1.5.3 - web3-eth-abi: 1.5.3 - web3-eth-accounts: 1.5.3 - web3-eth-contract: 1.5.3 - web3-eth-ens: 1.5.3 - web3-eth-iban: 1.5.3 - web3-eth-personal: 1.5.3 - web3-net: 1.5.3 - web3-utils: 1.5.3 - checksum: 2cbf70b2147d37fa3cb2c5802065c56cfacbf537808ab07e698280c8f977613329fc6ae08f351ae2cd9664e89360e4ace6a63f3cf36437226f06ac122427f19c - languageName: node - linkType: hard - "web3-net@npm:1.2.11": version: 1.2.11 resolution: "web3-net@npm:1.2.11" @@ -13812,17 +14082,6 @@ __metadata: languageName: node linkType: hard -"web3-net@npm:1.5.3": - version: 1.5.3 - resolution: "web3-net@npm:1.5.3" - dependencies: - web3-core: 1.5.3 - web3-core-method: 1.5.3 - web3-utils: 1.5.3 - checksum: 4075ef714dca534ebdf3f8766fbdc6951d22a0b5d4b70f48ab014cec38970646bb4a953d95387ece33e772f3384a1a3da64ceb683607d2560a14fc6fa4efec6d - languageName: node - linkType: hard - "web3-provider-engine@npm:14.2.1": version: 14.2.1 resolution: "web3-provider-engine@npm:14.2.1" @@ -13861,16 +14120,6 @@ __metadata: languageName: node linkType: hard -"web3-providers-http@npm:1.5.3": - version: 1.5.3 - resolution: "web3-providers-http@npm:1.5.3" - dependencies: - web3-core-helpers: 1.5.3 - xhr2-cookies: 1.1.0 - checksum: 0ab32c5b53ad9dbc68dbc09f12a04809da160c93054ed357cb59b905343e3a29172c9112b1600854c6f971c913c5e6dd715560aaced9639c4b51f5d3178b3a9b - languageName: node - linkType: hard - "web3-providers-ipc@npm:1.2.11": version: 1.2.11 resolution: "web3-providers-ipc@npm:1.2.11" @@ -13882,16 +14131,6 @@ __metadata: languageName: node linkType: hard -"web3-providers-ipc@npm:1.5.3": - version: 1.5.3 - resolution: "web3-providers-ipc@npm:1.5.3" - dependencies: - oboe: 2.1.5 - web3-core-helpers: 1.5.3 - checksum: 82103eab46cdc3071a30c3c2e564b3c44ceaf70f1780fb048c1156528cdc1d013070163a02bf2b50287b6cfcbe73f3db031fbce537d2fbc50cb8304f9e013117 - languageName: node - linkType: hard - "web3-providers-ws@npm:1.2.11": version: 1.2.11 resolution: "web3-providers-ws@npm:1.2.11" @@ -13904,17 +14143,6 @@ __metadata: languageName: node linkType: hard -"web3-providers-ws@npm:1.5.3": - version: 1.5.3 - resolution: "web3-providers-ws@npm:1.5.3" - dependencies: - eventemitter3: 4.0.4 - web3-core-helpers: 1.5.3 - websocket: ^1.0.32 - checksum: 8bc5b650ded0dc716baaee8c5eebdcdbc63b4630c4be3125f0523310cc547f3809735864a2f5a1584f5c3335ffb3e202da3a4ef554c24b98519a1eafb4fd688e - languageName: node - linkType: hard - "web3-shh@npm:1.2.11": version: 1.2.11 resolution: "web3-shh@npm:1.2.11" @@ -13927,18 +14155,6 @@ __metadata: languageName: node linkType: hard -"web3-shh@npm:1.5.3": - version: 1.5.3 - resolution: "web3-shh@npm:1.5.3" - dependencies: - web3-core: 1.5.3 - web3-core-method: 1.5.3 - web3-core-subscriptions: 1.5.3 - web3-net: 1.5.3 - checksum: 5bfdf787a55fa8c2b53f95ed1bc3f91191ebac06d99b2171d4695e83a7fde2afb4b7020bbfd59b27f3dbe44b6a2c54033fa1e62e56c3f567ea5c4c2b9ef7d75f - languageName: node - linkType: hard - "web3-utils@npm:1.2.11": version: 1.2.11 resolution: "web3-utils@npm:1.2.11" @@ -13955,24 +14171,24 @@ __metadata: languageName: node linkType: hard -"web3-utils@npm:1.5.3": - version: 1.5.3 - resolution: "web3-utils@npm:1.5.3" +"web3-utils@npm:^1.0.0-beta.31": + version: 1.7.4 + resolution: "web3-utils@npm:1.7.4" dependencies: - bn.js: ^4.11.9 - eth-lib: 0.2.8 + bn.js: ^5.2.1 ethereum-bloom-filters: ^1.0.6 + ethereumjs-util: ^7.1.0 ethjs-unit: 0.1.6 number-to-bn: 1.7.0 randombytes: ^2.1.0 utf8: 3.0.0 - checksum: 18398478065c8517568fedd68be977ee59d0d39a16c1e0a7746243cf22ff20333b3eaeb644cd6784a629cc7b5c50c3f76ebe8416b36801c052bef36f5ae1d76c + checksum: 5d9256366904e5c24c7198a8791aa76217100aa068650ccc18264ff670d1e8d42d40fcc5ddc66e3c05fac3b480753ccf7e519709e60aefd73d71dd4c4d2adcbb languageName: node linkType: hard -"web3-utils@npm:^1.0.0-beta.31, web3-utils@npm:^1.3.0": - version: 1.7.4 - resolution: "web3-utils@npm:1.7.4" +"web3-utils@npm:^1.3.6": + version: 1.8.2 + resolution: "web3-utils@npm:1.8.2" dependencies: bn.js: ^5.2.1 ethereum-bloom-filters: ^1.0.6 @@ -13981,7 +14197,7 @@ __metadata: number-to-bn: 1.7.0 randombytes: ^2.1.0 utf8: 3.0.0 - checksum: 5d9256366904e5c24c7198a8791aa76217100aa068650ccc18264ff670d1e8d42d40fcc5ddc66e3c05fac3b480753ccf7e519709e60aefd73d71dd4c4d2adcbb + checksum: a6cda086d7bde4939fc55be8f1dc5040b4cacd9205ac2ac07f37d14305214679e030af7814a3e97f6fabf2901e3452cd0dc8ce7c1cdd8bce4d0d4bae72c50ad9 languageName: node linkType: hard @@ -14000,21 +14216,6 @@ __metadata: languageName: node linkType: hard -"web3@npm:1.5.3": - version: 1.5.3 - resolution: "web3@npm:1.5.3" - dependencies: - web3-bzz: 1.5.3 - web3-core: 1.5.3 - web3-eth: 1.5.3 - web3-eth-personal: 1.5.3 - web3-net: 1.5.3 - web3-shh: 1.5.3 - web3-utils: 1.5.3 - checksum: ec5380536e85aedcc90be5a0bfb3293885d9c7f1684a92fd0c0a9a863002581ac26630e9c015a7b2c79406b44e82d87ee920a27694192ca7c53d95be58ac6e7b - languageName: node - linkType: hard - "webidl-conversions@npm:^3.0.0": version: 3.0.1 resolution: "webidl-conversions@npm:3.0.1" @@ -14036,7 +14237,7 @@ __metadata: languageName: node linkType: hard -"websocket@npm:^1.0.31, websocket@npm:^1.0.32": +"websocket@npm:^1.0.31": version: 1.0.34 resolution: "websocket@npm:1.0.34" dependencies: @@ -14094,20 +14295,6 @@ __metadata: languageName: node linkType: hard -"which-typed-array@npm:^1.1.2": - version: 1.1.8 - resolution: "which-typed-array@npm:1.1.8" - dependencies: - available-typed-arrays: ^1.0.5 - call-bind: ^1.0.2 - es-abstract: ^1.20.0 - for-each: ^0.3.3 - has-tostringtag: ^1.0.0 - is-typed-array: ^1.1.9 - checksum: bedf4d30a738e848404fe67fe0ace33433a7298cf3f5a4d4b2c624ba99c4d25f06a7fd6f3566c3d16af5f8a54f0c6293cbfded5b1208ce11812753990223b45a - languageName: node - linkType: hard - "which@npm:1.3.1, which@npm:^1.1.1, which@npm:^1.2.9, which@npm:^1.3.1": version: 1.3.1 resolution: "which@npm:1.3.1" @@ -14338,7 +14525,7 @@ __metadata: languageName: node linkType: hard -"xtend@npm:^4.0.0, xtend@npm:^4.0.1, xtend@npm:^4.0.2, xtend@npm:~4.0.0, xtend@npm:~4.0.1": +"xtend@npm:^4.0.0, xtend@npm:^4.0.1, xtend@npm:~4.0.0, xtend@npm:~4.0.1": version: 4.0.2 resolution: "xtend@npm:4.0.2" checksum: ac5dfa738b21f6e7f0dd6e65e1b3155036d68104e67e5d5d1bde74892e327d7e5636a076f625599dc394330a731861e87343ff184b0047fef1360a7ec0a5a36a