From be85d76d6604b0ae3f5d6dbe208957ddf1fedae8 Mon Sep 17 00:00:00 2001 From: Aalavandhan <6264334+aalavandhan@users.noreply.github.com> Date: Tue, 16 Apr 2024 13:46:58 -0400 Subject: [PATCH] V3.0.0 (#193) * Added perp redemption to vault recovery flow (#159) * added perp redemption to vault recovery flow * perp external functions returning ops data * added natspec comments and renamed rollover preview struct * refactors bond and tranche helpers, added a new method to compute proportional tranche balances Signed-off-by: aalavandhann <6264334+aalavandhan@users.noreply.github.com> * meld method on rollover vault Signed-off-by: aalavandhann <6264334+aalavandhan@users.noreply.github.com> * added todos Signed-off-by: aalavandhann <6264334+aalavandhan@users.noreply.github.com> * Apply suggestions from code review Co-authored-by: Brandon Iles Signed-off-by: aalavandhann <6264334+aalavandhan@users.noreply.github.com> * removed duplicate implementation Signed-off-by: aalavandhann <6264334+aalavandhan@users.noreply.github.com> * unit tests for meld Signed-off-by: aalavandhann <6264334+aalavandhan@users.noreply.github.com> * moved unused contracts to folder Signed-off-by: aalavandhann <6264334+aalavandhan@users.noreply.github.com> * imported sigmoid utility Signed-off-by: aalavandhann <6264334+aalavandhan@users.noreply.github.com> * Fee strategy 2.0 Signed-off-by: aalavandhann <6264334+aalavandhan@users.noreply.github.com> * perp changes for fee strategy 2.0 Signed-off-by: aalavandhann <6264334+aalavandhan@users.noreply.github.com> * updated unit tests with the new fee scheme Signed-off-by: aalavandhann <6264334+aalavandhan@users.noreply.github.com> * Apply suggestions from code review Co-authored-by: Brandon Iles Co-authored-by: nms-7 <57442379+nms-7@users.noreply.github.com> Signed-off-by: aalavandhann <6264334+aalavandhan@users.noreply.github.com> * updated fee strategy Signed-off-by: aalavandhann <6264334+aalavandhan@users.noreply.github.com> * using a normalized factor to compute sigmoid, rather than using a different target Signed-off-by: aalavandhann <6264334+aalavandhan@users.noreply.github.com> * Update spot-contracts/contracts/strategies/FeeStrategy.sol Signed-off-by: aalavandhann <6264334+aalavandhan@users.noreply.github.com> * using explicit cast instead of abs Signed-off-by: aalavandhann <6264334+aalavandhan@users.noreply.github.com> * removed holding pen and discount math from perp Signed-off-by: aalavandhann <6264334+aalavandhan@users.noreply.github.com> * perp cleanups, removed unused revert paramters Signed-off-by: aalavandhann <6264334+aalavandhan@users.noreply.github.com> * updated peripheral contracts with the new fee mechanism and rollover scheme Signed-off-by: aalavandhann <6264334+aalavandhan@users.noreply.github.com> * updated fee strategy to compute tranche ratios based on senior most tranche, rather than using discount adjusted ratios Signed-off-by: aalavandhann <6264334+aalavandhan@users.noreply.github.com> * updated unit test suite Signed-off-by: aalavandhann <6264334+aalavandhan@users.noreply.github.com> * removed deprecated fee strategy interface functions Signed-off-by: aalavandhann <6264334+aalavandhan@users.noreply.github.com> * deleted deprecated contracts Signed-off-by: aalavandhann <6264334+aalavandhan@users.noreply.github.com> * Apply suggestions from code review Co-authored-by: Brandon Iles Signed-off-by: aalavandhann <6264334+aalavandhan@users.noreply.github.com> * updated rollover math condition Signed-off-by: aalavandhann <6264334+aalavandhan@users.noreply.github.com> * removed dirty diff from rebase Signed-off-by: aalavandhann <6264334+aalavandhan@users.noreply.github.com> * deleted unused contracts Signed-off-by: aalavandhann <6264334+aalavandhan@users.noreply.github.com> * updated sigmoid utility, imported two pow code Signed-off-by: aalavandhann <6264334+aalavandhan@users.noreply.github.com> * replaced == 0 conditions with <= 0 Signed-off-by: aalavandhann <6264334+aalavandhan@users.noreply.github.com> * reverted exp val snapping Signed-off-by: aalavandhann <6264334+aalavandhan@users.noreply.github.com> * imported two pow tests Signed-off-by: aalavandhann <6264334+aalavandhan@users.noreply.github.com> * handling zero vault deposit/redeem Signed-off-by: aalavandhann <6264334+aalavandhan@users.noreply.github.com> * cleaned up error codes Signed-off-by: aalavandhann <6264334+aalavandhan@users.noreply.github.com> * updated fee (monetary) policy for spot Signed-off-by: aalavandhann <6264334+aalavandhan@users.noreply.github.com> * updated bond issuer Signed-off-by: aalavandhann <6264334+aalavandhan@users.noreply.github.com> * updated perp Signed-off-by: aalavandhann <6264334+aalavandhan@users.noreply.github.com> * removed meld from rollover vault Signed-off-by: aalavandhann <6264334+aalavandhan@users.noreply.github.com> * vault code cleanup and private fn refactor Signed-off-by: aalavandhann <6264334+aalavandhan@users.noreply.github.com> * vault v2 storage update, mint redeem and deployment fees Signed-off-by: aalavandhann <6264334+aalavandhan@users.noreply.github.com> * moved rollover vault to parent dir Signed-off-by: aalavandhann <6264334+aalavandhan@users.noreply.github.com> * Apply suggestions from code review Co-authored-by: Brandon Iles Signed-off-by: aalavandhann <6264334+aalavandhan@users.noreply.github.com> * fee policy terminology updates and review fixes Signed-off-by: aalavandhann <6264334+aalavandhan@users.noreply.github.com> * review pass, removed annualized rollover fees, comment and naming updates Signed-off-by: aalavandhann <6264334+aalavandhan@users.noreply.github.com> * code review updates Signed-off-by: aalavandhann <6264334+aalavandhan@users.noreply.github.com> * vault allows 2-way swap between perps and underlying Signed-off-by: aalavandhann <6264334+aalavandhan@users.noreply.github.com> * subscription aware fee policy for swapping, using minting bond as the source of truth for tranche ratios Signed-off-by: aalavandhann <6264334+aalavandhan@users.noreply.github.com> * removed unused interface fn from bond issuer Signed-off-by: aalavandhann <6264334+aalavandhan@users.noreply.github.com> * fee policy updates for perp, skipping fees when used by vault, reverting to only one authorized roller ie vault Signed-off-by: aalavandhann <6264334+aalavandhan@users.noreply.github.com> * utility to calculate ampl required to create x perps Signed-off-by: aalavandhann <6264334+aalavandhan@users.noreply.github.com> * error and comment cleanup Signed-off-by: aalavandhann <6264334+aalavandhan@users.noreply.github.com> * updated swap fee logic Signed-off-by: aalavandhann <6264334+aalavandhan@users.noreply.github.com> * code review fixes Signed-off-by: aalavandhann <6264334+aalavandhan@users.noreply.github.com> * Apply suggestions from code review Co-authored-by: Brandon Iles Signed-off-by: aalavandhann <6264334+aalavandhan@users.noreply.github.com> * code review fixes Signed-off-by: aalavandhann <6264334+aalavandhan@users.noreply.github.com> * removed perp as a vault asset, and redeeming perps only on swaps Signed-off-by: aalavandhann <6264334+aalavandhan@users.noreply.github.com> * simplified fee policy interface, accepts sr instead of computing it Signed-off-by: aalavandhann <6264334+aalavandhan@users.noreply.github.com> * Perp removed external pricing strategy and optimized tranche value computation Signed-off-by: aalavandhann <6264334+aalavandhan@users.noreply.github.com> * Vault clean up, function reorg and optimized tvl comp Signed-off-by: aalavandhann <6264334+aalavandhan@users.noreply.github.com> * Assuming that incoming tranches withold no fees, thus removing all bw fee related math Signed-off-by: aalavandhann <6264334+aalavandhan@users.noreply.github.com> * interface cleanup Signed-off-by: aalavandhann <6264334+aalavandhan@users.noreply.github.com> * fee policy: code review fixes Signed-off-by: aalavandhann <6264334+aalavandhan@users.noreply.github.com> * perp: code review fixes Signed-off-by: aalavandhann <6264334+aalavandhan@users.noreply.github.com> * vault: code review fixes Signed-off-by: aalavandhann <6264334+aalavandhan@users.noreply.github.com> * interface: code review fixes Signed-off-by: aalavandhann <6264334+aalavandhan@users.noreply.github.com> * removed string require errors infavor of custom errors Signed-off-by: aalavandhann <6264334+aalavandhan@users.noreply.github.com> * feePolicy: updated fee policy interface Signed-off-by: aalavandhann <6264334+aalavandhan@users.noreply.github.com> * perp: new fee policy interface, view methods regorg, skipping update state when paused, optimized cdr Signed-off-by: aalavandhann <6264334+aalavandhan@users.noreply.github.com> * vault: handling dust Signed-off-by: aalavandhann <6264334+aalavandhan@users.noreply.github.com> * vault: new fee policy interface, variable caching and tvl optimization, recoverAndRedeem and view methods reorg Signed-off-by: aalavandhann <6264334+aalavandhan@users.noreply.github.com> * unit tests Signed-off-by: aalavandhann <6264334+aalavandhan@users.noreply.github.com> * fee policy: code review fix Signed-off-by: aalavandhann <6264334+aalavandhan@users.noreply.github.com> * perp: code review fix Signed-off-by: aalavandhann <6264334+aalavandhan@users.noreply.github.com> * fee policy: updated dr condition Signed-off-by: aalavandhann <6264334+aalavandhan@users.noreply.github.com> * vault : code review fix Signed-off-by: aalavandhann <6264334+aalavandhan@users.noreply.github.com> * code review fix: unit-tests, perp expects valid vault Signed-off-by: aalavandhann <6264334+aalavandhan@users.noreply.github.com> * reorg return data Signed-off-by: aalavandhann <6264334+aalavandhan@users.noreply.github.com> * redemption amt calc optimization, assuming bond has only 2 tranches Signed-off-by: aalavandhann <6264334+aalavandhan@users.noreply.github.com> * code review fix for commit 1&2 Signed-off-by: aalavandhann <6264334+aalavandhan@users.noreply.github.com> * updated lisc Signed-off-by: aalavandhann <6264334+aalavandhan@users.noreply.github.com> * Renamed fee variable Signed-off-by: aalavandhann <6264334+aalavandhan@users.noreply.github.com> * moved keeper only methods to its own section Signed-off-by: aalavandhann <6264334+aalavandhan@users.noreply.github.com> * moved rollover vault Signed-off-by: aalavandhann <6264334+aalavandhan@users.noreply.github.com> * ran linter Signed-off-by: aalavandhann <6264334+aalavandhan@users.noreply.github.com> * bumped up solidity version to 0.8.20 Signed-off-by: aalavandhann <6264334+aalavandhan@users.noreply.github.com> * updated hyper-param methods, removed logs & zero checks Signed-off-by: aalavandhann <6264334+aalavandhan@users.noreply.github.com> * comment fix Signed-off-by: aalavandhann <6264334+aalavandhan@users.noreply.github.com> * nonReentrant updateState Signed-off-by: aalavandhann <6264334+aalavandhan@users.noreply.github.com> * set keeper in init method Signed-off-by: aalavandhann <6264334+aalavandhan@users.noreply.github.com> * comment update Signed-off-by: aalavandhann <6264334+aalavandhan@users.noreply.github.com> * made transfer fn nonReentrant Signed-off-by: aalavandhann <6264334+aalavandhan@users.noreply.github.com> * returning instead of reverting in deposit,redeem and rollover Signed-off-by: aalavandhann <6264334+aalavandhan@users.noreply.github.com> * moved value fn into perp Signed-off-by: aalavandhann <6264334+aalavandhan@users.noreply.github.com> * removed perp supply zero check on burn computation Signed-off-by: aalavandhann <6264334+aalavandhan@users.noreply.github.com> * Removed reserve count method Signed-off-by: aalavandhann <6264334+aalavandhan@users.noreply.github.com> * removed multiple reads Signed-off-by: aalavandhann <6264334+aalavandhan@users.noreply.github.com> * refactored rollover condition Signed-off-by: aalavandhann <6264334+aalavandhan@users.noreply.github.com> * removed paramter from rollover amt Signed-off-by: aalavandhann <6264334+aalavandhan@users.noreply.github.com> * removed one time use variable Signed-off-by: aalavandhann <6264334+aalavandhan@users.noreply.github.com> * removed zero price check Signed-off-by: aalavandhann <6264334+aalavandhan@users.noreply.github.com> * added comment Signed-off-by: aalavandhann <6264334+aalavandhan@users.noreply.github.com> * removed zero check Signed-off-by: aalavandhann <6264334+aalavandhan@users.noreply.github.com> * added else if Signed-off-by: aalavandhann <6264334+aalavandhan@users.noreply.github.com> * flipped rollover condition Signed-off-by: aalavandhann <6264334+aalavandhan@users.noreply.github.com> * moved perp supply call into compute redemption amt method Signed-off-by: aalavandhann <6264334+aalavandhan@users.noreply.github.com> * Renamed fee policy variable Signed-off-by: aalavandhann <6264334+aalavandhan@users.noreply.github.com> * added fee initiailzier Signed-off-by: aalavandhann <6264334+aalavandhan@users.noreply.github.com> * router preview deposit update return struct Signed-off-by: aalavandhann <6264334+aalavandhan@users.noreply.github.com> * removed approve 0 Signed-off-by: aalavandhann <6264334+aalavandhan@users.noreply.github.com> * added back perp rollover zero price check for safety Signed-off-by: aalavandhann <6264334+aalavandhan@users.noreply.github.com> * router assumming bond has only 2 tranches Signed-off-by: aalavandhann <6264334+aalavandhan@users.noreply.github.com> * fee policy update, dr bounds Signed-off-by: aalavandhann <6264334+aalavandhan@users.noreply.github.com> * moved rollover vault Signed-off-by: aalavandhann <6264334+aalavandhan@users.noreply.github.com> * vault init cleanup Signed-off-by: aalavandhann <6264334+aalavandhan@users.noreply.github.com> * minUnderlyingBal check Signed-off-by: aalavandhann <6264334+aalavandhan@users.noreply.github.com> * removed address(0) check in updateFeePolicy Signed-off-by: aalavandhann <6264334+aalavandhan@users.noreply.github.com> * non-reentrant transferERC20 Signed-off-by: aalavandhann <6264334+aalavandhan@users.noreply.github.com> * removed virtual updateKeeper Signed-off-by: aalavandhann <6264334+aalavandhan@users.noreply.github.com> * removed deployedAmt from deploy Signed-off-by: aalavandhann <6264334+aalavandhan@users.noreply.github.com> * rollover typo fix Signed-off-by: aalavandhann <6264334+aalavandhan@users.noreply.github.com> * vault sync refactor Signed-off-by: aalavandhann <6264334+aalavandhan@users.noreply.github.com> * rollover gas optimizaiton, removed storage read for Signed-off-by: aalavandhann <6264334+aalavandhan@users.noreply.github.com> * rollover performs tranche cleanup Signed-off-by: aalavandhann <6264334+aalavandhan@users.noreply.github.com> * made MAX_DEPLOYED_COUNT uint8 Signed-off-by: aalavandhann <6264334+aalavandhan@users.noreply.github.com> * recover comment fix Signed-off-by: aalavandhann <6264334+aalavandhan@users.noreply.github.com> * vault deposit/redeem return zero instead of reverting Signed-off-by: aalavandhann <6264334+aalavandhan@users.noreply.github.com> * renamed totalSupply_ to noteSupply Signed-off-by: aalavandhann <6264334+aalavandhan@users.noreply.github.com> * removed whenNotPaused from recover and redeem Signed-off-by: aalavandhann <6264334+aalavandhan@users.noreply.github.com> * using safeTransfer for transfer Signed-off-by: aalavandhann <6264334+aalavandhan@users.noreply.github.com> * removed _enforceVaultComposition Signed-off-by: aalavandhann <6264334+aalavandhan@users.noreply.github.com> * removed MIN_SWAP_UNITS check Signed-off-by: aalavandhann <6264334+aalavandhan@users.noreply.github.com> * min underlying perc in vault Signed-off-by: aalavandhann <6264334+aalavandhan@users.noreply.github.com> * renamed perp and vault share fee variables Signed-off-by: aalavandhann <6264334+aalavandhan@users.noreply.github.com> * vault get tvl cleanup Signed-off-by: aalavandhann <6264334+aalavandhan@users.noreply.github.com> * tranche helpers cleanup Signed-off-by: aalavandhann <6264334+aalavandhan@users.noreply.github.com> * removed deployed count and deployed at Signed-off-by: aalavandhann <6264334+aalavandhan@users.noreply.github.com> * comment update Signed-off-by: aalavandhann <6264334+aalavandhan@users.noreply.github.com> * assuming bond has only two tranches, thus bond-tranches data structure has fixed len arrays Signed-off-by: aalavandhann <6264334+aalavandhan@users.noreply.github.com> * increased vault's dust amt Signed-off-by: aalavandhann <6264334+aalavandhan@users.noreply.github.com> * meld perps style change Signed-off-by: aalavandhann <6264334+aalavandhan@users.noreply.github.com> * vault comment fix Signed-off-by: aalavandhann <6264334+aalavandhan@users.noreply.github.com> * removed comment vault n Signed-off-by: aalavandhann <6264334+aalavandhan@users.noreply.github.com> * vault using cached variable Signed-off-by: aalavandhann <6264334+aalavandhan@users.noreply.github.com> * cleaned up perp mint estimation, added comments Signed-off-by: aalavandhann <6264334+aalavandhan@users.noreply.github.com> * sync asset made into online fn Signed-off-by: aalavandhann <6264334+aalavandhan@users.noreply.github.com> * removed bond duration method Signed-off-by: aalavandhann <6264334+aalavandhan@users.noreply.github.com> * bond helpers using return val for getSeniorTranche Signed-off-by: aalavandhann <6264334+aalavandhan@users.noreply.github.com> * fixed sigmoid condition Signed-off-by: aalavandhann <6264334+aalavandhan@users.noreply.github.com> * Ran linter Signed-off-by: aalavandhann <6264334+aalavandhan@users.noreply.github.com> * removed bond/tranche double check in favor of one check for bond validity Signed-off-by: aalavandhann <6264334+aalavandhan@users.noreply.github.com> * fixed bug, with underlying perc calc after swap Signed-off-by: aalavandhann <6264334+aalavandhan@users.noreply.github.com> * using cached value of reserve count Signed-off-by: aalavandhann <6264334+aalavandhan@users.noreply.github.com> * perp: renamed rollout condition and fixed order of checks in Signed-off-by: aalavandhann <6264334+aalavandhan@users.noreply.github.com> * removed duplicate comment in bond helpers Signed-off-by: aalavandhann <6264334+aalavandhan@users.noreply.github.com> * perp enforcing max len for reserve and using uint8 for reserve len, returning only tokens up for rollover Signed-off-by: aalavandhann <6264334+aalavandhan@users.noreply.github.com> * fee policy cleanup Signed-off-by: aalavandhann <6264334+aalavandhan@users.noreply.github.com> * perp defensive check on rollover Signed-off-by: aalavandhann <6264334+aalavandhan@users.noreply.github.com> * vault enforcing liquidity constraints on both swap functions Signed-off-by: aalavandhann <6264334+aalavandhan@users.noreply.github.com> * bond helper minor style fix Signed-off-by: aalavandhann <6264334+aalavandhan@users.noreply.github.com> * updated testcases, back to full coverage Signed-off-by: aalavandhann <6264334+aalavandhan@users.noreply.github.com> * vault: removed redundant sync with redeem tranches on meld and rollover Signed-off-by: aalavandhann <6264334+aalavandhan@users.noreply.github.com> * fixed router bug, with undeployed bonds Signed-off-by: aalavandhann <6264334+aalavandhan@users.noreply.github.com> * removed returns in bond helper Signed-off-by: aalavandhann <6264334+aalavandhan@users.noreply.github.com> * Fee delta update Signed-off-by: aalavandhann <6264334+aalavandhan@users.noreply.github.com> * updated variable name Signed-off-by: aalavandhann <6264334+aalavandhan@users.noreply.github.com> * updated tasks with the new interface Signed-off-by: aalavandhann <6264334+aalavandhan@users.noreply.github.com> * testnet deployment Signed-off-by: aalavandhann <6264334+aalavandhan@users.noreply.github.com> * Pre release fixes (#199) * using fixed perc fees for ops * removed some unreachable code * added test case to check for dust recovery * removed vault deployment fee * pve002 router validation * pve004 preview deposit mature bond handling * Removed from paramters from asset transfer wrapper functions and using msg.sender directly, also using msg.sender instead of _msgSender * ran linter * updated unit tests * pve001 updating constructors * pve002-3 bond issuer tranche granularity check * Removed redundant computeDeviationRatio method from fee policy * openzeppelin defender report informational issues * Reverted pve002 * deployed to sepolia testnet (#201) Signed-off-by: aalavandhann <6264334+aalavandhan@users.noreply.github.com> * fixed liner issue Signed-off-by: aalavandhann <6264334+aalavandhan@users.noreply.github.com> * V2 final updates (#204) * updated sigmoid bound for 20% yearly rollover rate * updated LICENSE Signed-off-by: aalavandhann <6264334+aalavandhan@users.noreply.github.com> * natspec comment updates for variable reanmes Signed-off-by: aalavandhann <6264334+aalavandhan@users.noreply.github.com> * moved hyper paramter controls to keeper Signed-off-by: aalavandhann <6264334+aalavandhan@users.noreply.github.com> * a few more unit tests to improve coverage Signed-off-by: aalavandhann <6264334+aalavandhan@users.noreply.github.com> * upgrade tasks Signed-off-by: aalavandhann <6264334+aalavandhan@users.noreply.github.com> * v2 deployment Signed-off-by: aalavandhann <6264334+aalavandhan@users.noreply.github.com> --------- Signed-off-by: aalavandhann <6264334+aalavandhan@users.noreply.github.com> Co-authored-by: Brandon Iles Co-authored-by: nms-7 <57442379+nms-7@users.noreply.github.com> --- LICENSE | 2 +- spot-contracts/.eslintrc.js | 4 +- spot-contracts/.openzeppelin/goerli.json | 3708 ----------- spot-contracts/.openzeppelin/mainnet.json | 880 +++ spot-contracts/.openzeppelin/sepolia.json | 1091 +++ spot-contracts/.solcover.js | 2 +- spot-contracts/README.md | 14 +- spot-contracts/contracts/BondIssuer.sol | 44 +- spot-contracts/contracts/FeePolicy.sol | 331 + spot-contracts/contracts/PerpetualTranche.sol | 1242 ++-- spot-contracts/contracts/RolloverVault.sol | 977 +++ spot-contracts/contracts/RouterV1.sol | 311 - spot-contracts/contracts/RouterV2.sol | 97 + .../contracts/_interfaces/CommonTypes.sol | 28 + .../contracts/_interfaces/IBondIssuer.sol | 3 - .../_interfaces/IDiscountStrategy.sol | 14 - .../contracts/_interfaces/IERC20Burnable.sol | 6 + .../contracts/_interfaces/IFeePolicy.sol | 53 + .../contracts/_interfaces/IFeeStrategy.sol | 39 - .../_interfaces/IPerpetualTranche.sol | 129 +- .../_interfaces/IPricingStrategy.sol | 26 - .../contracts/_interfaces/IRolloverVault.sol | 35 + .../contracts/_interfaces/IVault.sol | 71 +- .../contracts/_interfaces/ProtocolErrors.sol | 80 + .../contracts/_test/HelpersTester.sol | 86 + spot-contracts/contracts/_test/MathTester.sol | 14 + .../{test => _test}/mocks/MockERC20.sol | 2 +- .../{test => _test}/mocks/MockTranche.sol | 2 +- .../contracts/_test/mocks/MockVault.sol | 67 + .../contracts/_utils/BondHelpers.sol | 224 +- .../contracts/_utils/BondTranchesHelpers.sol | 62 + .../contracts/_utils/PerpHelpers.sol | 105 + spot-contracts/contracts/_utils/Sigmoid.sol | 98 + .../contracts/_utils/SignedMathHelpers.sol | 14 - .../contracts/_utils/TrancheHelpers.sol | 61 + .../contracts/strategies/BasicFeeStrategy.sol | 123 - .../strategies/CDRPricingStrategy.sol | 42 - .../NonEquityCDRLBPricingStrategy.sol | 44 - .../TrancheClassDiscountStrategy.sol | 79 - .../strategies/UnitPricingStrategy.sol | 38 - .../contracts/test/BondHelpersTester.sol | 56 - spot-contracts/contracts/test/MathTester.sol | 12 - .../contracts/vaults/RolloverVault.sol | 598 -- spot-contracts/deployments/goerli.json | 12 - spot-contracts/deployments/sepolia.json | 13 + spot-contracts/hardhat.config.ts | 16 +- spot-contracts/package.json | 10 +- spot-contracts/tasks/deploy/perp.ts | 170 +- spot-contracts/tasks/ganache.sh | 56 - spot-contracts/tasks/goeril.sh | 94 - spot-contracts/tasks/ops/index.ts | 2 +- spot-contracts/tasks/ops/perp.ts | 157 +- spot-contracts/tasks/ops/perp_rollover.ts | 458 -- spot-contracts/tasks/ops/tranche.ts | 176 + spot-contracts/tasks/ops/vaults.ts | 190 +- spot-contracts/tasks/scripts/ganache.sh | 110 + spot-contracts/tasks/{ => scripts}/mainnet.sh | 5 +- spot-contracts/tasks/scripts/mainnet_v2.sh | 15 + spot-contracts/tasks/scripts/sepolia.sh | 93 + spot-contracts/tasks/upgrade/index.ts | 80 +- spot-contracts/tasks/upgrade/perp.ts | 54 - spot-contracts/tasks/upgrade/vaults.ts | 54 - spot-contracts/test/BondIssuer.ts | 27 +- spot-contracts/test/FeePolicy.ts | 542 ++ .../test/PerpetualTranche_deposit.ts | 883 --- .../test/PerpetualTranche_redeem.ts | 987 --- .../test/PerpetualTranche_rollover.ts | 1588 ----- spot-contracts/test/RouterV1.ts | 907 --- spot-contracts/test/RouterV2.ts | 149 + spot-contracts/test/_utils/BondHelpers.ts | 598 -- spot-contracts/test/_utils/HelpersTester.ts | 1013 +++ spot-contracts/test/_utils/Sigmoid.ts | 130 + spot-contracts/test/_utils/SignedMath.ts | 22 - spot-contracts/test/helpers.ts | 63 +- .../test/{ => perp}/PerpetualTranche.ts | 817 +-- .../test/perp/PerpetualTranche_deposit.ts | 481 ++ .../test/perp/PerpetualTranche_redeem.ts | 708 ++ .../test/perp/PerpetualTranche_rollover.ts | 908 +++ .../RolloverVault.ts | 227 +- .../rollover-vault/RolloverVault_deploy.ts | 481 ++ .../RolloverVault_deposit_redeem.ts | 907 +++ .../rollover-vault/RolloverVault_recover.ts | 610 ++ .../test/rollover-vault/RolloverVault_swap.ts | 1264 ++++ .../test/strategies/BasicFeeStrategy.ts | 204 - .../test/strategies/CDRPricingStrategy.ts | 163 - .../NonEquityCDRLBPricingStrategy.ts | 153 - .../TrancheClassDiscountStrategy.ts | 200 - .../test/strategies/UnitPricingStrategy.ts | 30 - .../test/vaults/RolloverVault_deploy.ts | 1113 ---- .../vaults/RolloverVault_deposit_redeem.ts | 741 --- .../test/vaults/RolloverVault_recover.ts | 730 -- yarn.lock | 5848 +++++++++-------- 92 files changed, 16174 insertions(+), 18999 deletions(-) delete mode 100644 spot-contracts/.openzeppelin/goerli.json create mode 100644 spot-contracts/.openzeppelin/sepolia.json create mode 100644 spot-contracts/contracts/FeePolicy.sol create mode 100644 spot-contracts/contracts/RolloverVault.sol delete mode 100644 spot-contracts/contracts/RouterV1.sol create mode 100644 spot-contracts/contracts/RouterV2.sol create mode 100644 spot-contracts/contracts/_interfaces/CommonTypes.sol delete mode 100644 spot-contracts/contracts/_interfaces/IDiscountStrategy.sol create mode 100644 spot-contracts/contracts/_interfaces/IERC20Burnable.sol create mode 100644 spot-contracts/contracts/_interfaces/IFeePolicy.sol delete mode 100644 spot-contracts/contracts/_interfaces/IFeeStrategy.sol delete mode 100644 spot-contracts/contracts/_interfaces/IPricingStrategy.sol create mode 100644 spot-contracts/contracts/_interfaces/IRolloverVault.sol create mode 100644 spot-contracts/contracts/_interfaces/ProtocolErrors.sol create mode 100644 spot-contracts/contracts/_test/HelpersTester.sol create mode 100644 spot-contracts/contracts/_test/MathTester.sol rename spot-contracts/contracts/{test => _test}/mocks/MockERC20.sol (94%) rename spot-contracts/contracts/{test => _test}/mocks/MockTranche.sol (89%) create mode 100644 spot-contracts/contracts/_test/mocks/MockVault.sol create mode 100644 spot-contracts/contracts/_utils/BondTranchesHelpers.sol create mode 100644 spot-contracts/contracts/_utils/PerpHelpers.sol create mode 100644 spot-contracts/contracts/_utils/Sigmoid.sol delete mode 100644 spot-contracts/contracts/_utils/SignedMathHelpers.sol create mode 100644 spot-contracts/contracts/_utils/TrancheHelpers.sol delete mode 100644 spot-contracts/contracts/strategies/BasicFeeStrategy.sol delete mode 100644 spot-contracts/contracts/strategies/CDRPricingStrategy.sol delete mode 100644 spot-contracts/contracts/strategies/NonEquityCDRLBPricingStrategy.sol delete mode 100644 spot-contracts/contracts/strategies/TrancheClassDiscountStrategy.sol delete mode 100644 spot-contracts/contracts/strategies/UnitPricingStrategy.sol delete mode 100644 spot-contracts/contracts/test/BondHelpersTester.sol delete mode 100644 spot-contracts/contracts/test/MathTester.sol delete mode 100644 spot-contracts/contracts/vaults/RolloverVault.sol delete mode 100644 spot-contracts/deployments/goerli.json create mode 100644 spot-contracts/deployments/sepolia.json delete mode 100755 spot-contracts/tasks/ganache.sh delete mode 100644 spot-contracts/tasks/goeril.sh delete mode 100644 spot-contracts/tasks/ops/perp_rollover.ts create mode 100644 spot-contracts/tasks/ops/tranche.ts create mode 100755 spot-contracts/tasks/scripts/ganache.sh rename spot-contracts/tasks/{ => scripts}/mainnet.sh (92%) create mode 100644 spot-contracts/tasks/scripts/mainnet_v2.sh create mode 100644 spot-contracts/tasks/scripts/sepolia.sh delete mode 100644 spot-contracts/tasks/upgrade/perp.ts delete mode 100644 spot-contracts/tasks/upgrade/vaults.ts create mode 100644 spot-contracts/test/FeePolicy.ts delete mode 100644 spot-contracts/test/PerpetualTranche_deposit.ts delete mode 100644 spot-contracts/test/PerpetualTranche_redeem.ts delete mode 100644 spot-contracts/test/PerpetualTranche_rollover.ts delete mode 100644 spot-contracts/test/RouterV1.ts create mode 100644 spot-contracts/test/RouterV2.ts delete mode 100644 spot-contracts/test/_utils/BondHelpers.ts create mode 100644 spot-contracts/test/_utils/HelpersTester.ts create mode 100644 spot-contracts/test/_utils/Sigmoid.ts delete mode 100644 spot-contracts/test/_utils/SignedMath.ts rename spot-contracts/test/{ => perp}/PerpetualTranche.ts (56%) create mode 100644 spot-contracts/test/perp/PerpetualTranche_deposit.ts create mode 100644 spot-contracts/test/perp/PerpetualTranche_redeem.ts create mode 100644 spot-contracts/test/perp/PerpetualTranche_rollover.ts rename spot-contracts/test/{vaults => rollover-vault}/RolloverVault.ts (59%) create mode 100644 spot-contracts/test/rollover-vault/RolloverVault_deploy.ts create mode 100644 spot-contracts/test/rollover-vault/RolloverVault_deposit_redeem.ts create mode 100644 spot-contracts/test/rollover-vault/RolloverVault_recover.ts create mode 100644 spot-contracts/test/rollover-vault/RolloverVault_swap.ts delete mode 100644 spot-contracts/test/strategies/BasicFeeStrategy.ts delete mode 100644 spot-contracts/test/strategies/CDRPricingStrategy.ts delete mode 100644 spot-contracts/test/strategies/NonEquityCDRLBPricingStrategy.ts delete mode 100644 spot-contracts/test/strategies/TrancheClassDiscountStrategy.ts delete mode 100644 spot-contracts/test/strategies/UnitPricingStrategy.ts delete mode 100644 spot-contracts/test/vaults/RolloverVault_deploy.ts delete mode 100644 spot-contracts/test/vaults/RolloverVault_deposit_redeem.ts delete mode 100644 spot-contracts/test/vaults/RolloverVault_recover.ts diff --git a/LICENSE b/LICENSE index 0e9f50c5..04204fba 100644 --- a/LICENSE +++ b/LICENSE @@ -15,7 +15,7 @@ Licensed Work: Ampleforth Spot Additional Use Grant: Any uses listed and defined at spot-license-grants.ampleforthorg.eth -Change Date: The earlier of 2025-01-01 or a date specified at +Change Date: The earlier of 2027-01-01 or a date specified at spot-license-date.ampleforthorg.eth Change License: GNU General Public License v3.0 or later diff --git a/spot-contracts/.eslintrc.js b/spot-contracts/.eslintrc.js index ce316247..41f507d2 100644 --- a/spot-contracts/.eslintrc.js +++ b/spot-contracts/.eslintrc.js @@ -5,7 +5,7 @@ module.exports = { mocha: true, node: true, }, - plugins: ["@typescript-eslint", "no-only-tests"], + plugins: ["@typescript-eslint", "no-only-tests", "unused-imports"], extends: ["standard", "plugin:prettier/recommended", "plugin:node/recommended"], parser: "@typescript-eslint/parser", parserOptions: { @@ -27,5 +27,7 @@ module.exports = { }, ], "no-only-tests/no-only-tests": "error", + "unused-imports/no-unused-imports": "error", + "unused-imports/no-unused-vars": ["warn", { vars: "all" }], }, }; diff --git a/spot-contracts/.openzeppelin/goerli.json b/spot-contracts/.openzeppelin/goerli.json deleted file mode 100644 index 7bca2b33..00000000 --- a/spot-contracts/.openzeppelin/goerli.json +++ /dev/null @@ -1,3708 +0,0 @@ -{ - "manifestVersion": "3.2", - "admin": { - "address": "0x47bF554606254dCEC37119348AA201c5A4ef2C58", - "txHash": "0x3ab729a7345f93c399f9f13141fb307c3a533760a5526b901e4d28751b598523" - }, - "proxies": [ - { - "address": "0x7fdd750CdFbE76327B1bA855aaDD908Ea115A749", - "txHash": "0xf7215ff85d12cd6bfe4d22f127b9c27c22a95bcb107bab4da05d688a5af7a669", - "kind": "transparent" - }, - { - "address": "0x90b243433820bEe22191fdD7C544D241187A3F1c", - "txHash": "0x2f580ae1935ca10e6cdbc5e87fc1ca400d26bbdf9cb2312de9647da394f9a043", - "kind": "transparent" - }, - { - "address": "0x70Fb13F60e4AD4e8642D308BA247496Ff68955B3", - "txHash": "0xc4f3a34c4890f38f1319c895a1ecce4e2305a112f683b9b4c502e6d40a3feb99", - "kind": "transparent" - }, - { - "address": "0x534e2cA3Ce918321BDd6F151B7D1A0f8832Ab1c6", - "txHash": "0xf7874a567053047926665e5c8391e7666620d705c1ecfd21922c69f10735c2c3", - "kind": "transparent" - }, - { - "address": "0xDDE733Ec275a82F07AE8fE6cA5165f7bF64AcB3b", - "txHash": "0x5d38056f2e2f6cb43144d8669028f330166871e3eefc6dd011d6f06b17c2374d", - "kind": "transparent" - }, - { - "address": "0x0cF0bcE1d837AF29AB81eCC2F7383a175f538706", - "txHash": "0x3f8f94a30c0dd1a7d5809a5c7bd411fba8e6b5e6556b6ea0c97aa25191640c25", - "kind": "transparent" - }, - { - "address": "0x60156bB86e9125639c624712a360FD3AbBb52421", - "txHash": "0x6958f7c22d3cf137715c9a49da0a24f715376a9b99250be33561b6280af0ba26", - "kind": "transparent" - }, - { - "address": "0x288E35A925b962DC9112a74F01b3d5d50C7352AB", - "txHash": "0x69a8c3011c591bb3b75beba04a3677f383f599f70612a4c1355784e942e796f2", - "kind": "transparent" - }, - { - "address": "0x95014Bc18F82a98CFAA3253fbD3184125A01f848", - "txHash": "0xb0b77c343e9f98acdcd119a04025b9cd6ba414fc643fc2a2a1f0bd6293d268fa", - "kind": "transparent" - }, - { - "address": "0xD38C80aa5178F39cFB398066200E68af325D36b8", - "txHash": "0x6e3e6dcb71e6966319d37e42d5ba51ffb1c74e600460620760472b39729b1621", - "kind": "transparent" - }, - { - "address": "0xDf7c8364bb6007dB68Bb1c88317f8f7f5bBAfff8", - "txHash": "0x16c9cfb4d59c1030fd1acc05e0437b2e8a8ebab194449aba8780827429918711", - "kind": "transparent" - }, - { - "address": "0x1bcA4E8Cc3f93150132Bee5b0A32760b00686836", - "txHash": "0xd152a65e4f5f63e9860120e6a5b8ff262c76aaa33f4e6c80be78ae18af9881a7", - "kind": "transparent" - }, - { - "address": "0x942f35ce5885F737Beccd8EE3FDAAC81574D058E", - "txHash": "0x15a966c6945db5d308b949ebf0271dff2ba0ef97001dc8b43be62d4a72aa80ea", - "kind": "transparent" - }, - { - "address": "0xca36B64BEbdf141623911987b93767dcA4bF6F1f", - "txHash": "0x882e7b38cb8bfd743672130567a0eaf88bc59cafd4106059dc6945f2738810f3", - "kind": "transparent" - } - ], - "impls": { - "6efe38fadc14b840fc7ac930ec2e9375290d4b81e9f4b0d2fd62ad1cf71ecef2": { - "address": "0x0E31FedD01B934246FE08Ff8B0e996a3FBaa3feA", - "txHash": "0xb46030ef86232ceb6f30705c9714a9b8da97398f0d2a96a8f567a228317f0ba1", - "layout": { - "storage": [ - { - "label": "_initialized", - "offset": 0, - "slot": "0", - "type": "t_uint8", - "contract": "Initializable", - "src": "@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol:62", - "retypedFrom": "bool" - }, - { - "label": "_initializing", - "offset": 1, - "slot": "0", - "type": "t_bool", - "contract": "Initializable", - "src": "@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol:67" - }, - { - "label": "__gap", - "offset": 0, - "slot": "1", - "type": "t_array(t_uint256)50_storage", - "contract": "ContextUpgradeable", - "src": "@openzeppelin/contracts-upgradeable/utils/ContextUpgradeable.sol:36" - }, - { - "label": "_balances", - "offset": 0, - "slot": "51", - "type": "t_mapping(t_address,t_uint256)", - "contract": "ERC20Upgradeable", - "src": "@openzeppelin/contracts-upgradeable/token/ERC20/ERC20Upgradeable.sol:37" - }, - { - "label": "_allowances", - "offset": 0, - "slot": "52", - "type": "t_mapping(t_address,t_mapping(t_address,t_uint256))", - "contract": "ERC20Upgradeable", - "src": "@openzeppelin/contracts-upgradeable/token/ERC20/ERC20Upgradeable.sol:39" - }, - { - "label": "_totalSupply", - "offset": 0, - "slot": "53", - "type": "t_uint256", - "contract": "ERC20Upgradeable", - "src": "@openzeppelin/contracts-upgradeable/token/ERC20/ERC20Upgradeable.sol:41" - }, - { - "label": "_name", - "offset": 0, - "slot": "54", - "type": "t_string_storage", - "contract": "ERC20Upgradeable", - "src": "@openzeppelin/contracts-upgradeable/token/ERC20/ERC20Upgradeable.sol:43" - }, - { - "label": "_symbol", - "offset": 0, - "slot": "55", - "type": "t_string_storage", - "contract": "ERC20Upgradeable", - "src": "@openzeppelin/contracts-upgradeable/token/ERC20/ERC20Upgradeable.sol:44" - }, - { - "label": "__gap", - "offset": 0, - "slot": "56", - "type": "t_array(t_uint256)45_storage", - "contract": "ERC20Upgradeable", - "src": "@openzeppelin/contracts-upgradeable/token/ERC20/ERC20Upgradeable.sol:394" - }, - { - "label": "_owner", - "offset": 0, - "slot": "101", - "type": "t_address", - "contract": "OwnableUpgradeable", - "src": "@openzeppelin/contracts-upgradeable/access/OwnableUpgradeable.sol:22" - }, - { - "label": "__gap", - "offset": 0, - "slot": "102", - "type": "t_array(t_uint256)49_storage", - "contract": "OwnableUpgradeable", - "src": "@openzeppelin/contracts-upgradeable/access/OwnableUpgradeable.sol:87" - }, - { - "label": "feeStrategy", - "offset": 0, - "slot": "151", - "type": "t_contract(IFeeStrategy)6304", - "contract": "PerpetualTranche", - "src": "contracts/PerpetualTranche.sol:199" - }, - { - "label": "pricingStrategy", - "offset": 0, - "slot": "152", - "type": "t_contract(IPricingStrategy)6618", - "contract": "PerpetualTranche", - "src": "contracts/PerpetualTranche.sol:203" - }, - { - "label": "discountStrategy", - "offset": 0, - "slot": "153", - "type": "t_contract(IDiscountStrategy)6636", - "contract": "PerpetualTranche", - "src": "contracts/PerpetualTranche.sol:211" - }, - { - "label": "bondIssuer", - "offset": 0, - "slot": "154", - "type": "t_contract(IBondIssuer)6272", - "contract": "PerpetualTranche", - "src": "contracts/PerpetualTranche.sol:216" - }, - { - "label": "_depositBond", - "offset": 0, - "slot": "155", - "type": "t_contract(IBondController)6707", - "contract": "PerpetualTranche", - "src": "contracts/PerpetualTranche.sol:219" - }, - { - "label": "minTrancheMaturitySec", - "offset": 0, - "slot": "156", - "type": "t_uint256", - "contract": "PerpetualTranche", - "src": "contracts/PerpetualTranche.sol:223" - }, - { - "label": "maxTrancheMaturitySec", - "offset": 0, - "slot": "157", - "type": "t_uint256", - "contract": "PerpetualTranche", - "src": "contracts/PerpetualTranche.sol:227" - }, - { - "label": "maxSupply", - "offset": 0, - "slot": "158", - "type": "t_uint256", - "contract": "PerpetualTranche", - "src": "contracts/PerpetualTranche.sol:230" - }, - { - "label": "maxMintAmtPerTranche", - "offset": 0, - "slot": "159", - "type": "t_uint256", - "contract": "PerpetualTranche", - "src": "contracts/PerpetualTranche.sol:233" - }, - { - "label": "skimPerc", - "offset": 0, - "slot": "160", - "type": "t_uint256", - "contract": "PerpetualTranche", - "src": "contracts/PerpetualTranche.sol:237" - }, - { - "label": "_mintedSupplyPerTranche", - "offset": 0, - "slot": "161", - "type": "t_mapping(t_contract(ITranche)6734,t_uint256)", - "contract": "PerpetualTranche", - "src": "contracts/PerpetualTranche.sol:240" - }, - { - "label": "_appliedDiscounts", - "offset": 0, - "slot": "162", - "type": "t_mapping(t_contract(IERC20Upgradeable)954,t_uint256)", - "contract": "PerpetualTranche", - "src": "contracts/PerpetualTranche.sol:246" - }, - { - "label": "collateral", - "offset": 0, - "slot": "163", - "type": "t_contract(IERC20Upgradeable)954", - "contract": "PerpetualTranche", - "src": "contracts/PerpetualTranche.sol:256" - }, - { - "label": "_reserveTranches", - "offset": 0, - "slot": "164", - "type": "t_struct(AddressSet)2386_storage", - "contract": "PerpetualTranche", - "src": "contracts/PerpetualTranche.sol:259" - }, - { - "label": "_stdTotalTrancheBalance", - "offset": 0, - "slot": "166", - "type": "t_uint256", - "contract": "PerpetualTranche", - "src": "contracts/PerpetualTranche.sol:262" - }, - { - "label": "_stdMatureTrancheBalance", - "offset": 0, - "slot": "167", - "type": "t_uint256", - "contract": "PerpetualTranche", - "src": "contracts/PerpetualTranche.sol:266" - } - ], - "types": { - "t_address": { - "label": "address", - "numberOfBytes": "20" - }, - "t_array(t_bytes32)dyn_storage": { - "label": "bytes32[]", - "numberOfBytes": "32" - }, - "t_array(t_uint256)45_storage": { - "label": "uint256[45]", - "numberOfBytes": "1440" - }, - "t_array(t_uint256)49_storage": { - "label": "uint256[49]", - "numberOfBytes": "1568" - }, - "t_array(t_uint256)50_storage": { - "label": "uint256[50]", - "numberOfBytes": "1600" - }, - "t_bool": { - "label": "bool", - "numberOfBytes": "1" - }, - "t_bytes32": { - "label": "bytes32", - "numberOfBytes": "32" - }, - "t_contract(IBondController)6707": { - "label": "contract IBondController", - "numberOfBytes": "20" - }, - "t_contract(IBondIssuer)6272": { - "label": "contract IBondIssuer", - "numberOfBytes": "20" - }, - "t_contract(IERC20Upgradeable)954": { - "label": "contract IERC20Upgradeable", - "numberOfBytes": "20" - }, - "t_contract(IFeeStrategy)6304": { - "label": "contract IFeeStrategy", - "numberOfBytes": "20" - }, - "t_contract(IPricingStrategy)6618": { - "label": "contract IPricingStrategy", - "numberOfBytes": "20" - }, - "t_contract(ITranche)6734": { - "label": "contract ITranche", - "numberOfBytes": "20" - }, - "t_contract(IDiscountStrategy)6636": { - "label": "contract IDiscountStrategy", - "numberOfBytes": "20" - }, - "t_mapping(t_address,t_mapping(t_address,t_uint256))": { - "label": "mapping(address => mapping(address => uint256))", - "numberOfBytes": "32" - }, - "t_mapping(t_address,t_uint256)": { - "label": "mapping(address => uint256)", - "numberOfBytes": "32" - }, - "t_mapping(t_bytes32,t_uint256)": { - "label": "mapping(bytes32 => uint256)", - "numberOfBytes": "32" - }, - "t_mapping(t_contract(IERC20Upgradeable)954,t_uint256)": { - "label": "mapping(contract IERC20Upgradeable => uint256)", - "numberOfBytes": "32" - }, - "t_mapping(t_contract(ITranche)6734,t_uint256)": { - "label": "mapping(contract ITranche => uint256)", - "numberOfBytes": "32" - }, - "t_string_storage": { - "label": "string", - "numberOfBytes": "32" - }, - "t_struct(AddressSet)2386_storage": { - "label": "struct EnumerableSetUpgradeable.AddressSet", - "members": [ - { - "label": "_inner", - "type": "t_struct(Set)2085_storage", - "offset": 0, - "slot": "0" - } - ], - "numberOfBytes": "64" - }, - "t_struct(Set)2085_storage": { - "label": "struct EnumerableSetUpgradeable.Set", - "members": [ - { - "label": "_values", - "type": "t_array(t_bytes32)dyn_storage", - "offset": 0, - "slot": "0" - }, - { - "label": "_indexes", - "type": "t_mapping(t_bytes32,t_uint256)", - "offset": 0, - "slot": "1" - } - ], - "numberOfBytes": "64" - }, - "t_uint256": { - "label": "uint256", - "numberOfBytes": "32" - }, - "t_uint8": { - "label": "uint8", - "numberOfBytes": "1" - } - } - } - }, - "86c2d96d55d9bc4dbd3f01df03770b02414ef3632bbbbe62a3f46679478f62ca": { - "address": "0x0E9d5ee87EF9b19d599aeda062A9F9d85542b702", - "txHash": "0x8d434383d11ab3462ef8409a3841e3794aa35cff28e56d83545c48ef6fcc6861", - "layout": { - "storage": [ - { - "label": "_initialized", - "offset": 0, - "slot": "0", - "type": "t_uint8", - "contract": "Initializable", - "src": "@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol:62", - "retypedFrom": "bool" - }, - { - "label": "_initializing", - "offset": 1, - "slot": "0", - "type": "t_bool", - "contract": "Initializable", - "src": "@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol:67" - }, - { - "label": "__gap", - "offset": 0, - "slot": "1", - "type": "t_array(t_uint256)50_storage", - "contract": "ContextUpgradeable", - "src": "@openzeppelin/contracts-upgradeable/utils/ContextUpgradeable.sol:36" - }, - { - "label": "_balances", - "offset": 0, - "slot": "51", - "type": "t_mapping(t_address,t_uint256)", - "contract": "ERC20Upgradeable", - "src": "@openzeppelin/contracts-upgradeable/token/ERC20/ERC20Upgradeable.sol:37" - }, - { - "label": "_allowances", - "offset": 0, - "slot": "52", - "type": "t_mapping(t_address,t_mapping(t_address,t_uint256))", - "contract": "ERC20Upgradeable", - "src": "@openzeppelin/contracts-upgradeable/token/ERC20/ERC20Upgradeable.sol:39" - }, - { - "label": "_totalSupply", - "offset": 0, - "slot": "53", - "type": "t_uint256", - "contract": "ERC20Upgradeable", - "src": "@openzeppelin/contracts-upgradeable/token/ERC20/ERC20Upgradeable.sol:41" - }, - { - "label": "_name", - "offset": 0, - "slot": "54", - "type": "t_string_storage", - "contract": "ERC20Upgradeable", - "src": "@openzeppelin/contracts-upgradeable/token/ERC20/ERC20Upgradeable.sol:43" - }, - { - "label": "_symbol", - "offset": 0, - "slot": "55", - "type": "t_string_storage", - "contract": "ERC20Upgradeable", - "src": "@openzeppelin/contracts-upgradeable/token/ERC20/ERC20Upgradeable.sol:44" - }, - { - "label": "__gap", - "offset": 0, - "slot": "56", - "type": "t_array(t_uint256)45_storage", - "contract": "ERC20Upgradeable", - "src": "@openzeppelin/contracts-upgradeable/token/ERC20/ERC20Upgradeable.sol:394" - }, - { - "label": "_owner", - "offset": 0, - "slot": "101", - "type": "t_address", - "contract": "OwnableUpgradeable", - "src": "@openzeppelin/contracts-upgradeable/access/OwnableUpgradeable.sol:22" - }, - { - "label": "__gap", - "offset": 0, - "slot": "102", - "type": "t_array(t_uint256)49_storage", - "contract": "OwnableUpgradeable", - "src": "@openzeppelin/contracts-upgradeable/access/OwnableUpgradeable.sol:87" - }, - { - "label": "feeStrategy", - "offset": 0, - "slot": "151", - "type": "t_contract(IFeeStrategy)6338", - "contract": "PerpetualTranche", - "src": "contracts/PerpetualTranche.sol:199" - }, - { - "label": "pricingStrategy", - "offset": 0, - "slot": "152", - "type": "t_contract(IPricingStrategy)6654", - "contract": "PerpetualTranche", - "src": "contracts/PerpetualTranche.sol:203" - }, - { - "label": "discountStrategy", - "offset": 0, - "slot": "153", - "type": "t_contract(IDiscountStrategy)6672", - "contract": "PerpetualTranche", - "src": "contracts/PerpetualTranche.sol:211" - }, - { - "label": "bondIssuer", - "offset": 0, - "slot": "154", - "type": "t_contract(IBondIssuer)6306", - "contract": "PerpetualTranche", - "src": "contracts/PerpetualTranche.sol:216" - }, - { - "label": "_depositBond", - "offset": 0, - "slot": "155", - "type": "t_contract(IBondController)6743", - "contract": "PerpetualTranche", - "src": "contracts/PerpetualTranche.sol:219" - }, - { - "label": "minTrancheMaturitySec", - "offset": 0, - "slot": "156", - "type": "t_uint256", - "contract": "PerpetualTranche", - "src": "contracts/PerpetualTranche.sol:223" - }, - { - "label": "maxTrancheMaturitySec", - "offset": 0, - "slot": "157", - "type": "t_uint256", - "contract": "PerpetualTranche", - "src": "contracts/PerpetualTranche.sol:227" - }, - { - "label": "maxSupply", - "offset": 0, - "slot": "158", - "type": "t_uint256", - "contract": "PerpetualTranche", - "src": "contracts/PerpetualTranche.sol:230" - }, - { - "label": "maxMintAmtPerTranche", - "offset": 0, - "slot": "159", - "type": "t_uint256", - "contract": "PerpetualTranche", - "src": "contracts/PerpetualTranche.sol:233" - }, - { - "label": "skimPerc", - "offset": 0, - "slot": "160", - "type": "t_uint256", - "contract": "PerpetualTranche", - "src": "contracts/PerpetualTranche.sol:237" - }, - { - "label": "_mintedSupplyPerTranche", - "offset": 0, - "slot": "161", - "type": "t_mapping(t_contract(ITranche)6770,t_uint256)", - "contract": "PerpetualTranche", - "src": "contracts/PerpetualTranche.sol:240" - }, - { - "label": "_appliedDiscounts", - "offset": 0, - "slot": "162", - "type": "t_mapping(t_contract(IERC20Upgradeable)954,t_uint256)", - "contract": "PerpetualTranche", - "src": "contracts/PerpetualTranche.sol:246" - }, - { - "label": "collateral", - "offset": 0, - "slot": "163", - "type": "t_contract(IERC20Upgradeable)954", - "contract": "PerpetualTranche", - "src": "contracts/PerpetualTranche.sol:256" - }, - { - "label": "_reserveTranches", - "offset": 0, - "slot": "164", - "type": "t_struct(AddressSet)2386_storage", - "contract": "PerpetualTranche", - "src": "contracts/PerpetualTranche.sol:259" - }, - { - "label": "_stdTotalTrancheBalance", - "offset": 0, - "slot": "166", - "type": "t_uint256", - "contract": "PerpetualTranche", - "src": "contracts/PerpetualTranche.sol:262" - }, - { - "label": "_stdMatureTrancheBalance", - "offset": 0, - "slot": "167", - "type": "t_uint256", - "contract": "PerpetualTranche", - "src": "contracts/PerpetualTranche.sol:266" - } - ], - "types": { - "t_address": { - "label": "address", - "numberOfBytes": "20" - }, - "t_array(t_bytes32)dyn_storage": { - "label": "bytes32[]", - "numberOfBytes": "32" - }, - "t_array(t_uint256)45_storage": { - "label": "uint256[45]", - "numberOfBytes": "1440" - }, - "t_array(t_uint256)49_storage": { - "label": "uint256[49]", - "numberOfBytes": "1568" - }, - "t_array(t_uint256)50_storage": { - "label": "uint256[50]", - "numberOfBytes": "1600" - }, - "t_bool": { - "label": "bool", - "numberOfBytes": "1" - }, - "t_bytes32": { - "label": "bytes32", - "numberOfBytes": "32" - }, - "t_contract(IBondController)6743": { - "label": "contract IBondController", - "numberOfBytes": "20" - }, - "t_contract(IBondIssuer)6306": { - "label": "contract IBondIssuer", - "numberOfBytes": "20" - }, - "t_contract(IERC20Upgradeable)954": { - "label": "contract IERC20Upgradeable", - "numberOfBytes": "20" - }, - "t_contract(IFeeStrategy)6338": { - "label": "contract IFeeStrategy", - "numberOfBytes": "20" - }, - "t_contract(IPricingStrategy)6654": { - "label": "contract IPricingStrategy", - "numberOfBytes": "20" - }, - "t_contract(ITranche)6770": { - "label": "contract ITranche", - "numberOfBytes": "20" - }, - "t_contract(IDiscountStrategy)6672": { - "label": "contract IDiscountStrategy", - "numberOfBytes": "20" - }, - "t_mapping(t_address,t_mapping(t_address,t_uint256))": { - "label": "mapping(address => mapping(address => uint256))", - "numberOfBytes": "32" - }, - "t_mapping(t_address,t_uint256)": { - "label": "mapping(address => uint256)", - "numberOfBytes": "32" - }, - "t_mapping(t_bytes32,t_uint256)": { - "label": "mapping(bytes32 => uint256)", - "numberOfBytes": "32" - }, - "t_mapping(t_contract(IERC20Upgradeable)954,t_uint256)": { - "label": "mapping(contract IERC20Upgradeable => uint256)", - "numberOfBytes": "32" - }, - "t_mapping(t_contract(ITranche)6770,t_uint256)": { - "label": "mapping(contract ITranche => uint256)", - "numberOfBytes": "32" - }, - "t_string_storage": { - "label": "string", - "numberOfBytes": "32" - }, - "t_struct(AddressSet)2386_storage": { - "label": "struct EnumerableSetUpgradeable.AddressSet", - "members": [ - { - "label": "_inner", - "type": "t_struct(Set)2085_storage", - "offset": 0, - "slot": "0" - } - ], - "numberOfBytes": "64" - }, - "t_struct(Set)2085_storage": { - "label": "struct EnumerableSetUpgradeable.Set", - "members": [ - { - "label": "_values", - "type": "t_array(t_bytes32)dyn_storage", - "offset": 0, - "slot": "0" - }, - { - "label": "_indexes", - "type": "t_mapping(t_bytes32,t_uint256)", - "offset": 0, - "slot": "1" - } - ], - "numberOfBytes": "64" - }, - "t_uint256": { - "label": "uint256", - "numberOfBytes": "32" - }, - "t_uint8": { - "label": "uint8", - "numberOfBytes": "1" - } - } - } - }, - "d2e53de4902ffb8bac77413957093a553f7cf2f06e66a7ece6769b7ff90a1dc2": { - "address": "0x56D973424A697a0CfE7FBD8b280FE09019d2CCcC", - "txHash": "0xc8e5e9aa3cedcabcac6af8db6eefc019344922539017ace7c64f1a307df6b705", - "layout": { - "storage": [ - { - "label": "_initialized", - "offset": 0, - "slot": "0", - "type": "t_uint8", - "contract": "Initializable", - "src": "@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol:62", - "retypedFrom": "bool" - }, - { - "label": "_initializing", - "offset": 1, - "slot": "0", - "type": "t_bool", - "contract": "Initializable", - "src": "@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol:67" - }, - { - "label": "__gap", - "offset": 0, - "slot": "1", - "type": "t_array(t_uint256)50_storage", - "contract": "ContextUpgradeable", - "src": "@openzeppelin/contracts-upgradeable/utils/ContextUpgradeable.sol:36" - }, - { - "label": "_balances", - "offset": 0, - "slot": "51", - "type": "t_mapping(t_address,t_uint256)", - "contract": "ERC20Upgradeable", - "src": "@openzeppelin/contracts-upgradeable/token/ERC20/ERC20Upgradeable.sol:37" - }, - { - "label": "_allowances", - "offset": 0, - "slot": "52", - "type": "t_mapping(t_address,t_mapping(t_address,t_uint256))", - "contract": "ERC20Upgradeable", - "src": "@openzeppelin/contracts-upgradeable/token/ERC20/ERC20Upgradeable.sol:39" - }, - { - "label": "_totalSupply", - "offset": 0, - "slot": "53", - "type": "t_uint256", - "contract": "ERC20Upgradeable", - "src": "@openzeppelin/contracts-upgradeable/token/ERC20/ERC20Upgradeable.sol:41" - }, - { - "label": "_name", - "offset": 0, - "slot": "54", - "type": "t_string_storage", - "contract": "ERC20Upgradeable", - "src": "@openzeppelin/contracts-upgradeable/token/ERC20/ERC20Upgradeable.sol:43" - }, - { - "label": "_symbol", - "offset": 0, - "slot": "55", - "type": "t_string_storage", - "contract": "ERC20Upgradeable", - "src": "@openzeppelin/contracts-upgradeable/token/ERC20/ERC20Upgradeable.sol:44" - }, - { - "label": "__gap", - "offset": 0, - "slot": "56", - "type": "t_array(t_uint256)45_storage", - "contract": "ERC20Upgradeable", - "src": "@openzeppelin/contracts-upgradeable/token/ERC20/ERC20Upgradeable.sol:394" - }, - { - "label": "_owner", - "offset": 0, - "slot": "101", - "type": "t_address", - "contract": "OwnableUpgradeable", - "src": "@openzeppelin/contracts-upgradeable/access/OwnableUpgradeable.sol:22" - }, - { - "label": "__gap", - "offset": 0, - "slot": "102", - "type": "t_array(t_uint256)49_storage", - "contract": "OwnableUpgradeable", - "src": "@openzeppelin/contracts-upgradeable/access/OwnableUpgradeable.sol:87" - }, - { - "label": "feeStrategy", - "offset": 0, - "slot": "151", - "type": "t_contract(IFeeStrategy)6338", - "contract": "PerpetualTranche", - "src": "contracts/PerpetualTranche.sol:200" - }, - { - "label": "pricingStrategy", - "offset": 0, - "slot": "152", - "type": "t_contract(IPricingStrategy)6654", - "contract": "PerpetualTranche", - "src": "contracts/PerpetualTranche.sol:204" - }, - { - "label": "discountStrategy", - "offset": 0, - "slot": "153", - "type": "t_contract(IDiscountStrategy)6672", - "contract": "PerpetualTranche", - "src": "contracts/PerpetualTranche.sol:212" - }, - { - "label": "bondIssuer", - "offset": 0, - "slot": "154", - "type": "t_contract(IBondIssuer)6306", - "contract": "PerpetualTranche", - "src": "contracts/PerpetualTranche.sol:217" - }, - { - "label": "_depositBond", - "offset": 0, - "slot": "155", - "type": "t_contract(IBondController)6743", - "contract": "PerpetualTranche", - "src": "contracts/PerpetualTranche.sol:220" - }, - { - "label": "minTrancheMaturitySec", - "offset": 0, - "slot": "156", - "type": "t_uint256", - "contract": "PerpetualTranche", - "src": "contracts/PerpetualTranche.sol:224" - }, - { - "label": "maxTrancheMaturitySec", - "offset": 0, - "slot": "157", - "type": "t_uint256", - "contract": "PerpetualTranche", - "src": "contracts/PerpetualTranche.sol:228" - }, - { - "label": "maxSupply", - "offset": 0, - "slot": "158", - "type": "t_uint256", - "contract": "PerpetualTranche", - "src": "contracts/PerpetualTranche.sol:231" - }, - { - "label": "maxMintAmtPerTranche", - "offset": 0, - "slot": "159", - "type": "t_uint256", - "contract": "PerpetualTranche", - "src": "contracts/PerpetualTranche.sol:234" - }, - { - "label": "skimPerc", - "offset": 0, - "slot": "160", - "type": "t_uint256", - "contract": "PerpetualTranche", - "src": "contracts/PerpetualTranche.sol:238" - }, - { - "label": "_mintedSupplyPerTranche", - "offset": 0, - "slot": "161", - "type": "t_mapping(t_contract(ITranche)6770,t_uint256)", - "contract": "PerpetualTranche", - "src": "contracts/PerpetualTranche.sol:241" - }, - { - "label": "_appliedDiscounts", - "offset": 0, - "slot": "162", - "type": "t_mapping(t_contract(IERC20Upgradeable)954,t_uint256)", - "contract": "PerpetualTranche", - "src": "contracts/PerpetualTranche.sol:247" - }, - { - "label": "collateral", - "offset": 0, - "slot": "163", - "type": "t_contract(IERC20Upgradeable)954", - "contract": "PerpetualTranche", - "src": "contracts/PerpetualTranche.sol:257" - }, - { - "label": "_reserveTranches", - "offset": 0, - "slot": "164", - "type": "t_struct(AddressSet)2386_storage", - "contract": "PerpetualTranche", - "src": "contracts/PerpetualTranche.sol:260" - }, - { - "label": "_stdTotalTrancheBalance", - "offset": 0, - "slot": "166", - "type": "t_uint256", - "contract": "PerpetualTranche", - "src": "contracts/PerpetualTranche.sol:263" - }, - { - "label": "_stdMatureTrancheBalance", - "offset": 0, - "slot": "167", - "type": "t_uint256", - "contract": "PerpetualTranche", - "src": "contracts/PerpetualTranche.sol:267" - } - ], - "types": { - "t_address": { - "label": "address", - "numberOfBytes": "20" - }, - "t_array(t_bytes32)dyn_storage": { - "label": "bytes32[]", - "numberOfBytes": "32" - }, - "t_array(t_uint256)45_storage": { - "label": "uint256[45]", - "numberOfBytes": "1440" - }, - "t_array(t_uint256)49_storage": { - "label": "uint256[49]", - "numberOfBytes": "1568" - }, - "t_array(t_uint256)50_storage": { - "label": "uint256[50]", - "numberOfBytes": "1600" - }, - "t_bool": { - "label": "bool", - "numberOfBytes": "1" - }, - "t_bytes32": { - "label": "bytes32", - "numberOfBytes": "32" - }, - "t_contract(IBondController)6743": { - "label": "contract IBondController", - "numberOfBytes": "20" - }, - "t_contract(IBondIssuer)6306": { - "label": "contract IBondIssuer", - "numberOfBytes": "20" - }, - "t_contract(IERC20Upgradeable)954": { - "label": "contract IERC20Upgradeable", - "numberOfBytes": "20" - }, - "t_contract(IFeeStrategy)6338": { - "label": "contract IFeeStrategy", - "numberOfBytes": "20" - }, - "t_contract(IPricingStrategy)6654": { - "label": "contract IPricingStrategy", - "numberOfBytes": "20" - }, - "t_contract(ITranche)6770": { - "label": "contract ITranche", - "numberOfBytes": "20" - }, - "t_contract(IDiscountStrategy)6672": { - "label": "contract IDiscountStrategy", - "numberOfBytes": "20" - }, - "t_mapping(t_address,t_mapping(t_address,t_uint256))": { - "label": "mapping(address => mapping(address => uint256))", - "numberOfBytes": "32" - }, - "t_mapping(t_address,t_uint256)": { - "label": "mapping(address => uint256)", - "numberOfBytes": "32" - }, - "t_mapping(t_bytes32,t_uint256)": { - "label": "mapping(bytes32 => uint256)", - "numberOfBytes": "32" - }, - "t_mapping(t_contract(IERC20Upgradeable)954,t_uint256)": { - "label": "mapping(contract IERC20Upgradeable => uint256)", - "numberOfBytes": "32" - }, - "t_mapping(t_contract(ITranche)6770,t_uint256)": { - "label": "mapping(contract ITranche => uint256)", - "numberOfBytes": "32" - }, - "t_string_storage": { - "label": "string", - "numberOfBytes": "32" - }, - "t_struct(AddressSet)2386_storage": { - "label": "struct EnumerableSetUpgradeable.AddressSet", - "members": [ - { - "label": "_inner", - "type": "t_struct(Set)2085_storage", - "offset": 0, - "slot": "0" - } - ], - "numberOfBytes": "64" - }, - "t_struct(Set)2085_storage": { - "label": "struct EnumerableSetUpgradeable.Set", - "members": [ - { - "label": "_values", - "type": "t_array(t_bytes32)dyn_storage", - "offset": 0, - "slot": "0" - }, - { - "label": "_indexes", - "type": "t_mapping(t_bytes32,t_uint256)", - "offset": 0, - "slot": "1" - } - ], - "numberOfBytes": "64" - }, - "t_uint256": { - "label": "uint256", - "numberOfBytes": "32" - }, - "t_uint8": { - "label": "uint8", - "numberOfBytes": "1" - } - } - } - }, - "0eb7cc5e870bc05123f992843a10ffb34f2fa7f1365b3cca2acd6dfdd3a40a9c": { - "address": "0xceD5A1061F5507172059FE760CA2e9F050caBF02", - "txHash": "0x66758ebec9a5527033f0279eec4d7228d2b76e24c0e9810706de9323852523ad", - "layout": { - "storage": [ - { - "label": "_initialized", - "offset": 0, - "slot": "0", - "type": "t_uint8", - "contract": "Initializable", - "src": "@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol:62", - "retypedFrom": "bool" - }, - { - "label": "_initializing", - "offset": 1, - "slot": "0", - "type": "t_bool", - "contract": "Initializable", - "src": "@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol:67" - }, - { - "label": "__gap", - "offset": 0, - "slot": "1", - "type": "t_array(t_uint256)50_storage", - "contract": "ContextUpgradeable", - "src": "@openzeppelin/contracts-upgradeable/utils/ContextUpgradeable.sol:36" - }, - { - "label": "_balances", - "offset": 0, - "slot": "51", - "type": "t_mapping(t_address,t_uint256)", - "contract": "ERC20Upgradeable", - "src": "@openzeppelin/contracts-upgradeable/token/ERC20/ERC20Upgradeable.sol:37" - }, - { - "label": "_allowances", - "offset": 0, - "slot": "52", - "type": "t_mapping(t_address,t_mapping(t_address,t_uint256))", - "contract": "ERC20Upgradeable", - "src": "@openzeppelin/contracts-upgradeable/token/ERC20/ERC20Upgradeable.sol:39" - }, - { - "label": "_totalSupply", - "offset": 0, - "slot": "53", - "type": "t_uint256", - "contract": "ERC20Upgradeable", - "src": "@openzeppelin/contracts-upgradeable/token/ERC20/ERC20Upgradeable.sol:41" - }, - { - "label": "_name", - "offset": 0, - "slot": "54", - "type": "t_string_storage", - "contract": "ERC20Upgradeable", - "src": "@openzeppelin/contracts-upgradeable/token/ERC20/ERC20Upgradeable.sol:43" - }, - { - "label": "_symbol", - "offset": 0, - "slot": "55", - "type": "t_string_storage", - "contract": "ERC20Upgradeable", - "src": "@openzeppelin/contracts-upgradeable/token/ERC20/ERC20Upgradeable.sol:44" - }, - { - "label": "__gap", - "offset": 0, - "slot": "56", - "type": "t_array(t_uint256)45_storage", - "contract": "ERC20Upgradeable", - "src": "@openzeppelin/contracts-upgradeable/token/ERC20/ERC20Upgradeable.sol:394" - }, - { - "label": "__gap", - "offset": 0, - "slot": "101", - "type": "t_array(t_uint256)50_storage", - "contract": "ERC20BurnableUpgradeable", - "src": "@openzeppelin/contracts-upgradeable/token/ERC20/extensions/ERC20BurnableUpgradeable.sol:51" - }, - { - "label": "_owner", - "offset": 0, - "slot": "151", - "type": "t_address", - "contract": "OwnableUpgradeable", - "src": "@openzeppelin/contracts-upgradeable/access/OwnableUpgradeable.sol:22" - }, - { - "label": "__gap", - "offset": 0, - "slot": "152", - "type": "t_array(t_uint256)49_storage", - "contract": "OwnableUpgradeable", - "src": "@openzeppelin/contracts-upgradeable/access/OwnableUpgradeable.sol:94" - }, - { - "label": "_paused", - "offset": 0, - "slot": "201", - "type": "t_bool", - "contract": "PausableUpgradeable", - "src": "@openzeppelin/contracts-upgradeable/security/PausableUpgradeable.sol:29" - }, - { - "label": "__gap", - "offset": 0, - "slot": "202", - "type": "t_array(t_uint256)49_storage", - "contract": "PausableUpgradeable", - "src": "@openzeppelin/contracts-upgradeable/security/PausableUpgradeable.sol:116" - }, - { - "label": "_status", - "offset": 0, - "slot": "251", - "type": "t_uint256", - "contract": "ReentrancyGuardUpgradeable", - "src": "@openzeppelin/contracts-upgradeable/security/ReentrancyGuardUpgradeable.sol:38" - }, - { - "label": "__gap", - "offset": 0, - "slot": "252", - "type": "t_array(t_uint256)49_storage", - "contract": "ReentrancyGuardUpgradeable", - "src": "@openzeppelin/contracts-upgradeable/security/ReentrancyGuardUpgradeable.sol:74" - }, - { - "label": "_decimals", - "offset": 0, - "slot": "301", - "type": "t_uint8", - "contract": "PerpetualTranche", - "src": "contracts/PerpetualTranche.sol:192" - }, - { - "label": "keeper", - "offset": 1, - "slot": "301", - "type": "t_address", - "contract": "PerpetualTranche", - "src": "contracts/PerpetualTranche.sol:198" - }, - { - "label": "feeStrategy", - "offset": 0, - "slot": "302", - "type": "t_contract(IFeeStrategy)8980", - "contract": "PerpetualTranche", - "src": "contracts/PerpetualTranche.sol:201" - }, - { - "label": "pricingStrategy", - "offset": 0, - "slot": "303", - "type": "t_contract(IPricingStrategy)9335", - "contract": "PerpetualTranche", - "src": "contracts/PerpetualTranche.sol:205" - }, - { - "label": "discountStrategy", - "offset": 0, - "slot": "304", - "type": "t_contract(IDiscountStrategy)8938", - "contract": "PerpetualTranche", - "src": "contracts/PerpetualTranche.sol:213" - }, - { - "label": "bondIssuer", - "offset": 0, - "slot": "305", - "type": "t_contract(IBondIssuer)8918", - "contract": "PerpetualTranche", - "src": "contracts/PerpetualTranche.sol:218" - }, - { - "label": "_depositBond", - "offset": 0, - "slot": "306", - "type": "t_contract(IBondController)9414", - "contract": "PerpetualTranche", - "src": "contracts/PerpetualTranche.sol:221" - }, - { - "label": "minTrancheMaturitySec", - "offset": 0, - "slot": "307", - "type": "t_uint256", - "contract": "PerpetualTranche", - "src": "contracts/PerpetualTranche.sol:225" - }, - { - "label": "maxTrancheMaturitySec", - "offset": 0, - "slot": "308", - "type": "t_uint256", - "contract": "PerpetualTranche", - "src": "contracts/PerpetualTranche.sol:229" - }, - { - "label": "matureValueTargetPerc", - "offset": 0, - "slot": "309", - "type": "t_uint256", - "contract": "PerpetualTranche", - "src": "contracts/PerpetualTranche.sol:232" - }, - { - "label": "maxSupply", - "offset": 0, - "slot": "310", - "type": "t_uint256", - "contract": "PerpetualTranche", - "src": "contracts/PerpetualTranche.sol:235" - }, - { - "label": "maxMintAmtPerTranche", - "offset": 0, - "slot": "311", - "type": "t_uint256", - "contract": "PerpetualTranche", - "src": "contracts/PerpetualTranche.sol:238" - }, - { - "label": "mintedSupplyPerTranche", - "offset": 0, - "slot": "312", - "type": "t_mapping(t_contract(ITranche)9441,t_uint256)", - "contract": "PerpetualTranche", - "src": "contracts/PerpetualTranche.sol:241" - }, - { - "label": "_appliedDiscounts", - "offset": 0, - "slot": "313", - "type": "t_mapping(t_contract(IERC20Upgradeable)1157,t_uint256)", - "contract": "PerpetualTranche", - "src": "contracts/PerpetualTranche.sol:247" - }, - { - "label": "_reserves", - "offset": 0, - "slot": "314", - "type": "t_struct(AddressSet)4620_storage", - "contract": "PerpetualTranche", - "src": "contracts/PerpetualTranche.sol:253" - }, - { - "label": "_matureTrancheBalance", - "offset": 0, - "slot": "316", - "type": "t_uint256", - "contract": "PerpetualTranche", - "src": "contracts/PerpetualTranche.sol:259" - } - ], - "types": { - "t_address": { - "label": "address", - "numberOfBytes": "20" - }, - "t_array(t_bytes32)dyn_storage": { - "label": "bytes32[]", - "numberOfBytes": "32" - }, - "t_array(t_uint256)45_storage": { - "label": "uint256[45]", - "numberOfBytes": "1440" - }, - "t_array(t_uint256)49_storage": { - "label": "uint256[49]", - "numberOfBytes": "1568" - }, - "t_array(t_uint256)50_storage": { - "label": "uint256[50]", - "numberOfBytes": "1600" - }, - "t_bool": { - "label": "bool", - "numberOfBytes": "1" - }, - "t_bytes32": { - "label": "bytes32", - "numberOfBytes": "32" - }, - "t_contract(IBondController)9414": { - "label": "contract IBondController", - "numberOfBytes": "20" - }, - "t_contract(IBondIssuer)8918": { - "label": "contract IBondIssuer", - "numberOfBytes": "20" - }, - "t_contract(IDiscountStrategy)8938": { - "label": "contract IDiscountStrategy", - "numberOfBytes": "20" - }, - "t_contract(IERC20Upgradeable)1157": { - "label": "contract IERC20Upgradeable", - "numberOfBytes": "20" - }, - "t_contract(IFeeStrategy)8980": { - "label": "contract IFeeStrategy", - "numberOfBytes": "20" - }, - "t_contract(IPricingStrategy)9335": { - "label": "contract IPricingStrategy", - "numberOfBytes": "20" - }, - "t_contract(ITranche)9441": { - "label": "contract ITranche", - "numberOfBytes": "20" - }, - "t_mapping(t_address,t_mapping(t_address,t_uint256))": { - "label": "mapping(address => mapping(address => uint256))", - "numberOfBytes": "32" - }, - "t_mapping(t_address,t_uint256)": { - "label": "mapping(address => uint256)", - "numberOfBytes": "32" - }, - "t_mapping(t_bytes32,t_uint256)": { - "label": "mapping(bytes32 => uint256)", - "numberOfBytes": "32" - }, - "t_mapping(t_contract(IERC20Upgradeable)1157,t_uint256)": { - "label": "mapping(contract IERC20Upgradeable => uint256)", - "numberOfBytes": "32" - }, - "t_mapping(t_contract(ITranche)9441,t_uint256)": { - "label": "mapping(contract ITranche => uint256)", - "numberOfBytes": "32" - }, - "t_string_storage": { - "label": "string", - "numberOfBytes": "32" - }, - "t_struct(AddressSet)4620_storage": { - "label": "struct EnumerableSetUpgradeable.AddressSet", - "members": [ - { - "label": "_inner", - "type": "t_struct(Set)4319_storage", - "offset": 0, - "slot": "0" - } - ], - "numberOfBytes": "64" - }, - "t_struct(Set)4319_storage": { - "label": "struct EnumerableSetUpgradeable.Set", - "members": [ - { - "label": "_values", - "type": "t_array(t_bytes32)dyn_storage", - "offset": 0, - "slot": "0" - }, - { - "label": "_indexes", - "type": "t_mapping(t_bytes32,t_uint256)", - "offset": 0, - "slot": "1" - } - ], - "numberOfBytes": "64" - }, - "t_uint256": { - "label": "uint256", - "numberOfBytes": "32" - }, - "t_uint8": { - "label": "uint8", - "numberOfBytes": "1" - } - } - } - }, - "97db98753b6b6dafb311d3a33acee374762650670647a38ecf9f5911c8b3750f": { - "address": "0x38f600e08540178719BF656e6B43FC15A529c393", - "txHash": "0x9a3d2aa21eddaccb71ccc5d10e73e4e4f86c7420a7626600b9a9143062bcef10", - "layout": { - "storage": [ - { - "label": "_initialized", - "offset": 0, - "slot": "0", - "type": "t_uint8", - "contract": "Initializable", - "src": "@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol:62", - "retypedFrom": "bool" - }, - { - "label": "_initializing", - "offset": 1, - "slot": "0", - "type": "t_bool", - "contract": "Initializable", - "src": "@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol:67" - }, - { - "label": "__gap", - "offset": 0, - "slot": "1", - "type": "t_array(t_uint256)50_storage", - "contract": "ContextUpgradeable", - "src": "@openzeppelin/contracts-upgradeable/utils/ContextUpgradeable.sol:36" - }, - { - "label": "_balances", - "offset": 0, - "slot": "51", - "type": "t_mapping(t_address,t_uint256)", - "contract": "ERC20Upgradeable", - "src": "@openzeppelin/contracts-upgradeable/token/ERC20/ERC20Upgradeable.sol:37" - }, - { - "label": "_allowances", - "offset": 0, - "slot": "52", - "type": "t_mapping(t_address,t_mapping(t_address,t_uint256))", - "contract": "ERC20Upgradeable", - "src": "@openzeppelin/contracts-upgradeable/token/ERC20/ERC20Upgradeable.sol:39" - }, - { - "label": "_totalSupply", - "offset": 0, - "slot": "53", - "type": "t_uint256", - "contract": "ERC20Upgradeable", - "src": "@openzeppelin/contracts-upgradeable/token/ERC20/ERC20Upgradeable.sol:41" - }, - { - "label": "_name", - "offset": 0, - "slot": "54", - "type": "t_string_storage", - "contract": "ERC20Upgradeable", - "src": "@openzeppelin/contracts-upgradeable/token/ERC20/ERC20Upgradeable.sol:43" - }, - { - "label": "_symbol", - "offset": 0, - "slot": "55", - "type": "t_string_storage", - "contract": "ERC20Upgradeable", - "src": "@openzeppelin/contracts-upgradeable/token/ERC20/ERC20Upgradeable.sol:44" - }, - { - "label": "__gap", - "offset": 0, - "slot": "56", - "type": "t_array(t_uint256)45_storage", - "contract": "ERC20Upgradeable", - "src": "@openzeppelin/contracts-upgradeable/token/ERC20/ERC20Upgradeable.sol:394" - }, - { - "label": "__gap", - "offset": 0, - "slot": "101", - "type": "t_array(t_uint256)50_storage", - "contract": "ERC20BurnableUpgradeable", - "src": "@openzeppelin/contracts-upgradeable/token/ERC20/extensions/ERC20BurnableUpgradeable.sol:51" - }, - { - "label": "_owner", - "offset": 0, - "slot": "151", - "type": "t_address", - "contract": "OwnableUpgradeable", - "src": "@openzeppelin/contracts-upgradeable/access/OwnableUpgradeable.sol:22" - }, - { - "label": "__gap", - "offset": 0, - "slot": "152", - "type": "t_array(t_uint256)49_storage", - "contract": "OwnableUpgradeable", - "src": "@openzeppelin/contracts-upgradeable/access/OwnableUpgradeable.sol:94" - }, - { - "label": "_paused", - "offset": 0, - "slot": "201", - "type": "t_bool", - "contract": "PausableUpgradeable", - "src": "@openzeppelin/contracts-upgradeable/security/PausableUpgradeable.sol:29" - }, - { - "label": "__gap", - "offset": 0, - "slot": "202", - "type": "t_array(t_uint256)49_storage", - "contract": "PausableUpgradeable", - "src": "@openzeppelin/contracts-upgradeable/security/PausableUpgradeable.sol:116" - }, - { - "label": "_status", - "offset": 0, - "slot": "251", - "type": "t_uint256", - "contract": "ReentrancyGuardUpgradeable", - "src": "@openzeppelin/contracts-upgradeable/security/ReentrancyGuardUpgradeable.sol:38" - }, - { - "label": "__gap", - "offset": 0, - "slot": "252", - "type": "t_array(t_uint256)49_storage", - "contract": "ReentrancyGuardUpgradeable", - "src": "@openzeppelin/contracts-upgradeable/security/ReentrancyGuardUpgradeable.sol:74" - }, - { - "label": "_decimals", - "offset": 0, - "slot": "301", - "type": "t_uint8", - "contract": "PerpetualTranche", - "src": "contracts/PerpetualTranche.sol:192" - }, - { - "label": "keeper", - "offset": 1, - "slot": "301", - "type": "t_address", - "contract": "PerpetualTranche", - "src": "contracts/PerpetualTranche.sol:198" - }, - { - "label": "feeStrategy", - "offset": 0, - "slot": "302", - "type": "t_contract(IFeeStrategy)8990", - "contract": "PerpetualTranche", - "src": "contracts/PerpetualTranche.sol:201" - }, - { - "label": "pricingStrategy", - "offset": 0, - "slot": "303", - "type": "t_contract(IPricingStrategy)9345", - "contract": "PerpetualTranche", - "src": "contracts/PerpetualTranche.sol:205" - }, - { - "label": "discountStrategy", - "offset": 0, - "slot": "304", - "type": "t_contract(IDiscountStrategy)8948", - "contract": "PerpetualTranche", - "src": "contracts/PerpetualTranche.sol:213" - }, - { - "label": "bondIssuer", - "offset": 0, - "slot": "305", - "type": "t_contract(IBondIssuer)8928", - "contract": "PerpetualTranche", - "src": "contracts/PerpetualTranche.sol:218" - }, - { - "label": "_depositBond", - "offset": 0, - "slot": "306", - "type": "t_contract(IBondController)9424", - "contract": "PerpetualTranche", - "src": "contracts/PerpetualTranche.sol:221" - }, - { - "label": "minTrancheMaturitySec", - "offset": 0, - "slot": "307", - "type": "t_uint256", - "contract": "PerpetualTranche", - "src": "contracts/PerpetualTranche.sol:225" - }, - { - "label": "maxTrancheMaturitySec", - "offset": 0, - "slot": "308", - "type": "t_uint256", - "contract": "PerpetualTranche", - "src": "contracts/PerpetualTranche.sol:229" - }, - { - "label": "matureValueTargetPerc", - "offset": 0, - "slot": "309", - "type": "t_uint256", - "contract": "PerpetualTranche", - "src": "contracts/PerpetualTranche.sol:232" - }, - { - "label": "maxSupply", - "offset": 0, - "slot": "310", - "type": "t_uint256", - "contract": "PerpetualTranche", - "src": "contracts/PerpetualTranche.sol:235" - }, - { - "label": "maxMintAmtPerTranche", - "offset": 0, - "slot": "311", - "type": "t_uint256", - "contract": "PerpetualTranche", - "src": "contracts/PerpetualTranche.sol:238" - }, - { - "label": "mintedSupplyPerTranche", - "offset": 0, - "slot": "312", - "type": "t_mapping(t_contract(ITranche)9451,t_uint256)", - "contract": "PerpetualTranche", - "src": "contracts/PerpetualTranche.sol:241" - }, - { - "label": "_appliedDiscounts", - "offset": 0, - "slot": "313", - "type": "t_mapping(t_contract(IERC20Upgradeable)1157,t_uint256)", - "contract": "PerpetualTranche", - "src": "contracts/PerpetualTranche.sol:247" - }, - { - "label": "_reserves", - "offset": 0, - "slot": "314", - "type": "t_struct(AddressSet)4620_storage", - "contract": "PerpetualTranche", - "src": "contracts/PerpetualTranche.sol:253" - }, - { - "label": "_matureTrancheBalance", - "offset": 0, - "slot": "316", - "type": "t_uint256", - "contract": "PerpetualTranche", - "src": "contracts/PerpetualTranche.sol:259" - } - ], - "types": { - "t_address": { - "label": "address", - "numberOfBytes": "20" - }, - "t_array(t_bytes32)dyn_storage": { - "label": "bytes32[]", - "numberOfBytes": "32" - }, - "t_array(t_uint256)45_storage": { - "label": "uint256[45]", - "numberOfBytes": "1440" - }, - "t_array(t_uint256)49_storage": { - "label": "uint256[49]", - "numberOfBytes": "1568" - }, - "t_array(t_uint256)50_storage": { - "label": "uint256[50]", - "numberOfBytes": "1600" - }, - "t_bool": { - "label": "bool", - "numberOfBytes": "1" - }, - "t_bytes32": { - "label": "bytes32", - "numberOfBytes": "32" - }, - "t_contract(IBondController)9424": { - "label": "contract IBondController", - "numberOfBytes": "20" - }, - "t_contract(IBondIssuer)8928": { - "label": "contract IBondIssuer", - "numberOfBytes": "20" - }, - "t_contract(IDiscountStrategy)8948": { - "label": "contract IDiscountStrategy", - "numberOfBytes": "20" - }, - "t_contract(IERC20Upgradeable)1157": { - "label": "contract IERC20Upgradeable", - "numberOfBytes": "20" - }, - "t_contract(IFeeStrategy)8990": { - "label": "contract IFeeStrategy", - "numberOfBytes": "20" - }, - "t_contract(IPricingStrategy)9345": { - "label": "contract IPricingStrategy", - "numberOfBytes": "20" - }, - "t_contract(ITranche)9451": { - "label": "contract ITranche", - "numberOfBytes": "20" - }, - "t_mapping(t_address,t_mapping(t_address,t_uint256))": { - "label": "mapping(address => mapping(address => uint256))", - "numberOfBytes": "32" - }, - "t_mapping(t_address,t_uint256)": { - "label": "mapping(address => uint256)", - "numberOfBytes": "32" - }, - "t_mapping(t_bytes32,t_uint256)": { - "label": "mapping(bytes32 => uint256)", - "numberOfBytes": "32" - }, - "t_mapping(t_contract(IERC20Upgradeable)1157,t_uint256)": { - "label": "mapping(contract IERC20Upgradeable => uint256)", - "numberOfBytes": "32" - }, - "t_mapping(t_contract(ITranche)9451,t_uint256)": { - "label": "mapping(contract ITranche => uint256)", - "numberOfBytes": "32" - }, - "t_string_storage": { - "label": "string", - "numberOfBytes": "32" - }, - "t_struct(AddressSet)4620_storage": { - "label": "struct EnumerableSetUpgradeable.AddressSet", - "members": [ - { - "label": "_inner", - "type": "t_struct(Set)4319_storage", - "offset": 0, - "slot": "0" - } - ], - "numberOfBytes": "64" - }, - "t_struct(Set)4319_storage": { - "label": "struct EnumerableSetUpgradeable.Set", - "members": [ - { - "label": "_values", - "type": "t_array(t_bytes32)dyn_storage", - "offset": 0, - "slot": "0" - }, - { - "label": "_indexes", - "type": "t_mapping(t_bytes32,t_uint256)", - "offset": 0, - "slot": "1" - } - ], - "numberOfBytes": "64" - }, - "t_uint256": { - "label": "uint256", - "numberOfBytes": "32" - }, - "t_uint8": { - "label": "uint8", - "numberOfBytes": "1" - } - } - } - }, - "3935b74d2f245056fda5a20fbcfa0bfc7990f5aa54c7e5bdc3f91a93860d3ed7": { - "address": "0x1Be92B4753C1A2B3D97644390AE1C6d93E95f025", - "txHash": "0x169f8e8fdb1f48b16851ca19cccd792b84c1bcdcc074712bed6f547918135769", - "layout": { - "storage": [ - { - "label": "_initialized", - "offset": 0, - "slot": "0", - "type": "t_uint8", - "contract": "Initializable", - "src": "@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol:62", - "retypedFrom": "bool" - }, - { - "label": "_initializing", - "offset": 1, - "slot": "0", - "type": "t_bool", - "contract": "Initializable", - "src": "@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol:67" - }, - { - "label": "__gap", - "offset": 0, - "slot": "1", - "type": "t_array(t_uint256)50_storage", - "contract": "ContextUpgradeable", - "src": "@openzeppelin/contracts-upgradeable/utils/ContextUpgradeable.sol:36" - }, - { - "label": "_balances", - "offset": 0, - "slot": "51", - "type": "t_mapping(t_address,t_uint256)", - "contract": "ERC20Upgradeable", - "src": "@openzeppelin/contracts-upgradeable/token/ERC20/ERC20Upgradeable.sol:37" - }, - { - "label": "_allowances", - "offset": 0, - "slot": "52", - "type": "t_mapping(t_address,t_mapping(t_address,t_uint256))", - "contract": "ERC20Upgradeable", - "src": "@openzeppelin/contracts-upgradeable/token/ERC20/ERC20Upgradeable.sol:39" - }, - { - "label": "_totalSupply", - "offset": 0, - "slot": "53", - "type": "t_uint256", - "contract": "ERC20Upgradeable", - "src": "@openzeppelin/contracts-upgradeable/token/ERC20/ERC20Upgradeable.sol:41" - }, - { - "label": "_name", - "offset": 0, - "slot": "54", - "type": "t_string_storage", - "contract": "ERC20Upgradeable", - "src": "@openzeppelin/contracts-upgradeable/token/ERC20/ERC20Upgradeable.sol:43" - }, - { - "label": "_symbol", - "offset": 0, - "slot": "55", - "type": "t_string_storage", - "contract": "ERC20Upgradeable", - "src": "@openzeppelin/contracts-upgradeable/token/ERC20/ERC20Upgradeable.sol:44" - }, - { - "label": "__gap", - "offset": 0, - "slot": "56", - "type": "t_array(t_uint256)45_storage", - "contract": "ERC20Upgradeable", - "src": "@openzeppelin/contracts-upgradeable/token/ERC20/ERC20Upgradeable.sol:394" - }, - { - "label": "__gap", - "offset": 0, - "slot": "101", - "type": "t_array(t_uint256)50_storage", - "contract": "ERC20BurnableUpgradeable", - "src": "@openzeppelin/contracts-upgradeable/token/ERC20/extensions/ERC20BurnableUpgradeable.sol:51" - }, - { - "label": "_owner", - "offset": 0, - "slot": "151", - "type": "t_address", - "contract": "OwnableUpgradeable", - "src": "@openzeppelin/contracts-upgradeable/access/OwnableUpgradeable.sol:22" - }, - { - "label": "__gap", - "offset": 0, - "slot": "152", - "type": "t_array(t_uint256)49_storage", - "contract": "OwnableUpgradeable", - "src": "@openzeppelin/contracts-upgradeable/access/OwnableUpgradeable.sol:94" - }, - { - "label": "_paused", - "offset": 0, - "slot": "201", - "type": "t_bool", - "contract": "PausableUpgradeable", - "src": "@openzeppelin/contracts-upgradeable/security/PausableUpgradeable.sol:29" - }, - { - "label": "__gap", - "offset": 0, - "slot": "202", - "type": "t_array(t_uint256)49_storage", - "contract": "PausableUpgradeable", - "src": "@openzeppelin/contracts-upgradeable/security/PausableUpgradeable.sol:116" - }, - { - "label": "_status", - "offset": 0, - "slot": "251", - "type": "t_uint256", - "contract": "ReentrancyGuardUpgradeable", - "src": "@openzeppelin/contracts-upgradeable/security/ReentrancyGuardUpgradeable.sol:38" - }, - { - "label": "__gap", - "offset": 0, - "slot": "252", - "type": "t_array(t_uint256)49_storage", - "contract": "ReentrancyGuardUpgradeable", - "src": "@openzeppelin/contracts-upgradeable/security/ReentrancyGuardUpgradeable.sol:74" - }, - { - "label": "_decimals", - "offset": 0, - "slot": "301", - "type": "t_uint8", - "contract": "PerpetualTranche", - "src": "contracts/PerpetualTranche.sol:241" - }, - { - "label": "keeper", - "offset": 1, - "slot": "301", - "type": "t_address", - "contract": "PerpetualTranche", - "src": "contracts/PerpetualTranche.sol:247" - }, - { - "label": "feeStrategy", - "offset": 0, - "slot": "302", - "type": "t_contract(IFeeStrategy)9188", - "contract": "PerpetualTranche", - "src": "contracts/PerpetualTranche.sol:250" - }, - { - "label": "pricingStrategy", - "offset": 0, - "slot": "303", - "type": "t_contract(IPricingStrategy)9502", - "contract": "PerpetualTranche", - "src": "contracts/PerpetualTranche.sol:254" - }, - { - "label": "discountStrategy", - "offset": 0, - "slot": "304", - "type": "t_contract(IDiscountStrategy)9146", - "contract": "PerpetualTranche", - "src": "contracts/PerpetualTranche.sol:262" - }, - { - "label": "bondIssuer", - "offset": 0, - "slot": "305", - "type": "t_contract(IBondIssuer)9126", - "contract": "PerpetualTranche", - "src": "contracts/PerpetualTranche.sol:267" - }, - { - "label": "_depositBond", - "offset": 0, - "slot": "306", - "type": "t_contract(IBondController)9581", - "contract": "PerpetualTranche", - "src": "contracts/PerpetualTranche.sol:270" - }, - { - "label": "minTrancheMaturitySec", - "offset": 0, - "slot": "307", - "type": "t_uint256", - "contract": "PerpetualTranche", - "src": "contracts/PerpetualTranche.sol:274" - }, - { - "label": "maxTrancheMaturitySec", - "offset": 0, - "slot": "308", - "type": "t_uint256", - "contract": "PerpetualTranche", - "src": "contracts/PerpetualTranche.sol:278" - }, - { - "label": "matureValueTargetPerc", - "offset": 0, - "slot": "309", - "type": "t_uint256", - "contract": "PerpetualTranche", - "src": "contracts/PerpetualTranche.sol:281" - }, - { - "label": "maxSupply", - "offset": 0, - "slot": "310", - "type": "t_uint256", - "contract": "PerpetualTranche", - "src": "contracts/PerpetualTranche.sol:284" - }, - { - "label": "maxMintAmtPerTranche", - "offset": 0, - "slot": "311", - "type": "t_uint256", - "contract": "PerpetualTranche", - "src": "contracts/PerpetualTranche.sol:287" - }, - { - "label": "mintedSupplyPerTranche", - "offset": 0, - "slot": "312", - "type": "t_mapping(t_contract(ITranche)9608,t_uint256)", - "contract": "PerpetualTranche", - "src": "contracts/PerpetualTranche.sol:290" - }, - { - "label": "_appliedDiscounts", - "offset": 0, - "slot": "313", - "type": "t_mapping(t_contract(IERC20Upgradeable)1157,t_uint256)", - "contract": "PerpetualTranche", - "src": "contracts/PerpetualTranche.sol:296" - }, - { - "label": "_reserves", - "offset": 0, - "slot": "314", - "type": "t_struct(AddressSet)4620_storage", - "contract": "PerpetualTranche", - "src": "contracts/PerpetualTranche.sol:302" - }, - { - "label": "_matureTrancheBalance", - "offset": 0, - "slot": "316", - "type": "t_uint256", - "contract": "PerpetualTranche", - "src": "contracts/PerpetualTranche.sol:308" - }, - { - "label": "_rollers", - "offset": 0, - "slot": "317", - "type": "t_struct(AddressSet)4620_storage", - "contract": "PerpetualTranche", - "src": "contracts/PerpetualTranche.sol:317" - } - ], - "types": { - "t_address": { - "label": "address", - "numberOfBytes": "20" - }, - "t_array(t_bytes32)dyn_storage": { - "label": "bytes32[]", - "numberOfBytes": "32" - }, - "t_array(t_uint256)45_storage": { - "label": "uint256[45]", - "numberOfBytes": "1440" - }, - "t_array(t_uint256)49_storage": { - "label": "uint256[49]", - "numberOfBytes": "1568" - }, - "t_array(t_uint256)50_storage": { - "label": "uint256[50]", - "numberOfBytes": "1600" - }, - "t_bool": { - "label": "bool", - "numberOfBytes": "1" - }, - "t_bytes32": { - "label": "bytes32", - "numberOfBytes": "32" - }, - "t_contract(IBondController)9581": { - "label": "contract IBondController", - "numberOfBytes": "20" - }, - "t_contract(IBondIssuer)9126": { - "label": "contract IBondIssuer", - "numberOfBytes": "20" - }, - "t_contract(IDiscountStrategy)9146": { - "label": "contract IDiscountStrategy", - "numberOfBytes": "20" - }, - "t_contract(IERC20Upgradeable)1157": { - "label": "contract IERC20Upgradeable", - "numberOfBytes": "20" - }, - "t_contract(IFeeStrategy)9188": { - "label": "contract IFeeStrategy", - "numberOfBytes": "20" - }, - "t_contract(IPricingStrategy)9502": { - "label": "contract IPricingStrategy", - "numberOfBytes": "20" - }, - "t_contract(ITranche)9608": { - "label": "contract ITranche", - "numberOfBytes": "20" - }, - "t_mapping(t_address,t_mapping(t_address,t_uint256))": { - "label": "mapping(address => mapping(address => uint256))", - "numberOfBytes": "32" - }, - "t_mapping(t_address,t_uint256)": { - "label": "mapping(address => uint256)", - "numberOfBytes": "32" - }, - "t_mapping(t_bytes32,t_uint256)": { - "label": "mapping(bytes32 => uint256)", - "numberOfBytes": "32" - }, - "t_mapping(t_contract(IERC20Upgradeable)1157,t_uint256)": { - "label": "mapping(contract IERC20Upgradeable => uint256)", - "numberOfBytes": "32" - }, - "t_mapping(t_contract(ITranche)9608,t_uint256)": { - "label": "mapping(contract ITranche => uint256)", - "numberOfBytes": "32" - }, - "t_string_storage": { - "label": "string", - "numberOfBytes": "32" - }, - "t_struct(AddressSet)4620_storage": { - "label": "struct EnumerableSetUpgradeable.AddressSet", - "members": [ - { - "label": "_inner", - "type": "t_struct(Set)4319_storage", - "offset": 0, - "slot": "0" - } - ], - "numberOfBytes": "64" - }, - "t_struct(Set)4319_storage": { - "label": "struct EnumerableSetUpgradeable.Set", - "members": [ - { - "label": "_values", - "type": "t_array(t_bytes32)dyn_storage", - "offset": 0, - "slot": "0" - }, - { - "label": "_indexes", - "type": "t_mapping(t_bytes32,t_uint256)", - "offset": 0, - "slot": "1" - } - ], - "numberOfBytes": "64" - }, - "t_uint256": { - "label": "uint256", - "numberOfBytes": "32" - }, - "t_uint8": { - "label": "uint8", - "numberOfBytes": "1" - } - } - } - }, - "d22de1c236eb88a7f29757885cb30e820a698673edfc65e1d5350f2a7c377f8a": { - "address": "0x6A62b32eD7900D53abcA6C9147ef206022241EB1", - "txHash": "0xed71d0c4e50c410c5f8ba601a8f386e90460a598c5f9c4adfa68bc4375c77274", - "layout": { - "storage": [ - { - "label": "_initialized", - "offset": 0, - "slot": "0", - "type": "t_uint8", - "contract": "Initializable", - "src": "@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol:62", - "retypedFrom": "bool" - }, - { - "label": "_initializing", - "offset": 1, - "slot": "0", - "type": "t_bool", - "contract": "Initializable", - "src": "@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol:67" - }, - { - "label": "__gap", - "offset": 0, - "slot": "1", - "type": "t_array(t_uint256)50_storage", - "contract": "ContextUpgradeable", - "src": "@openzeppelin/contracts-upgradeable/utils/ContextUpgradeable.sol:36" - }, - { - "label": "_balances", - "offset": 0, - "slot": "51", - "type": "t_mapping(t_address,t_uint256)", - "contract": "ERC20Upgradeable", - "src": "@openzeppelin/contracts-upgradeable/token/ERC20/ERC20Upgradeable.sol:37" - }, - { - "label": "_allowances", - "offset": 0, - "slot": "52", - "type": "t_mapping(t_address,t_mapping(t_address,t_uint256))", - "contract": "ERC20Upgradeable", - "src": "@openzeppelin/contracts-upgradeable/token/ERC20/ERC20Upgradeable.sol:39" - }, - { - "label": "_totalSupply", - "offset": 0, - "slot": "53", - "type": "t_uint256", - "contract": "ERC20Upgradeable", - "src": "@openzeppelin/contracts-upgradeable/token/ERC20/ERC20Upgradeable.sol:41" - }, - { - "label": "_name", - "offset": 0, - "slot": "54", - "type": "t_string_storage", - "contract": "ERC20Upgradeable", - "src": "@openzeppelin/contracts-upgradeable/token/ERC20/ERC20Upgradeable.sol:43" - }, - { - "label": "_symbol", - "offset": 0, - "slot": "55", - "type": "t_string_storage", - "contract": "ERC20Upgradeable", - "src": "@openzeppelin/contracts-upgradeable/token/ERC20/ERC20Upgradeable.sol:44" - }, - { - "label": "__gap", - "offset": 0, - "slot": "56", - "type": "t_array(t_uint256)45_storage", - "contract": "ERC20Upgradeable", - "src": "@openzeppelin/contracts-upgradeable/token/ERC20/ERC20Upgradeable.sol:394" - }, - { - "label": "__gap", - "offset": 0, - "slot": "101", - "type": "t_array(t_uint256)50_storage", - "contract": "ERC20BurnableUpgradeable", - "src": "@openzeppelin/contracts-upgradeable/token/ERC20/extensions/ERC20BurnableUpgradeable.sol:51" - }, - { - "label": "_owner", - "offset": 0, - "slot": "151", - "type": "t_address", - "contract": "OwnableUpgradeable", - "src": "@openzeppelin/contracts-upgradeable/access/OwnableUpgradeable.sol:22" - }, - { - "label": "__gap", - "offset": 0, - "slot": "152", - "type": "t_array(t_uint256)49_storage", - "contract": "OwnableUpgradeable", - "src": "@openzeppelin/contracts-upgradeable/access/OwnableUpgradeable.sol:94" - }, - { - "label": "_paused", - "offset": 0, - "slot": "201", - "type": "t_bool", - "contract": "PausableUpgradeable", - "src": "@openzeppelin/contracts-upgradeable/security/PausableUpgradeable.sol:29" - }, - { - "label": "__gap", - "offset": 0, - "slot": "202", - "type": "t_array(t_uint256)49_storage", - "contract": "PausableUpgradeable", - "src": "@openzeppelin/contracts-upgradeable/security/PausableUpgradeable.sol:116" - }, - { - "label": "_status", - "offset": 0, - "slot": "251", - "type": "t_uint256", - "contract": "ReentrancyGuardUpgradeable", - "src": "@openzeppelin/contracts-upgradeable/security/ReentrancyGuardUpgradeable.sol:38" - }, - { - "label": "__gap", - "offset": 0, - "slot": "252", - "type": "t_array(t_uint256)49_storage", - "contract": "ReentrancyGuardUpgradeable", - "src": "@openzeppelin/contracts-upgradeable/security/ReentrancyGuardUpgradeable.sol:74" - }, - { - "label": "underlying", - "offset": 0, - "slot": "301", - "type": "t_contract(IERC20Upgradeable)1157", - "contract": "RolloverVault", - "src": "contracts/vaults/RolloverVault.sol:88" - }, - { - "label": "_deployed", - "offset": 0, - "slot": "302", - "type": "t_struct(AddressSet)4620_storage", - "contract": "RolloverVault", - "src": "contracts/vaults/RolloverVault.sol:92" - }, - { - "label": "perp", - "offset": 0, - "slot": "304", - "type": "t_contract(IPerpetualTranche)9610", - "contract": "RolloverVault", - "src": "contracts/vaults/RolloverVault.sol:98" - } - ], - "types": { - "t_address": { - "label": "address", - "numberOfBytes": "20" - }, - "t_array(t_bytes32)dyn_storage": { - "label": "bytes32[]", - "numberOfBytes": "32" - }, - "t_array(t_uint256)45_storage": { - "label": "uint256[45]", - "numberOfBytes": "1440" - }, - "t_array(t_uint256)49_storage": { - "label": "uint256[49]", - "numberOfBytes": "1568" - }, - "t_array(t_uint256)50_storage": { - "label": "uint256[50]", - "numberOfBytes": "1600" - }, - "t_bool": { - "label": "bool", - "numberOfBytes": "1" - }, - "t_bytes32": { - "label": "bytes32", - "numberOfBytes": "32" - }, - "t_contract(IERC20Upgradeable)1157": { - "label": "contract IERC20Upgradeable", - "numberOfBytes": "20" - }, - "t_contract(IPerpetualTranche)9610": { - "label": "contract IPerpetualTranche", - "numberOfBytes": "20" - }, - "t_mapping(t_address,t_mapping(t_address,t_uint256))": { - "label": "mapping(address => mapping(address => uint256))", - "numberOfBytes": "32" - }, - "t_mapping(t_address,t_uint256)": { - "label": "mapping(address => uint256)", - "numberOfBytes": "32" - }, - "t_mapping(t_bytes32,t_uint256)": { - "label": "mapping(bytes32 => uint256)", - "numberOfBytes": "32" - }, - "t_string_storage": { - "label": "string", - "numberOfBytes": "32" - }, - "t_struct(AddressSet)4620_storage": { - "label": "struct EnumerableSetUpgradeable.AddressSet", - "members": [ - { - "label": "_inner", - "type": "t_struct(Set)4319_storage", - "offset": 0, - "slot": "0" - } - ], - "numberOfBytes": "64" - }, - "t_struct(Set)4319_storage": { - "label": "struct EnumerableSetUpgradeable.Set", - "members": [ - { - "label": "_values", - "type": "t_array(t_bytes32)dyn_storage", - "offset": 0, - "slot": "0" - }, - { - "label": "_indexes", - "type": "t_mapping(t_bytes32,t_uint256)", - "offset": 0, - "slot": "1" - } - ], - "numberOfBytes": "64" - }, - "t_uint256": { - "label": "uint256", - "numberOfBytes": "32" - }, - "t_uint8": { - "label": "uint8", - "numberOfBytes": "1" - } - } - } - }, - "9edc9af44c4f4a75f717bd966dc754c0374895a6501d3a771c5ed6a16dfe0ccb": { - "address": "0x989c595E6FC8AF16e403d7d3fC8f36A71714dCd7", - "txHash": "0x3474b6f4cfd71134478351d768f2c31e4a3083f9bcf2e63251ab6d3fcd2c1068", - "layout": { - "storage": [ - { - "label": "_initialized", - "offset": 0, - "slot": "0", - "type": "t_uint8", - "contract": "Initializable", - "src": "@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol:62", - "retypedFrom": "bool" - }, - { - "label": "_initializing", - "offset": 1, - "slot": "0", - "type": "t_bool", - "contract": "Initializable", - "src": "@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol:67" - }, - { - "label": "__gap", - "offset": 0, - "slot": "1", - "type": "t_array(t_uint256)50_storage", - "contract": "ContextUpgradeable", - "src": "@openzeppelin/contracts-upgradeable/utils/ContextUpgradeable.sol:36" - }, - { - "label": "_balances", - "offset": 0, - "slot": "51", - "type": "t_mapping(t_address,t_uint256)", - "contract": "ERC20Upgradeable", - "src": "@openzeppelin/contracts-upgradeable/token/ERC20/ERC20Upgradeable.sol:37" - }, - { - "label": "_allowances", - "offset": 0, - "slot": "52", - "type": "t_mapping(t_address,t_mapping(t_address,t_uint256))", - "contract": "ERC20Upgradeable", - "src": "@openzeppelin/contracts-upgradeable/token/ERC20/ERC20Upgradeable.sol:39" - }, - { - "label": "_totalSupply", - "offset": 0, - "slot": "53", - "type": "t_uint256", - "contract": "ERC20Upgradeable", - "src": "@openzeppelin/contracts-upgradeable/token/ERC20/ERC20Upgradeable.sol:41" - }, - { - "label": "_name", - "offset": 0, - "slot": "54", - "type": "t_string_storage", - "contract": "ERC20Upgradeable", - "src": "@openzeppelin/contracts-upgradeable/token/ERC20/ERC20Upgradeable.sol:43" - }, - { - "label": "_symbol", - "offset": 0, - "slot": "55", - "type": "t_string_storage", - "contract": "ERC20Upgradeable", - "src": "@openzeppelin/contracts-upgradeable/token/ERC20/ERC20Upgradeable.sol:44" - }, - { - "label": "__gap", - "offset": 0, - "slot": "56", - "type": "t_array(t_uint256)45_storage", - "contract": "ERC20Upgradeable", - "src": "@openzeppelin/contracts-upgradeable/token/ERC20/ERC20Upgradeable.sol:394" - }, - { - "label": "__gap", - "offset": 0, - "slot": "101", - "type": "t_array(t_uint256)50_storage", - "contract": "ERC20BurnableUpgradeable", - "src": "@openzeppelin/contracts-upgradeable/token/ERC20/extensions/ERC20BurnableUpgradeable.sol:51" - }, - { - "label": "_owner", - "offset": 0, - "slot": "151", - "type": "t_address", - "contract": "OwnableUpgradeable", - "src": "@openzeppelin/contracts-upgradeable/access/OwnableUpgradeable.sol:22" - }, - { - "label": "__gap", - "offset": 0, - "slot": "152", - "type": "t_array(t_uint256)49_storage", - "contract": "OwnableUpgradeable", - "src": "@openzeppelin/contracts-upgradeable/access/OwnableUpgradeable.sol:94" - }, - { - "label": "_paused", - "offset": 0, - "slot": "201", - "type": "t_bool", - "contract": "PausableUpgradeable", - "src": "@openzeppelin/contracts-upgradeable/security/PausableUpgradeable.sol:29" - }, - { - "label": "__gap", - "offset": 0, - "slot": "202", - "type": "t_array(t_uint256)49_storage", - "contract": "PausableUpgradeable", - "src": "@openzeppelin/contracts-upgradeable/security/PausableUpgradeable.sol:116" - }, - { - "label": "_status", - "offset": 0, - "slot": "251", - "type": "t_uint256", - "contract": "ReentrancyGuardUpgradeable", - "src": "@openzeppelin/contracts-upgradeable/security/ReentrancyGuardUpgradeable.sol:38" - }, - { - "label": "__gap", - "offset": 0, - "slot": "252", - "type": "t_array(t_uint256)49_storage", - "contract": "ReentrancyGuardUpgradeable", - "src": "@openzeppelin/contracts-upgradeable/security/ReentrancyGuardUpgradeable.sol:74" - }, - { - "label": "minDeploymentAmt", - "offset": 0, - "slot": "301", - "type": "t_uint256", - "contract": "RolloverVault", - "src": "contracts/vaults/RolloverVault.sol:83" - }, - { - "label": "underlying", - "offset": 0, - "slot": "302", - "type": "t_contract(IERC20Upgradeable)1157", - "contract": "RolloverVault", - "src": "contracts/vaults/RolloverVault.sol:95" - }, - { - "label": "_deployed", - "offset": 0, - "slot": "303", - "type": "t_struct(AddressSet)4620_storage", - "contract": "RolloverVault", - "src": "contracts/vaults/RolloverVault.sol:99" - }, - { - "label": "perp", - "offset": 0, - "slot": "305", - "type": "t_contract(IPerpetualTranche)9174", - "contract": "RolloverVault", - "src": "contracts/vaults/RolloverVault.sol:105" - } - ], - "types": { - "t_address": { - "label": "address", - "numberOfBytes": "20" - }, - "t_array(t_bytes32)dyn_storage": { - "label": "bytes32[]", - "numberOfBytes": "32" - }, - "t_array(t_uint256)45_storage": { - "label": "uint256[45]", - "numberOfBytes": "1440" - }, - "t_array(t_uint256)49_storage": { - "label": "uint256[49]", - "numberOfBytes": "1568" - }, - "t_array(t_uint256)50_storage": { - "label": "uint256[50]", - "numberOfBytes": "1600" - }, - "t_bool": { - "label": "bool", - "numberOfBytes": "1" - }, - "t_bytes32": { - "label": "bytes32", - "numberOfBytes": "32" - }, - "t_contract(IERC20Upgradeable)1157": { - "label": "contract IERC20Upgradeable", - "numberOfBytes": "20" - }, - "t_contract(IPerpetualTranche)9174": { - "label": "contract IPerpetualTranche", - "numberOfBytes": "20" - }, - "t_mapping(t_address,t_mapping(t_address,t_uint256))": { - "label": "mapping(address => mapping(address => uint256))", - "numberOfBytes": "32" - }, - "t_mapping(t_address,t_uint256)": { - "label": "mapping(address => uint256)", - "numberOfBytes": "32" - }, - "t_mapping(t_bytes32,t_uint256)": { - "label": "mapping(bytes32 => uint256)", - "numberOfBytes": "32" - }, - "t_string_storage": { - "label": "string", - "numberOfBytes": "32" - }, - "t_struct(AddressSet)4620_storage": { - "label": "struct EnumerableSetUpgradeable.AddressSet", - "members": [ - { - "label": "_inner", - "type": "t_struct(Set)4319_storage", - "offset": 0, - "slot": "0" - } - ], - "numberOfBytes": "64" - }, - "t_struct(Set)4319_storage": { - "label": "struct EnumerableSetUpgradeable.Set", - "members": [ - { - "label": "_values", - "type": "t_array(t_bytes32)dyn_storage", - "offset": 0, - "slot": "0" - }, - { - "label": "_indexes", - "type": "t_mapping(t_bytes32,t_uint256)", - "offset": 0, - "slot": "1" - } - ], - "numberOfBytes": "64" - }, - "t_uint256": { - "label": "uint256", - "numberOfBytes": "32" - }, - "t_uint8": { - "label": "uint8", - "numberOfBytes": "1" - } - } - } - }, - "d1cd30a416ca26066c11c9f363f7ca43c0a6fce789d74cfa43b3d58a3b92ce39": { - "address": "0x5D352731b5A82A089Fe49BCd143d705c3A3c8889", - "txHash": "0x44d72a59b89802d03f627fdbf860862013d83b3d0966be661aef101bfb2afbeb", - "layout": { - "storage": [ - { - "label": "_initialized", - "offset": 0, - "slot": "0", - "type": "t_uint8", - "contract": "Initializable", - "src": "@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol:62", - "retypedFrom": "bool" - }, - { - "label": "_initializing", - "offset": 1, - "slot": "0", - "type": "t_bool", - "contract": "Initializable", - "src": "@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol:67" - }, - { - "label": "__gap", - "offset": 0, - "slot": "1", - "type": "t_array(t_uint256)50_storage", - "contract": "ContextUpgradeable", - "src": "@openzeppelin/contracts-upgradeable/utils/ContextUpgradeable.sol:36" - }, - { - "label": "_balances", - "offset": 0, - "slot": "51", - "type": "t_mapping(t_address,t_uint256)", - "contract": "ERC20Upgradeable", - "src": "@openzeppelin/contracts-upgradeable/token/ERC20/ERC20Upgradeable.sol:37" - }, - { - "label": "_allowances", - "offset": 0, - "slot": "52", - "type": "t_mapping(t_address,t_mapping(t_address,t_uint256))", - "contract": "ERC20Upgradeable", - "src": "@openzeppelin/contracts-upgradeable/token/ERC20/ERC20Upgradeable.sol:39" - }, - { - "label": "_totalSupply", - "offset": 0, - "slot": "53", - "type": "t_uint256", - "contract": "ERC20Upgradeable", - "src": "@openzeppelin/contracts-upgradeable/token/ERC20/ERC20Upgradeable.sol:41" - }, - { - "label": "_name", - "offset": 0, - "slot": "54", - "type": "t_string_storage", - "contract": "ERC20Upgradeable", - "src": "@openzeppelin/contracts-upgradeable/token/ERC20/ERC20Upgradeable.sol:43" - }, - { - "label": "_symbol", - "offset": 0, - "slot": "55", - "type": "t_string_storage", - "contract": "ERC20Upgradeable", - "src": "@openzeppelin/contracts-upgradeable/token/ERC20/ERC20Upgradeable.sol:44" - }, - { - "label": "__gap", - "offset": 0, - "slot": "56", - "type": "t_array(t_uint256)45_storage", - "contract": "ERC20Upgradeable", - "src": "@openzeppelin/contracts-upgradeable/token/ERC20/ERC20Upgradeable.sol:394" - }, - { - "label": "__gap", - "offset": 0, - "slot": "101", - "type": "t_array(t_uint256)50_storage", - "contract": "ERC20BurnableUpgradeable", - "src": "@openzeppelin/contracts-upgradeable/token/ERC20/extensions/ERC20BurnableUpgradeable.sol:51" - }, - { - "label": "_owner", - "offset": 0, - "slot": "151", - "type": "t_address", - "contract": "OwnableUpgradeable", - "src": "@openzeppelin/contracts-upgradeable/access/OwnableUpgradeable.sol:22" - }, - { - "label": "__gap", - "offset": 0, - "slot": "152", - "type": "t_array(t_uint256)49_storage", - "contract": "OwnableUpgradeable", - "src": "@openzeppelin/contracts-upgradeable/access/OwnableUpgradeable.sol:94" - }, - { - "label": "_paused", - "offset": 0, - "slot": "201", - "type": "t_bool", - "contract": "PausableUpgradeable", - "src": "@openzeppelin/contracts-upgradeable/security/PausableUpgradeable.sol:29" - }, - { - "label": "__gap", - "offset": 0, - "slot": "202", - "type": "t_array(t_uint256)49_storage", - "contract": "PausableUpgradeable", - "src": "@openzeppelin/contracts-upgradeable/security/PausableUpgradeable.sol:116" - }, - { - "label": "_status", - "offset": 0, - "slot": "251", - "type": "t_uint256", - "contract": "ReentrancyGuardUpgradeable", - "src": "@openzeppelin/contracts-upgradeable/security/ReentrancyGuardUpgradeable.sol:38" - }, - { - "label": "__gap", - "offset": 0, - "slot": "252", - "type": "t_array(t_uint256)49_storage", - "contract": "ReentrancyGuardUpgradeable", - "src": "@openzeppelin/contracts-upgradeable/security/ReentrancyGuardUpgradeable.sol:74" - }, - { - "label": "underlying", - "offset": 0, - "slot": "301", - "type": "t_contract(IERC20Upgradeable)1157", - "contract": "RolloverVault", - "src": "contracts/vaults/RolloverVault.sol:87" - }, - { - "label": "_deployed", - "offset": 0, - "slot": "302", - "type": "t_struct(AddressSet)4620_storage", - "contract": "RolloverVault", - "src": "contracts/vaults/RolloverVault.sol:91" - }, - { - "label": "minDeploymentAmt", - "offset": 0, - "slot": "304", - "type": "t_uint256", - "contract": "RolloverVault", - "src": "contracts/vaults/RolloverVault.sol:99" - }, - { - "label": "perp", - "offset": 0, - "slot": "305", - "type": "t_contract(IPerpetualTranche)9651", - "contract": "RolloverVault", - "src": "contracts/vaults/RolloverVault.sol:102" - } - ], - "types": { - "t_address": { - "label": "address", - "numberOfBytes": "20" - }, - "t_array(t_bytes32)dyn_storage": { - "label": "bytes32[]", - "numberOfBytes": "32" - }, - "t_array(t_uint256)45_storage": { - "label": "uint256[45]", - "numberOfBytes": "1440" - }, - "t_array(t_uint256)49_storage": { - "label": "uint256[49]", - "numberOfBytes": "1568" - }, - "t_array(t_uint256)50_storage": { - "label": "uint256[50]", - "numberOfBytes": "1600" - }, - "t_bool": { - "label": "bool", - "numberOfBytes": "1" - }, - "t_bytes32": { - "label": "bytes32", - "numberOfBytes": "32" - }, - "t_contract(IERC20Upgradeable)1157": { - "label": "contract IERC20Upgradeable", - "numberOfBytes": "20" - }, - "t_contract(IPerpetualTranche)9651": { - "label": "contract IPerpetualTranche", - "numberOfBytes": "20" - }, - "t_mapping(t_address,t_mapping(t_address,t_uint256))": { - "label": "mapping(address => mapping(address => uint256))", - "numberOfBytes": "32" - }, - "t_mapping(t_address,t_uint256)": { - "label": "mapping(address => uint256)", - "numberOfBytes": "32" - }, - "t_mapping(t_bytes32,t_uint256)": { - "label": "mapping(bytes32 => uint256)", - "numberOfBytes": "32" - }, - "t_string_storage": { - "label": "string", - "numberOfBytes": "32" - }, - "t_struct(AddressSet)4620_storage": { - "label": "struct EnumerableSetUpgradeable.AddressSet", - "members": [ - { - "label": "_inner", - "type": "t_struct(Set)4319_storage", - "offset": 0, - "slot": "0" - } - ], - "numberOfBytes": "64" - }, - "t_struct(Set)4319_storage": { - "label": "struct EnumerableSetUpgradeable.Set", - "members": [ - { - "label": "_values", - "type": "t_array(t_bytes32)dyn_storage", - "offset": 0, - "slot": "0" - }, - { - "label": "_indexes", - "type": "t_mapping(t_bytes32,t_uint256)", - "offset": 0, - "slot": "1" - } - ], - "numberOfBytes": "64" - }, - "t_uint256": { - "label": "uint256", - "numberOfBytes": "32" - }, - "t_uint8": { - "label": "uint8", - "numberOfBytes": "1" - } - } - } - }, - "ac12aceddf8379e717d1486a2e7721e38459244dab050f70225d047f2a3b136c": { - "address": "0x5Ec6f02D0b657E4a56d6020Bc21F19f2Ca13EcA9", - "txHash": "0x998860fce2b51560368735dbbb258a847be95f218e3ce321c46725bcf04efd89", - "layout": { - "storage": [ - { - "label": "_initialized", - "offset": 0, - "slot": "0", - "type": "t_uint8", - "contract": "Initializable", - "src": "@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol:62", - "retypedFrom": "bool" - }, - { - "label": "_initializing", - "offset": 1, - "slot": "0", - "type": "t_bool", - "contract": "Initializable", - "src": "@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol:67" - }, - { - "label": "__gap", - "offset": 0, - "slot": "1", - "type": "t_array(t_uint256)50_storage", - "contract": "ContextUpgradeable", - "src": "@openzeppelin/contracts-upgradeable/utils/ContextUpgradeable.sol:36" - }, - { - "label": "_balances", - "offset": 0, - "slot": "51", - "type": "t_mapping(t_address,t_uint256)", - "contract": "ERC20Upgradeable", - "src": "@openzeppelin/contracts-upgradeable/token/ERC20/ERC20Upgradeable.sol:37" - }, - { - "label": "_allowances", - "offset": 0, - "slot": "52", - "type": "t_mapping(t_address,t_mapping(t_address,t_uint256))", - "contract": "ERC20Upgradeable", - "src": "@openzeppelin/contracts-upgradeable/token/ERC20/ERC20Upgradeable.sol:39" - }, - { - "label": "_totalSupply", - "offset": 0, - "slot": "53", - "type": "t_uint256", - "contract": "ERC20Upgradeable", - "src": "@openzeppelin/contracts-upgradeable/token/ERC20/ERC20Upgradeable.sol:41" - }, - { - "label": "_name", - "offset": 0, - "slot": "54", - "type": "t_string_storage", - "contract": "ERC20Upgradeable", - "src": "@openzeppelin/contracts-upgradeable/token/ERC20/ERC20Upgradeable.sol:43" - }, - { - "label": "_symbol", - "offset": 0, - "slot": "55", - "type": "t_string_storage", - "contract": "ERC20Upgradeable", - "src": "@openzeppelin/contracts-upgradeable/token/ERC20/ERC20Upgradeable.sol:44" - }, - { - "label": "__gap", - "offset": 0, - "slot": "56", - "type": "t_array(t_uint256)45_storage", - "contract": "ERC20Upgradeable", - "src": "@openzeppelin/contracts-upgradeable/token/ERC20/ERC20Upgradeable.sol:394" - }, - { - "label": "__gap", - "offset": 0, - "slot": "101", - "type": "t_array(t_uint256)50_storage", - "contract": "ERC20BurnableUpgradeable", - "src": "@openzeppelin/contracts-upgradeable/token/ERC20/extensions/ERC20BurnableUpgradeable.sol:51" - }, - { - "label": "_owner", - "offset": 0, - "slot": "151", - "type": "t_address", - "contract": "OwnableUpgradeable", - "src": "@openzeppelin/contracts-upgradeable/access/OwnableUpgradeable.sol:22" - }, - { - "label": "__gap", - "offset": 0, - "slot": "152", - "type": "t_array(t_uint256)49_storage", - "contract": "OwnableUpgradeable", - "src": "@openzeppelin/contracts-upgradeable/access/OwnableUpgradeable.sol:94" - }, - { - "label": "_paused", - "offset": 0, - "slot": "201", - "type": "t_bool", - "contract": "PausableUpgradeable", - "src": "@openzeppelin/contracts-upgradeable/security/PausableUpgradeable.sol:29" - }, - { - "label": "__gap", - "offset": 0, - "slot": "202", - "type": "t_array(t_uint256)49_storage", - "contract": "PausableUpgradeable", - "src": "@openzeppelin/contracts-upgradeable/security/PausableUpgradeable.sol:116" - }, - { - "label": "_status", - "offset": 0, - "slot": "251", - "type": "t_uint256", - "contract": "ReentrancyGuardUpgradeable", - "src": "@openzeppelin/contracts-upgradeable/security/ReentrancyGuardUpgradeable.sol:38" - }, - { - "label": "__gap", - "offset": 0, - "slot": "252", - "type": "t_array(t_uint256)49_storage", - "contract": "ReentrancyGuardUpgradeable", - "src": "@openzeppelin/contracts-upgradeable/security/ReentrancyGuardUpgradeable.sol:74" - }, - { - "label": "_decimals", - "offset": 0, - "slot": "301", - "type": "t_uint8", - "contract": "PerpetualTranche", - "src": "contracts/PerpetualTranche.sol:239" - }, - { - "label": "keeper", - "offset": 1, - "slot": "301", - "type": "t_address", - "contract": "PerpetualTranche", - "src": "contracts/PerpetualTranche.sol:245" - }, - { - "label": "feeStrategy", - "offset": 0, - "slot": "302", - "type": "t_contract(IFeeStrategy)9363", - "contract": "PerpetualTranche", - "src": "contracts/PerpetualTranche.sol:248" - }, - { - "label": "pricingStrategy", - "offset": 0, - "slot": "303", - "type": "t_contract(IPricingStrategy)9686", - "contract": "PerpetualTranche", - "src": "contracts/PerpetualTranche.sol:252" - }, - { - "label": "discountStrategy", - "offset": 0, - "slot": "304", - "type": "t_contract(IDiscountStrategy)9321", - "contract": "PerpetualTranche", - "src": "contracts/PerpetualTranche.sol:260" - }, - { - "label": "bondIssuer", - "offset": 0, - "slot": "305", - "type": "t_contract(IBondIssuer)9301", - "contract": "PerpetualTranche", - "src": "contracts/PerpetualTranche.sol:265" - }, - { - "label": "_depositBond", - "offset": 0, - "slot": "306", - "type": "t_contract(IBondController)9903", - "contract": "PerpetualTranche", - "src": "contracts/PerpetualTranche.sol:268" - }, - { - "label": "minTrancheMaturitySec", - "offset": 0, - "slot": "307", - "type": "t_uint256", - "contract": "PerpetualTranche", - "src": "contracts/PerpetualTranche.sol:272" - }, - { - "label": "maxTrancheMaturitySec", - "offset": 0, - "slot": "308", - "type": "t_uint256", - "contract": "PerpetualTranche", - "src": "contracts/PerpetualTranche.sol:276" - }, - { - "label": "matureValueTargetPerc", - "offset": 0, - "slot": "309", - "type": "t_uint256", - "contract": "PerpetualTranche", - "src": "contracts/PerpetualTranche.sol:279" - }, - { - "label": "maxSupply", - "offset": 0, - "slot": "310", - "type": "t_uint256", - "contract": "PerpetualTranche", - "src": "contracts/PerpetualTranche.sol:282" - }, - { - "label": "maxMintAmtPerTranche", - "offset": 0, - "slot": "311", - "type": "t_uint256", - "contract": "PerpetualTranche", - "src": "contracts/PerpetualTranche.sol:285" - }, - { - "label": "mintedSupplyPerTranche", - "offset": 0, - "slot": "312", - "type": "t_mapping(t_contract(ITranche)9930,t_uint256)", - "contract": "PerpetualTranche", - "src": "contracts/PerpetualTranche.sol:288" - }, - { - "label": "_appliedDiscounts", - "offset": 0, - "slot": "313", - "type": "t_mapping(t_contract(IERC20Upgradeable)1157,t_uint256)", - "contract": "PerpetualTranche", - "src": "contracts/PerpetualTranche.sol:294" - }, - { - "label": "_reserves", - "offset": 0, - "slot": "314", - "type": "t_struct(AddressSet)4620_storage", - "contract": "PerpetualTranche", - "src": "contracts/PerpetualTranche.sol:300" - }, - { - "label": "_matureTrancheBalance", - "offset": 0, - "slot": "316", - "type": "t_uint256", - "contract": "PerpetualTranche", - "src": "contracts/PerpetualTranche.sol:306" - }, - { - "label": "_rollers", - "offset": 0, - "slot": "317", - "type": "t_struct(AddressSet)4620_storage", - "contract": "PerpetualTranche", - "src": "contracts/PerpetualTranche.sol:315" - } - ], - "types": { - "t_address": { - "label": "address", - "numberOfBytes": "20" - }, - "t_array(t_bytes32)dyn_storage": { - "label": "bytes32[]", - "numberOfBytes": "32" - }, - "t_array(t_uint256)45_storage": { - "label": "uint256[45]", - "numberOfBytes": "1440" - }, - "t_array(t_uint256)49_storage": { - "label": "uint256[49]", - "numberOfBytes": "1568" - }, - "t_array(t_uint256)50_storage": { - "label": "uint256[50]", - "numberOfBytes": "1600" - }, - "t_bool": { - "label": "bool", - "numberOfBytes": "1" - }, - "t_bytes32": { - "label": "bytes32", - "numberOfBytes": "32" - }, - "t_contract(IBondController)9903": { - "label": "contract IBondController", - "numberOfBytes": "20" - }, - "t_contract(IBondIssuer)9301": { - "label": "contract IBondIssuer", - "numberOfBytes": "20" - }, - "t_contract(IDiscountStrategy)9321": { - "label": "contract IDiscountStrategy", - "numberOfBytes": "20" - }, - "t_contract(IERC20Upgradeable)1157": { - "label": "contract IERC20Upgradeable", - "numberOfBytes": "20" - }, - "t_contract(IFeeStrategy)9363": { - "label": "contract IFeeStrategy", - "numberOfBytes": "20" - }, - "t_contract(IPricingStrategy)9686": { - "label": "contract IPricingStrategy", - "numberOfBytes": "20" - }, - "t_contract(ITranche)9930": { - "label": "contract ITranche", - "numberOfBytes": "20" - }, - "t_mapping(t_address,t_mapping(t_address,t_uint256))": { - "label": "mapping(address => mapping(address => uint256))", - "numberOfBytes": "32" - }, - "t_mapping(t_address,t_uint256)": { - "label": "mapping(address => uint256)", - "numberOfBytes": "32" - }, - "t_mapping(t_bytes32,t_uint256)": { - "label": "mapping(bytes32 => uint256)", - "numberOfBytes": "32" - }, - "t_mapping(t_contract(IERC20Upgradeable)1157,t_uint256)": { - "label": "mapping(contract IERC20Upgradeable => uint256)", - "numberOfBytes": "32" - }, - "t_mapping(t_contract(ITranche)9930,t_uint256)": { - "label": "mapping(contract ITranche => uint256)", - "numberOfBytes": "32" - }, - "t_string_storage": { - "label": "string", - "numberOfBytes": "32" - }, - "t_struct(AddressSet)4620_storage": { - "label": "struct EnumerableSetUpgradeable.AddressSet", - "members": [ - { - "label": "_inner", - "type": "t_struct(Set)4319_storage", - "offset": 0, - "slot": "0" - } - ], - "numberOfBytes": "64" - }, - "t_struct(Set)4319_storage": { - "label": "struct EnumerableSetUpgradeable.Set", - "members": [ - { - "label": "_values", - "type": "t_array(t_bytes32)dyn_storage", - "offset": 0, - "slot": "0" - }, - { - "label": "_indexes", - "type": "t_mapping(t_bytes32,t_uint256)", - "offset": 0, - "slot": "1" - } - ], - "numberOfBytes": "64" - }, - "t_uint256": { - "label": "uint256", - "numberOfBytes": "32" - }, - "t_uint8": { - "label": "uint8", - "numberOfBytes": "1" - } - } - } - }, - "434e3f9e5ca89604923f560238c1549d17341d5000a67900fb3b705d5348ddd7": { - "address": "0x795F55f37bd9ab8fa361D0FC0eAd6ff036aebA44", - "txHash": "0xc7aefe44394c954dca2ff7315e5c11c3e6d48c10da9a76b724964c2f650eb19f", - "layout": { - "storage": [ - { - "label": "_initialized", - "offset": 0, - "slot": "0", - "type": "t_uint8", - "contract": "Initializable", - "src": "contracts/oz/Initializable.sol:62", - "retypedFrom": "bool" - }, - { - "label": "_initializing", - "offset": 1, - "slot": "0", - "type": "t_bool", - "contract": "Initializable", - "src": "contracts/oz/Initializable.sol:67" - }, - { - "label": "__gap", - "offset": 0, - "slot": "1", - "type": "t_array(t_uint256)50_storage", - "contract": "ContextUpgradeable", - "src": "contracts/oz/ContextUpgradeable.sol:35" - }, - { - "label": "_balances", - "offset": 0, - "slot": "51", - "type": "t_mapping(t_address,t_uint256)", - "contract": "ERC20Upgradeable", - "src": "contracts/oz/ERC20Upgradeable.sol:37" - }, - { - "label": "_allowances", - "offset": 0, - "slot": "52", - "type": "t_mapping(t_address,t_mapping(t_address,t_uint256))", - "contract": "ERC20Upgradeable", - "src": "contracts/oz/ERC20Upgradeable.sol:39" - }, - { - "label": "_totalSupply", - "offset": 0, - "slot": "53", - "type": "t_uint256", - "contract": "ERC20Upgradeable", - "src": "contracts/oz/ERC20Upgradeable.sol:41" - }, - { - "label": "_name", - "offset": 0, - "slot": "54", - "type": "t_string_storage", - "contract": "ERC20Upgradeable", - "src": "contracts/oz/ERC20Upgradeable.sol:43" - }, - { - "label": "_symbol", - "offset": 0, - "slot": "55", - "type": "t_string_storage", - "contract": "ERC20Upgradeable", - "src": "contracts/oz/ERC20Upgradeable.sol:44" - }, - { - "label": "__gap", - "offset": 0, - "slot": "56", - "type": "t_array(t_uint256)45_storage", - "contract": "ERC20Upgradeable", - "src": "contracts/oz/ERC20Upgradeable.sol:394" - }, - { - "label": "__gap", - "offset": 0, - "slot": "101", - "type": "t_array(t_uint256)50_storage", - "contract": "ERC20BurnableUpgradeable", - "src": "contracts/oz/ERC20BurnableUpgradeable.sol:50" - }, - { - "label": "_owner", - "offset": 0, - "slot": "151", - "type": "t_address", - "contract": "OwnableUpgradeable", - "src": "contracts/oz/OwnableUpgradeable.sol:22" - }, - { - "label": "__gap", - "offset": 0, - "slot": "152", - "type": "t_array(t_uint256)49_storage", - "contract": "OwnableUpgradeable", - "src": "contracts/oz/OwnableUpgradeable.sol:94" - }, - { - "label": "_paused", - "offset": 0, - "slot": "201", - "type": "t_bool", - "contract": "PausableUpgradeable", - "src": "contracts/oz/PausableUpgradeable.sol:29" - }, - { - "label": "__gap", - "offset": 0, - "slot": "202", - "type": "t_array(t_uint256)49_storage", - "contract": "PausableUpgradeable", - "src": "contracts/oz/PausableUpgradeable.sol:116" - }, - { - "label": "_status", - "offset": 0, - "slot": "251", - "type": "t_uint256", - "contract": "ReentrancyGuardUpgradeable", - "src": "contracts/oz/ReentrancyGuardUpgradeable.sol:38" - }, - { - "label": "__gap", - "offset": 0, - "slot": "252", - "type": "t_array(t_uint256)49_storage", - "contract": "ReentrancyGuardUpgradeable", - "src": "contracts/oz/ReentrancyGuardUpgradeable.sol:74" - }, - { - "label": "underlying", - "offset": 0, - "slot": "301", - "type": "t_contract(IERC20Upgradeable)6122", - "contract": "RolloverVault", - "src": "contracts/vaults/RolloverVault.sol:88" - }, - { - "label": "_deployed", - "offset": 0, - "slot": "302", - "type": "t_struct(AddressSet)5731_storage", - "contract": "RolloverVault", - "src": "contracts/vaults/RolloverVault.sol:92" - }, - { - "label": "minDeploymentAmt", - "offset": 0, - "slot": "304", - "type": "t_uint256", - "contract": "RolloverVault", - "src": "contracts/vaults/RolloverVault.sol:100" - }, - { - "label": "perp", - "offset": 0, - "slot": "305", - "type": "t_contract(IPerpetualTranche)3406", - "contract": "RolloverVault", - "src": "contracts/vaults/RolloverVault.sol:103" - } - ], - "types": { - "t_address": { - "label": "address", - "numberOfBytes": "20" - }, - "t_array(t_bytes32)dyn_storage": { - "label": "bytes32[]", - "numberOfBytes": "32" - }, - "t_array(t_uint256)45_storage": { - "label": "uint256[45]", - "numberOfBytes": "1440" - }, - "t_array(t_uint256)49_storage": { - "label": "uint256[49]", - "numberOfBytes": "1568" - }, - "t_array(t_uint256)50_storage": { - "label": "uint256[50]", - "numberOfBytes": "1600" - }, - "t_bool": { - "label": "bool", - "numberOfBytes": "1" - }, - "t_bytes32": { - "label": "bytes32", - "numberOfBytes": "32" - }, - "t_contract(IERC20Upgradeable)6122": { - "label": "contract IERC20Upgradeable", - "numberOfBytes": "20" - }, - "t_contract(IPerpetualTranche)3406": { - "label": "contract IPerpetualTranche", - "numberOfBytes": "20" - }, - "t_mapping(t_address,t_mapping(t_address,t_uint256))": { - "label": "mapping(address => mapping(address => uint256))", - "numberOfBytes": "32" - }, - "t_mapping(t_address,t_uint256)": { - "label": "mapping(address => uint256)", - "numberOfBytes": "32" - }, - "t_mapping(t_bytes32,t_uint256)": { - "label": "mapping(bytes32 => uint256)", - "numberOfBytes": "32" - }, - "t_string_storage": { - "label": "string", - "numberOfBytes": "32" - }, - "t_struct(AddressSet)5731_storage": { - "label": "struct EnumerableSetUpgradeable.AddressSet", - "members": [ - { - "label": "_inner", - "type": "t_struct(Set)5430_storage", - "offset": 0, - "slot": "0" - } - ], - "numberOfBytes": "64" - }, - "t_struct(Set)5430_storage": { - "label": "struct EnumerableSetUpgradeable.Set", - "members": [ - { - "label": "_values", - "type": "t_array(t_bytes32)dyn_storage", - "offset": 0, - "slot": "0" - }, - { - "label": "_indexes", - "type": "t_mapping(t_bytes32,t_uint256)", - "offset": 0, - "slot": "1" - } - ], - "numberOfBytes": "64" - }, - "t_uint256": { - "label": "uint256", - "numberOfBytes": "32" - }, - "t_uint8": { - "label": "uint8", - "numberOfBytes": "1" - } - } - } - } - } -} diff --git a/spot-contracts/.openzeppelin/mainnet.json b/spot-contracts/.openzeppelin/mainnet.json index a2913ff3..b417140d 100644 --- a/spot-contracts/.openzeppelin/mainnet.json +++ b/spot-contracts/.openzeppelin/mainnet.json @@ -14,6 +14,11 @@ "address": "0x82A91a0D599A45d8E9Af781D67f695d7C72869Bd", "txHash": "0xc5b2302ca23f26fa26327bb3abb36827ca282cd20f0e571bd9cede84e6b5d329", "kind": "transparent" + }, + { + "address": "0xE22977381506bF094CB3ed50CB8834E358F7ef6c", + "txHash": "0x4942e46f0c306087f3485aa8c6d74fec37d1fa1f7a59acfb385b2ed8173e745c", + "kind": "transparent" } ], "impls": { @@ -1569,6 +1574,881 @@ } } } + }, + "2546bfa1b5a5142a0d95cea7e7372f3a735712bf05bbaab3a7df09b1022fb6ee": { + "address": "0xA3dA50b74e2e3b64bc648F142BAfFb164bCf158c", + "txHash": "0x0984fe32f721b10249ec6be9f5d1fef495bf3d60b88e78f296890964e44ae69f", + "layout": { + "solcVersion": "0.8.20", + "storage": [ + { + "label": "_initialized", + "offset": 0, + "slot": "0", + "type": "t_uint8", + "contract": "Initializable", + "src": "@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol:63", + "retypedFrom": "bool" + }, + { + "label": "_initializing", + "offset": 1, + "slot": "0", + "type": "t_bool", + "contract": "Initializable", + "src": "@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol:68" + }, + { + "label": "__gap", + "offset": 0, + "slot": "1", + "type": "t_array(t_uint256)50_storage", + "contract": "ContextUpgradeable", + "src": "@openzeppelin/contracts-upgradeable/utils/ContextUpgradeable.sol:40" + }, + { + "label": "_owner", + "offset": 0, + "slot": "51", + "type": "t_address", + "contract": "OwnableUpgradeable", + "src": "@openzeppelin/contracts-upgradeable/access/OwnableUpgradeable.sol:22" + }, + { + "label": "__gap", + "offset": 0, + "slot": "52", + "type": "t_array(t_uint256)49_storage", + "contract": "OwnableUpgradeable", + "src": "@openzeppelin/contracts-upgradeable/access/OwnableUpgradeable.sol:94" + }, + { + "label": "targetSubscriptionRatio", + "offset": 0, + "slot": "101", + "type": "t_uint256", + "contract": "FeePolicy", + "src": "contracts/FeePolicy.sol:84" + }, + { + "label": "deviationRatioBoundLower", + "offset": 0, + "slot": "102", + "type": "t_uint256", + "contract": "FeePolicy", + "src": "contracts/FeePolicy.sol:87" + }, + { + "label": "deviationRatioBoundUpper", + "offset": 0, + "slot": "103", + "type": "t_uint256", + "contract": "FeePolicy", + "src": "contracts/FeePolicy.sol:90" + }, + { + "label": "perpMintFeePerc", + "offset": 0, + "slot": "104", + "type": "t_uint256", + "contract": "FeePolicy", + "src": "contracts/FeePolicy.sol:98" + }, + { + "label": "perpBurnFeePerc", + "offset": 0, + "slot": "105", + "type": "t_uint256", + "contract": "FeePolicy", + "src": "contracts/FeePolicy.sol:101" + }, + { + "label": "perpRolloverFee", + "offset": 0, + "slot": "106", + "type": "t_struct(RolloverFeeSigmoidParams)5764_storage", + "contract": "FeePolicy", + "src": "contracts/FeePolicy.sol:113" + }, + { + "label": "vaultMintFeePerc", + "offset": 0, + "slot": "109", + "type": "t_uint256", + "contract": "FeePolicy", + "src": "contracts/FeePolicy.sol:121" + }, + { + "label": "vaultBurnFeePerc", + "offset": 0, + "slot": "110", + "type": "t_uint256", + "contract": "FeePolicy", + "src": "contracts/FeePolicy.sol:124" + }, + { + "label": "vaultUnderlyingToPerpSwapFeePerc", + "offset": 0, + "slot": "111", + "type": "t_uint256", + "contract": "FeePolicy", + "src": "contracts/FeePolicy.sol:127" + }, + { + "label": "vaultPerpToUnderlyingSwapFeePerc", + "offset": 0, + "slot": "112", + "type": "t_uint256", + "contract": "FeePolicy", + "src": "contracts/FeePolicy.sol:130" + } + ], + "types": { + "t_address": { + "label": "address", + "numberOfBytes": "20" + }, + "t_array(t_uint256)49_storage": { + "label": "uint256[49]", + "numberOfBytes": "1568" + }, + "t_array(t_uint256)50_storage": { + "label": "uint256[50]", + "numberOfBytes": "1600" + }, + "t_bool": { + "label": "bool", + "numberOfBytes": "1" + }, + "t_int256": { + "label": "int256", + "numberOfBytes": "32" + }, + "t_struct(RolloverFeeSigmoidParams)5764_storage": { + "label": "struct FeePolicy.RolloverFeeSigmoidParams", + "members": [ + { + "label": "lower", + "type": "t_int256", + "offset": 0, + "slot": "0" + }, + { + "label": "upper", + "type": "t_int256", + "offset": 0, + "slot": "1" + }, + { + "label": "growth", + "type": "t_int256", + "offset": 0, + "slot": "2" + } + ], + "numberOfBytes": "96" + }, + "t_uint256": { + "label": "uint256", + "numberOfBytes": "32" + }, + "t_uint8": { + "label": "uint8", + "numberOfBytes": "1" + } + }, + "namespaces": {} + } + }, + "a97f5817944b04eeb1798cf38ca8a64927b934bebfe28c9686697a995cefdc0b": { + "address": "0x9Bdba3bc5aB8EC0E895344705dC85fC29645748a", + "txHash": "0xe34dc2c05d3c9ea02964ce5ef541130a348a4e8d0c1ee53a2d1036b0a96955a4", + "layout": { + "solcVersion": "0.8.20", + "storage": [ + { + "label": "_initialized", + "offset": 0, + "slot": "0", + "type": "t_uint8", + "contract": "Initializable", + "src": "@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol:63", + "retypedFrom": "bool" + }, + { + "label": "_initializing", + "offset": 1, + "slot": "0", + "type": "t_bool", + "contract": "Initializable", + "src": "@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol:68" + }, + { + "label": "__gap", + "offset": 0, + "slot": "1", + "type": "t_array(t_uint256)50_storage", + "contract": "ContextUpgradeable", + "src": "@openzeppelin/contracts-upgradeable/utils/ContextUpgradeable.sol:40" + }, + { + "label": "_balances", + "offset": 0, + "slot": "51", + "type": "t_mapping(t_address,t_uint256)", + "contract": "ERC20Upgradeable", + "src": "@openzeppelin/contracts-upgradeable/token/ERC20/ERC20Upgradeable.sol:40" + }, + { + "label": "_allowances", + "offset": 0, + "slot": "52", + "type": "t_mapping(t_address,t_mapping(t_address,t_uint256))", + "contract": "ERC20Upgradeable", + "src": "@openzeppelin/contracts-upgradeable/token/ERC20/ERC20Upgradeable.sol:42" + }, + { + "label": "_totalSupply", + "offset": 0, + "slot": "53", + "type": "t_uint256", + "contract": "ERC20Upgradeable", + "src": "@openzeppelin/contracts-upgradeable/token/ERC20/ERC20Upgradeable.sol:44" + }, + { + "label": "_name", + "offset": 0, + "slot": "54", + "type": "t_string_storage", + "contract": "ERC20Upgradeable", + "src": "@openzeppelin/contracts-upgradeable/token/ERC20/ERC20Upgradeable.sol:46" + }, + { + "label": "_symbol", + "offset": 0, + "slot": "55", + "type": "t_string_storage", + "contract": "ERC20Upgradeable", + "src": "@openzeppelin/contracts-upgradeable/token/ERC20/ERC20Upgradeable.sol:47" + }, + { + "label": "__gap", + "offset": 0, + "slot": "56", + "type": "t_array(t_uint256)45_storage", + "contract": "ERC20Upgradeable", + "src": "@openzeppelin/contracts-upgradeable/token/ERC20/ERC20Upgradeable.sol:376" + }, + { + "label": "__gap", + "offset": 0, + "slot": "101", + "type": "t_array(t_uint256)50_storage", + "contract": "ERC20BurnableUpgradeable", + "src": "@openzeppelin/contracts-upgradeable/token/ERC20/extensions/ERC20BurnableUpgradeable.sol:51" + }, + { + "label": "_owner", + "offset": 0, + "slot": "151", + "type": "t_address", + "contract": "OwnableUpgradeable", + "src": "@openzeppelin/contracts-upgradeable/access/OwnableUpgradeable.sol:22" + }, + { + "label": "__gap", + "offset": 0, + "slot": "152", + "type": "t_array(t_uint256)49_storage", + "contract": "OwnableUpgradeable", + "src": "@openzeppelin/contracts-upgradeable/access/OwnableUpgradeable.sol:94" + }, + { + "label": "_paused", + "offset": 0, + "slot": "201", + "type": "t_bool", + "contract": "PausableUpgradeable", + "src": "@openzeppelin/contracts-upgradeable/security/PausableUpgradeable.sol:29" + }, + { + "label": "__gap", + "offset": 0, + "slot": "202", + "type": "t_array(t_uint256)49_storage", + "contract": "PausableUpgradeable", + "src": "@openzeppelin/contracts-upgradeable/security/PausableUpgradeable.sol:116" + }, + { + "label": "_status", + "offset": 0, + "slot": "251", + "type": "t_uint256", + "contract": "ReentrancyGuardUpgradeable", + "src": "@openzeppelin/contracts-upgradeable/security/ReentrancyGuardUpgradeable.sol:38" + }, + { + "label": "__gap", + "offset": 0, + "slot": "252", + "type": "t_array(t_uint256)49_storage", + "contract": "ReentrancyGuardUpgradeable", + "src": "@openzeppelin/contracts-upgradeable/security/ReentrancyGuardUpgradeable.sol:88" + }, + { + "label": "underlying", + "offset": 0, + "slot": "301", + "type": "t_contract(IERC20Upgradeable)1205", + "contract": "RolloverVault", + "src": "contracts/RolloverVault.sol:107" + }, + { + "label": "_deployed", + "offset": 0, + "slot": "302", + "type": "t_struct(AddressSet)4926_storage", + "contract": "RolloverVault", + "src": "contracts/RolloverVault.sol:111" + }, + { + "label": "minDeploymentAmt", + "offset": 0, + "slot": "304", + "type": "t_uint256", + "contract": "RolloverVault", + "src": "contracts/RolloverVault.sol:119" + }, + { + "label": "perp", + "offset": 0, + "slot": "305", + "type": "t_contract(IPerpetualTranche)11641", + "contract": "RolloverVault", + "src": "contracts/RolloverVault.sol:122" + }, + { + "label": "feePolicy", + "offset": 0, + "slot": "306", + "type": "t_contract(IFeePolicy)11421", + "contract": "RolloverVault", + "src": "contracts/RolloverVault.sol:128" + }, + { + "label": "keeper", + "offset": 0, + "slot": "307", + "type": "t_address", + "contract": "RolloverVault", + "src": "contracts/RolloverVault.sol:133" + }, + { + "label": "minUnderlyingBal", + "offset": 0, + "slot": "308", + "type": "t_uint256", + "contract": "RolloverVault", + "src": "contracts/RolloverVault.sol:139" + }, + { + "label": "minUnderlyingPerc", + "offset": 0, + "slot": "309", + "type": "t_uint256", + "contract": "RolloverVault", + "src": "contracts/RolloverVault.sol:144" + } + ], + "types": { + "t_address": { + "label": "address", + "numberOfBytes": "20" + }, + "t_array(t_bytes32)dyn_storage": { + "label": "bytes32[]", + "numberOfBytes": "32" + }, + "t_array(t_uint256)45_storage": { + "label": "uint256[45]", + "numberOfBytes": "1440" + }, + "t_array(t_uint256)49_storage": { + "label": "uint256[49]", + "numberOfBytes": "1568" + }, + "t_array(t_uint256)50_storage": { + "label": "uint256[50]", + "numberOfBytes": "1600" + }, + "t_bool": { + "label": "bool", + "numberOfBytes": "1" + }, + "t_bytes32": { + "label": "bytes32", + "numberOfBytes": "32" + }, + "t_contract(IERC20Upgradeable)1205": { + "label": "contract IERC20Upgradeable", + "numberOfBytes": "20" + }, + "t_contract(IFeePolicy)11421": { + "label": "contract IFeePolicy", + "numberOfBytes": "20" + }, + "t_contract(IPerpetualTranche)11641": { + "label": "contract IPerpetualTranche", + "numberOfBytes": "20" + }, + "t_mapping(t_address,t_mapping(t_address,t_uint256))": { + "label": "mapping(address => mapping(address => uint256))", + "numberOfBytes": "32" + }, + "t_mapping(t_address,t_uint256)": { + "label": "mapping(address => uint256)", + "numberOfBytes": "32" + }, + "t_mapping(t_bytes32,t_uint256)": { + "label": "mapping(bytes32 => uint256)", + "numberOfBytes": "32" + }, + "t_string_storage": { + "label": "string", + "numberOfBytes": "32" + }, + "t_struct(AddressSet)4926_storage": { + "label": "struct EnumerableSetUpgradeable.AddressSet", + "members": [ + { + "label": "_inner", + "type": "t_struct(Set)4611_storage", + "offset": 0, + "slot": "0" + } + ], + "numberOfBytes": "64" + }, + "t_struct(Set)4611_storage": { + "label": "struct EnumerableSetUpgradeable.Set", + "members": [ + { + "label": "_values", + "type": "t_array(t_bytes32)dyn_storage", + "offset": 0, + "slot": "0" + }, + { + "label": "_indexes", + "type": "t_mapping(t_bytes32,t_uint256)", + "offset": 0, + "slot": "1" + } + ], + "numberOfBytes": "64" + }, + "t_uint256": { + "label": "uint256", + "numberOfBytes": "32" + }, + "t_uint8": { + "label": "uint8", + "numberOfBytes": "1" + } + }, + "namespaces": {} + } + }, + "b01734c3d3e3b7cc922eaa57fbbeca88c6d3183b53a54881fd76bbb7bfafdc8d": { + "address": "0xf4FF6a7203F91Ae72D0273DF7596a5Df5a85999b", + "txHash": "0xc6e37759d3c309426e08964f3b170fd3f37998634fad5e40ce45c93ce11cb13b", + "layout": { + "solcVersion": "0.8.20", + "storage": [ + { + "label": "_initialized", + "offset": 0, + "slot": "0", + "type": "t_uint8", + "contract": "Initializable", + "src": "@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol:63", + "retypedFrom": "bool" + }, + { + "label": "_initializing", + "offset": 1, + "slot": "0", + "type": "t_bool", + "contract": "Initializable", + "src": "@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol:68" + }, + { + "label": "__gap", + "offset": 0, + "slot": "1", + "type": "t_array(t_uint256)50_storage", + "contract": "ContextUpgradeable", + "src": "@openzeppelin/contracts-upgradeable/utils/ContextUpgradeable.sol:40" + }, + { + "label": "_balances", + "offset": 0, + "slot": "51", + "type": "t_mapping(t_address,t_uint256)", + "contract": "ERC20Upgradeable", + "src": "@openzeppelin/contracts-upgradeable/token/ERC20/ERC20Upgradeable.sol:40" + }, + { + "label": "_allowances", + "offset": 0, + "slot": "52", + "type": "t_mapping(t_address,t_mapping(t_address,t_uint256))", + "contract": "ERC20Upgradeable", + "src": "@openzeppelin/contracts-upgradeable/token/ERC20/ERC20Upgradeable.sol:42" + }, + { + "label": "_totalSupply", + "offset": 0, + "slot": "53", + "type": "t_uint256", + "contract": "ERC20Upgradeable", + "src": "@openzeppelin/contracts-upgradeable/token/ERC20/ERC20Upgradeable.sol:44" + }, + { + "label": "_name", + "offset": 0, + "slot": "54", + "type": "t_string_storage", + "contract": "ERC20Upgradeable", + "src": "@openzeppelin/contracts-upgradeable/token/ERC20/ERC20Upgradeable.sol:46" + }, + { + "label": "_symbol", + "offset": 0, + "slot": "55", + "type": "t_string_storage", + "contract": "ERC20Upgradeable", + "src": "@openzeppelin/contracts-upgradeable/token/ERC20/ERC20Upgradeable.sol:47" + }, + { + "label": "__gap", + "offset": 0, + "slot": "56", + "type": "t_array(t_uint256)45_storage", + "contract": "ERC20Upgradeable", + "src": "@openzeppelin/contracts-upgradeable/token/ERC20/ERC20Upgradeable.sol:376" + }, + { + "label": "__gap", + "offset": 0, + "slot": "101", + "type": "t_array(t_uint256)50_storage", + "contract": "ERC20BurnableUpgradeable", + "src": "@openzeppelin/contracts-upgradeable/token/ERC20/extensions/ERC20BurnableUpgradeable.sol:51" + }, + { + "label": "_owner", + "offset": 0, + "slot": "151", + "type": "t_address", + "contract": "OwnableUpgradeable", + "src": "@openzeppelin/contracts-upgradeable/access/OwnableUpgradeable.sol:22" + }, + { + "label": "__gap", + "offset": 0, + "slot": "152", + "type": "t_array(t_uint256)49_storage", + "contract": "OwnableUpgradeable", + "src": "@openzeppelin/contracts-upgradeable/access/OwnableUpgradeable.sol:94" + }, + { + "label": "_paused", + "offset": 0, + "slot": "201", + "type": "t_bool", + "contract": "PausableUpgradeable", + "src": "@openzeppelin/contracts-upgradeable/security/PausableUpgradeable.sol:29" + }, + { + "label": "__gap", + "offset": 0, + "slot": "202", + "type": "t_array(t_uint256)49_storage", + "contract": "PausableUpgradeable", + "src": "@openzeppelin/contracts-upgradeable/security/PausableUpgradeable.sol:116" + }, + { + "label": "_status", + "offset": 0, + "slot": "251", + "type": "t_uint256", + "contract": "ReentrancyGuardUpgradeable", + "src": "@openzeppelin/contracts-upgradeable/security/ReentrancyGuardUpgradeable.sol:38" + }, + { + "label": "__gap", + "offset": 0, + "slot": "252", + "type": "t_array(t_uint256)49_storage", + "contract": "ReentrancyGuardUpgradeable", + "src": "@openzeppelin/contracts-upgradeable/security/ReentrancyGuardUpgradeable.sol:88" + }, + { + "label": "_decimals", + "offset": 0, + "slot": "301", + "type": "t_uint8", + "contract": "PerpetualTranche", + "src": "contracts/PerpetualTranche.sol:121" + }, + { + "label": "keeper", + "offset": 1, + "slot": "301", + "type": "t_address", + "contract": "PerpetualTranche", + "src": "contracts/PerpetualTranche.sol:127" + }, + { + "label": "feePolicy", + "offset": 0, + "slot": "302", + "type": "t_contract(IFeePolicy)11421", + "contract": "PerpetualTranche", + "src": "contracts/PerpetualTranche.sol:131" + }, + { + "label": "_pricingStrategy_DEPRECATED", + "offset": 0, + "slot": "303", + "type": "t_address", + "contract": "PerpetualTranche", + "src": "contracts/PerpetualTranche.sol:137" + }, + { + "label": "_discountStrategy_DEPRECATED", + "offset": 0, + "slot": "304", + "type": "t_address", + "contract": "PerpetualTranche", + "src": "contracts/PerpetualTranche.sol:145" + }, + { + "label": "bondIssuer", + "offset": 0, + "slot": "305", + "type": "t_contract(IBondIssuer)11341", + "contract": "PerpetualTranche", + "src": "contracts/PerpetualTranche.sol:149" + }, + { + "label": "_depositBond", + "offset": 0, + "slot": "306", + "type": "t_contract(IBondController)11968", + "contract": "PerpetualTranche", + "src": "contracts/PerpetualTranche.sol:152" + }, + { + "label": "minTrancheMaturitySec", + "offset": 0, + "slot": "307", + "type": "t_uint256", + "contract": "PerpetualTranche", + "src": "contracts/PerpetualTranche.sol:156" + }, + { + "label": "maxTrancheMaturitySec", + "offset": 0, + "slot": "308", + "type": "t_uint256", + "contract": "PerpetualTranche", + "src": "contracts/PerpetualTranche.sol:160" + }, + { + "label": "_matureValueTargetPerc_DEPRECATED", + "offset": 0, + "slot": "309", + "type": "t_uint256", + "contract": "PerpetualTranche", + "src": "contracts/PerpetualTranche.sol:168" + }, + { + "label": "maxSupply", + "offset": 0, + "slot": "310", + "type": "t_uint256", + "contract": "PerpetualTranche", + "src": "contracts/PerpetualTranche.sol:171" + }, + { + "label": "maxMintAmtPerTranche", + "offset": 0, + "slot": "311", + "type": "t_uint256", + "contract": "PerpetualTranche", + "src": "contracts/PerpetualTranche.sol:174" + }, + { + "label": "mintedSupplyPerTranche", + "offset": 0, + "slot": "312", + "type": "t_mapping(t_contract(ITranche)11995,t_uint256)", + "contract": "PerpetualTranche", + "src": "contracts/PerpetualTranche.sol:177" + }, + { + "label": "_appliedDiscounts_DEPRECATED", + "offset": 0, + "slot": "313", + "type": "t_mapping(t_contract(IERC20Upgradeable)1205,t_uint256)", + "contract": "PerpetualTranche", + "src": "contracts/PerpetualTranche.sol:184" + }, + { + "label": "_reserves", + "offset": 0, + "slot": "314", + "type": "t_struct(AddressSet)4926_storage", + "contract": "PerpetualTranche", + "src": "contracts/PerpetualTranche.sol:190" + }, + { + "label": "_matureTrancheBalance_DEPRECATED", + "offset": 0, + "slot": "316", + "type": "t_uint256", + "contract": "PerpetualTranche", + "src": "contracts/PerpetualTranche.sol:196" + }, + { + "label": "vault", + "offset": 0, + "slot": "317", + "type": "t_contract(IRolloverVault)11692", + "contract": "PerpetualTranche", + "src": "contracts/PerpetualTranche.sol:204" + } + ], + "types": { + "t_address": { + "label": "address", + "numberOfBytes": "20" + }, + "t_array(t_bytes32)dyn_storage": { + "label": "bytes32[]", + "numberOfBytes": "32" + }, + "t_array(t_uint256)45_storage": { + "label": "uint256[45]", + "numberOfBytes": "1440" + }, + "t_array(t_uint256)49_storage": { + "label": "uint256[49]", + "numberOfBytes": "1568" + }, + "t_array(t_uint256)50_storage": { + "label": "uint256[50]", + "numberOfBytes": "1600" + }, + "t_bool": { + "label": "bool", + "numberOfBytes": "1" + }, + "t_bytes32": { + "label": "bytes32", + "numberOfBytes": "32" + }, + "t_contract(IBondController)11968": { + "label": "contract IBondController", + "numberOfBytes": "20" + }, + "t_contract(IBondIssuer)11341": { + "label": "contract IBondIssuer", + "numberOfBytes": "20" + }, + "t_contract(IERC20Upgradeable)1205": { + "label": "contract IERC20Upgradeable", + "numberOfBytes": "20" + }, + "t_contract(IFeePolicy)11421": { + "label": "contract IFeePolicy", + "numberOfBytes": "20" + }, + "t_contract(IRolloverVault)11692": { + "label": "contract IRolloverVault", + "numberOfBytes": "20" + }, + "t_contract(ITranche)11995": { + "label": "contract ITranche", + "numberOfBytes": "20" + }, + "t_mapping(t_address,t_mapping(t_address,t_uint256))": { + "label": "mapping(address => mapping(address => uint256))", + "numberOfBytes": "32" + }, + "t_mapping(t_address,t_uint256)": { + "label": "mapping(address => uint256)", + "numberOfBytes": "32" + }, + "t_mapping(t_bytes32,t_uint256)": { + "label": "mapping(bytes32 => uint256)", + "numberOfBytes": "32" + }, + "t_mapping(t_contract(IERC20Upgradeable)1205,t_uint256)": { + "label": "mapping(contract IERC20Upgradeable => uint256)", + "numberOfBytes": "32" + }, + "t_mapping(t_contract(ITranche)11995,t_uint256)": { + "label": "mapping(contract ITranche => uint256)", + "numberOfBytes": "32" + }, + "t_string_storage": { + "label": "string", + "numberOfBytes": "32" + }, + "t_struct(AddressSet)4926_storage": { + "label": "struct EnumerableSetUpgradeable.AddressSet", + "members": [ + { + "label": "_inner", + "type": "t_struct(Set)4611_storage", + "offset": 0, + "slot": "0" + } + ], + "numberOfBytes": "64" + }, + "t_struct(Set)4611_storage": { + "label": "struct EnumerableSetUpgradeable.Set", + "members": [ + { + "label": "_values", + "type": "t_array(t_bytes32)dyn_storage", + "offset": 0, + "slot": "0" + }, + { + "label": "_indexes", + "type": "t_mapping(t_bytes32,t_uint256)", + "offset": 0, + "slot": "1" + } + ], + "numberOfBytes": "64" + }, + "t_uint256": { + "label": "uint256", + "numberOfBytes": "32" + }, + "t_uint8": { + "label": "uint8", + "numberOfBytes": "1" + } + }, + "namespaces": {} + } } } } diff --git a/spot-contracts/.openzeppelin/sepolia.json b/spot-contracts/.openzeppelin/sepolia.json new file mode 100644 index 00000000..9a8fda8a --- /dev/null +++ b/spot-contracts/.openzeppelin/sepolia.json @@ -0,0 +1,1091 @@ +{ + "manifestVersion": "3.2", + "admin": { + "address": "0x0584042677d469C0B95775368cF1EFfe9cc222F5", + "txHash": "0xb4f79db73bdb9e613779fb7f8525cb14819b21a170c1d46a84b9f02dbdca2762" + }, + "proxies": [ + { + "address": "0x2DdF288F26490D1147296cC0FA2B3c4da5E15f10", + "txHash": "0x8b03bdb50fa09ed2eebd1fe195ad9a245a4bd93df4ece963d5cf839e50fdaf63", + "kind": "transparent" + }, + { + "address": "0xdcCef9065876fD654bAddeBAa778FDA43E0bfC1F", + "txHash": "0x6c5ab049cab0bd3553b47fd8991aa62993dc46744304db9887e56bfc2045c085", + "kind": "transparent" + }, + { + "address": "0x107614c6602A8e602952Da107B8fE62b5Ab13b04", + "txHash": "0xda9cf80585a7da2935caa35a5dde76c35cf1e7a24bd0b4754e20a7a9eed275b1", + "kind": "transparent" + }, + { + "address": "0x3205ade922457db878039fDbF0e5A88872027A67", + "txHash": "0x19de9d6b4d49a8bb03deef871232212e6c696942feb6476c98ad7a8017e0975b", + "kind": "transparent" + } + ], + "impls": { + "65cc9a3236bde54808c6dc9a5b271f2cbe3cab237ad5553af67ad3bcab47bd67": { + "address": "0x5396479b65ed39360Ba6C16f6D7c9fd357674534", + "txHash": "0xd02f9e1ecf28f092422ba15470a794c17e5a04b4a4453d6376be399328cd445d", + "layout": { + "solcVersion": "0.8.20", + "storage": [ + { + "label": "_initialized", + "offset": 0, + "slot": "0", + "type": "t_uint8", + "contract": "Initializable", + "src": "@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol:63", + "retypedFrom": "bool" + }, + { + "label": "_initializing", + "offset": 1, + "slot": "0", + "type": "t_bool", + "contract": "Initializable", + "src": "@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol:68" + }, + { + "label": "__gap", + "offset": 0, + "slot": "1", + "type": "t_array(t_uint256)50_storage", + "contract": "ContextUpgradeable", + "src": "@openzeppelin/contracts-upgradeable/utils/ContextUpgradeable.sol:40" + }, + { + "label": "_owner", + "offset": 0, + "slot": "51", + "type": "t_address", + "contract": "OwnableUpgradeable", + "src": "@openzeppelin/contracts-upgradeable/access/OwnableUpgradeable.sol:22" + }, + { + "label": "__gap", + "offset": 0, + "slot": "52", + "type": "t_array(t_uint256)49_storage", + "contract": "OwnableUpgradeable", + "src": "@openzeppelin/contracts-upgradeable/access/OwnableUpgradeable.sol:94" + }, + { + "label": "targetSubscriptionRatio", + "offset": 0, + "slot": "101", + "type": "t_uint256", + "contract": "FeePolicy", + "src": "contracts/FeePolicy.sol:84" + }, + { + "label": "deviationRatioBoundLower", + "offset": 0, + "slot": "102", + "type": "t_uint256", + "contract": "FeePolicy", + "src": "contracts/FeePolicy.sol:87" + }, + { + "label": "deviationRatioBoundUpper", + "offset": 0, + "slot": "103", + "type": "t_uint256", + "contract": "FeePolicy", + "src": "contracts/FeePolicy.sol:90" + }, + { + "label": "perpMintFeePerc", + "offset": 0, + "slot": "104", + "type": "t_uint256", + "contract": "FeePolicy", + "src": "contracts/FeePolicy.sol:98" + }, + { + "label": "perpBurnFeePerc", + "offset": 0, + "slot": "105", + "type": "t_uint256", + "contract": "FeePolicy", + "src": "contracts/FeePolicy.sol:101" + }, + { + "label": "perpRolloverFee", + "offset": 0, + "slot": "106", + "type": "t_struct(RolloverFeeSigmoidParams)5764_storage", + "contract": "FeePolicy", + "src": "contracts/FeePolicy.sol:113" + }, + { + "label": "vaultMintFeePerc", + "offset": 0, + "slot": "109", + "type": "t_uint256", + "contract": "FeePolicy", + "src": "contracts/FeePolicy.sol:121" + }, + { + "label": "vaultBurnFeePerc", + "offset": 0, + "slot": "110", + "type": "t_uint256", + "contract": "FeePolicy", + "src": "contracts/FeePolicy.sol:124" + }, + { + "label": "vaultUnderlyingToPerpSwapFeePerc", + "offset": 0, + "slot": "111", + "type": "t_uint256", + "contract": "FeePolicy", + "src": "contracts/FeePolicy.sol:127" + }, + { + "label": "vaultPerpToUnderlyingSwapFeePerc", + "offset": 0, + "slot": "112", + "type": "t_uint256", + "contract": "FeePolicy", + "src": "contracts/FeePolicy.sol:130" + } + ], + "types": { + "t_address": { + "label": "address", + "numberOfBytes": "20" + }, + "t_array(t_uint256)49_storage": { + "label": "uint256[49]", + "numberOfBytes": "1568" + }, + "t_array(t_uint256)50_storage": { + "label": "uint256[50]", + "numberOfBytes": "1600" + }, + "t_bool": { + "label": "bool", + "numberOfBytes": "1" + }, + "t_int256": { + "label": "int256", + "numberOfBytes": "32" + }, + "t_struct(RolloverFeeSigmoidParams)5764_storage": { + "label": "struct FeePolicy.RolloverFeeSigmoidParams", + "members": [ + { + "label": "lower", + "type": "t_int256", + "offset": 0, + "slot": "0" + }, + { + "label": "upper", + "type": "t_int256", + "offset": 0, + "slot": "1" + }, + { + "label": "growth", + "type": "t_int256", + "offset": 0, + "slot": "2" + } + ], + "numberOfBytes": "96" + }, + "t_uint256": { + "label": "uint256", + "numberOfBytes": "32" + }, + "t_uint8": { + "label": "uint8", + "numberOfBytes": "1" + } + }, + "namespaces": {} + } + }, + "b94ae93ec4994748604bed6ebbd132484686fb511a62020d3ca280c20426e0a8": { + "address": "0x47081ee3dD9FE896e99D132dfB759dD252868c36", + "txHash": "0xb3c6a40448183102f5f22c4adabd72ac815ee8365d236b118f287a4bdc117c56", + "layout": { + "solcVersion": "0.8.20", + "storage": [ + { + "label": "_initialized", + "offset": 0, + "slot": "0", + "type": "t_uint8", + "contract": "Initializable", + "src": "@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol:63", + "retypedFrom": "bool" + }, + { + "label": "_initializing", + "offset": 1, + "slot": "0", + "type": "t_bool", + "contract": "Initializable", + "src": "@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol:68" + }, + { + "label": "__gap", + "offset": 0, + "slot": "1", + "type": "t_array(t_uint256)50_storage", + "contract": "ContextUpgradeable", + "src": "@openzeppelin/contracts-upgradeable/utils/ContextUpgradeable.sol:40" + }, + { + "label": "_balances", + "offset": 0, + "slot": "51", + "type": "t_mapping(t_address,t_uint256)", + "contract": "ERC20Upgradeable", + "src": "@openzeppelin/contracts-upgradeable/token/ERC20/ERC20Upgradeable.sol:40" + }, + { + "label": "_allowances", + "offset": 0, + "slot": "52", + "type": "t_mapping(t_address,t_mapping(t_address,t_uint256))", + "contract": "ERC20Upgradeable", + "src": "@openzeppelin/contracts-upgradeable/token/ERC20/ERC20Upgradeable.sol:42" + }, + { + "label": "_totalSupply", + "offset": 0, + "slot": "53", + "type": "t_uint256", + "contract": "ERC20Upgradeable", + "src": "@openzeppelin/contracts-upgradeable/token/ERC20/ERC20Upgradeable.sol:44" + }, + { + "label": "_name", + "offset": 0, + "slot": "54", + "type": "t_string_storage", + "contract": "ERC20Upgradeable", + "src": "@openzeppelin/contracts-upgradeable/token/ERC20/ERC20Upgradeable.sol:46" + }, + { + "label": "_symbol", + "offset": 0, + "slot": "55", + "type": "t_string_storage", + "contract": "ERC20Upgradeable", + "src": "@openzeppelin/contracts-upgradeable/token/ERC20/ERC20Upgradeable.sol:47" + }, + { + "label": "__gap", + "offset": 0, + "slot": "56", + "type": "t_array(t_uint256)45_storage", + "contract": "ERC20Upgradeable", + "src": "@openzeppelin/contracts-upgradeable/token/ERC20/ERC20Upgradeable.sol:376" + }, + { + "label": "__gap", + "offset": 0, + "slot": "101", + "type": "t_array(t_uint256)50_storage", + "contract": "ERC20BurnableUpgradeable", + "src": "@openzeppelin/contracts-upgradeable/token/ERC20/extensions/ERC20BurnableUpgradeable.sol:51" + }, + { + "label": "_owner", + "offset": 0, + "slot": "151", + "type": "t_address", + "contract": "OwnableUpgradeable", + "src": "@openzeppelin/contracts-upgradeable/access/OwnableUpgradeable.sol:22" + }, + { + "label": "__gap", + "offset": 0, + "slot": "152", + "type": "t_array(t_uint256)49_storage", + "contract": "OwnableUpgradeable", + "src": "@openzeppelin/contracts-upgradeable/access/OwnableUpgradeable.sol:94" + }, + { + "label": "_paused", + "offset": 0, + "slot": "201", + "type": "t_bool", + "contract": "PausableUpgradeable", + "src": "@openzeppelin/contracts-upgradeable/security/PausableUpgradeable.sol:29" + }, + { + "label": "__gap", + "offset": 0, + "slot": "202", + "type": "t_array(t_uint256)49_storage", + "contract": "PausableUpgradeable", + "src": "@openzeppelin/contracts-upgradeable/security/PausableUpgradeable.sol:116" + }, + { + "label": "_status", + "offset": 0, + "slot": "251", + "type": "t_uint256", + "contract": "ReentrancyGuardUpgradeable", + "src": "@openzeppelin/contracts-upgradeable/security/ReentrancyGuardUpgradeable.sol:38" + }, + { + "label": "__gap", + "offset": 0, + "slot": "252", + "type": "t_array(t_uint256)49_storage", + "contract": "ReentrancyGuardUpgradeable", + "src": "@openzeppelin/contracts-upgradeable/security/ReentrancyGuardUpgradeable.sol:88" + }, + { + "label": "_decimals", + "offset": 0, + "slot": "301", + "type": "t_uint8", + "contract": "PerpetualTranche", + "src": "contracts/PerpetualTranche.sol:121" + }, + { + "label": "keeper", + "offset": 1, + "slot": "301", + "type": "t_address", + "contract": "PerpetualTranche", + "src": "contracts/PerpetualTranche.sol:127" + }, + { + "label": "feePolicy", + "offset": 0, + "slot": "302", + "type": "t_contract(IFeePolicy)11421", + "contract": "PerpetualTranche", + "src": "contracts/PerpetualTranche.sol:131" + }, + { + "label": "_pricingStrategy_DEPRECATED", + "offset": 0, + "slot": "303", + "type": "t_address", + "contract": "PerpetualTranche", + "src": "contracts/PerpetualTranche.sol:137" + }, + { + "label": "_discountStrategy_DEPRECATED", + "offset": 0, + "slot": "304", + "type": "t_address", + "contract": "PerpetualTranche", + "src": "contracts/PerpetualTranche.sol:145" + }, + { + "label": "bondIssuer", + "offset": 0, + "slot": "305", + "type": "t_contract(IBondIssuer)11341", + "contract": "PerpetualTranche", + "src": "contracts/PerpetualTranche.sol:149" + }, + { + "label": "_depositBond", + "offset": 0, + "slot": "306", + "type": "t_contract(IBondController)11968", + "contract": "PerpetualTranche", + "src": "contracts/PerpetualTranche.sol:152" + }, + { + "label": "minTrancheMaturitySec", + "offset": 0, + "slot": "307", + "type": "t_uint256", + "contract": "PerpetualTranche", + "src": "contracts/PerpetualTranche.sol:156" + }, + { + "label": "maxTrancheMaturitySec", + "offset": 0, + "slot": "308", + "type": "t_uint256", + "contract": "PerpetualTranche", + "src": "contracts/PerpetualTranche.sol:160" + }, + { + "label": "_matureValueTargetPerc_DEPRECATED", + "offset": 0, + "slot": "309", + "type": "t_uint256", + "contract": "PerpetualTranche", + "src": "contracts/PerpetualTranche.sol:168" + }, + { + "label": "maxSupply", + "offset": 0, + "slot": "310", + "type": "t_uint256", + "contract": "PerpetualTranche", + "src": "contracts/PerpetualTranche.sol:171" + }, + { + "label": "maxMintAmtPerTranche", + "offset": 0, + "slot": "311", + "type": "t_uint256", + "contract": "PerpetualTranche", + "src": "contracts/PerpetualTranche.sol:174" + }, + { + "label": "mintedSupplyPerTranche", + "offset": 0, + "slot": "312", + "type": "t_mapping(t_contract(ITranche)11995,t_uint256)", + "contract": "PerpetualTranche", + "src": "contracts/PerpetualTranche.sol:177" + }, + { + "label": "_appliedDiscounts_DEPRECATED", + "offset": 0, + "slot": "313", + "type": "t_mapping(t_contract(IERC20Upgradeable)1205,t_uint256)", + "contract": "PerpetualTranche", + "src": "contracts/PerpetualTranche.sol:184" + }, + { + "label": "_reserves", + "offset": 0, + "slot": "314", + "type": "t_struct(AddressSet)4926_storage", + "contract": "PerpetualTranche", + "src": "contracts/PerpetualTranche.sol:190" + }, + { + "label": "_matureTrancheBalance_DEPRECATED", + "offset": 0, + "slot": "316", + "type": "t_uint256", + "contract": "PerpetualTranche", + "src": "contracts/PerpetualTranche.sol:196" + }, + { + "label": "vault", + "offset": 0, + "slot": "317", + "type": "t_contract(IRolloverVault)11692", + "contract": "PerpetualTranche", + "src": "contracts/PerpetualTranche.sol:204" + } + ], + "types": { + "t_address": { + "label": "address", + "numberOfBytes": "20" + }, + "t_array(t_bytes32)dyn_storage": { + "label": "bytes32[]", + "numberOfBytes": "32" + }, + "t_array(t_uint256)45_storage": { + "label": "uint256[45]", + "numberOfBytes": "1440" + }, + "t_array(t_uint256)49_storage": { + "label": "uint256[49]", + "numberOfBytes": "1568" + }, + "t_array(t_uint256)50_storage": { + "label": "uint256[50]", + "numberOfBytes": "1600" + }, + "t_bool": { + "label": "bool", + "numberOfBytes": "1" + }, + "t_bytes32": { + "label": "bytes32", + "numberOfBytes": "32" + }, + "t_contract(IBondController)11968": { + "label": "contract IBondController", + "numberOfBytes": "20" + }, + "t_contract(IBondIssuer)11341": { + "label": "contract IBondIssuer", + "numberOfBytes": "20" + }, + "t_contract(IERC20Upgradeable)1205": { + "label": "contract IERC20Upgradeable", + "numberOfBytes": "20" + }, + "t_contract(IFeePolicy)11421": { + "label": "contract IFeePolicy", + "numberOfBytes": "20" + }, + "t_contract(IRolloverVault)11692": { + "label": "contract IRolloverVault", + "numberOfBytes": "20" + }, + "t_contract(ITranche)11995": { + "label": "contract ITranche", + "numberOfBytes": "20" + }, + "t_mapping(t_address,t_mapping(t_address,t_uint256))": { + "label": "mapping(address => mapping(address => uint256))", + "numberOfBytes": "32" + }, + "t_mapping(t_address,t_uint256)": { + "label": "mapping(address => uint256)", + "numberOfBytes": "32" + }, + "t_mapping(t_bytes32,t_uint256)": { + "label": "mapping(bytes32 => uint256)", + "numberOfBytes": "32" + }, + "t_mapping(t_contract(IERC20Upgradeable)1205,t_uint256)": { + "label": "mapping(contract IERC20Upgradeable => uint256)", + "numberOfBytes": "32" + }, + "t_mapping(t_contract(ITranche)11995,t_uint256)": { + "label": "mapping(contract ITranche => uint256)", + "numberOfBytes": "32" + }, + "t_string_storage": { + "label": "string", + "numberOfBytes": "32" + }, + "t_struct(AddressSet)4926_storage": { + "label": "struct EnumerableSetUpgradeable.AddressSet", + "members": [ + { + "label": "_inner", + "type": "t_struct(Set)4611_storage", + "offset": 0, + "slot": "0" + } + ], + "numberOfBytes": "64" + }, + "t_struct(Set)4611_storage": { + "label": "struct EnumerableSetUpgradeable.Set", + "members": [ + { + "label": "_values", + "type": "t_array(t_bytes32)dyn_storage", + "offset": 0, + "slot": "0" + }, + { + "label": "_indexes", + "type": "t_mapping(t_bytes32,t_uint256)", + "offset": 0, + "slot": "1" + } + ], + "numberOfBytes": "64" + }, + "t_uint256": { + "label": "uint256", + "numberOfBytes": "32" + }, + "t_uint8": { + "label": "uint8", + "numberOfBytes": "1" + } + }, + "namespaces": {} + } + }, + "31d602c9c7102be63744ee4f2dde2f008800ce0023c0791e98defe6ae2346024": { + "address": "0x16088740AeBfAbC96e41e8144Dbfffe41A40288a", + "txHash": "0x92c01b19a3167f090d4def0625f03346540af3fa28fb00ae2bdc888ab8508959", + "layout": { + "solcVersion": "0.8.20", + "storage": [ + { + "label": "_initialized", + "offset": 0, + "slot": "0", + "type": "t_uint8", + "contract": "Initializable", + "src": "@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol:63", + "retypedFrom": "bool" + }, + { + "label": "_initializing", + "offset": 1, + "slot": "0", + "type": "t_bool", + "contract": "Initializable", + "src": "@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol:68" + }, + { + "label": "__gap", + "offset": 0, + "slot": "1", + "type": "t_array(t_uint256)50_storage", + "contract": "ContextUpgradeable", + "src": "@openzeppelin/contracts-upgradeable/utils/ContextUpgradeable.sol:40" + }, + { + "label": "_balances", + "offset": 0, + "slot": "51", + "type": "t_mapping(t_address,t_uint256)", + "contract": "ERC20Upgradeable", + "src": "@openzeppelin/contracts-upgradeable/token/ERC20/ERC20Upgradeable.sol:40" + }, + { + "label": "_allowances", + "offset": 0, + "slot": "52", + "type": "t_mapping(t_address,t_mapping(t_address,t_uint256))", + "contract": "ERC20Upgradeable", + "src": "@openzeppelin/contracts-upgradeable/token/ERC20/ERC20Upgradeable.sol:42" + }, + { + "label": "_totalSupply", + "offset": 0, + "slot": "53", + "type": "t_uint256", + "contract": "ERC20Upgradeable", + "src": "@openzeppelin/contracts-upgradeable/token/ERC20/ERC20Upgradeable.sol:44" + }, + { + "label": "_name", + "offset": 0, + "slot": "54", + "type": "t_string_storage", + "contract": "ERC20Upgradeable", + "src": "@openzeppelin/contracts-upgradeable/token/ERC20/ERC20Upgradeable.sol:46" + }, + { + "label": "_symbol", + "offset": 0, + "slot": "55", + "type": "t_string_storage", + "contract": "ERC20Upgradeable", + "src": "@openzeppelin/contracts-upgradeable/token/ERC20/ERC20Upgradeable.sol:47" + }, + { + "label": "__gap", + "offset": 0, + "slot": "56", + "type": "t_array(t_uint256)45_storage", + "contract": "ERC20Upgradeable", + "src": "@openzeppelin/contracts-upgradeable/token/ERC20/ERC20Upgradeable.sol:376" + }, + { + "label": "__gap", + "offset": 0, + "slot": "101", + "type": "t_array(t_uint256)50_storage", + "contract": "ERC20BurnableUpgradeable", + "src": "@openzeppelin/contracts-upgradeable/token/ERC20/extensions/ERC20BurnableUpgradeable.sol:51" + }, + { + "label": "_owner", + "offset": 0, + "slot": "151", + "type": "t_address", + "contract": "OwnableUpgradeable", + "src": "@openzeppelin/contracts-upgradeable/access/OwnableUpgradeable.sol:22" + }, + { + "label": "__gap", + "offset": 0, + "slot": "152", + "type": "t_array(t_uint256)49_storage", + "contract": "OwnableUpgradeable", + "src": "@openzeppelin/contracts-upgradeable/access/OwnableUpgradeable.sol:94" + }, + { + "label": "_paused", + "offset": 0, + "slot": "201", + "type": "t_bool", + "contract": "PausableUpgradeable", + "src": "@openzeppelin/contracts-upgradeable/security/PausableUpgradeable.sol:29" + }, + { + "label": "__gap", + "offset": 0, + "slot": "202", + "type": "t_array(t_uint256)49_storage", + "contract": "PausableUpgradeable", + "src": "@openzeppelin/contracts-upgradeable/security/PausableUpgradeable.sol:116" + }, + { + "label": "_status", + "offset": 0, + "slot": "251", + "type": "t_uint256", + "contract": "ReentrancyGuardUpgradeable", + "src": "@openzeppelin/contracts-upgradeable/security/ReentrancyGuardUpgradeable.sol:38" + }, + { + "label": "__gap", + "offset": 0, + "slot": "252", + "type": "t_array(t_uint256)49_storage", + "contract": "ReentrancyGuardUpgradeable", + "src": "@openzeppelin/contracts-upgradeable/security/ReentrancyGuardUpgradeable.sol:88" + }, + { + "label": "underlying", + "offset": 0, + "slot": "301", + "type": "t_contract(IERC20Upgradeable)1205", + "contract": "RolloverVault", + "src": "contracts/RolloverVault.sol:107" + }, + { + "label": "_deployed", + "offset": 0, + "slot": "302", + "type": "t_struct(AddressSet)4926_storage", + "contract": "RolloverVault", + "src": "contracts/RolloverVault.sol:111" + }, + { + "label": "minDeploymentAmt", + "offset": 0, + "slot": "304", + "type": "t_uint256", + "contract": "RolloverVault", + "src": "contracts/RolloverVault.sol:119" + }, + { + "label": "perp", + "offset": 0, + "slot": "305", + "type": "t_contract(IPerpetualTranche)11641", + "contract": "RolloverVault", + "src": "contracts/RolloverVault.sol:122" + }, + { + "label": "feePolicy", + "offset": 0, + "slot": "306", + "type": "t_contract(IFeePolicy)11421", + "contract": "RolloverVault", + "src": "contracts/RolloverVault.sol:128" + }, + { + "label": "keeper", + "offset": 0, + "slot": "307", + "type": "t_address", + "contract": "RolloverVault", + "src": "contracts/RolloverVault.sol:133" + }, + { + "label": "minUnderlyingBal", + "offset": 0, + "slot": "308", + "type": "t_uint256", + "contract": "RolloverVault", + "src": "contracts/RolloverVault.sol:139" + }, + { + "label": "minUnderlyingPerc", + "offset": 0, + "slot": "309", + "type": "t_uint256", + "contract": "RolloverVault", + "src": "contracts/RolloverVault.sol:144" + } + ], + "types": { + "t_address": { + "label": "address", + "numberOfBytes": "20" + }, + "t_array(t_bytes32)dyn_storage": { + "label": "bytes32[]", + "numberOfBytes": "32" + }, + "t_array(t_uint256)45_storage": { + "label": "uint256[45]", + "numberOfBytes": "1440" + }, + "t_array(t_uint256)49_storage": { + "label": "uint256[49]", + "numberOfBytes": "1568" + }, + "t_array(t_uint256)50_storage": { + "label": "uint256[50]", + "numberOfBytes": "1600" + }, + "t_bool": { + "label": "bool", + "numberOfBytes": "1" + }, + "t_bytes32": { + "label": "bytes32", + "numberOfBytes": "32" + }, + "t_contract(IERC20Upgradeable)1205": { + "label": "contract IERC20Upgradeable", + "numberOfBytes": "20" + }, + "t_contract(IFeePolicy)11421": { + "label": "contract IFeePolicy", + "numberOfBytes": "20" + }, + "t_contract(IPerpetualTranche)11641": { + "label": "contract IPerpetualTranche", + "numberOfBytes": "20" + }, + "t_mapping(t_address,t_mapping(t_address,t_uint256))": { + "label": "mapping(address => mapping(address => uint256))", + "numberOfBytes": "32" + }, + "t_mapping(t_address,t_uint256)": { + "label": "mapping(address => uint256)", + "numberOfBytes": "32" + }, + "t_mapping(t_bytes32,t_uint256)": { + "label": "mapping(bytes32 => uint256)", + "numberOfBytes": "32" + }, + "t_string_storage": { + "label": "string", + "numberOfBytes": "32" + }, + "t_struct(AddressSet)4926_storage": { + "label": "struct EnumerableSetUpgradeable.AddressSet", + "members": [ + { + "label": "_inner", + "type": "t_struct(Set)4611_storage", + "offset": 0, + "slot": "0" + } + ], + "numberOfBytes": "64" + }, + "t_struct(Set)4611_storage": { + "label": "struct EnumerableSetUpgradeable.Set", + "members": [ + { + "label": "_values", + "type": "t_array(t_bytes32)dyn_storage", + "offset": 0, + "slot": "0" + }, + { + "label": "_indexes", + "type": "t_mapping(t_bytes32,t_uint256)", + "offset": 0, + "slot": "1" + } + ], + "numberOfBytes": "64" + }, + "t_uint256": { + "label": "uint256", + "numberOfBytes": "32" + }, + "t_uint8": { + "label": "uint8", + "numberOfBytes": "1" + } + }, + "namespaces": {} + } + }, + "2546bfa1b5a5142a0d95cea7e7372f3a735712bf05bbaab3a7df09b1022fb6ee": { + "address": "0x1c611ab4D40781781d564e3efb8ACEBC23884942", + "txHash": "0x6ad9a6f0b7b0a7c2189815c55bcfd2e7781b0ec1c95e5226be19930ed0d7865b", + "layout": { + "solcVersion": "0.8.20", + "storage": [ + { + "label": "_initialized", + "offset": 0, + "slot": "0", + "type": "t_uint8", + "contract": "Initializable", + "src": "@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol:63", + "retypedFrom": "bool" + }, + { + "label": "_initializing", + "offset": 1, + "slot": "0", + "type": "t_bool", + "contract": "Initializable", + "src": "@openzeppelin/contracts-upgradeable/proxy/utils/Initializable.sol:68" + }, + { + "label": "__gap", + "offset": 0, + "slot": "1", + "type": "t_array(t_uint256)50_storage", + "contract": "ContextUpgradeable", + "src": "@openzeppelin/contracts-upgradeable/utils/ContextUpgradeable.sol:40" + }, + { + "label": "_owner", + "offset": 0, + "slot": "51", + "type": "t_address", + "contract": "OwnableUpgradeable", + "src": "@openzeppelin/contracts-upgradeable/access/OwnableUpgradeable.sol:22" + }, + { + "label": "__gap", + "offset": 0, + "slot": "52", + "type": "t_array(t_uint256)49_storage", + "contract": "OwnableUpgradeable", + "src": "@openzeppelin/contracts-upgradeable/access/OwnableUpgradeable.sol:94" + }, + { + "label": "targetSubscriptionRatio", + "offset": 0, + "slot": "101", + "type": "t_uint256", + "contract": "FeePolicy", + "src": "contracts/FeePolicy.sol:84" + }, + { + "label": "deviationRatioBoundLower", + "offset": 0, + "slot": "102", + "type": "t_uint256", + "contract": "FeePolicy", + "src": "contracts/FeePolicy.sol:87" + }, + { + "label": "deviationRatioBoundUpper", + "offset": 0, + "slot": "103", + "type": "t_uint256", + "contract": "FeePolicy", + "src": "contracts/FeePolicy.sol:90" + }, + { + "label": "perpMintFeePerc", + "offset": 0, + "slot": "104", + "type": "t_uint256", + "contract": "FeePolicy", + "src": "contracts/FeePolicy.sol:98" + }, + { + "label": "perpBurnFeePerc", + "offset": 0, + "slot": "105", + "type": "t_uint256", + "contract": "FeePolicy", + "src": "contracts/FeePolicy.sol:101" + }, + { + "label": "perpRolloverFee", + "offset": 0, + "slot": "106", + "type": "t_struct(RolloverFeeSigmoidParams)5764_storage", + "contract": "FeePolicy", + "src": "contracts/FeePolicy.sol:113" + }, + { + "label": "vaultMintFeePerc", + "offset": 0, + "slot": "109", + "type": "t_uint256", + "contract": "FeePolicy", + "src": "contracts/FeePolicy.sol:121" + }, + { + "label": "vaultBurnFeePerc", + "offset": 0, + "slot": "110", + "type": "t_uint256", + "contract": "FeePolicy", + "src": "contracts/FeePolicy.sol:124" + }, + { + "label": "vaultUnderlyingToPerpSwapFeePerc", + "offset": 0, + "slot": "111", + "type": "t_uint256", + "contract": "FeePolicy", + "src": "contracts/FeePolicy.sol:127" + }, + { + "label": "vaultPerpToUnderlyingSwapFeePerc", + "offset": 0, + "slot": "112", + "type": "t_uint256", + "contract": "FeePolicy", + "src": "contracts/FeePolicy.sol:130" + } + ], + "types": { + "t_address": { + "label": "address", + "numberOfBytes": "20" + }, + "t_array(t_uint256)49_storage": { + "label": "uint256[49]", + "numberOfBytes": "1568" + }, + "t_array(t_uint256)50_storage": { + "label": "uint256[50]", + "numberOfBytes": "1600" + }, + "t_bool": { + "label": "bool", + "numberOfBytes": "1" + }, + "t_int256": { + "label": "int256", + "numberOfBytes": "32" + }, + "t_struct(RolloverFeeSigmoidParams)5764_storage": { + "label": "struct FeePolicy.RolloverFeeSigmoidParams", + "members": [ + { + "label": "lower", + "type": "t_int256", + "offset": 0, + "slot": "0" + }, + { + "label": "upper", + "type": "t_int256", + "offset": 0, + "slot": "1" + }, + { + "label": "growth", + "type": "t_int256", + "offset": 0, + "slot": "2" + } + ], + "numberOfBytes": "96" + }, + "t_uint256": { + "label": "uint256", + "numberOfBytes": "32" + }, + "t_uint8": { + "label": "uint8", + "numberOfBytes": "1" + } + }, + "namespaces": {} + } + } + } +} diff --git a/spot-contracts/.solcover.js b/spot-contracts/.solcover.js index d2439471..081573c5 100644 --- a/spot-contracts/.solcover.js +++ b/spot-contracts/.solcover.js @@ -1,3 +1,3 @@ module.exports = { - skipFiles: ["test", "_interfaces", "_external"], + skipFiles: ["_test", "_interfaces", "_external"], }; diff --git a/spot-contracts/README.md b/spot-contracts/README.md index cbc14c8c..dc7845f7 100644 --- a/spot-contracts/README.md +++ b/spot-contracts/README.md @@ -6,8 +6,9 @@ The official mainnet addresses are: - SPOT ERC-20 Token: [0xC1f33e0cf7e40a67375007104B929E49a581bafE](https://etherscan.io/address/0xC1f33e0cf7e40a67375007104B929E49a581bafE) - Bond issuer: [0x5613Fc36A431c9c2746763B80C1DD89e03593871](https://etherscan.io/address/0x5613Fc36A431c9c2746763B80C1DD89e03593871) -- Router: [0x38f600e08540178719BF656e6B43FC15A529c393](https://etherscan.io/address/0x38f600e08540178719BF656e6B43FC15A529c393) +- Router: [0xCe2878d1f2901EFaF48cd456E586B470C145d1BC](https://etherscan.io/address/0xCe2878d1f2901EFaF48cd456E586B470C145d1BC) - RolloverVault: [0x82A91a0D599A45d8E9Af781D67f695d7C72869Bd](https://etherscan.io//address/0x82A91a0D599A45d8E9Af781D67f695d7C72869Bd) +- FeePolicy: [0xE22977381506bF094CB3ed50CB8834E358F7ef6c](https://etherscan.io//address/0xE22977381506bF094CB3ed50CB8834E358F7ef6c) ## Install @@ -25,12 +26,13 @@ yarn test ### Testnets -There is a testnet deployment on Goerli. +There is a testnet deployment on Sepolia. -- SPOT ERC-20 Token: [0x95014Bc18F82a98CFAA3253fbD3184125A01f848](https://goerli.etherscan.io//address/0x95014Bc18F82a98CFAA3253fbD3184125A01f848) -- Bond issuer: [0xbC060a1EbEC5eC869C4D51d4563244d4a223D307](https://goerli.etherscan.io//address/0xbC060a1EbEC5eC869C4D51d4563244d4a223D307) -- Router: [0x5e902bdCC408550b4BD612678bE2d57677664Dc9](https://goerli.etherscan.io//address/0x5e902bdCC408550b4BD612678bE2d57677664Dc9) -- RolloverVault: [0xca36B64BEbdf141623911987b93767dcA4bF6F1f](https://goerli.etherscan.io//address/0xca36B64BEbdf141623911987b93767dcA4bF6F1f) +- SPOT ERC-20 Token: [0xdcCef9065876fD654bAddeBAa778FDA43E0bfC1F](https://sepolia.etherscan.io//address/0xdcCef9065876fD654bAddeBAa778FDA43E0bfC1F) +- Bond issuer: [0x3838C8d4D092d40Cb27DD22Dafc6E1A81ea2DB60](https://sepolia.etherscan.io//address/0x3838C8d4D092d40Cb27DD22Dafc6E1A81ea2DB60) +- Router: [0xE5b53ee8182086790C1ab79cbf801F0c5EE241BF](https://sepolia.etherscan.io//address/0xE5b53ee8182086790C1ab79cbf801F0c5EE241BF) +- RolloverVault: [0x107614c6602A8e602952Da107B8fE62b5Ab13b04](https://sepolia.etherscan.io//address/0x107614c6602A8e602952Da107B8fE62b5Ab13b04) +- FeePolicy: [0x2DdF288F26490D1147296cC0FA2B3c4da5E15f10](https://sepolia.etherscan.io//address/0x2DdF288F26490D1147296cC0FA2B3c4da5E15f10) ## Contribute diff --git a/spot-contracts/contracts/BondIssuer.sol b/spot-contracts/contracts/BondIssuer.sol index 37075edf..7b983471 100644 --- a/spot-contracts/contracts/BondIssuer.sol +++ b/spot-contracts/contracts/BondIssuer.sol @@ -1,14 +1,20 @@ // SPDX-License-Identifier: BUSL-1.1 -pragma solidity ^0.8.19; +pragma solidity ^0.8.20; import { IBondFactory } from "./_interfaces/buttonwood/IBondFactory.sol"; import { IBondController } from "./_interfaces/buttonwood/IBondController.sol"; -import { IBondIssuer, NoMaturedBonds } from "./_interfaces/IBondIssuer.sol"; +import { IBondIssuer } from "./_interfaces/IBondIssuer.sol"; import { OwnableUpgradeable } from "@openzeppelin/contracts-upgradeable/access/OwnableUpgradeable.sol"; import { EnumerableSetUpgradeable } from "@openzeppelin/contracts-upgradeable/utils/structs/EnumerableSetUpgradeable.sol"; import { BondHelpers } from "./_utils/BondHelpers.sol"; +/// @notice Expected tranche ratios to sum up to {TRANCHE_RATIO_GRANULARITY}. +error UnacceptableTrancheRatios(); + +/// @notice Expected at least one matured bond. +error NoMaturedBonds(); + /** * @title BondIssuer * @@ -26,10 +32,10 @@ contract BondIssuer is IBondIssuer, OwnableUpgradeable { uint256 private constant TRANCHE_RATIO_GRANULARITY = 1000; /// @notice Address of the bond factory. - IBondFactory public immutable bondFactory; + IBondFactory public bondFactory; /// @notice The underlying rebasing token used for tranching. - address public immutable collateral; + address public collateral; /// @notice The maximum maturity duration for the issued bonds. /// @dev In practice, bonds issued by this issuer won't have a constant duration as @@ -63,12 +69,9 @@ contract BondIssuer is IBondIssuer, OwnableUpgradeable { /// @notice The timestamp when the issue window opened during the last issue. uint256 public lastIssueWindowTimestamp; - /// @notice Contract constructor - /// @param bondFactory_ The bond factory reference. - /// @param collateral_ The address of the collateral ERC-20. - constructor(IBondFactory bondFactory_, address collateral_) { - bondFactory = bondFactory_; - collateral = collateral_; + /// @custom:oz-upgrades-unsafe-allow constructor + constructor() { + _disableInitializers(); } /// @notice Contract initializer. @@ -77,12 +80,16 @@ contract BondIssuer is IBondIssuer, OwnableUpgradeable { /// @param minIssueTimeIntervalSec_ The minimum time between successive issues. /// @param issueWindowOffsetSec_ The issue window offset. function init( + IBondFactory bondFactory_, + address collateral_, uint256 maxMaturityDuration_, uint256[] memory trancheRatios_, uint256 minIssueTimeIntervalSec_, uint256 issueWindowOffsetSec_ - ) public initializer { + ) external initializer { __Ownable_init(); + bondFactory = bondFactory_; + collateral = collateral_; updateMaxMaturityDuration(maxMaturityDuration_); updateTrancheRatios(trancheRatios_); updateIssuanceTimingConfig(minIssueTimeIntervalSec_, issueWindowOffsetSec_); @@ -99,19 +106,22 @@ contract BondIssuer is IBondIssuer, OwnableUpgradeable { function updateTrancheRatios(uint256[] memory trancheRatios_) public onlyOwner { trancheRatios = trancheRatios_; uint256 ratioSum; - for (uint8 i = 0; i < trancheRatios_.length; i++) { + uint8 numTranches = uint8(trancheRatios_.length); + for (uint8 i = 0; i < numTranches; ++i) { ratioSum += trancheRatios_[i]; } - require(ratioSum == TRANCHE_RATIO_GRANULARITY, "BondIssuer: Invalid tranche ratios"); + if (ratioSum != TRANCHE_RATIO_GRANULARITY) { + revert UnacceptableTrancheRatios(); + } } /// @notice Updates the bond frequency and offset. /// @param minIssueTimeIntervalSec_ The new issuance interval. /// @param issueWindowOffsetSec_ The new issue window offset. - function updateIssuanceTimingConfig(uint256 minIssueTimeIntervalSec_, uint256 issueWindowOffsetSec_) - public - onlyOwner - { + function updateIssuanceTimingConfig( + uint256 minIssueTimeIntervalSec_, + uint256 issueWindowOffsetSec_ + ) public onlyOwner { minIssueTimeIntervalSec = minIssueTimeIntervalSec_; issueWindowOffsetSec = issueWindowOffsetSec_; } diff --git a/spot-contracts/contracts/FeePolicy.sol b/spot-contracts/contracts/FeePolicy.sol new file mode 100644 index 00000000..7376e105 --- /dev/null +++ b/spot-contracts/contracts/FeePolicy.sol @@ -0,0 +1,331 @@ +// SPDX-License-Identifier: BUSL-1.1 +pragma solidity ^0.8.20; + +import { IFeePolicy } from "./_interfaces/IFeePolicy.sol"; +import { SubscriptionParams } from "./_interfaces/CommonTypes.sol"; +import { InvalidPerc, InvalidTargetSRBounds, InvalidDRBounds, InvalidSigmoidAsymptotes } from "./_interfaces/ProtocolErrors.sol"; + +import { MathUpgradeable } from "@openzeppelin/contracts-upgradeable/utils/math/MathUpgradeable.sol"; +import { SafeCastUpgradeable } from "@openzeppelin/contracts-upgradeable/utils/math/SafeCastUpgradeable.sol"; +import { OwnableUpgradeable } from "@openzeppelin/contracts-upgradeable/access/OwnableUpgradeable.sol"; +import { Sigmoid } from "./_utils/Sigmoid.sol"; + +/** + * @title FeePolicy + * + * @notice This contract determines fees for interacting with the perp and vault systems. + * + * The fee policy attempts to balance the demand for holding perp tokens with + * the demand for holding vault tokens; such that the total collateral in the vault + * supports rolling over all mature collateral backing perps. + * + * Fees are computed based on the deviation between the system's current subscription ratio + * and the target subscription ratio. + * - `subscriptionRatio` = (vaultTVL * seniorTR) / (perpTVL * 1-seniorTR) + * - `deviationRatio` (dr) = subscriptionRatio / targetSubscriptionRatio + * + * When the system is "under-subscribed" (dr <= 1): + * - Rollover fees flow from perp holders to vault note holders. + * - Fees are charged for minting new perps. + * - No fees are charged for redeeming perps. + * + * When the system is "over-subscribed" (dr > 1): + * - Rollover fees flow from vault note holders to perp holders. + * - No fees are charged for minting new perps. + * - Fees are charged for redeeming perps. + * + * Regardless of the `deviationRatio`, the system charges a fixed percentage fee + * for minting and redeeming vault notes. + * + * + * The rollover fees are signed and can flow in either direction based on the `deviationRatio`. + * The fee is a percentage is computed through a sigmoid function. + * The slope and asymptotes are set by the owner. + * + * CRITICAL: The rollover fee percentage is NOT annualized, the fee percentage is applied per rollover. + * The number of rollovers per year changes based on the duration of perp's minting bond. + * + * We consider a `deviationRatio` of greater than 1.0 healthy (or "over-subscribed"). + * In general, the system favors an elastic perp supply and an inelastic vault note supply. + * + * + */ +contract FeePolicy is IFeePolicy, OwnableUpgradeable { + // Libraries + using MathUpgradeable for uint256; + using SafeCastUpgradeable for uint256; + + // Replicating value used here: + // https://github.com/buttonwood-protocol/tranche/blob/main/contracts/BondController.sol + uint256 private constant TRANCHE_RATIO_GRANULARITY = 1000; + + /// @notice The returned fee percentages are fixed point numbers with {DECIMALS} places. + /// @dev The decimals should line up with value expected by consumer (perp, vault). + /// NOTE: 10**DECIMALS => 100% or 1.0 + uint8 public constant DECIMALS = 8; + + /// @notice Fixed point representation of 1.0 or 100%. + uint256 public constant ONE = (1 * 10 ** DECIMALS); + + /// @notice Sigmoid asymptote bound. + /// @dev Set to 0.05 or 5%, i.e) the rollover fee can be at most 5% on either direction. + uint256 public constant SIGMOID_BOUND = ONE / 20; + + /// @notice Target subscription ratio lower bound, 0.75 or 75%. + uint256 public constant TARGET_SR_LOWER_BOUND = (ONE * 75) / 100; + + /// @notice Target subscription ratio higher bound, 2.0 or 200%. + uint256 public constant TARGET_SR_UPPER_BOUND = 2 * ONE; + + //----------------------------------------------------------------------------- + /// @notice The target subscription ratio i.e) the normalization factor. + /// @dev The ratio under which the system is considered "under-subscribed". + /// Adds a safety buffer to ensure that rollovers are better sustained. + uint256 public targetSubscriptionRatio; + + /// @notice The lower bound of deviation ratio, below which some operations (which decrease the dr) are disabled. + uint256 public deviationRatioBoundLower; + + /// @notice The upper bound of deviation ratio, above which some operations (which increase the dr) are disabled. + uint256 public deviationRatioBoundUpper; + + //----------------------------------------------------------------------------- + + //----------------------------------------------------------------------------- + // Perp fee parameters + + /// @notice The percentage fee charged on minting perp tokens. + uint256 public perpMintFeePerc; + + /// @notice The percentage fee charged on burning perp tokens. + uint256 public perpBurnFeePerc; + + struct RolloverFeeSigmoidParams { + /// @notice Lower asymptote + int256 lower; + /// @notice Upper asymptote + int256 upper; + /// @notice sigmoid slope + int256 growth; + } + + /// @notice Parameters which control the asymptotes and the slope of the perp token's rollover fee. + RolloverFeeSigmoidParams public perpRolloverFee; + + //----------------------------------------------------------------------------- + + //----------------------------------------------------------------------------- + // Vault fee parameters + + /// @notice The percentage fee charged on minting vault notes. + uint256 public vaultMintFeePerc; + + /// @notice The percentage fee charged on burning vault notes. + uint256 public vaultBurnFeePerc; + + /// @notice The percentage fee charged by the vault to swap underlying tokens for perp tokens. + uint256 public vaultUnderlyingToPerpSwapFeePerc; + + /// @notice The percentage fee charged by the vault to swap perp tokens for underlying tokens. + uint256 public vaultPerpToUnderlyingSwapFeePerc; + + //----------------------------------------------------------------------------- + + /// @custom:oz-upgrades-unsafe-allow constructor + constructor() { + _disableInitializers(); + } + + /// @notice Contract initializer. + function init() external initializer { + __Ownable_init(); + + // initializing mint/burn fees to zero + perpMintFeePerc = 0; + perpBurnFeePerc = 0; + vaultMintFeePerc = 0; + vaultBurnFeePerc = 0; + + // initializing swap fees to 100%, to disable swapping initially + vaultUnderlyingToPerpSwapFeePerc = ONE; + vaultPerpToUnderlyingSwapFeePerc = ONE; + + // NOTE: With the current bond length of 28 days, rollover rate is annualized by dividing by: 365/28 ~= 13 + perpRolloverFee.lower = -int256(ONE) / (30 * 13); // -0.033/13 = -0.00253 (3.3% annualized) + perpRolloverFee.upper = int256(ONE) / (10 * 13); // 0.1/13 = 0.00769 (10% annualized) + perpRolloverFee.growth = 5 * int256(ONE); // 5.0 + + targetSubscriptionRatio = (ONE * 133) / 100; // 1.33 + deviationRatioBoundLower = (ONE * 75) / 100; // 0.75 + deviationRatioBoundUpper = 2 * ONE; // 2.0 + } + + //----------------------------------------------------------------------------- + // Owner only + + /// @notice Updates the target subscription ratio. + /// @param targetSubscriptionRatio_ The new target subscription ratio as a fixed point number with {DECIMALS} places. + function updateTargetSubscriptionRatio(uint256 targetSubscriptionRatio_) external onlyOwner { + if (targetSubscriptionRatio_ < TARGET_SR_LOWER_BOUND || targetSubscriptionRatio_ > TARGET_SR_UPPER_BOUND) { + revert InvalidTargetSRBounds(); + } + targetSubscriptionRatio = targetSubscriptionRatio_; + } + + /// @notice Updates the deviation ratio bounds. + /// @param deviationRatioBoundLower_ The new lower deviation ratio bound as fixed point number with {DECIMALS} places. + /// @param deviationRatioBoundUpper_ The new upper deviation ratio bound as fixed point number with {DECIMALS} places. + function updateDeviationRatioBounds( + uint256 deviationRatioBoundLower_, + uint256 deviationRatioBoundUpper_ + ) external onlyOwner { + if (deviationRatioBoundLower_ > ONE || deviationRatioBoundUpper_ < ONE) { + revert InvalidDRBounds(); + } + deviationRatioBoundLower = deviationRatioBoundLower_; + deviationRatioBoundUpper = deviationRatioBoundUpper_; + } + + /// @notice Updates the perp mint fee parameters. + /// @param perpMintFeePerc_ The new perp mint fee ceiling percentage + /// as a fixed point number with {DECIMALS} places. + function updatePerpMintFees(uint256 perpMintFeePerc_) external onlyOwner { + if (perpMintFeePerc_ > ONE) { + revert InvalidPerc(); + } + perpMintFeePerc = perpMintFeePerc_; + } + + /// @notice Updates the perp burn fee parameters. + /// @param perpBurnFeePerc_ The new perp burn fee ceiling percentage + /// as a fixed point number with {DECIMALS} places. + function updatePerpBurnFees(uint256 perpBurnFeePerc_) external onlyOwner { + if (perpBurnFeePerc_ > ONE) { + revert InvalidPerc(); + } + perpBurnFeePerc = perpBurnFeePerc_; + } + + /// @notice Update the parameters determining the slope and asymptotes of the sigmoid fee curve. + /// @param p Lower, Upper and Growth sigmoid paramters are fixed point numbers with {DECIMALS} places. + function updatePerpRolloverFees(RolloverFeeSigmoidParams calldata p) external onlyOwner { + // If the bond duration is 28 days and 13 rollovers happen per year, + // perp can be inflated or enriched up to ~65% annually. + if (p.lower < -int256(SIGMOID_BOUND) || p.upper > int256(SIGMOID_BOUND) || p.lower > p.upper) { + revert InvalidSigmoidAsymptotes(); + } + perpRolloverFee.lower = p.lower; + perpRolloverFee.upper = p.upper; + perpRolloverFee.growth = p.growth; + } + + /// @notice Updates the vault mint fee parameters. + /// @param vaultMintFeePerc_ The new vault mint fee ceiling percentage + /// as a fixed point number with {DECIMALS} places. + function updateVaultMintFees(uint256 vaultMintFeePerc_) external onlyOwner { + if (vaultMintFeePerc_ > ONE) { + revert InvalidPerc(); + } + vaultMintFeePerc = vaultMintFeePerc_; + } + + /// @notice Updates the vault burn fee parameters. + /// @param vaultBurnFeePerc_ The new vault burn fee ceiling percentage + /// as a fixed point number with {DECIMALS} places. + function updateVaultBurnFees(uint256 vaultBurnFeePerc_) external onlyOwner { + if (vaultBurnFeePerc_ > ONE) { + revert InvalidPerc(); + } + vaultBurnFeePerc = vaultBurnFeePerc_; + } + + /// @notice Updates the vault's share of the underlying to perp swap fee. + /// @param feePerc The new fee percentage. + function updateVaultUnderlyingToPerpSwapFeePerc(uint256 feePerc) external onlyOwner { + if (feePerc > ONE) { + revert InvalidPerc(); + } + vaultUnderlyingToPerpSwapFeePerc = feePerc; + } + + /// @notice Updates the vault's share of the perp to underlying swap fee. + /// @param feePerc The new fee percentage. + function updateVaultPerpToUnderlyingSwapFeePerc(uint256 feePerc) external onlyOwner { + if (feePerc > ONE) { + revert InvalidPerc(); + } + vaultPerpToUnderlyingSwapFeePerc = feePerc; + } + + //----------------------------------------------------------------------------- + // Public methods + + /// @inheritdoc IFeePolicy + /// @dev Minting perps reduces system dr, i.e) drPost < drPre. + function computePerpMintFeePerc() public view override returns (uint256) { + return perpMintFeePerc; + } + + /// @inheritdoc IFeePolicy + /// @dev Burning perps increases system dr, i.e) drPost > drPre. + function computePerpBurnFeePerc() public view override returns (uint256) { + return perpBurnFeePerc; + } + + /// @inheritdoc IFeePolicy + function computePerpRolloverFeePerc(uint256 dr) external view override returns (int256) { + return + Sigmoid.compute( + dr.toInt256(), + perpRolloverFee.lower, + perpRolloverFee.upper, + perpRolloverFee.growth, + ONE.toInt256() + ); + } + + /// @inheritdoc IFeePolicy + /// @dev Minting vault notes increases system dr, i.e) drPost > drPre. + function computeVaultMintFeePerc() external view override returns (uint256) { + return vaultMintFeePerc; + } + + /// @inheritdoc IFeePolicy + function computeVaultBurnFeePerc() external view override returns (uint256) { + return vaultBurnFeePerc; + } + + /// @inheritdoc IFeePolicy + /// @dev Swapping by minting perps reduces system dr, i.e) drPost < drPre. + function computeUnderlyingToPerpVaultSwapFeePerc( + uint256 /*drPre*/, + uint256 drPost + ) external view override returns (uint256) { + // When the after op deviation ratio is below the bound, + // swapping is disabled. (fees are set to 100%) + return (drPost < deviationRatioBoundLower ? ONE : vaultUnderlyingToPerpSwapFeePerc); + } + + /// @inheritdoc IFeePolicy + /// @dev Swapping by burning perps increases system dr, i.e) drPost > drPre. + function computePerpToUnderlyingVaultSwapFeePerc( + uint256 /*drPre*/, + uint256 drPost + ) external view override returns (uint256) { + // When the after op deviation ratio is above the bound, + // swapping is disabled. (fees are set to 100%) + return (drPost > deviationRatioBoundUpper ? ONE : vaultPerpToUnderlyingSwapFeePerc); + } + + /// @inheritdoc IFeePolicy + function decimals() external pure override returns (uint8) { + return DECIMALS; + } + + /// @inheritdoc IFeePolicy + function computeDeviationRatio(SubscriptionParams memory s) public view returns (uint256) { + // NOTE: We assume that perp's TVL and vault's TVL values have the same base denomination. + uint256 juniorTR = TRANCHE_RATIO_GRANULARITY - s.seniorTR; + return (s.vaultTVL * s.seniorTR).mulDiv(ONE, (s.perpTVL * juniorTR)).mulDiv(ONE, targetSubscriptionRatio); + } +} diff --git a/spot-contracts/contracts/PerpetualTranche.sol b/spot-contracts/contracts/PerpetualTranche.sol index c96eb92a..7450cddd 100644 --- a/spot-contracts/contracts/PerpetualTranche.sol +++ b/spot-contracts/contracts/PerpetualTranche.sol @@ -1,123 +1,61 @@ // SPDX-License-Identifier: BUSL-1.1 -pragma solidity ^0.8.19; +pragma solidity ^0.8.20; import { IERC20MetadataUpgradeable } from "@openzeppelin/contracts-upgradeable/token/ERC20/extensions/IERC20MetadataUpgradeable.sol"; -import { IERC20Upgradeable, IPerpetualTranche, IBondIssuer, IFeeStrategy, IPricingStrategy, IDiscountStrategy, IBondController, ITranche } from "./_interfaces/IPerpetualTranche.sol"; +import { IERC20Upgradeable, IPerpetualTranche, IBondIssuer, IFeePolicy, IBondController, ITranche } from "./_interfaces/IPerpetualTranche.sol"; +import { IRolloverVault } from "./_interfaces/IRolloverVault.sol"; +import { TokenAmount, RolloverData, SubscriptionParams } from "./_interfaces/CommonTypes.sol"; +import { UnauthorizedCall, UnauthorizedTransferOut, UnexpectedDecimals, UnexpectedAsset, UnacceptableParams, UnacceptableRollover, ExceededMaxSupply, ExceededMaxMintPerTranche, ReserveCountOverLimit } from "./_interfaces/ProtocolErrors.sol"; import { OwnableUpgradeable } from "@openzeppelin/contracts-upgradeable/access/OwnableUpgradeable.sol"; import { PausableUpgradeable } from "@openzeppelin/contracts-upgradeable/security/PausableUpgradeable.sol"; import { ReentrancyGuardUpgradeable } from "@openzeppelin/contracts-upgradeable/security/ReentrancyGuardUpgradeable.sol"; import { MathUpgradeable } from "@openzeppelin/contracts-upgradeable/utils/math/MathUpgradeable.sol"; +import { SafeCastUpgradeable } from "@openzeppelin/contracts-upgradeable/utils/math/SafeCastUpgradeable.sol"; import { SignedMathUpgradeable } from "@openzeppelin/contracts-upgradeable/utils/math/SignedMathUpgradeable.sol"; import { ERC20BurnableUpgradeable } from "@openzeppelin/contracts-upgradeable/token/ERC20/extensions/ERC20BurnableUpgradeable.sol"; import { EnumerableSetUpgradeable } from "@openzeppelin/contracts-upgradeable/utils/structs/EnumerableSetUpgradeable.sol"; import { SafeERC20Upgradeable } from "@openzeppelin/contracts-upgradeable/token/ERC20/utils/SafeERC20Upgradeable.sol"; import { BondHelpers } from "./_utils/BondHelpers.sol"; -/// @notice Expected contract call to be triggered by authorized caller. -/// @param caller The address which triggered the call. -/// @param authorizedCaller The address which is authorized to trigger the call. -error UnauthorizedCall(address caller, address authorizedCaller); - -/// @notice Expected a valid percentage value from 0-100 as a fixed point number with {PERC_DECIMALS}. -/// @param value Invalid value. -error InvalidPerc(uint256 value); - -/// @notice Expected contract reference to not be `address(0)`. -error UnacceptableReference(); - -/// @notice Expected strategy to return a fixed point with exactly expected decimals. -error InvalidStrategyDecimals(uint256 decimals, uint256 expectDecimals); - -/// @notice Expected bond issuer's collateral token to match underlying collateral token. -/// @param invalidCollateral Address of the input bond issuer's collateral token. -/// @param underlyingCollateral Address of underlying system collateral token. -error InvalidCollateral(address invalidCollateral, address underlyingCollateral); - -/// @notice Expected minTrancheMaturity be less than or equal to maxTrancheMaturity. -/// @param minTrancheMaturitySec Minimum tranche maturity time in seconds. -/// @param minTrancheMaturitySec Maximum tranche maturity time in seconds. -error InvalidTrancheMaturityBounds(uint256 minTrancheMaturitySec, uint256 maxTrancheMaturitySec); - -/// @notice Expected deposited tranche to be of current deposit bond. -/// @param trancheIn Address of the deposit tranche. -/// @param depositBond Address of the currently accepted deposit bond. -error UnacceptableDepositTranche(ITranche trancheIn, IBondController depositBond); - -/// @notice Expected to mint a non-zero amount of tokens. -/// @param trancheInAmt The amount of tranche tokens deposited. -/// @param perpAmtMint The amount of tranche tokens mint. -error UnacceptableMintAmt(uint256 trancheInAmt, uint256 perpAmtMint); - -/// @notice Expected to burn a non-zero amount of tokens. -/// @param requestedBurnAmt The amount of tranche tokens requested to be burnt. -/// @param perpSupply The current supply of perp tokens. -error UnacceptableBurnAmt(uint256 requestedBurnAmt, uint256 perpSupply); - -/// @notice Expected redemption to result in supply reduction. -/// @param newSupply The new total supply after redemption. -/// @param perpSupply The current supply of perp tokens. -error ExpectedSupplyReduction(uint256 newSupply, uint256 perpSupply); - -/// @notice Expected rollover to be acceptable. -/// @param trancheIn Address of the tranche token transferred in. -/// @param tokenOut Address of the reserve token transferred out. -error UnacceptableRollover(ITranche trancheIn, IERC20Upgradeable tokenOut); - -/// @notice Expected to rollover a non-zero amount of tokens. -/// @param trancheInAmt The amount of tranche tokens deposited. -/// @param trancheOutAmt The amount of tranche tokens withdrawn. -/// @param rolloverAmt The perp denominated value of tokens rolled over. -error UnacceptableRolloverAmt(uint256 trancheInAmt, uint256 trancheOutAmt, uint256 rolloverAmt); - -/// @notice Expected supply to be lower than the defined max supply. -/// @param newSupply The new total supply after minting. -/// @param currentMaxSupply The current max supply. -error ExceededMaxSupply(uint256 newSupply, uint256 currentMaxSupply); - -/// @notice Expected the total mint amount per tranche to be lower than the limit. -/// @param trancheIn Address of the deposit tranche. -/// @param mintAmtForCurrentTranche The amount of perps that have been minted using the tranche. -/// @param maxMintAmtPerTranche The amount of perps that can be minted per tranche. -error ExceededMaxMintPerTranche(ITranche trancheIn, uint256 mintAmtForCurrentTranche, uint256 maxMintAmtPerTranche); - -/// @notice Expected the percentage of reserve value held as mature tranches to be at least -/// as much as the target percentage. -/// @param matureValuePerc The current percentage of reserve value held as mature tranches. -/// @param matureValueTargetPerc The target percentage. -error BelowMatureValueTargetPerc(uint256 matureValuePerc, uint256 matureValueTargetPerc); - -/// @notice Expected transfer out asset to not be a reserve asset. -/// @param token Address of the token transferred. -error UnauthorizedTransferOut(IERC20Upgradeable token); - /** * @title PerpetualTranche * * @notice An opinionated implementation of a perpetual note ERC-20 token contract, backed by buttonwood tranches. * - * Perpetual note tokens (or perps for short) are backed by tranche tokens held in this contract's reserve. - * Users can mint perps by depositing tranche tokens into the reserve. + * Perpetual note tokens (or perps for short) are backed by senior tranche tokens (aka seniors) held in this contract's reserve. + * Users can mint perps by depositing seniors into the reserve. * They can redeem tokens from the reserve by burning their perps. * * The whitelisted bond issuer issues new deposit bonds periodically based on a predefined frequency. - * Users can ONLY mint perps for tranche tokens belonging to the active "deposit" bond. + * Users can ONLY mint perps for seniors belonging to the active "deposit" bond. * Users can burn perps, and redeem a proportional share of tokens held in the reserve. * - * Once tranche tokens held in the reserve mature the underlying collateral is extracted - * into the reserve. The system keeps track of total mature tranches held by the reserve. - * This acts as a "virtual" tranche balance for all collateral extracted from the mature tranches. + * Once seniors held in the reserve mature, the underlying collateral is extracted + * into the reserve. At any time, the reserve holds at most 2 classes of tokens + * i.e) the seniors and the underlying collateral. + * + * Incentivized parties can "rollover" tranches approaching maturity or the underlying collateral, + * for newer seniors (which expire further out in the future) that belong to the updated "depositBond". + * + * + * @dev The time dependent system state is updated "lazily" without a need for an explicit poke + * from the outside world. Every external function that deals with the reserve + * invokes the `afterStateUpdate` modifier at the entry-point. + * This brings the system storage state up to date. * - * At any time, the reserve holds at most 2 classes of tokens - * ie) the normal tranche tokens and mature tranche (which is essentially the underlying collateral token). + * CRITICAL: On the 3 main system operations: deposit, redeem and rollover; + * We first compute fees before executing any transfers in or out of the system. + * The ordering of operations is very important as the fee computation logic, + * requires the system TVL as an input and which should be recorded prior to any value + * entering or leaving the system. * - * Incentivized parties can "rollover" tranches approaching maturity or the mature tranche, - * for newer tranche tokens that belong to the current "depositBond". + * With the new demand based fee policy implementation, + * both perp and the rollover have a mutual dependency on each other. + * None of the perp operations will work unless it's pointed to a valid vault. * - * The time dependent system state is updated "lazily" without a need for an explicit poke - * from the outside world. Every external function that deals with the reserve - * invokes the `afterStateUpdate` modifier at the entry-point. - * This brings the system storage state up to date. + * When computing the value of assets in the system, the code always over-values by + * rounding up. When computing the value of incoming assets, the code rounds down. * */ contract PerpetualTranche is @@ -140,49 +78,7 @@ contract PerpetualTranche is // Math using MathUpgradeable for uint256; using SignedMathUpgradeable for int256; - - //------------------------------------------------------------------------- - // Events - - /// @notice Event emitted when the keeper is updated. - /// @param prevKeeper The address of the previous keeper. - /// @param newKeeper The address of the new keeper. - event UpdatedKeeper(address prevKeeper, address newKeeper); - - /// @notice Event emitted when the bond issuer is updated. - /// @param issuer Address of the issuer contract. - event UpdatedBondIssuer(IBondIssuer issuer); - - /// @notice Event emitted when the fee strategy is updated. - /// @param strategy Address of the strategy contract. - event UpdatedFeeStrategy(IFeeStrategy strategy); - - /// @notice Event emitted when the pricing strategy is updated. - /// @param strategy Address of the strategy contract. - event UpdatedPricingStrategy(IPricingStrategy strategy); - - /// @notice Event emitted when the discount strategy is updated. - /// @param strategy Address of the strategy contract. - event UpdatedDiscountStrategy(IDiscountStrategy strategy); - - /// @notice Event emitted when maturity tolerance parameters are updated. - /// @param min The minimum maturity time. - /// @param max The maximum maturity time. - event UpdatedTolerableTrancheMaturity(uint256 min, uint256 max); - - /// @notice Event emitted when the supply caps are updated. - /// @param maxSupply The max total supply. - /// @param maxMintAmtPerTranche The max mint amount per tranche. - event UpdatedMintingLimits(uint256 maxSupply, uint256 maxMintAmtPerTranche); - - /// @notice Event emitted when the mature value target percentage is updated. - /// @param matureValueTargetPerc The new target percentage. - event UpdatedMatureValueTargetPerc(uint256 matureValueTargetPerc); - - /// @notice Event emitted when the authorized rollers are updated. - /// @param roller The address of the roller. - /// @param authorized If the roller is has been authorized or not. - event UpdatedRollerAuthorization(address roller, bool authorized); + using SafeCastUpgradeable for int256; //------------------------------------------------------------------------- // Perp Math Basics: @@ -190,47 +86,33 @@ contract PerpetualTranche is // System holds tokens in the reserve {t1, t2 ... tn} // with balances {b1, b2 ... bn}. // - // Internally reserve token denominations (amounts/balances) are - // standardized using a discount factor. - // Standard denomination: b'i = bi . discount(ti) - // - // Discount are typically expected to be ~1.0 for safe tranches, - // but could be less for riskier junior tranches. - // - // // System reserve value: - // RV => b'1 . price(t1) + b'2 . price(t2) + .... + b'n . price(tn) - // => Σ b'i . price(ti) - // + // RV => b1 . price(t1) + b2 . price(t2) + .... + bn . price(tn) + // => Σ bi . price(ti) // // When `ai` tokens of type `ti` are deposited into the system: - // Mint: mintAmt (perps) => (a'i * price(ti) / RV) * supply(perps) + // Mint: mintAmt (perps) => (ai * price(ti) / RV) * supply(perps) // // This ensures that if 10% of the collateral value is deposited, // the minter receives 10% of the perp token supply. // This removes any race conditions for minters based on reserve state. // - // // When `p` perp tokens are redeemed: // Redeem: ForEach ti => (p / supply(perps)) * bi // - // // When `ai` tokens of type `ti` are rolled in for tokens of type `tj` - // => ai * discount(ti) * price(ti) = aj * discount(tj) * price(tj) - // Rollover: aj => ai * discount(ti) * price(ti) / (discount(tj) * price(tj)) + // => ai * price(ti) = aj * price(tj) + // Rollover: aj => ai * price(ti) / (price(tj)) // // //------------------------------------------------------------------------- // Constants & Immutables - uint8 public constant DISCOUNT_DECIMALS = 18; - uint256 public constant UNIT_DISCOUNT = (10**DISCOUNT_DECIMALS); + // Number of decimals for a multiplier of 1.0x (i.e. 100%) + uint8 public constant FEE_POLICY_DECIMALS = 8; + uint256 public constant FEE_ONE = (10 ** FEE_POLICY_DECIMALS); - uint8 public constant PRICE_DECIMALS = 8; - uint256 public constant UNIT_PRICE = (10**PRICE_DECIMALS); - - uint8 public constant PERC_DECIMALS = 6; - uint256 public constant UNIT_PERC = 10**PERC_DECIMALS; - uint256 public constant HUNDRED_PERC = 100 * UNIT_PERC; + /// @dev The maximum number of reserve assets that can be held by perp. + uint8 public constant MAX_RESERVE_COUNT = 11; //------------------------------------------------------------------------- // Storage @@ -244,25 +126,27 @@ contract PerpetualTranche is /// @inheritdoc IPerpetualTranche address public override keeper; - /// @notice External contract points controls fees & incentives. - IFeeStrategy public override feeStrategy; + /// @notice External contract that orchestrates fees across the spot protocol. + /// @custom:oz-upgrades-renamed-from feeStrategy + IFeePolicy public override feePolicy; - /// @notice External contract that computes a given reserve token's price. - /// @dev The computed price is expected to be a fixed point unsigned integer with {PRICE_DECIMALS} decimals. - IPricingStrategy public pricingStrategy; + /// @notice DEPRECATED. + /// @dev This used to point to the external strategy that computes a given reserve token's price. + /// @custom:oz-upgrades-renamed-from pricingStrategy + // solhint-disable-next-line var-name-mixedcase + address private _pricingStrategy_DEPRECATED; - /// @notice External contract that computes a given reserve token's discount factor. - /// @dev It is a multiplier, applied to every asset when added to the reserve. - /// This accounts for things like tranche seniority and underlying collateral volatility. - /// It also allows for standardizing denominations when comparing two different reserve tokens. - /// For example, a factor of 0.95 on a particular tranche results in a 5% discount. - /// The discount factor is expected to be a fixed point unsigned integer with {DISCOUNT_DECIMALS} decimals. - IDiscountStrategy public discountStrategy; + /// @notice DEPRECATED. + /// @dev This used to point to the external strategy that computes a given reserve token's discount factor. + /// Now, we assume perp accepts only the "senior" most tranche from a bond. Seniors have a discount of 1.0, + /// every other tranche has a discount of 0. + /// @custom:oz-upgrades-renamed-from discountStrategy + // solhint-disable-next-line var-name-mixedcase + address private _discountStrategy_DEPRECATED; - /// @notice External contract that stores a predefined bond config and frequency, - /// and issues new bonds when poked. + /// @inheritdoc IPerpetualTranche /// @dev Only tranches of bonds issued by this whitelisted issuer are accepted into the reserve. - IBondIssuer public bondIssuer; + IBondIssuer public override bondIssuer; /// @notice The active deposit bond of whose tranches are currently being accepted to mint perps. IBondController private _depositBond; @@ -275,23 +159,29 @@ contract PerpetualTranche is /// it can NOT get added into the reserve. uint256 public maxTrancheMaturitySec; - /// @notice The percentage of the reserve value to be held as mature tranches. - uint256 public matureValueTargetPerc; + /// @notice DEPRECATED. + /// @dev This used to control the percentage of the reserve value to be held as the underlying collateral. + /// With V2 perp cannot control this anymore, the rollover mechanics are dictated + /// by the amount of capital in the vault system. + /// @custom:oz-upgrades-renamed-from matureValueTargetPerc + // solhint-disable-next-line var-name-mixedcase + uint256 private _matureValueTargetPerc_DEPRECATED; /// @notice The maximum supply of perps that can exist at any given time. uint256 public maxSupply; - /// @notice The max number of perps that can be minted for each tranche in the minting bond. + /// @notice The max number of perps that can be minted using the senior tranche in the minting bond. uint256 public maxMintAmtPerTranche; /// @notice The total number of perps that have been minted using a given tranche. mapping(ITranche => uint256) public mintedSupplyPerTranche; - /// @notice Discount factor actually "applied" on each reserve token. It is computed and recorded when - /// a token is deposited into the system for the first time. - /// @dev For all calculations thereafter, the token's applied discount will be used. - /// The discount is stored as a fixed point unsigned integer with {DISCOUNT_DECIMALS} decimals. - mapping(IERC20Upgradeable => uint256) private _appliedDiscounts; + /// @notice DEPRECATED. + /// @dev This used to store the discount factor applied on each reserve token. + /// Now, we assume all tokens in perp have a discount factor of 1. + /// @custom:oz-upgrades-renamed-from appliedDiscounts + // solhint-disable-next-line var-name-mixedcase + mapping(IERC20Upgradeable => uint256) private _appliedDiscounts_DEPRECATED; //-------------------------------------------------------------------------- // RESERVE @@ -299,20 +189,19 @@ contract PerpetualTranche is /// @notice Set of all tokens in the reserve which back the perps. EnumerableSetUpgradeable.AddressSet private _reserves; - /// @notice The amount of all the mature tranches extracted and held as the collateral token, - /// i.e) the reserve's "virtual" mature tranche balance. - /// @dev The mature tranche is assumed to have {UNIT_DISCOUNT}. So we do NOT have to - /// scale using the discount factor when dealing with the mature tranche balance. - uint256 private _matureTrancheBalance; + /// @notice DEPRECATED. + /// @dev The used to store the amount of all the mature tranches extracted and held as the collateral token, + /// i.e) the reserve's "virtual" mature tranche balance. The system no longer tracks this. + // solhint-disable-next-line var-name-mixedcase + uint256 private _matureTrancheBalance_DEPRECATED; //-------------------------------------------------------------------------- - // v1.1.0 STORAGE ADDITION + // v2.0.0 STORAGE ADDITION - /// @notice Set of all authorized addresses which can execute rollovers. - /// @dev The contract owner can modify this set. - /// NOTE: If the set is empty, all addresses are considered authorized and can execute rollovers. - /// else only addresses in the set can execute rollovers. - EnumerableSetUpgradeable.AddressSet private _rollers; + /// @notice Address of the authorized rollover vault. + /// @dev If this address is set, only the rollover vault can perform rollovers. + /// If not rollovers are publicly accessible. + IRolloverVault public override vault; //-------------------------------------------------------------------------- // Modifiers @@ -325,18 +214,16 @@ contract PerpetualTranche is /// @dev Throws if called by any account other than the keeper. modifier onlyKeeper() { - if (keeper != _msgSender()) { - revert UnauthorizedCall(_msgSender(), keeper); + if (keeper != msg.sender) { + revert UnauthorizedCall(); } _; } - /// @dev Throws if called by any account other than an authorized roller. - modifier onlyRollers() { - // If the set it empty, permit all callers - // else permit only whitelisted callers. - if (_rollers.length() > 0 && !_rollers.contains(_msgSender())) { - revert UnauthorizedCall(_msgSender(), address(0)); + /// @dev Throws if called not called by vault. + modifier onlyVault() { + if (address(vault) != msg.sender) { + revert UnauthorizedCall(); } _; } @@ -344,23 +231,24 @@ contract PerpetualTranche is //-------------------------------------------------------------------------- // Construction & Initialization + /// @custom:oz-upgrades-unsafe-allow constructor + constructor() { + _disableInitializers(); + } + /// @notice Contract state initialization. /// @param name ERC-20 Name of the Perp token. /// @param symbol ERC-20 Symbol of the Perp token. /// @param collateral_ Address of the underlying collateral token. /// @param bondIssuer_ Address of the bond issuer contract. - /// @param feeStrategy_ Address of the fee strategy contract. - /// @param pricingStrategy_ Address of the pricing strategy contract. - /// @param discountStrategy_ Address of the discount strategy contract. + /// @param feePolicy_ Address of the fee policy contract. function init( string memory name, string memory symbol, IERC20Upgradeable collateral_, IBondIssuer bondIssuer_, - IFeeStrategy feeStrategy_, - IPricingStrategy pricingStrategy_, - IDiscountStrategy discountStrategy_ - ) public initializer { + IFeePolicy feePolicy_ + ) external initializer { __ERC20_init(name, symbol); __ERC20Burnable_init(); __Ownable_init(); @@ -372,138 +260,60 @@ contract PerpetualTranche is // and is to be never updated. _reserves.add(address(collateral_)); _syncReserve(collateral_); - _applyDiscount(collateral_, UNIT_DISCOUNT); + updateKeeper(owner()); + updateFeePolicy(feePolicy_); updateBondIssuer(bondIssuer_); - updateFeeStrategy(feeStrategy_); - updatePricingStrategy(pricingStrategy_); - updateDiscountStrategy(discountStrategy_); updateTolerableTrancheMaturity(1, type(uint256).max); updateMintingLimits(type(uint256).max, type(uint256).max); - updateMatureValueTargetPerc(0); } //-------------------------------------------------------------------------- // ADMIN only methods - /// @notice Pauses deposits, withdrawals and rollovers. - /// @dev NOTE: ERC-20 functions, like transfers will always remain operational. - function pause() public onlyKeeper { - _pause(); - } - - /// @notice Unpauses deposits, withdrawals and rollovers. - /// @dev NOTE: ERC-20 functions, like transfers will always remain operational. - function unpause() public onlyKeeper { - _unpause(); + /// @notice Updates the reference to the rollover vault. + /// @param vault_ The address of the new vault. + function updateVault(IRolloverVault vault_) external onlyOwner { + vault = vault_; } /// @notice Updates the reference to the keeper. - /// @param newKeeper The address of the new keeper. - function updateKeeper(address newKeeper) public virtual onlyOwner { - address prevKeeper = keeper; - keeper = newKeeper; - emit UpdatedKeeper(prevKeeper, newKeeper); - } - - /// @notice Updates the authorized roller set. - /// @dev CAUTION: If the authorized roller set is empty, all rollers are authorized. - /// @param roller The address of the roller. - /// @param authorize If the roller is to be authorized or unauthorized. - function authorizeRoller(address roller, bool authorize) external onlyOwner { - if (authorize && !_rollers.contains(roller)) { - _rollers.add(roller); - } else if (!authorize && _rollers.contains(roller)) { - _rollers.remove(roller); - } else { - return; - } - - emit UpdatedRollerAuthorization(roller, authorize); + /// @param keeper_ The address of the new keeper. + function updateKeeper(address keeper_) public onlyOwner { + keeper = keeper_; } /// @notice Update the reference to the bond issuer contract. /// @param bondIssuer_ New bond issuer address. function updateBondIssuer(IBondIssuer bondIssuer_) public onlyOwner { - if (address(bondIssuer_) == address(0)) { - revert UnacceptableReference(); - } - if (address(_reserveAt(0)) != bondIssuer_.collateral()) { - revert InvalidCollateral(bondIssuer_.collateral(), address(_reserveAt(0))); + if (bondIssuer_.collateral() != address(_reserveAt(0))) { + revert UnexpectedAsset(); } bondIssuer = bondIssuer_; - emit UpdatedBondIssuer(bondIssuer_); - } - - /// @notice Update the reference to the fee strategy contract. - /// @param feeStrategy_ New strategy address. - function updateFeeStrategy(IFeeStrategy feeStrategy_) public onlyOwner { - if (address(feeStrategy_) == address(0)) { - revert UnacceptableReference(); - } - feeStrategy = feeStrategy_; - emit UpdatedFeeStrategy(feeStrategy_); - } - - /// @notice Update the reference to the pricing strategy contract. - /// @param pricingStrategy_ New strategy address. - function updatePricingStrategy(IPricingStrategy pricingStrategy_) public onlyOwner { - if (address(pricingStrategy_) == address(0)) { - revert UnacceptableReference(); - } - if (pricingStrategy_.decimals() != PRICE_DECIMALS) { - revert InvalidStrategyDecimals(pricingStrategy_.decimals(), PRICE_DECIMALS); - } - pricingStrategy = pricingStrategy_; - emit UpdatedPricingStrategy(pricingStrategy_); } - /// @notice Update the reference to the discount strategy contract. - /// @param discountStrategy_ New strategy address. - function updateDiscountStrategy(IDiscountStrategy discountStrategy_) public onlyOwner { - if (address(discountStrategy_) == address(0)) { - revert UnacceptableReference(); + /// @notice Update the reference to the fee policy contract. + /// @param feePolicy_ New strategy address. + function updateFeePolicy(IFeePolicy feePolicy_) public onlyOwner { + if (feePolicy_.decimals() != FEE_POLICY_DECIMALS) { + revert UnexpectedDecimals(); } - if (discountStrategy_.decimals() != DISCOUNT_DECIMALS) { - revert InvalidStrategyDecimals(discountStrategy_.decimals(), DISCOUNT_DECIMALS); - } - discountStrategy = discountStrategy_; - emit UpdatedDiscountStrategy(discountStrategy_); + feePolicy = feePolicy_; } /// @notice Update the maturity tolerance parameters. /// @param minTrancheMaturitySec_ New minimum maturity time. /// @param maxTrancheMaturitySec_ New maximum maturity time. - function updateTolerableTrancheMaturity(uint256 minTrancheMaturitySec_, uint256 maxTrancheMaturitySec_) - public - onlyOwner - { + function updateTolerableTrancheMaturity( + uint256 minTrancheMaturitySec_, + uint256 maxTrancheMaturitySec_ + ) public onlyOwner { if (minTrancheMaturitySec_ > maxTrancheMaturitySec_) { - revert InvalidTrancheMaturityBounds(minTrancheMaturitySec_, maxTrancheMaturitySec_); + revert UnacceptableParams(); } minTrancheMaturitySec = minTrancheMaturitySec_; maxTrancheMaturitySec = maxTrancheMaturitySec_; - emit UpdatedTolerableTrancheMaturity(minTrancheMaturitySec_, maxTrancheMaturitySec_); - } - - /// @notice Update parameters controlling the perp token mint limits. - /// @param maxSupply_ New max total supply. - /// @param maxMintAmtPerTranche_ New max total for per tranche in minting bond. - function updateMintingLimits(uint256 maxSupply_, uint256 maxMintAmtPerTranche_) public onlyOwner { - maxSupply = maxSupply_; - maxMintAmtPerTranche = maxMintAmtPerTranche_; - emit UpdatedMintingLimits(maxSupply_, maxMintAmtPerTranche_); - } - - /// @notice Update the mature value target percentage parameter. - /// @param matureValueTargetPerc_ The new target percentage. - function updateMatureValueTargetPerc(uint256 matureValueTargetPerc_) public onlyOwner { - if (matureValueTargetPerc_ > HUNDRED_PERC) { - revert InvalidPerc(matureValueTargetPerc_); - } - matureValueTargetPerc = matureValueTargetPerc_; - emit UpdatedMatureValueTargetPerc(matureValueTargetPerc); } /// @notice Allows the owner to transfer non-critical assets out of the system if required. @@ -514,86 +324,93 @@ contract PerpetualTranche is IERC20Upgradeable token, address to, uint256 amount - ) external afterStateUpdate onlyOwner { - if (_inReserve(token) || feeToken() == token) { - revert UnauthorizedTransferOut(token); + ) external afterStateUpdate nonReentrant onlyOwner { + if (_inReserve(token)) { + revert UnauthorizedTransferOut(); } token.safeTransfer(to, amount); } + //-------------------------------------------------------------------------- + // Keeper only methods + + /// @notice Pauses deposits, withdrawals and rollovers. + /// @dev ERC-20 functions, like transfers will always remain operational. + function pause() external onlyKeeper { + _pause(); + } + + /// @notice Unpauses deposits, withdrawals and rollovers. + /// @dev ERC-20 functions, like transfers will always remain operational. + function unpause() external onlyKeeper { + _unpause(); + } + + /// @notice Update parameters controlling the perp token mint limits. + /// @param maxSupply_ New max total supply. + /// @param maxMintAmtPerTranche_ New max total for per tranche in minting bond. + function updateMintingLimits(uint256 maxSupply_, uint256 maxMintAmtPerTranche_) public onlyKeeper { + maxSupply = maxSupply_; + maxMintAmtPerTranche = maxMintAmtPerTranche_; + } + //-------------------------------------------------------------------------- // External methods /// @inheritdoc IPerpetualTranche - function deposit(ITranche trancheIn, uint256 trancheInAmt) - external - override - nonReentrant - whenNotPaused - afterStateUpdate - { - if (!_isDepositBondTranche(trancheIn)) { - revert UnacceptableDepositTranche(trancheIn, _depositBond); + function deposit( + ITranche trancheIn, + uint256 trancheInAmt + ) external override afterStateUpdate nonReentrant whenNotPaused returns (uint256) { + if (!_isDepositTranche(trancheIn)) { + revert UnexpectedAsset(); } - // calculates the amount of perp tokens when depositing `trancheInAmt` of tranche tokens + // Calculates the fee adjusted amount of perp tokens minted when depositing `trancheInAmt` of tranche tokens + // NOTE: This operation should precede any token transfers. uint256 perpAmtMint = _computeMintAmt(trancheIn, trancheInAmt); - if (trancheInAmt == 0 || perpAmtMint == 0) { - revert UnacceptableMintAmt(trancheInAmt, perpAmtMint); + if (trancheInAmt <= 0 || perpAmtMint <= 0) { + return 0; } - // calculates the fees to mint `perpAmtMint` of perp token - (int256 reserveFee, uint256 protocolFee) = feeStrategy.computeMintFees(perpAmtMint); - // transfers tranche tokens from the sender to the reserve - _transferIntoReserve(msg.sender, trancheIn, trancheInAmt); + _transferIntoReserve(trancheIn, trancheInAmt); // mints perp tokens to the sender _mint(msg.sender, perpAmtMint); - // settles fees - _settleFee(msg.sender, reserveFee, protocolFee); - // post-deposit checks mintedSupplyPerTranche[trancheIn] += perpAmtMint; - _enforcePerTrancheSupplyCap(trancheIn); - _enforceTotalSupplyCap(); + _enforceMintCaps(trancheIn); + + return perpAmtMint; } /// @inheritdoc IPerpetualTranche - function redeem(uint256 perpAmtBurnt) external override nonReentrant whenNotPaused afterStateUpdate { - // gets the current perp supply - uint256 perpSupply = totalSupply(); - + function redeem( + uint256 perpAmtBurnt + ) external override afterStateUpdate nonReentrant whenNotPaused returns (TokenAmount[] memory) { // verifies if burn amount is acceptable - if (perpAmtBurnt == 0 || perpAmtBurnt > perpSupply) { - revert UnacceptableBurnAmt(perpAmtBurnt, perpSupply); + if (perpAmtBurnt <= 0) { + return new TokenAmount[](0); } - // calculates share of reserve tokens to be redeemed - (IERC20Upgradeable[] memory tokensOuts, uint256[] memory tokenOutAmts) = _computeRedemptionAmts(perpAmtBurnt); - - // calculates the fees to burn `perpAmtBurnt` of perp token - (int256 reserveFee, uint256 protocolFee) = feeStrategy.computeBurnFees(perpAmtBurnt); - - // updates the mature tranche balance - _updateMatureTrancheBalance(_matureTrancheBalance.mulDiv((perpSupply - perpAmtBurnt), perpSupply)); - - // settles fees - _settleFee(msg.sender, reserveFee, protocolFee); + // Calculates the fee adjusted share of reserve tokens to be redeemed + // NOTE: This operation should precede any token transfers. + TokenAmount[] memory tokensOut = _computeRedemptionAmts(perpAmtBurnt); // burns perp tokens from the sender _burn(msg.sender, perpAmtBurnt); // transfers reserve tokens out - for (uint256 i = 0; i < tokensOuts.length; i++) { - if (tokenOutAmts[i] > 0) { - _transferOutOfReserve(msg.sender, tokensOuts[i], tokenOutAmts[i]); + uint8 tokensOutCount = uint8(tokensOut.length); + for (uint8 i = 0; i < tokensOutCount; ++i) { + if (tokensOut[i].amount > 0) { + _transferOutOfReserve(tokensOut[i].token, tokensOut[i].amount); } } - // post-redeem checks - _enforceSupplyReduction(perpSupply); + return tokensOut; } /// @inheritdoc IPerpetualTranche @@ -601,53 +418,28 @@ contract PerpetualTranche is ITranche trancheIn, IERC20Upgradeable tokenOut, uint256 trancheInAmtAvailable - ) external override onlyRollers nonReentrant whenNotPaused afterStateUpdate { + ) external override onlyVault afterStateUpdate nonReentrant whenNotPaused returns (RolloverData memory) { // verifies if rollover is acceptable if (!_isAcceptableRollover(trancheIn, tokenOut)) { - revert UnacceptableRollover(trancheIn, tokenOut); + revert UnacceptableRollover(); } - // calculates the perp denominated amount rolled over and the tokenOutAmt - IPerpetualTranche.RolloverPreview memory r = _computeRolloverAmt( - trancheIn, - tokenOut, - trancheInAmtAvailable, - type(uint256).max - ); + // Calculates the fee adjusted amount of tranches exchanged during a rolled over + // NOTE: This operation should precede any token transfers. + RolloverData memory r = _computeRolloverAmt(trancheIn, tokenOut, trancheInAmtAvailable); - // verifies if rollover amount is acceptable - if (r.trancheInAmt == 0 || r.tokenOutAmt == 0 || r.perpRolloverAmt == 0) { - revert UnacceptableRolloverAmt(r.trancheInAmt, r.tokenOutAmt, r.perpRolloverAmt); + // Verifies if rollover amount is acceptable + if (r.trancheInAmt <= 0 || r.tokenOutAmt <= 0) { + return r; } - // calculates the fees to rollover `r.perpRolloverAmt` of perp token - (int256 reserveFee, uint256 protocolFee) = feeStrategy.computeRolloverFees(r.perpRolloverAmt); - // transfers tranche tokens from the sender to the reserve - _transferIntoReserve(msg.sender, trancheIn, r.trancheInAmt); - - // settles fees - _settleFee(msg.sender, reserveFee, protocolFee); - - // updates the mature tranche balance - if (_isMatureTranche(tokenOut)) { - _updateMatureTrancheBalance(_matureTrancheBalance - r.trancheOutAmt); - } + _transferIntoReserve(trancheIn, r.trancheInAmt); // transfers tranche from the reserve to the sender - _transferOutOfReserve(msg.sender, tokenOut, r.tokenOutAmt); - - // post-rollover checks - _enforceMatureValueTarget(); - // NOTE: though the rollover operation does not change the perp token's total supply, - // we still enforce the supply cap here as the -ve rollover fees - // might mint more perp tokens which could increase the perp total supply. - _enforceTotalSupplyCap(); - } + _transferOutOfReserve(tokenOut, r.tokenOutAmt); - /// @inheritdoc IPerpetualTranche - function getMatureTrancheBalance() external override afterStateUpdate returns (uint256) { - return _matureTrancheBalance; + return r; } /// @inheritdoc IPerpetualTranche @@ -656,18 +448,18 @@ contract PerpetualTranche is } /// @inheritdoc IPerpetualTranche - function isAcceptableRollover(ITranche trancheIn, IERC20Upgradeable tokenOut) - external - override - afterStateUpdate - returns (bool) - { - return _isAcceptableRollover(trancheIn, tokenOut); + function getDepositTranche() external override afterStateUpdate returns (ITranche) { + return _depositBond.getSeniorTranche(); + } + + /// @inheritdoc IPerpetualTranche + function getDepositTrancheRatio() external override afterStateUpdate returns (uint256) { + return _depositBond.getSeniorTrancheRatio(); } /// @inheritdoc IPerpetualTranche function getReserveCount() external override afterStateUpdate returns (uint256) { - return _reserveCount(); + return _reserves.length(); } /// @inheritdoc IPerpetualTranche @@ -685,45 +477,53 @@ contract PerpetualTranche is if (!_inReserve(token)) { return 0; } - return _reserveBalance(token); + return token.balanceOf(address(this)); } /// @inheritdoc IPerpetualTranche - /// @dev In the case of the collateral token, it returns the "virtual" tranche balance. - // In all other cases, it just returns the token balance. - function getReserveTrancheBalance(IERC20Upgradeable tranche) external override afterStateUpdate returns (uint256) { - if (!_inReserve(tranche)) { + function getReserveTokenValue(IERC20Upgradeable token) external override afterStateUpdate returns (uint256) { + if (!_inReserve(token)) { return 0; } - return _isMatureTranche(tranche) ? _matureTrancheBalance : _reserveBalance(tranche); - } - - /// @inheritdoc IPerpetualTranche - function getReserveTrancheValue(IERC20Upgradeable tranche) external override afterStateUpdate returns (uint256) { - if (!_inReserve(tranche)) { - return 0; + if (_isUnderlying(token)) { + return token.balanceOf(address(this)); } - uint256 balance = _isMatureTranche(tranche) ? _matureTrancheBalance : _reserveBalance(tranche); - uint256 stdTrancheAmt = _toStdTrancheAmt(balance, computeDiscount(tranche)); - return stdTrancheAmt.mulDiv(computePrice(tranche), UNIT_PRICE); + + ITranche tranche = ITranche(address(token)); + IBondController parentBond = IBondController(tranche.bond()); + return _computeReserveTrancheValue(tranche, parentBond, _reserveAt(0), tranche.balanceOf(address(this)), true); } /// @inheritdoc IPerpetualTranche - /// @dev Reserve tokens which are not up for rollover are marked by `address(0)`. function getReserveTokensUpForRollover() external override afterStateUpdate returns (IERC20Upgradeable[] memory) { - uint256 reserveCount = _reserveCount(); - IERC20Upgradeable[] memory rolloverTokens = new IERC20Upgradeable[](reserveCount); + uint8 reserveCount = uint8(_reserves.length()); + IERC20Upgradeable[] memory activeRolloverTokens = new IERC20Upgradeable[](reserveCount); + + // We count the number of tokens up for rollover. + uint8 numTokensUpForRollover = 0; - if (_matureTrancheBalance > 0) { - rolloverTokens[0] = _reserveAt(0); + // If any underlying collateral exists it can be rolled over. + IERC20Upgradeable underlying_ = _reserveAt(0); + if (underlying_.balanceOf(address(this)) > 0) { + activeRolloverTokens[0] = underlying_; + numTokensUpForRollover++; } - // Iterating through the reserve to find tranches that are no longer "acceptable" - for (uint256 i = 1; i < reserveCount; i++) { + // Iterating through the reserve to find tranches that are ready to be rolled out. + for (uint8 i = 1; i < reserveCount; ++i) { IERC20Upgradeable token = _reserveAt(i); - IBondController bond = IBondController(ITranche(address(token)).bond()); - if (!_isAcceptableForReserve(bond)) { - rolloverTokens[i] = token; + if (_isTimeForRollout(ITranche(address(token)))) { + activeRolloverTokens[i] = token; + numTokensUpForRollover++; + } + } + + // We recreate a smaller array with just the tokens up for rollover. + IERC20Upgradeable[] memory rolloverTokens = new IERC20Upgradeable[](numTokensUpForRollover); + uint8 j = 0; + for (uint8 i = 0; i < reserveCount; ++i) { + if (address(activeRolloverTokens[i]) != address(0)) { + rolloverTokens[j++] = activeRolloverTokens[i]; } } @@ -731,52 +531,39 @@ contract PerpetualTranche is } /// @inheritdoc IPerpetualTranche - /// @dev Returns a fixed point with {PRICE_DECIMALS} decimals. - function getAvgPrice() external override afterStateUpdate returns (uint256) { - uint256 totalSupply_ = totalSupply(); - return totalSupply_ > 0 ? _reserveValue() / totalSupply_ : 0; + /// @dev Returns a fixed point with the same decimals as the underlying collateral. + function getTVL() external override afterStateUpdate returns (uint256) { + return _reserveValue(); } /// @inheritdoc IPerpetualTranche - function computeMintAmt(ITranche trancheIn, uint256 trancheInAmt) - external - override - afterStateUpdate - returns (uint256) - { + function computeMintAmt( + ITranche trancheIn, + uint256 trancheInAmt + ) external override afterStateUpdate returns (uint256) { + if (!_isDepositTranche(trancheIn)) { + revert UnexpectedAsset(); + } return _computeMintAmt(trancheIn, trancheInAmt); } /// @inheritdoc IPerpetualTranche - function computeRedemptionAmts(uint256 perpAmtBurnt) - external - override - afterStateUpdate - returns (IERC20Upgradeable[] memory, uint256[] memory) - { + function computeRedemptionAmts( + uint256 perpAmtBurnt + ) external override afterStateUpdate returns (TokenAmount[] memory) { return _computeRedemptionAmts(perpAmtBurnt); } /// @inheritdoc IPerpetualTranche - /// @dev Set `tokenOutAmtRequested` to max(uint256) to use the reserve balance. function computeRolloverAmt( ITranche trancheIn, IERC20Upgradeable tokenOut, - uint256 trancheInAmtAvailable, - uint256 tokenOutAmtRequested - ) external override afterStateUpdate returns (IPerpetualTranche.RolloverPreview memory) { - return _computeRolloverAmt(trancheIn, tokenOut, trancheInAmtAvailable, tokenOutAmtRequested); - } - - /// @return Returns the number of authorized rollers. - function authorizedRollersCount() external view returns (uint256) { - return _rollers.length(); - } - - /// @return Returns the roller address from the authorized set by index. - /// @param i The index of the address in the set. - function authorizedRollerAt(uint256 i) external view returns (address) { - return _rollers.at(i); + uint256 trancheInAmtAvailable + ) external override afterStateUpdate returns (RolloverData memory) { + if (!_isAcceptableRollover(trancheIn, tokenOut)) { + revert UnacceptableRollover(); + } + return _computeRolloverAmt(trancheIn, tokenOut, trancheInAmtAvailable); } //-------------------------------------------------------------------------- @@ -786,13 +573,18 @@ contract PerpetualTranche is /// @dev Lazily updates time-dependent reserve storage state. /// This function is to be invoked on all external function entry points which are /// read the reserve storage. This function is intended to be idempotent. - function updateState() public override { + function updateState() public override nonReentrant { + // Skip state update when system is paused. + if (paused()) { + return; + } + // Lazily queries the bond issuer to get the most recently issued bond // and updates with the new deposit bond if it's "acceptable". IBondController newBond = bondIssuer.getLatestBond(); // If the new bond has been issued by the issuer and is "acceptable" - if (_depositBond != newBond && _isAcceptableForReserve(newBond)) { + if (_depositBond != newBond && _isValidDepositBond(newBond)) { // updates `_depositBond` with the new bond _depositBond = newBond; emit UpdatedDepositBond(newBond); @@ -804,10 +596,10 @@ contract PerpetualTranche is // NOTE: We traverse the reserve set in the reverse order // as deletions involve swapping the deleted element to the // end of the set and removing the last element. - // We also skip the `reserveAt(0)`, i.e) the mature tranche, + // We also skip the `reserveAt(0)`, i.e) the underlying collateral, // which is never removed. - uint256 reserveCount = _reserveCount(); - for (uint256 i = reserveCount - 1; i > 0; i--) { + uint8 reserveCount = uint8(_reserves.length()); + for (uint8 i = reserveCount - 1; i > 0; i--) { ITranche tranche = ITranche(address(_reserveAt(i))); IBondController bond = IBondController(tranche.bond()); @@ -822,18 +614,11 @@ contract PerpetualTranche is } // Redeeming the underlying collateral token - uint256 trancheBalance = _reserveBalance(tranche); - bond.redeemMature(address(tranche), trancheBalance); + bond.redeemMature(address(tranche), tranche.balanceOf(address(this))); _syncReserve(tranche); - - // Keeps track of the total tranches redeemed - _updateMatureTrancheBalance( - _matureTrancheBalance + _toStdTrancheAmt(trancheBalance, computeDiscount(tranche)) - ); } - // Keeps track of the mature tranche's underlying balance - // ie) the rebasing collateral token + // Keeps track of the underlying collateral balance _syncReserve(_reserveAt(0)); } @@ -841,49 +626,13 @@ contract PerpetualTranche is // External view methods /// @inheritdoc IPerpetualTranche - function collateral() external view override returns (IERC20Upgradeable) { + function underlying() external view override returns (IERC20Upgradeable) { return _reserveAt(0); } //-------------------------------------------------------------------------- // Public view methods - /// @inheritdoc IPerpetualTranche - function perpERC20() public view override returns (IERC20Upgradeable) { - return IERC20Upgradeable(address(this)); - } - - /// @inheritdoc IPerpetualTranche - function reserve() public view override returns (address) { - return address(this); - } - - /// @inheritdoc IPerpetualTranche - function protocolFeeCollector() public view override returns (address) { - return owner(); - } - - /// @inheritdoc IPerpetualTranche - function feeToken() public view override returns (IERC20Upgradeable) { - return feeStrategy.feeToken(); - } - - /// @inheritdoc IPerpetualTranche - /// @dev Gets the applied discount for the given tranche if it's set, - /// if NOT computes the discount. - function computeDiscount(IERC20Upgradeable token) public view override returns (uint256) { - uint256 discount = _appliedDiscounts[token]; - return (discount > 0) ? discount : discountStrategy.computeTrancheDiscount(token); - } - - /// @inheritdoc IPerpetualTranche - function computePrice(IERC20Upgradeable token) public view override returns (uint256) { - return - _isMatureTranche(token) - ? pricingStrategy.computeMatureTranchePrice(token, _reserveBalance(token), _matureTrancheBalance) - : pricingStrategy.computeTranchePrice(ITranche(address(token))); - } - /// @notice Returns the number of decimals used to get its user representation. /// @dev For example, if `decimals` equals `2`, a balance of `505` tokens should /// be displayed to a user as `5.05` (`505 / 10 ** 2`). @@ -894,121 +643,28 @@ contract PerpetualTranche is //-------------------------------------------------------------------------- // Private methods - /// @dev Computes the perp mint amount for given amount of tranche tokens deposited into the reserve. - function _computeMintAmt(ITranche trancheIn, uint256 trancheInAmt) private view returns (uint256) { - uint256 totalSupply_ = totalSupply(); - uint256 stdTrancheInAmt = _toStdTrancheAmt(trancheInAmt, computeDiscount(trancheIn)); - uint256 trancheInPrice = computePrice(trancheIn); - uint256 perpAmtMint = (totalSupply_ > 0) - ? (stdTrancheInAmt * trancheInPrice).mulDiv(totalSupply_, _reserveValue()) - : stdTrancheInAmt.mulDiv(trancheInPrice, UNIT_PRICE); - return (perpAmtMint); - } - - /// @dev Computes the reserve token amounts redeemed when a given number of perps are burnt. - function _computeRedemptionAmts(uint256 perpAmtBurnt) - private - view - returns (IERC20Upgradeable[] memory, uint256[] memory) - { - uint256 totalSupply_ = totalSupply(); - uint256 reserveCount = _reserveCount(); - IERC20Upgradeable[] memory reserveTokens = new IERC20Upgradeable[](reserveCount); - uint256[] memory redemptionAmts = new uint256[](reserveCount); - for (uint256 i = 0; i < reserveCount; i++) { - reserveTokens[i] = _reserveAt(i); - redemptionAmts[i] = (totalSupply_ > 0) - ? _reserveBalance(reserveTokens[i]).mulDiv(perpAmtBurnt, totalSupply_) - : 0; - } - return (reserveTokens, redemptionAmts); - } - - /// @dev Computes the amount of reserve tokens that can be rolled out for the given amount of tranches deposited. - function _computeRolloverAmt( - ITranche trancheIn, - IERC20Upgradeable tokenOut, - uint256 trancheInAmtAvailable, - uint256 tokenOutAmtRequested - ) private view returns (IPerpetualTranche.RolloverPreview memory) { - IPerpetualTranche.RolloverPreview memory r; - - uint256 trancheInDiscount = computeDiscount(trancheIn); - uint256 trancheOutDiscount = computeDiscount(tokenOut); - uint256 trancheInPrice = computePrice(trancheIn); - uint256 trancheOutPrice = computePrice(tokenOut); - uint256 tokenOutBalance = _reserveBalance(tokenOut); - tokenOutAmtRequested = MathUpgradeable.min(tokenOutAmtRequested, tokenOutBalance); - - if (trancheInDiscount == 0 || trancheOutDiscount == 0 || trancheInPrice == 0 || trancheOutPrice == 0) { - r.remainingTrancheInAmt = trancheInAmtAvailable; - return r; - } - - r.trancheInAmt = trancheInAmtAvailable; - uint256 stdTrancheInAmt = _toStdTrancheAmt(trancheInAmtAvailable, trancheInDiscount); - - // Basic rollover: - // (stdTrancheInAmt . trancheInPrice) = (stdTrancheOutAmt . trancheOutPrice) - uint256 stdTrancheOutAmt = stdTrancheInAmt.mulDiv(trancheInPrice, trancheOutPrice); - r.trancheOutAmt = _fromStdTrancheAmt(stdTrancheOutAmt, trancheOutDiscount); - - // However, if the tokenOut is the mature tranche (held as naked collateral), - // we infer the tokenOut amount from the tranche denomination. - // (tokenOutAmt = collateralBalance * trancheOutAmt / matureTrancheBalance) - bool isMatureTrancheOut = _isMatureTranche(tokenOut); - r.tokenOutAmt = isMatureTrancheOut - ? tokenOutBalance.mulDiv(r.trancheOutAmt, _matureTrancheBalance) - : r.trancheOutAmt; - - // When the token out balance is NOT covered: - // we fix tokenOutAmt = tokenOutAmtRequested and back calculate other values - if (r.tokenOutAmt > tokenOutAmtRequested) { - r.tokenOutAmt = tokenOutAmtRequested; - r.trancheOutAmt = isMatureTrancheOut - ? _matureTrancheBalance.mulDiv(r.tokenOutAmt, tokenOutBalance) - : r.tokenOutAmt; - stdTrancheOutAmt = _toStdTrancheAmt(r.trancheOutAmt, trancheOutDiscount); - stdTrancheInAmt = stdTrancheOutAmt.mulDiv(trancheOutPrice, trancheInPrice, MathUpgradeable.Rounding.Up); - r.trancheInAmt = _fromStdTrancheAmt(stdTrancheInAmt, trancheInDiscount); - } - - r.perpRolloverAmt = (stdTrancheOutAmt * trancheOutPrice).mulDiv(totalSupply(), _reserveValue()); - r.remainingTrancheInAmt = trancheInAmtAvailable - r.trancheInAmt; - return r; - } - - /// @dev Transfers tokens from the given address to self and updates the reserve set. + /// @dev Transfers tokens from the caller (msg.sender) and updates the reserve set. /// @return Reserve's token balance after transfer in. - function _transferIntoReserve( - address from, - IERC20Upgradeable token, - uint256 trancheAmt - ) private returns (uint256) { - token.safeTransferFrom(from, reserve(), trancheAmt); + function _transferIntoReserve(IERC20Upgradeable token, uint256 trancheAmt) private returns (uint256) { + token.safeTransferFrom(msg.sender, address(this), trancheAmt); return _syncReserve(token); } - /// @dev Transfers tokens from self into the given address and updates the reserve set. + /// @dev Transfers tokens from self into the caller (msg.sender) and updates the reserve set. /// @return Reserve's token balance after transfer out. - function _transferOutOfReserve( - address to, - IERC20Upgradeable token, - uint256 tokenAmt - ) private returns (uint256) { - token.safeTransfer(to, tokenAmt); + function _transferOutOfReserve(IERC20Upgradeable token, uint256 tokenAmt) private returns (uint256) { + token.safeTransfer(msg.sender, tokenAmt); return _syncReserve(token); } /// @dev Keeps the reserve storage up to date. Logs the token balance held by the reserve. /// @return The Reserve's token balance. function _syncReserve(IERC20Upgradeable token) private returns (uint256) { - uint256 balance = _reserveBalance(token); + uint256 balance = token.balanceOf(address(this)); emit ReserveSynced(token, balance); - // If token is the mature tranche, - // it NEVER gets removed from the `_reserves` set. - if (_isMatureTranche(token)) { + // The underlying collateral NEVER gets removed from the `_reserves` set. + if (_isUnderlying(token)) { return balance; } @@ -1018,17 +674,13 @@ contract PerpetualTranche is // Inserts new tranche into reserve set. _reserves.add(address(token)); - // Stores the discount for future usage. - _applyDiscount(token, computeDiscount(token)); - } - - if (balance == 0 && inReserve_) { + if (_reserves.length() > MAX_RESERVE_COUNT) { + revert ReserveCountOverLimit(); + } + } else if (balance <= 0 && inReserve_) { // Removes tranche from reserve set. _reserves.remove(address(token)); - // Frees up stored discount. - _applyDiscount(token, 0); - // Frees up minted supply. delete mintedSupplyPerTranche[ITranche(address(token))]; } @@ -1036,161 +688,218 @@ contract PerpetualTranche is return balance; } - /// @dev Handles fee transfer between the payer, the reserve and the protocol fee collector. - function _settleFee( - address payer, - int256 reserveFee, - uint256 protocolFee - ) private { - // Handling reserve fees - uint256 reserveFeeAbs = reserveFee.abs(); - if (reserveFee > 0) { - _handleFeeTransferIn(payer, reserve(), reserveFeeAbs); - } else if (reserveFee < 0) { - _handleFeeTransferOut(payer, reserveFeeAbs); - } - // Handling protocol fees - if (protocolFee > 0) { - _handleFeeTransferIn(payer, protocolFeeCollector(), protocolFee); + /// @dev Computes the fee adjusted perp mint amount for given amount of tranche tokens deposited into the reserve. + function _computeMintAmt(ITranche trancheIn, uint256 trancheInAmt) private view returns (uint256) { + uint256 valueIn = _computeReserveTrancheValue(trancheIn, _depositBond, _reserveAt(0), trancheInAmt, false); + + //----------------------------------------------------------------------------- + // We charge no mint fee when interacting with other callers within the system. + uint256 feePerc = _isProtocolCaller() ? 0 : feePolicy.computePerpMintFeePerc(); + //----------------------------------------------------------------------------- + + // Compute mint amt + uint256 perpSupply = totalSupply(); + uint256 perpAmtMint = valueIn; + if (perpSupply > 0) { + perpAmtMint = perpAmtMint.mulDiv(perpSupply, _reserveValue()); } - } - /// @dev Transfers fee tokens from the payer to the destination. - function _handleFeeTransferIn( - address payer, - address destination, - uint256 feeAmt - ) private { - IERC20Upgradeable feeToken_ = feeToken(); - bool isNativeFeeToken = (feeToken_ == perpERC20()); - // Funds are coming in - if (isNativeFeeToken) { - // Handling a special case, when the fee is to be charged as the perp token itself - // In this case we don't need to make an external call to the token ERC-20 to "transferFrom" - // the payer, since this is still an internal call {msg.sender} will still point to the payer - // and we can just "transfer" from the payer's wallet. - transfer(destination, feeAmt); - } else { - feeToken_.safeTransferFrom(payer, destination, feeAmt); + // The mint fees are settled by simply minting fewer perps. + if (feePerc > 0) { + perpAmtMint = perpAmtMint.mulDiv(FEE_ONE - feePerc, FEE_ONE); } + + return perpAmtMint; } - /// @dev Transfers fee from the reserve to the destination. - function _handleFeeTransferOut(address destination, uint256 feeAmt) private { - IERC20Upgradeable feeToken_ = feeToken(); - bool isNativeFeeToken = (feeToken_ == perpERC20()); - // Funds are going out - if (isNativeFeeToken) { - uint256 balance = _reserveBalance(feeToken_); - feeToken_.safeTransfer(destination, MathUpgradeable.min(feeAmt, balance)); - - // In case that the reserve's balance doesn't cover the entire fee amount, - // we mint perps to cover the difference. - if (balance < feeAmt) { - _mint(destination, feeAmt - balance); + /// @dev Computes the reserve token amounts redeemed when a given number of perps are burnt. + function _computeRedemptionAmts(uint256 perpAmtBurnt) private view returns (TokenAmount[] memory) { + uint256 perpSupply = totalSupply(); + + //----------------------------------------------------------------------------- + // We charge no burn fee when interacting with other parts of the system. + uint256 feePerc = _isProtocolCaller() ? 0 : feePolicy.computePerpBurnFeePerc(); + //----------------------------------------------------------------------------- + + // Compute redemption amounts + uint8 reserveCount = uint8(_reserves.length()); + TokenAmount[] memory reserveTokens = new TokenAmount[](reserveCount); + for (uint8 i = 0; i < reserveCount; ++i) { + IERC20Upgradeable tokenOut = _reserveAt(i); + reserveTokens[i] = TokenAmount({ + token: tokenOut, + amount: tokenOut.balanceOf(address(this)).mulDiv(perpAmtBurnt, perpSupply) + }); + + // The burn fees are settled by simply redeeming for fewer tranches. + if (feePerc > 0) { + reserveTokens[i].amount = reserveTokens[i].amount.mulDiv(FEE_ONE - feePerc, FEE_ONE); } - } else { - feeToken_.safeTransfer(destination, feeAmt); } + + return (reserveTokens); } - /// @dev Updates contract store with provided discount. - function _applyDiscount(IERC20Upgradeable token, uint256 discount) private { - if (discount > 0) { - _appliedDiscounts[token] = discount; - } else { - delete _appliedDiscounts[token]; + /// @dev Computes the amount of reserve tokens that can be rolled out for the given amount of tranches deposited. + /// The relative ratios of tokens In/Out are adjusted based on the current rollover fee perc. + function _computeRolloverAmt( + ITranche trancheIn, + IERC20Upgradeable tokenOut, + uint256 trancheInAmtAvailable + ) private view returns (RolloverData memory) { + //----------------------------------------------------------------------------- + // The rollover fees are settled by, adjusting the exchange rate + // between `trancheInAmt` and `tokenOutAmt`. + // + int256 feePerc = feePolicy.computePerpRolloverFeePerc( + feePolicy.computeDeviationRatio( + SubscriptionParams({ + perpTVL: _reserveValue(), + vaultTVL: vault.getTVL(), + seniorTR: _depositBond.getSeniorTrancheRatio() + }) + ) + ); + //----------------------------------------------------------------------------- + + // We compute "price" as the value of a unit token. + // The perp, tranche tokens and the underlying are denominated as fixed point numbers + // with the same number of decimals. + IERC20Upgradeable underlying_ = _reserveAt(0); + uint256 unitTokenAmt = (10 ** _decimals); + uint256 trancheInPrice = _computeReserveTrancheValue(trancheIn, _depositBond, underlying_, unitTokenAmt, false); + uint256 tokenOutPrice = unitTokenAmt; + if (tokenOut != underlying_) { + ITranche trancheOut = ITranche(address(tokenOut)); + tokenOutPrice = _computeReserveTrancheValue( + trancheOut, + IBondController(trancheOut.bond()), + underlying_, + unitTokenAmt, + true + ); + } + + uint256 tokenOutBalance = tokenOut.balanceOf(address(this)); + if (trancheInAmtAvailable <= 0 || tokenOutBalance <= 0 || trancheInPrice <= 0 || tokenOutPrice <= 0) { + return RolloverData({ trancheInAmt: 0, tokenOutAmt: 0 }); } - emit DiscountApplied(token, discount); - } + //----------------------------------------------------------------------------- + // Basic rollover with fees: + // (1 +/- f) . (trancheInAmt . trancheInPrice) = (tokenOutAmt . tokenOutPrice) + //----------------------------------------------------------------------------- + + // Using perp's tokenOutBalance, we calculate the amount of tokens in to rollover + // the entire balance. + + RolloverData memory r = RolloverData({ + tokenOutAmt: tokenOutBalance, + trancheInAmt: tokenOutBalance.mulDiv(tokenOutPrice, trancheInPrice, MathUpgradeable.Rounding.Up) + }); + + // A positive fee percentage implies that perp charges rotators by + // offering tranchesOut for a premium, i.e) more tranches in. + if (feePerc > 0) { + r.trancheInAmt = r.trancheInAmt.mulDiv(FEE_ONE, FEE_ONE - feePerc.toUint256(), MathUpgradeable.Rounding.Up); + } + // A negative fee percentage (or a reward) implies that perp pays the rotators by + // offering tranchesOut at a discount, i.e) fewer tranches in. + else if (feePerc < 0) { + r.trancheInAmt = r.trancheInAmt.mulDiv(FEE_ONE, FEE_ONE + feePerc.abs(), MathUpgradeable.Rounding.Up); + } + + //----------------------------------------------------------------------------- + + // When the trancheInAmt exceeds trancheInAmtAvailable: + // we fix trancheInAmt = trancheInAmtAvailable and re-calculate tokenOutAmt + + if (r.trancheInAmt > trancheInAmtAvailable) { + // Given the amount of tranches In, we compute the amount of tokens out + r.trancheInAmt = trancheInAmtAvailable; + r.tokenOutAmt = trancheInAmtAvailable.mulDiv(trancheInPrice, tokenOutPrice); - /// @dev Updates the mature tranche balance in storage. - function _updateMatureTrancheBalance(uint256 matureTrancheBalance) private { - _matureTrancheBalance = matureTrancheBalance; - emit UpdatedMatureTrancheBalance(matureTrancheBalance); + // A positive fee percentage implies that perp charges rotators by + // accepting tranchesIn at a discount, i.e) fewer tokens out. + // This results in perp enrichment. + if (feePerc > 0) { + r.tokenOutAmt = r.tokenOutAmt.mulDiv(FEE_ONE - feePerc.abs(), FEE_ONE); + } + // A negative fee percentage (or a reward) implies that perp pays the rotators by + // accepting tranchesIn at a premium, i.e) more tokens out. + // This results in perp debasement. + else if (feePerc < 0) { + r.tokenOutAmt = r.tokenOutAmt.mulDiv(FEE_ONE + feePerc.abs(), FEE_ONE); + } + } + + return r; } /// @dev Checks if the given token pair is a valid rollover. - /// * When rolling out mature tranche, + /// * When rolling out underlying collateral, /// - expects incoming tranche to be part of the deposit bond /// * When rolling out immature tranches, /// - expects incoming tranche to be part of the deposit bond /// - expects outgoing tranche to NOT be part of the deposit bond, (ie bondIn != bondOut) /// - expects outgoing tranche to be in the reserve - /// - expects outgoing bond to NOT be "acceptable" any more + /// - expects outgoing tranche to be ready for rollout. function _isAcceptableRollover(ITranche trancheIn, IERC20Upgradeable tokenOut) private view returns (bool) { - // when rolling out the mature tranche - if (_isMatureTranche(tokenOut)) { - return _isDepositBondTranche(trancheIn); + // when rolling out the underlying collateral + if (_isUnderlying(tokenOut)) { + return _isDepositTranche(trancheIn); } // when rolling out a normal tranche ITranche trancheOut = ITranche(address(tokenOut)); - IBondController bondOut = IBondController(trancheOut.bond()); - return (_isDepositBondTranche(trancheIn) && - !_isDepositBondTranche(trancheOut) && + return (_isDepositTranche(trancheIn) && _inReserve(trancheOut) && - !_isAcceptableForReserve(bondOut)); + !_isDepositTranche(trancheOut) && + _isTimeForRollout(trancheOut)); } - /// @dev Checks if the bond's tranches can be accepted into the reserve. + /// @dev Checks if the given bond is valid and can be accepted into the reserve. /// * Expects the bond to to have the same collateral token as perp. - /// * Expects the bond's maturity to be within expected bounds. - /// @return True if the bond is "acceptable". - function _isAcceptableForReserve(IBondController bond) private view returns (bool) { + /// * Expects the bond to have only two tranches. + /// * Expects the bond controller to not withhold any fees. + /// * Expects the bond's time to maturity to be within the max safety bound. + /// * Expects the bond's senior and junior tranches to point back to the bond. + /// @return True if the bond is valid. + function _isValidDepositBond(IBondController bond) private view returns (bool) { + return (bond.collateralToken() == address(_reserveAt(0)) && + bond.trancheCount() == 2 && + bond.feeBps() == 0 && + bond.secondsToMaturity() < maxTrancheMaturitySec && + (bond.trancheAt(0)).bond() == address(bond) && + (bond.trancheAt(1)).bond() == address(bond)); + } + + /// @dev Checks if the given tranche's parent bond's time remaining to maturity is less than `minTrancheMaturitySec`. + /// @return True if the tranche can be rolled out of perp. + function _isTimeForRollout(ITranche tranche) private view returns (bool) { // NOTE: `secondsToMaturity` will be 0 if the bond is past maturity. - uint256 secondsToMaturity = bond.secondsToMaturity(); - return (address(_reserveAt(0)) == bond.collateralToken() && - secondsToMaturity >= minTrancheMaturitySec && - secondsToMaturity < maxTrancheMaturitySec); - } - - /// @dev Checks if the given tranche belongs to the current deposit bond. - /// @return True if the deposit bond is the tranche's parent. - function _isDepositBondTranche(ITranche tranche) private view returns (bool) { - return (_depositBond.trancheTokenAddresses(tranche) && address(_depositBond) == tranche.bond()); + return (IBondController(tranche.bond()).secondsToMaturity() <= minTrancheMaturitySec); } - /// @dev Enforces the total supply cap. To be invoked AFTER the mint operation. - function _enforceTotalSupplyCap() private view { - // checks if new total supply is within the max supply cap - uint256 newSupply = totalSupply(); - if (newSupply > maxSupply) { - revert ExceededMaxSupply(newSupply, maxSupply); - } + /// @dev Checks if the given tranche is the most senior tranche of the current deposit bond. + /// @return True if the tranche is the deposit tranche. + function _isDepositTranche(ITranche tranche) private view returns (bool) { + return (_depositBond.getSeniorTranche() == tranche); } - /// @dev Enforces the per tranche supply cap. To be invoked AFTER the mint operation. - function _enforcePerTrancheSupplyCap(ITranche trancheIn) private view { + /// @dev Enforces the total supply and per tranche mint cap. To be invoked AFTER the mint operation. + function _enforceMintCaps(ITranche trancheIn) private view { // checks if supply minted using the given tranche is within the cap if (mintedSupplyPerTranche[trancheIn] > maxMintAmtPerTranche) { - revert ExceededMaxMintPerTranche(trancheIn, mintedSupplyPerTranche[trancheIn], maxMintAmtPerTranche); + revert ExceededMaxMintPerTranche(); } - } - /// @dev Enforces that supply strictly reduces after the redemption. - function _enforceSupplyReduction(uint256 prevSupply) private view { + // checks if new total supply is within the max supply cap uint256 newSupply = totalSupply(); - if (newSupply >= prevSupply) { - revert ExpectedSupplyReduction(newSupply, prevSupply); - } - } - - /// @dev Enforces that the percentage of the reserve value is within the target percentage. - /// To be invoked AFTER the rollover operation. - function _enforceMatureValueTarget() private view { - uint256 matureValue = (_matureTrancheBalance * computePrice(_reserveAt(0))); - uint256 matureValuePerc = matureValue.mulDiv(HUNDRED_PERC, _reserveValue()); - if (matureValuePerc < matureValueTargetPerc) { - revert BelowMatureValueTargetPerc(matureValuePerc, matureValueTargetPerc); + if (newSupply > maxSupply) { + revert ExceededMaxSupply(); } } - /// @dev Counts the number of tokens currently in the reserve. - function _reserveCount() private view returns (uint256) { - return _reserves.length(); - } - /// @dev Fetches the reserve token by index. function _reserveAt(uint256 i) private view returns (IERC20Upgradeable) { return IERC20Upgradeable(_reserves.at(i)); @@ -1202,40 +911,59 @@ contract PerpetualTranche is } /// @dev Calculates the total value of all the tranches in the reserve. - /// Value of each reserve tranche is calculated as = (trancheDiscount . trancheBalance) . tranchePrice. + /// Value of each reserve tranche is denominated in the underlying collateral. function _reserveValue() private view returns (uint256) { - // For the mature tranche we use the "virtual" tranche balance - uint256 totalVal = (_matureTrancheBalance * computePrice(_reserveAt(0))); - - // For normal tranches we use the tranche token balance - for (uint256 i = 1; i < _reserveCount(); i++) { - IERC20Upgradeable token = _reserveAt(i); - uint256 stdTrancheBalance = _toStdTrancheAmt(_reserveBalance(token), computeDiscount(token)); - totalVal += (stdTrancheBalance * computePrice(token)); + IERC20Upgradeable underlying_ = _reserveAt(0); + uint256 totalVal = underlying_.balanceOf(address(this)); + uint8 reserveCount = uint8(_reserves.length()); + for (uint8 i = 1; i < reserveCount; ++i) { + ITranche tranche = ITranche(address(_reserveAt(i))); + IBondController parentBond = IBondController(tranche.bond()); + totalVal += _computeReserveTrancheValue( + tranche, + parentBond, + underlying_, + tranche.balanceOf(address(this)), + true + ); } - return totalVal; } - /// @dev Checks if the given token is the mature tranche, ie) the underlying collateral token. - function _isMatureTranche(IERC20Upgradeable token) private view returns (bool) { - return (token == _reserveAt(0)); - } - - /// @dev Fetches the reserve's token balance. - function _reserveBalance(IERC20Upgradeable token) private view returns (uint256) { - return token.balanceOf(reserve()); + /// @dev Computes the value of the given amount reserve tranche tokens (i.e ones already accepted in the reserve or to be accepted), + /// based on it's current CDR. + /// NOTE: Callers should round up when valuing reserve assets and round down for incoming assets. + function _computeReserveTrancheValue( + ITranche tranche, + IBondController parentBond, + IERC20Upgradeable collateralToken, + uint256 trancheAmt, + bool roundUp + ) private view returns (uint256) { + // NOTE: As an optimization here, we assume that the reserve tranche is immature and has the most senior claim. + uint256 parentBondCollateralBalance = collateralToken.balanceOf(address(parentBond)); + uint256 trancheSupply = tranche.totalSupply(); + uint256 trancheClaim = MathUpgradeable.min(trancheSupply, parentBondCollateralBalance); + // Tranche supply is zero (its parent bond has no deposits yet); + // the tranche's CDR is assumed 1.0. + return + (trancheSupply > 0) + ? trancheClaim.mulDiv( + trancheAmt, + trancheSupply, + roundUp ? MathUpgradeable.Rounding.Up : MathUpgradeable.Rounding.Down + ) + : trancheAmt; } - /// @dev Calculates the standardized tranche amount for internal book keeping. - /// stdTrancheAmt = (trancheAmt * discount). - function _toStdTrancheAmt(uint256 trancheAmt, uint256 discount) private pure returns (uint256) { - return trancheAmt.mulDiv(discount, UNIT_DISCOUNT); + /// @dev Checks if the given token is the underlying collateral token. + function _isUnderlying(IERC20Upgradeable token) private view returns (bool) { + return (token == _reserveAt(0)); } - /// @dev Calculates the external tranche amount from the internal standardized tranche amount. - /// trancheAmt = stdTrancheAmt / discount. - function _fromStdTrancheAmt(uint256 stdTrancheAmt, uint256 discount) private pure returns (uint256) { - return stdTrancheAmt.mulDiv(UNIT_DISCOUNT, discount); + /// @dev Checks if caller is another module within the protocol. + /// If so, we do not charge mint/burn for internal operations. + function _isProtocolCaller() private view returns (bool) { + return (msg.sender == address(vault)); } } diff --git a/spot-contracts/contracts/RolloverVault.sol b/spot-contracts/contracts/RolloverVault.sol new file mode 100644 index 00000000..d166993a --- /dev/null +++ b/spot-contracts/contracts/RolloverVault.sol @@ -0,0 +1,977 @@ +// SPDX-License-Identifier: BUSL-1.1 +pragma solidity ^0.8.20; + +import { IERC20Upgradeable, IPerpetualTranche, IBondController, ITranche, IFeePolicy } from "./_interfaces/IPerpetualTranche.sol"; +import { IVault } from "./_interfaces/IVault.sol"; +import { IRolloverVault } from "./_interfaces/IRolloverVault.sol"; +import { IERC20Burnable } from "./_interfaces/IERC20Burnable.sol"; +import { TokenAmount, RolloverData, SubscriptionParams } from "./_interfaces/CommonTypes.sol"; +import { UnauthorizedCall, UnauthorizedTransferOut, UnexpectedDecimals, UnexpectedAsset, OutOfBounds, UnacceptableSwap, InsufficientDeployment, DeployedCountOverLimit, InvalidPerc, InsufficientLiquidity } from "./_interfaces/ProtocolErrors.sol"; + +import { OwnableUpgradeable } from "@openzeppelin/contracts-upgradeable/access/OwnableUpgradeable.sol"; +import { PausableUpgradeable } from "@openzeppelin/contracts-upgradeable/security/PausableUpgradeable.sol"; +import { ReentrancyGuardUpgradeable } from "@openzeppelin/contracts-upgradeable/security/ReentrancyGuardUpgradeable.sol"; +import { MathUpgradeable } from "@openzeppelin/contracts-upgradeable/utils/math/MathUpgradeable.sol"; +import { ERC20BurnableUpgradeable } from "@openzeppelin/contracts-upgradeable/token/ERC20/extensions/ERC20BurnableUpgradeable.sol"; +import { EnumerableSetUpgradeable } from "@openzeppelin/contracts-upgradeable/utils/structs/EnumerableSetUpgradeable.sol"; +import { SafeERC20Upgradeable } from "@openzeppelin/contracts-upgradeable/token/ERC20/utils/SafeERC20Upgradeable.sol"; +import { BondTranches, BondTranchesHelpers } from "./_utils/BondTranchesHelpers.sol"; +import { TrancheHelpers } from "./_utils/TrancheHelpers.sol"; +import { BondHelpers } from "./_utils/BondHelpers.sol"; +import { PerpHelpers } from "./_utils/PerpHelpers.sol"; + +/* + * @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. + * + * 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. + * 2) recover: The vault redeems the tranches it holds for the underlying asset. + * NOTE: It performs both mature and immature redemption. Read more: https://bit.ly/3tuN6OC + * + * With v2.0, vault provides perp<>underlying swap liquidity and charges a fee. + * The swap fees are an additional source of yield for vault note holders. + * + * @dev When new tranches are added into the system, always double check if they are not malicious + * by only accepting one whitelisted by perp (ones part of perp's deposit bond or ones part of the perp reserve). + * + * We use `_syncAsset` and `_syncDeployedAsset` to keep track of tokens entering and leaving the system. + * When ever a tranche token enters or leaves the system, we immediately invoke `_syncDeployedAsset` to update book-keeping. + * We call `_syncAsset` at the very end of every external function which changes the vault's underlying or perp balance. + * + */ +contract RolloverVault is + ERC20BurnableUpgradeable, + OwnableUpgradeable, + PausableUpgradeable, + ReentrancyGuardUpgradeable, + IRolloverVault +{ + // data handling + using EnumerableSetUpgradeable for EnumerableSetUpgradeable.AddressSet; + using BondHelpers for IBondController; + using TrancheHelpers for ITranche; + using BondTranchesHelpers for BondTranches; + + // ERC20 operations + using SafeERC20Upgradeable for IERC20Upgradeable; + + // math + using MathUpgradeable for uint256; + + //------------------------------------------------------------------------- + // Events + + /// @notice Emits the vault asset's token balance that's recorded after a change. + /// @param token Address of token. + /// @param balance The recorded ERC-20 balance of the token. + event AssetSynced(IERC20Upgradeable token, uint256 balance); + + //------------------------------------------------------------------------- + // Constants + + /// @dev Number of decimals for a multiplier of 1.0x (i.e. 100%) + uint8 public constant FEE_POLICY_DECIMALS = 8; + uint256 public constant FEE_ONE = (10 ** FEE_POLICY_DECIMALS); + + /// @dev Internal percentages are fixed point numbers with {PERC_DECIMALS} places. + uint8 public constant PERC_DECIMALS = 8; + uint256 public constant ONE = (10 ** PERC_DECIMALS); // 1.0 or 100% + + /// @dev Initial exchange rate between the underlying asset and notes. + uint256 private constant INITIAL_RATE = 10 ** 6; + + /// @dev The maximum number of deployed assets that can be held in this vault at any given time. + uint8 public constant MAX_DEPLOYED_COUNT = 47; + + /// @dev Immature redemption may result in some dust tranches when balances are not perfectly divisible by the tranche ratio. + /// Based on current the implementation of `computeRedeemableTrancheAmounts`, + /// the dust balances which remain after immature redemption will be *at most* {TRANCHE_RATIO_GRANULARITY} or 1000. + /// We exclude the vault's dust tranche balances from TVL computation, note redemption and + /// during recovery (through recurrent immature redemption). + uint256 public constant TRANCHE_DUST_AMT = 10000000; + + //-------------------------------------------------------------------------- + // ASSETS + // + // The vault's assets are represented by a master list of ERC-20 tokens + // => { [underlying] U _deployed } + // + // + + /// @notice The ERC20 token that can be deposited into this vault. + IERC20Upgradeable public underlying; + + /// @dev The set of the intermediate ERC-20 tokens when the underlying asset has been put to use. + /// In the case of this vault, they represent the tranche tokens held before maturity. + EnumerableSetUpgradeable.AddressSet private _deployed; + + //------------------------------------------------------------------------- + // Storage + + /// @notice Minimum amount of underlying assets that must be deployed, for a deploy operation to succeed. + /// @dev The deployment transaction reverts, if the vaults does not have sufficient underlying tokens + /// to cover the minimum deployment amount. + uint256 public minDeploymentAmt; + + /// @notice The perpetual token on which rollovers are performed. + IPerpetualTranche public perp; + + //-------------------------------------------------------------------------- + // v2.0.0 STORAGE ADDITION + + /// @notice External contract that orchestrates fees across the spot protocol. + IFeePolicy public feePolicy; + + /// @notice Reference to the address that has the ability to pause/unpause operations. + /// @dev The keeper is meant for time-sensitive operations, and may be different from the owner address. + /// @return The address of the keeper. + address public keeper; + + /// @notice The enforced minimum absolute balance of underlying tokens to be held by the vault. + /// @dev On deployment only the delta greater than this balance is deployed. + /// `minUnderlyingBal` is enforced on deployment and swapping operations which reduce the underlying balance. + /// This parameter ensures that the vault's tvl is never too low, which guards against the "share" manipulation attack. + uint256 public minUnderlyingBal; + + /// @notice The enforced minimum percentage of the vault's value to be held as underlying tokens. + /// @dev The percentage minimum is enforced after swaps which reduce the vault's underlying token liquidity. + /// This ensures that the vault has sufficient liquid underlying tokens for upcoming rollovers. + uint256 public minUnderlyingPerc; + + //-------------------------------------------------------------------------- + // Modifiers + + /// @dev Throws if called by any account other than the keeper. + modifier onlyKeeper() { + if (msg.sender != keeper) { + revert UnauthorizedCall(); + } + _; + } + + //-------------------------------------------------------------------------- + // Construction & Initialization + + /// @custom:oz-upgrades-unsafe-allow constructor + constructor() { + _disableInitializers(); + } + + /// @notice Contract state initialization. + /// @param name ERC-20 Name of the vault token. + /// @param symbol ERC-20 Symbol of the vault token. + /// @param perp_ ERC-20 address of the perpetual tranche rolled over. + /// @param feePolicy_ Address of the fee policy contract. + function init( + string memory name, + string memory symbol, + IPerpetualTranche perp_, + IFeePolicy feePolicy_ + ) external initializer { + // initialize dependencies + __ERC20_init(name, symbol); + __ERC20Burnable_init(); + __Ownable_init(); + __Pausable_init(); + __ReentrancyGuard_init(); + + // setup underlying collateral + underlying = perp_.underlying(); + + // set reference to perp + perp = perp_; + + // set the reference to the fee policy + updateFeePolicy(feePolicy_); + + // set keeper reference + updateKeeper(owner()); + + // setting initial parameter values + minDeploymentAmt = 0; + minUnderlyingBal = 0; + minUnderlyingPerc = ONE / 3; // 33% + + // sync underlying + _syncAsset(underlying); + } + + //-------------------------------------------------------------------------- + // Owner only methods + + /// @notice Update the reference to the fee policy contract. + /// @param feePolicy_ New strategy address. + function updateFeePolicy(IFeePolicy feePolicy_) public onlyOwner { + if (feePolicy_.decimals() != FEE_POLICY_DECIMALS) { + revert UnexpectedDecimals(); + } + feePolicy = feePolicy_; + } + + /// @notice Transfers a non-vault token out of the contract, which may have been added accidentally. + /// @param token The token address. + /// @param to The destination address. + /// @param amount The amount of tokens to be transferred. + function transferERC20(IERC20Upgradeable token, address to, uint256 amount) external onlyOwner nonReentrant { + if (isVaultAsset(token)) { + revert UnauthorizedTransferOut(); + } + token.safeTransfer(to, amount); + } + + /// @notice Updates the reference to the keeper. + /// @param keeper_ The address of the new keeper. + function updateKeeper(address keeper_) public onlyOwner { + keeper = keeper_; + } + + //-------------------------------------------------------------------------- + // Keeper only methods + + /// @notice Pauses deposits, withdrawals and vault operations. + /// @dev NOTE: ERC-20 functions, like transfers will always remain operational. + function pause() external onlyKeeper { + _pause(); + } + + /// @notice Unpauses deposits, withdrawals and vault operations. + /// @dev NOTE: ERC-20 functions, like transfers will always remain operational. + function unpause() external onlyKeeper { + _unpause(); + } + + /// @notice Updates the minimum deployment amount requirement. + /// @param minDeploymentAmt_ The new minimum deployment amount, denominated in underlying tokens. + function updateMinDeploymentAmt(uint256 minDeploymentAmt_) external onlyKeeper { + minDeploymentAmt = minDeploymentAmt_; + } + + /// @notice Updates the minimum underlying balance requirement (Absolute number of underlying tokens). + /// @param minUnderlyingBal_ The new minimum underlying balance. + function updateMinUnderlyingBal(uint256 minUnderlyingBal_) external onlyKeeper { + minUnderlyingBal = minUnderlyingBal_; + } + + /// @notice Updates the minimum underlying percentage requirement (Expressed as a percentage). + /// @param minUnderlyingPerc_ The new minimum underlying percentage. + function updateMinUnderlyingPerc(uint256 minUnderlyingPerc_) external onlyKeeper { + if (minUnderlyingPerc_ > ONE) { + revert InvalidPerc(); + } + minUnderlyingPerc = minUnderlyingPerc_; + } + + //-------------------------------------------------------------------------- + // External & Public write methods + + /// @inheritdoc IVault + /// @dev Simply batches the `recover` and `deploy` functions. Reverts if there are no funds to deploy. + function recoverAndRedeploy() external override { + recover(); + deploy(); + } + + /// @inheritdoc IVault + /// @dev Its safer to call `recover` before `deploy` so the full available balance can be deployed. + /// The vault holds `minUnderlyingBal` as underlying tokens and deploys the rest. + /// Reverts if no funds are rolled over or enforced deployment threshold is not reached. + function deploy() public override nonReentrant whenNotPaused { + IERC20Upgradeable underlying_ = underlying; + IPerpetualTranche perp_ = perp; + + // `minUnderlyingBal` worth of underlying liquidity is excluded from the usable balance + uint256 usableBal = underlying_.balanceOf(address(this)); + if (usableBal <= minUnderlyingBal) { + revert InsufficientLiquidity(); + } + usableBal -= minUnderlyingBal; + + // We ensure that at-least `minDeploymentAmt` amount of underlying tokens are deployed + if (usableBal <= minDeploymentAmt) { + revert InsufficientDeployment(); + } + + // We tranche all the underlying held by the vault to create seniors and juniors + _tranche(perp_.getDepositBond(), underlying_, usableBal); + + // Newly minted seniors are rolled into perp + if (!_rollover(perp_, underlying_)) { + revert InsufficientDeployment(); + } + + // sync underlying + _syncAsset(underlying); + } + + /// @inheritdoc IVault + function recover() public override nonReentrant whenNotPaused { + // Redeem deployed tranches + uint8 deployedCount_ = uint8(_deployed.length()); + if (deployedCount_ <= 0) { + return; + } + + // execute redemption on each deployed asset + for (uint8 i = 0; i < deployedCount_; ++i) { + ITranche tranche = ITranche(_deployed.at(i)); + uint256 trancheBalance = tranche.balanceOf(address(this)); + + // if the vault has no tranche balance, + // we continue to the next one. + if (trancheBalance <= 0) { + continue; + } + + // get the parent bond + IBondController bond = IBondController(tranche.bond()); + BondTranches memory bt = bond.getTranches(); + + // if bond has matured, redeem the tranche token + if (bond.secondsToMaturity() <= 0) { + // execute redemption + _execMatureTrancheRedemption(bond, tranche, trancheBalance); + } + // if not redeem using proportional balances + // redeems this tranche and it's siblings if the vault holds balances. + // NOTE: For gas optimization, we perform this operation only once + // i.e) when we encounter the most-senior tranche. + // We also skip if the tranche balance is too low as immature redemption will be a no-op. + else if (tranche == bt.tranches[0] && trancheBalance > TRANCHE_DUST_AMT) { + // execute redemption + _execImmatureTrancheRedemption(bond, bt); + } + } + + // sync deployed tranches + // 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 (uint8 i = deployedCount_; i > 0; i--) { + _syncDeployedAsset(IERC20Upgradeable(_deployed.at(i - 1))); + } + + // sync underlying + _syncAsset(underlying); + } + + /// @inheritdoc IVault + function recover(IERC20Upgradeable token) public override nonReentrant whenNotPaused { + if (_deployed.contains(address(token))) { + _redeemTranche(ITranche(address(token))); + _syncAsset(underlying); + return; + } + + IPerpetualTranche perp_ = perp; + if (address(token) == address(perp_)) { + // In case the vault holds perp tokens after swaps or if transferred in erroneously, + // anyone can execute this function to recover perps into tranches. + // This is not part of the regular recovery flow. + _meldPerps(perp_); + _syncAsset(perp_); + _syncAsset(underlying); + return; + } + revert UnexpectedAsset(); + } + + /// @inheritdoc IVault + function deposit(uint256 underlyingAmtIn) external override nonReentrant whenNotPaused returns (uint256) { + // Calculates the fee adjusted amount of notes minted when depositing `underlyingAmtIn` of underlying tokens. + // NOTE: This operation should precede any token transfers. + uint256 notes = computeMintAmt(underlyingAmtIn); + if (underlyingAmtIn <= 0 || notes <= 0) { + return 0; + } + + // transfer user assets in + underlying.safeTransferFrom(msg.sender, address(this), underlyingAmtIn); + + // mint notes + _mint(msg.sender, notes); + + // sync underlying + _syncAsset(underlying); + return notes; + } + + /// @inheritdoc IVault + function redeem(uint256 notes) public override nonReentrant whenNotPaused returns (TokenAmount[] memory) { + if (notes <= 0) { + return new TokenAmount[](0); + } + + // Calculates the fee adjusted share of vault tokens to be redeemed + // NOTE: This operation should precede any token transfers. + TokenAmount[] memory redemptions = computeRedemptionAmts(notes); + + // burn notes + _burn(msg.sender, notes); + + // transfer assets out + uint8 redemptionsCount = uint8(redemptions.length); + for (uint8 i = 0; i < redemptionsCount; ++i) { + if (redemptions[i].amount == 0) { + continue; + } + + // Transfer token share out + redemptions[i].token.safeTransfer(msg.sender, redemptions[i].amount); + + // sync balances, wkt i=0 is the underlying and remaining are tranches + if (i == 0) { + _syncAsset(redemptions[i].token); + } else { + _syncDeployedAsset(redemptions[i].token); + } + } + return redemptions; + } + + /// @inheritdoc IVault + function recoverAndRedeem(uint256 notes) external override returns (TokenAmount[] memory) { + recover(); + return redeem(notes); + } + + /// @inheritdoc IRolloverVault + /// @dev Callers should call `recover` before executing `swapUnderlyingForPerps` to maximize vault liquidity. + function swapUnderlyingForPerps(uint256 underlyingAmtIn) external nonReentrant whenNotPaused returns (uint256) { + // Calculates the fee adjusted perp amount to transfer to the user. + // NOTE: This operation should precede any token transfers. + IERC20Upgradeable underlying_ = underlying; + IPerpetualTranche perp_ = perp; + (uint256 perpAmtOut, uint256 perpFeeAmtToBurn, SubscriptionParams memory s) = computeUnderlyingToPerpSwapAmt( + underlyingAmtIn + ); + + // Revert if insufficient tokens are swapped in or out + if (perpAmtOut <= 0 || underlyingAmtIn <= 0) { + revert UnacceptableSwap(); + } + + // transfer underlying in + underlying_.safeTransferFrom(msg.sender, address(this), underlyingAmtIn); + + // tranche and mint perps as needed + _trancheAndMintPerps(perp_, underlying_, s.perpTVL, s.seniorTR, perpAmtOut + perpFeeAmtToBurn); + + // Pay perp's fee share by burning some of the minted perps + if (perpFeeAmtToBurn > 0) { + IERC20Burnable(address(perp_)).burn(perpFeeAmtToBurn); + } + + // transfer remaining perps out to the user + IERC20Upgradeable(address(perp_)).safeTransfer(msg.sender, perpAmtOut); + + // NOTE: In case this operation mints slightly more perps than that are required for the swap, + // The vault continues to hold the perp dust until the subsequent `swapPerpsForUnderlying` or manual `recover(perp)`. + + // Revert if vault liquidity is too low. + _enforceUnderlyingBalAfterSwap(underlying_, s.vaultTVL); + + // sync underlying + _syncAsset(underlying_); + + return perpAmtOut; + } + + /// @inheritdoc IRolloverVault + function swapPerpsForUnderlying(uint256 perpAmtIn) external nonReentrant whenNotPaused returns (uint256) { + // Calculates the fee adjusted underlying amount to transfer to the user. + IPerpetualTranche perp_ = perp; + IERC20Upgradeable underlying_ = underlying; + ( + uint256 underlyingAmtOut, + uint256 perpFeeAmtToBurn, + SubscriptionParams memory s + ) = computePerpToUnderlyingSwapAmt(perpAmtIn); + + // Revert if insufficient tokens are swapped in or out + if (underlyingAmtOut <= 0 || perpAmtIn <= 0) { + revert UnacceptableSwap(); + } + + // transfer perps in + IERC20Upgradeable(perp_).safeTransferFrom(msg.sender, address(this), perpAmtIn); + + // Pay perp's fee share by burning some of the transferred perps + if (perpFeeAmtToBurn > 0) { + IERC20Burnable(address(perp_)).burn(perpFeeAmtToBurn); + } + + // Meld incoming perps + _meldPerps(perp_); + + // transfer underlying out + underlying_.safeTransfer(msg.sender, underlyingAmtOut); + + // Revert if vault liquidity is too low. + _enforceUnderlyingBalAfterSwap(underlying_, s.vaultTVL); + + // sync underlying + _syncAsset(underlying_); + + return underlyingAmtOut; + } + + //-------------------------------------------------------------------------- + // External & Public methods + + /// @inheritdoc IRolloverVault + function computeUnderlyingToPerpSwapAmt( + uint256 underlyingAmtIn + ) public returns (uint256, uint256, SubscriptionParams memory) { + IPerpetualTranche perp_ = perp; + // Compute equal value perps to swap out to the user + SubscriptionParams memory s = _querySubscriptionState(perp_); + uint256 perpAmtOut = underlyingAmtIn.mulDiv(perp_.totalSupply(), s.perpTVL); + + //----------------------------------------------------------------------------- + // When user swaps underlying for vault's perps -> perps are minted by the vault + // We thus compute fees based on the post-mint subscription state. + uint256 perpFeePerc = feePolicy.computePerpMintFeePerc(); + uint256 vaultFeePerc = feePolicy.computeUnderlyingToPerpVaultSwapFeePerc( + feePolicy.computeDeviationRatio(s), + feePolicy.computeDeviationRatio( + SubscriptionParams({ perpTVL: s.perpTVL + underlyingAmtIn, vaultTVL: s.vaultTVL, seniorTR: s.seniorTR }) + ) + ); + //----------------------------------------------------------------------------- + + // Calculate perp fee share to be paid by the vault + uint256 perpFeeAmtToBurn = perpAmtOut.mulDiv(perpFeePerc, FEE_ONE, MathUpgradeable.Rounding.Up); + + // We deduct fees by transferring out fewer perp tokens + perpAmtOut = perpAmtOut.mulDiv(FEE_ONE - (perpFeePerc + vaultFeePerc), FEE_ONE); + + return (perpAmtOut, perpFeeAmtToBurn, s); + } + + /// @inheritdoc IRolloverVault + function computePerpToUnderlyingSwapAmt( + uint256 perpAmtIn + ) public returns (uint256, uint256, SubscriptionParams memory) { + IPerpetualTranche perp_ = perp; + // Compute equal value underlying tokens to swap out + SubscriptionParams memory s = _querySubscriptionState(perp_); + uint256 underlyingAmtOut = perpAmtIn.mulDiv(s.perpTVL, perp_.totalSupply()); + + //----------------------------------------------------------------------------- + // When user swaps perps for vault's underlying -> perps are redeemed by the vault + // We thus compute fees based on the post-burn subscription state. + uint256 perpFeePerc = feePolicy.computePerpBurnFeePerc(); + uint256 vaultFeePerc = feePolicy.computePerpToUnderlyingVaultSwapFeePerc( + feePolicy.computeDeviationRatio(s), + feePolicy.computeDeviationRatio( + SubscriptionParams({ + perpTVL: s.perpTVL - underlyingAmtOut, + vaultTVL: s.vaultTVL, + seniorTR: s.seniorTR + }) + ) + ); + //----------------------------------------------------------------------------- + + // Calculate perp fee share to be paid by the vault + uint256 perpFeeAmtToBurn = perpAmtIn.mulDiv(perpFeePerc, FEE_ONE, MathUpgradeable.Rounding.Up); + + // We deduct fees by transferring out fewer underlying tokens + underlyingAmtOut = underlyingAmtOut.mulDiv(FEE_ONE - (perpFeePerc + vaultFeePerc), FEE_ONE); + + return (underlyingAmtOut, perpFeeAmtToBurn, s); + } + + //-------------------------------------------------------------------------- + // External & Public read methods + + /// @inheritdoc IVault + function computeMintAmt(uint256 underlyingAmtIn) public view returns (uint256) { + //----------------------------------------------------------------------------- + uint256 feePerc = feePolicy.computeVaultMintFeePerc(); + //----------------------------------------------------------------------------- + + // Compute mint amt + uint256 noteSupply = totalSupply(); + uint256 notes = (noteSupply > 0) + ? noteSupply.mulDiv(underlyingAmtIn, getTVL()) + : (underlyingAmtIn * INITIAL_RATE); + + // The mint fees are settled by simply minting fewer vault notes. + notes = notes.mulDiv(FEE_ONE - feePerc, FEE_ONE); + return notes; + } + + /// @inheritdoc IVault + function computeRedemptionAmts(uint256 noteAmtBurnt) public view returns (TokenAmount[] memory) { + uint256 noteSupply = totalSupply(); + + //----------------------------------------------------------------------------- + uint256 feePerc = feePolicy.computeVaultBurnFeePerc(); + //----------------------------------------------------------------------------- + uint8 assetCount_ = 1 + uint8(_deployed.length()); + + // aggregating vault assets to be redeemed + TokenAmount[] memory redemptions = new TokenAmount[](assetCount_); + + // underlying share to be redeemed + IERC20Upgradeable underlying_ = underlying; + redemptions[0] = TokenAmount({ + token: underlying_, + amount: underlying_.balanceOf(address(this)).mulDiv(noteAmtBurnt, noteSupply) + }); + redemptions[0].amount = redemptions[0].amount.mulDiv(FEE_ONE - feePerc, FEE_ONE); + + for (uint8 i = 1; i < assetCount_; ++i) { + // tranche token share to be redeemed + IERC20Upgradeable token = IERC20Upgradeable(_deployed.at(i - 1)); + redemptions[i] = TokenAmount({ + token: token, + amount: token.balanceOf(address(this)).mulDiv(noteAmtBurnt, noteSupply) + }); + + // deduct redemption fee + redemptions[i].amount = redemptions[i].amount.mulDiv(FEE_ONE - feePerc, FEE_ONE); + + // in case the redemption amount is just dust, we skip + if (redemptions[i].amount < TRANCHE_DUST_AMT) { + redemptions[i].amount = 0; + } + } + + return redemptions; + } + + /// @inheritdoc IVault + /// @dev The total value is denominated in the underlying asset. + function getTVL() public view override returns (uint256) { + // The underlying balance + uint256 totalValue = underlying.balanceOf(address(this)); + + // The deployed asset value denominated in the underlying + uint8 deployedCount_ = uint8(_deployed.length()); + for (uint8 i = 0; i < deployedCount_; ++i) { + ITranche tranche = ITranche(_deployed.at(i)); + uint256 balance = tranche.balanceOf(address(this)); + if (balance > TRANCHE_DUST_AMT) { + totalValue += _computeVaultTrancheValue(tranche, underlying, balance); + } + } + + return totalValue; + } + + /// @inheritdoc IVault + /// @dev The asset value is denominated in the underlying asset. + function getVaultAssetValue(IERC20Upgradeable token) external view override returns (uint256) { + uint256 balance = token.balanceOf(address(this)); + + // Underlying asset + if (token == underlying) { + return balance; + } + // Deployed asset + else if (_deployed.contains(address(token))) { + ITranche tranche = ITranche(address(token)); + return (balance > TRANCHE_DUST_AMT) ? _computeVaultTrancheValue(tranche, underlying, balance) : 0; + } + + // Not a vault asset, so returning zero + return 0; + } + + /// @inheritdoc IVault + function assetCount() external view override returns (uint256) { + return _deployed.length() + 1; + } + + /// @inheritdoc IVault + function assetAt(uint256 i) external view override returns (IERC20Upgradeable) { + if (i == 0) { + return underlying; + } else if (i <= _deployed.length()) { + return IERC20Upgradeable(_deployed.at(i - 1)); + } + revert OutOfBounds(); + } + + /// @inheritdoc IVault + function vaultAssetBalance(IERC20Upgradeable token) external view override returns (uint256) { + return isVaultAsset(token) ? token.balanceOf(address(this)) : 0; + } + + /// @inheritdoc IVault + function isVaultAsset(IERC20Upgradeable token) public view override returns (bool) { + return token == underlying || _deployed.contains(address(token)); + } + + //-------------------------------------------------------------------------- + // Private write methods + + /// @dev Redeems tranche tokens held by the vault, for underlying. + /// In the case of immature redemption, this method will recover other sibling tranches as well. + /// Performs some book-keeping to keep track of the vault's assets. + function _redeemTranche(ITranche tranche) private { + uint256 trancheBalance = tranche.balanceOf(address(this)); + + // if the vault has no tranche balance, + // we update our internal book-keeping and return. + if (trancheBalance <= 0) { + _syncDeployedAsset(tranche); + return; + } + + // get the parent bond + IBondController bond = IBondController(tranche.bond()); + + // if bond has matured, redeem the tranche token + if (bond.secondsToMaturity() <= 0) { + // execute redemption + _execMatureTrancheRedemption(bond, tranche, trancheBalance); + + // sync deployed asset + _syncDeployedAsset(tranche); + } + // if not redeem using proportional balances + // redeems this tranche and it's siblings if the vault holds balances. + // We skip if the tranche balance is too low as immature redemption will be a no-op. + else if (trancheBalance > TRANCHE_DUST_AMT) { + // execute redemption + BondTranches memory bt = bond.getTranches(); + _execImmatureTrancheRedemption(bond, bt); + + // sync deployed asset, i.e) current tranche and its sibling. + _syncDeployedAsset(bt.tranches[0]); + _syncDeployedAsset(bt.tranches[1]); + } else { + _syncDeployedAsset(tranche); + } + } + + /// @dev Redeems perp tokens held by the vault for tranches and + /// melds them with existing tranches to redeem more underlying tokens. + /// Performs some book-keeping to keep track of the vault's assets. + function _meldPerps(IPerpetualTranche perp_) private { + uint256 perpBalance = perp_.balanceOf(address(this)); + if (perpBalance <= 0) { + return; + } + + TokenAmount[] memory tranchesRedeemed = perp_.redeem(perpBalance); + + // sync and meld perp's tranches + uint8 tranchesRedeemedCount = uint8(tranchesRedeemed.length); + for (uint8 i = 1; i < tranchesRedeemedCount; ++i) { + ITranche tranche = ITranche(address(tranchesRedeemed[i].token)); + + // if possible, meld redeemed tranche with + // existing tranches to redeem underlying. + _redeemTranche(tranche); + } + } + + /// @dev Tranches the vault's underlying to mint perps.. + /// Performs some book-keeping to keep track of the vault's assets. + function _trancheAndMintPerps( + IPerpetualTranche perp_, + IERC20Upgradeable underlying_, + uint256 perpTVL, + uint256 seniorTR, + uint256 perpAmtToMint + ) private { + // Tranche as needed + IBondController depositBond = perp_.getDepositBond(); + ITranche trancheIntoPerp = perp_.getDepositTranche(); + (uint256 underylingAmtToTranche, uint256 seniorAmtToDeposit) = PerpHelpers.estimateUnderlyingAmtToTranche( + PerpHelpers.MintEstimationParams({ + perpTVL: perpTVL, + perpSupply: perp_.totalSupply(), + depositBondCollateralBalance: underlying_.balanceOf(address(depositBond)), + depositBondTotalDebt: depositBond.totalDebt(), + depositTrancheSupply: trancheIntoPerp.totalSupply(), + depositTrancheTR: seniorTR + }), + perpAmtToMint + ); + _tranche(depositBond, underlying_, underylingAmtToTranche); + + // Mint perps + _checkAndApproveMax(trancheIntoPerp, address(perp_), seniorAmtToDeposit); + perp_.deposit(trancheIntoPerp, seniorAmtToDeposit); + + // sync holdings + _syncDeployedAsset(trancheIntoPerp); + } + + /// @dev Given a bond and its tranche data, deposits the provided amount into the bond + /// and receives tranche tokens in return. + /// Performs some book-keeping to keep track of the vault's assets. + function _tranche(IBondController bond, IERC20Upgradeable underlying_, uint256 underlyingAmt) private { + // Get bond tranches + BondTranches memory bt = bond.getTranches(); + + // amount is tranched + _checkAndApproveMax(underlying_, address(bond), underlyingAmt); + bond.deposit(underlyingAmt); + + // sync holdings + _syncDeployedAsset(bt.tranches[0]); + _syncDeployedAsset(bt.tranches[1]); + } + + /// @dev Rolls over freshly tranched tokens from the given bond for older tranches (close to maturity) from perp. + /// Redeems intermediate tranches for underlying if possible. + /// Performs some book-keeping to keep track of the vault's assets. + /// @return Flag indicating if any tokens were rolled over. + function _rollover(IPerpetualTranche perp_, IERC20Upgradeable underlying_) private returns (bool) { + // NOTE: The first element of the list is the mature tranche, + // there after the list is NOT ordered by maturity. + IERC20Upgradeable[] memory rolloverTokens = perp_.getReserveTokensUpForRollover(); + + // Batch rollover + bool rollover = false; + + // We query perp's current deposit tranche + ITranche trancheIntoPerp = perp_.getDepositTranche(); + + // Compute available tranche in to rollover + uint256 trancheInAmtAvailable = trancheIntoPerp.balanceOf(address(this)); + + // Approve once for all rollovers + _checkAndApproveMax(trancheIntoPerp, address(perp_), trancheInAmtAvailable); + + // We pair the senior tranche token held by the vault (from the deposit bond) + // with each of the perp's tokens available for rollovers and execute a rollover. + // We continue to rollover till either the vault's senior tranche balance is exhausted or + // there are no more tokens in perp available to be rolled-over. + uint8 rolloverTokensCount = uint8(rolloverTokens.length); + for (uint8 i = 0; (i < rolloverTokensCount && trancheInAmtAvailable > 0); ++i) { + // tokenOutOfPerp is the reserve token coming out of perp into the vault + IERC20Upgradeable tokenOutOfPerp = rolloverTokens[i]; + + // Perform rollover + RolloverData memory r = perp_.rollover(trancheIntoPerp, tokenOutOfPerp, trancheInAmtAvailable); + + // no rollover occurred, skip updating balances + if (r.tokenOutAmt <= 0) { + continue; + } + + // skip insertion into the deployed list the case of the mature tranche, ie underlying + if (rolloverTokens[i] != underlying_) { + // Clean up after rollover, merge seniors from perp + // with vault held juniors to recover more underlying. + _redeemTranche(ITranche(address(tokenOutOfPerp))); + } + + // Calculate trancheIn available amount + trancheInAmtAvailable -= r.trancheInAmt; + + // keep track if "at least" one rolled over operation occurred + rollover = true; + } + + // Final cleanup, if there remain excess seniors we recover back to underlying. + _redeemTranche(trancheIntoPerp); + + return (rollover); + } + + /// @dev Low level method that redeems the given mature tranche for the underlying asset. + /// It interacts with the button-wood bond contract. + /// This function should NOT be called directly, use `recover()` or `_redeemTranche(tranche)` + /// which wrap this function with the internal book-keeping necessary, + /// to keep track of the vault's assets. + function _execMatureTrancheRedemption(IBondController bond, ITranche tranche, uint256 amount) private { + if (!bond.isMature()) { + bond.mature(); + } + bond.redeemMature(address(tranche), amount); + } + + /// @dev Low level method that redeems the given tranche for the underlying asset, before maturity. + /// If the vault holds sibling tranches with proportional balances, those will also get redeemed. + /// It interacts with the button-wood bond contract. + /// This function should NOT be called directly, use `recover()` or `recover(tranche)` + /// which wrap this function with the internal book-keeping necessary, + /// to keep track of the vault's assets. + function _execImmatureTrancheRedemption(IBondController bond, BondTranches memory bt) private { + uint256[] memory trancheAmts = bt.computeRedeemableTrancheAmounts(address(this)); + + // NOTE: It is guaranteed that if one tranche amount is zero, all amounts are zeros. + if (trancheAmts[0] > 0) { + bond.redeem(trancheAmts); + } + } + + /// @dev Syncs balance and updates the deployed list based on the vault's token balance. + function _syncDeployedAsset(IERC20Upgradeable token) private { + uint256 balance = token.balanceOf(address(this)); + emit AssetSynced(token, balance); + + bool inVault = _deployed.contains(address(token)); + if (balance > 0 && !inVault) { + // Inserts new token into the deployed assets list. + _deployed.add(address(token)); + if (_deployed.length() > MAX_DEPLOYED_COUNT) { + revert DeployedCountOverLimit(); + } + } else if (balance <= 0 && inVault) { + // Removes token into the deployed assets list. + _deployed.remove(address(token)); + } + } + + /// @dev Logs the token balance held by the vault. + function _syncAsset(IERC20Upgradeable token) private { + emit AssetSynced(token, token.balanceOf(address(this))); + } + + /// @dev Checks if the spender has sufficient allowance. If not, approves the maximum possible amount. + function _checkAndApproveMax(IERC20Upgradeable token, address spender, uint256 amount) private { + uint256 allowance = token.allowance(address(this), spender); + if (allowance < amount) { + token.safeApprove(spender, type(uint256).max); + } + } + + /// @dev Queries the current subscription state of the perp and vault systems. + function _querySubscriptionState(IPerpetualTranche perp_) private returns (SubscriptionParams memory) { + return + SubscriptionParams({ + perpTVL: perp_.getTVL(), + vaultTVL: getTVL(), + seniorTR: perp_.getDepositTrancheRatio() + }); + } + + //-------------------------------------------------------------------------- + // Private methods + + /// @dev Computes the value of the given amount of tranche tokens, based on it's current CDR. + /// Value is denominated in the underlying collateral. + function _computeVaultTrancheValue( + ITranche tranche, + IERC20Upgradeable collateralToken, + uint256 trancheAmt + ) private view returns (uint256) { + (uint256 trancheClaim, uint256 trancheSupply) = tranche.getTrancheCollateralization(collateralToken); + return trancheClaim.mulDiv(trancheAmt, trancheSupply, MathUpgradeable.Rounding.Up); + } + + /// @dev Checks if the vault's underlying balance is above admin defined constraints. + /// - Absolute balance is strictly greater than `minUnderlyingBal`. + /// - Ratio of the balance to the vault's TVL is strictly greater than `minUnderlyingPerc`. + /// NOTE: We assume the vault TVL and the underlying to have the same base denomination. + function _enforceUnderlyingBalAfterSwap(IERC20Upgradeable underlying_, uint256 vaultTVL) private view { + uint256 underlyingBal = underlying_.balanceOf(address(this)); + if (underlyingBal <= minUnderlyingBal || underlyingBal.mulDiv(ONE, vaultTVL) <= minUnderlyingPerc) { + revert InsufficientLiquidity(); + } + } +} diff --git a/spot-contracts/contracts/RouterV1.sol b/spot-contracts/contracts/RouterV1.sol deleted file mode 100644 index a2857a3c..00000000 --- a/spot-contracts/contracts/RouterV1.sol +++ /dev/null @@ -1,311 +0,0 @@ -// SPDX-License-Identifier: BUSL-1.1 -pragma solidity ^0.8.19; - -import { IERC20Upgradeable } from "@openzeppelin/contracts-upgradeable/token/ERC20/IERC20Upgradeable.sol"; -import { ITranche } from "./_interfaces/buttonwood/ITranche.sol"; -import { IBondController } from "./_interfaces/buttonwood/IBondController.sol"; -import { IPerpetualTranche } from "./_interfaces/IPerpetualTranche.sol"; - -import { SafeERC20Upgradeable } from "@openzeppelin/contracts-upgradeable/token/ERC20/utils/SafeERC20Upgradeable.sol"; -import { SafeCastUpgradeable } from "@openzeppelin/contracts-upgradeable/utils/math/SafeCastUpgradeable.sol"; -import { BondTranches, BondTranchesHelpers, BondHelpers } from "./_utils/BondHelpers.sol"; - -/** - * @title RouterV1 - * - * @notice Contract to dry-run and batch multiple operations. - * - */ -contract RouterV1 { - // math - using SafeCastUpgradeable for uint256; - - // data handling - using BondHelpers for IBondController; - using BondTranchesHelpers for BondTranches; - - // ERC20 operations - using SafeERC20Upgradeable for IERC20Upgradeable; - using SafeERC20Upgradeable for ITranche; - using SafeERC20Upgradeable for IPerpetualTranche; - - modifier afterPerpStateUpdate(IPerpetualTranche perp) { - perp.updateState(); - _; - } - - /// @notice Calculates the amount of tranche tokens minted after depositing into the deposit bond. - /// @dev Used by off-chain services to preview a tranche operation. - /// @param perp Address of the perp contract. - /// @param collateralAmount The amount of collateral the user wants to tranche. - /// @return bond The address of the current deposit bond. - /// @return trancheAmts The tranche token amounts minted. - function previewTranche(IPerpetualTranche perp, uint256 collateralAmount) - external - afterPerpStateUpdate(perp) - returns ( - IBondController, - ITranche[] memory, - uint256[] memory - ) - { - IBondController bond = perp.getDepositBond(); - - BondTranches memory bt; - uint256[] memory trancheAmts; - (bt, trancheAmts, ) = bond.previewDeposit(collateralAmount); - - return (bond, bt.tranches, trancheAmts); - } - - /// @notice Calculates the amount of perp tokens minted and fees for the operation. - /// @dev Used by off-chain services to preview a deposit operation. - /// @param perp Address of the perp contract. - /// @param trancheIn The address of the tranche token to be deposited. - /// @param trancheInAmt The amount of tranche tokens deposited. - /// @return mintAmt The amount of perp tokens minted. - /// @return feeToken The address of the fee token. - /// @return mintFee The fee charged for minting. - function previewDeposit( - IPerpetualTranche perp, - ITranche trancheIn, - uint256 trancheInAmt - ) - external - afterPerpStateUpdate(perp) - returns ( - uint256, - IERC20Upgradeable, - int256 - ) - { - uint256 mintAmt = perp.computeMintAmt(trancheIn, trancheInAmt); - IERC20Upgradeable feeToken = perp.feeToken(); - (int256 reserveFee, uint256 protocolFee) = perp.feeStrategy().computeMintFees(mintAmt); - int256 mintFee = reserveFee + protocolFee.toInt256(); - return (mintAmt, feeToken, mintFee); - } - - /// @notice Tranches the collateral using the current deposit bond and then deposits individual tranches - /// to mint perp tokens. It transfers the perp tokens back to the - /// transaction sender along with any unused tranches and fees. - /// @param perp Address of the perp contract. - /// @param bond Address of the deposit bond. - /// @param collateralAmount The amount of collateral the user wants to tranche. - /// @param feePaid The fee paid to the perp contract to mint perp when the fee token is not the perp token itself, otherwise 0. - /// @dev Fee to be paid should be pre-computed off-chain using the preview function. - function trancheAndDeposit( - IPerpetualTranche perp, - IBondController bond, - uint256 collateralAmount, - uint256 feePaid - ) external afterPerpStateUpdate(perp) { - BondTranches memory bt = bond.getTranches(); - IERC20Upgradeable collateralToken = IERC20Upgradeable(bond.collateralToken()); - IERC20Upgradeable feeToken = perp.feeToken(); - - // transfers collateral & fees to router - collateralToken.safeTransferFrom(msg.sender, address(this), collateralAmount); - if (feePaid > 0) { - feeToken.safeTransferFrom(msg.sender, address(this), feePaid); - } - - // approves collateral to be tranched - _checkAndApproveMax(collateralToken, address(bond), collateralAmount); - - // tranches collateral - bond.deposit(collateralAmount); - - // approves fee to be spent to mint perp tokens - _checkAndApproveMax(feeToken, address(perp), feePaid); - - for (uint8 i = 0; i < bt.tranches.length; i++) { - uint256 trancheAmt = bt.tranches[i].balanceOf(address(this)); - uint256 mintAmt = perp.computeMintAmt(bt.tranches[i], trancheAmt); - if (mintAmt > 0) { - // approves tranches to be spent - _checkAndApproveMax(bt.tranches[i], address(perp), trancheAmt); - - // mints perp tokens using tranches - perp.deposit(bt.tranches[i], trancheAmt); - } else { - // transfers unused tranches back - bt.tranches[i].safeTransfer(msg.sender, trancheAmt); - } - } - - // transfers any remaining collateral tokens back - uint256 collateralBalance = collateralToken.balanceOf(address(this)); - if (collateralBalance > 0) { - collateralToken.safeTransfer(msg.sender, collateralBalance); - } - - // transfers remaining fee back if overpaid or reward - uint256 feeBalance = feeToken.balanceOf(address(this)); - if (feeBalance > 0) { - feeToken.safeTransfer(msg.sender, feeBalance); - } - - // transfers perp tokens back - perp.safeTransfer(msg.sender, perp.balanceOf(address(this))); - } - - /// @notice Calculates the reserve tokens that can be redeemed from the queue - /// for burning up to the requested amount of perp tokens. - /// @dev Used by off-chain services to preview a redeem operation. - /// @param perp Address of the perp contract. - /// @param perpAmtBurnt The amount of perp tokens requested to be burnt. - /// @return reserveTokens The list of reserve tokens redeemed. - /// @return redemptionAmts The list of reserve token amounts redeemed. - /// @return feeToken The address of the fee token. - /// @return burnFee The fee charged for burning. - function previewRedeem(IPerpetualTranche perp, uint256 perpAmtBurnt) - external - afterPerpStateUpdate(perp) - returns ( - IERC20Upgradeable[] memory, - uint256[] memory, - IERC20Upgradeable, - int256 - ) - { - (IERC20Upgradeable[] memory reserveTokens, uint256[] memory redemptionAmts) = perp.computeRedemptionAmts( - perpAmtBurnt - ); - (int256 reserveFee, uint256 protocolFee) = perp.feeStrategy().computeBurnFees(perpAmtBurnt); - int256 burnFee = reserveFee + protocolFee.toInt256(); - IERC20Upgradeable feeToken = perp.feeToken(); - return (reserveTokens, redemptionAmts, feeToken, burnFee); - } - - /// @notice Calculates the amount tranche tokens that can be rolled out, remainders and fees, - /// with a given tranche token rolled in and amount. - /// @dev Used by off-chain services to preview a rollover operation. - /// @param perp Address of the perp contract. - /// @param trancheIn The tranche token deposited. - /// @param tokenOut The reserve token requested to be withdrawn. - /// @param trancheInAmtRequested The amount of trancheIn tokens available to deposit. - /// @param maxTokenOutAmtUsed The token balance to be used for rollover. - /// @dev Set maxTokenOutAmtUsed to max(uint256) to use the entire balance. - /// @return r The amounts rolled over and remaining. - /// @return feeToken The address of the fee token. - /// @return rolloverFee The fee paid by the caller. - function previewRollover( - IPerpetualTranche perp, - ITranche trancheIn, - IERC20Upgradeable tokenOut, - uint256 trancheInAmtRequested, - uint256 maxTokenOutAmtUsed - ) - external - afterPerpStateUpdate(perp) - returns ( - IPerpetualTranche.RolloverPreview memory, - IERC20Upgradeable, - int256 - ) - { - IPerpetualTranche.RolloverPreview memory r; - r.remainingTrancheInAmt = trancheInAmtRequested; - - IERC20Upgradeable feeToken = perp.feeToken(); - int256 reserveFee = 0; - uint256 protocolFee = 0; - if (perp.isAcceptableRollover(trancheIn, tokenOut)) { - r = perp.computeRolloverAmt(trancheIn, tokenOut, trancheInAmtRequested, maxTokenOutAmtUsed); - (reserveFee, protocolFee) = perp.feeStrategy().computeRolloverFees(r.perpRolloverAmt); - } - int256 rolloverFee = reserveFee + protocolFee.toInt256(); - return (r, feeToken, rolloverFee); - } - - struct RolloverBatch { - ITranche trancheIn; - IERC20Upgradeable tokenOut; - uint256 trancheInAmt; - } - - /// @notice Tranches collateral and performs a batch rollover. - /// @param perp Address of the perp contract. - /// @param bond Address of the deposit bond. - /// @param collateralAmount The amount of collateral the user wants to tranche. - /// @param rollovers List of batch rollover operations pre-computed off-chain. - /// @param feePaid The fee paid by the user performing rollover (fee could be negative). - function trancheAndRollover( - IPerpetualTranche perp, - IBondController bond, - uint256 collateralAmount, - RolloverBatch[] calldata rollovers, - uint256 feePaid - ) external afterPerpStateUpdate(perp) { - BondTranches memory bt = bond.getTranches(); - IERC20Upgradeable collateralToken = IERC20Upgradeable(bond.collateralToken()); - IERC20Upgradeable feeToken = perp.feeToken(); - - // transfers collateral & fees to router - collateralToken.safeTransferFrom(msg.sender, address(this), collateralAmount); - if (feePaid > 0) { - feeToken.safeTransferFrom(msg.sender, address(this), feePaid); - } - - // approves collateral to be tranched - _checkAndApproveMax(collateralToken, address(bond), collateralAmount); - - // tranches collateral - bond.deposit(collateralAmount); - - // approves fee to be spent to rollover - if (feePaid > 0) { - _checkAndApproveMax(feeToken, address(perp), feePaid); - } - - for (uint256 i = 0; i < rollovers.length; i++) { - // approve trancheIn to be spent by perp - _checkAndApproveMax(rollovers[i].trancheIn, address(perp), rollovers[i].trancheInAmt); - - // perform rollover - perp.rollover(rollovers[i].trancheIn, rollovers[i].tokenOut, rollovers[i].trancheInAmt); - } - - for (uint256 i = 0; i < rollovers.length; i++) { - // transfer remaining tokenOut tokens back - uint256 tokenOutBalance = rollovers[i].tokenOut.balanceOf(address(this)); - if (tokenOutBalance > 0) { - rollovers[i].tokenOut.safeTransfer(msg.sender, tokenOutBalance); - } - } - - // transfers unused tranches back - for (uint8 i = 0; i < bt.tranches.length; i++) { - uint256 trancheBalance = bt.tranches[i].balanceOf(address(this)); - if (trancheBalance > 0) { - bt.tranches[i].safeTransfer(msg.sender, trancheBalance); - } - } - - // transfers any remaining collateral tokens back - uint256 collateralBalance = collateralToken.balanceOf(address(this)); - if (collateralBalance > 0) { - collateralToken.safeTransfer(msg.sender, collateralBalance); - } - - // transfers remaining fee back if overpaid or reward - uint256 feeBalance = feeToken.balanceOf(address(this)); - if (feeBalance > 0) { - feeToken.safeTransfer(msg.sender, feeBalance); - } - } - - /// @dev Checks if the spender has sufficient allowance. If not, approves the maximum possible amount. - function _checkAndApproveMax( - IERC20Upgradeable token, - address spender, - uint256 amount - ) private { - uint256 allowance = token.allowance(address(this), spender); - if (allowance < amount) { - token.safeApprove(spender, 0); - token.safeApprove(spender, type(uint256).max); - } - } -} diff --git a/spot-contracts/contracts/RouterV2.sol b/spot-contracts/contracts/RouterV2.sol new file mode 100644 index 00000000..a81ea85e --- /dev/null +++ b/spot-contracts/contracts/RouterV2.sol @@ -0,0 +1,97 @@ +// SPDX-License-Identifier: BUSL-1.1 +pragma solidity ^0.8.20; + +import { IERC20Upgradeable } from "@openzeppelin/contracts-upgradeable/token/ERC20/IERC20Upgradeable.sol"; +import { ITranche } from "./_interfaces/buttonwood/ITranche.sol"; +import { IBondController } from "./_interfaces/buttonwood/IBondController.sol"; +import { IPerpetualTranche } from "./_interfaces/IPerpetualTranche.sol"; +import { TokenAmount } from "./_interfaces/CommonTypes.sol"; + +import { SafeERC20Upgradeable } from "@openzeppelin/contracts-upgradeable/token/ERC20/utils/SafeERC20Upgradeable.sol"; +import { SafeCastUpgradeable } from "@openzeppelin/contracts-upgradeable/utils/math/SafeCastUpgradeable.sol"; +import { BondTranches, BondTranchesHelpers } from "./_utils/BondTranchesHelpers.sol"; +import { BondHelpers } from "./_utils/BondHelpers.sol"; + +/** + * @title RouterV2 + * + * @notice Contract to dry-run and batch multiple operations. + * + */ +contract RouterV2 { + // math + using SafeCastUpgradeable for uint256; + + // data handling + using BondHelpers for IBondController; + using BondTranchesHelpers for BondTranches; + + // ERC20 operations + using SafeERC20Upgradeable for IERC20Upgradeable; + using SafeERC20Upgradeable for ITranche; + using SafeERC20Upgradeable for IPerpetualTranche; + + /// @notice Calculates the amount of tranche tokens minted after depositing into the deposit bond. + /// @dev Used by off-chain services to preview a tranche operation. + /// @param perp Address of the perp contract. + /// @param collateralAmount The amount of collateral the user wants to tranche. + /// @return bond The address of the current deposit bond. + /// @return trancheAmts The tranche tokens and amounts minted. + function previewTranche( + IPerpetualTranche perp, + uint256 collateralAmount + ) external returns (IBondController, TokenAmount[] memory) { + IBondController bond = perp.getDepositBond(); + return (bond, bond.previewDeposit(collateralAmount)); + } + + /// @notice Tranches the collateral using the current deposit bond and then deposits individual tranches + /// to mint perp tokens. It transfers the perp tokens back to the + /// transaction sender along with any unused tranches and fees. + /// @param perp Address of the perp contract. + /// @param bond Address of the deposit bond. + /// @param collateralAmount The amount of collateral the user wants to tranche. + function trancheAndDeposit(IPerpetualTranche perp, IBondController bond, uint256 collateralAmount) external { + // If deposit bond does not exist, we first issue it. + if (address(bond).code.length <= 0) { + perp.updateState(); + } + + BondTranches memory bt = bond.getTranches(); + IERC20Upgradeable collateralToken = IERC20Upgradeable(bond.collateralToken()); + + // transfers collateral & fees to router + collateralToken.safeTransferFrom(msg.sender, address(this), collateralAmount); + + // approves collateral to be tranched + _checkAndApproveMax(collateralToken, address(bond), collateralAmount); + + // tranches collateral + bond.deposit(collateralAmount); + + // uses senior tranches to mint perps + uint256 trancheAmt = bt.tranches[0].balanceOf(address(this)); + _checkAndApproveMax(bt.tranches[0], address(perp), trancheAmt); + perp.deposit(bt.tranches[0], trancheAmt); + + // transfers remaining junior tranches back + bt.tranches[1].safeTransfer(msg.sender, bt.tranches[1].balanceOf(address(this))); + + // transfers any remaining collateral tokens back + uint256 collateralBalance = collateralToken.balanceOf(address(this)); + if (collateralBalance > 0) { + collateralToken.safeTransfer(msg.sender, collateralBalance); + } + + // transfers perp tokens back + perp.safeTransfer(msg.sender, perp.balanceOf(address(this))); + } + + /// @dev Checks if the spender has sufficient allowance. If not, approves the maximum possible amount. + function _checkAndApproveMax(IERC20Upgradeable token, address spender, uint256 amount) private { + uint256 allowance = token.allowance(address(this), spender); + if (allowance < amount) { + token.safeApprove(spender, type(uint256).max); + } + } +} diff --git a/spot-contracts/contracts/_interfaces/CommonTypes.sol b/spot-contracts/contracts/_interfaces/CommonTypes.sol new file mode 100644 index 00000000..52fbacf4 --- /dev/null +++ b/spot-contracts/contracts/_interfaces/CommonTypes.sol @@ -0,0 +1,28 @@ +// SPDX-License-Identifier: BUSL-1.1 +pragma solidity ^0.8.0; + +import { IERC20Upgradeable } from "@openzeppelin/contracts-upgradeable/token/ERC20/IERC20Upgradeable.sol"; + +struct TokenAmount { + /// @notice The asset token redeemed. + IERC20Upgradeable token; + /// @notice The amount redeemed. + uint256 amount; +} + +/// @notice The system subscription parameters. +struct SubscriptionParams { + /// @notice The current TVL of perp denominated in the underlying. + uint256 perpTVL; + /// @notice The current TVL of the vault denominated in the underlying. + uint256 vaultTVL; + /// @notice The tranche ratio of seniors accepted by perp. + uint256 seniorTR; +} + +struct RolloverData { + /// @notice The amount of tokens rolled out. + uint256 tokenOutAmt; + /// @notice The amount of trancheIn tokens rolled in. + uint256 trancheInAmt; +} diff --git a/spot-contracts/contracts/_interfaces/IBondIssuer.sol b/spot-contracts/contracts/_interfaces/IBondIssuer.sol index db332c18..94d72055 100644 --- a/spot-contracts/contracts/_interfaces/IBondIssuer.sol +++ b/spot-contracts/contracts/_interfaces/IBondIssuer.sol @@ -3,9 +3,6 @@ pragma solidity ^0.8.0; import { IBondController } from "./buttonwood/IBondController.sol"; -/// @notice Expected at least one matured bond. -error NoMaturedBonds(); - interface IBondIssuer { /// @notice Event emitted when a new bond is issued by the issuer. /// @param bond The newly issued bond. diff --git a/spot-contracts/contracts/_interfaces/IDiscountStrategy.sol b/spot-contracts/contracts/_interfaces/IDiscountStrategy.sol deleted file mode 100644 index 37401c43..00000000 --- a/spot-contracts/contracts/_interfaces/IDiscountStrategy.sol +++ /dev/null @@ -1,14 +0,0 @@ -// SPDX-License-Identifier: GPL-3.0-or-later -pragma solidity ^0.8.0; - -import { IERC20Upgradeable } from "@openzeppelin/contracts-upgradeable/token/ERC20/IERC20Upgradeable.sol"; - -interface IDiscountStrategy { - /// @notice Computes the discount to be applied to a given tranche token. - /// @param tranche The tranche token to compute discount for. - /// @return The discount as a fixed point number with `decimals()`. - function computeTrancheDiscount(IERC20Upgradeable tranche) external view returns (uint256); - - /// @notice Number of discount decimals. - function decimals() external view returns (uint8); -} diff --git a/spot-contracts/contracts/_interfaces/IERC20Burnable.sol b/spot-contracts/contracts/_interfaces/IERC20Burnable.sol new file mode 100644 index 00000000..18d9dbf7 --- /dev/null +++ b/spot-contracts/contracts/_interfaces/IERC20Burnable.sol @@ -0,0 +1,6 @@ +// SPDX-License-Identifier: GPL-3.0-or-later +pragma solidity ^0.8.0; + +interface IERC20Burnable { + function burn(uint256 value) external; +} diff --git a/spot-contracts/contracts/_interfaces/IFeePolicy.sol b/spot-contracts/contracts/_interfaces/IFeePolicy.sol new file mode 100644 index 00000000..5699611e --- /dev/null +++ b/spot-contracts/contracts/_interfaces/IFeePolicy.sol @@ -0,0 +1,53 @@ +// SPDX-License-Identifier: GPL-3.0-or-later +pragma solidity ^0.8.0; + +import { SubscriptionParams } from "./CommonTypes.sol"; + +interface IFeePolicy { + /// @return The percentage of the mint perp tokens to be charged as fees, + /// as a fixed-point number with {DECIMALS} decimal places. + function computePerpMintFeePerc() external view returns (uint256); + + /// @return The percentage of the burnt perp tokens to be charged as fees, + /// as a fixed-point number with {DECIMALS} decimal places. + function computePerpBurnFeePerc() external view returns (uint256); + + /// @param dr The current system deviation ratio. + /// @return The applied exchange rate adjustment between tranches into perp and + /// tokens out of perp during a rollover, + /// as a fixed-point number with {DECIMALS} decimal places. + /// @dev - A fee of 0%, implies the rollover exchange rate is unaltered. + /// example) 100 tranchesIn for 100 tranchesOut + /// - A fee of 1%, implies the exchange rate is adjusted in favor of tranchesIn. + /// example) 100 tranchesIn for 99 tranchesOut; i.e) perp enrichment + /// - A fee of -1%, implies the exchange rate is adjusted in favor of tranchesOut. + /// example) 99 tranchesIn for 100 tranchesOut + function computePerpRolloverFeePerc(uint256 dr) external view returns (int256); + + /// @return The percentage of the mint vault note amount to be charged as fees, + /// as a fixed-point number with {DECIMALS} decimal places. + function computeVaultMintFeePerc() external view returns (uint256); + + /// @return The percentage of the burnt vault note amount to be charged as fees, + /// as a fixed-point number with {DECIMALS} decimal places. + function computeVaultBurnFeePerc() external view returns (uint256); + + /// @param dr The current system deviation ratio. + /// @param dr_ The deviation ratio of the system after the operation is complete. + /// @return The percentage of perp tokens out to be charged as swap fees by the vault, + /// as a fixed-point numbers with {DECIMALS} decimal places. + function computeUnderlyingToPerpVaultSwapFeePerc(uint256 dr, uint256 dr_) external view returns (uint256); + + /// @param dr The current system deviation ratio. + /// @param dr_ The deviation ratio of the system after the operation is complete. + /// @return The percentage of underlying tokens out to be charged as swap fees by the vault, + /// as a fixed-point numbers with {DECIMALS} decimal places. + function computePerpToUnderlyingVaultSwapFeePerc(uint256 dr, uint256 dr_) external view returns (uint256); + + /// @return Number of decimals representing a multiplier of 1.0. So, 100% = 1*10**decimals. + function decimals() external view returns (uint8); + + /// @param s The subscription parameters of both the perp and vault systems. + /// @return The deviation ratio given the system subscription parameters. + function computeDeviationRatio(SubscriptionParams memory s) external view returns (uint256); +} diff --git a/spot-contracts/contracts/_interfaces/IFeeStrategy.sol b/spot-contracts/contracts/_interfaces/IFeeStrategy.sol deleted file mode 100644 index 7692d62e..00000000 --- a/spot-contracts/contracts/_interfaces/IFeeStrategy.sol +++ /dev/null @@ -1,39 +0,0 @@ -// SPDX-License-Identifier: GPL-3.0-or-later -pragma solidity ^0.8.0; - -import { IERC20Upgradeable } from "@openzeppelin/contracts-upgradeable/token/ERC20/IERC20Upgradeable.sol"; - -interface IFeeStrategy { - /// @notice Address of the fee token. - function feeToken() external view returns (IERC20Upgradeable); - - /// @notice Computes the fees while minting given amount of perp tokens. - /// @dev The mint fee can be either positive or negative. When positive it's paid by the minting users to the reserve. - /// When negative its paid to the minting users by the reserve. - /// The protocol fee is always non-negative and is paid by the users minting to the - /// perp contract's fee collector. - /// @param amount The amount of perp tokens to be minted. - /// @return reserveFee The fee paid to the reserve to mint perp tokens. - /// @return protocolFee The fee paid to the protocol to mint perp tokens. - function computeMintFees(uint256 amount) external view returns (int256 reserveFee, uint256 protocolFee); - - /// @notice Computes the fees while burning given amount of perp tokens. - /// @dev The burn fee can be either positive or negative. When positive it's paid by the burning users to the reserve. - /// When negative its paid to the burning users by the reserve. - /// The protocol fee is always non-negative and is paid by the users burning to the - /// perp contract's fee collector. - /// @param amount The amount of perp tokens to be burnt. - /// @return reserveFee The fee paid to the reserve to burn perp tokens. - /// @return protocolFee The fee paid to the protocol to burn perp tokens. - function computeBurnFees(uint256 amount) external view returns (int256 reserveFee, uint256 protocolFee); - - /// @notice Computes the fees while rolling over given amount of perp tokens. - /// @dev The rollover fee can be either positive or negative. When positive it's paid by the users rolling over to the reserve. - /// When negative its paid to the users rolling over by the reserve. - /// The protocol fee is always positive and is paid by the users rolling over to the - /// perp contract's fee collector. - /// @param amount The Perp-denominated value of the tranches being rolled over. - /// @return reserveFee The fee paid to the reserve to rollover tokens. - /// @return protocolFee The fee paid to the protocol to rollover tokens. - function computeRolloverFees(uint256 amount) external view returns (int256 reserveFee, uint256 protocolFee); -} diff --git a/spot-contracts/contracts/_interfaces/IPerpetualTranche.sol b/spot-contracts/contracts/_interfaces/IPerpetualTranche.sol index 8fc3b944..7d37a4e6 100644 --- a/spot-contracts/contracts/_interfaces/IPerpetualTranche.sol +++ b/spot-contracts/contracts/_interfaces/IPerpetualTranche.sol @@ -4,21 +4,16 @@ pragma solidity ^0.8.0; import { IERC20Upgradeable } from "@openzeppelin/contracts-upgradeable/token/ERC20/IERC20Upgradeable.sol"; import { IBondIssuer } from "./IBondIssuer.sol"; -import { IFeeStrategy } from "./IFeeStrategy.sol"; -import { IPricingStrategy } from "./IPricingStrategy.sol"; -import { IDiscountStrategy } from "./IDiscountStrategy.sol"; +import { IFeePolicy } from "./IFeePolicy.sol"; import { IBondController } from "./buttonwood/IBondController.sol"; import { ITranche } from "./buttonwood/ITranche.sol"; +import { IRolloverVault } from "./IRolloverVault.sol"; +import { TokenAmount, RolloverData } from "./CommonTypes.sol"; interface IPerpetualTranche is IERC20Upgradeable { //-------------------------------------------------------------------------- // Events - /// @notice Event emitted when the applied discount for a given token is set. - /// @param token The address of the token. - /// @param discount The discount factor applied. - event DiscountApplied(IERC20Upgradeable token, uint256 discount); - /// @notice Event emitted the reserve's current token balance is recorded after change. /// @param token Address of token. /// @param balance The recorded ERC-20 balance of the token held by the reserve. @@ -28,73 +23,61 @@ interface IPerpetualTranche is IERC20Upgradeable { /// @param bond Address of the new deposit bond. event UpdatedDepositBond(IBondController bond); - /// @notice Event emitted when the mature tranche balance is updated. - /// @param matureTrancheBalance The mature tranche balance. - event UpdatedMatureTrancheBalance(uint256 matureTrancheBalance); - //-------------------------------------------------------------------------- // Methods /// @notice Deposits tranche tokens into the system and mint perp tokens. /// @param trancheIn The address of the tranche token to be deposited. /// @param trancheInAmt The amount of tranche tokens deposited. - function deposit(ITranche trancheIn, uint256 trancheInAmt) external; + /// @return The amount of perp tokens minted. + function deposit(ITranche trancheIn, uint256 trancheInAmt) external returns (uint256); /// @notice Burn perp tokens and redeem the share of reserve assets. /// @param perpAmtBurnt The amount of perp tokens burnt from the caller. - function redeem(uint256 perpAmtBurnt) external; + /// @return tokensOut The list of reserve tokens and amounts redeemed. + function redeem(uint256 perpAmtBurnt) external returns (TokenAmount[] memory tokensOut); /// @notice Rotates newer tranches in for reserve tokens. /// @param trancheIn The tranche token deposited. /// @param tokenOut The reserve token to be redeemed. /// @param trancheInAmt The amount of trancheIn tokens deposited. + /// @return r The rollover amounts in various denominations. function rollover( ITranche trancheIn, IERC20Upgradeable tokenOut, uint256 trancheInAmt - ) external; + ) external returns (RolloverData memory r); - /// @notice Reference to the wallet or contract that has the ability to pause/unpause operations. + /// @notice External contract that stores a predefined bond config and frequency, + /// and issues new bonds when poked. + /// @return The address of the bond issuer. + function bondIssuer() external view returns (IBondIssuer); + + /// @notice Reference to the address that has the ability to pause/unpause operations. /// @return The address of the keeper. function keeper() external view returns (address); /// @notice The address of the underlying rebasing ERC-20 collateral token backing the tranches. - /// @return Address of the collateral token. - function collateral() external view returns (IERC20Upgradeable); + /// @return Address of the underlying collateral token. + function underlying() external view returns (IERC20Upgradeable); - /// @notice The "virtual" balance of all mature tranches held by the system. - /// @return The mature tranche balance. - function getMatureTrancheBalance() external returns (uint256); + /// @return Address of perp's rollover vault. + function vault() external view returns (IRolloverVault); /// @notice The parent bond whose tranches are currently accepted to mint perp tokens. /// @return Address of the deposit bond. function getDepositBond() external returns (IBondController); - /// @notice Checks if the given `trancheIn` can be rolled out for `tokenOut`. - /// @param trancheIn The tranche token deposited. - /// @param tokenOut The reserve token to be redeemed. - /// @return If the given pair is a valid rollover. - function isAcceptableRollover(ITranche trancheIn, IERC20Upgradeable tokenOut) external returns (bool); + /// @notice The tranche token contract currently accepted to mint perp tokens. + /// @return Address of the deposit tranche ERC-20 token. + function getDepositTranche() external returns (ITranche); - /// @notice The strategy contract with the fee computation logic. - /// @return Address of the strategy contract. - function feeStrategy() external view returns (IFeeStrategy); + /// @return The tranche ratio of the current deposit tranche. + function getDepositTrancheRatio() external returns (uint256); - /// @notice The ERC-20 contract which holds perp balances. - /// @return Address of the token. - function perpERC20() external view returns (IERC20Upgradeable); - - /// @notice The contract where the protocol holds funds which back the perp token supply. - /// @return Address of the reserve. - function reserve() external view returns (address); - - /// @notice The address which holds any revenue extracted by protocol. - /// @return Address of the fee collector. - function protocolFeeCollector() external view returns (address); - - /// @notice The fee token currently used to receive fees in. - /// @return Address of the fee token. - function feeToken() external view returns (IERC20Upgradeable); + /// @notice The policy contract with the fee computation logic for the perp and vault systems. + /// @return Address of the policy contract. + function feePolicy() external view returns (IFeePolicy); /// @notice Total count of tokens held in the reserve. /// @return The reserve token count. @@ -115,20 +98,15 @@ interface IPerpetualTranche is IERC20Upgradeable { /// @return The ERC-20 balance of the reserve token. function getReserveTokenBalance(IERC20Upgradeable token) external returns (uint256); - /// @notice Fetches the reserve's tranche token balance. - /// @param tranche The address of the tranche token held by the reserve. - /// @return The ERC-20 balance of the reserve tranche token. - function getReserveTrancheBalance(IERC20Upgradeable tranche) external returns (uint256); - - /// @notice Calculates the reserve's tranche token value, + /// @notice Calculates the reserve's token value, /// in a standard denomination as defined by the implementation. - /// @param tranche The address of the tranche token held by the reserve. - /// @return The value of the reserve tranche balance held by the reserve, in a standard denomination. - function getReserveTrancheValue(IERC20Upgradeable tranche) external returns (uint256); + /// @param token The address of the tranche token held by the reserve. + /// @return The value of the reserve token balance held by the reserve, in a standard denomination. + function getReserveTokenValue(IERC20Upgradeable token) external returns (uint256); - /// @notice Computes the price of each perp token, i.e) reserve value / total supply. - /// @return The average price per perp token. - function getAvgPrice() external returns (uint256); + /// @notice Computes the total value of assets currently held in the reserve. + /// @return The total value of the perp system, in a standard denomination. + function getTVL() external returns (uint256); /// @notice Fetches the list of reserve tokens which are up for rollover. /// @return The list of reserve tokens up for rollover. @@ -143,51 +121,20 @@ interface IPerpetualTranche is IERC20Upgradeable { /// @notice Computes the amount reserve tokens redeemed when burning given number of perp tokens. /// @param perpAmtBurnt The amount of perp tokens to be burnt. - /// @return tokensOut The list of reserve tokens redeemed. - /// @return tokenOutAmts The list of reserve token amounts redeemed. - function computeRedemptionAmts(uint256 perpAmtBurnt) - external - returns (IERC20Upgradeable[] memory tokensOut, uint256[] memory tokenOutAmts); - - struct RolloverPreview { - /// @notice The perp denominated value of tokens rolled over. - uint256 perpRolloverAmt; - /// @notice The amount of tokens rolled out. - uint256 tokenOutAmt; - /// @notice The tranche denominated amount of tokens rolled out. - /// @dev tokenOutAmt and trancheOutAmt can only be different values - /// in the case of rolling over the mature tranche. - uint256 trancheOutAmt; - /// @notice The amount of trancheIn tokens rolled in. - uint256 trancheInAmt; - /// @notice The difference between the available trancheIn amount and - /// the amount of tokens used for the rollover. - uint256 remainingTrancheInAmt; - } + /// @return tokensOut The list of reserve tokens and amounts redeemed. + function computeRedemptionAmts(uint256 perpAmtBurnt) external returns (TokenAmount[] memory tokensOut); /// @notice Computes the amount reserve tokens that are rolled out for the given number /// of `trancheIn` tokens rolled in. /// @param trancheIn The tranche token rolled in. /// @param tokenOut The reserve token to be rolled out. /// @param trancheInAmtAvailable The amount of trancheIn tokens rolled in. - /// @param tokenOutAmtRequested The amount of tokenOut tokens requested to be rolled out. /// @return r The rollover amounts in various denominations. function computeRolloverAmt( ITranche trancheIn, IERC20Upgradeable tokenOut, - uint256 trancheInAmtAvailable, - uint256 tokenOutAmtRequested - ) external returns (RolloverPreview memory); - - /// @notice The discount to be applied given the reserve token. - /// @param token The address of the reserve token. - /// @return The discount applied. - function computeDiscount(IERC20Upgradeable token) external view returns (uint256); - - /// @notice The price of the given reserve token. - /// @param token The address of the reserve token. - /// @return The computed price. - function computePrice(IERC20Upgradeable token) external view returns (uint256); + uint256 trancheInAmtAvailable + ) external returns (RolloverData memory r); /// @notice Updates time dependent storage state. function updateState() external; diff --git a/spot-contracts/contracts/_interfaces/IPricingStrategy.sol b/spot-contracts/contracts/_interfaces/IPricingStrategy.sol deleted file mode 100644 index cd2342a5..00000000 --- a/spot-contracts/contracts/_interfaces/IPricingStrategy.sol +++ /dev/null @@ -1,26 +0,0 @@ -// SPDX-License-Identifier: GPL-3.0-or-later -pragma solidity ^0.8.0; - -import { IERC20Upgradeable } from "@openzeppelin/contracts-upgradeable/token/ERC20/IERC20Upgradeable.sol"; -import { ITranche } from "../_interfaces/buttonwood/ITranche.sol"; - -interface IPricingStrategy { - /// @notice Computes the price of a given tranche token. - /// @param tranche The tranche to compute price of. - /// @return The price as a fixed point number with `decimals()`. - function computeTranchePrice(ITranche tranche) external view returns (uint256); - - /// @notice Computes the price of mature tranches extracted and held as naked collateral. - /// @param collateralToken The collateral token. - /// @param collateralBalance The collateral balance of all the mature tranches. - /// @param debt The total count of mature tranches. - /// @return The price as a fixed point number with `decimals()`. - function computeMatureTranchePrice( - IERC20Upgradeable collateralToken, - uint256 collateralBalance, - uint256 debt - ) external view returns (uint256); - - /// @notice Number of price decimals. - function decimals() external view returns (uint8); -} diff --git a/spot-contracts/contracts/_interfaces/IRolloverVault.sol b/spot-contracts/contracts/_interfaces/IRolloverVault.sol new file mode 100644 index 00000000..dea92298 --- /dev/null +++ b/spot-contracts/contracts/_interfaces/IRolloverVault.sol @@ -0,0 +1,35 @@ +// SPDX-License-Identifier: GPL-3.0-or-later +pragma solidity ^0.8.0; + +import { IVault } from "./IVault.sol"; +import { SubscriptionParams } from "./CommonTypes.sol"; + +interface IRolloverVault is IVault { + /// @notice Allows users to swap their underlying tokens for perps held by the vault. + /// @param underlyingAmtIn The amount of underlying tokens swapped in. + /// @return The amount of perp tokens swapped out. + function swapUnderlyingForPerps(uint256 underlyingAmtIn) external returns (uint256); + + /// @notice Allows users to swap their perp tokens for underlying tokens held by the vault. + /// @param perpAmtIn The amount of perp tokens swapped in. + /// @return The amount of underlying tokens swapped out. + function swapPerpsForUnderlying(uint256 perpAmtIn) external returns (uint256); + + /// @notice Computes the amount of perp tokens that are returned when user swaps a given number of underlying tokens. + /// @param underlyingAmtIn The number of underlying tokens the user swaps in. + /// @return perpAmtOut The number of perp tokens returned to the user. + /// @return perpFeeAmtToBurn The amount of perp tokens to be paid to the perp contract as mint fees. + /// @return s The pre-swap perp and vault subscription state. + function computeUnderlyingToPerpSwapAmt( + uint256 underlyingAmtIn + ) external returns (uint256, uint256, SubscriptionParams memory); + + /// @notice Computes the amount of underlying tokens that are returned when user swaps a given number of perp tokens. + /// @param perpAmtIn The number of perp tokens the user swaps in. + /// @return underlyingAmtOut The number of underlying tokens returned to the user. + /// @return perpFeeAmtToBurn The amount of perp tokens to be paid to the perp contract as burn fees. + /// @return s The pre-swap perp and vault subscription state. + function computePerpToUnderlyingSwapAmt( + uint256 perpAmtIn + ) external returns (uint256, uint256, SubscriptionParams memory); +} diff --git a/spot-contracts/contracts/_interfaces/IVault.sol b/spot-contracts/contracts/_interfaces/IVault.sol index a499e60b..28cd8258 100644 --- a/spot-contracts/contracts/_interfaces/IVault.sol +++ b/spot-contracts/contracts/_interfaces/IVault.sol @@ -2,20 +2,7 @@ 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 a minimum amount of vault assets to be deployed. -error InsufficientDeployment(); - -/// @notice Expected the number of vault assets deployed to be under the limit. -error DeployedCountOverLimit(); +import { TokenAmount } from "./CommonTypes.sol"; /* * @title IVault @@ -24,8 +11,8 @@ error DeployedCountOverLimit(); * 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 "deploys" underlying asset in a rules-based fashion (through a hard-coded strategy). + * 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 @@ -36,13 +23,13 @@ error DeployedCountOverLimit(); * 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). + * They can be a combination of the underlying 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 { +interface IVault is IERC20Upgradeable { /// @notice Recovers deployed funds and redeploys them. function recoverAndRedeploy() external; @@ -61,47 +48,49 @@ interface IVault { /// @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); + /// @notice Batches the recover and redeem functions. + /// @param notes The amount of notes to be burnt. + /// @return The list of asset tokens and amounts redeemed. + function recoverAndRedeem(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); + function getTVL() external view returns (uint256); /// @param token The address of the asset ERC-20 token held by the vault. /// @return The vault's asset token value, denominated in a standard unit of account. - function getVaultAssetValue(IERC20Upgradeable token) external returns (uint256); + function getVaultAssetValue(IERC20Upgradeable token) external view 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); + /// @return Total count of ERC-20 tokens held by the vault. + function assetCount() 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 view returns (uint256); + /// @return The vault's asset token address by index. + function assetAt(uint256 i) external view returns (IERC20Upgradeable); - /// @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 the asset ERC-20 token held by the vault. + /// @return The vault's asset token balance. + function vaultAssetBalance(IERC20Upgradeable token) external view returns (uint256); /// @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); + + /// @notice Computes the amount of notes minted when given amount of underlying asset tokens + /// are deposited into the system. + /// @param amount The amount tokens to be deposited into the vault. + /// @return The amount of notes to be minted. + function computeMintAmt(uint256 amount) external returns (uint256); + + /// @notice Computes the amount of asset tokens redeemed when burning given number of vault notes. + /// @param notes The amount of notes to be burnt. + /// @return The list of asset tokens and amounts redeemed. + function computeRedemptionAmts(uint256 notes) external returns (TokenAmount[] memory); } diff --git a/spot-contracts/contracts/_interfaces/ProtocolErrors.sol b/spot-contracts/contracts/_interfaces/ProtocolErrors.sol new file mode 100644 index 00000000..9015f188 --- /dev/null +++ b/spot-contracts/contracts/_interfaces/ProtocolErrors.sol @@ -0,0 +1,80 @@ +// SPDX-License-Identifier: BUSL-1.1 +pragma solidity ^0.8.20; + +//------------------------------------------------------------------------- +// Generic + +/// @notice Expected contract call to be triggered by authorized caller. +error UnauthorizedCall(); + +/// @notice Expected transfer out asset to not be a reserve asset. +error UnauthorizedTransferOut(); + +/// @notice Expected contract reference to not be `address(0)`. +error UnacceptableReference(); + +/// @notice Expected interface contract to return a fixed point with a different number of decimals. +error UnexpectedDecimals(); + +/// @notice Expected asset to be a valid reserve/vault asset. +error UnexpectedAsset(); + +/// @notice Expected to mint a non-zero amount of notes. +error UnacceptableDeposit(); + +/// @notice Expected to redeem a non-zero amount of notes. +error UnacceptableRedemption(); + +/// @notice Updated parameters violate defined constraints. +error UnacceptableParams(); + +/// @notice Storage array access out of bounds. +error OutOfBounds(); + +/// @notice Expected the number of reserve assets to be under the limit. +error ReserveCountOverLimit(); + +//------------------------------------------------------------------------- +// Perp + +/// @notice Expected rollover to be acceptable. +error UnacceptableRollover(); + +/// @notice Expected supply to be lower than the defined max supply. +error ExceededMaxSupply(); + +/// @notice Expected the total mint amount per tranche to be lower than the limit. +error ExceededMaxMintPerTranche(); + +//------------------------------------------------------------------------- +// Vault + +/// @notice Expected more underlying token liquidity to perform operation. +error InsufficientLiquidity(); + +/// @notice Expected to swap non-zero assets. +error UnacceptableSwap(); + +/// @notice Expected more assets to be deployed. +error InsufficientDeployment(); + +/// @notice Expected the number of vault assets deployed to be under the limit. +error DeployedCountOverLimit(); + +/// @notice Expected parent bond to have only 2 children tranches. +error UnacceptableTrancheLength(); + +//------------------------------------------------------------------------- +// Fee Policy + +/// @notice Expected perc value to be at most (1 * 10**DECIMALS), i.e) 1.0 or 100%. +error InvalidPerc(); + +/// @notice Expected target subscription ratio to be within defined bounds. +error InvalidTargetSRBounds(); + +/// @notice Expected deviation ratio bounds to be valid. +error InvalidDRBounds(); + +/// @notice Expected sigmoid asymptotes to be within defined bounds. +error InvalidSigmoidAsymptotes(); diff --git a/spot-contracts/contracts/_test/HelpersTester.sol b/spot-contracts/contracts/_test/HelpersTester.sol new file mode 100644 index 00000000..b082b957 --- /dev/null +++ b/spot-contracts/contracts/_test/HelpersTester.sol @@ -0,0 +1,86 @@ +// SPDX-License-Identifier: BUSL-1.1 +pragma solidity ^0.8.20; + +import { IERC20Upgradeable } from "@openzeppelin/contracts-upgradeable/token/ERC20/IERC20Upgradeable.sol"; +import { IBondController } from "../_interfaces/buttonwood/IBondController.sol"; +import { ITranche } from "../_interfaces/buttonwood/ITranche.sol"; +import { IPerpetualTranche } from "../_interfaces/IPerpetualTranche.sol"; +import { TokenAmount } from "../_interfaces/CommonTypes.sol"; + +import { BondTranches, BondTranchesHelpers } from "../_utils/BondTranchesHelpers.sol"; +import { BondHelpers } from "../_utils/BondHelpers.sol"; +import { TrancheHelpers } from "../_utils/TrancheHelpers.sol"; +import { PerpHelpers } from "../_utils/PerpHelpers.sol"; + +contract HelpersTester { + using BondHelpers for IBondController; + using BondTranchesHelpers for BondTranches; + using TrancheHelpers for ITranche; + + function secondsToMaturity(IBondController b) public view returns (uint256) { + return b.secondsToMaturity(); + } + + function getTranches(IBondController b) public view returns (BondTranches memory) { + return b.getTranches(); + } + + function trancheAt(IBondController b, uint8 index) public view returns (ITranche) { + return b.trancheAt(index); + } + + function getSeniorTranche(IBondController b) public view returns (ITranche) { + return b.getSeniorTranche(); + } + + function getSeniorTrancheRatio(IBondController b) public view returns (uint256) { + return b.getSeniorTrancheRatio(); + } + + function previewDeposit(IBondController b, uint256 collateralAmount) public view returns (TokenAmount[] memory) { + return b.previewDeposit(collateralAmount); + } + + function computeRedeemableTrancheAmounts( + IBondController b, + address u + ) public view returns (BondTranches memory, uint256[] memory) { + BondTranches memory bt = b.getTranches(); + return (bt, bt.computeRedeemableTrancheAmounts(u)); + } + + function computeRedeemableTrancheAmounts( + IBondController b, + uint256[] memory trancheBalsAvailable + ) public view returns (BondTranches memory, uint256[] memory) { + BondTranches memory bt = b.getTranches(); + return (bt, bt.computeRedeemableTrancheAmounts(trancheBalsAvailable)); + } + + function getTrancheCollateralizations(ITranche t) public view returns (uint256, uint256) { + IBondController bond = IBondController(t.bond()); + return t.getTrancheCollateralization(IERC20Upgradeable(bond.collateralToken())); + } + + function estimateUnderlyingAmtToTranche( + IPerpetualTranche perp, + uint256 perpTVL, + uint256 perpAmtToMint + ) public returns (uint256, uint256) { + IBondController depositBond = perp.getDepositBond(); + return + PerpHelpers.estimateUnderlyingAmtToTranche( + PerpHelpers.MintEstimationParams({ + perpTVL: perpTVL, + perpSupply: perp.totalSupply(), + depositBondCollateralBalance: (IERC20Upgradeable(depositBond.collateralToken())).balanceOf( + address(depositBond) + ), + depositBondTotalDebt: depositBond.totalDebt(), + depositTrancheSupply: (depositBond.getSeniorTranche()).totalSupply(), + depositTrancheTR: depositBond.getSeniorTrancheRatio() + }), + perpAmtToMint + ); + } +} diff --git a/spot-contracts/contracts/_test/MathTester.sol b/spot-contracts/contracts/_test/MathTester.sol new file mode 100644 index 00000000..112faeeb --- /dev/null +++ b/spot-contracts/contracts/_test/MathTester.sol @@ -0,0 +1,14 @@ +// SPDX-License-Identifier: BUSL-1.1 +pragma solidity ^0.8.20; + +import { Sigmoid } from "../_utils/Sigmoid.sol"; + +contract MathTester { + function twoPower(int256 exp, int256 one) public pure returns (int256) { + return Sigmoid.twoPower(exp, one); + } + + function compute(int256 x, int256 lower, int256 upper, int256 growth, int256 one) public pure returns (int256) { + return Sigmoid.compute(x, lower, upper, growth, one); + } +} diff --git a/spot-contracts/contracts/test/mocks/MockERC20.sol b/spot-contracts/contracts/_test/mocks/MockERC20.sol similarity index 94% rename from spot-contracts/contracts/test/mocks/MockERC20.sol rename to spot-contracts/contracts/_test/mocks/MockERC20.sol index 384eb928..76a84e79 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.19; +pragma solidity ^0.8.20; import { ERC20Upgradeable } from "@openzeppelin/contracts-upgradeable/token/ERC20/ERC20Upgradeable.sol"; diff --git a/spot-contracts/contracts/test/mocks/MockTranche.sol b/spot-contracts/contracts/_test/mocks/MockTranche.sol similarity index 89% rename from spot-contracts/contracts/test/mocks/MockTranche.sol rename to spot-contracts/contracts/_test/mocks/MockTranche.sol index 310c3077..9e19deb0 100644 --- a/spot-contracts/contracts/test/mocks/MockTranche.sol +++ b/spot-contracts/contracts/_test/mocks/MockTranche.sol @@ -1,5 +1,5 @@ // SPDX-License-Identifier: BUSL-1.1 -pragma solidity ^0.8.15; +pragma solidity ^0.8.20; import { MockERC20 } from "./MockERC20.sol"; diff --git a/spot-contracts/contracts/_test/mocks/MockVault.sol b/spot-contracts/contracts/_test/mocks/MockVault.sol new file mode 100644 index 00000000..1c9c8415 --- /dev/null +++ b/spot-contracts/contracts/_test/mocks/MockVault.sol @@ -0,0 +1,67 @@ +// SPDX-License-Identifier: BUSL-1.1 +pragma solidity ^0.8.20; + +import { IPerpetualTranche, IERC20Upgradeable, ITranche } from "../../_interfaces/IPerpetualTranche.sol"; +import { TokenAmount, RolloverData } from "../../_interfaces/CommonTypes.sol"; + +contract MockVault { + function getTVL() public pure returns (uint256) { + return 0; + } + + function mintPerps(IPerpetualTranche perp, ITranche trancheIn, uint256 trancheInAmt) public { + trancheIn.transferFrom(msg.sender, address(this), trancheInAmt); + + trancheIn.approve(address(perp), trancheInAmt); + perp.deposit(trancheIn, trancheInAmt); + + perp.transfer(msg.sender, perp.balanceOf(address(this))); + } + + function computePerpMintAmt( + IPerpetualTranche perp, + ITranche trancheIn, + uint256 trancheInAmt + ) public returns (uint256) { + return perp.computeMintAmt(trancheIn, trancheInAmt); + } + + function redeemPerps(IPerpetualTranche perp, uint256 perpAmt) public { + perp.transferFrom(msg.sender, address(this), perpAmt); + TokenAmount[] memory tokensOut = perp.redeem(perpAmt); + for (uint256 i = 0; i < tokensOut.length; ++i) { + IERC20Upgradeable tokenOut = tokensOut[i].token; + tokenOut.transfer(msg.sender, tokenOut.balanceOf(address(this))); + } + } + + function computePerpRedemptionAmts(IPerpetualTranche perp, uint256 perpAmt) public returns (TokenAmount[] memory) { + return perp.computeRedemptionAmts(perpAmt); + } + + function rollover( + IPerpetualTranche perp, + ITranche trancheIn, + IERC20Upgradeable tokenOut, + uint256 trancheInAmt + ) public returns (RolloverData memory) { + trancheIn.transferFrom(msg.sender, address(this), trancheInAmt); + + trancheIn.approve(address(perp), trancheInAmt); + RolloverData memory r = perp.rollover(trancheIn, tokenOut, trancheInAmt); + + trancheIn.transfer(msg.sender, trancheIn.balanceOf(address(this))); + tokenOut.transfer(msg.sender, tokenOut.balanceOf(address(this))); + + return r; + } + + function callRollover( + IPerpetualTranche perp, + ITranche trancheIn, + IERC20Upgradeable tokenOut, + uint256 trancheInAmt + ) public { + perp.rollover(trancheIn, tokenOut, trancheInAmt); + } +} diff --git a/spot-contracts/contracts/_utils/BondHelpers.sol b/spot-contracts/contracts/_utils/BondHelpers.sol index 1a7245e1..b9aefc14 100644 --- a/spot-contracts/contracts/_utils/BondHelpers.sol +++ b/spot-contracts/contracts/_utils/BondHelpers.sol @@ -1,63 +1,15 @@ // SPDX-License-Identifier: BUSL-1.1 -pragma solidity ^0.8.19; +pragma solidity ^0.8.20; import { IERC20Upgradeable } from "@openzeppelin/contracts-upgradeable/token/ERC20/IERC20Upgradeable.sol"; import { IBondController } from "../_interfaces/buttonwood/IBondController.sol"; import { ITranche } from "../_interfaces/buttonwood/ITranche.sol"; +import { TokenAmount } from "../_interfaces/CommonTypes.sol"; +import { UnacceptableDeposit, UnacceptableTrancheLength } from "../_interfaces/ProtocolErrors.sol"; import { SafeCastUpgradeable } from "@openzeppelin/contracts-upgradeable/utils/math/SafeCastUpgradeable.sol"; import { MathUpgradeable } from "@openzeppelin/contracts-upgradeable/utils/math/MathUpgradeable.sol"; - -/// @notice Expected tranche to be part of bond. -/// @param tranche Address of the tranche token. -error UnacceptableTranche(ITranche tranche); - -struct BondTranches { - ITranche[] tranches; - uint256[] trancheRatios; -} - -/** - * @title BondTranchesHelpers - * - * @notice Library with helper functions for the bond's retrieved tranche data. - * - */ -library BondTranchesHelpers { - /// @notice Iterates through the tranche data to find the seniority index of the given tranche. - /// @param bt The tranche data object. - /// @param t The address of the tranche to check. - /// @return the index of the tranche in the tranches array. - function indexOf(BondTranches memory bt, ITranche t) internal pure returns (uint8) { - for (uint8 i = 0; i < bt.tranches.length; i++) { - if (bt.tranches[i] == t) { - return i; - } - } - revert UnacceptableTranche(t); - } -} - -/** - * @title TrancheHelpers - * - * @notice Library with helper functions for tranche tokens. - * - */ -library TrancheHelpers { - /// @notice Given a tranche, looks up the collateral balance backing the tranche supply. - /// @param t Address of the tranche token. - /// @return The collateral balance and the tranche token supply. - function getTrancheCollateralization(ITranche t) internal view returns (uint256, uint256) { - IBondController bond = IBondController(t.bond()); - BondTranches memory bt; - uint256[] memory collateralBalances; - uint256[] memory trancheSupplies; - (bt, collateralBalances, trancheSupplies) = BondHelpers.getTrancheCollateralizations(bond); - uint256 trancheIndex = BondTranchesHelpers.indexOf(bt, t); - return (collateralBalances[trancheIndex], trancheSupplies[trancheIndex]); - } -} +import { BondTranches } from "./BondTranchesHelpers.sol"; /** * @title BondHelpers @@ -72,7 +24,6 @@ library BondHelpers { // Replicating value used here: // https://github.com/buttonwood-protocol/tranche/blob/main/contracts/BondController.sol uint256 private constant TRANCHE_RATIO_GRANULARITY = 1000; - uint256 private constant BPS = 10_000; /// @notice Given a bond, calculates the time remaining to maturity. /// @param b The address of the bond contract. @@ -84,19 +35,13 @@ library BondHelpers { /// @notice Given a bond, retrieves all of the bond's tranches. /// @param b The address of the bond contract. - /// @return The tranche data. - function getTranches(IBondController b) internal view returns (BondTranches memory) { - BondTranches memory bt; - uint8 trancheCount = b.trancheCount().toUint8(); - bt.tranches = new ITranche[](trancheCount); - bt.trancheRatios = new uint256[](trancheCount); - // Max tranches per bond < 2**8 - 1 - for (uint8 i = 0; i < trancheCount; i++) { - (ITranche t, uint256 ratio) = b.tranches(i); - bt.tranches[i] = t; - bt.trancheRatios[i] = ratio; + /// @return bt The bond's tranche data. + function getTranches(IBondController b) internal view returns (BondTranches memory bt) { + if (b.trancheCount() != 2) { + revert UnacceptableTrancheLength(); } - return bt; + (bt.tranches[0], bt.trancheRatios[0]) = b.tranches(0); + (bt.tranches[1], bt.trancheRatios[1]) = b.tranches(1); } /// @notice Given a bond, returns the tranche at the specified index. @@ -105,142 +50,51 @@ library BondHelpers { /// @return t The tranche address. function trancheAt(IBondController b, uint8 i) internal view returns (ITranche t) { (t, ) = b.tranches(i); - return t; } - /// @notice Helper function to estimate the amount of tranches minted when a given amount of collateral - /// is deposited into the bond. - /// @dev This function is used off-chain services (using callStatic) to preview tranches minted after + /// @notice Given a bond, returns the address of the most senior tranche. /// @param b The address of the bond contract. - /// @return The tranche data, an array of tranche amounts and fees. - function previewDeposit(IBondController b, uint256 collateralAmount) - internal - view - returns ( - BondTranches memory, - uint256[] memory, - uint256[] memory - ) - { - BondTranches memory bt = getTranches(b); - uint256[] memory trancheAmts = new uint256[](bt.tranches.length); - uint256[] memory fees = new uint256[](bt.tranches.length); - - uint256 totalDebt = b.totalDebt(); - uint256 collateralBalance = IERC20Upgradeable(b.collateralToken()).balanceOf(address(b)); - uint256 feeBps = b.feeBps(); - - for (uint8 i = 0; i < bt.tranches.length; i++) { - trancheAmts[i] = collateralAmount.mulDiv(bt.trancheRatios[i], TRANCHE_RATIO_GRANULARITY); - if (collateralBalance > 0) { - trancheAmts[i] = trancheAmts[i].mulDiv(totalDebt, collateralBalance); - } - } - - if (feeBps > 0) { - for (uint8 i = 0; i < bt.tranches.length; i++) { - fees[i] = trancheAmts[i].mulDiv(feeBps, BPS); - trancheAmts[i] -= fees[i]; - } - } - - return (bt, trancheAmts, fees); + /// @return t The senior tranche address. + function getSeniorTranche(IBondController b) internal view returns (ITranche t) { + (t, ) = b.tranches(0); } - /// @notice Given a bond, for each tranche token retrieves the total collateral redeemable - /// for the total supply of the tranche token (aka debt issued). - /// @dev The cdr can be computed for each tranche by dividing the - /// returned tranche's collateralBalance by the tranche's totalSupply. + /// @notice Given a bond, returns the tranche ratio of the most senior tranche. /// @param b The address of the bond contract. - /// @return The tranche data and the list of collateral balances and the total supplies for each tranche. - function getTrancheCollateralizations(IBondController b) - internal - view - returns ( - BondTranches memory, - uint256[] memory, - uint256[] memory - ) - { - BondTranches memory bt = getTranches(b); - uint256[] memory collateralBalances = new uint256[](bt.tranches.length); - uint256[] memory trancheSupplies = new uint256[](bt.tranches.length); + /// @return r The tranche ratio of the senior most tranche. + function getSeniorTrancheRatio(IBondController b) internal view returns (uint256 r) { + (, r) = b.tranches(0); + } - // When the bond is mature, the collateral is transferred over to the individual tranche token contracts + /// @notice Helper function to estimate the amount of tranches minted when a given amount of collateral + /// is deposited into the bond. + /// @dev This function is used off-chain services (using callStatic) to preview tranches minted. + /// This function assumes that the no fees are withheld for tranching. + /// @param b The address of the bond contract. + /// @return The tranche data, an array of tranche amounts. + function previewDeposit(IBondController b, uint256 collateralAmount) internal view returns (TokenAmount[] memory) { if (b.isMature()) { - for (uint8 i = 0; i < bt.tranches.length; i++) { - trancheSupplies[i] = bt.tranches[i].totalSupply(); - collateralBalances[i] = IERC20Upgradeable(b.collateralToken()).balanceOf(address(bt.tranches[i])); - } - return (bt, collateralBalances, trancheSupplies); + revert UnacceptableDeposit(); } - // Before the bond is mature, all the collateral is held by the bond contract - uint256 bondCollateralBalance = IERC20Upgradeable(b.collateralToken()).balanceOf(address(b)); - uint256 zTrancheIndex = bt.tranches.length - 1; - for (uint8 i = 0; i < bt.tranches.length; i++) { - trancheSupplies[i] = bt.tranches[i].totalSupply(); - - // a to y tranches - if (i != zTrancheIndex) { - collateralBalances[i] = (trancheSupplies[i] <= bondCollateralBalance) - ? trancheSupplies[i] - : bondCollateralBalance; - bondCollateralBalance -= collateralBalances[i]; - } - // z tranche - else { - collateralBalances[i] = bondCollateralBalance; - } - } - - return (bt, collateralBalances, trancheSupplies); - } - - /// @notice For a given bond and user address, computes the maximum number of each of the bond's tranches - /// the user is able to redeem before the bond's maturity. These tranche amounts necessarily match the bond's tranche ratios. - /// @param b The address of the bond contract. - /// @param u The address to check balance for. - /// @return The tranche data and an array of tranche token balances. - function computeRedeemableTrancheAmounts(IBondController b, address u) - internal - view - returns (BondTranches memory, uint256[] memory) - { BondTranches memory bt = getTranches(b); - uint256[] memory redeemableAmts = new uint256[](bt.tranches.length); - - // We Calculate how many underlying assets could be redeemed from each tranche balance, - // assuming other tranches are not an issue, and record the smallest amount. - // - // Usually one tranche balance is the limiting factor, we first loop through to identify - // it by figuring out the one which has the least `trancheBalance/trancheRatio`. - // - uint256 minBalanceToTrancheRatio = type(uint256).max; - uint8 i; - for (i = 0; i < bt.tranches.length; i++) { - // NOTE: We round the avaiable balance down to the nearest multiple of the - // tranche ratio. This ensures that `minBalanceToTrancheRatio` - // can be represented without loss as a fixedPt number. - uint256 bal = bt.tranches[i].balanceOf(u); - bal = bal - (bal % bt.trancheRatios[i]); + TokenAmount[] memory tranchesOut = new TokenAmount[](2); - uint256 d = bal.mulDiv(TRANCHE_RATIO_GRANULARITY, bt.trancheRatios[i]); - if (d < minBalanceToTrancheRatio) { - minBalanceToTrancheRatio = d; - } + uint256 totalDebt = b.totalDebt(); + uint256 collateralBalance = IERC20Upgradeable(b.collateralToken()).balanceOf(address(b)); - // if one of the balances is zero, we return - if (minBalanceToTrancheRatio == 0) { - return (bt, redeemableAmts); - } + uint256 seniorAmt = collateralAmount.mulDiv(bt.trancheRatios[0], TRANCHE_RATIO_GRANULARITY); + if (collateralBalance > 0) { + seniorAmt = seniorAmt.mulDiv(totalDebt, collateralBalance); } + tranchesOut[0] = TokenAmount({ token: bt.tranches[0], amount: seniorAmt }); - // Now that we have `minBalanceToTrancheRatio`, we compute the redeemable amounts. - for (i = 0; i < bt.tranches.length; i++) { - redeemableAmts[i] = bt.trancheRatios[i].mulDiv(minBalanceToTrancheRatio, TRANCHE_RATIO_GRANULARITY); + uint256 juniorAmt = collateralAmount.mulDiv(bt.trancheRatios[1], TRANCHE_RATIO_GRANULARITY); + if (collateralBalance > 0) { + juniorAmt = juniorAmt.mulDiv(totalDebt, collateralBalance); } + tranchesOut[1] = TokenAmount({ token: bt.tranches[1], amount: juniorAmt }); - return (bt, redeemableAmts); + return tranchesOut; } } diff --git a/spot-contracts/contracts/_utils/BondTranchesHelpers.sol b/spot-contracts/contracts/_utils/BondTranchesHelpers.sol new file mode 100644 index 00000000..5e5f1586 --- /dev/null +++ b/spot-contracts/contracts/_utils/BondTranchesHelpers.sol @@ -0,0 +1,62 @@ +// SPDX-License-Identifier: BUSL-1.1 +pragma solidity ^0.8.20; + +import { ITranche } from "../_interfaces/buttonwood/ITranche.sol"; + +import { MathUpgradeable } from "@openzeppelin/contracts-upgradeable/utils/math/MathUpgradeable.sol"; + +// @dev We assume that all bonds in the system just have 2 tranches, i.e) one senior and one junior. +struct BondTranches { + ITranche[2] tranches; + uint256[2] trancheRatios; +} + +/** + * @title BondTranchesHelpers + * + * @notice Library with helper functions for the bond's retrieved tranche data. + * + */ +library BondTranchesHelpers { + using MathUpgradeable for uint256; + + /// @notice For a given bond's tranche data and user address, computes the maximum number of each of the bond's tranches + /// the user is able to redeem before the bond's maturity. These tranche amounts necessarily match the bond's tranche ratios. + /// @param bt The bond's tranche data. + /// @param u The address to check balance for. + /// @return An array of tranche token balances. + function computeRedeemableTrancheAmounts( + BondTranches memory bt, + address u + ) internal view returns (uint256[] memory) { + uint256[] memory trancheBalsAvailable = new uint256[](2); + trancheBalsAvailable[0] = bt.tranches[0].balanceOf(u); + trancheBalsAvailable[1] = bt.tranches[1].balanceOf(u); + return computeRedeemableTrancheAmounts(bt, trancheBalsAvailable); + } + + /// @notice For a given bond's tranche data and tranche balances available, computes the maximum number of each of the bond's tranches + /// the user is able to redeem before the bond's maturity. + /// The returned tranche amounts necessarily match the bond's tranche ratios. + /// @param bt The bond's tranche data. + /// @param trancheBalsAvailable The tranche balance of each bond tranche available to be used for redemption. + /// @return An array of tranche token balances. + function computeRedeemableTrancheAmounts( + BondTranches memory bt, + uint256[] memory trancheBalsAvailable + ) internal pure returns (uint256[] memory) { + uint256[] memory trancheAmtsReq = new uint256[](2); + + // We compute the amount of seniors required using all the juniors + trancheAmtsReq[1] = trancheBalsAvailable[1] - (trancheBalsAvailable[1] % bt.trancheRatios[1]); + trancheAmtsReq[0] = (trancheAmtsReq[1] * bt.trancheRatios[0]) / bt.trancheRatios[1]; + + // If enough seniors aren't available, we compute the amount of juniors required using all the seniors + if (trancheAmtsReq[0] > trancheBalsAvailable[0]) { + trancheAmtsReq[0] = trancheBalsAvailable[0] - (trancheBalsAvailable[0] % bt.trancheRatios[0]); + trancheAmtsReq[1] = (trancheAmtsReq[0] * bt.trancheRatios[1]) / bt.trancheRatios[0]; + } + + return trancheAmtsReq; + } +} diff --git a/spot-contracts/contracts/_utils/PerpHelpers.sol b/spot-contracts/contracts/_utils/PerpHelpers.sol new file mode 100644 index 00000000..97c779ef --- /dev/null +++ b/spot-contracts/contracts/_utils/PerpHelpers.sol @@ -0,0 +1,105 @@ +// SPDX-License-Identifier: BUSL-1.1 +pragma solidity ^0.8.20; + +import { IBondController } from "../_interfaces/buttonwood/IBondController.sol"; + +import { MathUpgradeable } from "@openzeppelin/contracts-upgradeable/utils/math/MathUpgradeable.sol"; +import { BondHelpers } from "./BondHelpers.sol"; + +/** + * @title PerpHelpers + * + * @notice Library with helper functions for the Perpetual tranche contract. + * + */ +library PerpHelpers { + using MathUpgradeable for uint256; + using BondHelpers for IBondController; + + // Replicating value used here: + // https://github.com/buttonwood-protocol/tranche/blob/main/contracts/BondController.sol + uint256 private constant TRANCHE_RATIO_GRANULARITY = 1000; + + /// @dev Input data required to estimate the amount of underlying required to mint perps. + struct MintEstimationParams { + /// @notice perpTVL The current TVL of perp. + uint256 perpTVL; + /// @notice perpSupply The total supply of perp tokens. + uint256 perpSupply; + /// @notice depositBondCollateralBalance The total collateral balance of perp's deposit bond. + uint256 depositBondCollateralBalance; + /// @notice depositBondTotalDebt The total debt of perp's deposit bond. + uint256 depositBondTotalDebt; + /// @notice depositTrancheSupply The total supply of perp's deposit tranche. + uint256 depositTrancheSupply; + /// @notice depositTrancheTR The tranche ratio of perp's deposit tranche. + uint256 depositTrancheTR; + } + + /// @notice This function estimates the amount of underlying tokens that need to be tranched + /// in order to mint the given amount of perp tokens. + /// @dev If this function errs, it is guaranteed to err by overestimating, i.e) when you tranche the estimated amount + /// of underlying tokens, then and use the senior tranches to mint perps, + /// you might end up minting slightly more than `perpAmtToMint`. + /// @param p The estimation input parameters. + /// @param perpAmtToMint The required number of perp tokens to mint. + /// @return underylingAmtToTranche The number of underlying tokens to tranche. + /// @return seniorAmtToDeposit The number of minted seniors to then deposit into perp. + function estimateUnderlyingAmtToTranche( + MintEstimationParams memory p, + uint256 perpAmtToMint + ) internal pure returns (uint256, uint256) { + // We assume that: + // - Perp's deposit tranche is the most senior tranche in the deposit bond. + // - The deposit bond is NOT mature. + // - No fees are withheld while tranching + + // Math explanation: + // + // Given [Y] underlying tokens, + // We can create S seniors, + // S = Y * seniorRatio / bondCDR + // = Y * (depositTrancheTR/TRANCHE_RATIO_GRANULARITY) / (depositBondCollateralBalance/depositBondTotalDebt) + // = (Y * depositTrancheTR * depositBondTotalDebt) / (TRANCHE_RATIO_GRANULARITY * depositBondCollateralBalance) + // + // Given [S] senior tranche tokens, + // We can mint X perps, + // X = S * price(senior) / price(perp) + // X = S * (seniorClaim / seniorSupply) / (perpTVL / perpSupply) + // X = (S * seniorClaim * perpSupply) / (seniorSupply * perpTVL) + // + // Thus given X (perpAmtToMint), we calculate S (seniorAmtToDeposit) and Y (underlyingAmtToTranche) + // + // S = (X * perpTVL * seniorSupply) / (perpSupply * seniorClaim) + // = X * (perpTVL / perpSupply) * (seniorSupply / seniorClaim) + // + // Y = (S * depositBondCollateralBalance * TRANCHE_RATIO_GRANULARITY) / (depositBondTotalDebt * depositTrancheTR) + // = S * (depositBondCollateralBalance / depositBondTotalDebt) * (TRANCHE_RATIO_GRANULARITY / depositTrancheTR) + // + + uint256 seniorAmtToDeposit = (p.perpSupply > 0) + ? perpAmtToMint.mulDiv(p.perpTVL, p.perpSupply, MathUpgradeable.Rounding.Up) + : perpAmtToMint; + + uint256 depositTrancheClaim = MathUpgradeable.min(p.depositTrancheSupply, p.depositBondCollateralBalance); + seniorAmtToDeposit = (p.depositTrancheSupply > 0) + ? seniorAmtToDeposit.mulDiv(p.depositTrancheSupply, depositTrancheClaim, MathUpgradeable.Rounding.Up) + : seniorAmtToDeposit; + + uint256 underlyingAmtToTranche = (p.depositBondTotalDebt > 0) + ? seniorAmtToDeposit.mulDiv( + p.depositBondCollateralBalance, + p.depositBondTotalDebt, + MathUpgradeable.Rounding.Up + ) + : seniorAmtToDeposit; + + underlyingAmtToTranche = underlyingAmtToTranche.mulDiv( + TRANCHE_RATIO_GRANULARITY, + p.depositTrancheTR, + MathUpgradeable.Rounding.Up + ); + + return (underlyingAmtToTranche, seniorAmtToDeposit); + } +} diff --git a/spot-contracts/contracts/_utils/Sigmoid.sol b/spot-contracts/contracts/_utils/Sigmoid.sol new file mode 100644 index 00000000..0095ccee --- /dev/null +++ b/spot-contracts/contracts/_utils/Sigmoid.sol @@ -0,0 +1,98 @@ +// SPDX-License-Identifier: GPL-3.0-or-later +// https://github.com/ampleforth/ampleforth-contracts/blob/master/contracts/UFragmentsPolicy.sol +pragma solidity ^0.8.20; + +import { SignedMathUpgradeable } from "@openzeppelin/contracts-upgradeable/utils/math/SignedMathUpgradeable.sol"; +import { SafeCastUpgradeable } from "@openzeppelin/contracts-upgradeable/utils/math/SafeCastUpgradeable.sol"; + +/// @notice Expected exponent to be at most 100. +error ExpTooLarge(); + +/** + * @title Sigmoid + * + * @notice Library with helper functions to compute y = sigmoid(x). + * + */ +library Sigmoid { + using SignedMathUpgradeable for int256; + using SafeCastUpgradeable for uint256; + + /// @notice Computes 2^exp with limited precision where -100 <= exp <= 100 * one + /// @param exp The power to raise 2 to -100 <= exp <= 100 * one + /// @param one 1.0 represented in the same fixed point number format as exp + /// @return 2^exp represented with same number of decimals after the point as one + function twoPower(int256 exp, int256 one) internal pure returns (int256) { + bool reciprocal = false; + if (exp < 0) { + reciprocal = true; + exp = exp.abs().toInt256(); + } + + // Precomputed values for 2^(1/2^i) in 18 decimals fixed point numbers + int256[5] memory ks = [ + int256(1414213562373095049), + 1189207115002721067, + 1090507732665257659, + 1044273782427413840, + 1021897148654116678 + ]; + int256 whole = exp / one; + if (whole > 100) { + revert ExpTooLarge(); + } + int256 result = int256(uint256(1) << uint256(whole)) * one; + int256 remaining = exp - (whole * one); + + int256 current = one / 2; + for (uint256 i = 0; i < 5; ++i) { + if (remaining >= current) { + remaining = remaining - current; + result = (result * ks[i]) / 10 ** 18; // 10**18 to match hardcoded ks values + } + current = current / 2; + } + if (reciprocal) { + result = (one * one) / result; + } + return result; + } + + /// @notice Given number x and sigmoid parameters all represented as fixed-point number with + /// the same number of decimals, it computes y = sigmoid(x). + /// @param x The sigmoid function input value. + /// @param lower The lower asymptote. + /// @param upper The upper asymptote. + /// @param growth The growth parameter. + /// @param one 1.0 as a fixed-point. + /// @return The computed value of sigmoid(x) as fixed-point number. + function compute(int256 x, int256 lower, int256 upper, int256 growth, int256 one) internal pure returns (int256) { + int256 delta; + + delta = x - one; + + // Compute: (Upper-Lower)/(1-(Upper/Lower)/2^(Growth*delta))) + Lower + + int256 exponent = (growth * delta) / one; + + // Cap exponent to guarantee it is not too big for twoPower + if (exponent > one * 100) { + exponent = one * 100; + } + if (exponent < one * -100) { + exponent = one * -100; + } + + int256 pow = twoPower(exponent, one); // 2^(Growth*Delta) + if (pow == 0) { + return lower; + } + int256 numerator = upper - lower; //(Upper-Lower) + int256 intermediate = (upper * one) / lower; + intermediate = (intermediate * one) / pow; + int256 denominator = one - intermediate; // (1-(Upper/Lower)/2^(Growth*delta))) + + int256 y = ((numerator * one) / denominator) + lower; + return y; + } +} diff --git a/spot-contracts/contracts/_utils/SignedMathHelpers.sol b/spot-contracts/contracts/_utils/SignedMathHelpers.sol deleted file mode 100644 index 2b6f4f9e..00000000 --- a/spot-contracts/contracts/_utils/SignedMathHelpers.sol +++ /dev/null @@ -1,14 +0,0 @@ -// SPDX-License-Identifier: BUSL-1.1 -pragma solidity ^0.8.19; - -/** - * @title SignedMathHelpers - * - * @notice Library with helper functions for signed integer math. - * - */ -library SignedMathHelpers { - function sign(int256 a) internal pure returns (int256) { - return (a > 0) ? int256(1) : ((a < 0) ? int256(-1) : int256(0)); - } -} diff --git a/spot-contracts/contracts/_utils/TrancheHelpers.sol b/spot-contracts/contracts/_utils/TrancheHelpers.sol new file mode 100644 index 00000000..f95530e9 --- /dev/null +++ b/spot-contracts/contracts/_utils/TrancheHelpers.sol @@ -0,0 +1,61 @@ +// SPDX-License-Identifier: BUSL-1.1 +pragma solidity ^0.8.20; + +import { IERC20Upgradeable } from "@openzeppelin/contracts-upgradeable/token/ERC20/IERC20Upgradeable.sol"; +import { IBondController } from "../_interfaces/buttonwood/IBondController.sol"; +import { ITranche } from "../_interfaces/buttonwood/ITranche.sol"; +import { UnacceptableTrancheLength } from "../_interfaces/ProtocolErrors.sol"; + +import { MathUpgradeable } from "@openzeppelin/contracts-upgradeable/utils/math/MathUpgradeable.sol"; +import { BondHelpers } from "./BondHelpers.sol"; + +/** + * @title TrancheHelpers + * + * @notice Library with helper functions for tranche tokens. + * + */ +library TrancheHelpers { + using BondHelpers for IBondController; + + /// @notice Given a tranche, calculates the claimable collateral balance backing the tranche supply. + /// @param tranche Address of the tranche token. + /// @param collateralToken Address of the tranche's underlying collateral token. + /// @return The collateral balance and the tranche token supply. + function getTrancheCollateralization( + ITranche tranche, + IERC20Upgradeable collateralToken + ) internal view returns (uint256, uint256) { + IBondController bond = IBondController(tranche.bond()); + + uint256 trancheSupply = tranche.totalSupply(); + uint256 trancheClaim = 0; + + // When the tranche's parent bond is mature + if (bond.isMature()) { + trancheClaim = collateralToken.balanceOf(address(tranche)); + return (trancheClaim, trancheSupply); + } + + // NOTE: This implementation assumes the bond has only two tranches. + if (bond.trancheCount() != 2) { + revert UnacceptableTrancheLength(); + } + + uint256 bondCollateralBalance = collateralToken.balanceOf(address(bond)); + + // For junior tranche + if (bond.trancheAt(1) == tranche) { + uint256 seniorSupply = bond.totalDebt() - trancheSupply; + uint256 seniorClaim = MathUpgradeable.min(seniorSupply, bondCollateralBalance); + trancheClaim = bondCollateralBalance - seniorClaim; + } + // For senior tranche + else { + // require(bond.trancheAt(0) == tranche); + trancheClaim = MathUpgradeable.min(trancheSupply, bondCollateralBalance); + } + + return (trancheClaim, trancheSupply); + } +} diff --git a/spot-contracts/contracts/strategies/BasicFeeStrategy.sol b/spot-contracts/contracts/strategies/BasicFeeStrategy.sol deleted file mode 100644 index b615cf22..00000000 --- a/spot-contracts/contracts/strategies/BasicFeeStrategy.sol +++ /dev/null @@ -1,123 +0,0 @@ -// SPDX-License-Identifier: BUSL-1.1 -pragma solidity ^0.8.19; - -import { IERC20Upgradeable } from "@openzeppelin/contracts-upgradeable/token/ERC20/IERC20Upgradeable.sol"; -import { IFeeStrategy } from "../_interfaces/IFeeStrategy.sol"; -import { IPerpetualTranche } from "../_interfaces/IPerpetualTranche.sol"; - -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 { SignedMathHelpers } from "../_utils/SignedMathHelpers.sol"; - -/// @notice Expected perc value to be less than 100 with {PERC_DECIMALS}. -/// @param perc The percentage value. -error UnacceptablePercValue(int256 perc); - -/** - * @title BasicFeeStrategy - * - * @notice Basic fee strategy using fixed percentages. This strategy extracts NO protocol fees. - * - * @dev IMPORTANT: If mint or burn fee is negative, the other must overcompensate in the positive direction. - * Otherwise, user could extract from the fee collector by constant mint/burn transactions. - * - */ -contract BasicFeeStrategy is IFeeStrategy, OwnableUpgradeable { - using SignedMathUpgradeable for int256; - using SignedMathHelpers for int256; - using SafeCastUpgradeable for uint256; - using SafeCastUpgradeable for int256; - - /// @dev {10 ** PERC_DECIMALS} is considered 1% - uint8 public constant PERC_DECIMALS = 6; - uint256 public constant UNIT_PERC = 10**PERC_DECIMALS; - uint256 public constant HUNDRED_PERC = 100 * UNIT_PERC; - - /// @inheritdoc IFeeStrategy - /// @custom:oz-upgrades-unsafe-allow state-variable-immutable - IERC20Upgradeable public immutable override feeToken; - - /// @notice Fixed percentage of the mint amount to be used as fee. - int256 public mintFeePerc; - - /// @notice Fixed percentage of the burn amount to be used as fee. - int256 public burnFeePerc; - - /// @notice Fixed percentage of the rollover amount to be used as fee. - int256 public rolloverFeePerc; - - // EVENTS - - /// @notice Event emitted when the mint fee percentage is updated. - /// @param mintFeePerc Mint fee percentage. - event UpdatedMintPerc(int256 mintFeePerc); - - /// @notice Event emitted when the burn fee percentage is updated. - /// @param burnFeePerc Burn fee percentage. - event UpdatedBurnPerc(int256 burnFeePerc); - - /// @notice Event emitted when the rollover fee percentage is updated. - /// @param rolloverFeePerc Rollover fee percentage. - event UpdatedRolloverPerc(int256 rolloverFeePerc); - - /// @notice Contract constructor. - /// @param feeToken_ Address of the fee ERC-20 token contract. - constructor(IERC20Upgradeable feeToken_) { - feeToken = feeToken_; - } - - /// @notice Contract initializer. - /// @param mintFeePerc_ Mint fee percentage. - /// @param burnFeePerc_ Burn fee percentage. - /// @param rolloverFeePerc_ Rollover fee percentage. - function init( - int256 mintFeePerc_, - int256 burnFeePerc_, - int256 rolloverFeePerc_ - ) public initializer { - __Ownable_init(); - updateMintFeePerc(mintFeePerc_); - updateBurnFeePerc(burnFeePerc_); - updateRolloverFeePerc(rolloverFeePerc_); - } - - /// @notice Updates the mint fee percentage. - /// @param mintFeePerc_ New mint fee percentage. - function updateMintFeePerc(int256 mintFeePerc_) public onlyOwner { - mintFeePerc = mintFeePerc_; - emit UpdatedMintPerc(mintFeePerc_); - } - - /// @notice Updates the burn fee percentage. - /// @param burnFeePerc_ New burn fee percentage. - function updateBurnFeePerc(int256 burnFeePerc_) public onlyOwner { - burnFeePerc = burnFeePerc_; - emit UpdatedBurnPerc(burnFeePerc_); - } - - /// @notice Updates the rollover fee percentage. - /// @param rolloverFeePerc_ New rollover fee percentage. - function updateRolloverFeePerc(int256 rolloverFeePerc_) public onlyOwner { - rolloverFeePerc = rolloverFeePerc_; - emit UpdatedRolloverPerc(rolloverFeePerc_); - } - - /// @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); - } - - /// @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); - } - - /// @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); - } -} diff --git a/spot-contracts/contracts/strategies/CDRPricingStrategy.sol b/spot-contracts/contracts/strategies/CDRPricingStrategy.sol deleted file mode 100644 index 0fb261c9..00000000 --- a/spot-contracts/contracts/strategies/CDRPricingStrategy.sol +++ /dev/null @@ -1,42 +0,0 @@ -// SPDX-License-Identifier: BUSL-1.1 -pragma solidity ^0.8.19; - -import { IERC20Upgradeable } from "@openzeppelin/contracts-upgradeable/token/ERC20/IERC20Upgradeable.sol"; -import { ITranche } from "../_interfaces/buttonwood/ITranche.sol"; -import { IPricingStrategy } from "../_interfaces/IPricingStrategy.sol"; - -import { TrancheHelpers } from "../_utils/BondHelpers.sol"; - -/** - * @title CDRPricingStrategy (CDR -> collateral to debt ratio) - * - * @notice Prices the given tranche token based on it's CDR. - * - */ -contract CDRPricingStrategy is IPricingStrategy { - using TrancheHelpers for ITranche; - - uint8 private constant DECIMALS = 8; - uint256 private constant UNIT_PRICE = 10**DECIMALS; - - /// @inheritdoc IPricingStrategy - function computeTranchePrice(ITranche tranche) external view override returns (uint256) { - (uint256 collateralBalance, uint256 debt) = tranche.getTrancheCollateralization(); - return (debt > 0) ? ((collateralBalance * UNIT_PRICE) / debt) : UNIT_PRICE; - } - - /// @inheritdoc IPricingStrategy - /// @dev Selective handling for collateral for mature tranches are held by the perp reserve. - function computeMatureTranchePrice( - IERC20Upgradeable, /* collateralToken */ - uint256 collateralBalance, - uint256 debt - ) external pure override returns (uint256) { - return (debt > 0) ? ((collateralBalance * UNIT_PRICE) / debt) : UNIT_PRICE; - } - - /// @inheritdoc IPricingStrategy - function decimals() external pure override returns (uint8) { - return DECIMALS; - } -} diff --git a/spot-contracts/contracts/strategies/NonEquityCDRLBPricingStrategy.sol b/spot-contracts/contracts/strategies/NonEquityCDRLBPricingStrategy.sol deleted file mode 100644 index 0245625b..00000000 --- a/spot-contracts/contracts/strategies/NonEquityCDRLBPricingStrategy.sol +++ /dev/null @@ -1,44 +0,0 @@ -// SPDX-License-Identifier: BUSL-1.1 -pragma solidity ^0.8.4; - -import { IERC20Upgradeable } from "@openzeppelin/contracts-upgradeable/token/ERC20/IERC20Upgradeable.sol"; -import { ITranche } from "../_interfaces/buttonwood/ITranche.sol"; -import { IPricingStrategy } from "../_interfaces/IPricingStrategy.sol"; - -import { MathUpgradeable } from "@openzeppelin/contracts-upgradeable/utils/math/MathUpgradeable.sol"; - -/** - * @title NonEquityCDRLBPricingStrategy (CDRLB -> collateral to debt ratio - lower bound) - * - * @notice Prices a given tranche as the max(tranche's CDR, UNIT_PRICE). - * - * @dev This is only to be used for non-equity tranches. - * - */ -contract NonEquityCDRLBPricingStrategy is IPricingStrategy { - uint8 private constant DECIMALS = 8; - uint256 private constant UNIT_PRICE = 10**DECIMALS; - - /// @inheritdoc IPricingStrategy - function computeTranchePrice( - ITranche /* tranche */ - ) external pure override returns (uint256) { - // NOTE: The is an optimization. Non-equity tranches will never have a CDR > 1 before they mature. - return UNIT_PRICE; - } - - /// @inheritdoc IPricingStrategy - function computeMatureTranchePrice( - IERC20Upgradeable, /* collateralToken */ - uint256 collateralBalance, - uint256 debt - ) external pure override returns (uint256) { - uint256 trancheCDR = (debt > 0) ? ((collateralBalance * UNIT_PRICE) / debt) : UNIT_PRICE; - return MathUpgradeable.max(trancheCDR, UNIT_PRICE); - } - - /// @inheritdoc IPricingStrategy - function decimals() external pure override returns (uint8) { - return DECIMALS; - } -} diff --git a/spot-contracts/contracts/strategies/TrancheClassDiscountStrategy.sol b/spot-contracts/contracts/strategies/TrancheClassDiscountStrategy.sol deleted file mode 100644 index 6aca7dcb..00000000 --- a/spot-contracts/contracts/strategies/TrancheClassDiscountStrategy.sol +++ /dev/null @@ -1,79 +0,0 @@ -// SPDX-License-Identifier: BUSL-1.1 -pragma solidity ^0.8.19; - -import { IERC20Upgradeable } from "@openzeppelin/contracts-upgradeable/token/ERC20/IERC20Upgradeable.sol"; -import { ITranche } from "../_interfaces/buttonwood/ITranche.sol"; -import { IDiscountStrategy } from "../_interfaces/IDiscountStrategy.sol"; -import { IBondController } from "../_interfaces/buttonwood/IBondController.sol"; - -import { OwnableUpgradeable } from "@openzeppelin/contracts-upgradeable/access/OwnableUpgradeable.sol"; -import { BondTranches, BondTranchesHelpers, BondHelpers } from "../_utils/BondHelpers.sol"; - -/* - * @title TrancheClassDiscountStrategy - * - * @dev Discount factor defined for a particular "class" of tranches. - * Any tranche's class is defined as the unique combination of: - * - it's collateraToken - * - it's parent bond's trancheRatios - * - it's seniorityIDX. - * - * For example: - * - All AMPL [35-65] bonds can be configured to have a discount of [1, 0] and - * => An AMPL-A tranche token from any [35-65] bond will be applied a discount factor of 1. - * - All AMPL [50-50] bonds can be configured to have a discount of [0.8,0] - * => An AMPL-A tranche token from any [50-50] bond will be applied a discount factor of 0.8. - * - */ -contract TrancheClassDiscountStrategy is IDiscountStrategy, OwnableUpgradeable { - using BondHelpers for IBondController; - using BondTranchesHelpers for BondTranches; - - uint8 private constant DECIMALS = 18; - - /// @notice Mapping between a tranche class and the discount to be applied. - mapping(bytes32 => uint256) private _trancheDiscounts; - - /// @notice Event emitted when the defined tranche discounts are updated. - /// @param hash The tranche class hash. - /// @param discount The discount factor for any tranche belonging to that class. - event UpdatedDefinedTrancheDiscounts(bytes32 hash, uint256 discount); - - /// @notice Contract initializer. - function init() public initializer { - __Ownable_init(); - } - - /// @notice Updates the tranche class's discount. - /// @param classHash The tranche class (hash(collteralToken, trancheRatios, seniority)). - /// @param discount The discount factor. - function updateDefinedDiscount(bytes32 classHash, uint256 discount) public onlyOwner { - if (discount > 0) { - _trancheDiscounts[classHash] = discount; - } else { - delete _trancheDiscounts[classHash]; - } - emit UpdatedDefinedTrancheDiscounts(classHash, discount); - } - - /// @inheritdoc IDiscountStrategy - function computeTrancheDiscount(IERC20Upgradeable token) external view override returns (uint256) { - return _trancheDiscounts[trancheClass(ITranche(address(token)))]; - } - - /// @inheritdoc IDiscountStrategy - function decimals() external pure override returns (uint8) { - return DECIMALS; - } - - /// @notice The computes the class hash of a given tranche. - /// @dev A given tranche's computed class is the hash(collateralToken, trancheRatios, seniority). - /// This is used to identify different tranche tokens instances of the same class - /// @param tranche The address of the tranche token. - /// @return The class hash. - function trancheClass(ITranche tranche) public view returns (bytes32) { - IBondController bond = IBondController(tranche.bond()); - BondTranches memory bt = bond.getTranches(); - return keccak256(abi.encode(bond.collateralToken(), bt.trancheRatios, bt.indexOf(tranche))); - } -} diff --git a/spot-contracts/contracts/strategies/UnitPricingStrategy.sol b/spot-contracts/contracts/strategies/UnitPricingStrategy.sol deleted file mode 100644 index 521fbb71..00000000 --- a/spot-contracts/contracts/strategies/UnitPricingStrategy.sol +++ /dev/null @@ -1,38 +0,0 @@ -// SPDX-License-Identifier: BUSL-1.1 -pragma solidity ^0.8.19; - -import { IERC20Upgradeable } from "@openzeppelin/contracts-upgradeable/token/ERC20/IERC20Upgradeable.sol"; -import { ITranche } from "../_interfaces/buttonwood/ITranche.sol"; -import { IPricingStrategy } from "../_interfaces/IPricingStrategy.sol"; - -/** - * @title UnitPricingStrategy - * - * @notice All tranche tokens are assumed to have a price of 1. - * - */ -contract UnitPricingStrategy is IPricingStrategy { - uint8 private constant DECIMALS = 8; - uint256 private constant UNIT_PRICE = 10**DECIMALS; - - /// @inheritdoc IPricingStrategy - function computeTranchePrice( - ITranche /* tranche */ - ) external pure override returns (uint256) { - return UNIT_PRICE; - } - - /// @inheritdoc IPricingStrategy - function computeMatureTranchePrice( - IERC20Upgradeable, /* collateralToken */ - uint256, /* collateralBalance */ - uint256 /* debt */ - ) external pure override returns (uint256) { - return UNIT_PRICE; - } - - /// @inheritdoc IPricingStrategy - function decimals() external pure override returns (uint8) { - return DECIMALS; - } -} diff --git a/spot-contracts/contracts/test/BondHelpersTester.sol b/spot-contracts/contracts/test/BondHelpersTester.sol deleted file mode 100644 index 28009d49..00000000 --- a/spot-contracts/contracts/test/BondHelpersTester.sol +++ /dev/null @@ -1,56 +0,0 @@ -// SPDX-License-Identifier: BUSL-1.1 -pragma solidity ^0.8.19; - -import { BondHelpers, BondTranches, BondTranchesHelpers } from "../_utils/BondHelpers.sol"; -import { IBondController } from "../_interfaces/buttonwood/IBondController.sol"; -import { ITranche } from "../_interfaces/buttonwood/ITranche.sol"; - -contract BondHelpersTester { - using BondHelpers for IBondController; - using BondTranchesHelpers for BondTranches; - - function secondsToMaturity(IBondController b) public view returns (uint256) { - return b.secondsToMaturity(); - } - - function getTranches(IBondController b) public view returns (BondTranches memory bt) { - return b.getTranches(); - } - - function previewDeposit(IBondController b, uint256 collateralAmount) - public - view - returns ( - BondTranches memory, - uint256[] memory, - uint256[] memory - ) - { - return b.previewDeposit(collateralAmount); - } - - function getTrancheCollateralizations(IBondController b) - public - view - returns ( - BondTranches memory bt, - uint256[] memory, - uint256[] memory - ) - { - return b.getTrancheCollateralizations(); - } - - function indexOf(IBondController b, ITranche t) public view returns (uint8) { - BondTranches memory bt = b.getTranches(); - return bt.indexOf(t); - } - - function computeRedeemableTrancheAmounts(IBondController b, address u) - public - view - returns (BondTranches memory bt, uint256[] memory) - { - return b.computeRedeemableTrancheAmounts(u); - } -} diff --git a/spot-contracts/contracts/test/MathTester.sol b/spot-contracts/contracts/test/MathTester.sol deleted file mode 100644 index ce473f28..00000000 --- a/spot-contracts/contracts/test/MathTester.sol +++ /dev/null @@ -1,12 +0,0 @@ -// SPDX-License-Identifier: BUSL-1.1 -pragma solidity ^0.8.19; - -import { SignedMathHelpers } from "../_utils/SignedMathHelpers.sol"; - -contract MathTester { - using SignedMathHelpers for int256; - - function sign(int256 a) public pure returns (int256) { - return a.sign(); - } -} diff --git a/spot-contracts/contracts/vaults/RolloverVault.sol b/spot-contracts/contracts/vaults/RolloverVault.sol deleted file mode 100644 index 0e6dc0c6..00000000 --- a/spot-contracts/contracts/vaults/RolloverVault.sol +++ /dev/null @@ -1,598 +0,0 @@ -// SPDX-License-Identifier: BUSL-1.1 -pragma solidity ^0.8.19; - -import { IERC20Upgradeable, IPerpetualTranche, IBondIssuer, IBondController, ITranche } from "../_interfaces/IPerpetualTranche.sol"; -import { IVault, UnexpectedAsset, UnauthorizedTransferOut, InsufficientDeployment, DeployedCountOverLimit } from "../_interfaces/IVault.sol"; - -import { OwnableUpgradeable } from "@openzeppelin/contracts-upgradeable/access/OwnableUpgradeable.sol"; -import { PausableUpgradeable } from "@openzeppelin/contracts-upgradeable/security/PausableUpgradeable.sol"; -import { ReentrancyGuardUpgradeable } from "@openzeppelin/contracts-upgradeable/security/ReentrancyGuardUpgradeable.sol"; -import { MathUpgradeable } from "@openzeppelin/contracts-upgradeable/utils/math/MathUpgradeable.sol"; -import { ERC20BurnableUpgradeable } from "@openzeppelin/contracts-upgradeable/token/ERC20/extensions/ERC20BurnableUpgradeable.sol"; -import { EnumerableSetUpgradeable } from "@openzeppelin/contracts-upgradeable/utils/structs/EnumerableSetUpgradeable.sol"; -import { SafeERC20Upgradeable } from "@openzeppelin/contracts-upgradeable/token/ERC20/utils/SafeERC20Upgradeable.sol"; -import { BondTranches, TrancheHelpers, BondHelpers } from "../_utils/BondHelpers.sol"; - -/// @notice Storage array access out of bounds. -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. - * - * 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: The vault redeems tranches for the underlying asset. - * NOTE: It performs both mature and immature redemption. Read more: https://bit.ly/3tuN6OC - * - * - */ -contract RolloverVault is - ERC20BurnableUpgradeable, - OwnableUpgradeable, - PausableUpgradeable, - ReentrancyGuardUpgradeable, - IVault -{ - // data handling - using EnumerableSetUpgradeable for EnumerableSetUpgradeable.AddressSet; - using BondHelpers for IBondController; - using TrancheHelpers for ITranche; - - // ERC20 operations - using SafeERC20Upgradeable for IERC20Upgradeable; - - // math - using MathUpgradeable for uint256; - - //------------------------------------------------------------------------- - // Events - - /// @notice Emits the vault asset's token balance that's recorded after a change. - /// @param token Address of token. - /// @param balance The recorded ERC-20 balance of the token. - event AssetSynced(IERC20Upgradeable token, uint256 balance); - - //------------------------------------------------------------------------- - // Constants - uint8 public constant PERC_DECIMALS = 6; - uint256 public constant UNIT_PERC = 10**PERC_DECIMALS; - uint256 public constant HUNDRED_PERC = 100 * UNIT_PERC; - - /// @dev Initial exchange rate between the underlying asset and notes. - uint256 private constant INITIAL_RATE = 10**6; - - /// @dev Values should line up as is in the perp contract. - uint8 private constant PERP_PRICE_DECIMALS = 8; - uint256 private constant PERP_UNIT_PRICE = (10**PERP_PRICE_DECIMALS); - - /// @dev The maximum number of deployed assets that can be held in this vault at any given time. - uint256 public constant MAX_DEPLOYED_COUNT = 47; - - //-------------------------------------------------------------------------- - // ASSETS - // - // The vault's assets are represented by a master list of ERC-20 tokens - // => { [underlying] U _deployed U _earned } - // - // In the case of this vault, the "earned" assets are the perp tokens themselves. - // The reward (or yield) for performing rollovers is paid out in perp tokens. - - /// @notice The ERC20 token that can be deposited into this vault. - IERC20Upgradeable public underlying; - - /// @dev The set of the intermediate ERC-20 tokens when the underlying asset has been put to use. - /// In the case of this vault, they represent the tranche tokens held before maturity. - EnumerableSetUpgradeable.AddressSet private _deployed; - - //------------------------------------------------------------------------- - // Storage - - /// @notice Minimum amount of underlying assets that must be deployed, for a deploy operation to succeed. - /// @dev The deployment transaction reverts, if the vaults does not have sufficient underlying tokens - /// to cover the minimum deployment amount. - uint256 public minDeploymentAmt; - - /// @notice The perpetual token on which rollovers are performed. - IPerpetualTranche public perp; - - //-------------------------------------------------------------------------- - // Construction & Initialization - - /// @notice Contract state initialization. - /// @param name ERC-20 Name of the vault token. - /// @param symbol ERC-20 Symbol of the vault token. - /// @param perp_ ERC-20 address of the perpetual tranche rolled over. - function init( - string memory name, - string memory symbol, - IPerpetualTranche perp_ - ) public initializer { - __ERC20_init(name, symbol); - __ERC20Burnable_init(); - __Ownable_init(); - __Pausable_init(); - __ReentrancyGuard_init(); - - underlying = perp_.collateral(); - _syncAsset(underlying); - - perp = perp_; - } - - //-------------------------------------------------------------------------- - // ADMIN only methods - - /// @notice Pauses deposits, withdrawals and vault operations. - /// @dev NOTE: ERC-20 functions, like transfers will always remain operational. - function pause() external onlyOwner { - _pause(); - } - - /// @notice Unpauses deposits, withdrawals and vault operations. - /// @dev NOTE: ERC-20 functions, like transfers will always remain operational. - function unpause() external onlyOwner { - _unpause(); - } - - /// @notice Updates the minimum deployment amount. - /// @param minDeploymentAmt_ The new minimum deployment amount, denominated in underlying tokens. - function updateMinDeploymentAmt(uint256 minDeploymentAmt_) external onlyOwner { - minDeploymentAmt = minDeploymentAmt_; - } - - /// @notice Transfers a non-vault token out of the contract, which may have been added accidentally. - /// @param token The token address. - /// @param to The destination address. - /// @param amount The amount of tokens to be transferred. - function transferERC20( - IERC20Upgradeable token, - address to, - uint256 amount - ) external onlyOwner { - if (isVaultAsset(token)) { - revert UnauthorizedTransferOut(token); - } - token.safeTransfer(to, amount); - } - - //-------------------------------------------------------------------------- - // External & Public write methods - - /// @inheritdoc IVault - /// @dev Simply batches the `recover` and `deploy` functions. Reverts if there are no funds to deploy. - function recoverAndRedeploy() external override { - recover(); - deploy(); - } - - /// @inheritdoc IVault - /// @dev Its safer to call `recover` before `deploy` so the full available balance can be deployed. - /// Reverts if no funds are rolled over or if the minimum deployment threshold is not reached. - function deploy() public override nonReentrant whenNotPaused { - (uint256 deployedAmt, BondTranches memory bt) = _tranche(perp.getDepositBond()); - uint256 perpsRolledOver = _rollover(perp, bt); - // NOTE: The following enforces that we only tranche the underlying if it can immediately be used for rotations. - if (deployedAmt <= minDeploymentAmt || perpsRolledOver <= 0) { - revert InsufficientDeployment(); - } - } - - /// @inheritdoc IVault - function recover() public override nonReentrant whenNotPaused { - uint256 deployedCount_ = _deployed.length(); - if (deployedCount_ <= 0) { - return; - } - - // execute redemption on each deployed asset - for (uint256 i = 0; i < deployedCount_; i++) { - ITranche tranche = ITranche(_deployed.at(i)); - uint256 trancheBalance = tranche.balanceOf(address(this)); - - // if the vault has no tranche balance, - // we update our internal book-keeping and continue to the next one. - if (trancheBalance <= 0) { - continue; - } - - // get the parent bond - IBondController bond = IBondController(tranche.bond()); - - // if bond has matured, redeem the tranche token - if (bond.secondsToMaturity() <= 0) { - // execute redemption - _execMatureTrancheRedemption(bond, tranche, trancheBalance); - } - // if not redeem using proportional balances - // redeems this tranche and it's siblings if the vault holds balances. - // NOTE: For gas optimization, we perform this operation only once - // ie) when we encounter the most-senior tranche. - else if (tranche == bond.trancheAt(0)) { - // execute redemption - _execImmatureTrancheRedemption(bond); - } - } - - // sync deployed tranches - // 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 = deployedCount_; i > 0; i--) { - _syncAndRemoveDeployedAsset(IERC20Upgradeable(_deployed.at(i - 1))); - } - - // sync underlying - _syncAsset(underlying); - } - - /// @inheritdoc IVault - /// @dev Reverts when attempting to recover a tranche which is not part of the deployed list. - /// In the case of immature redemption, this method will recover other sibling tranches as well. - function recover(IERC20Upgradeable token) external override nonReentrant whenNotPaused { - if (!_deployed.contains(address(token))) { - revert UnexpectedAsset(token); - } - - ITranche tranche = ITranche(address(token)); - uint256 trancheBalance = tranche.balanceOf(address(this)); - - // if the vault has no tranche balance, - // we update our internal book-keeping and return. - if (trancheBalance <= 0) { - _syncAndRemoveDeployedAsset(tranche); - return; - } - - // get the parent bond - IBondController bond = IBondController(tranche.bond()); - - // if bond has matured, redeem the tranche token - if (bond.secondsToMaturity() <= 0) { - // execute redemption - _execMatureTrancheRedemption(bond, tranche, trancheBalance); - - // sync deployed asset - _syncAndRemoveDeployedAsset(tranche); - } - // if not redeem using proportional balances - // redeems this tranche and it's siblings if the vault holds balances. - else { - // execute redemption - BondTranches memory bt = _execImmatureTrancheRedemption(bond); - - // sync deployed asset, ie current tranche and all its siblings. - for (uint8 j = 0; j < bt.tranches.length; j++) { - _syncAndRemoveDeployedAsset(bt.tranches[j]); - } - } - - // sync underlying - _syncAsset(underlying); - } - - /// @inheritdoc IVault - function deposit(uint256 amount) external override nonReentrant whenNotPaused returns (uint256) { - uint256 totalSupply_ = totalSupply(); - uint256 notes = (totalSupply_ > 0) ? totalSupply_.mulDiv(amount, getTVL()) : (amount * INITIAL_RATE); - - underlying.safeTransferFrom(_msgSender(), address(this), amount); - _syncAsset(underlying); - - _mint(_msgSender(), notes); - return notes; - } - - /// @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 - 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)); - } - redemptions[deployedCount_ + 1].token = IERC20Upgradeable(perp); - - // burn notes - _burn(_msgSender(), notes); - - // calculating amounts and transferring assets out proportionally - for (uint256 i = 0; i < assetCount; i++) { - redemptions[i].amount = redemptions[i].token.balanceOf(address(this)).mulDiv(notes, totalNotes); - redemptions[i].token.safeTransfer(_msgSender(), redemptions[i].amount); - _syncAsset(redemptions[i].token); - } - - return redemptions; - } - - /// @inheritdoc IVault - /// @dev The total value is denominated in the underlying asset. - function getTVL() public override returns (uint256) { - uint256 totalValue = 0; - - // The underlying balance - totalValue += underlying.balanceOf(address(this)); - - // The deployed asset value denominated in the underlying - for (uint256 i = 0; i < _deployed.length(); i++) { - ITranche tranche = ITranche(_deployed.at(i)); - uint256 trancheBalance = tranche.balanceOf(address(this)); - if (trancheBalance > 0) { - (uint256 collateralBalance, uint256 trancheSupply) = tranche.getTrancheCollateralization(); - totalValue += collateralBalance.mulDiv(trancheBalance, trancheSupply); - } - } - - // The earned asset (perp token) value denominated in the underlying - uint256 perpBalance = perp.balanceOf(address(this)); - if (perpBalance > 0) { - // The "earned" asset is assumed to be the perp token. - // Perp tokens are assumed to have the same denomination as the underlying - totalValue += perpBalance.mulDiv(IPerpetualTranche(address(perp)).getAvgPrice(), PERP_UNIT_PRICE); - } - - return totalValue; - } - - /// @inheritdoc IVault - /// @dev The asset value is denominated in the underlying asset. - function getVaultAssetValue(IERC20Upgradeable token) external override returns (uint256) { - // Underlying asset - if (token == underlying) { - return token.balanceOf(address(this)); - } - // Deployed asset - else if (_deployed.contains(address(token))) { - (uint256 collateralBalance, uint256 trancheSupply) = ITranche(address(token)).getTrancheCollateralization(); - return collateralBalance.mulDiv(token.balanceOf(address(this)), trancheSupply); - } - // Earned asset - else if (address(token) == address(perp)) { - return ( - token.balanceOf(address(this)).mulDiv(IPerpetualTranche(address(perp)).getAvgPrice(), PERP_UNIT_PRICE) - ); - } - - // Not a vault asset, so returning zero - return 0; - } - - //-------------------------------------------------------------------------- - // External & Public read methods - - /// @inheritdoc IVault - function vaultAssetBalance(IERC20Upgradeable token) external view override returns (uint256) { - return isVaultAsset(token) ? token.balanceOf(address(this)) : 0; - } - - /// @inheritdoc IVault - function deployedCount() external view override returns (uint256) { - return _deployed.length(); - } - - /// @inheritdoc IVault - function deployedAt(uint256 i) external view override returns (IERC20Upgradeable) { - return IERC20Upgradeable(_deployed.at(i)); - } - - /// @inheritdoc IVault - function earnedCount() external pure returns (uint256) { - return 1; - } - - /// @inheritdoc IVault - function earnedAt(uint256 i) external view override returns (IERC20Upgradeable) { - if (i > 0) { - revert OutOfBounds(); - } - return IERC20Upgradeable(perp); - } - - /// @inheritdoc IVault - function isVaultAsset(IERC20Upgradeable token) public view override returns (bool) { - return (token == underlying) || _deployed.contains(address(token)) || (address(token) == address(perp)); - } - - //-------------------------------------------------------------------------- - // Private write methods - - /// @dev Deposits underlying balance into the provided bond and receives tranche tokens in return. - /// And performs some book-keeping to keep track of the vault's assets. - /// @return balance The amount of underlying assets tranched. - /// @return bt The given bonds tranche data. - function _tranche(IBondController bond) private returns (uint256, BondTranches memory) { - // Get bond's tranche data - BondTranches memory bt = bond.getTranches(); - - // Get underlying balance - uint256 balance = underlying.balanceOf(address(this)); - - // Skip if balance is zero - if (balance <= 0) { - return (0, bt); - } - - // balance is tranched - _checkAndApproveMax(underlying, address(bond), balance); - bond.deposit(balance); - - // sync holdings - for (uint8 i = 0; i < bt.tranches.length; i++) { - _syncAndAddDeployedAsset(bt.tranches[i]); - } - _syncAsset(underlying); - - return (balance, bt); - } - - /// @dev Rolls over freshly tranched tokens from the given bond for older tranches (close to maturity) from perp. - /// And performs some book-keeping to keep track of the vault's assets. - /// @return The amount of perps rolled over. - function _rollover(IPerpetualTranche perp_, BondTranches memory bt) private returns (uint256) { - // NOTE: The first element of the list is the mature tranche, - // there after the list is NOT ordered by maturity. - IERC20Upgradeable[] memory rolloverTokens = perp_.getReserveTokensUpForRollover(); - - // Batch rollover - uint256 totalPerpRolledOver = 0; - uint8 vaultTrancheIdx = 0; - uint256 perpTokenIdx = 0; - - // We pair tranche tokens held by the vault with tranche tokens held by perp, - // And execute the rollover and continue to the next token with a usable balance. - while (vaultTrancheIdx < bt.tranches.length && perpTokenIdx < rolloverTokens.length) { - // trancheIntoPerp refers to the tranche going into perp from the vault - ITranche trancheIntoPerp = bt.tranches[vaultTrancheIdx]; - - // tokenOutOfPerp is the reserve token coming out of perp into the vault - IERC20Upgradeable tokenOutOfPerp = rolloverTokens[perpTokenIdx]; - - // compute available token out - uint256 tokenOutAmtAvailable = address(tokenOutOfPerp) != address(0) - ? tokenOutOfPerp.balanceOf(perp_.reserve()) - : 0; - - // trancheIntoPerp tokens are NOT exhausted but tokenOutOfPerp is exhausted - if (tokenOutAmtAvailable <= 0) { - // Rollover is a no-op, so skipping to next tokenOutOfPerp - ++perpTokenIdx; - continue; - } - - // Compute available tranche in - uint256 trancheInAmtAvailable = trancheIntoPerp.balanceOf(address(this)); - - // trancheInAmtAvailable is exhausted - if (trancheInAmtAvailable <= 0) { - // Rollover is a no-op, so skipping to next trancheIntoPerp - ++vaultTrancheIdx; - continue; - } - - // Preview rollover - IPerpetualTranche.RolloverPreview memory rd = perp_.computeRolloverAmt( - trancheIntoPerp, - tokenOutOfPerp, - trancheInAmtAvailable, - tokenOutAmtAvailable - ); - - // trancheIntoPerp isn't accepted by perp, likely because it's yield=0, refer perp docs for more info - if (rd.perpRolloverAmt <= 0) { - // Rollover is a no-op, so skipping to next trancheIntoPerp - ++vaultTrancheIdx; - continue; - } - - // Perform rollover - _checkAndApproveMax(trancheIntoPerp, address(perp_), trancheInAmtAvailable); - perp_.rollover(trancheIntoPerp, tokenOutOfPerp, trancheInAmtAvailable); - - // sync deployed asset sent to perp - _syncAndRemoveDeployedAsset(trancheIntoPerp); - - // skip insertion into the deployed list the case of the mature tranche, ie underlying - if (tokenOutOfPerp != underlying) { - // sync deployed asset retrieved from perp - _syncAndAddDeployedAsset(tokenOutOfPerp); - } - - // keep track of total amount rolled over - totalPerpRolledOver += rd.perpRolloverAmt; - } - - // sync underlying and earned (ie perp) - _syncAsset(underlying); - _syncAsset(perp_); - - return totalPerpRolledOver; - } - - /// @dev Low level method that redeems the given mature tranche for the underlying asset. - /// It interacts with the button-wood bond contract. - /// This function should NOT be called directly, use `recover()` or `recover(tranche)` - /// which wrap this function with the internal book-keeping necessary, - /// to keep track of the vault's assets. - function _execMatureTrancheRedemption( - IBondController bond, - ITranche tranche, - uint256 amount - ) private { - if (!bond.isMature()) { - bond.mature(); - } - bond.redeemMature(address(tranche), amount); - } - - /// @dev Low level method that redeems the given tranche for the underlying asset, before maturity. - /// If the vault holds sibling tranches with proportional balances, those will also get redeemed. - /// It interacts with the button-wood bond contract. - /// This function should NOT be called directly, use `recover()` or `recover(tranche)` - /// which wrap this function with the internal book-keeping necessary, - /// to keep track of the vault's assets. - function _execImmatureTrancheRedemption(IBondController bond) private returns (BondTranches memory bt) { - uint256[] memory trancheAmts; - (bt, trancheAmts) = bond.computeRedeemableTrancheAmounts(address(this)); - - // NOTE: It is guaranteed that if one tranche amount is zero, all amounts are zeros. - if (trancheAmts[0] > 0) { - bond.redeem(trancheAmts); - } - - return bt; - } - - /// @dev Syncs balance and adds the given asset into the deployed list if the vault has a balance. - function _syncAndAddDeployedAsset(IERC20Upgradeable token) private { - uint256 balance = token.balanceOf(address(this)); - emit AssetSynced(token, balance); - - if (balance > 0 && !_deployed.contains(address(token))) { - // Inserts new token into the deployed assets list. - _deployed.add(address(token)); - if (_deployed.length() > MAX_DEPLOYED_COUNT) { - revert DeployedCountOverLimit(); - } - } - } - - /// @dev Syncs balance and removes the given asset from the deployed list if the vault has no balance. - function _syncAndRemoveDeployedAsset(IERC20Upgradeable token) private { - uint256 balance = token.balanceOf(address(this)); - emit AssetSynced(token, balance); - - if (balance <= 0 && _deployed.contains(address(token))) { - // Removes token into the deployed assets list. - _deployed.remove(address(token)); - } - } - - /// @dev Logs the token balance held by the vault. - function _syncAsset(IERC20Upgradeable token) private { - uint256 balance = token.balanceOf(address(this)); - emit AssetSynced(token, balance); - } - - /// @dev Checks if the spender has sufficient allowance. If not, approves the maximum possible amount. - function _checkAndApproveMax( - IERC20Upgradeable token, - address spender, - uint256 amount - ) private { - uint256 allowance = token.allowance(address(this), spender); - if (allowance < amount) { - token.safeApprove(spender, 0); - token.safeApprove(spender, type(uint256).max); - } - } -} diff --git a/spot-contracts/deployments/goerli.json b/spot-contracts/deployments/goerli.json deleted file mode 100644 index 91b889d4..00000000 --- a/spot-contracts/deployments/goerli.json +++ /dev/null @@ -1,12 +0,0 @@ -{ - "network": "goerli", - "startBlock": 7476901, - "ampl": "0x74567107828843070087F1c6ec8322A3e8450725", - "bondFactory": "0xdDe914EfBF5C472a590e61658d8E342d17E3AAB7", - "bondIssuer": "0xbC060a1EbEC5eC869C4D51d4563244d4a223D307", - "spot": "0x95014Bc18F82a98CFAA3253fbD3184125A01f848", - "proxyAdmin": "0x47bF554606254dCEC37119348AA201c5A4ef2C58", - "router": "0x5e902bdCC408550b4BD612678bE2d57677664Dc9", - "previousIssuers": ["0xAb7d17864463dEdA6c19060Ad6556e1B218c5Ba0"], - "rolloverVault": "0xca36B64BEbdf141623911987b93767dcA4bF6F1f" -} diff --git a/spot-contracts/deployments/sepolia.json b/spot-contracts/deployments/sepolia.json new file mode 100644 index 00000000..ddef753b --- /dev/null +++ b/spot-contracts/deployments/sepolia.json @@ -0,0 +1,13 @@ +{ + "network": "sepolia", + "startBlock": 5492003, + "ampl": "0x251410f849ad67bebffdb5a549e5f02d5d9c25ba", + "bondFactory": "0x25BcaEd6377CEAA345f12C2005a42e669B8a29fC", + "bondIssuer": "0x3838C8d4D092d40Cb27DD22Dafc6E1A81ea2DB60", + "previousIssuers": [], + "proxyAdmin": "0x0584042677d469C0B95775368cF1EFfe9cc222F5", + "feePolicy": "0x2DdF288F26490D1147296cC0FA2B3c4da5E15f10", + "spot": "0xdcCef9065876fD654bAddeBAa778FDA43E0bfC1F", + "vault": "0x107614c6602A8e602952Da107B8fE62b5Ab13b04", + "router": "0x5B59915E5754C62C40Ba5e7467382ced958F8559" +} diff --git a/spot-contracts/hardhat.config.ts b/spot-contracts/hardhat.config.ts index 56840a33..5d66ddf7 100644 --- a/spot-contracts/hardhat.config.ts +++ b/spot-contracts/hardhat.config.ts @@ -2,8 +2,8 @@ import { HardhatUserConfig } from "hardhat/config"; import { Wallet } from "ethers"; import "@nomiclabs/hardhat-ethers"; -import "@nomiclabs/hardhat-waffle"; import "@nomicfoundation/hardhat-chai-matchers"; +import "@nomicfoundation/hardhat-verify"; import "@openzeppelin/hardhat-upgrades"; import "solidity-coverage"; import "hardhat-gas-reporter"; @@ -30,14 +30,13 @@ export default { url: "http://127.0.0.1:8545", chainId: 1337, }, - goerli: { - // url: `https://goerli.infura.io/v3/${process.env.INFURA_SECRET}`, - url: `https://eth-goerli.g.alchemy.com/v2/${process.env.ALCHEMY_SECRET}`, + sepolia: { + // url: `https://sepolia.infura.io/v3/${process.env.INFURA_SECRET}`, + url: `https://eth-sepolia.g.alchemy.com/v2/${process.env.ALCHEMY_SECRET}`, accounts: { mnemonic: process.env.PROD_MNEMONIC || Wallet.createRandom().mnemonic.phrase, }, - gasMultiplier: 1.03, - allowUnlimitedContractSize: true, + gasMultiplier: 1.01, }, mainnet: { // url: `https://mainnet.infura.io/v3/${process.env.INFURA_SECRET}`, @@ -51,11 +50,11 @@ export default { solidity: { compilers: [ { - version: "0.8.19", + version: "0.8.20", settings: { optimizer: { enabled: true, - runs: 200, + runs: 750, }, }, }, @@ -72,5 +71,6 @@ export default { }, mocha: { bail: false, + timeout: 100000000, }, } as HardhatUserConfig; diff --git a/spot-contracts/package.json b/spot-contracts/package.json index 1b8ac53c..28a57f1d 100644 --- a/spot-contracts/package.json +++ b/spot-contracts/package.json @@ -21,11 +21,13 @@ "devDependencies": { "@defi-wonderland/smock": "^2.3.4", "@ethersproject/abi": "^5.6.4", + "@ethersproject/abstract-provider": "^5.7.0", + "@ethersproject/abstract-signer": "^5.7.0", "@ethersproject/bytes": "^5.6.1", "@ethersproject/providers": "^5.6.8", "@nomicfoundation/hardhat-chai-matchers": "^1.0.6", + "@nomicfoundation/hardhat-verify": "^1.1.0", "@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", @@ -34,6 +36,7 @@ "@types/chai": "^4.3.1", "@types/mocha": "^9.1.1", "@types/node": "^18.6.1", + "@types/sinon-chai": "^3.2.12", "@typescript-eslint/eslint-plugin": "^5.0.0", "@typescript-eslint/parser": "^5.0.0", "chai": "^4.3.6", @@ -47,16 +50,17 @@ "eslint-plugin-node": "^11.1.0", "eslint-plugin-prettier": "^4.2.1", "eslint-plugin-promise": "^6.0.0", + "eslint-plugin-unused-imports": "^3.0.0", "ethereum-waffle": "^3.4.4", "ethers": "^5.6.9", "ganache-cli": "^6.12.2", - "hardhat": "^2.12.6", + "hardhat": "^2.19.4", "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.8.2", + "solidity-coverage": "^0.8.5", "ts-node": "^10.9.1", "typechain": "^8.1.0", "typescript": "^4.7.4" diff --git a/spot-contracts/tasks/deploy/perp.ts b/spot-contracts/tasks/deploy/perp.ts index 68ba5618..b6e3f5c6 100644 --- a/spot-contracts/tasks/deploy/perp.ts +++ b/spot-contracts/tasks/deploy/perp.ts @@ -1,5 +1,6 @@ import { task, types } from "hardhat/config"; import { TaskArguments } from "hardhat/types"; +import { getImplementationAddress } from "@openzeppelin/upgrades-core"; import { sleep } from "../helpers"; task("deploy:BondIssuer") @@ -10,6 +11,7 @@ task("deploy:BondIssuer") .addParam("collateralTokenAddress", "address of the collateral token", undefined, types.string, false) .addParam("trancheRatios", "list of tranche ratios", undefined, types.json, false) .addParam("verify", "flag to set false for local deployments", true, types.boolean) + .addParam("issue", "flag to set true to issue first bond", false, types.boolean) .setAction(async function (args: TaskArguments, hre) { const { bondFactoryAddress, @@ -19,18 +21,26 @@ task("deploy:BondIssuer") collateralTokenAddress, trancheRatios, verify, + issue, } = args; - console.log("Signer", await (await hre.ethers.getSigners())[0].getAddress()); - - const BondIssuer = await hre.ethers.getContractFactory("BondIssuer"); - const bondIssuer = await BondIssuer.deploy(bondFactoryAddress, collateralTokenAddress); - await bondIssuer.deployed(); - await (await bondIssuer.init(bondDuration, trancheRatios, issueFrequency, issueWindowOffset)).wait(); - // await (await bondIssuer.issue()).wait(); + const signers = await hre.ethers.getSigners(); + const deployer = signers[0]; + console.log("Signer", await deployer.getAddress()); + const BondIssuer = hre.ethers.getContractFactory("BondIssuer"); + const bondIssuer = await hre.upgrades.deployProxy( + BondIssuer.connect(deployer), + [bondFactoryAddress, collateralTokenAddress, bondDuration, trancheRatios, issueFrequency, issueWindowOffset], + { + initializer: "init(address,address,uint256,uint256[],uint256,uint256)", + }, + ); + if (issue) { + await (await bondIssuer.issue()).wait(); + } if (verify) { - await sleep(15); + await sleep(30); await hre.run("verify:contract", { address: bondIssuer.address, constructorArguments: [bondFactoryAddress, collateralTokenAddress], @@ -42,122 +52,88 @@ task("deploy:BondIssuer") console.log("Bond issuer", bondIssuer.address); }); -task("deploy:PerpetualTranche") +task("deploy:PerpSystem") .addParam("bondIssuerAddress", "the address of the bond issuer", undefined, types.string, false) .addParam("collateralTokenAddress", "the address of the collateral token", undefined, types.string, false) - .addParam("name", "the ERC20 name", undefined, types.string, false) - .addParam("symbol", "the ERC20 symbol", undefined, types.string, false) - .addParam("pricingStrategyRef", "the pricing strategy to be used", "CDRPricingStrategy", types.string) + .addParam("perpName", "the ERC20 name of the perp token", undefined, types.string, false) + .addParam("perpSymbol", "the ERC20 symbol of the perp token", undefined, types.string, false) + .addParam("vaultName", "the ERC20 name of the vault token", undefined, types.string, false) + .addParam("vaultSymbol", "the ERC20 symbol of the vault token", undefined, types.string, false) .addParam("verify", "flag to set false for local deployments", true, types.boolean) - .setAction(async function (args: TaskArguments, hre) { - const { bondIssuerAddress, collateralTokenAddress, name, symbol, pricingStrategyRef, verify } = args; + const { bondIssuerAddress, collateralTokenAddress, perpName, perpSymbol, vaultName, vaultSymbol, verify } = args; const deployer = (await hre.ethers.getSigners())[0]; console.log("Signer", await deployer.getAddress()); + const FeePolicy = await hre.ethers.getContractFactory("FeePolicy"); + const feePolicy = await hre.upgrades.deployProxy(FeePolicy.connect(deployer)); + await feePolicy.deployed(); + const PerpetualTranche = await hre.ethers.getContractFactory("PerpetualTranche"); const perp = await hre.upgrades.deployProxy(PerpetualTranche.connect(deployer)); await perp.deployed(); - const BasicFeeStrategy = await hre.ethers.getContractFactory("BasicFeeStrategy"); - const feeStrategy = await BasicFeeStrategy.deploy(perp.address, perp.address); - await feeStrategy.deployed(); - await (await feeStrategy.init("0", "0", "0")).wait(); - - const PricingStrategy = await hre.ethers.getContractFactory(pricingStrategyRef); - const pricingStrategy = await PricingStrategy.deploy(); - await pricingStrategy.deployed(); - - const TrancheClassDiscountStrategy = await hre.ethers.getContractFactory("TrancheClassDiscountStrategy"); - const discountStrategy = await TrancheClassDiscountStrategy.deploy(); - await discountStrategy.deployed(); - await (await discountStrategy.init()).wait(); + const RolloverVault = await hre.ethers.getContractFactory("RolloverVault"); + const vault = await hre.upgrades.deployProxy(RolloverVault.connect(deployer)); + await vault.deployed(); console.log("perp", perp.address); - console.log("feeStrategy", feeStrategy.address); - console.log("pricingStrategy", pricingStrategy.address); - console.log("discountStrategy", discountStrategy.address); + console.log("vault", vault.address); + console.log("feePolicy", feePolicy.address); - const initTx = await perp.init( - name, - symbol, + console.log("fee policy init"); + await (await feePolicy.init()).wait(); + + console.log("perp init"); + const perpInitTx = await perp.init( + perpName, + perpSymbol, collateralTokenAddress, bondIssuerAddress, - feeStrategy.address, - pricingStrategy.address, - discountStrategy.address, + feePolicy.address, ); - await initTx.wait(); + await perpInitTx.wait(); + + console.log("vault init"); + const vaultInitTx = await vault.init(vaultName, vaultSymbol, perp.address, feePolicy.address); + await vaultInitTx.wait(); + + console.log("point perp to vault"); + await (await perp.updateVault(vault.address)).wait(); if (verify) { - await sleep(15); + await sleep(30); + // We just need to verify the proxy once await hre.run("verify:contract", { - address: feeStrategy.address, - constructorArguments: [perp.address, perp.address, "1000000", "1000000", "0"], + address: feePolicy.address, }); - + // Verifying implementations await hre.run("verify:contract", { - address: pricingStrategy.address, + address: await getImplementationAddress(hre.ethers.provider, feePolicy.address), }); - await hre.run("verify:contract", { - address: discountStrategy.address, + address: await getImplementationAddress(hre.ethers.provider, perp.address), }); - await hre.run("verify:contract", { - address: perp.address, + address: await getImplementationAddress(hre.ethers.provider, vault.address), }); } else { console.log("Skipping verification"); } }); -task("deploy:DiscountStrategy:computeDiscountHash") - .addParam("collateralTokenAddress", "the address of the collateral token", undefined, types.string, false) - .addParam("trancheRatios", "the bond's tranche ratios", undefined, types.json, false) - .addParam("trancheIndex", "the tranche's index", undefined, types.string, false) - .setAction(async function (args: TaskArguments, hre) { - const { collateralTokenAddress, trancheRatios, trancheIndex } = args; - const abiCoder = new hre.ethers.utils.AbiCoder(); - const hash = hre.ethers.utils.keccak256( - abiCoder.encode(["address", "uint256[]", "uint256"], [collateralTokenAddress, trancheRatios, trancheIndex]), - ); - console.log(hash); - }); - -task("deploy:DiscountStrategy:setDiscount") - .addParam("discountStrategyAddress", "the address of the discount strategy contract", undefined, types.string, false) - .addParam("collateralTokenAddress", "the address of the collateral token", undefined, types.string, false) - .addParam("trancheRatios", "the bond's tranche ratios", undefined, types.json, false) - .addParam("trancheIndex", "the tranche's index", undefined, types.string, false) - .addParam("trancheDiscount", "the discounts to be set in float", undefined, types.string, false) - .setAction(async function (args: TaskArguments, hre) { - const { discountStrategyAddress, collateralTokenAddress, trancheRatios, trancheIndex, trancheDiscount } = args; - const discountStrategy = await hre.ethers.getContractAt("TrancheClassDiscountStrategy", discountStrategyAddress); - const abiCoder = new hre.ethers.utils.AbiCoder(); - const hash = hre.ethers.utils.keccak256( - abiCoder.encode(["address", "uint256[]", "uint8"], [collateralTokenAddress, trancheRatios, trancheIndex]), - ); - const tx = await discountStrategy.updateDefinedDiscount( - hash, - hre.ethers.utils.parseUnits(trancheDiscount, await discountStrategy.decimals()), - ); - console.log(tx.hash); - await tx.wait(); - }); - task("deploy:Router") .addParam("verify", "flag to set false for local deployments", true, types.boolean) .setAction(async function (args: TaskArguments, hre) { console.log("Signer", await (await hre.ethers.getSigners())[0].getAddress()); - const RouterV1 = await hre.ethers.getContractFactory("RouterV1"); - const router = await RouterV1.deploy(); + const RouterV2 = await hre.ethers.getContractFactory("RouterV2"); + const router = await RouterV2.deploy(); await router.deployed(); - console.log("router", router.address); + console.log("RouterV2", router.address); if (args.verify) { - await sleep(15); + await sleep(30); await hre.run("verify:contract", { address: router.address, }); @@ -165,3 +141,29 @@ task("deploy:Router") console.log("Skipping verification"); } }); + +task("deploy:FeePolicy") + .addParam("verify", "flag to set false for local deployments", true, types.boolean) + .setAction(async function (args: TaskArguments, hre) { + const deployer = (await hre.ethers.getSigners())[0]; + console.log("Signer", await deployer.getAddress()); + + const FeePolicy = await hre.ethers.getContractFactory("FeePolicy"); + const feePolicy = await hre.upgrades.deployProxy(FeePolicy.connect(deployer), [], { + initializer: "init()", + }); + await feePolicy.deployed(); + console.log("feePolicy", feePolicy.address); + + if (args.verify) { + await sleep(30); + await hre.run("verify:contract", { + address: feePolicy.address, + }); + await hre.run("verify:contract", { + address: await getImplementationAddress(hre.ethers.provider, feePolicy.address), + }); + } else { + console.log("Skipping verification"); + } + }); diff --git a/spot-contracts/tasks/ganache.sh b/spot-contracts/tasks/ganache.sh deleted file mode 100755 index e43f74b8..00000000 --- a/spot-contracts/tasks/ganache.sh +++ /dev/null @@ -1,56 +0,0 @@ -######################################################################## - -## COMPILE -yarn hardhat compile - -## DEPLOYMENT - -yarn hardhat --network ganache deploy:MockAMPL \ - --verify "false" - -yarn hardhat --network ganache deploy:BondFactory \ - --verify "false" - -yarn hardhat --network ganache deploy:BondIssuer \ - --bond-factory-address "0x25a02122Cd77FeB7981b6224b470111A8FA479F4" \ - --bond-duration "600" \ - --issue-frequency "60" \ - --issue-window-offset "0" \ - --collateral-token-address "0x00404F73C76BC75b0D86F8AdDA8500e987BF8232" \ - --tranche-ratios "[500,500]" \ - --verify "false" - -yarn hardhat --network ganache deploy:PerpetualTranche \ - --bond-issuer-address "0xeb289644a33df897B1E30f0aa5cC0F17DD29Bdc2" \ - --collateral-token-address "0x00404F73C76BC75b0D86F8AdDA8500e987BF8232" \ - --name "SPOT" \ - --symbol "SPOT" \ - --verify "false" - -yarn hardhat --network ganache deploy:DiscountStrategy:setDiscount \ - --discount-strategy-address "0xeEaC7F8841B8E4Aa4D9E63164227a4788dF6dC99" \ - --collateral-token-address "0x00404F73C76BC75b0D86F8AdDA8500e987BF8232" \ - --tranche-ratios "[500,500]" \ - --tranche-index "0" \ - --tranche-discount "1.0" - -yarn hardhat --network ganache ops:perp:updateTolerableTrancheMaturity \ - --address "0xC090cFC721ea0624A30BE6796A53CE1EEf703D67" \ - --minimum "500" \ - --maximum "1000" - -yarn hardhat --network ganache deploy:Router \ - --verify "false" - -yarn hardhat --network ganache deploy:RolloverVault \ - --name "SPOT Rollover Vault Note" \ - --symbol "SPOT-RV-NOTE" \ - --perp-address "0xC090cFC721ea0624A30BE6796A53CE1EEf703D67" \ - --verify "false" - -######################################################################## -## OPS -yarn hardhat --network ganache ops:increaseTimeBy 300 -yarn hardhat --network ganache ops:updateState 0xC090cFC721ea0624A30BE6796A53CE1EEf703D67 -yarn hardhat --network ganache ops:perp:info 0xC090cFC721ea0624A30BE6796A53CE1EEf703D67 - diff --git a/spot-contracts/tasks/goeril.sh b/spot-contracts/tasks/goeril.sh deleted file mode 100644 index 698b057b..00000000 --- a/spot-contracts/tasks/goeril.sh +++ /dev/null @@ -1,94 +0,0 @@ -######################################################################## -## DEPLOYMENT - -# using staging AMPL instance deployed to: 0x08c5b39F000705ebeC8427C1d64D6262392944EE -# https://github.com/ampleforth/ampleforth-contracts - -# using button wood's stating factory deployed to: 0xda5DbE504e7D532E4F8921B38E1F970D4b881BFB -# https://docs.prl.one/buttonwood/developers/deployed-contracts/goerli-testnet - -yarn hardhat --network goerli deploy:BondIssuer \ - --bond-factory-address "0xdDe914EfBF5C472a590e61658d8E342d17E3AAB7" \ - --bond-duration "3600" \ - --issue-frequency "1200" \ - --issue-window-offset "0" \ - --collateral-token-address "0x08c5b39F000705ebeC8427C1d64D6262392944EE" \ - --tranche-ratios "[500,500]" - -yarn hardhat --network goerli deploy:PerpetualTranche \ - --bond-issuer-address "0xbC060a1EbEC5eC869C4D51d4563244d4a223D307" \ - --collateral-token-address "0x74567107828843070087F1c6ec8322A3e8450725" \ - --name "SPOT" \ - --symbol "SPOT" \ - --pricing-strategy-ref "CDRPricingStrategy" - -yarn hardhat --network goerli deploy:DiscountStrategy:setDiscount \ - --discount-strategy-address "0xEDB171C18cE90B633DB442f2A6F72874093b49Ef" \ - --collateral-token-address "0x08c5b39F000705ebeC8427C1d64D6262392944EE" \ - --tranche-ratios "[500,500]" \ - --tranche-index "0" \ - --tranche-discount "1.0" - -yarn hardhat --network goerli deploy:Router - -yarn hardhat --network goerli deploy:RolloverVault \ - --name "SPOT Rollover Vault Note" \ - --symbol "SPOT-RV-NOTE" \ - --perp-address "0x95014Bc18F82a98CFAA3253fbD3184125A01f848" - - -######################################################################## -## OPS -yarn hardhat --network goerli ops:perp:info 0x95014Bc18F82a98CFAA3253fbD3184125A01f848 - -yarn hardhat --network goerli ops:updateState 0x95014Bc18F82a98CFAA3253fbD3184125A01f848 - -yarn hardhat --network goerli ops:trancheAndDeposit \ - --router-address 0x8be9cC958680A6b0AE8609150B489a161baD3dCd \ - --perp-address 0x6Da15e0ab0524841Ac5e55a77CFC3F5CB040a7B7 \ - --collateral-amount 250 - -yarn hardhat --network goerli ops:redeem \ - --router-address 0x8be9cC958680A6b0AE8609150B489a161baD3dCd \ - --perp-address 0x6Da15e0ab0524841Ac5e55a77CFC3F5CB040a7B7 \ - --amount 10 - -yarn hardhat --network goerli ops:redeemTranches \ - --bond-issuer-address 0xbC060a1EbEC5eC869C4D51d4563244d4a223D307 - -yarn hardhat --network goerli ops:redeemTranches \ - --bond-issuer-address 0xAb7d17864463dEdA6c19060Ad6556e1B218c5Ba0 - -yarn hardhat --network goerli ops:preview_tx:trancheAndRollover \ - --wallet-address [INSERT_WALLET_ADDRESS] \ - --router-address 0x5e902bdCC408550b4BD612678bE2d57677664Dc9 \ - --perp-address 0x95014Bc18F82a98CFAA3253fbD3184125A01f848 - -yarn hardhat --network goerli ops:trancheAndRollover \ - --router-address 0x8be9cC958680A6b0AE8609150B489a161baD3dCd \ - --perp-address 0x6Da15e0ab0524841Ac5e55a77CFC3F5CB040a7B7 \ - --collateral-amount 200 - -yarn hardhat --network goerli ops:rebase:MockAMPL \ - --ampl-address "0x74567107828843070087F1c6ec8322A3e8450725" \ - --rebase-perc 0.1 - -yarn hardhat --network goerli ops:vault:info 0xca36B64BEbdf141623911987b93767dcA4bF6F1f - -yarn hardhat --network goerli ops:vault:deposit \ - --vault-address 0xca36B64BEbdf141623911987b93767dcA4bF6F1f \ - --underlying-amount 1 - -yarn hardhat --network goerli ops:vault:redeem \ - --vault-address 0xca36B64BEbdf141623911987b93767dcA4bF6F1f \ - --amount 1 - -yarn hardhat --network goerli ops:vault:recoverAndRedeploy \ - --vault-address 0xca36B64BEbdf141623911987b93767dcA4bF6F1f - -######################################################################## -## upgrade - -yarn hardhat --network goerli upgrade:perp:testnet 0x95014Bc18F82a98CFAA3253fbD3184125A01f848 - -yarn hardhat --network goerli upgrade:rolloverVault:testnet 0xca36B64BEbdf141623911987b93767dcA4bF6F1f diff --git a/spot-contracts/tasks/ops/index.ts b/spot-contracts/tasks/ops/index.ts index 4290b627..0803240f 100644 --- a/spot-contracts/tasks/ops/index.ts +++ b/spot-contracts/tasks/ops/index.ts @@ -1,5 +1,5 @@ import "./ampl"; import "./perp"; -import "./perp_rollover"; +import "./tranche"; import "./testnet"; import "./vaults"; diff --git a/spot-contracts/tasks/ops/perp.ts b/spot-contracts/tasks/ops/perp.ts index 470476d3..ec84c8e6 100644 --- a/spot-contracts/tasks/ops/perp.ts +++ b/spot-contracts/tasks/ops/perp.ts @@ -1,7 +1,7 @@ import { getAdminAddress, getImplementationAddress } from "@openzeppelin/upgrades-core"; import { task, types } from "hardhat/config"; import { TaskArguments } from "hardhat/types"; -import { utils, constants, BigNumber } from "ethers"; +import { utils } from "ethers"; task("ops:perp:info") .addPositionalParam("perpAddress", "the address of the perp contract", undefined, types.string, false) @@ -14,17 +14,13 @@ task("ops:perp:info") const bondIssuer = await hre.ethers.getContractAt("BondIssuer", await perp.bondIssuer()); const latestBond = await hre.ethers.getContractAt("IBondController", await bondIssuer.callStatic.getLatestBond()); - const collateralToken = await hre.ethers.getContractAt("MockERC20", await perp.collateral()); - const feeStrategy = await hre.ethers.getContractAt("BasicFeeStrategy", await perp.feeStrategy()); - const pricingStrategy = await hre.ethers.getContractAt("CDRPricingStrategy", await perp.pricingStrategy()); - const discountStrategy = await hre.ethers.getContractAt( - "TrancheClassDiscountStrategy", - await perp.discountStrategy(), - ); + const collateralToken = await hre.ethers.getContractAt("MockERC20", await perp.underlying()); + const feePolicy = await hre.ethers.getContractAt("FeePolicy", await perp.feePolicy()); const depositBond = await hre.ethers.getContractAt("IBondController", await perp.callStatic.getDepositBond()); const issued = (await hre.ethers.provider.getCode(depositBond.address)) !== "0x"; const perpSupply = await perp.totalSupply(); - const priceDecimals = await pricingStrategy.decimals(); + const perpTVL = await perp.callStatic.getTVL(); + const perpPrice = perpSupply.gt("0") ? perpTVL.mul(1000).div(perpSupply) : 0; const proxyAdminAddress = await getAdminAddress(hre.ethers.provider, perpAddress); const implAddress = await getImplementationAddress(hre.ethers.provider, perpAddress); @@ -32,6 +28,7 @@ task("ops:perp:info") console.log("BondIssuer:", bondIssuer.address); console.log("bondFactory:", await bondIssuer.bondFactory()); console.log("collateral:", await bondIssuer.collateral()); + console.log("issuedCount:", utils.formatUnits(await bondIssuer.issuedCount()), 0); console.log("maxMaturityDuration:", utils.formatUnits(await bondIssuer.maxMaturityDuration(), 0)); console.log("minIssueTimeIntervalSec:", utils.formatUnits(await bondIssuer.minIssueTimeIntervalSec(), 0)); console.log("issueWindowOffsetSec:", utils.formatUnits(await bondIssuer.issueWindowOffsetSec(), 0)); @@ -48,19 +45,14 @@ task("ops:perp:info") console.log("latestBond:", latestBond.address); console.log("---------------------------------------------------------------"); - console.log("feeStrategy:", feeStrategy.address); - console.log("owner", await feeStrategy.owner()); - console.log("feeToken", await feeStrategy.feeToken()); - console.log("mintFeePerc:", utils.formatUnits(await feeStrategy.mintFeePerc(), 6)); - console.log("burnFeePerc:", utils.formatUnits(await feeStrategy.burnFeePerc(), 6)); - console.log("rolloverFeePerc:", utils.formatUnits(await feeStrategy.rolloverFeePerc(), 6)); - - console.log("---------------------------------------------------------------"); - console.log("discountStrategy:", discountStrategy.address); - console.log("owner", await discountStrategy.owner()); - - console.log("---------------------------------------------------------------"); - console.log("pricingStrategy:", pricingStrategy.address); + console.log("feePolicy:", feePolicy.address); + console.log("owner", await feePolicy.owner()); + console.log("perpMintFeePerc:", utils.formatUnits(await feePolicy.perpMintFeePerc(), 8)); + console.log("perpBurnFeePerc:", utils.formatUnits(await feePolicy.perpBurnFeePerc(), 8)); + const r = await feePolicy.perpRolloverFee(); + console.log("perpRolloverFeeLower:", utils.formatUnits(r.lower, 8)); + console.log("perpRolloverFeeUpper:", utils.formatUnits(r.upper, 8)); + console.log("perpRolloverFeeGrowth:", utils.formatUnits(r.growth, 8)); console.log("---------------------------------------------------------------"); console.log("PerpetualTranche:", perp.address); @@ -68,56 +60,38 @@ task("ops:perp:info") console.log("implementation:", implAddress); console.log("owner:", await perp.owner()); console.log("keeper:", await perp.keeper()); - console.log("reserve:", await perp.reserve()); - console.log("protocolFeeCollector:", await perp.protocolFeeCollector()); console.log("paused:", await perp.paused()); console.log("collateralToken:", collateralToken.address); console.log("---------------------------------------------------------------"); console.log(`maturityTolarance: [${await perp.minTrancheMaturitySec()}, ${await perp.maxTrancheMaturitySec()}]`); console.log("maxSupply:", utils.formatUnits(await perp.maxSupply(), await perp.decimals())); console.log("maxMintAmtPerTranche:", utils.formatUnits(await perp.maxMintAmtPerTranche(), await perp.decimals())); - console.log( - "matureValueTargetPerc:", - utils.formatUnits(await perp.matureValueTargetPerc(), await perp.PERC_DECIMALS()), - ); console.log("---------------------------------------------------------------"); console.log("depositBond:", depositBond.address); console.log("issued:", issued); - const matureTrancheBalance = await perp.callStatic.getMatureTrancheBalance(); - console.log("MatureTrancheBalance:", utils.formatUnits(matureTrancheBalance, perpDecimals)); console.log("TotalSupply:", utils.formatUnits(perpSupply, perpDecimals)); + console.log("TVL:", utils.formatUnits(perpTVL, perpDecimals)); console.log("---------------------------------------------------------------"); console.log("Reserve:"); const reserveCount = (await perp.callStatic.getReserveCount()).toNumber(); const upForRollover = await perp.callStatic.getReserveTokensUpForRollover(); - const perpPrice = await perp.callStatic.getAvgPrice(); - const reserveValue = (await perp.totalSupply()).mul(perpPrice); - let totalTrancheBalance = BigNumber.from(0); const data = []; for (let i = 0; i < reserveCount; i++) { const tokenAddress = await perp.callStatic.getReserveAt(i); - const balance = await perp.callStatic.getReserveTrancheBalance(tokenAddress); - totalTrancheBalance = totalTrancheBalance.add(balance); - const discountF = await perp.computeDiscount(tokenAddress); - const price = await perp.computePrice(tokenAddress); + const balance = await perp.callStatic.getReserveTokenBalance(tokenAddress); + const value = await perp.callStatic.getReserveTokenValue(tokenAddress); + const price = balance.gt("0") ? value.mul(1000).div(balance) : 0; data.push({ + token: tokenAddress, balance: utils.formatUnits(balance, await perp.decimals()), - discountFactor: utils.formatUnits(discountF, await discountStrategy.decimals()), - price: utils.formatUnits(price, priceDecimals), - upForRollover: upForRollover[i] !== constants.AddressZero && balance.gt(0), + price: utils.formatUnits(price, 3), + upForRollover: balance.gt("0") && upForRollover.find(t => t === tokenAddress) !== undefined, }); } console.table(data); console.log("reserveCount:", reserveCount); - console.log("reserveValue:", utils.formatUnits(reserveValue, perpDecimals + priceDecimals)); - if (perpSupply.gt("0")) { - console.log("price:", utils.formatUnits(perpPrice, priceDecimals)); - console.log( - "impliedPrice:", - utils.formatUnits(totalTrancheBalance.mul(10 ** priceDecimals).div(perpSupply), priceDecimals), - ); - } + console.log("price:", utils.formatUnits(perpPrice, 3)); console.log("---------------------------------------------------------------"); }); @@ -172,7 +146,7 @@ task("ops:perp:pause", "Pauses operations on the perpetual tranche contract") await tx.wait(); }); -task("ops:updateState") +task("ops:perp:updateState") .addPositionalParam("perpAddress", "the address of the perp contract", undefined, types.string, false) .addParam("fromIdx", "the index of sender", 0, types.int) .setAction(async function (args: TaskArguments, hre) { @@ -192,7 +166,7 @@ task("ops:updateState") console.log("Tx", tx.hash); }); -task("ops:trancheAndDeposit") +task("ops:perp:trancheAndDeposit") .addParam("perpAddress", "the address of the perp contract", undefined, types.string, false) .addParam("routerAddress", "the address of the router contract", undefined, types.string, false) .addParam( @@ -206,44 +180,35 @@ task("ops:trancheAndDeposit") .setAction(async function (args: TaskArguments, hre) { const { perpAddress, routerAddress, collateralAmount } = args; - const router = await hre.ethers.getContractAt("RouterV1", routerAddress); + const router = await hre.ethers.getContractAt("RouterV2", routerAddress); const perp = await hre.ethers.getContractAt("PerpetualTranche", perpAddress); const bondIssuer = await hre.ethers.getContractAt("BondIssuer", await perp.bondIssuer()); const collateralToken = await hre.ethers.getContractAt("MockERC20", await bondIssuer.collateral()); const fixedPtCollateralAmount = utils.parseUnits(collateralAmount, await collateralToken.decimals()); - const [depositBondAddress, trancheAddresses, trancheAmts] = await router.callStatic.previewTranche( + const [depositBondAddress, depositTranches] = await router.callStatic.previewTranche( perp.address, fixedPtCollateralAmount, ); + console.log(depositBondAddress, depositTranches); console.log("---------------------------------------------------------------"); console.log("Preview tranche:", collateralAmount); - for (let i = 0; i < trancheAddresses.length; i++) { - console.log( - `tranches(${i}):`, - trancheAddresses[i], - utils.formatUnits(trancheAmts[i].toString(), await collateralToken.decimals()), - ); - } + console.log( + "tranches(0):", + depositTranches[0].token, + utils.formatUnits(depositTranches[0].amount.toString(), await collateralToken.decimals()), + ); + console.log( + "tranches(1):", + depositTranches[1].token, + utils.formatUnits(depositTranches[1].amount.toString(), await collateralToken.decimals()), + ); console.log("---------------------------------------------------------------"); console.log("Preview mint:", collateralAmount); - const feeToken = await hre.ethers.getContractAt("PerpetualTranche", await perp.feeToken()); - let totalMintFee = BigNumber.from("0"); - let totalMintAmt = BigNumber.from("0"); - for (let i = 0; i < trancheAddresses.length; i++) { - const [mintAmt, , mintFee] = await router.callStatic.previewDeposit( - perp.address, - trancheAddresses[i], - trancheAmts[i], - ); - totalMintAmt = totalMintAmt.add(mintAmt); - totalMintFee = totalMintFee.add(mintFee); - } + const totalMintAmt = await perp.callStatic.computeMintAmt(depositTranches[0].token, depositTranches[0].amount); console.log("mintAmt", utils.formatUnits(totalMintAmt, await perp.decimals())); - console.log("mintFee", utils.formatUnits(totalMintFee, await feeToken.decimals())); - if (totalMintAmt.eq("0")) { throw Error("No perp minted"); } @@ -262,26 +227,17 @@ task("ops:trancheAndDeposit") console.log("Tx", tx1.hash); } - let fee = BigNumber.from("0"); - if (totalMintFee.gt("0") && feeToken.address !== perp.address) { - fee = totalMintFee; - console.log("Approving fees to be spent:"); - const tx2 = await feeToken.connect(signer).increaseAllowance(router.address, fee); - await tx2.wait(); - console.log("Tx", tx2.hash); - } - console.log("Tranche and deposit:"); - const tx3 = await router + const tx2 = await router .connect(signer) - .trancheAndDeposit(perp.address, depositBondAddress, fixedPtCollateralAmount, fee); - await tx3.wait(); - console.log("Tx", tx3.hash); + .trancheAndDeposit(perp.address, depositBondAddress, fixedPtCollateralAmount); + await tx2.wait(); + console.log("Tx", tx2.hash); console.log("Signer balance", utils.formatUnits(await perp.balanceOf(signerAddress), await perp.decimals())); }); -task("ops:redeem") +task("ops:perp:redeem") .addParam("perpAddress", "the address of the perp contract", undefined, types.string, false) .addParam("routerAddress", "the address of the router contract", undefined, types.string, false) .addParam("amount", "the total amount of perp tokens (in float) to redeem", undefined, types.string, false) @@ -289,19 +245,21 @@ task("ops:redeem") .setAction(async function (args: TaskArguments, hre) { const { perpAddress, routerAddress, amount } = args; - const router = await hre.ethers.getContractAt("RouterV1", routerAddress); + const router = await hre.ethers.getContractAt("RouterV2", routerAddress); const perp = await hre.ethers.getContractAt("PerpetualTranche", perpAddress); - const feeToken = await hre.ethers.getContractAt("PerpetualTranche", await perp.feeToken()); const fixedPtAmount = utils.parseUnits(amount, await perp.decimals()); console.log("---------------------------------------------------------------"); console.log("Preview redeem:", amount); - const [reserveTokens, , , burnFee] = await router.callStatic.previewRedeem(perp.address, fixedPtAmount); + const reserveTokens = await perp.callStatic.computeRedemptionAmts(fixedPtAmount); console.log("burnAmt", amount); - console.log("burnFee", utils.formatUnits(burnFee, await feeToken.decimals())); console.log("reserve token redeemed"); for (let i = 0; i < reserveTokens.length; i++) { - console.log(`reserve(${i}):`, reserveTokens[i]); + console.log( + `reserve(${i}):`, + reserveTokens[i].token, + utils.formatUnits(reserveTokens[i].amount.toString(), await perp.decimals()), + ); } console.log("---------------------------------------------------------------"); @@ -317,19 +275,10 @@ task("ops:redeem") console.log("Tx", tx1.hash); } - let fee = BigNumber.from("0"); - if (burnFee.gt("0")) { - fee = burnFee; - console.log("Approving fees to be spent:"); - const tx2 = await feeToken.connect(signer).increaseAllowance(router.address, fee); - await tx2.wait(); - console.log("Tx", tx2.hash); - } - console.log("Redeem:"); - const tx3 = await perp.connect(signer).redeem(fixedPtAmount); - await tx3.wait(); - console.log("Tx", tx3.hash); + const tx2 = await perp.connect(signer).redeem(fixedPtAmount); + await tx2.wait(); + console.log("Tx", tx2.hash); console.log("Signer balance", utils.formatUnits(await perp.balanceOf(signerAddress), await perp.decimals())); }); diff --git a/spot-contracts/tasks/ops/perp_rollover.ts b/spot-contracts/tasks/ops/perp_rollover.ts deleted file mode 100644 index 342f4949..00000000 --- a/spot-contracts/tasks/ops/perp_rollover.ts +++ /dev/null @@ -1,458 +0,0 @@ -import fs from "fs"; -import { task, types } from "hardhat/config"; -import { TaskArguments, HardhatRuntimeEnvironment } from "hardhat/types"; -import { utils, constants, Contract, BigNumber, Signer } from "ethers"; -import { generateGnosisSafeBatchFile, ProposedTransaction } from "../helpers"; - -async function matureBond(bond: Contract, signer: Signer) { - if (await bond.isMature()) { - return true; - } - try { - console.log("Invoking Mature"); - await bond.connect(signer).callStatic.mature(); - const tx = await bond.connect(signer).mature(); - await tx.wait(); - console.log("Tx:", tx.hash); - } catch (e) { - console.log("Not up for maturity"); - return false; - } - return true; -} - -async function getTranches(hre: HardhatRuntimeEnvironment, bond: Contract): Promise<[Contract, BigNumber][]> { - const trancheCount = await bond.trancheCount(); - const tranches: [Contract, BigNumber][] = []; - for (let i = 0; i < trancheCount; i++) { - const [address, ratio] = await bond.tranches(i); - const tranche = await hre.ethers.getContractAt("ITranche", address); - tranches.push([tranche, ratio]); - } - return tranches; -} - -function computeProportionalBalances(balances: BigNumber[], ratios: BigNumber[]): BigNumber[] { - if (balances.length !== ratios.length) { - throw Error("balances and ratios length mismatch"); - } - - const redeemableAmts: BigNumber[] = []; - let min = BigNumber.from(constants.MaxUint256); - for (let i = 0; i < balances.length && min.gt("0"); i++) { - const d = balances[i].mul("1000").div(ratios[i]); - if (d.lt(min)) { - min = d; - } - } - for (let i = 0; i < balances.length; i++) { - redeemableAmts[i] = ratios[i].mul(min).div("1000"); - } - return redeemableAmts; -} - -async function computeRedeemableTrancheAmounts(bt: [Contract, BigNumber][], address: string): Promise { - const balances: BigNumber[] = []; - const ratios: BigNumber[] = []; - for (let i = 0; i < bt.length; i++) { - balances.push(await bt[i][0].balanceOf(address)); - ratios.push(bt[i][1]); - } - return computeProportionalBalances(balances, ratios); -} - -interface RolloverData { - trancheIn: Contract; - tokenOut: Contract; - trancheInAmt: BigNumber; - tokenOutAmt: BigNumber; -} - -interface RolloverBatch { - depositBond: Contract; - depositTranches: Contract[]; - totalRolloverAmt: BigNumber; - totalRolloverFee: BigNumber; - remainingTrancheInAmts: BigNumber[]; - remainingTokenOutAmts: BigNumber[]; - rolloverData: RolloverData[]; - collateralUsed: BigNumber; - excessCollateral: BigNumber; -} - -async function computeRolloverBatchExact( - hre: HardhatRuntimeEnvironment, - router: Contract, - perp: Contract, - collateralUsed: BigNumber, -): Promise { - const bondIssuer = await hre.ethers.getContractAt("BondIssuer", await perp.bondIssuer()); - const collateralToken = await hre.ethers.getContractAt("MockERC20", await bondIssuer.collateral()); - const [depositBondAddress, trancheAddresses, depositTrancheAmts] = await router.callStatic.previewTranche( - perp.address, - collateralUsed, - ); - const depositBond = await hre.ethers.getContractAt("IBondController", depositBondAddress); - - // Fresh Tranches - const depositTranches = []; - const trancheRatios = []; - for (let i = 0; i < trancheAddresses.length; i++) { - depositTranches.push(await hre.ethers.getContractAt("ITranche", trancheAddresses[i])); - trancheRatios.push(await bondIssuer.trancheRatios(i)); - } - - // Tranches up for rollover - const reserveCount = (await perp.callStatic.getReserveCount()).toNumber(); - const upForRotation = await perp.callStatic.getReserveTokensUpForRollover(); - const reserveTokens = []; - const reserveTokenBalances = []; - const _rotationTokensAndBals = []; - for (let i = 0; i < reserveCount; i++) { - const tranche = await hre.ethers.getContractAt("ITranche", await perp.callStatic.getReserveAt(i)); - const balance = await perp.callStatic.getReserveTrancheBalance(tranche.address); - reserveTokens.push(tranche); - reserveTokenBalances.push(balance); - if (upForRotation[i] !== constants.AddressZero && balance.gt(0)) { - _rotationTokensAndBals.push({ token: tranche, balance }); - } - } - const rotationTokensAndBals = _rotationTokensAndBals.sort((a, b) => (a.balance.lt(b.balance) ? 1 : -1)); - - // continues to the next token when only DUST remains - const DUST_AMOUNT = utils.parseUnits("0.001", await perp.decimals()); - - // Amounts at the start - const remainingTrancheInAmts: BigNumber[] = depositTrancheAmts.map((t: BigNumber) => t); - const remainingTokenOutAmts: BigNumber[] = rotationTokensAndBals.map(t => t.balance); - - // For each tranche token, and each token up for rollover - // We try to rollover and once depleted (upto dust) and move on to the next pair - const rolloverData: RolloverData[] = []; - let totalRolloverAmt = BigNumber.from("0"); - let totalRolloverFee = BigNumber.from("0"); - - for (let i = 0, j = 0; i < depositTranches.length && j < rotationTokensAndBals.length; ) { - const trancheIn = depositTranches[i]; - const tokenOut = rotationTokensAndBals[j].token; - const [rd, , rolloverFee] = await router.callStatic.previewRollover( - perp.address, - trancheIn.address, - tokenOut.address, - remainingTrancheInAmts[i], - remainingTokenOutAmts[j], - ); - - // trancheIn isn't accepted by perp, likely because yield=0 - if (rd.perpRolloverAmt.eq("0")) { - i++; - continue; - } - - rolloverData.push({ - trancheIn, - tokenOut, - trancheInAmt: rd.trancheInAmt, - tokenOutAmt: rd.tokenOutAmt, - }); - - totalRolloverAmt = totalRolloverAmt.add(rd.perpRolloverAmt); - totalRolloverFee = totalRolloverFee.add(rolloverFee); - - remainingTrancheInAmts[i] = rd.remainingTrancheInAmt; - remainingTokenOutAmts[j] = remainingTokenOutAmts[j].sub(rd.tokenOutAmt); - - // trancheIn tokens are exhausted - if (remainingTrancheInAmts[i].lte(DUST_AMOUNT)) { - i++; - } - - // tokenOut is exhausted - if (remainingTokenOutAmts[j].lte(DUST_AMOUNT)) { - j++; - } - } - - // calculate if any excess collateral was tranched - let excessCollateral = BigNumber.from("0"); - if (remainingTrancheInAmts[0].gt("0")) { - const excessTrancheTokens = computeProportionalBalances(remainingTrancheInAmts, trancheRatios); - excessCollateral = excessTrancheTokens.reduce((m, t) => m.add(t), BigNumber.from("0")); - try { - // fails if bond isn't issued - const depositBondTotalDebt = await depositBond.totalDebt(); - if (depositBondTotalDebt.gt(0)) { - const bondCollateralBalance = await collateralToken.balanceOf(depositBond.address); - excessCollateral = excessCollateral.mul(bondCollateralBalance).div(depositBondTotalDebt); - } - } catch (e) {} - } - - return { - depositBond, - depositTranches, - totalRolloverAmt, - totalRolloverFee, - remainingTrancheInAmts, - remainingTokenOutAmts, - rolloverData, - collateralUsed, - excessCollateral, - }; -} - -async function computeRolloverBatch( - hre: HardhatRuntimeEnvironment, - router: Contract, - perp: Contract, - collateralUsed: BigNumber, -): Promise { - const r = await computeRolloverBatchExact(hre, router, perp, collateralUsed); - return r.excessCollateral.eq("0") - ? r - : computeRolloverBatchExact(hre, router, perp, r.collateralUsed.sub(r.excessCollateral)); -} - -task("ops:redeemTranches") - .addParam("bondIssuerAddress", "the address of the bond issuer contract", undefined, types.string, false) - .addParam("fromIdx", "the index of sender", 0, types.int) - .setAction(async function (args: TaskArguments, hre: HardhatRuntimeEnvironment) { - const { bondIssuerAddress } = args; - const bondIssuer = await hre.ethers.getContractAt("BondIssuer", bondIssuerAddress); - - console.log("---------------------------------------------------------------"); - console.log("Execution:"); - const signer = (await hre.ethers.getSigners())[args.fromIdx]; - const signerAddress = await signer.getAddress(); - console.log("Signer", signerAddress); - - // iterate through the bonds - const issuedCount = await bondIssuer.callStatic.issuedCount(); - for (let i = 0; i < issuedCount; i++) { - const bondAddress = await bondIssuer.callStatic.issuedBondAt(i); - const bond = await hre.ethers.getContractAt("IBondController", bondAddress); - - console.log("---------------------------------------------------------------"); - console.log("Processing bond", bondAddress); - - const bt = await getTranches(hre, bond); - const isMature = await matureBond(bond, signer); - - if (isMature) { - for (let j = 0; j < bt.length; j++) { - const b = await bt[j][0].balanceOf(signerAddress); - if (b.gt(0)) { - console.log("Redeeming mature tranche", bt[j][0].address); - const tx = await bond.connect(signer).redeemMature(bt[j][0].address, b); - await tx.wait(); - console.log("Tx:", tx.hash); - } - } - } else { - const redemptionAmounts = await computeRedeemableTrancheAmounts(bt, signerAddress); - if (redemptionAmounts[0].gt("0")) { - console.log( - "Redeeming immature bond", - redemptionAmounts.map(a => a.toString()), - ); - const tx = await bond.connect(signer).redeem(redemptionAmounts); - await tx.wait(); - console.log("Tx:", tx.hash); - } - } - } - }); - -task("ops:trancheAndRollover") - .addParam("perpAddress", "the address of the perp contract", undefined, types.string, false) - .addParam("routerAddress", "the address of the router contract", undefined, types.string, false) - .addParam( - "collateralAmount", - "the total amount of collateral (in float) to tranche and use for rolling over", - undefined, - types.string, - false, - ) - .addParam("fromIdx", "the index of sender", 0, types.int) - .setAction(async function (args: TaskArguments, hre) { - const { perpAddress, routerAddress, collateralAmount } = args; - - const router = await hre.ethers.getContractAt("RouterV1", routerAddress); - const perp = await hre.ethers.getContractAt("PerpetualTranche", perpAddress); - const bondIssuer = await hre.ethers.getContractAt("BondIssuer", await perp.bondIssuer()); - const collateralToken = await hre.ethers.getContractAt("MockERC20", await bondIssuer.collateral()); - const feeToken = await hre.ethers.getContractAt("PerpetualTranche", await perp.feeToken()); - - const fixedPtCollateralAmount = utils.parseUnits(collateralAmount, await collateralToken.decimals()); - const { depositBond, totalRolloverFee, rolloverData } = await computeRolloverBatch( - hre, - router, - perp, - fixedPtCollateralAmount, - ); - - if (rolloverData.length === 0) { - throw Error("No tokens up for rollover"); - } - - console.log("---------------------------------------------------------------"); - console.log("Execution:"); - const signer = (await hre.ethers.getSigners())[args.fromIdx]; - const signerAddress = await signer.getAddress(); - console.log("Signer", signerAddress); - - console.log("Approving collateralToken to be spent"); - const allowance = await collateralToken.allowance(signerAddress, router.address); - if (allowance.lt(fixedPtCollateralAmount)) { - const tx1 = await collateralToken.connect(signer).approve(router.address, fixedPtCollateralAmount); - await tx1.wait(); - console.log("Tx", tx1.hash); - } - - let fee = BigNumber.from("0"); - if (totalRolloverFee.gt("0")) { - fee = totalRolloverFee; - console.log("Approving fees to be spent:"); - const tx2 = await feeToken.connect(signer).increaseAllowance(router.address, fee); - await tx2.wait(); - console.log("Tx", tx2.hash); - } - - // TODO: fee calculation has some rounding issues. Overpaying fixes it for now - fee = fee.mul("2"); - - console.log("Executing rollover:"); - const tx3 = await router.connect(signer).trancheAndRollover( - perp.address, - depositBond.address, - fixedPtCollateralAmount, - rolloverData.map(r => [r.trancheIn.address, r.tokenOut.address, r.trancheInAmt]), - fee, - ); - await tx3.wait(); - console.log("Tx", tx3.hash); - }); - -task("ops:preview_tx:trancheAndRollover") - .addParam("walletAddress", "the address of the wallet with the collateral token", undefined, types.string, false) - .addParam("perpAddress", "the address of the perp contract", undefined, types.string, false) - .addParam("routerAddress", "the address of the router contract", undefined, types.string, false) - .addParam( - "collateralAvailable", - "the amount of collateral available for rollover, if not specified uses the token balance", - undefined, - types.string, - true, - ) - .setAction(async function (args: TaskArguments, hre) { - const { walletAddress, perpAddress, routerAddress, collateralAvailable } = args; - - const router = await hre.ethers.getContractAt("RouterV1", routerAddress); - const perp = await hre.ethers.getContractAt("PerpetualTranche", perpAddress); - const bondIssuer = await hre.ethers.getContractAt("BondIssuer", await perp.bondIssuer()); - const collateralToken = await hre.ethers.getContractAt("MockERC20", await bondIssuer.collateral()); - - const maxCollateralAvailable = - collateralAvailable === undefined - ? await collateralToken.balanceOf(walletAddress) - : utils.parseUnits(collateralAvailable, await collateralToken.decimals()); - const { depositBond, totalRolloverAmt, totalRolloverFee, rolloverData, collateralUsed } = - await computeRolloverBatch(hre, router, perp, maxCollateralAvailable); - const rolloverDataInput = rolloverData.map(r => [ - r.trancheIn.address, - r.tokenOut.address, - r.trancheInAmt.toString(), - ]); - - console.log("---------------------------------------------------------------"); - console.log("Rollover preview"); - console.log("balanceAvailable", utils.formatUnits(maxCollateralAvailable, await collateralToken.decimals())); - console.log("collateralUsed", utils.formatUnits(collateralUsed, await collateralToken.decimals())); - console.log("rolloverAmt", utils.formatUnits(totalRolloverAmt, await perp.decimals())); - - console.log("---------------------------------------------------------------"); - console.log("collateralToken", collateralToken.address); - console.log("router", router.address); - console.log("perp", perp.address); - console.log("depositBond", depositBond.address); - console.log("collateralAmountFixedPt", collateralUsed.toString()); - console.log("rolloverData", JSON.stringify(rolloverDataInput, null, 2)); - console.log("rolloverFeeFixedPt", totalRolloverFee.toString()); - - console.log("---------------------------------------------------------------"); - console.log("Execute the following transactions"); - - const tx1: ProposedTransaction = { - contract: collateralToken, - method: "approve", - args: [router.address, collateralUsed.toString()], - }; - const tx2: ProposedTransaction = { - contract: router, - method: "trancheAndRollover", - args: [ - perp.address, - depositBond.address, - collateralUsed.toString(), - JSON.stringify(rolloverDataInput), - totalRolloverFee.gt("0") ? totalRolloverFee.toString() : "0", - ], - }; - - console.log({ to: tx1.contract.address, method: tx1.method, args: tx1.args }); - console.log({ to: tx2.contract.address, method: tx2.method, args: tx2.args }); - - console.log("Wrote tx batch to file:", "RolloverBatch.json"); - fs.writeFileSync("RolloverBatch.json", JSON.stringify(await generateGnosisSafeBatchFile(hre, [tx1, tx2]), null, 2)); - }); - -task("ops:preview_tx:redeemTranches") - .addParam("walletAddress", "the address of the wallet with the collateral token", undefined, types.string, false) - .addParam("bondIssuerAddress", "the address of the bond issuer", undefined, types.string, false) - .addParam("depth", "the number of bonds to check", 5, types.int) - .setAction(async function (args: TaskArguments, hre) { - const { walletAddress, bondIssuerAddress, depth } = args; - const txs: ProposedTransaction[] = []; - const bondIssuer = await hre.ethers.getContractAt("BondIssuer", bondIssuerAddress); - - const issuedCount = await bondIssuer.callStatic.issuedCount(); - for (let i = issuedCount - 1; i > 0 && issuedCount - 1 - i < depth; i--) { - const bondAddress = await bondIssuer.callStatic.issuedBondAt(i); - const bond = await hre.ethers.getContractAt("IBondController", bondAddress); - - const bt = await getTranches(hre, bond); - const isMature = await bond.isMature(); - - if (isMature) { - for (let j = 0; j < bt.length; j++) { - const b = await bt[j][0].balanceOf(walletAddress); - if (b.gt(0)) { - txs.push({ - contract: bond, - method: "redeemMature", - args: [bt[j][0].address, b.toString()], - }); - } - } - } else { - const redemptionAmounts = await computeRedeemableTrancheAmounts(bt, walletAddress); - if (redemptionAmounts[0].gt("0")) { - txs.push({ - contract: bond, - method: "redeem", - args: [JSON.stringify(redemptionAmounts.map(a => a.toString()))], - }); - } - } - } - - console.log("---------------------------------------------------------------"); - console.log("Execute the following transactions"); - - for (let i = 0; i < txs.length; i++) { - console.log({ to: txs[i].contract.address, method: txs[i].method, args: txs[i].args }); - } - - console.log("Wrote tx batch to file:", "RedeemBatch.json"); - fs.writeFileSync("RedeemBatch.json", JSON.stringify(await generateGnosisSafeBatchFile(hre, txs), null, 2)); - }); diff --git a/spot-contracts/tasks/ops/tranche.ts b/spot-contracts/tasks/ops/tranche.ts new file mode 100644 index 00000000..ecbc5880 --- /dev/null +++ b/spot-contracts/tasks/ops/tranche.ts @@ -0,0 +1,176 @@ +import fs from "fs"; +import { task, types } from "hardhat/config"; +import { TaskArguments, HardhatRuntimeEnvironment } from "hardhat/types"; +import { constants, Contract, BigNumber, Signer } from "ethers"; +import { generateGnosisSafeBatchFile, ProposedTransaction } from "../helpers"; + +async function matureBond(bond: Contract, signer: Signer) { + if (await bond.isMature()) { + return true; + } + try { + console.log("Invoking Mature"); + await bond.connect(signer).callStatic.mature(); + const tx = await bond.connect(signer).mature(); + await tx.wait(); + console.log("Tx:", tx.hash); + } catch (e) { + console.log("Not up for maturity"); + return false; + } + return true; +} + +async function getTranches(hre: HardhatRuntimeEnvironment, bond: Contract): Promise<[Contract, BigNumber][]> { + const trancheCount = await bond.trancheCount(); + const tranches: [Contract, BigNumber][] = []; + for (let i = 0; i < trancheCount; i++) { + const [address, ratio] = await bond.tranches(i); + const tranche = await hre.ethers.getContractAt("ITranche", address); + tranches.push([tranche, ratio]); + } + return tranches; +} + +function computeProportionalBalances(balances: BigNumber[], ratios: BigNumber[]): BigNumber[] { + if (balances.length !== ratios.length) { + throw Error("balances and ratios length mismatch"); + } + + const redeemableAmts: BigNumber[] = []; + let min = BigNumber.from(constants.MaxUint256); + for (let i = 0; i < balances.length && min.gt("0"); i++) { + const b = balances[i].sub(balances[i].mod(ratios[i])); + const d = b.mul("1000").div(ratios[i]); + if (d.lt(min)) { + min = d; + } + } + + for (let i = 0; i < balances.length; i++) { + redeemableAmts[i] = ratios[i].mul(min).div("1000"); + } + return redeemableAmts; +} + +async function computeRedeemableTrancheAmounts(bt: [Contract, BigNumber][], address: string): Promise { + const balances: BigNumber[] = []; + const ratios: BigNumber[] = []; + for (let i = 0; i < bt.length; i++) { + balances.push(await bt[i][0].balanceOf(address)); + ratios.push(bt[i][1]); + } + return computeProportionalBalances(balances, ratios); +} + +task("ops:redeemTranches") + .addParam("bondIssuerAddress", "the address of the bond issuer contract", undefined, types.string, false) + .addParam("fromIdx", "the index of sender", 0, types.int) + .setAction(async function (args: TaskArguments, hre: HardhatRuntimeEnvironment) { + const { bondIssuerAddress } = args; + const bondIssuer = await hre.ethers.getContractAt("BondIssuer", bondIssuerAddress); + console.log(await bondIssuer.collateral()); + + console.log("---------------------------------------------------------------"); + console.log("Execution:"); + const signer = (await hre.ethers.getSigners())[args.fromIdx]; + const signerAddress = await signer.getAddress(); + console.log("Signer", signerAddress); + + // mature active bonds + console.log("---------------------------------------------------------------"); + console.log("Mature active"); + try { + await bondIssuer.matureActive(); + } catch { + console.log("No active bonds mature"); + } + + // iterate through the bonds + const issuedCount = await bondIssuer.issuedCount(); + for (let i = 0; i < issuedCount; i++) { + console.log(i); + const bondAddress = await bondIssuer.issuedBondAt(i); + const bond = await hre.ethers.getContractAt("IBondController", bondAddress); + + console.log("---------------------------------------------------------------"); + console.log("Processing bond", bondAddress); + + const bt = await getTranches(hre, bond); + const isMature = await matureBond(bond, signer); + + if (isMature) { + for (let j = 0; j < bt.length; j++) { + const b = await bt[j][0].balanceOf(signerAddress); + if (b.gt(0)) { + console.log("Redeeming mature tranche", bt[j][0].address); + const tx = await bond.connect(signer).redeemMature(bt[j][0].address, b); + await tx.wait(); + console.log("Tx:", tx.hash); + } + } + } else { + const redemptionAmounts = await computeRedeemableTrancheAmounts(bt, signerAddress); + if (redemptionAmounts[0].gt("0")) { + console.log( + "Redeeming immature bond", + redemptionAmounts.map(a => a.toString()), + ); + const tx = await bond.connect(signer).redeem(redemptionAmounts); + await tx.wait(); + console.log("Tx:", tx.hash); + } + } + } + }); + +task("ops:preview_tx:redeemTranches") + .addParam("walletAddress", "the address of the wallet with the collateral token", undefined, types.string, false) + .addParam("bondIssuerAddress", "the address of the bond issuer", undefined, types.string, false) + .addParam("depth", "the number of bonds to check", 5, types.int) + .setAction(async function (args: TaskArguments, hre) { + const { walletAddress, bondIssuerAddress, depth } = args; + const txs: ProposedTransaction[] = []; + const bondIssuer = await hre.ethers.getContractAt("BondIssuer", bondIssuerAddress); + + const issuedCount = await bondIssuer.callStatic.issuedCount(); + for (let i = issuedCount - 1; i > 0 && issuedCount - 1 - i < depth; i--) { + const bondAddress = await bondIssuer.callStatic.issuedBondAt(i); + const bond = await hre.ethers.getContractAt("IBondController", bondAddress); + + const bt = await getTranches(hre, bond); + const isMature = await bond.isMature(); + + if (isMature) { + for (let j = 0; j < bt.length; j++) { + const b = await bt[j][0].balanceOf(walletAddress); + if (b.gt(0)) { + txs.push({ + contract: bond, + method: "redeemMature", + args: [bt[j][0].address, b.toString()], + }); + } + } + } else { + const redemptionAmounts = await computeRedeemableTrancheAmounts(bt, walletAddress); + if (redemptionAmounts[0].gt("0")) { + txs.push({ + contract: bond, + method: "redeem", + args: [JSON.stringify(redemptionAmounts.map(a => a.toString()))], + }); + } + } + } + + console.log("---------------------------------------------------------------"); + console.log("Execute the following transactions"); + + for (let i = 0; i < txs.length; i++) { + console.log({ to: txs[i].contract.address, method: txs[i].method, args: txs[i].args }); + } + + console.log("Wrote tx batch to file:", "RedeemBatch.json"); + fs.writeFileSync("RedeemBatch.json", JSON.stringify(await generateGnosisSafeBatchFile(hre, txs), null, 2)); + }); diff --git a/spot-contracts/tasks/ops/vaults.ts b/spot-contracts/tasks/ops/vaults.ts index 5dc37013..9862c099 100644 --- a/spot-contracts/tasks/ops/vaults.ts +++ b/spot-contracts/tasks/ops/vaults.ts @@ -9,6 +9,7 @@ task("ops:vault:info") const { vaultAddress } = args; const vault = await hre.ethers.getContractAt("RolloverVault", vaultAddress); + const feePolicy = await hre.ethers.getContractAt("FeePolicy", await vault.feePolicy()); const vaultDecimals = await vault.decimals(); const proxyAdminAddress = await getAdminAddress(hre.ethers.provider, vaultAddress); const implAddress = await getImplementationAddress(hre.ethers.provider, vaultAddress); @@ -16,7 +17,6 @@ task("ops:vault:info") const perp = await hre.ethers.getContractAt("PerpetualTranche", await vault.perp()); const perpDecimals = await perp.decimals(); - const priceDecimals = await perp.PRICE_DECIMALS(); const underlying = await hre.ethers.getContractAt("MockERC20", await vault.underlying()); const underlyingDecimals = await underlying.decimals(); @@ -37,7 +37,6 @@ task("ops:vault:info") console.log("underlying:", underlying.address); console.log("minDeploymentAmt:", utils.formatUnits(await vault.minDeploymentAmt(), underlyingDecimals)); console.log("totalSupply:", utils.formatUnits(vaultSupply, vaultDecimals)); - console.log("tvl:", utils.formatUnits(await vault.callStatic.getTVL(), underlyingDecimals)); console.log("pokeAvailable:", pokeAvailable); console.log("---------------------------------------------------------------"); const data = []; @@ -47,29 +46,85 @@ task("ops:vault:info") balance: utils.formatUnits(underlyingBalance, underlyingDecimals), price: "1", }); - const deployedCount = (await vault.deployedCount()).toNumber(); - for (let i = 0; i < deployedCount; i++) { - const tokenAddress = await vault.callStatic.deployedAt(i); + const assetCount = (await vault.assetCount()).toNumber(); + for (let i = 1; i < assetCount; i++) { + const tokenAddress = await vault.callStatic.assetAt(i); const balance = await vault.vaultAssetBalance(tokenAddress); const value = await vault.callStatic.getVaultAssetValue(tokenAddress); - const price = value.mul(BigNumber.from(10 ** priceDecimals)).div(balance); + const price = value.mul(BigNumber.from(10 ** perpDecimals)).div(balance); const token = await hre.ethers.getContractAt("MockERC20", tokenAddress); data.push({ asset: await token.symbol(), balance: utils.formatUnits(balance, underlyingDecimals), - price: utils.formatUnits(price, priceDecimals), + price: utils.formatUnits(price, perpDecimals), }); } - const earned1Balance = await vault.vaultAssetBalance(perp.address); - const earned1Price = await perp.callStatic.getAvgPrice(); + const perpBalance = await vault.vaultAssetBalance(perp.address); + const perpSupply = await perp.totalSupply(); + const perpTVL = await perp.callStatic.getTVL(); + const perpPrice = perpSupply.gt("0") ? perpTVL.mul(1000).div(perpSupply) : 0; data.push({ asset: await perp.symbol(), - balance: utils.formatUnits(earned1Balance, perpDecimals), - price: utils.formatUnits(earned1Price, priceDecimals), + balance: utils.formatUnits(perpBalance, perpDecimals), + price: utils.formatUnits(perpPrice, 3), }); console.table(data); + + console.log("---------------------------------------------------------------"); + const feeOne = await feePolicy.ONE(); + const feeDecimals = await feePolicy.decimals(); + const vaultTVL = await vault.callStatic.getTVL(); + const seniorTR = await perp.callStatic.getDepositTrancheRatio(); + const juniorTR = BigNumber.from("1000").sub(seniorTR); + const subscriptionRatio = vaultTVL.mul(seniorTR).mul(feeOne).div(perpTVL).div(juniorTR); + const targetSubscriptionRatio = await feePolicy.targetSubscriptionRatio(); + const expectedVaultTVL = targetSubscriptionRatio.mul(perpTVL).mul(juniorTR).div(seniorTR).div(feeOne); + const deviationRatio = await feePolicy["computeDeviationRatio((uint256,uint256,uint256))"]([ + perpTVL, + vaultTVL, + seniorTR, + ]); + console.log("perpTVL:", utils.formatUnits(perpTVL, underlyingDecimals)); + console.log("vaultTVL:", utils.formatUnits(vaultTVL, underlyingDecimals)); + console.log("expectedVaultTVL:", utils.formatUnits(expectedVaultTVL, underlyingDecimals)); + console.log("seniorTR:", utils.formatUnits(seniorTR, 3)); + console.log("juniorTR:", utils.formatUnits(juniorTR, 3)); + console.log("subscriptionRatio:", utils.formatUnits(subscriptionRatio, feeDecimals)); + console.log("targetSubscriptionRatio:", utils.formatUnits(targetSubscriptionRatio, feeDecimals)); + console.log("targetDeviationRatio:", utils.formatUnits(feeOne, feeDecimals)); + console.log( + "deviationRatioBoundLower:", + utils.formatUnits(await feePolicy.deviationRatioBoundLower(), feeDecimals), + ); + console.log( + "deviationRatioBoundUpper:", + utils.formatUnits(await feePolicy.deviationRatioBoundUpper(), feeDecimals), + ); + console.log("deviationRatio:", utils.formatUnits(deviationRatio, feeDecimals)); + + console.log("---------------------------------------------------------------"); + console.log("feePolicy:", feePolicy.address); + console.log("owner", await feePolicy.owner()); + console.log("computeVaultMintFeePerc:", utils.formatUnits(await feePolicy.computeVaultMintFeePerc(), 8)); + console.log("computeVaultBurnFeePerc:", utils.formatUnits(await feePolicy.computeVaultBurnFeePerc(), 8)); + console.log( + "computeUnderlyingToPerpVaultSwapFeePerc:", + utils.formatUnits(await feePolicy.computeUnderlyingToPerpVaultSwapFeePerc(deviationRatio, deviationRatio), 8), + ); + console.log( + "computePerpToUnderlyingVaultSwapFeePerc:", + utils.formatUnits(await feePolicy.computePerpToUnderlyingVaultSwapFeePerc(deviationRatio, deviationRatio), 8), + ); + console.log("---------------------------------------------------------------"); + console.log("Swap slippage"); + const buy1000Perps = await vault.callStatic.computeUnderlyingToPerpSwapAmt(utils.parseUnits("1000", perpDecimals)); + const sell1000Perps = await vault.callStatic.computePerpToUnderlyingSwapAmt(utils.parseUnits("1000", perpDecimals)); + console.log("PerpPrice:", utils.formatUnits(perpPrice, 3)); + console.log("Swap 1000 underlying for perps: ", utils.formatUnits(buy1000Perps[0], perpDecimals)); + console.log("Sell 1000 perps for underlying", utils.formatUnits(sell1000Perps[0], perpDecimals)); + console.log("---------------------------------------------------------------"); }); task("ops:vault:deposit") @@ -218,3 +273,116 @@ task("ops:vault:recover") await tx.wait(); console.log("Tx", tx.hash); }); + +task("ops:fee:setSwapFees", "Updates swap fees in fee policy") + .addParam("address", "the fee policy contract", undefined, types.string, false) + .addParam("feePerc", "the percentage to be set as the swap fee", undefined, types.string, false) + .setAction(async function (args: TaskArguments, hre) { + const { address, feePerc } = args; + const signer = (await hre.ethers.getSigners())[0]; + const signerAddress = await signer.getAddress(); + console.log("Signer", signerAddress); + const feePolicy = await hre.ethers.getContractAt("FeePolicy", address); + console.log(`Updating both swap fees to ${feePerc}`); + const feeAmtFixedPt = utils.parseUnits(feePerc, await feePolicy.decimals()); + + const tx1 = await feePolicy.updateVaultUnderlyingToPerpSwapFeePerc(feeAmtFixedPt); + console.log(tx1.hash); + await tx1.wait(); + + const tx2 = await feePolicy.updateVaultPerpToUnderlyingSwapFeePerc(feeAmtFixedPt); + console.log(tx2.hash); + await tx2.wait(); + }); + +task("ops:vault:swapUnderlyingForPerps") + .addParam("vaultAddress", "the address of the vault contract", undefined, types.string, false) + .addParam( + "underlyingAmount", + "the total amount of underlying tokens (in float) to deposit", + undefined, + types.string, + false, + ) + .addParam("fromIdx", "the index of sender", 0, types.int) + .setAction(async function (args: TaskArguments, hre) { + const { vaultAddress, underlyingAmount } = args; + + const vault = await hre.ethers.getContractAt("RolloverVault", vaultAddress); + const perp = await hre.ethers.getContractAt("PerpetualTranche", await vault.perp()); + const underlying = await hre.ethers.getContractAt("MockERC20", await vault.underlying()); + const fixedPtAmount = utils.parseUnits(underlyingAmount, await underlying.decimals()); + + const signer = (await hre.ethers.getSigners())[args.fromIdx]; + const signerAddress = await signer.getAddress(); + console.log("Signer", signerAddress); + + console.log("Signer perp balance", utils.formatUnits(await perp.balanceOf(signerAddress), await perp.decimals())); + console.log( + "Signer underlying balance", + utils.formatUnits(await underlying.balanceOf(signerAddress), await underlying.decimals()), + ); + + console.log("---------------------------------------------------------------"); + console.log("Execution:"); + console.log("Approving router to spend tokens:"); + if ((await underlying.allowance(signerAddress, vault.address)).lt(fixedPtAmount)) { + const tx1 = await underlying.connect(signer).approve(vault.address, fixedPtAmount); + await tx1.wait(); + console.log("Tx", tx1.hash); + } + + console.log("Swap:"); + const tx2 = await vault.connect(signer).swapUnderlyingForPerps(fixedPtAmount); + await tx2.wait(); + console.log("Tx", tx2.hash); + + console.log("Signer perp balance", utils.formatUnits(await perp.balanceOf(signerAddress), await perp.decimals())); + console.log( + "Signer underlying balance", + utils.formatUnits(await underlying.balanceOf(signerAddress), await underlying.decimals()), + ); + }); + +task("ops:vault:swapPerpsForUnderlying") + .addParam("vaultAddress", "the address of the vault contract", undefined, types.string, false) + .addParam("perpAmount", "the total amount of underlying tokens (in float) to deposit", undefined, types.string, false) + .addParam("fromIdx", "the index of sender", 0, types.int) + .setAction(async function (args: TaskArguments, hre) { + const { vaultAddress, perpAmount } = args; + + const vault = await hre.ethers.getContractAt("RolloverVault", vaultAddress); + const perp = await hre.ethers.getContractAt("PerpetualTranche", await vault.perp()); + const underlying = await hre.ethers.getContractAt("MockERC20", await vault.underlying()); + const fixedPtAmount = utils.parseUnits(perpAmount, await perp.decimals()); + + const signer = (await hre.ethers.getSigners())[args.fromIdx]; + const signerAddress = await signer.getAddress(); + console.log("Signer", signerAddress); + + console.log( + "Signer underlying balance", + utils.formatUnits(await underlying.balanceOf(signerAddress), await underlying.decimals()), + ); + console.log("Signer perp balance", utils.formatUnits(await perp.balanceOf(signerAddress), await perp.decimals())); + + console.log("---------------------------------------------------------------"); + console.log("Execution:"); + console.log("Approving router to spend tokens:"); + if ((await perp.allowance(signerAddress, vault.address)).lt(fixedPtAmount)) { + const tx1 = await perp.connect(signer).approve(vault.address, fixedPtAmount); + await tx1.wait(); + console.log("Tx", tx1.hash); + } + + console.log("Swap:"); + const tx2 = await vault.connect(signer).swapPerpsForUnderlying(fixedPtAmount); + await tx2.wait(); + console.log("Tx", tx2.hash); + + console.log( + "Signer underlying balance", + utils.formatUnits(await underlying.balanceOf(signerAddress), await underlying.decimals()), + ); + console.log("Signer perp balance", utils.formatUnits(await perp.balanceOf(signerAddress), await perp.decimals())); + }); diff --git a/spot-contracts/tasks/scripts/ganache.sh b/spot-contracts/tasks/scripts/ganache.sh new file mode 100755 index 00000000..3cccafde --- /dev/null +++ b/spot-contracts/tasks/scripts/ganache.sh @@ -0,0 +1,110 @@ +######################################################################## + +## COMPILE +yarn hardhat compile + +## Start network +yarn ganache-cli -p 8545 -s 123 + +## DEPLOYMENT + +yarn hardhat --network ganache deploy:MockAMPL \ + --verify "false" + +yarn hardhat --network ganache deploy:BondFactory \ + --verify "false" + +yarn hardhat --network ganache deploy:BondIssuer \ + --bond-factory-address "0x25a02122Cd77FeB7981b6224b470111A8FA479F4" \ + --bond-duration "600" \ + --issue-frequency "60" \ + --issue-window-offset "0" \ + --collateral-token-address "0x00404F73C76BC75b0D86F8AdDA8500e987BF8232" \ + --tranche-ratios "[333,667]" \ + --verify "false" + +yarn hardhat --network ganache deploy:PerpSystem \ + --bond-issuer-address "0xeb289644a33df897B1E30f0aa5cC0F17DD29Bdc2" \ + --collateral-token-address "0x00404F73C76BC75b0D86F8AdDA8500e987BF8232" \ + --perp-name "SPOT" \ + --perp-symbol "SPOT" \ + --vault-name "Staked Ampleforth" \ + --vault-symbol "stAMPL" \ + --verify "false" + +yarn hardhat --network ganache ops:perp:updateTolerableTrancheMaturity \ + --address "0x99b1445fC02b080c76AB85E5A41578e6fDF30510" \ + --minimum "300" \ + --maximum "601" + +yarn hardhat --network ganache ops:fee:setSwapFees \ + --address "0x89967625335C35c5FE1F3C1c03D37fdEb6f415Ed" \ + --fee-perc "0.05" + +yarn hardhat --network ganache deploy:Router \ + --verify "false" + +######################################################################## +## OPS +yarn hardhat --network ganache ops:increaseTimeBy 300 +yarn hardhat --network ganache ops:rebase:MockAMPL \ + --ampl-address "0x00404F73C76BC75b0D86F8AdDA8500e987BF8232" \ + --rebase-perc 0.1 + +## System/INFO +yarn hardhat --network ganache ops:perp:info 0x99b1445fC02b080c76AB85E5A41578e6fDF30510 +yarn hardhat --network ganache ops:vault:info 0x4741f9c161003100fF0Ba1097E149d143458bD0B + +yarn hardhat --network ganache ops:perp:updateState 0x99b1445fC02b080c76AB85E5A41578e6fDF30510 +yarn hardhat --network ganache ops:vault:recoverAndRedeploy \ + --vault-address 0x4741f9c161003100fF0Ba1097E149d143458bD0B + +yarn hardhat --network ganache ops:vault:deploy \ + --vault-address 0x4741f9c161003100fF0Ba1097E149d143458bD0B + +yarn hardhat --network ganache ops:vault:recover \ + --vault-address 0x4741f9c161003100fF0Ba1097E149d143458bD0B + +## Perp +yarn hardhat --network ganache ops:perp:trancheAndDeposit \ + --router-address 0x704c83b179fAD95A97b12aDda5c98Fde32d258c8 \ + --perp-address 0x99b1445fC02b080c76AB85E5A41578e6fDF30510 \ + --collateral-amount 250 + +yarn hardhat --network ganache ops:perp:redeem \ + --router-address 0x704c83b179fAD95A97b12aDda5c98Fde32d258c8 \ + --perp-address 0x99b1445fC02b080c76AB85E5A41578e6fDF30510 \ + --amount 10 + +## Vault +yarn hardhat --network ganache ops:vault:deposit \ + --vault-address 0x4741f9c161003100fF0Ba1097E149d143458bD0B \ + --underlying-amount 1000 + +yarn hardhat --network ganache ops:vault:redeem \ + --vault-address 0x4741f9c161003100fF0Ba1097E149d143458bD0B \ + --amount "0.001" + +yarn hardhat --network ganache ops:vault:swapUnderlyingForPerps \ + --vault-address 0x4741f9c161003100fF0Ba1097E149d143458bD0B \ + --underlying-amount 10 + +yarn hardhat --network ganache ops:vault:swapPerpsForUnderlying \ + --vault-address 0x4741f9c161003100fF0Ba1097E149d143458bD0B \ + --perp-amount 10 + +## Tranches +yarn hardhat --network ganache ops:redeemTranches \ + --bond-issuer-address "0xeb289644a33df897B1E30f0aa5cC0F17DD29Bdc2" + +### populate +yarn hardhat --network ganache ops:increaseTimeBy 120 +yarn hardhat --network ganache ops:trancheAndDeposit \ + --router-address 0x704c83b179fAD95A97b12aDda5c98Fde32d258c8 \ + --perp-address 0x99b1445fC02b080c76AB85E5A41578e6fDF30510 \ + --collateral-amount 250 +yarn hardhat --network ganache ops:vault:deposit \ + --vault-address 0x4741f9c161003100fF0Ba1097E149d143458bD0B \ + --underlying-amount 250 +yarn hardhat --network ganache ops:vault:recoverAndRedeploy \ + --vault-address 0x4741f9c161003100fF0Ba1097E149d143458bD0B \ No newline at end of file diff --git a/spot-contracts/tasks/mainnet.sh b/spot-contracts/tasks/scripts/mainnet.sh similarity index 92% rename from spot-contracts/tasks/mainnet.sh rename to spot-contracts/tasks/scripts/mainnet.sh index 0f6e7a97..d3330c6b 100644 --- a/spot-contracts/tasks/mainnet.sh +++ b/spot-contracts/tasks/scripts/mainnet.sh @@ -112,5 +112,8 @@ yarn hardhat --network mainnet ops:vault:deposit \ ######################################################################## ## upgrade +yarn hardhat --network mainnet validate_upgrade PerpetualTranche 0xC1f33e0cf7e40a67375007104B929E49a581bafE +yarn hardhat --network mainnet validate_upgrade RolloverVault 0x82A91a0D599A45d8E9Af781D67f695d7C72869Bd -yarn hardhat --network mainnet prepare_upgrade:perp:mainnet 0xC1f33e0cf7e40a67375007104B929E49a581bafE +yarn hardhat --network mainnet prepare_upgrade PerpetualTranche 0xC1f33e0cf7e40a67375007104B929E49a581bafE +yarn hardhat --network mainnet prepare_upgrade RolloverVault 0x82A91a0D599A45d8E9Af781D67f695d7C72869Bd \ No newline at end of file diff --git a/spot-contracts/tasks/scripts/mainnet_v2.sh b/spot-contracts/tasks/scripts/mainnet_v2.sh new file mode 100644 index 00000000..9eac4b7a --- /dev/null +++ b/spot-contracts/tasks/scripts/mainnet_v2.sh @@ -0,0 +1,15 @@ +# spot v2 check storage layout +yarn hardhat --network mainnet validate_upgrade PerpetualTranche 0xC1f33e0cf7e40a67375007104B929E49a581bafE +yarn hardhat --network mainnet validate_upgrade RolloverVault 0x82A91a0D599A45d8E9Af781D67f695d7C72869Bd + +# deploy new contracts +yarn hardhat --network mainnet deploy:FeePolicy +yarn hardhat --network mainnet deploy:Router + +# deploy new implementations +yarn hardhat --network mainnet prepare_upgrade PerpetualTranche 0xC1f33e0cf7e40a67375007104B929E49a581bafE +yarn hardhat --network mainnet prepare_upgrade RolloverVault 0x82A91a0D599A45d8E9Af781D67f695d7C72869Bd + +# execute via multisig +# proxyAdmin.upgrade(0xC1f33e0cf7e40a67375007104B929E49a581bafE, 0xf4FF6a7203F91Ae72D0273DF7596a5Df5a85999b) +# proxyAdmin.upgrade(0x82A91a0D599A45d8E9Af781D67f695d7C72869Bd, 0x9Bdba3bc5aB8EC0E895344705dC85fC29645748a) \ No newline at end of file diff --git a/spot-contracts/tasks/scripts/sepolia.sh b/spot-contracts/tasks/scripts/sepolia.sh new file mode 100644 index 00000000..97f54a10 --- /dev/null +++ b/spot-contracts/tasks/scripts/sepolia.sh @@ -0,0 +1,93 @@ +######################################################################## +## DEPLOYMENT + +yarn hardhat --network sepolia deploy:MockAMPL --verify "false" + +yarn hardhat --network sepolia deploy:BondFactory --verify "false" + +yarn hardhat --network sepolia deploy:BondIssuer \ + --bond-factory-address "0x25BcaEd6377CEAA345f12C2005a42e669B8a29fC" \ + --bond-duration "3600" \ + --issue-frequency "1200" \ + --issue-window-offset "0" \ + --collateral-token-address "0x251410f849ad67bebffdb5a549e5f02d5d9c25ba" \ + --tranche-ratios "[333,667]" \ + --issue true + +yarn hardhat --network sepolia deploy:PerpSystem \ + --bond-issuer-address "0x3838C8d4D092d40Cb27DD22Dafc6E1A81ea2DB60" \ + --collateral-token-address "0x251410f849ad67bebffdb5a549e5f02d5d9c25ba" \ + --perp-name "SPOT" \ + --perp-symbol "SPOT" \ + --vault-name "Staked Ampleforth" \ + --vault-symbol "stAMPL" + +yarn hardhat --network sepolia deploy:Router + +yarn hardhat --network sepolia ops:perp:updateTolerableTrancheMaturity \ + --address 0xdcCef9065876fD654bAddeBAa778FDA43E0bfC1F \ + --minimum 600 \ + --maximum 3600 + +yarn hardhat --network sepolia ops:fee:setSwapFees \ + --address "0x2DdF288F26490D1147296cC0FA2B3c4da5E15f10" \ + --fee-perc "0.05" + +######################################################################## +## OPS +yarn hardhat --network sepolia ops:perp:info 0xdcCef9065876fD654bAddeBAa778FDA43E0bfC1F +yarn hardhat --network sepolia ops:vault:info 0x107614c6602A8e602952Da107B8fE62b5Ab13b04 +yarn hardhat --network sepolia ops:perp:updateState 0xdcCef9065876fD654bAddeBAa778FDA43E0bfC1F +yarn hardhat --network sepolia ops:vault:recoverAndRedeploy \ + --vault-address 0x107614c6602A8e602952Da107B8fE62b5Ab13b04 +yarn hardhat --network sepolia ops:vault:deploy \ + --vault-address 0x107614c6602A8e602952Da107B8fE62b5Ab13b04 +yarn hardhat --network sepolia ops:vault:recover \ + --vault-address 0x107614c6602A8e602952Da107B8fE62b5Ab13b04 + +yarn hardhat --network sepolia ops:rebase:MockAMPL \ + --ampl-address "0x251410f849ad67bebffdb5a549e5f02d5d9c25ba" \ + --rebase-perc 0.1 + +# Perp +yarn hardhat --network sepolia ops:perp:trancheAndDeposit \ + --router-address 0x5B59915E5754C62C40Ba5e7467382ced958F8559 \ + --perp-address 0xdcCef9065876fD654bAddeBAa778FDA43E0bfC1F \ + --collateral-amount 250 + +yarn hardhat --network sepolia ops:perp:redeem \ + --router-address 0x5B59915E5754C62C40Ba5e7467382ced958F8559 \ + --perp-address 0xdcCef9065876fD654bAddeBAa778FDA43E0bfC1F \ + --amount 10 + +## Vault +yarn hardhat --network sepolia ops:vault:deposit \ + --vault-address 0x107614c6602A8e602952Da107B8fE62b5Ab13b04 \ + --underlying-amount 250 + +yarn hardhat --network sepolia ops:vault:redeem \ + --vault-address 0x107614c6602A8e602952Da107B8fE62b5Ab13b04 \ + --amount "0.001" + +yarn hardhat --network sepolia ops:vault:swapUnderlyingForPerps \ + --vault-address 0x107614c6602A8e602952Da107B8fE62b5Ab13b04 \ + --underlying-amount 10 + +yarn hardhat --network sepolia ops:vault:swapPerpsForUnderlying \ + --vault-address 0x107614c6602A8e602952Da107B8fE62b5Ab13b04 \ + --perp-amount 10 + +## Tranches +yarn hardhat --network sepolia ops:redeemTranches \ + --bond-issuer-address 0x3838C8d4D092d40Cb27DD22Dafc6E1A81ea2DB60 + +######################################################################## +## upgrade +yarn hardhat --network sepolia validate_upgrade PerpetualTranche 0xdcCef9065876fD654bAddeBAa778FDA43E0bfC1F +yarn hardhat --network sepolia validate_upgrade RolloverVault 0x107614c6602A8e602952Da107B8fE62b5Ab13b04 + +yarn hardhat --network sepolia prepare_upgrade PerpetualTranche 0xdcCef9065876fD654bAddeBAa778FDA43E0bfC1F +yarn hardhat --network sepolia prepare_upgrade RolloverVault 0x107614c6602A8e602952Da107B8fE62b5Ab13b04 + +yarn hardhat --network sepolia upgrade:testnet PerpetualTranche 0xdcCef9065876fD654bAddeBAa778FDA43E0bfC1F +yarn hardhat --network sepolia upgrade:testnet RolloverVault 0x107614c6602A8e602952Da107B8fE62b5Ab13b04 diff --git a/spot-contracts/tasks/upgrade/index.ts b/spot-contracts/tasks/upgrade/index.ts index 3dbc1c50..b3677f7a 100644 --- a/spot-contracts/tasks/upgrade/index.ts +++ b/spot-contracts/tasks/upgrade/index.ts @@ -1,2 +1,78 @@ -import "./perp"; -import "./vaults"; +import { getAdminAddress, getImplementationAddress } from "@openzeppelin/upgrades-core"; +import { task, types } from "hardhat/config"; +import { TaskArguments } from "hardhat/types"; +import { sleep } from "../helpers"; + +task("validate_upgrade") + .addPositionalParam("factory", "the name of the factory", undefined, types.string, false) + .addPositionalParam("address", "the address of the deployed proxy contract", undefined, types.string, false) + .setAction(async function (args: TaskArguments, hre) { + const { factory, address } = args; + const Factory = await hre.ethers.getContractFactory(factory); + console.log("Trying strict validation"); + try { + await hre.upgrades.validateUpgrade(address, Factory); + } catch (e) { + console.log("Strict validation failed. ", e); + console.log("Retrying but allowing variable renames."); + await hre.upgrades.validateUpgrade(address, Factory, { + unsafeAllowRenames: true, + }); + } + console.log("Success"); + }); + +task("prepare_upgrade") + .addPositionalParam("factory", "the name of the factory", undefined, types.string, false) + .addPositionalParam("address", "the address of the deployed proxy contract", undefined, types.string, false) + .addParam("fromIdx", "the index of sender", 0, types.int) + .setAction(async function (args: TaskArguments, hre) { + const { factory, address } = args; + + const signer = (await hre.ethers.getSigners())[args.fromIdx]; + const signerAddress = await signer.getAddress(); + console.log("Signer", signerAddress); + + console.log("Proxy Admin", await getAdminAddress(hre.ethers.provider, address)); + + const Factory = await hre.ethers.getContractFactory(factory); + const newImpl = await hre.upgrades.prepareUpgrade(address, Factory, { + unsafeAllowRenames: true, + }); + console.log("Deploying using", factory); + console.log("New implementation at:", newImpl); + + console.log("Update implementation by running the following:"); + console.log(`proxyAdmin.upgrade(${address}, ${newImpl})`); + + await sleep(15); + await hre.run("verify:contract", { + address: newImpl, + }); + }); + +task("upgrade:testnet") + .addPositionalParam("factory", "the name of the factory", undefined, types.string, false) + .addPositionalParam("address", "the address of the deployed proxy contract", undefined, types.string, false) + .addParam("fromIdx", "the index of sender", 0, types.int) + .setAction(async function (args: TaskArguments, hre) { + const signer = (await hre.ethers.getSigners())[args.fromIdx]; + const signerAddress = await signer.getAddress(); + console.log("Signer", signerAddress); + + const { factory, address } = args; + const Factory = await hre.ethers.getContractFactory(factory); + + console.log("Proxy", address); + console.log("Current implementation", await getImplementationAddress(hre.ethers.provider, address)); + + const impl = await hre.upgrades.upgradeProxy(address, Factory); + await impl.deployed(); + const newImpl = await getImplementationAddress(hre.ethers.provider, address); + console.log("Updated implementation", newImpl); + + await sleep(15); + await hre.run("verify:contract", { + address: newImpl, + }); + }); diff --git a/spot-contracts/tasks/upgrade/perp.ts b/spot-contracts/tasks/upgrade/perp.ts deleted file mode 100644 index 17e64f97..00000000 --- a/spot-contracts/tasks/upgrade/perp.ts +++ /dev/null @@ -1,54 +0,0 @@ -import { getAdminAddress, getImplementationAddress } from "@openzeppelin/upgrades-core"; -import { task, types } from "hardhat/config"; -import { TaskArguments } from "hardhat/types"; -import { sleep } from "../helpers"; - -task("upgrade:perp:testnet") - .addPositionalParam("perpAddress", "the address of the perp contract", undefined, types.string, false) - .addParam("fromIdx", "the index of sender", 0, types.int) - .setAction(async function (args: TaskArguments, hre) { - const { perpAddress } = args; - - const signer = (await hre.ethers.getSigners())[args.fromIdx]; - const signerAddress = await signer.getAddress(); - console.log("Signer", signerAddress); - - console.log("Current implementation", await getImplementationAddress(hre.ethers.provider, perpAddress)); - - const PerpetualTranche = await hre.ethers.getContractFactory("PerpetualTranche"); - const perp = await hre.upgrades.upgradeProxy(perpAddress, PerpetualTranche); - await perp.deployed(); - - const newImpl = await getImplementationAddress(hre.ethers.provider, perpAddress); - console.log("Updated implementation", newImpl); - - await sleep(15); - await hre.run("verify:contract", { - address: newImpl, - }); - }); - -task("prepare_upgrade:perp:mainnet") - .addPositionalParam("perpAddress", "the address of the perp contract", undefined, types.string, false) - .addParam("fromIdx", "the index of sender", 0, types.int) - .setAction(async function (args: TaskArguments, hre) { - const { perpAddress } = args; - - const signer = (await hre.ethers.getSigners())[args.fromIdx]; - const signerAddress = await signer.getAddress(); - console.log("Signer", signerAddress); - - console.log("Proxy Admin", await getAdminAddress(hre.ethers.provider, perpAddress)); - - const PerpetualTranche = await hre.ethers.getContractFactory("PerpetualTranche"); - const newImpl = await hre.upgrades.prepareUpgrade(perpAddress, PerpetualTranche); - console.log("New implementation at:", newImpl); - - console.log("Update implementation through the multisig"); - console.log(`proxyAdmin.upgrade(${perpAddress}, ${newImpl})`); - - await sleep(15); - await hre.run("verify:contract", { - address: newImpl, - }); - }); diff --git a/spot-contracts/tasks/upgrade/vaults.ts b/spot-contracts/tasks/upgrade/vaults.ts deleted file mode 100644 index b4bf3398..00000000 --- a/spot-contracts/tasks/upgrade/vaults.ts +++ /dev/null @@ -1,54 +0,0 @@ -import { getAdminAddress, getImplementationAddress } from "@openzeppelin/upgrades-core"; -import { task, types } from "hardhat/config"; -import { TaskArguments } from "hardhat/types"; -import { sleep } from "../helpers"; - -task("upgrade:rolloverVault:testnet") - .addPositionalParam("vaultAddress", "the address of the rollover vault contract", undefined, types.string, false) - .addParam("fromIdx", "the index of sender", 0, types.int) - .setAction(async function (args: TaskArguments, hre) { - const { vaultAddress } = args; - - const signer = (await hre.ethers.getSigners())[args.fromIdx]; - const signerAddress = await signer.getAddress(); - console.log("Signer", signerAddress); - - console.log("Current implementation", await getImplementationAddress(hre.ethers.provider, vaultAddress)); - - const RolloverVault = await hre.ethers.getContractFactory("RolloverVault"); - const vault = await hre.upgrades.upgradeProxy(vaultAddress, RolloverVault); - await vault.deployed(); - - const newImpl = await getImplementationAddress(hre.ethers.provider, vaultAddress); - console.log("Updated implementation", newImpl); - - await sleep(15); - await hre.run("verify:contract", { - address: newImpl, - }); - }); - -task("prepare_upgrade:rolloverVault:mainnet") - .addPositionalParam("vaultAddress", "the address of the perp contract", undefined, types.string, false) - .addParam("fromIdx", "the index of sender", 0, types.int) - .setAction(async function (args: TaskArguments, hre) { - const { vaultAddress } = args; - - const signer = (await hre.ethers.getSigners())[args.fromIdx]; - const signerAddress = await signer.getAddress(); - console.log("Signer", signerAddress); - - console.log("Proxy Admin", await getAdminAddress(hre.ethers.provider, vaultAddress)); - - const RolloverVault = await hre.ethers.getContractFactory("RolloverVault"); - const newImpl = await hre.upgrades.prepareUpgrade(vaultAddress, RolloverVault); - console.log("New implementation at:", newImpl); - - console.log("Update implementation through the multisig"); - console.log(`proxyAdmin.upgrade(${vaultAddress}, ${newImpl})`); - - await sleep(15); - await hre.run("verify:contract", { - address: newImpl, - }); - }); diff --git a/spot-contracts/test/BondIssuer.ts b/spot-contracts/test/BondIssuer.ts index 751b50ca..f27497b1 100644 --- a/spot-contracts/test/BondIssuer.ts +++ b/spot-contracts/test/BondIssuer.ts @@ -1,5 +1,5 @@ import { expect } from "chai"; -import { ethers, network } from "hardhat"; +import { ethers, network, upgrades } from "hardhat"; import { Contract, Transaction, Signer } from "ethers"; import { TimeHelpers, setupBondFactory, bondAt } from "./helpers"; @@ -18,8 +18,13 @@ describe("BondIssuer", function () { token = await Token.deploy(); await token.init("Test token", "TEST"); const BondIssuer = await ethers.getContractFactory("BondIssuer"); - issuer = await BondIssuer.deploy(bondFactory.address, token.address); - await issuer.init(86400, [200, 300, 500], 3600, 900); + issuer = await upgrades.deployProxy( + BondIssuer.connect(deployer), + [bondFactory.address, token.address, 86400, [200, 300, 500], 3600, 900], + { + initializer: "init(address,address,uint256,uint256[],uint256,uint256)", + }, + ); await TimeHelpers.setNextBlockTimestamp(mockTime(0)); }); @@ -72,8 +77,13 @@ describe("BondIssuer", function () { describe("when tranche ratios are improper", function () { it("should revert", async function () { - await expect(issuer.updateTrancheRatios([200, 300, 501])).to.be.revertedWith( - "BondIssuer: Invalid tranche ratios", + await expect(issuer.updateTrancheRatios([200, 300, 499])).to.be.revertedWithCustomError( + issuer, + "UnacceptableTrancheRatios", + ); + await expect(issuer.updateTrancheRatios([200, 300, 501])).to.be.revertedWithCustomError( + issuer, + "UnacceptableTrancheRatios", ); }); }); @@ -132,10 +142,9 @@ describe("BondIssuer", function () { expect(await issuer.lastIssueWindowTimestamp()).to.eq(mockTime(4500)); expect(await issuer.issuedCount()).to.eq(2); - await expect(issuer.issuedBondAt(1)).to.not.be.reverted; - + await expect(issuer.issuedBondAt(2)).to.be.reverted; expect(await issuer.activeCount()).to.eq(2); - await expect(issuer.activeBondAt(1)).to.not.be.reverted; + await expect(issuer.activeBondAt(2)).to.be.reverted; await TimeHelpers.setNextBlockTimestamp(mockTime(4505)); await expect(issuer.issue()).not.to.emit(issuer, "BondIssued"); @@ -221,7 +230,7 @@ describe("BondIssuer", function () { }); it("should revert", async function () { - await expect(issuer.matureActive()).to.be.revertedWith("NoMaturedBonds"); + await expect(issuer.matureActive()).to.be.revertedWithCustomError(issuer, "NoMaturedBonds"); }); }); diff --git a/spot-contracts/test/FeePolicy.ts b/spot-contracts/test/FeePolicy.ts new file mode 100644 index 00000000..b3eea137 --- /dev/null +++ b/spot-contracts/test/FeePolicy.ts @@ -0,0 +1,542 @@ +import { expect, use } from "chai"; +import { ethers, upgrades } from "hardhat"; +import { Contract, Signer } from "ethers"; +import { smock } from "@defi-wonderland/smock"; + +import { toPercFixedPtAmt, toFixedPtAmt } from "./helpers"; +use(smock.matchers); + +let feePolicy: Contract, deployer: Signer, otherUser: Signer; +const toPerc = toPercFixedPtAmt; +const toAmt = toFixedPtAmt; + +describe("FeePolicy", function () { + beforeEach(async function () { + const accounts = await ethers.getSigners(); + deployer = accounts[0]; + otherUser = accounts[1]; + + const FeePolicy = await ethers.getContractFactory("FeePolicy"); + feePolicy = await upgrades.deployProxy(FeePolicy.connect(deployer), [], { + initializer: "init()", + }); + }); + + describe("#init", function () { + it("should return the initial parameters", async function () { + expect(await feePolicy.targetSubscriptionRatio()).to.eq(toPerc("1.33")); + expect(await feePolicy.deviationRatioBoundLower()).to.eq(toPerc("0.75")); + expect(await feePolicy.deviationRatioBoundUpper()).to.eq(toPerc("2")); + }); + it("should return owner", async function () { + expect(await feePolicy.owner()).to.eq(await deployer.getAddress()); + }); + it("should return decimals", async function () { + expect(await feePolicy.decimals()).to.eq(8); + }); + }); + + describe("#updateTargetSubscriptionRatio", function () { + describe("when triggered by non-owner", function () { + it("should revert", async function () { + await expect(feePolicy.connect(otherUser).updateTargetSubscriptionRatio(toPerc("1.25"))).to.be.revertedWith( + "Ownable: caller is not the owner", + ); + }); + }); + + describe("when parameters are invalid", function () { + it("should revert", async function () { + await expect( + feePolicy.connect(deployer).updateTargetSubscriptionRatio(toPerc("0.5")), + ).to.be.revertedWithCustomError(feePolicy, "InvalidTargetSRBounds"); + }); + }); + + describe("when parameters are invalid", function () { + it("should revert", async function () { + await expect( + feePolicy.connect(deployer).updateTargetSubscriptionRatio(toPerc("2.1")), + ).to.be.revertedWithCustomError(feePolicy, "InvalidTargetSRBounds"); + }); + }); + + describe("when triggered by owner", function () { + it("should update the target sr", async function () { + expect(await feePolicy.targetSubscriptionRatio()).to.eq(toPerc("1.33")); + await feePolicy.connect(deployer).updateTargetSubscriptionRatio(toPerc("1.25")); + expect(await feePolicy.targetSubscriptionRatio()).to.eq(toPerc("1.25")); + }); + }); + }); + + describe("#updateDeviationRatioBounds", function () { + describe("when triggered by non-owner", function () { + it("should revert", async function () { + await expect( + feePolicy.connect(otherUser).updateDeviationRatioBounds(toPerc("1"), toPerc("1")), + ).to.be.revertedWith("Ownable: caller is not the owner"); + }); + }); + + describe("when parameters are invalid", function () { + it("should revert", async function () { + await expect( + feePolicy.connect(deployer).updateDeviationRatioBounds(toPerc("1.01"), toPerc("2")), + ).to.be.revertedWithCustomError(feePolicy, "InvalidDRBounds"); + }); + }); + + describe("when parameters are invalid", function () { + it("should revert", async function () { + await expect( + feePolicy.connect(deployer).updateDeviationRatioBounds(toPerc("0.5"), toPerc("0.99")), + ).to.be.revertedWithCustomError(feePolicy, "InvalidDRBounds"); + }); + }); + + describe("when triggered by owner", function () { + it("should update the target sr", async function () { + expect(await feePolicy.deviationRatioBoundLower()).to.eq(toPerc("0.75")); + expect(await feePolicy.deviationRatioBoundUpper()).to.eq(toPerc("2")); + await feePolicy.connect(deployer).updateDeviationRatioBounds(toPerc("0.5"), toPerc("1.5")); + expect(await feePolicy.deviationRatioBoundLower()).to.eq(toPerc("0.5")); + expect(await feePolicy.deviationRatioBoundUpper()).to.eq(toPerc("1.5")); + }); + }); + }); + + describe("#updatePerpMintFees", function () { + describe("when triggered by non-owner", function () { + it("should revert", async function () { + await expect(feePolicy.connect(otherUser).updatePerpMintFees(toPerc("0.01"))).to.be.revertedWith( + "Ownable: caller is not the owner", + ); + }); + }); + + describe("when parameters are invalid", function () { + it("should revert", async function () { + await expect(feePolicy.connect(deployer).updatePerpMintFees(toPerc("1.01"))).to.be.revertedWithCustomError( + feePolicy, + "InvalidPerc", + ); + }); + }); + + describe("when triggered by owner", function () { + it("should update the mint fees", async function () { + expect(await feePolicy.computePerpMintFeePerc()).to.eq("0"); + await feePolicy.connect(deployer).updatePerpMintFees(toPerc("0.01")); + expect(await feePolicy.computePerpMintFeePerc()).to.eq(toPerc("0.01")); + }); + }); + }); + + describe("#updatePerpBurnFees", function () { + describe("when triggered by non-owner", function () { + it("should revert", async function () { + await expect(feePolicy.connect(otherUser).updatePerpBurnFees(toPerc("0.01"))).to.be.revertedWith( + "Ownable: caller is not the owner", + ); + }); + }); + + describe("when parameters are invalid", function () { + it("should revert", async function () { + await expect(feePolicy.connect(deployer).updatePerpBurnFees(toPerc("1.01"))).to.be.revertedWithCustomError( + feePolicy, + "InvalidPerc", + ); + }); + }); + + describe("when triggered by owner", function () { + it("should update the burn fees", async function () { + expect(await feePolicy.computePerpBurnFeePerc()).to.eq("0"); + await feePolicy.connect(deployer).updatePerpBurnFees(toPerc("0.01")); + expect(await feePolicy.computePerpBurnFeePerc()).to.eq(toPerc("0.01")); + }); + }); + }); + + describe("#updatePerpRolloverFees", function () { + describe("when triggered by non-owner", function () { + it("should revert", async function () { + await expect( + feePolicy.connect(otherUser).updatePerpRolloverFees({ + lower: toPerc("-0.01"), + upper: toPerc("0.01"), + growth: toPerc("3"), + }), + ).to.be.revertedWith("Ownable: caller is not the owner"); + }); + }); + + describe("when parameters are invalid", function () { + it("should revert", async function () { + await expect( + feePolicy.connect(deployer).updatePerpRolloverFees({ + lower: toPerc("-0.051"), + upper: toPerc("0.01"), + growth: toPerc("3"), + }), + ).to.be.revertedWithCustomError(feePolicy, "InvalidSigmoidAsymptotes"); + }); + it("should revert", async function () { + await expect( + feePolicy.connect(deployer).updatePerpRolloverFees({ + lower: toPerc("-0.01"), + upper: toPerc("0.051"), + growth: toPerc("3"), + }), + ).to.be.revertedWithCustomError(feePolicy, "InvalidSigmoidAsymptotes"); + }); + + it("should revert", async function () { + await expect( + feePolicy.connect(deployer).updatePerpRolloverFees({ + lower: toPerc("0.02"), + upper: toPerc("0.01"), + growth: toPerc("3"), + }), + ).to.be.revertedWithCustomError(feePolicy, "InvalidSigmoidAsymptotes"); + }); + }); + + describe("when triggered by owner", function () { + it("should update parameters", async function () { + expect(await feePolicy.computePerpRolloverFeePerc(toPerc("1"))).to.eq(0); + expect(await feePolicy.computePerpRolloverFeePerc(toPerc("10"))).to.eq(toPerc("0.00769230")); + expect(await feePolicy.computePerpRolloverFeePerc("0")).to.eq(toPerc("-0.00245837")); + + await feePolicy.connect(deployer).updatePerpRolloverFees({ + lower: toPerc("-0.009"), + upper: toPerc("0.009"), + growth: toPerc("3"), + }); + + expect(await feePolicy.computePerpRolloverFeePerc(toPerc("1"))).to.eq(0); + expect(await feePolicy.computePerpRolloverFeePerc(toPerc("10"))).to.eq(toPerc("0.009")); + expect(await feePolicy.computePerpRolloverFeePerc("0")).to.eq(toPerc("-0.007")); + }); + }); + }); + + describe("#updateVaultMintFees", function () { + describe("when triggered by non-owner", function () { + it("should revert", async function () { + await expect(feePolicy.connect(otherUser).updateVaultMintFees(toPerc("0.01"))).to.be.revertedWith( + "Ownable: caller is not the owner", + ); + }); + }); + + describe("when parameters are invalid", function () { + it("should revert", async function () { + await expect(feePolicy.connect(deployer).updateVaultMintFees(toPerc("1.01"))).to.be.revertedWithCustomError( + feePolicy, + "InvalidPerc", + ); + }); + }); + + describe("when triggered by owner", function () { + beforeEach(async function () {}); + it("should update the vault mint fees", async function () { + expect(await feePolicy.computeVaultMintFeePerc()).to.eq("0"); + await feePolicy.connect(deployer).updateVaultMintFees(toPerc("0.025")); + expect(await feePolicy.computeVaultMintFeePerc()).to.eq(toPerc("0.025")); + }); + }); + }); + + describe("#updateVaultBurnFees", function () { + describe("when triggered by non-owner", function () { + it("should revert", async function () { + await expect(feePolicy.connect(otherUser).updateVaultBurnFees(toPerc("0.01"))).to.be.revertedWith( + "Ownable: caller is not the owner", + ); + }); + }); + + describe("when parameters are invalid", function () { + it("should revert", async function () { + await expect(feePolicy.connect(deployer).updateVaultBurnFees(toPerc("1.01"))).to.be.revertedWithCustomError( + feePolicy, + "InvalidPerc", + ); + }); + }); + + describe("when triggered by owner", function () { + it("should update the vault burn fees", async function () { + expect(await feePolicy.computeVaultBurnFeePerc()).to.eq("0"); + await feePolicy.connect(deployer).updateVaultBurnFees(toPerc("0.025")); + expect(await feePolicy.computeVaultBurnFeePerc()).to.eq(toPerc("0.025")); + }); + }); + }); + + describe("#updateVaultUnderlyingToPerpSwapFeePerc", function () { + describe("when triggered by non-owner", function () { + it("should revert", async function () { + await expect( + feePolicy.connect(otherUser).updateVaultUnderlyingToPerpSwapFeePerc(toPerc("0.1")), + ).to.be.revertedWith("Ownable: caller is not the owner"); + }); + }); + + describe("when parameters are invalid", function () { + it("should revert", async function () { + await expect( + feePolicy.connect(deployer).updateVaultUnderlyingToPerpSwapFeePerc(toPerc("1.01")), + ).to.be.revertedWithCustomError(feePolicy, "InvalidPerc"); + }); + }); + + describe("when triggered by owner", function () { + it("should update the vault burn fees", async function () { + expect(await feePolicy.computeUnderlyingToPerpVaultSwapFeePerc(toPerc("1.01"), toPerc("1.01"))).to.eq( + toPerc("1"), + ); + await feePolicy.connect(deployer).updateVaultUnderlyingToPerpSwapFeePerc(toPerc("0.1")); + expect(await feePolicy.computeUnderlyingToPerpVaultSwapFeePerc(toPerc("1.01"), toPerc("1.01"))).to.eq( + toPerc("0.1"), + ); + }); + }); + }); + + describe("#updateVaultPerpToUnderlyingSwapFeePerc", function () { + describe("when triggered by non-owner", function () { + it("should revert", async function () { + await expect( + feePolicy.connect(otherUser).updateVaultPerpToUnderlyingSwapFeePerc(toPerc("0.1")), + ).to.be.revertedWith("Ownable: caller is not the owner"); + }); + }); + + describe("when parameters are invalid", function () { + it("should revert", async function () { + await expect( + feePolicy.connect(deployer).updateVaultPerpToUnderlyingSwapFeePerc(toPerc("1.01")), + ).to.be.revertedWithCustomError(feePolicy, "InvalidPerc"); + }); + }); + + describe("when triggered by owner", function () { + it("should update the vault burn fees", async function () { + expect(await feePolicy.computePerpToUnderlyingVaultSwapFeePerc(toPerc("1"), toPerc("1"))).to.eq(toPerc("1")); + await feePolicy.connect(deployer).updateVaultPerpToUnderlyingSwapFeePerc(toPerc("0.2")); + expect(await feePolicy.computePerpToUnderlyingVaultSwapFeePerc(toPerc("1"), toPerc("1"))).to.eq(toPerc("0.2")); + }); + }); + }); + + describe("fee logic", function () { + beforeEach(async function () { + await feePolicy.updatePerpMintFees(toPerc("0.025")); + await feePolicy.updatePerpBurnFees(toPerc("0.035")); + await feePolicy.updatePerpRolloverFees({ + lower: toPerc("-0.00253"), + upper: toPerc("0.00769"), + growth: toPerc("5"), + }); + await feePolicy.updateVaultUnderlyingToPerpSwapFeePerc(toPerc("0.1")); + await feePolicy.updateVaultPerpToUnderlyingSwapFeePerc(toPerc("0.15")); + await feePolicy.updateVaultMintFees(toPerc("0.05")); + await feePolicy.updateVaultBurnFees(toPerc("0.075")); + await feePolicy.updateDeviationRatioBounds(toPerc("0.85"), toPerc("1.15")); + }); + + describe("when dr is decreasing", function () { + async function cmpFees(dr1, dr2, fees) { + // perp mint, vault burn and swap u2p + expect(await feePolicy.computePerpMintFeePerc()).to.eq(toPerc(fees[0])); + expect(await feePolicy.computeVaultBurnFeePerc()).to.eq(toPerc(fees[1])); + expect(await feePolicy.computeUnderlyingToPerpVaultSwapFeePerc(toPerc(dr1), toPerc(dr2))).to.eq( + toPerc(fees[2]), + ); + } + + describe("when ONE < dr2, dr1", function () { + it("should compute fees as expected", async function () { + await cmpFees("1.1", "1.01", ["0.025", "0.075", "0.1"]); + }); + }); + + describe("when ONE <= dr2, dr1", function () { + it("should compute fees as expected", async function () { + await cmpFees("1.01", "1", ["0.025", "0.075", "0.1"]); + }); + }); + + describe("when dr2 < ONE < dr1", function () { + it("should compute fees as expected", async function () { + await cmpFees("1.05", "0.95", ["0.025", "0.075", "0.1"]); + }); + }); + + describe("when dr2 < ONE < dr1", function () { + it("should compute fees as expected", async function () { + await cmpFees("1.01", "0.96", ["0.025", "0.075", "0.1"]); + }); + }); + + describe("when dr2 < ONE < dr1", function () { + it("should compute fees as expected", async function () { + await cmpFees("1.1", "0.99", ["0.025", "0.075", "0.1"]); + }); + }); + + describe("when dr2,dr1 < ONE", function () { + it("should compute fees as expected", async function () { + await cmpFees("0.99", "0.95", ["0.025", "0.075", "0.1"]); + }); + }); + + describe("when dr2 < lower < dr1 < ONE", function () { + it("should compute fees as expected", async function () { + await cmpFees("0.9", "0.8", ["0.025", "0.075", "1"]); + }); + }); + + describe("when dr2 < lower < ONE < dr1", function () { + it("should compute fees as expected", async function () { + await cmpFees("1.2", "0.8", ["0.025", "0.075", "1"]); + }); + }); + + describe("when dr2,dr1 < lower", function () { + it("should compute fees as expected", async function () { + await cmpFees("0.8", "0.75", ["0.025", "0.075", "1"]); + }); + }); + }); + + describe("when dr is increasing", function () { + async function cmpFees(dr1, dr2, fees) { + // perp burn, vault mint and swap p2u + expect(await feePolicy.computePerpBurnFeePerc()).to.eq(toPerc(fees[0])); + expect(await feePolicy.computeVaultMintFeePerc()).to.eq(toPerc(fees[1])); + expect(await feePolicy.computePerpToUnderlyingVaultSwapFeePerc(toPerc(dr1), toPerc(dr2))).to.eq( + toPerc(fees[2]), + ); + } + + describe("when dr1, dr2 < ONE", function () { + it("should compute fees as expected", async function () { + await cmpFees("0.9", "0.99", ["0.035", "0.05", "0.15"]); + }); + }); + + describe("when dr1, dr2 <= ONE", function () { + it("should compute fees as expected", async function () { + await cmpFees("0.9", "1", ["0.035", "0.05", "0.15"]); + }); + }); + + describe("when dr1 < ONE < dr2", function () { + it("should compute fees as expected", async function () { + await cmpFees("0.95", "1.05", ["0.035", "0.05", "0.15"]); + }); + }); + + describe("when dr1 < ONE < dr2", function () { + it("should compute fees as expected", async function () { + await cmpFees("0.99", "1.04", ["0.035", "0.05", "0.15"]); + }); + }); + + describe("when dr1 < ONE < dr2", function () { + it("should compute fees as expected", async function () { + await cmpFees("0.99", "1.1", ["0.035", "0.05", "0.15"]); + }); + }); + + describe("when ONE < dr1, dr2 < upper", function () { + it("should compute fees as expected", async function () { + await cmpFees("1.01", "1.05", ["0.035", "0.05", "0.15"]); + }); + }); + + describe("when ONE < dr1 < upper < dr2", function () { + it("should compute fees as expected", async function () { + await cmpFees("1.01", "1.25", ["0.035", "0.05", "1"]); + }); + }); + + describe("when dr1 < ONE < upper < dr2", function () { + it("should compute fees as expected", async function () { + await cmpFees("1.01", "1.25", ["0.035", "0.05", "1"]); + }); + }); + + describe("when upper < dr1, dr2", function () { + it("should compute fees as expected", async function () { + await cmpFees("1.25", "1.35", ["0.035", "0.05", "1"]); + }); + }); + }); + + describe("rollover fee", function () { + it("should compute fees as expected", async function () { + expect(await feePolicy.computePerpRolloverFeePerc(toPerc("0.01"))).to.eq(toPerc("-0.00242144")); + expect(await feePolicy.computePerpRolloverFeePerc(toPerc("0.25"))).to.eq(toPerc("-0.00228606")); + expect(await feePolicy.computePerpRolloverFeePerc(toPerc("0.5"))).to.eq(toPerc("-0.00196829")); + expect(await feePolicy.computePerpRolloverFeePerc(toPerc("0.75"))).to.eq(toPerc("-0.00128809")); + expect(await feePolicy.computePerpRolloverFeePerc(toPerc("0.9"))).to.eq(toPerc("-0.00060117")); + expect(await feePolicy.computePerpRolloverFeePerc(toPerc("0.99"))).to.eq(toPerc("-0.00004101")); + expect(await feePolicy.computePerpRolloverFeePerc(toPerc("1"))).to.eq("0"); + expect(await feePolicy.computePerpRolloverFeePerc(toPerc("1.01"))).to.eq(toPerc("0.00004146")); + expect(await feePolicy.computePerpRolloverFeePerc(toPerc("1.05"))).to.eq(toPerc("0.00034407")); + expect(await feePolicy.computePerpRolloverFeePerc(toPerc("1.1"))).to.eq(toPerc("0.00071519")); + expect(await feePolicy.computePerpRolloverFeePerc(toPerc("1.25"))).to.eq(toPerc("0.00195646")); + expect(await feePolicy.computePerpRolloverFeePerc(toPerc("1.5"))).to.eq(toPerc("0.00411794")); + expect(await feePolicy.computePerpRolloverFeePerc(toPerc("1.75"))).to.eq(toPerc("0.00580663")); + expect(await feePolicy.computePerpRolloverFeePerc(toPerc("2"))).to.eq(toPerc("0.00680345")); + expect(await feePolicy.computePerpRolloverFeePerc(toPerc("5"))).to.eq(toPerc("0.00768997")); + }); + }); + }); + + describe("#computeDeviationRatio", async function () { + beforeEach(async function () { + await feePolicy.updateTargetSubscriptionRatio(toPerc("1.25")); + }); + + describe("when deviation = 1.0", function () { + it("should return 1", async function () { + const r = await feePolicy["computeDeviationRatio((uint256,uint256,uint256))"]({ + perpTVL: toAmt("100"), + vaultTVL: toAmt("500"), + seniorTR: 200, + }); + expect(r).to.eq(toPerc("1")); + }); + }); + + describe("when deviation > 1.0", function () { + it("should compute dr", async function () { + const r = await feePolicy["computeDeviationRatio((uint256,uint256,uint256))"]({ + perpTVL: toAmt("100"), + vaultTVL: toAmt("1000"), + seniorTR: 200, + }); + expect(r).to.eq(toPerc("2")); + }); + }); + + describe("when deviation < 1.0", function () { + it("should compute dr", async function () { + const r = await feePolicy["computeDeviationRatio((uint256,uint256,uint256))"]({ + perpTVL: toAmt("100"), + vaultTVL: toAmt("250"), + seniorTR: 200, + }); + expect(r).to.eq(toPerc("0.5")); + }); + }); + }); +}); diff --git a/spot-contracts/test/PerpetualTranche_deposit.ts b/spot-contracts/test/PerpetualTranche_deposit.ts deleted file mode 100644 index dff69cd4..00000000 --- a/spot-contracts/test/PerpetualTranche_deposit.ts +++ /dev/null @@ -1,883 +0,0 @@ -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, - createBondWithFactory, - depositIntoBond, - bondAt, - getTranches, - toFixedPtAmt, - toDiscountFixedPtAmt, - toPriceFixedPtAmt, - advancePerpQueue, - checkReserveComposition, -} from "./helpers"; -use(smock.matchers); - -let perp: Contract, - bondFactory: Contract, - collateralToken: Contract, - issuer: Contract, - feeStrategy: Contract, - pricingStrategy: Contract, - discountStrategy: Contract, - deployer: Signer, - deployerAddress: string, - otherUser: Signer, - depositBond: Contract, - depositTrancheA: Contract, - depositTrancheZ: Contract; -describe("PerpetualTranche", function () { - beforeEach(async function () { - await network.provider.send("hardhat_reset"); - - const accounts = await ethers.getSigners(); - deployer = accounts[0]; - otherUser = accounts[1]; - deployerAddress = await deployer.getAddress(); - - 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(3600, [500, 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 advancePerpQueue(perp, 3600); - - depositBond = await bondAt(await perp.callStatic.getDepositBond()); - [depositTrancheA, depositTrancheZ] = await getTranches(depositBond); - - 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")); - await depositTrancheZ.approve(perp.address, toFixedPtAmt("500")); - }); - - afterEach(async function () { - await network.provider.send("hardhat_reset"); - }); - - describe("#deposit", function () { - describe("when paused", function () { - beforeEach(async function () { - await perp.updateKeeper(deployerAddress); - await perp.pause(); - }); - - it("should revert", async function () { - await expect(perp.deposit(depositTrancheA.address, toFixedPtAmt("500"))).to.revertedWith("Pausable: paused"); - }); - }); - - describe("when bond issuer is NOT set correctly", function () { - let bond: Contract; - beforeEach(async function () { - 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.getLatestBond.returns(bond.address); - }); - it("should not update the deposit bond", async function () { - await depositTrancheA.approve(perp.address, toFixedPtAmt("500")); - await expect(perp.deposit(depositTrancheA.address, toFixedPtAmt("500"))).to.not.be.reverted; - expect(await perp.callStatic.getDepositBond()).to.not.eq(bond.address); - }); - }); - - describe("when the trancheIn is not of deposit bond", function () { - beforeEach(async function () { - const bond = await createBondWithFactory(bondFactory, collateralToken, [500, 500], 3600); - await depositIntoBond(bond, toFixedPtAmt("1000"), deployer); - depositTrancheA = (await getTranches(bond))[0]; - }); - it("should revert", async function () { - await depositTrancheA.approve(perp.address, toFixedPtAmt("500")); - await expect(perp.deposit(depositTrancheA.address, toFixedPtAmt("500"))).to.revertedWithCustomError( - perp, - "UnacceptableDepositTranche", - ); - }); - }); - - describe("when the malicious trancheIn is deposited which points to the deposit bond", function () { - it("should revert", async function () { - const ERC20 = await ethers.getContractFactory("MockTranche"); - const maliciousTranche = await ERC20.deploy(); - await maliciousTranche.init("Tranche", "TRA"); - 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.revertedWithCustomError( - perp, - "UnacceptableDepositTranche", - ); - }); - }); - - describe("when user has not approved sufficient tranche tokens", function () { - beforeEach(async function () { - await depositTrancheA.approve(perp.address, toFixedPtAmt("0")); - }); - it("should revert", async function () { - await expect(perp.deposit(depositTrancheA.address, toFixedPtAmt("500"))).to.revertedWith( - "ERC20: transfer amount exceeds allowance", - ); - }); - }); - - describe("when user has insufficient balance", function () { - beforeEach(async function () { - await depositTrancheA.transfer(perp.address, toFixedPtAmt("500")); - }); - it("should revert", async function () { - expect(await depositTrancheA.balanceOf(deployerAddress)).to.eq("0"); - await expect(perp.deposit(depositTrancheA.address, toFixedPtAmt("500"))).to.revertedWith( - "ERC20: transfer amount exceeds balance", - ); - }); - }); - - describe("when the supply cap is exceeded", function () { - beforeEach(async function () { - await perp.updateMintingLimits(toFixedPtAmt("499"), toFixedPtAmt("1000")); - }); - - it("should revert", async function () { - await expect(perp.deposit(depositTrancheA.address, toFixedPtAmt("500"))).to.revertedWithCustomError( - perp, - "ExceededMaxSupply", - ); - }); - }); - - describe("when the supply cap is exceeded and existing supply > 0", function () { - beforeEach(async function () { - await perp.deposit(depositTrancheA.address, toFixedPtAmt("400")); - await perp.updateMintingLimits(toFixedPtAmt("499"), toFixedPtAmt("1000")); - }); - - it("should revert", async function () { - await expect(perp.deposit(depositTrancheA.address, toFixedPtAmt("100"))).to.revertedWithCustomError( - perp, - "ExceededMaxSupply", - ); - }); - }); - - describe("when the tranche mint limit is exceeded", function () { - beforeEach(async function () { - await perp.updateMintingLimits(toFixedPtAmt("1000"), toFixedPtAmt("499")); - }); - - it("should revert", async function () { - await expect(perp.deposit(depositTrancheA.address, toFixedPtAmt("500"))).to.revertedWithCustomError( - perp, - "ExceededMaxMintPerTranche", - ); - }); - }); - - describe("when the tranche mint limit is exceeded and existing supply > 0", function () { - beforeEach(async function () { - await perp.deposit(depositTrancheA.address, toFixedPtAmt("400")); - await perp.updateMintingLimits(toFixedPtAmt("1000"), toFixedPtAmt("499")); - }); - - 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.revertedWithCustomError( - perp, - "UnacceptableMintAmt", - ); - }); - }); - - describe("when tranche price is zero", function () { - beforeEach(async function () { - await pricingStrategy.computeTranchePrice - .whenCalledWith(depositTrancheA.address) - .returns(toPriceFixedPtAmt("0")); - }); - - it("should revert", async function () { - 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.revertedWithCustomError( - perp, - "UnacceptableMintAmt", - ); - }); - }); - - describe("when total supply is zero", function () { - describe("when tranche price is 0.5", function () { - beforeEach(async function () { - await pricingStrategy.computeTranchePrice - .whenCalledWith(depositTrancheA.address) - .returns(toPriceFixedPtAmt("0.5")); - }); - - it("should mint the correct amount", async function () { - const r = await perp.callStatic.computeMintAmt(depositTrancheA.address, toFixedPtAmt("500")); - expect(r).to.eq(toFixedPtAmt("250")); - }); - }); - - describe("when tranche discount is 0.5", function () { - beforeEach(async function () { - await discountStrategy.computeTrancheDiscount - .whenCalledWith(depositTrancheA.address) - .returns(toDiscountFixedPtAmt("0.5")); - }); - - it("should mint the correct amount", async function () { - const r = await perp.callStatic.computeMintAmt(depositTrancheA.address, toFixedPtAmt("500")); - expect(r).to.eq(toFixedPtAmt("250")); - }); - }); - - describe("when tranche discount is 0.5 and tranche price is 0.5", function () { - beforeEach(async function () { - 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 () { - const r = await perp.callStatic.computeMintAmt(depositTrancheA.address, toFixedPtAmt("500")); - expect(r).to.eq(toFixedPtAmt("125")); - }); - }); - }); - - describe("when total supply > zero", function () { - let newBond: Contract, newTranche: Contract; - beforeEach(async function () { - 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); - newBond = await bondAt(await perp.callStatic.getDepositBond()); - await depositIntoBond(newBond, toFixedPtAmt("1000"), deployer); - const tranches = await getTranches(newBond); - newTranche = tranches[0]; - await newTranche.approve(perp.address, toFixedPtAmt("250")); - }); - - describe("when price is eql to avg reserve price", function () { - describe("when discount is 1", function () { - beforeEach(async function () { - 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 () { - const r = await perp.callStatic.computeMintAmt(newTranche.address, toFixedPtAmt("250")); - expect(r).to.eq(toFixedPtAmt("250")); - }); - }); - - describe("when discount < 1", function () { - beforeEach(async function () { - 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 () { - const r = await perp.callStatic.computeMintAmt(newTranche.address, toFixedPtAmt("250")); - expect(r).to.eq(toFixedPtAmt("125")); - }); - }); - - describe("when discount > 1", function () { - beforeEach(async function () { - 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 () { - const r = await perp.callStatic.computeMintAmt(newTranche.address, toFixedPtAmt("250")); - expect(r).to.eq(toFixedPtAmt("500")); - }); - }); - }); - - describe("when price is > avg reserve price", function () { - beforeEach(async function () { - 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 () { - const r = await perp.callStatic.computeMintAmt(newTranche.address, toFixedPtAmt("250")); - expect(r).to.eq(toFixedPtAmt("500")); - }); - }); - - describe("when price is < avg reserve price", function () { - beforeEach(async function () { - 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 () { - const r = await perp.callStatic.computeMintAmt(newTranche.address, toFixedPtAmt("250")); - expect(r).to.eq(toFixedPtAmt("125")); - }); - }); - }); - - describe("when fee is in native token", function () { - describe("when fee is zero", function () { - it("should mint perp tokens", async function () { - await expect(() => perp.deposit(depositTrancheA.address, toFixedPtAmt("500"))).to.changeTokenBalance( - perp, - deployer, - toFixedPtAmt("500"), - ); - }); - it("should NOT withhold any fee amount", async function () { - await expect(() => perp.deposit(depositTrancheA.address, toFixedPtAmt("500"))).to.changeTokenBalance( - perp, - perp, - toFixedPtAmt("0"), - ); - }); - it("should transfer the tranches in", async function () { - await expect(() => perp.deposit(depositTrancheA.address, toFixedPtAmt("500"))).to.changeTokenBalances( - depositTrancheA, - [deployer, perp], - [toFixedPtAmt("-500"), toFixedPtAmt("500")], - ); - }); - it("should return the mintAmt", async function () { - const r = await perp.callStatic.computeMintAmt(depositTrancheA.address, toFixedPtAmt("500")); - expect(r).to.eq(toFixedPtAmt("500")); - }); - }); - describe("when fee > 0", function () { - beforeEach(async function () { - await feeStrategy.computeMintFees.returns([toFixedPtAmt("1"), "0"]); - }); - it("should mint perp tokens", async function () { - await expect(() => perp.deposit(depositTrancheA.address, toFixedPtAmt("500"))).to.changeTokenBalance( - perp, - deployer, - toFixedPtAmt("499"), - ); - }); - it("should withhold fee amount", async function () { - await expect(() => perp.deposit(depositTrancheA.address, toFixedPtAmt("500"))).to.changeTokenBalance( - perp, - perp, - toFixedPtAmt("1"), - ); - }); - it("should transfer the tranches in", async function () { - await expect(() => perp.deposit(depositTrancheA.address, toFixedPtAmt("500"))).to.changeTokenBalances( - depositTrancheA, - [deployer, perp], - [toFixedPtAmt("-500"), toFixedPtAmt("500")], - ); - }); - it("should return the mintAmt", async function () { - const r = await perp.callStatic.computeMintAmt(depositTrancheA.address, toFixedPtAmt("500")); - expect(r).to.eq(toFixedPtAmt("500")); - }); - }); - describe("when fee < 0", function () { - beforeEach(async function () { - await depositIntoBond(await bondAt(await perp.callStatic.getDepositBond()), toFixedPtAmt("2"), deployer); - await depositTrancheA.increaseAllowance(perp.address, toFixedPtAmt("1")); - await perp.deposit(depositTrancheA.address, toFixedPtAmt("1")); - await perp.transfer(perp.address, 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( - perp, - deployer, - toFixedPtAmt("501"), - ); - }); - it("should transfer reward amount", async function () { - await expect(() => perp.deposit(depositTrancheA.address, toFixedPtAmt("500"))).to.changeTokenBalance( - perp, - perp, - toFixedPtAmt("-1"), - ); - }); - it("should transfer the tranches in", async function () { - await expect(() => perp.deposit(depositTrancheA.address, toFixedPtAmt("500"))).to.changeTokenBalances( - depositTrancheA, - [deployer, perp], - [toFixedPtAmt("-500"), toFixedPtAmt("500")], - ); - }); - it("should return the mintAmt", async function () { - const r = await perp.callStatic.computeMintAmt(depositTrancheA.address, toFixedPtAmt("500")); - expect(r).to.eq(toFixedPtAmt("500")); - }); - }); - describe("when fee < 0 and abs(fee) < balance", function () { - beforeEach(async function () { - await depositIntoBond(await bondAt(await perp.callStatic.getDepositBond()), toFixedPtAmt("2"), deployer); - await depositTrancheA.increaseAllowance(perp.address, toFixedPtAmt("1")); - await perp.deposit(depositTrancheA.address, toFixedPtAmt("1")); - await perp.transfer(perp.address, toFixedPtAmt("0.5")); - await feeStrategy.computeMintFees.returns([toFixedPtAmt("-1"), "0"]); - }); - it("should mint perp tokens", async function () { - await expect(() => perp.deposit(depositTrancheA.address, toFixedPtAmt("500"))).to.changeTokenBalance( - perp, - deployer, - toFixedPtAmt("501"), - ); - }); - it("should transfer reward amount", async function () { - await expect(() => perp.deposit(depositTrancheA.address, toFixedPtAmt("500"))).to.changeTokenBalance( - perp, - perp, - toFixedPtAmt("-0.5"), - ); - }); - it("should mint the delta", async function () { - await expect(perp.deposit(depositTrancheA.address, toFixedPtAmt("500"))) - .to.emit(perp, "Transfer") - .withArgs(constants.AddressZero, deployerAddress, toFixedPtAmt("0.5")); - }); - it("should transfer the tranches in", async function () { - await expect(() => perp.deposit(depositTrancheA.address, toFixedPtAmt("500"))).to.changeTokenBalances( - depositTrancheA, - [deployer, perp], - [toFixedPtAmt("-500"), toFixedPtAmt("500")], - ); - }); - it("should return the mintAmt", async function () { - const r = await perp.callStatic.computeMintAmt(depositTrancheA.address, toFixedPtAmt("500")); - expect(r).to.eq(toFixedPtAmt("500")); - }); - }); - describe("when protocol fee > 0", function () { - beforeEach(async function () { - await perp.transferOwnership(await otherUser.getAddress()); - 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( - perp, - deployer, - toFixedPtAmt("498"), - ); - }); - it("should withhold reserve fee amount", async function () { - await expect(() => perp.deposit(depositTrancheA.address, toFixedPtAmt("500"))).to.changeTokenBalance( - perp, - perp, - toFixedPtAmt("1"), - ); - }); - it("should withhold protocol fee amount", async function () { - await expect(() => perp.deposit(depositTrancheA.address, toFixedPtAmt("500"))).to.changeTokenBalance( - perp, - otherUser, - toFixedPtAmt("1"), - ); - }); - it("should transfer the tranches in", async function () { - await expect(() => perp.deposit(depositTrancheA.address, toFixedPtAmt("500"))).to.changeTokenBalances( - depositTrancheA, - [deployer, perp], - [toFixedPtAmt("-500"), toFixedPtAmt("500")], - ); - }); - it("should return the mintAmt", async function () { - const r = await perp.callStatic.computeMintAmt(depositTrancheA.address, toFixedPtAmt("500")); - expect(r).to.eq(toFixedPtAmt("500")); - }); - }); - }); - - describe("when fee is in non-native token", function () { - let feeToken: Contract; - beforeEach(async function () { - const ERC20 = await ethers.getContractFactory("MockERC20"); - feeToken = await ERC20.deploy(); - await feeToken.init("Mock token", "MOCK"); - await feeStrategy.feeToken.returns(feeToken.address); - }); - - describe("when fee is zero", async function () { - it("should mint perp tokens", async function () { - await expect(() => perp.deposit(depositTrancheA.address, toFixedPtAmt("500"))).to.changeTokenBalance( - perp, - deployer, - toFixedPtAmt("500"), - ); - }); - it("should NOT transfer fees", async function () { - await expect(() => perp.deposit(depositTrancheA.address, toFixedPtAmt("500"))).to.changeTokenBalances( - feeToken, - [deployer, perp], - [toFixedPtAmt("0"), toFixedPtAmt("0")], - ); - }); - it("should transfer the tranches in", async function () { - await expect(() => perp.deposit(depositTrancheA.address, toFixedPtAmt("500"))).to.changeTokenBalances( - depositTrancheA, - [deployer, perp], - [toFixedPtAmt("-500"), toFixedPtAmt("500")], - ); - }); - it("should return the mintAmt", async function () { - const r = await perp.callStatic.computeMintAmt(depositTrancheA.address, toFixedPtAmt("500")); - expect(r).to.eq(toFixedPtAmt("500")); - }); - }); - describe("when fee > 0", async function () { - beforeEach(async function () { - await feeStrategy.computeMintFees.returns([toFixedPtAmt("1"), "0"]); - }); - - describe("with no approval", function () { - it("should revert", async function () { - await expect(perp.deposit(depositTrancheA.address, toFixedPtAmt("500"))).to.be.revertedWith( - "ERC20: insufficient allowance", - ); - }); - }); - - describe("with insufficient balance", function () { - beforeEach(async function () { - await feeToken.approve(perp.address, toFixedPtAmt("1")); - }); - - it("should revert", async function () { - await expect(perp.deposit(depositTrancheA.address, toFixedPtAmt("500"))).to.be.revertedWith( - "ERC20: transfer amount exceeds balance", - ); - }); - }); - - describe("with sufficient fee", async function () { - beforeEach(async function () { - await feeStrategy.computeMintFees.returns([toFixedPtAmt("1"), "0"]); - await feeToken.mint(deployerAddress, toFixedPtAmt("1")); - await feeToken.approve(perp.address, toFixedPtAmt("1")); - }); - - it("should mint perp tokens", async function () { - await expect(() => perp.deposit(depositTrancheA.address, toFixedPtAmt("500"))).to.changeTokenBalance( - perp, - deployer, - toFixedPtAmt("500"), - ); - }); - it("should transfer fees", async function () { - await expect(() => perp.deposit(depositTrancheA.address, toFixedPtAmt("500"))).to.changeTokenBalances( - feeToken, - [deployer, perp], - [toFixedPtAmt("-1"), toFixedPtAmt("1")], - ); - }); - it("should transfer the tranches in", async function () { - await expect(() => perp.deposit(depositTrancheA.address, toFixedPtAmt("500"))).to.changeTokenBalances( - depositTrancheA, - [deployer, perp], - [toFixedPtAmt("-500"), toFixedPtAmt("500")], - ); - }); - it("should return the mintAmt", async function () { - const r = await perp.callStatic.computeMintAmt(depositTrancheA.address, toFixedPtAmt("500")); - expect(r).to.eq(toFixedPtAmt("500")); - }); - }); - }); - describe("when fee < 0", async function () { - beforeEach(async function () { - await feeStrategy.computeMintFees.returns([toFixedPtAmt("-1"), "0"]); - await feeToken.mint(perp.address, toFixedPtAmt("1")); - }); - - it("should mint perp tokens", async function () { - await expect(() => perp.deposit(depositTrancheA.address, toFixedPtAmt("500"))).to.changeTokenBalance( - perp, - deployer, - toFixedPtAmt("500"), - ); - }); - it("should transfer fees", async function () { - await expect(() => perp.deposit(depositTrancheA.address, toFixedPtAmt("500"))).to.changeTokenBalances( - feeToken, - [deployer, perp], - [toFixedPtAmt("1"), toFixedPtAmt("-1")], - ); - }); - it("should transfer the tranches in", async function () { - await expect(() => perp.deposit(depositTrancheA.address, toFixedPtAmt("500"))).to.changeTokenBalances( - depositTrancheA, - [deployer, perp], - [toFixedPtAmt("-500"), toFixedPtAmt("500")], - ); - }); - it("should return the mintAmt", async function () { - const r = await perp.callStatic.computeMintAmt(depositTrancheA.address, toFixedPtAmt("500")); - expect(r).to.eq(toFixedPtAmt("500")); - }); - }); - describe("when protocol fee > 0", async function () { - beforeEach(async function () { - await perp.transferOwnership(await otherUser.getAddress()); - await feeStrategy.computeMintFees.returns([toFixedPtAmt("1"), toFixedPtAmt("0.5")]); - await feeToken.mint(deployerAddress, toFixedPtAmt("1.5")); - await feeToken.approve(perp.address, toFixedPtAmt("1.5")); - }); - - it("should mint perp tokens", async function () { - await expect(() => perp.deposit(depositTrancheA.address, toFixedPtAmt("500"))).to.changeTokenBalance( - perp, - deployer, - toFixedPtAmt("500"), - ); - }); - it("should transfer reserve & protocol fees", async function () { - await expect(() => perp.deposit(depositTrancheA.address, toFixedPtAmt("500"))).to.changeTokenBalances( - feeToken, - [deployer, perp, otherUser], - [toFixedPtAmt("-1.5"), toFixedPtAmt("1"), toFixedPtAmt("0.5")], - ); - }); - it("should transfer the tranches in", async function () { - await expect(() => perp.deposit(depositTrancheA.address, toFixedPtAmt("500"))).to.changeTokenBalances( - depositTrancheA, - [deployer, perp], - [toFixedPtAmt("-500"), toFixedPtAmt("500")], - ); - }); - it("should return the mintAmt", async function () { - const r = await perp.callStatic.computeMintAmt(depositTrancheA.address, toFixedPtAmt("500")); - expect(r).to.eq(toFixedPtAmt("500")); - }); - }); - }); - - describe("when the reserve has no tranches", function () { - let tx: Transaction; - beforeEach(async function () { - expect(await perp.callStatic.getDepositBond()).to.eq(depositBond.address); - - await checkReserveComposition(perp, [collateralToken], [toFixedPtAmt("0")]); - expect(await perp.totalSupply()).to.eq(0); - - tx = perp.deposit(depositTrancheA.address, toFixedPtAmt("500")); - await tx; - }); - - it("should NOT update the deposit bond", async function () { - expect(await perp.callStatic.getDepositBond()).to.eq(depositBond.address); - }); - it("should emit tranche discount", async function () { - await expect(tx).to.emit(perp, "DiscountApplied").withArgs(depositTrancheA.address, toDiscountFixedPtAmt("1")); - }); - it("should emit reserve synced", async function () { - await expect(tx).to.emit(perp, "ReserveSynced").withArgs(depositTrancheA.address, toFixedPtAmt("500")); - }); - it("should update the reserve", async function () { - await checkReserveComposition( - perp, - [collateralToken, depositTrancheA], - [toFixedPtAmt("0"), toFixedPtAmt("500")], - ); - }); - it("should update the total supply", async function () { - expect(await perp.totalSupply()).to.eq(toFixedPtAmt("500")); - }); - }); - - describe("when the reserve has tranches", function () { - beforeEach(async function () { - await perp.deposit(depositTrancheA.address, toFixedPtAmt("200")); - - expect(await perp.callStatic.getDepositBond()).to.eq(depositBond.address); - await checkReserveComposition( - perp, - [collateralToken, depositTrancheA], - [toFixedPtAmt("0"), toFixedPtAmt("200")], - ); - - expect(await perp.totalSupply()).to.eq(toFixedPtAmt("200")); - }); - - describe("when inserting the an existing tranche", async function () { - let tx: Transaction; - beforeEach(async function () { - tx = perp.deposit(depositTrancheA.address, toFixedPtAmt("300")); - await tx; - }); - - it("should NOT update the deposit bond", async function () { - expect(await perp.callStatic.getDepositBond()).to.eq(depositBond.address); - }); - it("should NOT emit tranche discount", async function () { - await expect(tx) - .not.to.emit(perp, "DiscountApplied") - .withArgs(depositTrancheA.address, toDiscountFixedPtAmt("1")); - }); - it("should emit reserve synced", async function () { - await expect(tx).to.emit(perp, "ReserveSynced").withArgs(depositTrancheA.address, toFixedPtAmt("500")); - }); - it("should update the reserve", async function () { - await checkReserveComposition( - perp, - [collateralToken, depositTrancheA], - [toFixedPtAmt("0"), toFixedPtAmt("500")], - ); - }); - it("should update the total supply", async function () { - expect(await perp.totalSupply()).to.eq(toFixedPtAmt("500")); - }); - }); - - describe("when inserting a new tranche", function () { - let newBond: Contract, newTranche: Contract, tx: Transaction; - beforeEach(async function () { - await advancePerpQueue(perp, 1200); - - newBond = await bondAt(await perp.callStatic.getDepositBond()); - await depositIntoBond(newBond, toFixedPtAmt("1000"), deployer); - const tranches = await getTranches(newBond); - newTranche = tranches[0]; - await discountStrategy.computeTrancheDiscount - .whenCalledWith(newTranche.address) - .returns(toDiscountFixedPtAmt("0.5")); - await pricingStrategy.computeTranchePrice - .whenCalledWith(newTranche.address) - .returns(toPriceFixedPtAmt("0.2")); - - await checkReserveComposition( - perp, - [collateralToken, depositTrancheA], - [toFixedPtAmt("0"), toFixedPtAmt("200")], - ); - - expect(await perp.totalSupply()).to.eq(toFixedPtAmt("200")); - - await newTranche.approve(perp.address, toFixedPtAmt("250")); - tx = perp.deposit(newTranche.address, toFixedPtAmt("250")); - await tx; - }); - - it("should update the deposit bond", async function () { - expect(await perp.callStatic.getDepositBond()).to.eq(newBond.address); - }); - it("should emit tranche discount", async function () { - await expect(tx).to.emit(perp, "DiscountApplied").withArgs(newTranche.address, toDiscountFixedPtAmt("0.5")); - }); - it("should emit reserve synced", async function () { - await expect(tx).to.emit(perp, "ReserveSynced").withArgs(newTranche.address, toFixedPtAmt("250")); - }); - it("should update the reserve", async function () { - await checkReserveComposition( - perp, - [collateralToken, depositTrancheA, newTranche], - [toFixedPtAmt("0"), toFixedPtAmt("200"), toFixedPtAmt("250")], - ); - }); - it("should update the total supply", async function () { - expect(await perp.totalSupply()).to.eq(toFixedPtAmt("225")); - }); - }); - }); - }); -}); diff --git a/spot-contracts/test/PerpetualTranche_redeem.ts b/spot-contracts/test/PerpetualTranche_redeem.ts deleted file mode 100644 index 29e0d9de..00000000 --- a/spot-contracts/test/PerpetualTranche_redeem.ts +++ /dev/null @@ -1,987 +0,0 @@ -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, - depositIntoBond, - bondAt, - getTranches, - toFixedPtAmt, - toDiscountFixedPtAmt, - toPriceFixedPtAmt, - advancePerpQueue, - checkReserveComposition, - rebase, -} from "./helpers"; -use(smock.matchers); - -let perp: Contract, - bondFactory: Contract, - collateralToken: Contract, - rebaseOracle: Contract, - issuer: Contract, - feeStrategy: Contract, - pricingStrategy: Contract, - discountStrategy: Contract, - deployer: Signer, - otherUser: Signer, - deployerAddress: string, - depositBond: Contract, - initialDepositTranche: Contract; - -describe("PerpetualTranche", function () { - beforeEach(async function () { - await network.provider.send("hardhat_reset"); - - const accounts = await ethers.getSigners(); - deployer = accounts[0]; - otherUser = accounts[1]; - deployerAddress = await deployer.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(3600, [500, 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 advancePerpQueue(perp, 3600); - - depositBond = await bondAt(await perp.callStatic.getDepositBond()); - [initialDepositTranche] = await getTranches(depositBond); - - 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")); - await perp.deposit(initialDepositTranche.address, toFixedPtAmt("500")); - }); - - afterEach(async function () { - await network.provider.send("hardhat_reset"); - }); - - describe("#burn", function () { - it("should burn tokens without redemption", async function () { - await checkReserveComposition( - perp, - [collateralToken, initialDepositTranche], - [toFixedPtAmt("0"), toFixedPtAmt("500")], - ); - expect(await perp.balanceOf(deployerAddress)).to.eq(toFixedPtAmt("500")); - await perp.burn(toFixedPtAmt("500")); - expect(await perp.balanceOf(deployerAddress)).to.eq(toFixedPtAmt("0")); - await checkReserveComposition( - perp, - [collateralToken, initialDepositTranche], - [toFixedPtAmt("0"), toFixedPtAmt("500")], - ); - }); - }); - - describe("#burnFrom", function () { - it("should burn tokens without redemption from authorized wallet", async function () { - await checkReserveComposition( - perp, - [collateralToken, initialDepositTranche], - [toFixedPtAmt("0"), toFixedPtAmt("500")], - ); - expect(await perp.balanceOf(deployerAddress)).to.eq(toFixedPtAmt("500")); - await perp.approve(await otherUser.getAddress(), toFixedPtAmt("500")); - await perp.connect(otherUser).burnFrom(deployerAddress, toFixedPtAmt("200")); - expect(await perp.balanceOf(deployerAddress)).to.eq(toFixedPtAmt("300")); - expect(await perp.allowance(deployerAddress, await otherUser.getAddress())).to.eq(toFixedPtAmt("300")); - await checkReserveComposition( - perp, - [collateralToken, initialDepositTranche], - [toFixedPtAmt("0"), toFixedPtAmt("500")], - ); - }); - }); - - describe("#redeem", function () { - describe("when paused", function () { - beforeEach(async function () { - await perp.updateKeeper(deployerAddress); - await perp.pause(); - }); - - it("should revert", async function () { - await expect(perp.redeem(toFixedPtAmt("500"))).to.revertedWith("Pausable: paused"); - }); - }); - - describe("when user has insufficient balance", function () { - beforeEach(async function () { - await perp.redeem(toFixedPtAmt("250")); - }); - - it("should revert", async function () { - expect(await perp.balanceOf(deployerAddress)).to.lte(toFixedPtAmt("500")); - 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.revertedWithCustomError(perp, "UnacceptableBurnAmt"); - }); - }); - - describe("when supply is zero", function () { - beforeEach(async function () { - await perp.burn(toFixedPtAmt("500")); - }); - - it("should revert", async function () { - await expect(perp.redeem(toFixedPtAmt("100"))).to.revertedWithCustomError(perp, "UnacceptableBurnAmt"); - }); - - it("should return 0", async function () { - const r = await perp.callStatic.computeRedemptionAmts(toFixedPtAmt("100")); - await expect(r[1][0]).to.eq(toFixedPtAmt("0")); - await expect(r[1][1]).to.eq(toFixedPtAmt("0")); - }); - }); - - describe("when the supply increases", function () { - beforeEach(async function () { - await feeStrategy.computeBurnFees.returns([toFixedPtAmt("-1"), "0"]); - }); - - it("should revert", async function () { - await expect(perp.redeem(toFixedPtAmt("0.01"))).to.revertedWithCustomError(perp, "ExpectedSupplyReduction"); - }); - }); - - describe("when fee is in native token", function () { - describe("when fee is zero", function () { - it("should burn perp tokens", async function () { - await expect(() => perp.redeem(toFixedPtAmt("500"))).to.changeTokenBalance( - perp, - deployer, - toFixedPtAmt("-500"), - ); - }); - it("should transfer the tranches out", async function () { - await expect(() => perp.redeem(toFixedPtAmt("500"))).to.changeTokenBalances( - initialDepositTranche, - [deployer, perp], - [toFixedPtAmt("500"), toFixedPtAmt("-500")], - ); - }); - it("should return the redemption amounts", async function () { - const r = await perp.callStatic.computeRedemptionAmts(toFixedPtAmt("500")); - expect(r[0][0]).to.eq(collateralToken.address); - expect(r[0][1]).to.eq(initialDepositTranche.address); - expect(r[1][0]).to.eq(toFixedPtAmt("0")); - expect(r[1][1]).to.eq(toFixedPtAmt("500")); - }); - }); - - describe("when fee > 0", function () { - beforeEach(async 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.computeBurnFees.returns([toFixedPtAmt("1"), "0"]); - }); - it("should burn perp tokens", async function () { - await expect(() => perp.redeem(toFixedPtAmt("500"))).to.changeTokenBalance( - perp, - deployer, - toFixedPtAmt("-501"), - ); - }); - it("should withhold fee", async function () { - await expect(() => perp.redeem(toFixedPtAmt("500"))).to.changeTokenBalance(perp, perp, toFixedPtAmt("1")); - }); - it("should transfer the tranches out", async function () { - await expect(() => perp.redeem(toFixedPtAmt("500"))).to.changeTokenBalances( - initialDepositTranche, - [deployer, perp], - [toFixedPtAmt("500"), toFixedPtAmt("-500")], - ); - }); - it("should return the redemption amounts", async function () { - const r = await perp.callStatic.computeRedemptionAmts(toFixedPtAmt("500")); - expect(r[0][0]).to.eq(collateralToken.address); - expect(r[0][1]).to.eq(initialDepositTranche.address); - expect(r[1][0]).to.eq(toFixedPtAmt("0")); - expect(r[1][1]).to.eq(toFixedPtAmt("500")); - }); - }); - - describe("when fee < 0", function () { - beforeEach(async 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 perp.transfer(perp.address, toFixedPtAmt("1")); - await feeStrategy.computeBurnFees.returns([toFixedPtAmt("-1"), "0"]); - }); - it("should burn perp tokens", async function () { - await expect(() => perp.redeem(toFixedPtAmt("500"))).to.changeTokenBalance( - perp, - deployer, - toFixedPtAmt("-499"), - ); - }); - it("should transfer reward", async function () { - await expect(() => perp.redeem(toFixedPtAmt("500"))).to.changeTokenBalance(perp, perp, toFixedPtAmt("-1")); - }); - it("should transfer the tranches out", async function () { - await expect(() => perp.redeem(toFixedPtAmt("500"))).to.changeTokenBalances( - initialDepositTranche, - [deployer, perp], - [toFixedPtAmt("500"), toFixedPtAmt("-500")], - ); - }); - it("should return the redemption amounts", async function () { - const r = await perp.callStatic.computeRedemptionAmts(toFixedPtAmt("500")); - expect(r[0][0]).to.eq(collateralToken.address); - expect(r[0][1]).to.eq(initialDepositTranche.address); - expect(r[1][0]).to.eq(toFixedPtAmt("0")); - expect(r[1][1]).to.eq(toFixedPtAmt("500")); - }); - }); - - describe("when fee < 0 && abs(fee) < balance", function () { - beforeEach(async 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 perp.transfer(perp.address, toFixedPtAmt("0.5")); - await feeStrategy.computeBurnFees.returns([toFixedPtAmt("-1"), "0"]); - }); - it("should burn perp tokens", async function () { - await expect(() => perp.redeem(toFixedPtAmt("500"))).to.changeTokenBalance( - perp, - deployer, - toFixedPtAmt("-499"), - ); - }); - it("should transfer reward", async function () { - await expect(() => perp.redeem(toFixedPtAmt("500"))).to.changeTokenBalance(perp, perp, toFixedPtAmt("-0.5")); - }); - it("should mint the delta", async function () { - await expect(perp.redeem(toFixedPtAmt("500"))) - .to.emit(perp, "Transfer") - .withArgs(constants.AddressZero, deployerAddress, toFixedPtAmt("0.5")); - }); - it("should transfer the tranches out", async function () { - await expect(() => perp.redeem(toFixedPtAmt("500"))).to.changeTokenBalances( - initialDepositTranche, - [deployer, perp], - [toFixedPtAmt("500"), toFixedPtAmt("-500")], - ); - }); - it("should return the redemption amounts", async function () { - const r = await perp.callStatic.computeRedemptionAmts(toFixedPtAmt("500")); - expect(r[0][0]).to.eq(collateralToken.address); - expect(r[0][1]).to.eq(initialDepositTranche.address); - expect(r[1][0]).to.eq(toFixedPtAmt("0")); - expect(r[1][1]).to.eq(toFixedPtAmt("500")); - }); - }); - - describe("when protocol fee > 0", function () { - beforeEach(async function () { - await perp.transferOwnership(await otherUser.getAddress()); - 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.computeBurnFees.returns([toFixedPtAmt("1"), toFixedPtAmt("0.5")]); - }); - it("should burn perp tokens", async function () { - await expect(() => perp.redeem(toFixedPtAmt("500"))).to.changeTokenBalance( - perp, - deployer, - toFixedPtAmt("-501.5"), - ); - }); - it("should withhold reserve fee", async function () { - await expect(() => perp.redeem(toFixedPtAmt("500"))).to.changeTokenBalance(perp, perp, toFixedPtAmt("1")); - }); - it("should withhold protocol fee", async function () { - await expect(() => perp.redeem(toFixedPtAmt("500"))).to.changeTokenBalance( - perp, - otherUser, - toFixedPtAmt("0.5"), - ); - }); - it("should transfer the tranches out", async function () { - await expect(() => perp.redeem(toFixedPtAmt("500"))).to.changeTokenBalances( - initialDepositTranche, - [deployer, perp], - [toFixedPtAmt("500"), toFixedPtAmt("-500")], - ); - }); - it("should return the redemption amounts", async function () { - const r = await perp.callStatic.computeRedemptionAmts(toFixedPtAmt("500")); - expect(r[0][0]).to.eq(collateralToken.address); - expect(r[0][1]).to.eq(initialDepositTranche.address); - expect(r[1][0]).to.eq(toFixedPtAmt("0")); - expect(r[1][1]).to.eq(toFixedPtAmt("500")); - }); - }); - }); - - describe("when fee is in non-native token", function () { - let feeToken: Contract; - beforeEach(async function () { - const ERC20 = await ethers.getContractFactory("MockERC20"); - feeToken = await ERC20.deploy(); - await feeToken.init("Mock token", "MOCK"); - await feeStrategy.feeToken.returns(feeToken.address); - }); - - describe("when fee is zero", async function () { - it("should burn perp tokens", async function () { - await expect(() => perp.redeem(toFixedPtAmt("500"))).to.changeTokenBalance( - perp, - deployer, - toFixedPtAmt("-500"), - ); - }); - it("should settle fee", async function () { - await expect(() => perp.redeem(toFixedPtAmt("500"))).to.changeTokenBalance( - feeToken, - deployer, - toFixedPtAmt("0"), - ); - }); - it("should transfer the tranches out", async function () { - await expect(() => perp.redeem(toFixedPtAmt("500"))).to.changeTokenBalances( - initialDepositTranche, - [deployer, perp], - [toFixedPtAmt("500"), toFixedPtAmt("-500")], - ); - }); - it("should return the redemption amounts", async function () { - const r = await perp.callStatic.computeRedemptionAmts(toFixedPtAmt("500")); - expect(r[0][0]).to.eq(collateralToken.address); - expect(r[0][1]).to.eq(initialDepositTranche.address); - expect(r[1][0]).to.eq(toFixedPtAmt("0")); - expect(r[1][1]).to.eq(toFixedPtAmt("500")); - }); - }); - - describe("when fee > 0", async function () { - beforeEach(async function () { - await feeStrategy.computeBurnFees.returns([toFixedPtAmt("1"), "0"]); - }); - - describe("with no approval", function () { - it("should revert", async function () { - await expect(perp.redeem(toFixedPtAmt("500"))).to.be.revertedWith("ERC20: insufficient allowance"); - }); - }); - - describe("with insufficient balance", function () { - beforeEach(async function () { - await feeToken.approve(perp.address, toFixedPtAmt("1")); - }); - - it("should revert", async function () { - await expect(perp.redeem(toFixedPtAmt("500"))).to.be.revertedWith("ERC20: transfer amount exceeds balance"); - }); - }); - - describe("with sufficient fee", async function () { - beforeEach(async function () { - await feeStrategy.computeBurnFees.returns([toFixedPtAmt("1"), "0"]); - await feeToken.mint(deployerAddress, toFixedPtAmt("1")); - await feeToken.approve(perp.address, toFixedPtAmt("1")); - }); - - it("should burn perp tokens", async function () { - await expect(() => perp.redeem(toFixedPtAmt("500"))).to.changeTokenBalance( - perp, - deployer, - toFixedPtAmt("-500"), - ); - }); - it("should transfer fee from redeemer", async function () { - await expect(() => perp.redeem(toFixedPtAmt("500"))).to.changeTokenBalance( - feeToken, - deployer, - toFixedPtAmt("-1"), - ); - }); - it("should transfer the tranches out", async function () { - await expect(() => perp.redeem(toFixedPtAmt("500"))).to.changeTokenBalances( - initialDepositTranche, - [deployer, perp], - [toFixedPtAmt("500"), toFixedPtAmt("-500")], - ); - }); - it("should return the redemption amounts", async function () { - const r = await perp.callStatic.computeRedemptionAmts(toFixedPtAmt("500")); - expect(r[0][0]).to.eq(collateralToken.address); - expect(r[0][1]).to.eq(initialDepositTranche.address); - expect(r[1][0]).to.eq(toFixedPtAmt("0")); - expect(r[1][1]).to.eq(toFixedPtAmt("500")); - }); - }); - }); - - describe("when fee < 0", async function () { - beforeEach(async function () { - await feeStrategy.computeBurnFees.returns([toFixedPtAmt("-1"), "0"]); - await feeToken.mint(perp.address, toFixedPtAmt("1")); - }); - - it("should burn perp tokens", async function () { - await expect(() => perp.redeem(toFixedPtAmt("500"))).to.changeTokenBalance( - perp, - deployer, - toFixedPtAmt("-500"), - ); - }); - it("should transfer fee to redeemer", async function () { - await expect(() => perp.redeem(toFixedPtAmt("500"))).to.changeTokenBalance( - feeToken, - deployer, - toFixedPtAmt("1"), - ); - }); - it("should transfer the tranches out", async function () { - await expect(() => perp.redeem(toFixedPtAmt("500"))).to.changeTokenBalances( - initialDepositTranche, - [deployer, perp], - [toFixedPtAmt("500"), toFixedPtAmt("-500")], - ); - }); - it("should return the redemption amounts", async function () { - const r = await perp.callStatic.computeRedemptionAmts(toFixedPtAmt("500")); - expect(r[0][0]).to.eq(collateralToken.address); - expect(r[0][1]).to.eq(initialDepositTranche.address); - expect(r[1][0]).to.eq(toFixedPtAmt("0")); - expect(r[1][1]).to.eq(toFixedPtAmt("500")); - }); - }); - - describe("when protocol fee > 0", async function () { - beforeEach(async function () { - await perp.transferOwnership(await otherUser.getAddress()); - await feeStrategy.computeBurnFees.returns([toFixedPtAmt("1"), toFixedPtAmt("0.5")]); - await feeToken.mint(deployerAddress, toFixedPtAmt("1.5")); - await feeToken.approve(perp.address, toFixedPtAmt("1.5")); - }); - - it("should burn perp tokens", async function () { - await expect(() => perp.redeem(toFixedPtAmt("500"))).to.changeTokenBalance( - perp, - deployer, - toFixedPtAmt("-500"), - ); - }); - - it("should transfer fees from redeemer", async function () { - await expect(() => perp.redeem(toFixedPtAmt("500"))).to.changeTokenBalances( - feeToken, - [deployer, perp, otherUser], - [toFixedPtAmt("-1.5"), toFixedPtAmt("1"), toFixedPtAmt("0.5")], - ); - }); - - it("should transfer the tranches out", async function () { - await expect(() => perp.redeem(toFixedPtAmt("500"))).to.changeTokenBalances( - initialDepositTranche, - [deployer, perp], - [toFixedPtAmt("500"), toFixedPtAmt("-500")], - ); - }); - - it("should return the redemption amounts", async function () { - const r = await perp.callStatic.computeRedemptionAmts(toFixedPtAmt("500")); - expect(r[0][0]).to.eq(collateralToken.address); - expect(r[0][1]).to.eq(initialDepositTranche.address); - expect(r[1][0]).to.eq(toFixedPtAmt("0")); - expect(r[1][1]).to.eq(toFixedPtAmt("500")); - }); - }); - }); - - describe("when redeeming all the tokens", function () { - it("should update the reserve composition", async function () { - await checkReserveComposition( - perp, - [collateralToken, initialDepositTranche], - [toFixedPtAmt("0"), toFixedPtAmt("500")], - ); - await perp.redeem(toFixedPtAmt("500")); - await checkReserveComposition(perp, [collateralToken], [toFixedPtAmt("0")]); - }); - }); - - describe("when reserve has more than one tranche", function () { - let newRedemptionTranche: Contract, tx: Transaction; - beforeEach(async function () { - await perp.updateTolerableTrancheMaturity(1200, 3600); - - await advancePerpQueue(perp, 1200); - - const newBond = await bondAt(await perp.callStatic.getDepositBond()); - await depositIntoBond(newBond, toFixedPtAmt("1000"), deployer); - const tranches = await getTranches(newBond); - newRedemptionTranche = tranches[0]; - - 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")); - - await checkReserveComposition( - perp, - [collateralToken, initialDepositTranche, newRedemptionTranche], - [toFixedPtAmt("0"), toFixedPtAmt("500"), toFixedPtAmt("500")], - ); - expect(await perp.totalSupply()).to.eq(toFixedPtAmt("750")); - expect(await perp.callStatic.getMatureTrancheBalance()).to.eq(toFixedPtAmt("0")); - expect(await collateralToken.balanceOf(await perp.reserve())).to.eq(toFixedPtAmt("0")); - expect(await perp.callStatic.getReserveTrancheBalance(initialDepositTranche.address)).to.eq( - toFixedPtAmt("500"), - ); - expect(await perp.callStatic.getReserveTrancheBalance(newRedemptionTranche.address)).to.eq(toFixedPtAmt("500")); - - tx = perp.redeem(toFixedPtAmt("375")); - await tx; - }); - - it("should update the reserve composition", async function () { - await checkReserveComposition( - perp, - [collateralToken, initialDepositTranche, newRedemptionTranche], - [toFixedPtAmt("0"), toFixedPtAmt("250"), toFixedPtAmt("250")], - ); - }); - it("should emit reserve synced", async function () { - await expect(tx) - .to.emit(perp, "ReserveSynced") - .withArgs(collateralToken.address, toFixedPtAmt("0")) - .to.emit(perp, "ReserveSynced") - .withArgs(initialDepositTranche.address, toFixedPtAmt("250")) - .to.emit(perp, "ReserveSynced") - .withArgs(newRedemptionTranche.address, toFixedPtAmt("250")); - }); - it("should transfer tokens out", async function () { - await expect(tx) - .to.emit(initialDepositTranche, "Transfer") - .withArgs(perp.address, deployerAddress, toFixedPtAmt("250")) - .to.emit(newRedemptionTranche, "Transfer") - .withArgs(perp.address, deployerAddress, toFixedPtAmt("250")); - }); - it("should emit tranche balance update", async function () { - await expect(tx).to.emit(perp, "UpdatedMatureTrancheBalance").withArgs(toFixedPtAmt("0")); - }); - it("should update the total supply", async function () { - expect(await perp.totalSupply()).to.eq(toFixedPtAmt("375")); - }); - it("should NOT update matureTrancheBalance", async function () { - expect(await perp.callStatic.getMatureTrancheBalance()).to.eq(toFixedPtAmt("0")); - }); - }); - - describe("when reserve has mature collateral and tranches", function () { - let newRedemptionTranche: Contract, tx: Transaction; - beforeEach(async function () { - await perp.updateTolerableTrancheMaturity(1200, 3600); - - await advancePerpQueue(perp, 1200); - - const newBond = await bondAt(await perp.callStatic.getDepositBond()); - await depositIntoBond(newBond, toFixedPtAmt("1000"), deployer); - const tranches = await getTranches(newBond); - newRedemptionTranche = tranches[0]; - - 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")); - - await checkReserveComposition( - perp, - [collateralToken, initialDepositTranche, newRedemptionTranche], - [toFixedPtAmt("0"), toFixedPtAmt("500"), toFixedPtAmt("500")], - ); - expect(await perp.totalSupply()).to.eq(toFixedPtAmt("750")); - expect(await perp.callStatic.getMatureTrancheBalance()).to.eq(toFixedPtAmt("0")); - - await advancePerpQueue(perp, 2400); - - tx = perp.redeem(toFixedPtAmt("375")); - await tx; - }); - - it("should update the reserve composition", async function () { - await checkReserveComposition( - perp, - [collateralToken, newRedemptionTranche], - [toFixedPtAmt("250"), toFixedPtAmt("250")], - ); - }); - it("should emit reserve synced", async function () { - await expect(tx) - .to.emit(perp, "ReserveSynced") - .withArgs(collateralToken.address, toFixedPtAmt("250")) - .to.emit(perp, "ReserveSynced") - .withArgs(newRedemptionTranche.address, toFixedPtAmt("250")); - }); - it("should transfer tokens out", async function () { - await expect(tx) - .to.emit(collateralToken, "Transfer") - .withArgs(perp.address, deployerAddress, toFixedPtAmt("250")) - .to.emit(newRedemptionTranche, "Transfer") - .withArgs(perp.address, deployerAddress, toFixedPtAmt("250")); - }); - it("should emit tranche balance update", async function () { - await expect(tx).to.emit(perp, "UpdatedMatureTrancheBalance").withArgs(toFixedPtAmt("250")); - }); - it("should update the total supply", async function () { - expect(await perp.totalSupply()).to.eq(toFixedPtAmt("375")); - }); - it("should NOT update matureTrancheBalance", async function () { - expect(await perp.callStatic.getMatureTrancheBalance()).to.eq(toFixedPtAmt("250")); - }); - }); - - describe("when the collateralToken balance is over the tranche balance", function () { - let newRedemptionTranche: Contract, tx: Transaction; - beforeEach(async function () { - await perp.updateTolerableTrancheMaturity(1200, 3600); - - await advancePerpQueue(perp, 1200); - - const newBond = await bondAt(await perp.callStatic.getDepositBond()); - await depositIntoBond(newBond, toFixedPtAmt("1000"), deployer); - const tranches = await getTranches(newBond); - newRedemptionTranche = tranches[0]; - - 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")); - - await checkReserveComposition( - perp, - [collateralToken, initialDepositTranche, newRedemptionTranche], - [toFixedPtAmt("0"), toFixedPtAmt("500"), toFixedPtAmt("500")], - ); - - expect(await perp.totalSupply()).to.eq(toFixedPtAmt("750")); - expect(await perp.callStatic.getMatureTrancheBalance()).to.eq(toFixedPtAmt("0")); - expect(await collateralToken.balanceOf(await perp.reserve())).to.eq(toFixedPtAmt("0")); - expect(await perp.callStatic.getReserveTrancheBalance(initialDepositTranche.address)).to.eq( - toFixedPtAmt("500"), - ); - expect(await perp.callStatic.getReserveTrancheBalance(newRedemptionTranche.address)).to.eq(toFixedPtAmt("500")); - - await advancePerpQueue(perp, 2400); - await rebase(collateralToken, rebaseOracle, +0.5); - - tx = perp.redeem(toFixedPtAmt("375")); - await tx; - }); - - it("should update the reserve composition", async function () { - await checkReserveComposition( - perp, - [collateralToken, newRedemptionTranche], - [toFixedPtAmt("375"), toFixedPtAmt("250")], - ); - }); - it("should emit reserve synced", async function () { - await expect(tx) - .to.emit(perp, "ReserveSynced") - .withArgs(collateralToken.address, toFixedPtAmt("250")) - .to.emit(perp, "ReserveSynced") - .withArgs(newRedemptionTranche.address, toFixedPtAmt("250")); - }); - it("should transfer tokens out", async function () { - await expect(tx) - .to.emit(collateralToken, "Transfer") - .withArgs(perp.address, deployerAddress, toFixedPtAmt("250")) - .to.emit(newRedemptionTranche, "Transfer") - .withArgs(perp.address, deployerAddress, toFixedPtAmt("250")); - }); - it("should emit tranche balance update", async function () { - await expect(tx).to.emit(perp, "UpdatedMatureTrancheBalance").withArgs(toFixedPtAmt("250")); - }); - it("should update the total supply", async function () { - expect(await perp.totalSupply()).to.eq(toFixedPtAmt("375")); - }); - it("should NOT update matureTrancheBalance", async function () { - expect(await perp.callStatic.getMatureTrancheBalance()).to.eq(toFixedPtAmt("250")); - }); - }); - - describe("when the collateralToken balance is over the tranche balance", function () { - let newRedemptionTranche: Contract, tx: Transaction; - beforeEach(async function () { - await perp.updateTolerableTrancheMaturity(1200, 3600); - - await advancePerpQueue(perp, 1200); - - const newBond = await bondAt(await perp.callStatic.getDepositBond()); - await depositIntoBond(newBond, toFixedPtAmt("1000"), deployer); - const tranches = await getTranches(newBond); - newRedemptionTranche = tranches[0]; - - 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")); - - await checkReserveComposition( - perp, - [collateralToken, initialDepositTranche, newRedemptionTranche], - [toFixedPtAmt("0"), toFixedPtAmt("500"), toFixedPtAmt("500")], - ); - expect(await perp.totalSupply()).to.eq(toFixedPtAmt("750")); - expect(await perp.callStatic.getMatureTrancheBalance()).to.eq(toFixedPtAmt("0")); - - await advancePerpQueue(perp, 2400); - await rebase(collateralToken, rebaseOracle, -0.5); - - tx = perp.redeem(toFixedPtAmt("375")); - await tx; - }); - - it("should update the reserve composition", async function () { - await checkReserveComposition( - perp, - [collateralToken, newRedemptionTranche], - [toFixedPtAmt("125"), toFixedPtAmt("250")], - ); - }); - it("should emit reserve synced", async function () { - await expect(tx) - .to.emit(perp, "ReserveSynced") - .withArgs(collateralToken.address, toFixedPtAmt("250")) - .to.emit(perp, "ReserveSynced") - .withArgs(newRedemptionTranche.address, toFixedPtAmt("250")); - }); - it("should transfer tokens out", async function () { - await expect(tx) - .to.emit(collateralToken, "Transfer") - .withArgs(perp.address, deployerAddress, toFixedPtAmt("250")) - .to.emit(newRedemptionTranche, "Transfer") - .withArgs(perp.address, deployerAddress, toFixedPtAmt("250")); - }); - it("should emit tranche balance update", async function () { - await expect(tx).to.emit(perp, "UpdatedMatureTrancheBalance").withArgs(toFixedPtAmt("250")); - }); - it("should update the total supply", async function () { - expect(await perp.totalSupply()).to.eq(toFixedPtAmt("375")); - }); - it("should NOT update matureTrancheBalance", async function () { - expect(await perp.callStatic.getMatureTrancheBalance()).to.eq(toFixedPtAmt("250")); - }); - }); - - describe("when reserve has only mature collateral", function () { - let newRedemptionTranche: Contract, tx: Transaction; - beforeEach(async function () { - await perp.updateTolerableTrancheMaturity(1200, 3600); - - await advancePerpQueue(perp, 1200); - - const newBond = await bondAt(await perp.callStatic.getDepositBond()); - await depositIntoBond(newBond, toFixedPtAmt("1000"), deployer); - const tranches = await getTranches(newBond); - newRedemptionTranche = tranches[0]; - - 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")); - - await checkReserveComposition(perp, [collateralToken, initialDepositTranche, newRedemptionTranche]); - await checkReserveComposition( - perp, - [collateralToken, initialDepositTranche, newRedemptionTranche], - [toFixedPtAmt("0"), toFixedPtAmt("500"), toFixedPtAmt("500")], - ); - - expect(await perp.totalSupply()).to.eq(toFixedPtAmt("750")); - expect(await perp.callStatic.getMatureTrancheBalance()).to.eq(toFixedPtAmt("0")); - - await advancePerpQueue(perp, 7200); - - tx = perp.redeem(toFixedPtAmt("375")); - await tx; - }); - - it("should update the reserve composition", async function () { - await checkReserveComposition(perp, [collateralToken]); - await checkReserveComposition(perp, [collateralToken], [toFixedPtAmt("500")]); - }); - it("should emit reserve synced", async function () { - await expect(tx).to.emit(perp, "ReserveSynced").withArgs(collateralToken.address, toFixedPtAmt("500")); - }); - it("should transfer tokens out", async function () { - await expect(tx) - .to.emit(collateralToken, "Transfer") - .withArgs(perp.address, deployerAddress, toFixedPtAmt("500")); - }); - it("should emit tranche balance update", async function () { - await expect(tx).to.emit(perp, "UpdatedMatureTrancheBalance").withArgs(toFixedPtAmt("375")); - }); - it("should update the total supply", async function () { - expect(await perp.totalSupply()).to.eq(toFixedPtAmt("375")); - }); - it("should NOT update matureTrancheBalance", async function () { - expect(await perp.callStatic.getMatureTrancheBalance()).to.eq(toFixedPtAmt("375")); - }); - }); - - describe("when redeeming entire supply", function () { - let newRedemptionTranche1: Contract, newRedemptionTranche2: Contract, tx: Transaction; - beforeEach(async function () { - await perp.updateTolerableTrancheMaturity(1200, 3600); - - await advancePerpQueue(perp, 1200); - - const newBond = await bondAt(await perp.callStatic.getDepositBond()); - await depositIntoBond(newBond, toFixedPtAmt("1000"), deployer); - const tranches = await getTranches(newBond); - newRedemptionTranche1 = tranches[0]; - newRedemptionTranche2 = tranches[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")); - await newRedemptionTranche2.approve(perp.address, toFixedPtAmt("500")); - await perp.deposit(newRedemptionTranche2.address, toFixedPtAmt("500")); - - await checkReserveComposition( - perp, - [collateralToken, initialDepositTranche, newRedemptionTranche1, newRedemptionTranche2], - [toFixedPtAmt("0"), toFixedPtAmt("500"), toFixedPtAmt("500"), toFixedPtAmt("500")], - ); - expect(await perp.totalSupply()).to.eq(toFixedPtAmt("1500")); - expect(await perp.callStatic.getMatureTrancheBalance()).to.eq(toFixedPtAmt("0")); - tx = perp.redeem(await perp.balanceOf(deployerAddress)); - await tx; - }); - - it("should update the reserve composition", async function () { - await checkReserveComposition(perp, [collateralToken], [toFixedPtAmt("0")]); - }); - it("should emit reserve synced", async function () { - await expect(tx) - .to.emit(perp, "ReserveSynced") - .withArgs(collateralToken.address, toFixedPtAmt("0")) - .to.emit(perp, "ReserveSynced") - .withArgs(initialDepositTranche.address, toFixedPtAmt("0")) - .to.emit(perp, "ReserveSynced") - .withArgs(newRedemptionTranche1.address, toFixedPtAmt("0")) - .to.emit(perp, "ReserveSynced") - .withArgs(newRedemptionTranche2.address, toFixedPtAmt("0")); - }); - it("should transfer tokens out", async function () { - await expect(tx) - .to.emit(initialDepositTranche, "Transfer") - .withArgs(perp.address, deployerAddress, toFixedPtAmt("500")) - .to.emit(newRedemptionTranche1, "Transfer") - .withArgs(perp.address, deployerAddress, toFixedPtAmt("500")) - .to.emit(newRedemptionTranche2, "Transfer") - .withArgs(perp.address, deployerAddress, toFixedPtAmt("500")); - }); - it("should emit tranche balance update", async function () { - await expect(tx).to.emit(perp, "UpdatedMatureTrancheBalance").withArgs(toFixedPtAmt("0")); - }); - it("should update the total supply", async function () { - expect(await perp.totalSupply()).to.eq(toFixedPtAmt("0")); - }); - it("should NOT update matureTrancheBalance", async function () { - expect(await perp.callStatic.getMatureTrancheBalance()).to.eq(toFixedPtAmt("0")); - }); - it("should update the balance", async function () { - expect(await perp.balanceOf(deployerAddress)).to.eq(toFixedPtAmt("0")); - }); - }); - }); -}); diff --git a/spot-contracts/test/PerpetualTranche_rollover.ts b/spot-contracts/test/PerpetualTranche_rollover.ts deleted file mode 100644 index 131be43d..00000000 --- a/spot-contracts/test/PerpetualTranche_rollover.ts +++ /dev/null @@ -1,1588 +0,0 @@ -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, - createBondWithFactory, - depositIntoBond, - bondAt, - getTranches, - toFixedPtAmt, - toDiscountFixedPtAmt, - toPriceFixedPtAmt, - advancePerpQueue, - advancePerpQueueToBondMaturity, - checkReserveComposition, - rebase, -} from "./helpers"; -use(smock.matchers); - -let perp: Contract, - bondFactory: Contract, - collateralToken: Contract, - rebaseOracle: Contract, - issuer: Contract, - feeStrategy: Contract, - pricingStrategy: Contract, - discountStrategy: Contract, - deployer: Signer, - otherUser: Signer, - deployerAddress: string, - holdingPenBond: Contract, - holdingPenTranche1: Contract, - reserveBond: Contract, - reserveTranche1: Contract, - reserveTranche2: Contract, - rolloverInBond: Contract, - rolloverInTranche: Contract; - -describe("PerpetualTranche", function () { - beforeEach(async function () { - await network.provider.send("hardhat_reset"); - - const accounts = await ethers.getSigners(); - deployer = accounts[0]; - otherUser = accounts[1]; - deployerAddress = await deployer.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(10800, [500, 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, 10800); - await advancePerpQueue(perp, 10900); - - holdingPenBond = await bondAt(await perp.callStatic.getDepositBond()); - [holdingPenTranche1] = await getTranches(holdingPenBond); - - await depositIntoBond(holdingPenBond, toFixedPtAmt("2000"), deployer); - - 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")); - - await advancePerpQueue(perp, 1200); - - reserveBond = await bondAt(await perp.callStatic.getDepositBond()); - [reserveTranche1, reserveTranche2] = await getTranches(reserveBond); - - await depositIntoBond(reserveBond, toFixedPtAmt("2000"), deployer); - - 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.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")); - - await advancePerpQueueToBondMaturity(perp, holdingPenBond); - - rolloverInBond = await bondAt(await perp.callStatic.getDepositBond()); - [rolloverInTranche] = await getTranches(rolloverInBond); - - 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")); - await perp.deposit(rolloverInTranche.address, toFixedPtAmt("500")); - - await checkReserveComposition( - perp, - [collateralToken, reserveTranche2, reserveTranche1, rolloverInTranche], - [toFixedPtAmt("500"), toFixedPtAmt("1000"), toFixedPtAmt("500"), toFixedPtAmt("500")], - ); - }); - - afterEach(async function () { - await network.provider.send("hardhat_reset"); - }); - - describe("#rollover", function () { - describe("when paused", function () { - beforeEach(async function () { - await perp.updateKeeper(deployerAddress); - await perp.pause(); - }); - - it("should revert", async function () { - await expect( - perp.rollover(rolloverInTranche.address, reserveTranche2.address, toFixedPtAmt("500")), - ).to.revertedWith("Pausable: paused"); - }); - }); - - describe("when rollers are authorized", function () { - beforeEach(async function () { - await perp.authorizeRoller(await otherUser.getAddress(), true); - }); - - it("should revert when invoked from unauthorized roller ", async function () { - await expect( - perp.rollover(rolloverInTranche.address, reserveTranche2.address, toFixedPtAmt("500")), - ).to.revertedWithCustomError(perp, "UnauthorizedCall"); - }); - - it("should NOT revert when invoked from authorized roller ", async function () { - await rolloverInTranche.transfer(await otherUser.getAddress(), toFixedPtAmt("500")); - await rolloverInTranche.connect(otherUser).approve(perp.address, toFixedPtAmt("500")); - await expect( - perp.connect(otherUser).rollover(rolloverInTranche.address, reserveTranche2.address, toFixedPtAmt("500")), - ).not.to.be.reverted; - }); - }); - - describe("when trancheIn and tokenOut belong to the same bond", function () { - let tranches: Contract[]; - beforeEach(async function () { - tranches = await getTranches(rolloverInBond); - 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.revertedWithCustomError(perp, "UnacceptableRollover"); - }); - }); - - describe("when trancheIn is NOT of deposit bond", function () { - it("should revert", async function () { - await expect( - perp.rollover(reserveTranche1.address, collateralToken.address, toFixedPtAmt("500")), - ).to.revertedWithCustomError(perp, "UnacceptableRollover"); - await expect( - perp.rollover(reserveTranche1.address, reserveTranche1.address, toFixedPtAmt("500")), - ).to.revertedWithCustomError(perp, "UnacceptableRollover"); - await expect( - perp.rollover(reserveTranche1.address, reserveTranche2.address, toFixedPtAmt("500")), - ).to.revertedWithCustomError(perp, "UnacceptableRollover"); - await expect( - perp.rollover(reserveTranche1.address, holdingPenTranche1.address, toFixedPtAmt("500")), - ).to.revertedWithCustomError(perp, "UnacceptableRollover"); - }); - }); - - describe("when tokenOut is NOT in the reserve", function () { - let maliciousTranche: Contract; - beforeEach(async function () { - const bond = await createBondWithFactory(bondFactory, collateralToken, [1, 999], 86400); - maliciousTranche = (await getTranches(bond))[0]; - }); - it("should revert", async function () { - await expect( - perp.rollover(rolloverInTranche.address, maliciousTranche.address, toFixedPtAmt("500")), - ).to.revertedWithCustomError(perp, "UnacceptableRollover"); - }); - }); - - describe("when tokenOut is still isAcceptableForReserve", function () { - let newRotationInTranche: Contract; - beforeEach(async function () { - await advancePerpQueue(perp, 1200); - const newRotationInBond = await bondAt(await perp.callStatic.getDepositBond()); - [newRotationInTranche] = await getTranches(newRotationInBond); - }); - it("should revert", async function () { - await expect( - perp.rollover(newRotationInTranche.address, rolloverInTranche.address, toFixedPtAmt("500")), - ).to.revertedWithCustomError(perp, "UnacceptableRollover"); - }); - }); - - describe("when the malicious trancheIn which points to the deposit bond is rolled in", function () { - let maliciousTranche: Contract; - beforeEach(async function () { - const ERC20 = await ethers.getContractFactory("MockTranche"); - maliciousTranche = await ERC20.deploy(); - await maliciousTranche.init("Tranche", "TRA"); - await maliciousTranche.mint(deployerAddress, toFixedPtAmt("500")); - await maliciousTranche.setBond(await perp.callStatic.getDepositBond()); - await maliciousTranche.approve(perp.address, toFixedPtAmt("500")); - }); - it("should revert", async function () { - await expect( - perp.rollover(maliciousTranche.address, reserveTranche1.address, toFixedPtAmt("500")), - ).to.revertedWithCustomError(perp, "UnacceptableRollover"); - }); - }); - - describe("when user has insufficient tranche balance", function () { - beforeEach(async function () { - await rolloverInTranche.transfer("0x000000000000000000000000000000000000dead", toFixedPtAmt("1501")); - }); - - it("should revert", async function () { - expect(await rolloverInTranche.balanceOf(deployerAddress)).to.lt(toFixedPtAmt("500")); - await expect( - perp.rollover(rolloverInTranche.address, reserveTranche1.address, toFixedPtAmt("500")), - ).to.revertedWith("ERC20: transfer amount exceeds balance"); - }); - }); - - describe("when user has insufficient approval", function () { - beforeEach(async function () { - await rolloverInTranche.approve(perp.address, toFixedPtAmt("0")); - }); - - it("should revert", async function () { - expect(await rolloverInTranche.allowance(deployerAddress, perp.address)).to.lte(toFixedPtAmt("500")); - await expect( - perp.rollover(rolloverInTranche.address, reserveTranche1.address, toFixedPtAmt("500")), - ).to.revertedWith("ERC20: transfer amount exceeds allowance"); - }); - }); - - describe("when trancheInAmt is zero", function () { - it("should revert", async function () { - await expect( - perp.rollover(rolloverInTranche.address, reserveTranche1.address, toFixedPtAmt("0")), - ).to.revertedWithCustomError(perp, "UnacceptableRolloverAmt"); - }); - }); - - describe("when tokenIn discount is zero", function () { - let newRotationInTranche: Contract; - beforeEach(async function () { - const tranches = await getTranches(rolloverInBond); - newRotationInTranche = tranches[1]; - }); - - it("should be reverted", async function () { - await expect( - perp.rollover(newRotationInTranche.address, reserveTranche1.address, toFixedPtAmt("0")), - ).to.revertedWithCustomError(perp, "UnacceptableRolloverAmt"); - }); - }); - - describe("when the supply cap is exceeded", function () { - beforeEach(async function () { - 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.revertedWithCustomError(perp, "ExceededMaxSupply"); - }); - }); - - describe("when trancheIn discount is 0.5", function () { - let newRotationInTranche: Contract; - beforeEach(async function () { - const tranches = await getTranches(rolloverInBond); - newRotationInTranche = tranches[1]; - await discountStrategy.computeTrancheDiscount - .whenCalledWith(newRotationInTranche.address) - .returns(toDiscountFixedPtAmt("0.5")); - }); - - it("should rollover the correct amount", async function () { - const r = await perp.callStatic.computeRolloverAmt( - newRotationInTranche.address, - reserveTranche1.address, - toFixedPtAmt("500"), - constants.MaxUint256, - ); - expect(r.perpRolloverAmt).to.eq(toFixedPtAmt("250")); - expect(r.tokenOutAmt).to.eq(toFixedPtAmt("250")); - expect(r.trancheOutAmt).to.eq(toFixedPtAmt("250")); - expect(r.trancheInAmt).to.eq(toFixedPtAmt("500")); - expect(r.remainingTrancheInAmt).to.eq(toFixedPtAmt("0")); - }); - }); - - describe("when tokenOut discount is 0.5", function () { - it("should rollover the correct amount", async function () { - const r = await perp.callStatic.computeRolloverAmt( - rolloverInTranche.address, - reserveTranche2.address, - toFixedPtAmt("500"), - constants.MaxUint256, - ); - expect(r.perpRolloverAmt).to.eq(toFixedPtAmt("500")); - expect(r.tokenOutAmt).to.eq(toFixedPtAmt("1000")); - expect(r.trancheOutAmt).to.eq(toFixedPtAmt("1000")); - expect(r.trancheInAmt).to.eq(toFixedPtAmt("500")); - expect(r.remainingTrancheInAmt).to.eq(toFixedPtAmt("0")); - }); - }); - - describe("when trancheIn price is zero", function () { - beforeEach(async function () { - 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 () { - const r = await perp.callStatic.computeRolloverAmt( - rolloverInTranche.address, - reserveTranche1.address, - toFixedPtAmt("500"), - constants.MaxUint256, - ); - expect(r.perpRolloverAmt).to.eq(toFixedPtAmt("0")); - expect(r.tokenOutAmt).to.eq(toFixedPtAmt("0")); - expect(r.trancheOutAmt).to.eq(toFixedPtAmt("0")); - expect(r.trancheInAmt).to.eq(toFixedPtAmt("0")); - expect(r.remainingTrancheInAmt).to.eq(toFixedPtAmt("500")); - }); - }); - - describe("when tokenOut price is zero", function () { - beforeEach(async function () { - 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 () { - const r = await perp.callStatic.computeRolloverAmt( - rolloverInTranche.address, - reserveTranche1.address, - toFixedPtAmt("500"), - constants.MaxUint256, - ); - expect(r.perpRolloverAmt).to.eq(toFixedPtAmt("0")); - expect(r.tokenOutAmt).to.eq(toFixedPtAmt("0")); - expect(r.trancheOutAmt).to.eq(toFixedPtAmt("0")); - expect(r.trancheInAmt).to.eq(toFixedPtAmt("0")); - expect(r.remainingTrancheInAmt).to.eq(toFixedPtAmt("500")); - }); - }); - - describe("when trancheIn price is 0.5", function () { - beforeEach(async function () { - 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 () { - const r = await perp.callStatic.computeRolloverAmt( - rolloverInTranche.address, - reserveTranche1.address, - toFixedPtAmt("500"), - constants.MaxUint256, - ); - // 250 / 1750 * 2000 - expect(r.perpRolloverAmt).to.eq(toFixedPtAmt("285.714285714285714285")); - expect(r.tokenOutAmt).to.eq(toFixedPtAmt("250")); - expect(r.trancheOutAmt).to.eq(toFixedPtAmt("250")); - expect(r.trancheInAmt).to.eq(toFixedPtAmt("500")); - expect(r.remainingTrancheInAmt).to.eq(toFixedPtAmt("0")); - }); - }); - - describe("tokenOut price is 0.5", function () { - beforeEach(async function () { - 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 () { - const r = await perp.callStatic.computeRolloverAmt( - rolloverInTranche.address, - reserveTranche1.address, - toFixedPtAmt("500"), - constants.MaxUint256, - ); - // 250 / 1750 * 2000 - expect(r.perpRolloverAmt).to.eq(toFixedPtAmt("285.714285714285714285")); - expect(r.tokenOutAmt).to.eq(toFixedPtAmt("500")); - expect(r.trancheOutAmt).to.eq(toFixedPtAmt("500")); - expect(r.trancheInAmt).to.eq(toFixedPtAmt("250")); - expect(r.remainingTrancheInAmt).to.eq(toFixedPtAmt("250")); - }); - }); - - describe("tokenOut is collateral which rebased up", function () { - beforeEach(async function () { - await pricingStrategy.computeTranchePrice - .whenCalledWith(rolloverInTranche.address) - .returns(toPriceFixedPtAmt("1")); - await pricingStrategy.computeMatureTranchePrice.returns(toPriceFixedPtAmt("2")); - await rebase(collateralToken, rebaseOracle, +1); - }); - - it("should rollover the correct amount", async function () { - const r = await perp.callStatic.computeRolloverAmt( - rolloverInTranche.address, - collateralToken.address, - toFixedPtAmt("500"), - constants.MaxUint256, - ); - // 500 / 2500 * 2000 - expect(r.perpRolloverAmt).to.eq(toFixedPtAmt("400")); - expect(r.tokenOutAmt).to.eq(toFixedPtAmt("500")); - expect(r.trancheOutAmt).to.eq(toFixedPtAmt("250")); - expect(r.trancheInAmt).to.eq(toFixedPtAmt("500")); - expect(r.remainingTrancheInAmt).to.eq(toFixedPtAmt("0")); - }); - }); - - describe("tokenOut is collateral which rebased down", function () { - beforeEach(async function () { - await pricingStrategy.computeTranchePrice - .whenCalledWith(rolloverInTranche.address) - .returns(toPriceFixedPtAmt("1")); - await pricingStrategy.computeMatureTranchePrice.returns(toPriceFixedPtAmt("0.5")); - await rebase(collateralToken, rebaseOracle, -0.5); - }); - - it("should rollover the correct amount", async function () { - const r = await perp.callStatic.computeRolloverAmt( - rolloverInTranche.address, - collateralToken.address, - toFixedPtAmt("500"), - constants.MaxUint256, - ); - // 250 / 1750 * 2000 - expect(r.perpRolloverAmt).to.eq(toFixedPtAmt("285.714285714285714285")); - expect(r.tokenOutAmt).to.eq(toFixedPtAmt("250")); - expect(r.trancheOutAmt).to.eq(toFixedPtAmt("500")); - expect(r.trancheInAmt).to.eq(toFixedPtAmt("250")); - expect(r.remainingTrancheInAmt).to.eq(toFixedPtAmt("250")); - }); - }); - - describe("tokenIn discount is 0.5 and tokenOut is collateral which rebased up", function () { - let rolloverInTranche2: Contract; - beforeEach(async function () { - const rolloverInTranches = await getTranches(rolloverInBond); - rolloverInTranche2 = rolloverInTranches[1]; - 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); - }); - - it("should rollover the correct amount", async function () { - const r = await perp.callStatic.computeRolloverAmt( - rolloverInTranche2.address, - collateralToken.address, - toFixedPtAmt("500"), - constants.MaxUint256, - ); - // 250 / 2500 * 2000 - expect(r.perpRolloverAmt).to.eq(toFixedPtAmt("200")); - expect(r.tokenOutAmt).to.eq(toFixedPtAmt("250")); - expect(r.trancheOutAmt).to.eq(toFixedPtAmt("125")); - expect(r.trancheInAmt).to.eq(toFixedPtAmt("500")); - expect(r.remainingTrancheInAmt).to.eq(toFixedPtAmt("0")); - }); - }); - - describe("tokenIn discount is 0.5 and tokenOut is collateral which rebased down", function () { - let rolloverInTranche2: Contract; - beforeEach(async function () { - const rolloverInTranches = await getTranches(rolloverInBond); - rolloverInTranche2 = rolloverInTranches[1]; - 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); - }); - - it("should rollover the correct amount", async function () { - const r = await perp.callStatic.computeRolloverAmt( - rolloverInTranche2.address, - collateralToken.address, - toFixedPtAmt("500"), - constants.MaxUint256, - ); - // 250 / 1750 * 2000 - expect(r.perpRolloverAmt).to.eq(toFixedPtAmt("285.714285714285714285")); - expect(r.tokenOutAmt).to.eq(toFixedPtAmt("250")); - expect(r.trancheOutAmt).to.eq(toFixedPtAmt("500")); - expect(r.trancheInAmt).to.eq(toFixedPtAmt("500")); - expect(r.remainingTrancheInAmt).to.eq(toFixedPtAmt("0")); - }); - }); - - describe("when tokenOut is tranche and not covered", function () { - it("should rollover the correct amount", async function () { - const r = await perp.callStatic.computeRolloverAmt( - rolloverInTranche.address, - reserveTranche1.address, - toFixedPtAmt("500"), - toFixedPtAmt("250"), - ); - expect(r.perpRolloverAmt).to.eq(toFixedPtAmt("250")); - expect(r.tokenOutAmt).to.eq(toFixedPtAmt("250")); - expect(r.trancheOutAmt).to.eq(toFixedPtAmt("250")); - expect(r.trancheInAmt).to.eq(toFixedPtAmt("250")); - expect(r.remainingTrancheInAmt).to.eq(toFixedPtAmt("250")); - }); - }); - - describe("when tokenOut is collateral and not covered", function () { - it("should rollover the correct amount", async function () { - const r = await perp.callStatic.computeRolloverAmt( - rolloverInTranche.address, - collateralToken.address, - toFixedPtAmt("500"), - toFixedPtAmt("250"), - ); - expect(r.perpRolloverAmt).to.eq(toFixedPtAmt("250")); - expect(r.tokenOutAmt).to.eq(toFixedPtAmt("250")); - expect(r.trancheOutAmt).to.eq(toFixedPtAmt("250")); - expect(r.trancheInAmt).to.eq(toFixedPtAmt("250")); - expect(r.remainingTrancheInAmt).to.eq(toFixedPtAmt("250")); - }); - }); - - describe("when tokenOut is collateral has rebased down", function () { - beforeEach(async function () { - await rebase(collateralToken, rebaseOracle, -0.25); - }); - it("should rollover the correct amount", async function () { - const r = await perp.callStatic.computeRolloverAmt( - rolloverInTranche.address, - collateralToken.address, - toFixedPtAmt("500"), - constants.MaxUint256, - ); - expect(r.perpRolloverAmt).to.eq(toFixedPtAmt("500")); - expect(r.tokenOutAmt).to.eq(toFixedPtAmt("375")); - expect(r.trancheOutAmt).to.eq(toFixedPtAmt("500")); - expect(r.trancheInAmt).to.eq(toFixedPtAmt("500")); - expect(r.remainingTrancheInAmt).to.eq(toFixedPtAmt("0")); - }); - }); - - describe("when tokenOut is collateral has rebased up", function () { - beforeEach(async function () { - await rebase(collateralToken, rebaseOracle, 0.25); - }); - it("should rollover the correct amount", async function () { - const r = await perp.callStatic.computeRolloverAmt( - rolloverInTranche.address, - collateralToken.address, - toFixedPtAmt("500"), - constants.MaxUint256, - ); - expect(r.perpRolloverAmt).to.eq(toFixedPtAmt("500")); - expect(r.tokenOutAmt).to.eq(toFixedPtAmt("625")); - expect(r.trancheOutAmt).to.eq(toFixedPtAmt("500")); - expect(r.trancheInAmt).to.eq(toFixedPtAmt("500")); - expect(r.remainingTrancheInAmt).to.eq(toFixedPtAmt("0")); - }); - }); - - describe("when tokenIn has 0.5 discount, tokenOut is collateral has rebased down", function () { - let newRotationInTranche: Contract; - beforeEach(async function () { - const tranches = await getTranches(rolloverInBond); - newRotationInTranche = tranches[1]; - 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 () { - const r = await perp.callStatic.computeRolloverAmt( - newRotationInTranche.address, - collateralToken.address, - toFixedPtAmt("500"), - constants.MaxUint256, - ); - expect(r.perpRolloverAmt).to.eq(toFixedPtAmt("250")); - expect(r.tokenOutAmt).to.eq(toFixedPtAmt("187.5")); - expect(r.trancheOutAmt).to.eq(toFixedPtAmt("250")); - expect(r.trancheInAmt).to.eq(toFixedPtAmt("500")); - expect(r.remainingTrancheInAmt).to.eq(toFixedPtAmt("0")); - }); - }); - - describe("when tokenIn has 0.5 discount, tokenOut is collateral has rebased up", function () { - let newRotationInTranche: Contract; - beforeEach(async function () { - const tranches = await getTranches(rolloverInBond); - newRotationInTranche = tranches[1]; - 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 () { - const r = await perp.callStatic.computeRolloverAmt( - newRotationInTranche.address, - collateralToken.address, - toFixedPtAmt("500"), - constants.MaxUint256, - ); - expect(r.perpRolloverAmt).to.eq(toFixedPtAmt("250")); - expect(r.tokenOutAmt).to.eq(toFixedPtAmt("312.5")); - expect(r.trancheOutAmt).to.eq(toFixedPtAmt("250")); - expect(r.trancheInAmt).to.eq(toFixedPtAmt("500")); - expect(r.remainingTrancheInAmt).to.eq(toFixedPtAmt("0")); - }); - }); - - describe("when tokenIn has 0.5 discount, tokenOut is collateral has rebased down and NOT covered", function () { - let newRotationInTranche: Contract; - beforeEach(async function () { - const tranches = await getTranches(rolloverInBond); - newRotationInTranche = tranches[1]; - 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 () { - const r = await perp.callStatic.computeRolloverAmt( - newRotationInTranche.address, - collateralToken.address, - toFixedPtAmt("500"), - toFixedPtAmt("93.75"), - ); - expect(r.perpRolloverAmt).to.eq(toFixedPtAmt("125")); - expect(r.tokenOutAmt).to.eq(toFixedPtAmt("93.75")); - expect(r.trancheOutAmt).to.eq(toFixedPtAmt("125")); - expect(r.trancheInAmt).to.eq(toFixedPtAmt("250")); - expect(r.remainingTrancheInAmt).to.eq(toFixedPtAmt("250")); - }); - }); - - describe("when tokenIn has 0.5 discount, tokenOut is collateral has rebased up and NOT covered", function () { - let newRotationInTranche: Contract; - beforeEach(async function () { - const tranches = await getTranches(rolloverInBond); - newRotationInTranche = tranches[1]; - 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 () { - const r = await perp.callStatic.computeRolloverAmt( - newRotationInTranche.address, - collateralToken.address, - toFixedPtAmt("500"), - toFixedPtAmt("156.25"), - ); - expect(r.perpRolloverAmt).to.eq(toFixedPtAmt("125")); - expect(r.tokenOutAmt).to.eq(toFixedPtAmt("156.25")); - expect(r.trancheOutAmt).to.eq(toFixedPtAmt("125")); - expect(r.trancheInAmt).to.eq(toFixedPtAmt("250")); - expect(r.remainingTrancheInAmt).to.eq(toFixedPtAmt("250")); - }); - }); - - describe("when fee is in native token", function () { - describe("when fee is zero", function () { - it("should transfer the tranches in", async function () { - await expect(() => - perp.rollover(rolloverInTranche.address, reserveTranche1.address, toFixedPtAmt("500")), - ).to.changeTokenBalances(rolloverInTranche, [deployer, perp], [toFixedPtAmt("-500"), toFixedPtAmt("500")]); - }); - it("should transfer the tranches out", async function () { - await expect(() => - perp.rollover(rolloverInTranche.address, reserveTranche1.address, toFixedPtAmt("500")), - ).to.changeTokenBalances(reserveTranche1, [deployer, perp], [toFixedPtAmt("500"), toFixedPtAmt("-500")]); - }); - it("should charge fee", async function () { - await expect(() => - perp.rollover(rolloverInTranche.address, reserveTranche1.address, toFixedPtAmt("500")), - ).to.changeTokenBalance(perp, perp, toFixedPtAmt("0")); - }); - it("should calculate rollover amt", async function () { - const r = await perp.callStatic.computeRolloverAmt( - rolloverInTranche.address, - reserveTranche1.address, - toFixedPtAmt("500"), - constants.MaxUint256, - ); - expect(r.perpRolloverAmt).to.eq(toFixedPtAmt("500")); - expect(r.tokenOutAmt).to.eq(toFixedPtAmt("500")); - expect(r.trancheOutAmt).to.eq(toFixedPtAmt("500")); - expect(r.trancheInAmt).to.eq(toFixedPtAmt("500")); - expect(r.remainingTrancheInAmt).to.eq(toFixedPtAmt("0")); - }); - }); - - describe("when fee > 0", function () { - beforeEach(async function () { - await feeStrategy.computeRolloverFees.returns([toFixedPtAmt("1"), "0"]); - }); - it("should transfer the tranches in", async function () { - await expect(() => - perp.rollover(rolloverInTranche.address, reserveTranche1.address, toFixedPtAmt("500")), - ).to.changeTokenBalances(rolloverInTranche, [deployer, perp], [toFixedPtAmt("-500"), toFixedPtAmt("500")]); - }); - it("should transfer the tranches out", async function () { - await expect(() => - perp.rollover(rolloverInTranche.address, reserveTranche1.address, toFixedPtAmt("500")), - ).to.changeTokenBalances(reserveTranche1, [deployer, perp], [toFixedPtAmt("500"), toFixedPtAmt("-500")]); - }); - it("should charge fee", async function () { - await expect(() => - perp.rollover(rolloverInTranche.address, reserveTranche1.address, toFixedPtAmt("500")), - ).to.changeTokenBalance(perp, perp, toFixedPtAmt("1")); - }); - it("should calculate rollover amt", async function () { - const r = await perp.callStatic.computeRolloverAmt( - rolloverInTranche.address, - reserveTranche1.address, - toFixedPtAmt("500"), - constants.MaxUint256, - ); - expect(r.perpRolloverAmt).to.eq(toFixedPtAmt("500")); - expect(r.tokenOutAmt).to.eq(toFixedPtAmt("500")); - expect(r.trancheOutAmt).to.eq(toFixedPtAmt("500")); - expect(r.trancheInAmt).to.eq(toFixedPtAmt("500")); - expect(r.remainingTrancheInAmt).to.eq(toFixedPtAmt("0")); - }); - }); - - describe("when fee < 0", function () { - beforeEach(async function () { - await perp.transfer(perp.address, toFixedPtAmt("1")); - await feeStrategy.computeRolloverFees.returns([toFixedPtAmt("-1"), "0"]); - }); - it("should transfer the tranches in", async function () { - await expect(() => - perp.rollover(rolloverInTranche.address, reserveTranche1.address, toFixedPtAmt("500")), - ).to.changeTokenBalances(rolloverInTranche, [deployer, perp], [toFixedPtAmt("-500"), toFixedPtAmt("500")]); - }); - it("should transfer the tranches out", async function () { - await expect(() => - perp.rollover(rolloverInTranche.address, reserveTranche1.address, toFixedPtAmt("500")), - ).to.changeTokenBalances(reserveTranche1, [deployer, perp], [toFixedPtAmt("500"), toFixedPtAmt("-500")]); - }); - it("should charge fee", async function () { - await expect(() => - perp.rollover(rolloverInTranche.address, reserveTranche1.address, toFixedPtAmt("500")), - ).to.changeTokenBalance(perp, perp, toFixedPtAmt("-1")); - }); - it("should calculate rollover amt", async function () { - const r = await perp.callStatic.computeRolloverAmt( - rolloverInTranche.address, - reserveTranche1.address, - toFixedPtAmt("500"), - constants.MaxUint256, - ); - expect(r.perpRolloverAmt).to.eq(toFixedPtAmt("500")); - expect(r.tokenOutAmt).to.eq(toFixedPtAmt("500")); - expect(r.trancheOutAmt).to.eq(toFixedPtAmt("500")); - expect(r.trancheInAmt).to.eq(toFixedPtAmt("500")); - expect(r.remainingTrancheInAmt).to.eq(toFixedPtAmt("0")); - }); - }); - - describe("when fee < 0 and abs(fee) < balance", function () { - beforeEach(async function () { - await perp.transfer(perp.address, toFixedPtAmt("0.5")); - await feeStrategy.computeRolloverFees.returns([toFixedPtAmt("-1"), "0"]); - }); - it("should transfer the tranches in", async function () { - await expect(() => - perp.rollover(rolloverInTranche.address, reserveTranche1.address, toFixedPtAmt("500")), - ).to.changeTokenBalances(rolloverInTranche, [deployer, perp], [toFixedPtAmt("-500"), toFixedPtAmt("500")]); - }); - it("should transfer the tranches out", async function () { - await expect(() => - perp.rollover(rolloverInTranche.address, reserveTranche1.address, toFixedPtAmt("500")), - ).to.changeTokenBalances(reserveTranche1, [deployer, perp], [toFixedPtAmt("500"), toFixedPtAmt("-500")]); - }); - it("should charge fee", async function () { - await expect(() => - perp.rollover(rolloverInTranche.address, reserveTranche1.address, toFixedPtAmt("500")), - ).to.changeTokenBalance(perp, perp, toFixedPtAmt("-0.5")); - }); - it("should mint the delta", async function () { - await expect(perp.rollover(rolloverInTranche.address, reserveTranche1.address, toFixedPtAmt("500"))) - .to.emit(perp, "Transfer") - .withArgs(constants.AddressZero, deployerAddress, toFixedPtAmt("0.5")); - }); - it("should calculate rollover amt", async function () { - const r = await perp.callStatic.computeRolloverAmt( - rolloverInTranche.address, - reserveTranche1.address, - toFixedPtAmt("500"), - constants.MaxUint256, - ); - expect(r.perpRolloverAmt).to.eq(toFixedPtAmt("500")); - expect(r.tokenOutAmt).to.eq(toFixedPtAmt("500")); - expect(r.trancheOutAmt).to.eq(toFixedPtAmt("500")); - expect(r.trancheInAmt).to.eq(toFixedPtAmt("500")); - expect(r.remainingTrancheInAmt).to.eq(toFixedPtAmt("0")); - }); - }); - - describe("when protocol fee > 0", function () { - beforeEach(async function () { - await perp.transferOwnership(await otherUser.getAddress()); - await feeStrategy.computeRolloverFees.returns([toFixedPtAmt("1"), toFixedPtAmt("0.5")]); - }); - it("should transfer the tranches in", async function () { - await expect(() => - perp.rollover(rolloverInTranche.address, reserveTranche1.address, toFixedPtAmt("500")), - ).to.changeTokenBalances(rolloverInTranche, [deployer, perp], [toFixedPtAmt("-500"), toFixedPtAmt("500")]); - }); - it("should transfer the tranches out", async function () { - await expect(() => - perp.rollover(rolloverInTranche.address, reserveTranche1.address, toFixedPtAmt("500")), - ).to.changeTokenBalances(reserveTranche1, [deployer, perp], [toFixedPtAmt("500"), toFixedPtAmt("-500")]); - }); - it("should charge fees", async function () { - await expect(() => - perp.rollover(rolloverInTranche.address, reserveTranche1.address, toFixedPtAmt("500")), - ).to.changeTokenBalances( - perp, - [deployer, perp, otherUser], - [toFixedPtAmt("-1.5"), toFixedPtAmt("1"), toFixedPtAmt("0.5")], - ); - }); - it("should calculate rollover amt", async function () { - const r = await perp.callStatic.computeRolloverAmt( - rolloverInTranche.address, - reserveTranche1.address, - toFixedPtAmt("500"), - constants.MaxUint256, - ); - expect(r.perpRolloverAmt).to.eq(toFixedPtAmt("500")); - expect(r.tokenOutAmt).to.eq(toFixedPtAmt("500")); - expect(r.trancheOutAmt).to.eq(toFixedPtAmt("500")); - expect(r.trancheInAmt).to.eq(toFixedPtAmt("500")); - expect(r.remainingTrancheInAmt).to.eq(toFixedPtAmt("0")); - }); - }); - }); - - describe("when fee is in non-native token", function () { - let feeToken: Contract; - beforeEach(async function () { - const ERC20 = await ethers.getContractFactory("MockERC20"); - feeToken = await ERC20.deploy(); - await feeToken.init("Mock token", "MOCK"); - await feeStrategy.feeToken.returns(feeToken.address); - }); - - describe("when fee is zero", async function () { - it("should transfer the tranches in", async function () { - await expect(() => - perp.rollover(rolloverInTranche.address, reserveTranche1.address, toFixedPtAmt("500")), - ).to.changeTokenBalances(rolloverInTranche, [deployer, perp], [toFixedPtAmt("-500"), toFixedPtAmt("500")]); - }); - it("should transfer the tranches out", async function () { - await expect(() => - perp.rollover(rolloverInTranche.address, reserveTranche1.address, toFixedPtAmt("500")), - ).to.changeTokenBalances(reserveTranche1, [deployer, perp], [toFixedPtAmt("500"), toFixedPtAmt("-500")]); - }); - it("should charge fee", async function () { - await expect(() => - perp.rollover(rolloverInTranche.address, reserveTranche1.address, toFixedPtAmt("500")), - ).to.changeTokenBalance(feeToken, perp, toFixedPtAmt("0")); - }); - it("should calculate rollover amt", async function () { - const r = await perp.callStatic.computeRolloverAmt( - rolloverInTranche.address, - reserveTranche1.address, - toFixedPtAmt("500"), - constants.MaxUint256, - ); - expect(r.perpRolloverAmt).to.eq(toFixedPtAmt("500")); - expect(r.tokenOutAmt).to.eq(toFixedPtAmt("500")); - expect(r.trancheOutAmt).to.eq(toFixedPtAmt("500")); - expect(r.trancheInAmt).to.eq(toFixedPtAmt("500")); - expect(r.remainingTrancheInAmt).to.eq(toFixedPtAmt("0")); - }); - }); - - describe("when fee > 0", async function () { - beforeEach(async function () { - await feeStrategy.computeRolloverFees.returns([toFixedPtAmt("1"), "0"]); - }); - - describe("with no approval", function () { - it("should revert", async function () { - await expect( - perp.rollover(rolloverInTranche.address, reserveTranche1.address, toFixedPtAmt("500")), - ).to.be.revertedWith("ERC20: insufficient allowance"); - }); - }); - - describe("with insufficient balance", function () { - beforeEach(async function () { - await feeToken.approve(perp.address, toFixedPtAmt("1")); - }); - - it("should revert", async function () { - await expect( - perp.rollover(rolloverInTranche.address, reserveTranche1.address, toFixedPtAmt("500")), - ).to.be.revertedWith("ERC20: transfer amount exceeds balance"); - }); - }); - - describe("with sufficient fee", async function () { - beforeEach(async function () { - await feeStrategy.computeRolloverFees.returns([toFixedPtAmt("1"), "0"]); - await feeToken.mint(deployerAddress, toFixedPtAmt("1")); - await feeToken.approve(perp.address, toFixedPtAmt("1")); - }); - - it("should transfer the tranches in", async function () { - await expect(() => - perp.rollover(rolloverInTranche.address, reserveTranche1.address, toFixedPtAmt("500")), - ).to.changeTokenBalances(rolloverInTranche, [deployer, perp], [toFixedPtAmt("-500"), toFixedPtAmt("500")]); - }); - it("should transfer the tranches out", async function () { - await expect(() => - perp.rollover(rolloverInTranche.address, reserveTranche1.address, toFixedPtAmt("500")), - ).to.changeTokenBalances(reserveTranche1, [deployer, perp], [toFixedPtAmt("500"), toFixedPtAmt("-500")]); - }); - it("should charge fee", async function () { - await expect(() => - perp.rollover(rolloverInTranche.address, reserveTranche1.address, toFixedPtAmt("500")), - ).to.changeTokenBalance(feeToken, perp, toFixedPtAmt("1")); - }); - it("should calculate rollover amt", async function () { - const r = await perp.callStatic.computeRolloverAmt( - rolloverInTranche.address, - reserveTranche1.address, - toFixedPtAmt("500"), - constants.MaxUint256, - ); - expect(r.perpRolloverAmt).to.eq(toFixedPtAmt("500")); - expect(r.tokenOutAmt).to.eq(toFixedPtAmt("500")); - expect(r.trancheOutAmt).to.eq(toFixedPtAmt("500")); - expect(r.trancheInAmt).to.eq(toFixedPtAmt("500")); - expect(r.remainingTrancheInAmt).to.eq(toFixedPtAmt("0")); - }); - }); - }); - - describe("when fee < 0", async function () { - beforeEach(async function () { - await feeStrategy.computeRolloverFees.returns([toFixedPtAmt("-1"), "0"]); - }); - describe("with insufficient balance", function () { - it("should revert", async function () { - await expect( - perp.rollover(rolloverInTranche.address, reserveTranche1.address, toFixedPtAmt("500")), - ).to.be.revertedWith("ERC20: transfer amount exceeds balance"); - }); - }); - - describe("with sufficient balance", function () { - beforeEach(async function () { - await feeToken.mint(perp.address, toFixedPtAmt("1")); - }); - - it("should transfer the tranches in", async function () { - await expect(() => - perp.rollover(rolloverInTranche.address, reserveTranche1.address, toFixedPtAmt("500")), - ).to.changeTokenBalances(rolloverInTranche, [deployer, perp], [toFixedPtAmt("-500"), toFixedPtAmt("500")]); - }); - it("should transfer the tranches out", async function () { - await expect(() => - perp.rollover(rolloverInTranche.address, reserveTranche1.address, toFixedPtAmt("500")), - ).to.changeTokenBalances(reserveTranche1, [deployer, perp], [toFixedPtAmt("500"), toFixedPtAmt("-500")]); - }); - it("should charge fee", async function () { - await expect(() => - perp.rollover(rolloverInTranche.address, reserveTranche1.address, toFixedPtAmt("500")), - ).to.changeTokenBalance(feeToken, perp, toFixedPtAmt("-1")); - }); - it("should calculate rollover amt", async function () { - const r = await perp.callStatic.computeRolloverAmt( - rolloverInTranche.address, - reserveTranche1.address, - toFixedPtAmt("500"), - constants.MaxUint256, - ); - expect(r.perpRolloverAmt).to.eq(toFixedPtAmt("500")); - expect(r.tokenOutAmt).to.eq(toFixedPtAmt("500")); - expect(r.trancheOutAmt).to.eq(toFixedPtAmt("500")); - expect(r.trancheInAmt).to.eq(toFixedPtAmt("500")); - expect(r.remainingTrancheInAmt).to.eq(toFixedPtAmt("0")); - }); - }); - }); - - describe("when protocol fee > 0", async function () { - beforeEach(async function () { - await perp.transferOwnership(await otherUser.getAddress()); - await feeStrategy.computeRolloverFees.returns([toFixedPtAmt("1"), toFixedPtAmt("0.5")]); - await feeToken.mint(deployerAddress, toFixedPtAmt("1.5")); - await feeToken.approve(perp.address, toFixedPtAmt("1.5")); - }); - - it("should transfer the tranches in", async function () { - await expect(() => - perp.rollover(rolloverInTranche.address, reserveTranche1.address, toFixedPtAmt("500")), - ).to.changeTokenBalances(rolloverInTranche, [deployer, perp], [toFixedPtAmt("-500"), toFixedPtAmt("500")]); - }); - it("should transfer the tranches out", async function () { - await expect(() => - perp.rollover(rolloverInTranche.address, reserveTranche1.address, toFixedPtAmt("500")), - ).to.changeTokenBalances(reserveTranche1, [deployer, perp], [toFixedPtAmt("500"), toFixedPtAmt("-500")]); - }); - it("should charge reserve and protocol fees", async function () { - await expect(() => - perp.rollover(rolloverInTranche.address, reserveTranche1.address, toFixedPtAmt("500")), - ).to.changeTokenBalances( - feeToken, - [deployer, perp, otherUser], - [toFixedPtAmt("-1.5"), toFixedPtAmt("1"), toFixedPtAmt("0.5")], - ); - }); - it("should calculate rollover amt", async function () { - const r = await perp.callStatic.computeRolloverAmt( - rolloverInTranche.address, - reserveTranche1.address, - toFixedPtAmt("500"), - constants.MaxUint256, - ); - expect(r.perpRolloverAmt).to.eq(toFixedPtAmt("500")); - expect(r.tokenOutAmt).to.eq(toFixedPtAmt("500")); - expect(r.trancheOutAmt).to.eq(toFixedPtAmt("500")); - expect(r.trancheInAmt).to.eq(toFixedPtAmt("500")); - expect(r.remainingTrancheInAmt).to.eq(toFixedPtAmt("0")); - }); - }); - }); - - describe("when tokenIn is not in the reserve", async function () { - let tx: Transaction, newRotationInTranche: Contract, r: any; - beforeEach(async function () { - const tranches = await getTranches(rolloverInBond); - newRotationInTranche = tranches[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")); - - await newRotationInTranche.approve(perp.address, toFixedPtAmt("250")); - - r = await perp.callStatic.computeRolloverAmt( - newRotationInTranche.address, - reserveTranche1.address, - toFixedPtAmt("250"), - constants.MaxUint256, - ); - tx = perp.rollover(newRotationInTranche.address, reserveTranche1.address, toFixedPtAmt("250")); - await tx; - }); - - it("should NOT update tranche balances", async function () { - expect(await perp.callStatic.getMatureTrancheBalance()).to.eq(toFixedPtAmt("500")); - }); - it("should update the reserve", async function () { - await checkReserveComposition( - perp, - [collateralToken, reserveTranche2, reserveTranche1, rolloverInTranche, newRotationInTranche], - [toFixedPtAmt("500"), toFixedPtAmt("1000"), toFixedPtAmt("250"), toFixedPtAmt("500"), toFixedPtAmt("250")], - ); - }); - it("should emit reserve synced", async function () { - await expect(tx) - .to.emit(perp, "ReserveSynced") - .withArgs(newRotationInTranche.address, toFixedPtAmt("250")) - .to.emit(perp, "ReserveSynced") - .withArgs(reserveTranche1.address, toFixedPtAmt("250")); - }); - it("should compute the rollover amounts", async function () { - expect(r.perpRolloverAmt).to.eq(toFixedPtAmt("250")); - expect(r.tokenOutAmt).to.eq(toFixedPtAmt("250")); - expect(r.trancheOutAmt).to.eq(toFixedPtAmt("250")); - expect(r.trancheInAmt).to.eq(toFixedPtAmt("250")); - expect(r.remainingTrancheInAmt).to.eq(toFixedPtAmt("0")); - }); - }); - - describe("when tokenOut is a reserve tranche", async function () { - let tx: Transaction, r: any; - beforeEach(async function () { - await checkReserveComposition(perp, [collateralToken, reserveTranche2, reserveTranche1, rolloverInTranche]); - expect(await perp.callStatic.getMatureTrancheBalance()).to.eq(toFixedPtAmt("500")); - r = await perp.callStatic.computeRolloverAmt( - rolloverInTranche.address, - reserveTranche1.address, - toFixedPtAmt("250"), - constants.MaxUint256, - ); - tx = perp.rollover(rolloverInTranche.address, reserveTranche1.address, toFixedPtAmt("250")); - await tx; - }); - - it("should NOT update tranche balances", async function () { - expect(await perp.callStatic.getMatureTrancheBalance()).to.eq(toFixedPtAmt("500")); - }); - it("should update the reserve", async function () { - await checkReserveComposition( - perp, - [collateralToken, reserveTranche2, reserveTranche1, rolloverInTranche], - [toFixedPtAmt("500"), toFixedPtAmt("1000"), toFixedPtAmt("250"), toFixedPtAmt("750")], - ); - }); - it("should emit reserve synced", async function () { - await expect(tx) - .to.emit(perp, "ReserveSynced") - .withArgs(rolloverInTranche.address, toFixedPtAmt("750")) - .to.emit(perp, "ReserveSynced") - .withArgs(reserveTranche1.address, toFixedPtAmt("250")); - }); - it("should compute the rollover amounts", async function () { - expect(r.perpRolloverAmt).to.eq(toFixedPtAmt("250")); - expect(r.tokenOutAmt).to.eq(toFixedPtAmt("250")); - expect(r.trancheOutAmt).to.eq(toFixedPtAmt("250")); - expect(r.trancheInAmt).to.eq(toFixedPtAmt("250")); - expect(r.remainingTrancheInAmt).to.eq(toFixedPtAmt("0")); - }); - }); - - describe("when tokenOut is the mature collateral", async function () { - let tx: Transaction, r: any; - beforeEach(async function () { - await checkReserveComposition(perp, [collateralToken, reserveTranche2, reserveTranche1, rolloverInTranche]); - expect(await perp.callStatic.getMatureTrancheBalance()).to.eq(toFixedPtAmt("500")); - r = await perp.callStatic.computeRolloverAmt( - rolloverInTranche.address, - collateralToken.address, - toFixedPtAmt("250"), - constants.MaxUint256, - ); - tx = perp.rollover(rolloverInTranche.address, collateralToken.address, toFixedPtAmt("250")); - await tx; - }); - - it("should emit tranche balance update", async function () { - await expect(tx).to.emit(perp, "UpdatedMatureTrancheBalance").withArgs(toFixedPtAmt("250")); - }); - it("should update mature tranche balance", async function () { - expect(await perp.callStatic.getMatureTrancheBalance()).to.eq(toFixedPtAmt("250")); - }); - it("should update the reserve", async function () { - await checkReserveComposition( - perp, - [collateralToken, reserveTranche2, reserveTranche1, rolloverInTranche], - [toFixedPtAmt("250"), toFixedPtAmt("1000"), toFixedPtAmt("500"), toFixedPtAmt("750")], - ); - }); - it("should emit reserve synced", async function () { - await expect(tx) - .to.emit(perp, "ReserveSynced") - .withArgs(rolloverInTranche.address, toFixedPtAmt("250")) - .to.emit(perp, "ReserveSynced") - .withArgs(collateralToken.address, toFixedPtAmt("250")); - }); - it("should compute the rollover amounts", async function () { - expect(r.perpRolloverAmt).to.eq(toFixedPtAmt("250")); - expect(r.tokenOutAmt).to.eq(toFixedPtAmt("250")); - expect(r.trancheOutAmt).to.eq(toFixedPtAmt("250")); - expect(r.trancheInAmt).to.eq(toFixedPtAmt("250")); - expect(r.remainingTrancheInAmt).to.eq(toFixedPtAmt("0")); - }); - }); - - describe("when tokenOut is the mature collateral which has rebased up", async function () { - let tx: Transaction, r: any; - beforeEach(async function () { - await rebase(collateralToken, rebaseOracle, +0.5); - await checkReserveComposition(perp, [collateralToken, reserveTranche2, reserveTranche1, rolloverInTranche]); - expect(await perp.callStatic.getMatureTrancheBalance()).to.eq(toFixedPtAmt("500")); - r = await perp.callStatic.computeRolloverAmt( - rolloverInTranche.address, - collateralToken.address, - toFixedPtAmt("250"), - constants.MaxUint256, - ); - tx = perp.rollover(rolloverInTranche.address, collateralToken.address, toFixedPtAmt("250")); - await tx; - }); - - it("should emit tranche balance update", async function () { - await expect(tx).to.emit(perp, "UpdatedMatureTrancheBalance").withArgs(toFixedPtAmt("250")); - }); - it("should update mature tranche balance", async function () { - expect(await perp.callStatic.getMatureTrancheBalance()).to.eq(toFixedPtAmt("250")); - }); - it("should update the reserve", async function () { - await checkReserveComposition( - perp, - [collateralToken, reserveTranche2, reserveTranche1, rolloverInTranche], - [toFixedPtAmt("375"), toFixedPtAmt("1000"), toFixedPtAmt("500"), toFixedPtAmt("750")], - ); - }); - it("should emit reserve synced", async function () { - await expect(tx) - .to.emit(perp, "ReserveSynced") - .withArgs(rolloverInTranche.address, toFixedPtAmt("250")) - .to.emit(perp, "ReserveSynced") - .withArgs(collateralToken.address, toFixedPtAmt("375")); - }); - it("should compute the rollover amounts", async function () { - expect(r.perpRolloverAmt).to.eq(toFixedPtAmt("250")); - expect(r.tokenOutAmt).to.eq(toFixedPtAmt("375")); - expect(r.trancheOutAmt).to.eq(toFixedPtAmt("250")); - expect(r.trancheInAmt).to.eq(toFixedPtAmt("250")); - expect(r.remainingTrancheInAmt).to.eq(toFixedPtAmt("0")); - }); - }); - - describe("when tokenOut is the mature collateral which has rebased down", async function () { - let tx: Transaction, r: any; - beforeEach(async function () { - await rebase(collateralToken, rebaseOracle, -0.5); - await checkReserveComposition(perp, [collateralToken, reserveTranche2, reserveTranche1, rolloverInTranche]); - expect(await perp.callStatic.getMatureTrancheBalance()).to.eq(toFixedPtAmt("500")); - r = await perp.callStatic.computeRolloverAmt( - rolloverInTranche.address, - collateralToken.address, - toFixedPtAmt("250"), - constants.MaxUint256, - ); - tx = perp.rollover(rolloverInTranche.address, collateralToken.address, toFixedPtAmt("250")); - await tx; - }); - - it("should emit tranche balance update", async function () { - await expect(tx).to.emit(perp, "UpdatedMatureTrancheBalance").withArgs(toFixedPtAmt("250")); - }); - it("should update mature tranche balance", async function () { - expect(await perp.callStatic.getMatureTrancheBalance()).to.eq(toFixedPtAmt("250")); - }); - it("should update the reserve", async function () { - await checkReserveComposition( - perp, - [collateralToken, reserveTranche2, reserveTranche1, rolloverInTranche], - [toFixedPtAmt("125"), toFixedPtAmt("1000"), toFixedPtAmt("500"), toFixedPtAmt("750")], - ); - }); - it("should emit reserve synced", async function () { - await expect(tx) - .to.emit(perp, "ReserveSynced") - .withArgs(rolloverInTranche.address, toFixedPtAmt("250")) - .to.emit(perp, "ReserveSynced") - .withArgs(collateralToken.address, toFixedPtAmt("125")); - }); - it("should compute the rollover amounts", async function () { - expect(r.perpRolloverAmt).to.eq(toFixedPtAmt("250")); - expect(r.tokenOutAmt).to.eq(toFixedPtAmt("125")); - expect(r.trancheOutAmt).to.eq(toFixedPtAmt("250")); - expect(r.trancheInAmt).to.eq(toFixedPtAmt("250")); - expect(r.remainingTrancheInAmt).to.eq(toFixedPtAmt("0")); - }); - }); - - describe("when tokenOut is tranche and fully withdrawn", async function () { - let tx: Transaction, r: any; - beforeEach(async function () { - await checkReserveComposition(perp, [collateralToken, reserveTranche2, reserveTranche1, rolloverInTranche]); - r = await perp.callStatic.computeRolloverAmt( - rolloverInTranche.address, - reserveTranche1.address, - toFixedPtAmt("500"), - constants.MaxUint256, - ); - tx = perp.rollover(rolloverInTranche.address, reserveTranche1.address, toFixedPtAmt("500")); - await tx; - }); - - it("should update the reserve", async function () { - await checkReserveComposition( - perp, - [collateralToken, reserveTranche2, rolloverInTranche], - [toFixedPtAmt("500"), toFixedPtAmt("1000"), toFixedPtAmt("1000")], - ); - }); - - it("should emit reserve synced", async function () { - await expect(tx) - .to.emit(perp, "ReserveSynced") - .withArgs(rolloverInTranche.address, toFixedPtAmt("1000")) - .to.emit(perp, "ReserveSynced") - .withArgs(reserveTranche1.address, toFixedPtAmt("0")); - }); - it("should compute the rollover amounts", async function () { - expect(r.perpRolloverAmt).to.eq(toFixedPtAmt("500")); - expect(r.tokenOutAmt).to.eq(toFixedPtAmt("500")); - expect(r.trancheOutAmt).to.eq(toFixedPtAmt("500")); - expect(r.trancheInAmt).to.eq(toFixedPtAmt("500")); - expect(r.remainingTrancheInAmt).to.eq(toFixedPtAmt("0")); - }); - }); - - describe("when tokenOut is collateral and fully withdrawn", async function () { - let tx: Transaction, r: any; - beforeEach(async function () { - await checkReserveComposition(perp, [collateralToken, reserveTranche2, reserveTranche1, rolloverInTranche]); - r = await perp.callStatic.computeRolloverAmt( - rolloverInTranche.address, - collateralToken.address, - toFixedPtAmt("500"), - constants.MaxUint256, - ); - tx = perp.rollover(rolloverInTranche.address, collateralToken.address, toFixedPtAmt("500")); - await tx; - }); - - it("should update the reserve", async function () { - await checkReserveComposition( - perp, - [collateralToken, reserveTranche2, reserveTranche1, rolloverInTranche], - [toFixedPtAmt("0"), toFixedPtAmt("1000"), toFixedPtAmt("500"), toFixedPtAmt("1000")], - ); - }); - it("should emit reserve synced", async function () { - await expect(tx) - .to.emit(perp, "ReserveSynced") - .withArgs(rolloverInTranche.address, toFixedPtAmt("1000")) - .to.emit(perp, "ReserveSynced") - .withArgs(collateralToken.address, toFixedPtAmt("0")); - }); - it("should compute the rollover amounts", async function () { - expect(r.perpRolloverAmt).to.eq(toFixedPtAmt("500")); - expect(r.tokenOutAmt).to.eq(toFixedPtAmt("500")); - expect(r.trancheOutAmt).to.eq(toFixedPtAmt("500")); - expect(r.trancheInAmt).to.eq(toFixedPtAmt("500")); - expect(r.remainingTrancheInAmt).to.eq(toFixedPtAmt("0")); - }); - }); - - describe("when tokenOut is partially redeemed", async function () { - let tx: Transaction, r: any; - beforeEach(async function () { - await checkReserveComposition(perp, [collateralToken, reserveTranche2, reserveTranche1, rolloverInTranche]); - r = await perp.callStatic.computeRolloverAmt( - rolloverInTranche.address, - reserveTranche1.address, - toFixedPtAmt("100"), - constants.MaxUint256, - ); - tx = perp.rollover(rolloverInTranche.address, reserveTranche1.address, toFixedPtAmt("100")); - await tx; - }); - - it("should update the reserve", async function () { - await checkReserveComposition( - perp, - [collateralToken, reserveTranche2, reserveTranche1, rolloverInTranche], - [toFixedPtAmt("500"), toFixedPtAmt("1000"), toFixedPtAmt("400"), toFixedPtAmt("600")], - ); - }); - - it("should emit reserve synced", async function () { - await expect(tx) - .to.emit(perp, "ReserveSynced") - .withArgs(rolloverInTranche.address, toFixedPtAmt("600")) - .to.emit(perp, "ReserveSynced") - .withArgs(reserveTranche1.address, toFixedPtAmt("400")); - }); - it("should compute the rollover amounts", async function () { - expect(r.perpRolloverAmt).to.eq(toFixedPtAmt("100")); - expect(r.tokenOutAmt).to.eq(toFixedPtAmt("100")); - expect(r.trancheOutAmt).to.eq(toFixedPtAmt("100")); - expect(r.trancheInAmt).to.eq(toFixedPtAmt("100")); - expect(r.remainingTrancheInAmt).to.eq(toFixedPtAmt("0")); - }); - }); - - describe("when tokenOut is NOT covered", async function () { - let tx: Transaction, r: any; - beforeEach(async function () { - r = await perp.callStatic.computeRolloverAmt( - rolloverInTranche.address, - reserveTranche1.address, - toFixedPtAmt("2000"), - constants.MaxUint256, - ); - tx = perp.rollover(rolloverInTranche.address, reserveTranche1.address, toFixedPtAmt("2000")); - await tx; - }); - - it("should update the reserve (only transfers covered amount)", async function () { - await checkReserveComposition( - perp, - [collateralToken, reserveTranche2, rolloverInTranche], - [toFixedPtAmt("500"), toFixedPtAmt("1000"), toFixedPtAmt("1000")], - ); - }); - - it("should emit reserve synced", async function () { - await expect(tx) - .to.emit(perp, "ReserveSynced") - .withArgs(rolloverInTranche.address, toFixedPtAmt("1000")) - .to.emit(perp, "ReserveSynced") - .withArgs(reserveTranche1.address, toFixedPtAmt("0")); - }); - it("should compute the rollover amounts", async function () { - expect(r.perpRolloverAmt).to.eq(toFixedPtAmt("500")); - expect(r.tokenOutAmt).to.eq(toFixedPtAmt("500")); - expect(r.trancheOutAmt).to.eq(toFixedPtAmt("500")); - expect(r.trancheInAmt).to.eq(toFixedPtAmt("500")); - expect(r.remainingTrancheInAmt).to.eq(toFixedPtAmt("1500")); - }); - }); - - describe("when valid rollover", async function () { - let tx: Transaction, r: any; - beforeEach(async function () { - await checkReserveComposition(perp, [collateralToken, reserveTranche2, reserveTranche1, rolloverInTranche]); - await pricingStrategy.computeMatureTranchePrice.returns(toPriceFixedPtAmt("2")); - - expect(await perp.callStatic.getMatureTrancheBalance()).to.eq(toFixedPtAmt("500")); - r = await perp.callStatic.computeRolloverAmt( - rolloverInTranche.address, - collateralToken.address, - toFixedPtAmt("100"), - constants.MaxUint256, - ); - tx = perp.rollover(rolloverInTranche.address, collateralToken.address, toFixedPtAmt("100")); - await tx; - }); - - it("should emit tranche balance update", async function () { - await expect(tx).to.emit(perp, "UpdatedMatureTrancheBalance").withArgs(toFixedPtAmt("450")); - }); - - it("should update tranche balances", async function () { - expect(await perp.callStatic.getMatureTrancheBalance()).to.eq(toFixedPtAmt("450")); - }); - - it("should update the reserve", async function () { - await checkReserveComposition( - perp, - [collateralToken, reserveTranche2, reserveTranche1, rolloverInTranche], - [toFixedPtAmt("450"), toFixedPtAmt("1000"), toFixedPtAmt("500"), toFixedPtAmt("600")], - ); - }); - - it("should emit reserve synced", async function () { - await expect(tx) - .to.emit(perp, "ReserveSynced") - .withArgs(rolloverInTranche.address, toFixedPtAmt("600")) - .to.emit(perp, "ReserveSynced") - .withArgs(collateralToken.address, toFixedPtAmt("450")); - }); - it("should compute the rollover amounts", async function () { - expect(r.perpRolloverAmt).to.eq(toFixedPtAmt("80")); - expect(r.tokenOutAmt).to.eq(toFixedPtAmt("50")); - expect(r.trancheOutAmt).to.eq(toFixedPtAmt("50")); - expect(r.trancheInAmt).to.eq(toFixedPtAmt("100")); - expect(r.remainingTrancheInAmt).to.eq(toFixedPtAmt("0")); - }); - }); - - describe("when mature tranche target it set", async function () { - beforeEach(async function () { - await perp.updateMatureValueTargetPerc("20000000"); - }); - - describe("when rolling over above the target", function () { - it("should NOT revert", async function () { - await expect( - perp.rollover(rolloverInTranche.address, collateralToken.address, toFixedPtAmt("100")), - ).not.to.be.revertedWithCustomError(perp, "BelowMatureValueTargetPerc"); - }); - }); - - describe("when rolling over below the target", function () { - it("should revert", async function () { - await expect( - perp.rollover(rolloverInTranche.address, collateralToken.address, toFixedPtAmt("100.000001")), - ).to.be.revertedWithCustomError(perp, "BelowMatureValueTargetPerc"); - }); - }); - }); - }); -}); diff --git a/spot-contracts/test/RouterV1.ts b/spot-contracts/test/RouterV1.ts deleted file mode 100644 index 32d2a805..00000000 --- a/spot-contracts/test/RouterV1.ts +++ /dev/null @@ -1,907 +0,0 @@ -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, - depositIntoBond, - bondAt, - getTranches, - toFixedPtAmt, - toDiscountFixedPtAmt, - toPriceFixedPtAmt, - advancePerpQueue, - mintCollteralToken, - advancePerpQueueToRollover, -} from "./helpers"; -use(smock.matchers); - -let perp: Contract, - bondFactory: Contract, - collateralToken: Contract, - issuer: Contract, - feeStrategy: Contract, - pricingStrategy: Contract, - discountStrategy: Contract, - deployer: Signer, - deployerAddress: string, - router: Contract, - depositBond: Contract, - depositTranches: Contract[]; - -describe("RouterV1", function () { - beforeEach(async function () { - await network.provider.send("hardhat_reset"); - - const accounts = await ethers.getSigners(); - deployer = accounts[0]; - deployerAddress = await deployer.getAddress(); - - 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(3600, [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 perp.updateTolerableTrancheMaturity(600, 3600); - await advancePerpQueue(perp, 3600); - - depositBond = await bondAt(await perp.callStatic.getDepositBond()); - depositTranches = await getTranches(depositBond); - - 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 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(); - }); - - afterEach(async function () { - await network.provider.send("hardhat_reset"); - }); - - describe("#previewTranche", function () { - it("should compute the tranche amounts", async function () { - const r = await router.callStatic.previewTranche(perp.address, toFixedPtAmt("1000")); - expect(r[0]).to.eq(await perp.callStatic.getDepositBond()); - expect(r[1][0]).to.eq(depositTranches[0].address); - expect(r[1][1]).to.eq(depositTranches[1].address); - expect(r[1][2]).to.eq(depositTranches[2].address); - expect(r[2][0]).to.eq(toFixedPtAmt("200")); - expect(r[2][1]).to.eq(toFixedPtAmt("300")); - expect(r[2][2]).to.eq(toFixedPtAmt("500")); - }); - }); - - describe("#previewDeposit", function () { - beforeEach(async function () { - await feeStrategy.computeMintFees.returns([toFixedPtAmt("10"), "0"]); - }); - - describe("when fee token is the native token", async function () { - it("should compute the mint amount and fee", async function () { - const r = await router.callStatic.previewDeposit(perp.address, depositTranches[1].address, toFixedPtAmt("300")); - expect(r[0]).to.eq(toFixedPtAmt("225")); - expect(r[1]).to.eq(perp.address); - expect(r[2]).to.eq(toFixedPtAmt("10")); - }); - }); - - describe("when fee token is the non-native token", async function () { - let feeToken: Contract; - beforeEach(async function () { - const ERC20 = await ethers.getContractFactory("MockERC20"); - feeToken = await ERC20.deploy(); - await feeToken.init("Mock token", "MOCK"); - await feeStrategy.feeToken.returns(feeToken.address); - }); - - it("should compute the mint amount and fee", async function () { - const r = await router.callStatic.previewDeposit(perp.address, depositTranches[0].address, toFixedPtAmt("200")); - expect(r[0]).to.eq(toFixedPtAmt("200")); - expect(r[1]).to.eq(feeToken.address); - expect(r[2]).to.eq(toFixedPtAmt("10")); - }); - }); - }); - - describe("#previewRedeem", function () { - let depositTranches1: Contract[], depositTranches2: Contract[], depositTranches3: Contract[]; - beforeEach(async function () { - 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.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")); - await depositTranches1[1].approve(perp.address, toFixedPtAmt("300")); - await perp.deposit(depositTranches1[1].address, toFixedPtAmt("300")); - - await advancePerpQueue(perp, 1200); - - const depositBond2 = await bondAt(await perp.callStatic.getDepositBond()); - depositTranches2 = await getTranches(depositBond2); - await depositIntoBond(depositBond2, toFixedPtAmt("1000"), deployer); - - 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")); - await depositTranches2[1].approve(perp.address, toFixedPtAmt("300")); - await perp.deposit(depositTranches2[1].address, toFixedPtAmt("300")); - - await advancePerpQueue(perp, 1200); - - const depositBond3 = await bondAt(await perp.callStatic.getDepositBond()); - depositTranches3 = await getTranches(depositBond3); - await depositIntoBond(depositBond3, toFixedPtAmt("1000"), deployer); - - 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")); - await depositTranches3[1].approve(perp.address, toFixedPtAmt("300")); - await perp.deposit(depositTranches3[1].address, toFixedPtAmt("300")); - }); - - describe("full redemption", function () { - it("should compute the burn amount and fee", async function () { - const r = await router.callStatic.previewRedeem(perp.address, toFixedPtAmt("1275")); - - expect(r[0][0]).to.eq(collateralToken.address); - expect(r[0][1]).to.eq(depositTranches1[0].address); - expect(r[0][2]).to.eq(depositTranches1[1].address); - expect(r[0][3]).to.eq(depositTranches2[0].address); - expect(r[0][4]).to.eq(depositTranches2[1].address); - expect(r[0][5]).to.eq(depositTranches3[0].address); - expect(r[0][6]).to.eq(depositTranches3[1].address); - - expect(r[1][0]).to.eq(toFixedPtAmt("0")); - expect(r[1][1]).to.eq(toFixedPtAmt("200")); - expect(r[1][2]).to.eq(toFixedPtAmt("300")); - expect(r[1][3]).to.eq(toFixedPtAmt("200")); - expect(r[1][4]).to.eq(toFixedPtAmt("300")); - expect(r[1][5]).to.eq(toFixedPtAmt("200")); - expect(r[1][6]).to.eq(toFixedPtAmt("300")); - - expect(r[2]).to.eq(perp.address); - expect(r[3]).to.eq(toFixedPtAmt("12.75")); - }); - }); - - describe("partial redemption", async function () { - it("should compute the burn amount and fee", async function () { - const r = await router.callStatic.previewRedeem(perp.address, toFixedPtAmt("637.5")); - - expect(r[0][0]).to.eq(collateralToken.address); - expect(r[0][1]).to.eq(depositTranches1[0].address); - expect(r[0][2]).to.eq(depositTranches1[1].address); - expect(r[0][3]).to.eq(depositTranches2[0].address); - expect(r[0][4]).to.eq(depositTranches2[1].address); - expect(r[0][5]).to.eq(depositTranches3[0].address); - expect(r[0][6]).to.eq(depositTranches3[1].address); - - expect(r[1][0]).to.eq(toFixedPtAmt("0")); - expect(r[1][1]).to.eq(toFixedPtAmt("100")); - expect(r[1][2]).to.eq(toFixedPtAmt("150")); - expect(r[1][3]).to.eq(toFixedPtAmt("100")); - expect(r[1][4]).to.eq(toFixedPtAmt("150")); - expect(r[1][5]).to.eq(toFixedPtAmt("100")); - expect(r[1][6]).to.eq(toFixedPtAmt("150")); - - expect(r[2]).to.eq(perp.address); - expect(r[3]).to.eq(toFixedPtAmt("12.75")); - }); - }); - - describe("when fee is in non native token", async function () { - let feeToken: Contract; - beforeEach(async function () { - const ERC20 = await ethers.getContractFactory("MockERC20"); - feeToken = await ERC20.deploy(); - await feeToken.init("Mock token", "MOCK"); - await feeStrategy.feeToken.returns(feeToken.address); - }); - - it("should compute the burn amount and fee", async function () { - const r = await router.callStatic.previewRedeem(perp.address, toFixedPtAmt("637.5")); - - expect(r[0][0]).to.eq(collateralToken.address); - expect(r[0][1]).to.eq(depositTranches1[0].address); - expect(r[0][2]).to.eq(depositTranches1[1].address); - expect(r[0][3]).to.eq(depositTranches2[0].address); - expect(r[0][4]).to.eq(depositTranches2[1].address); - expect(r[0][5]).to.eq(depositTranches3[0].address); - expect(r[0][6]).to.eq(depositTranches3[1].address); - - expect(r[1][0]).to.eq(toFixedPtAmt("0")); - expect(r[1][1]).to.eq(toFixedPtAmt("100")); - expect(r[1][2]).to.eq(toFixedPtAmt("150")); - expect(r[1][3]).to.eq(toFixedPtAmt("100")); - expect(r[1][4]).to.eq(toFixedPtAmt("150")); - expect(r[1][5]).to.eq(toFixedPtAmt("100")); - expect(r[1][6]).to.eq(toFixedPtAmt("150")); - - expect(r[2]).to.eq(feeToken.address); - expect(r[3]).to.eq(toFixedPtAmt("12.75")); - }); - }); - }); - - describe("#previewRollover", function () { - let holdingPenTranches: Contract[], reserveTranches: Contract[], depositTranches: Contract[]; - beforeEach(async function () { - 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.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")); - await holdingPenTranches[1].approve(perp.address, toFixedPtAmt("300")); - await perp.deposit(holdingPenTranches[1].address, toFixedPtAmt("300")); - - await advancePerpQueue(perp, 7200); - - const reserveBond = await bondAt(await perp.callStatic.getDepositBond()); - reserveTranches = await getTranches(reserveBond); - await depositIntoBond(reserveBond, toFixedPtAmt("1000"), deployer); - - 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")); - await reserveTranches[1].approve(perp.address, toFixedPtAmt("300")); - await perp.deposit(reserveTranches[1].address, toFixedPtAmt("300")); - - await advancePerpQueueToRollover(perp, reserveBond); - - const depositBond = await bondAt(await perp.callStatic.getDepositBond()); - depositTranches = await getTranches(depositBond); - await depositIntoBond(depositBond, toFixedPtAmt("1000"), deployer); - - 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")); - await depositTranches[1].approve(perp.address, toFixedPtAmt("300")); - await perp.deposit(depositTranches[1].address, toFixedPtAmt("300")); - }); - - describe("when rollover is not acceptable", function () { - beforeEach(async function () { - await feeStrategy.computeRolloverFees.returns([toFixedPtAmt("1"), "0"]); - }); - it("should return 0", async function () { - const r = await router.callStatic.previewRollover( - perp.address, - depositTranches[0].address, - depositTranches[0].address, - toFixedPtAmt("200"), - constants.MaxUint256, - ); - expect(r[0].perpRolloverAmt).to.eq(toFixedPtAmt("0")); - expect(r[0].tokenOutAmt).to.eq(toFixedPtAmt("0")); - expect(r[0].trancheInAmt).to.eq(toFixedPtAmt("0")); - expect(r[0].remainingTrancheInAmt).to.eq(toFixedPtAmt("200")); - expect(r[1]).to.eq(perp.address); - expect(r[2]).to.eq(toFixedPtAmt("0")); - }); - }); - - describe("when tranche out balance is NOT covered", function () { - beforeEach(async function () { - await feeStrategy.computeRolloverFees.returns([toFixedPtAmt("1"), "0"]); - }); - it("should compute the rollover fees and amounts", async function () { - const r = await router.callStatic.previewRollover( - perp.address, - depositTranches[0].address, - reserveTranches[0].address, - toFixedPtAmt("250"), - constants.MaxUint256, - ); - expect(r[0].perpRolloverAmt).to.eq(toFixedPtAmt("200")); - expect(r[0].tokenOutAmt).to.eq(toFixedPtAmt("200")); - expect(r[0].trancheInAmt).to.eq(toFixedPtAmt("200")); - expect(r[0].remainingTrancheInAmt).to.eq(toFixedPtAmt("50")); - expect(r[1]).to.eq(perp.address); - expect(r[2]).to.eq(toFixedPtAmt("1")); - }); - }); - - describe("when tranche in has different rate", function () { - beforeEach(async function () { - await feeStrategy.computeRolloverFees.returns([toFixedPtAmt("1"), "0"]); - }); - it("should compute the rollover fees and amounts", async function () { - const r = await router.callStatic.previewRollover( - perp.address, - depositTranches[1].address, - reserveTranches[0].address, - toFixedPtAmt("250"), - constants.MaxUint256, - ); - expect(r[0].perpRolloverAmt).to.eq(toFixedPtAmt("187.5")); - expect(r[0].tokenOutAmt).to.eq(toFixedPtAmt("187.5")); - expect(r[0].trancheInAmt).to.eq(toFixedPtAmt("250")); - expect(r[0].remainingTrancheInAmt).to.eq(toFixedPtAmt("0")); - expect(r[1]).to.eq(perp.address); - expect(r[2]).to.eq(toFixedPtAmt("1")); - }); - }); - - describe("when tranche out has different rate", function () { - beforeEach(async function () { - await feeStrategy.computeRolloverFees.returns([toFixedPtAmt("1"), "0"]); - }); - it("should compute the rollover fees and amounts", async function () { - const r = await router.callStatic.previewRollover( - perp.address, - depositTranches[0].address, - reserveTranches[1].address, - toFixedPtAmt("250"), - constants.MaxUint256, - ); - expect(r[0].perpRolloverAmt).to.eq(toFixedPtAmt("225")); - expect(r[0].tokenOutAmt).to.eq(toFixedPtAmt("300")); - expect(r[0].trancheInAmt).to.eq(toFixedPtAmt("225")); - expect(r[0].remainingTrancheInAmt).to.eq(toFixedPtAmt("25")); - expect(r[1]).to.eq(perp.address); - expect(r[2]).to.eq(toFixedPtAmt("1")); - }); - }); - - describe("when tranche out balance is covered exactly", function () { - beforeEach(async function () { - await feeStrategy.computeRolloverFees.returns([toFixedPtAmt("1"), "0"]); - }); - it("should compute the rollover fees and amounts", async function () { - const r = await router.callStatic.previewRollover( - perp.address, - depositTranches[0].address, - reserveTranches[0].address, - toFixedPtAmt("200"), - constants.MaxUint256, - ); - expect(r[0].perpRolloverAmt).to.eq(toFixedPtAmt("200")); - expect(r[0].tokenOutAmt).to.eq(toFixedPtAmt("200")); - expect(r[0].trancheInAmt).to.eq(toFixedPtAmt("200")); - expect(r[0].remainingTrancheInAmt).to.eq(toFixedPtAmt("0")); - expect(r[1]).to.eq(perp.address); - expect(r[2]).to.eq(toFixedPtAmt("1")); - }); - }); - - describe("when tranche out used is less than the balance", function () { - beforeEach(async function () { - await feeStrategy.computeRolloverFees.returns([toFixedPtAmt("1"), "0"]); - }); - it("should compute the rollover fees and amounts", async function () { - const r = await router.callStatic.previewRollover( - perp.address, - depositTranches[0].address, - reserveTranches[0].address, - toFixedPtAmt("200"), - toFixedPtAmt("190"), - ); - expect(r[0].perpRolloverAmt).to.eq(toFixedPtAmt("190")); - expect(r[0].tokenOutAmt).to.eq(toFixedPtAmt("190")); - expect(r[0].trancheInAmt).to.eq(toFixedPtAmt("190")); - expect(r[0].remainingTrancheInAmt).to.eq(toFixedPtAmt("10")); - expect(r[1]).to.eq(perp.address); - expect(r[2]).to.eq(toFixedPtAmt("1")); - }); - }); - - describe("when tranche out balance is covered", function () { - beforeEach(async function () { - await feeStrategy.computeRolloverFees.returns([toFixedPtAmt("1"), "0"]); - }); - it("should compute the rollover fees and amounts", async function () { - const r = await router.callStatic.previewRollover( - perp.address, - depositTranches[0].address, - reserveTranches[0].address, - toFixedPtAmt("190"), - constants.MaxUint256, - ); - expect(r[0].perpRolloverAmt).to.eq(toFixedPtAmt("190")); - expect(r[0].tokenOutAmt).to.eq(toFixedPtAmt("190")); - expect(r[0].trancheInAmt).to.eq(toFixedPtAmt("190")); - expect(r[0].remainingTrancheInAmt).to.eq(toFixedPtAmt("0")); - expect(r[1]).to.eq(perp.address); - expect(r[2]).to.eq(toFixedPtAmt("1")); - }); - }); - - describe("when collateral token is transferred out", function () { - beforeEach(async function () { - await feeStrategy.computeRolloverFees.returns([toFixedPtAmt("1"), "0"]); - }); - it("should compute the rollover fees and amounts", async function () { - const r = await router.callStatic.previewRollover( - perp.address, - depositTranches[0].address, - collateralToken.address, - toFixedPtAmt("250"), - constants.MaxUint256, - ); - expect(r[0].perpRolloverAmt).to.eq(toFixedPtAmt("250")); - expect(r[0].tokenOutAmt).to.eq("294117647058823529411"); - expect(r[0].trancheInAmt).to.eq(toFixedPtAmt("250")); - expect(r[0].remainingTrancheInAmt).to.eq(toFixedPtAmt("0")); - expect(r[1]).to.eq(perp.address); - expect(r[2]).to.eq(toFixedPtAmt("1")); - }); - }); - - describe("when fee is -ve", function () { - beforeEach(async function () { - await feeStrategy.computeRolloverFees.returns([toFixedPtAmt("-1"), "0"]); - }); - it("should compute the rollover fees and amounts", async function () { - const r = await router.callStatic.previewRollover( - perp.address, - depositTranches[0].address, - reserveTranches[0].address, - toFixedPtAmt("200"), - constants.MaxUint256, - ); - expect(r[0].perpRolloverAmt).to.eq(toFixedPtAmt("200")); - expect(r[0].tokenOutAmt).to.eq(toFixedPtAmt("200")); - expect(r[0].trancheInAmt).to.eq(toFixedPtAmt("200")); - expect(r[0].remainingTrancheInAmt).to.eq(toFixedPtAmt("0")); - expect(r[1]).to.eq(perp.address); - expect(r[2]).to.eq(toFixedPtAmt("-1")); - }); - }); - - describe("when fee is in non native token", function () { - let feeToken: Contract; - beforeEach(async function () { - const ERC20 = await ethers.getContractFactory("MockERC20"); - feeToken = await ERC20.deploy(); - await feeToken.init("Mock token", "MOCK"); - 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( - perp.address, - depositTranches[0].address, - reserveTranches[0].address, - toFixedPtAmt("200"), - constants.MaxUint256, - ); - expect(r[0].perpRolloverAmt).to.eq(toFixedPtAmt("200")); - expect(r[0].tokenOutAmt).to.eq(toFixedPtAmt("200")); - expect(r[0].trancheInAmt).to.eq(toFixedPtAmt("200")); - expect(r[0].remainingTrancheInAmt).to.eq(toFixedPtAmt("0")); - expect(r[1]).to.eq(feeToken.address); - expect(r[2]).to.eq(toFixedPtAmt("1")); - }); - }); - }); - - describe("#trancheAndDeposit", function () { - beforeEach(async function () { - await mintCollteralToken(collateralToken, toFixedPtAmt("2000"), deployer); - await feeStrategy.computeMintFees.returns([toFixedPtAmt("5"), "0"]); - }); - - describe("when deposit bond is incorrect", function () { - beforeEach(async function () { - await collateralToken.approve(router.address, constants.MaxUint256); - await advancePerpQueue(perp, 7200); - }); - it("should revert", async function () { - await expect( - router.trancheAndDeposit(perp.address, depositBond.address, toFixedPtAmt("1000"), 0), - ).to.revertedWithCustomError(perp, "UnacceptableDepositTranche"); - }); - }); - - describe("when fee is in native token", function () { - beforeEach(async function () { - await collateralToken.approve(router.address, constants.MaxUint256); - await router.trancheAndDeposit(perp.address, depositBond.address, toFixedPtAmt("1000"), 0); - }); - - it("should mint tranches", async function () { - expect(await perp.balanceOf(deployerAddress)).to.eq(toFixedPtAmt("415")); - }); - - it("should transfer unused tranches back", async function () { - expect(await depositTranches[2].balanceOf(deployerAddress)).to.eq(toFixedPtAmt("500")); - }); - - it("should leave no dust", async function () { - expect(await depositTranches[0].balanceOf(router.address)).to.eq("0"); - expect(await depositTranches[1].balanceOf(router.address)).to.eq("0"); - expect(await perp.balanceOf(router.address)).to.eq("0"); - }); - }); - - describe("when fee is in non-native token", function () { - let feeToken: Contract; - beforeEach(async function () { - const ERC20 = await ethers.getContractFactory("MockERC20"); - feeToken = await ERC20.deploy(); - await feeToken.init("Mock token", "MOCK"); - await feeStrategy.feeToken.returns(feeToken.address); - await feeToken.mint(deployerAddress, toFixedPtAmt("10")); - - await feeToken.approve(router.address, constants.MaxUint256); - await collateralToken.approve(router.address, constants.MaxUint256); - await router.trancheAndDeposit(perp.address, depositBond.address, toFixedPtAmt("1000"), toFixedPtAmt("10")); - }); - - it("should mint tranches", async function () { - expect(await perp.balanceOf(deployerAddress)).to.eq(toFixedPtAmt("425")); - }); - - it("should transfer fee", async function () { - expect(await feeToken.balanceOf(perp.address)).to.eq(toFixedPtAmt("10")); - }); - - it("should transfer unused tranches back", async function () { - expect(await depositTranches[2].balanceOf(deployerAddress)).to.eq(toFixedPtAmt("500")); - }); - - it("should leave no dust", async function () { - expect(await depositTranches[0].balanceOf(router.address)).to.eq("0"); - expect(await depositTranches[1].balanceOf(router.address)).to.eq("0"); - expect(await perp.balanceOf(router.address)).to.eq("0"); - expect(await feeToken.balanceOf(router.address)).to.eq("0"); - }); - }); - - describe("when fee is overpaid", function () { - let feeToken: Contract; - beforeEach(async function () { - const ERC20 = await ethers.getContractFactory("MockERC20"); - feeToken = await ERC20.deploy(); - await feeToken.init("Mock token", "MOCK"); - await feeStrategy.feeToken.returns(feeToken.address); - await feeToken.mint(deployerAddress, toFixedPtAmt("25")); - - await feeToken.approve(router.address, constants.MaxUint256); - await collateralToken.approve(router.address, constants.MaxUint256); - await mintCollteralToken(collateralToken, toFixedPtAmt("1"), deployer); - await collateralToken.transfer(router.address, toFixedPtAmt("1")); - await router.trancheAndDeposit(perp.address, depositBond.address, toFixedPtAmt("1000"), toFixedPtAmt("25")); - }); - - it("should mint tranches", async function () { - expect(await perp.balanceOf(deployerAddress)).to.eq(toFixedPtAmt("425")); - }); - - it("should transfer fee", async function () { - expect(await feeToken.balanceOf(perp.address)).to.eq(toFixedPtAmt("10")); - }); - - it("should remaining fee back", async function () { - expect(await feeToken.balanceOf(deployerAddress)).to.eq(toFixedPtAmt("15")); - }); - - it("should transfer unused tranches back", async function () { - expect(await depositTranches[2].balanceOf(deployerAddress)).to.eq(toFixedPtAmt("500")); - }); - - it("should leave no dust", async function () { - expect(await collateralToken.balanceOf(router.address)).to.eq(toFixedPtAmt("0")); - expect(await depositTranches[0].balanceOf(router.address)).to.eq("0"); - expect(await depositTranches[1].balanceOf(router.address)).to.eq("0"); - expect(await perp.balanceOf(router.address)).to.eq("0"); - expect(await feeToken.balanceOf(router.address)).to.eq("0"); - }); - }); - }); - - describe("#trancheAndRollover", function () { - let holdingPenTranches: Contract[], reserveTranches: Contract[], depositBond: Contract, depositTranches: Contract[]; - beforeEach(async function () { - 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.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")); - await holdingPenTranches[1].approve(perp.address, toFixedPtAmt("300")); - await perp.deposit(holdingPenTranches[1].address, toFixedPtAmt("300")); - - await advancePerpQueue(perp, 7200); - - const reserveBond = await bondAt(await perp.callStatic.getDepositBond()); - reserveTranches = await getTranches(reserveBond); - await depositIntoBond(reserveBond, toFixedPtAmt("1000"), deployer); - - 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")); - await reserveTranches[1].approve(perp.address, toFixedPtAmt("300")); - await perp.deposit(reserveTranches[1].address, toFixedPtAmt("300")); - - await advancePerpQueueToRollover(perp, reserveBond); - - depositBond = await bondAt(await perp.callStatic.getDepositBond()); - depositTranches = await getTranches(depositBond); - - 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 feeStrategy.computeRolloverFees.returns([toFixedPtAmt("1"), "0"]); - await perp.approve(router.address, toFixedPtAmt("15")); - - await mintCollteralToken(collateralToken, toFixedPtAmt("2000"), deployer); - await collateralToken.approve(router.address, toFixedPtAmt("2000")); - - expect(await collateralToken.balanceOf(deployerAddress)).to.eq(toFixedPtAmt("2000")); - expect(await holdingPenTranches[0].balanceOf(deployerAddress)).to.eq(toFixedPtAmt("0")); - expect(await holdingPenTranches[1].balanceOf(deployerAddress)).to.eq(toFixedPtAmt("0")); - expect(await reserveTranches[0].balanceOf(deployerAddress)).to.eq(toFixedPtAmt("0")); - expect(await depositTranches[0].balanceOf(deployerAddress)).to.eq(toFixedPtAmt("0")); - expect(await depositTranches[1].balanceOf(deployerAddress)).to.eq(toFixedPtAmt("0")); - expect(await depositTranches[0].balanceOf(router.address)).to.eq(toFixedPtAmt("0")); - expect(await depositTranches[1].balanceOf(router.address)).to.eq(toFixedPtAmt("0")); - expect(await perp.balanceOf(deployerAddress)).to.eq(toFixedPtAmt("1000")); - - await mintCollteralToken(collateralToken, toFixedPtAmt("1"), deployer); - await collateralToken.transfer(router.address, toFixedPtAmt("1")); - await router.trancheAndRollover( - perp.address, - depositBond.address, - toFixedPtAmt("2000"), - [ - [depositTranches[0].address, collateralToken.address, toFixedPtAmt("300")], - [depositTranches[0].address, reserveTranches[0].address, toFixedPtAmt("100")], - [depositTranches[1].address, reserveTranches[0].address, toFixedPtAmt("100")], - [depositTranches[1].address, reserveTranches[1].address, toFixedPtAmt("100")], - ], - toFixedPtAmt("15"), - ); - }); - - it("should transfer tranches out", async function () { - expect(await collateralToken.balanceOf(deployerAddress)).to.eq(toFixedPtAmt("301")); - expect(await holdingPenTranches[0].balanceOf(deployerAddress)).to.eq(toFixedPtAmt("0")); - expect(await holdingPenTranches[1].balanceOf(deployerAddress)).to.eq(toFixedPtAmt("0")); - expect(await reserveTranches[0].balanceOf(deployerAddress)).to.eq(toFixedPtAmt("200")); - expect(await reserveTranches[1].balanceOf(deployerAddress)).to.eq(toFixedPtAmt("100")); - expect(await depositTranches[0].balanceOf(deployerAddress)).to.eq(toFixedPtAmt("0")); - expect(await depositTranches[1].balanceOf(deployerAddress)).to.eq(toFixedPtAmt("400")); - expect(await depositTranches[0].balanceOf(router.address)).to.eq(toFixedPtAmt("0")); - expect(await depositTranches[1].balanceOf(router.address)).to.eq(toFixedPtAmt("0")); - }); - - it("should transfer excess fees back", async function () { - expect(await perp.balanceOf(deployerAddress)).to.eq(toFixedPtAmt("996")); - }); - - it("should leave no dust", async function () { - expect(await collateralToken.balanceOf(router.address)).to.eq(toFixedPtAmt("0")); - expect(await perp.balanceOf(router.address)).to.eq(toFixedPtAmt("0")); - }); - }); - - describe("successful tranche & rollover and return the remainder (with no fees)", function () { - beforeEach(async function () { - await feeStrategy.computeRolloverFees.returns(["0", "0"]); - - await mintCollteralToken(collateralToken, toFixedPtAmt("2000"), deployer); - await collateralToken.approve(router.address, toFixedPtAmt("2000")); - - expect(await collateralToken.balanceOf(deployerAddress)).to.eq(toFixedPtAmt("2000")); - expect(await holdingPenTranches[0].balanceOf(deployerAddress)).to.eq(toFixedPtAmt("0")); - expect(await holdingPenTranches[1].balanceOf(deployerAddress)).to.eq(toFixedPtAmt("0")); - expect(await reserveTranches[0].balanceOf(deployerAddress)).to.eq(toFixedPtAmt("0")); - expect(await depositTranches[0].balanceOf(deployerAddress)).to.eq(toFixedPtAmt("0")); - expect(await depositTranches[1].balanceOf(deployerAddress)).to.eq(toFixedPtAmt("0")); - expect(await depositTranches[0].balanceOf(router.address)).to.eq(toFixedPtAmt("0")); - expect(await depositTranches[1].balanceOf(router.address)).to.eq(toFixedPtAmt("0")); - expect(await perp.balanceOf(deployerAddress)).to.eq(toFixedPtAmt("1000")); - - await mintCollteralToken(collateralToken, toFixedPtAmt("1"), deployer); - await collateralToken.transfer(router.address, toFixedPtAmt("1")); - await router.trancheAndRollover( - perp.address, - depositBond.address, - toFixedPtAmt("1000"), - [ - [depositTranches[0].address, reserveTranches[0].address, toFixedPtAmt("100")], - [depositTranches[1].address, reserveTranches[0].address, toFixedPtAmt("100")], - [depositTranches[1].address, reserveTranches[1].address, toFixedPtAmt("100")], - ], - "0", - ); - }); - - it("should transfer tranches out", async function () { - expect(await collateralToken.balanceOf(deployerAddress)).to.eq(toFixedPtAmt("1001")); - expect(await holdingPenTranches[0].balanceOf(deployerAddress)).to.eq(toFixedPtAmt("0")); - expect(await holdingPenTranches[1].balanceOf(deployerAddress)).to.eq(toFixedPtAmt("0")); - expect(await reserveTranches[0].balanceOf(deployerAddress)).to.eq(toFixedPtAmt("200")); - expect(await reserveTranches[1].balanceOf(deployerAddress)).to.eq(toFixedPtAmt("100")); - expect(await depositTranches[0].balanceOf(deployerAddress)).to.eq(toFixedPtAmt("100")); - expect(await depositTranches[1].balanceOf(deployerAddress)).to.eq(toFixedPtAmt("100")); - expect(await depositTranches[0].balanceOf(router.address)).to.eq(toFixedPtAmt("0")); - expect(await depositTranches[1].balanceOf(router.address)).to.eq(toFixedPtAmt("0")); - }); - - it("should transfer excess fees back", async function () { - expect(await perp.balanceOf(deployerAddress)).to.eq(toFixedPtAmt("1000")); - }); - - it("should leave no dust", async function () { - expect(await collateralToken.balanceOf(router.address)).to.eq(toFixedPtAmt("0")); - expect(await perp.balanceOf(router.address)).to.eq(toFixedPtAmt("0")); - }); - }); - }); -}); diff --git a/spot-contracts/test/RouterV2.ts b/spot-contracts/test/RouterV2.ts new file mode 100644 index 00000000..040f3433 --- /dev/null +++ b/spot-contracts/test/RouterV2.ts @@ -0,0 +1,149 @@ +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, + bondAt, + getTranches, + toFixedPtAmt, + advancePerpQueue, + advanceTime, + mintCollteralToken, +} from "./helpers"; +use(smock.matchers); + +let perp: Contract, + bondFactory: Contract, + collateralToken: Contract, + issuer: Contract, + feePolicy: Contract, + vault: Contract, + deployer: Signer, + deployerAddress: string, + router: Contract, + depositBond: Contract, + depositTranches: Contract[]; + +describe("RouterV2", function () { + beforeEach(async function () { + await network.provider.send("hardhat_reset"); + + const accounts = await ethers.getSigners(); + deployer = accounts[0]; + deployerAddress = await deployer.getAddress(); + + bondFactory = await setupBondFactory(); + ({ collateralToken } = await setupCollateralToken("Bitcoin", "BTC")); + + const BondIssuer = await ethers.getContractFactory("BondIssuer"); + issuer = await upgrades.deployProxy( + BondIssuer.connect(deployer), + [bondFactory.address, collateralToken.address, 3600, [200, 800], 1200, 0], + { + initializer: "init(address,address,uint256,uint256[],uint256,uint256)", + }, + ); + + const FeePolicy = await ethers.getContractFactory("FeePolicy"); + feePolicy = await smock.fake(FeePolicy); + await feePolicy.computePerpRolloverFeePerc.returns("0"); + await feePolicy.decimals.returns(8); + + const PerpetualTranche = await ethers.getContractFactory("PerpetualTranche"); + perp = await upgrades.deployProxy( + PerpetualTranche.connect(deployer), + ["PerpetualTranche", "PERP", collateralToken.address, issuer.address, feePolicy.address], + { + initializer: "init(string,string,address,address,address)", + }, + ); + await perp.updateTolerableTrancheMaturity(600, 3600); + await advancePerpQueue(perp, 3600); + + const RolloverVault = await ethers.getContractFactory("RolloverVault"); + vault = await smock.fake(RolloverVault); + await vault.getTVL.returns("0"); + await perp.updateVault(vault.address); + + depositBond = await bondAt(await perp.callStatic.getDepositBond()); + depositTranches = await getTranches(depositBond); + + const Router = await ethers.getContractFactory("RouterV2"); + router = await Router.deploy(); + }); + + afterEach(async function () { + await network.provider.send("hardhat_reset"); + }); + + describe("#previewTranche", function () { + it("should compute the tranche amounts", async function () { + const r = await router.callStatic.previewTranche(perp.address, toFixedPtAmt("1000")); + expect(r[0]).to.eq(await perp.callStatic.getDepositBond()); + expect(r[1][0].token).to.eq(depositTranches[0].address); + expect(r[1][0].amount).to.eq(toFixedPtAmt("200")); + expect(r[1][1].token).to.eq(depositTranches[1].address); + expect(r[1][1].amount).to.eq(toFixedPtAmt("800")); + }); + }); + + describe("#trancheAndDeposit", function () { + beforeEach(async function () { + await mintCollteralToken(collateralToken, toFixedPtAmt("1100"), deployer); + await collateralToken.transfer(router.address, toFixedPtAmt("100")); + }); + + describe("when deposit bond is incorrect", function () { + beforeEach(async function () { + await collateralToken.approve(router.address, constants.MaxUint256); + await advancePerpQueue(perp, 7200); + }); + it("should revert", async function () { + await expect( + router.trancheAndDeposit(perp.address, depositBond.address, toFixedPtAmt("1000")), + ).to.revertedWithCustomError(perp, "UnexpectedAsset"); + }); + }); + + describe("when deposit bond is not issued", function () { + beforeEach(async function () { + await collateralToken.approve(router.address, constants.MaxUint256); + await advanceTime(7200); + }); + it("should not revert", async function () { + const depositBond = await bondAt(await perp.callStatic.getDepositBond()); + await expect(router.trancheAndDeposit(perp.address, depositBond.address, toFixedPtAmt("1000"))).not.to.be + .reverted; + }); + }); + + describe("when deposit bond is correct", function () { + beforeEach(async function () { + await collateralToken.approve(router.address, constants.MaxUint256); + await router.trancheAndDeposit(perp.address, depositBond.address, toFixedPtAmt("1000")); + }); + + it("should mint tranches", async function () { + expect(await perp.balanceOf(deployerAddress)).to.eq(toFixedPtAmt("200")); + }); + + it("should dust collateral tokens back", async function () { + expect(await collateralToken.balanceOf(deployerAddress)).to.eq(toFixedPtAmt("100")); + }); + + it("should transfer unused tranches back", async function () { + expect(await depositTranches[0].balanceOf(deployerAddress)).to.eq("0"); + expect(await depositTranches[1].balanceOf(deployerAddress)).to.eq(toFixedPtAmt("800")); + }); + + it("should leave no dust", async function () { + expect(await depositTranches[0].balanceOf(router.address)).to.eq("0"); + expect(await depositTranches[1].balanceOf(router.address)).to.eq("0"); + expect(await perp.balanceOf(router.address)).to.eq("0"); + expect(await collateralToken.balanceOf(router.address)).to.eq("0"); + }); + }); + }); +}); diff --git a/spot-contracts/test/_utils/BondHelpers.ts b/spot-contracts/test/_utils/BondHelpers.ts deleted file mode 100644 index 4191fdf8..00000000 --- a/spot-contracts/test/_utils/BondHelpers.ts +++ /dev/null @@ -1,598 +0,0 @@ -import { expect } from "chai"; -import { network, ethers } from "hardhat"; -import { Contract, Signer, constants } from "ethers"; - -import { - TimeHelpers, - setupCollateralToken, - setupBondFactory, - createBondWithFactory, - toFixedPtAmt, - rebase, - depositIntoBond, - getTranches, - getTrancheBalances, -} from "../helpers"; - -let bondFactory: Contract, - collateralToken: Contract, - rebaseOracle: Contract, - bondHelpers: Contract, - accounts: Signer[], - deployer: Signer, - 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")); - - const BondHelpersTester = await ethers.getContractFactory("BondHelpersTester"); - bondHelpers = await BondHelpersTester.deploy(); - await bondHelpers.deployed(); -} - -describe("BondHelpers", function () { - beforeEach(async () => { - await setupContracts(); - }); - - afterEach(async function () { - await network.provider.send("hardhat_reset"); - }); - - describe("#timeToMatirity & #duration", function () { - let maturityDate: number, bondLength: number, bond: Contract; - beforeEach(async function () { - bondLength = 86400; - bond = await createBondWithFactory(bondFactory, collateralToken, [1000], bondLength); - maturityDate = (await bond.maturityDate()).toNumber(); - }); - - describe("when bond is NOT mature", function () { - it("should return the time to maturity", async function () { - await TimeHelpers.setNextBlockTimestamp(maturityDate - bondLength / 2); - expect(await bondHelpers.secondsToMaturity(bond.address)).to.eq(bondLength / 2); - }); - }); - - describe("when bond is mature", function () { - it("should return the time to maturity", async function () { - await TimeHelpers.setNextBlockTimestamp(maturityDate + 1); - expect(await bondHelpers.secondsToMaturity(bond.address)).to.eq(0); - }); - }); - }); - - describe("#getTranches", function () { - let bond: Contract; - beforeEach(async function () { - bond = await createBondWithFactory(bondFactory, collateralToken, [201, 301, 498], 86400); - }); - - it("should return the tranche data", async function () { - const td = await bondHelpers.getTranches(bond.address); - expect(td.tranches.length).to.eq(3); - expect(td.trancheRatios.length).to.eq(3); - expect(td.trancheRatios[0]).to.eq(201); - expect(td.trancheRatios[1]).to.eq(301); - expect(td.trancheRatios[2]).to.eq(498); - expect(td.tranches.length).to.eq(3); - expect(td.tranches[0]).to.eq((await bond.tranches(0))[0]); - expect(td.tranches[1]).to.eq((await bond.tranches(1))[0]); - expect(td.tranches[2]).to.eq((await bond.tranches(2))[0]); - }); - }); - - describe("#indexOf", function () { - it("should return the tranche index", async function () { - const bond = await createBondWithFactory(bondFactory, collateralToken, [100, 100, 100, 100, 100, 500], 86400); - const td = await bondHelpers.getTranches(bond.address); - - for (const t in td.tranches) { - expect(await bondHelpers.indexOf(bond.address, td.tranches[t])).to.eq(parseInt(t)); - } - - await expect(bondHelpers.indexOf(bond.address, bond.address)).to.be.revertedWithCustomError( - bondHelpers, - "UnacceptableTranche", - ); - await expect(bondHelpers.indexOf(bond.address, deployerAddress)).to.be.revertedWithCustomError( - bondHelpers, - "UnacceptableTranche", - ); - await expect(bondHelpers.indexOf(bond.address, constants.AddressZero)).to.be.revertedWithCustomError( - bondHelpers, - "UnacceptableTranche", - ); - }); - }); - - describe("#previewDeposit", function () { - let bond: Contract; - beforeEach(async function () { - bond = await createBondWithFactory(bondFactory, collateralToken, [500, 500], 86400); - }); - - describe("fee = 0", function () { - describe("first deposit", function () { - it("should calculate the tranche balances after deposit", async function () { - const d = await bondHelpers.previewDeposit(bond.address, toFixedPtAmt("1000")); - expect(d[1][0]).to.eq(toFixedPtAmt("500")); - expect(d[1][1]).to.eq(toFixedPtAmt("500")); - expect(d[2][0]).to.eq("0"); - expect(d[2][1]).to.eq("0"); - }); - - it("should be consistent with deposit", async function () { - await depositIntoBond(bond, toFixedPtAmt("1000"), deployer); - const b = await getTrancheBalances(bond, deployerAddress); - expect(b[0]).to.eq(toFixedPtAmt("500")); - expect(b[1]).to.eq(toFixedPtAmt("500")); - }); - }); - - describe("later deposit", function () { - beforeEach(async function () { - await depositIntoBond(bond, toFixedPtAmt("1000"), deployer); - }); - - describe("with no supply change", function () { - beforeEach(async function () { - await rebase(collateralToken, rebaseOracle, 0); - }); - it("should calculate the tranche balances after deposit", async function () { - const d = await bondHelpers.previewDeposit(bond.address, toFixedPtAmt("1000")); - expect(d[1][0]).to.eq(toFixedPtAmt("500")); - expect(d[1][1]).to.eq(toFixedPtAmt("500")); - expect(d[2][0]).to.eq("0"); - expect(d[2][1]).to.eq("0"); - }); - - it("should be consistent with deposit", async function () { - await depositIntoBond(bond, toFixedPtAmt("1000"), deployer); - const b = await getTrancheBalances(bond, deployerAddress); - expect(b[0]).to.eq(toFixedPtAmt("1000")); // 500 + 500 - expect(b[1]).to.eq(toFixedPtAmt("1000")); - }); - }); - - describe("with supply increase", function () { - beforeEach(async function () { - await rebase(collateralToken, rebaseOracle, +0.25); - }); - it("should calculate the tranche balances after deposit", async function () { - const d = await bondHelpers.previewDeposit(bond.address, toFixedPtAmt("1000")); - expect(d[1][0]).to.eq(toFixedPtAmt("400")); - expect(d[1][1]).to.eq(toFixedPtAmt("400")); - expect(d[2][0]).to.eq("0"); - expect(d[2][1]).to.eq("0"); - }); - - it("should be consistent with deposit", async function () { - await depositIntoBond(bond, toFixedPtAmt("1000"), deployer); - const b = await getTrancheBalances(bond, deployerAddress); - expect(b[0]).to.eq(toFixedPtAmt("900")); // 500 + 400 - expect(b[1]).to.eq(toFixedPtAmt("900")); - }); - }); - - describe("with supply decrease", function () { - beforeEach(async function () { - await rebase(collateralToken, rebaseOracle, -0.5); - }); - it("should calculate the tranche balances after deposit", async function () { - const d = await bondHelpers.previewDeposit(bond.address, toFixedPtAmt("1000")); - expect(d[1][0]).to.eq(toFixedPtAmt("1000")); - expect(d[1][1]).to.eq(toFixedPtAmt("1000")); - expect(d[2][0]).to.eq("0"); - expect(d[2][1]).to.eq("0"); - }); - it("should be consistent with deposit", async function () { - await depositIntoBond(bond, toFixedPtAmt("1000"), deployer); - const b = await getTrancheBalances(bond, deployerAddress); - expect(b[0]).to.eq(toFixedPtAmt("1500")); // 500 + 1000 - expect(b[1]).to.eq(toFixedPtAmt("1500")); - }); - }); - }); - }); - - describe("fee > 0", function () { - beforeEach(async function () { - await bond.setFee(50); - }); - - describe("first deposit", function () { - it("should calculate the tranche balances after deposit", async function () { - const d = await bondHelpers.previewDeposit(bond.address, toFixedPtAmt("1000")); - expect(d[1][0]).to.eq(toFixedPtAmt("497.5")); - expect(d[1][1]).to.eq(toFixedPtAmt("497.5")); - expect(d[2][0]).to.eq(toFixedPtAmt("2.5")); - expect(d[2][1]).to.eq(toFixedPtAmt("2.5")); - }); - - it("should be consistent with deposit", async function () { - await depositIntoBond(bond, toFixedPtAmt("1000"), deployer); - const b = await getTrancheBalances(bond, deployerAddress); - expect(b[0]).to.eq(toFixedPtAmt("497.5")); - expect(b[1]).to.eq(toFixedPtAmt("497.5")); - const c = await getTrancheBalances(bond, bond.address); - expect(c[0]).to.eq(toFixedPtAmt("2.5")); - expect(c[1]).to.eq(toFixedPtAmt("2.5")); - }); - }); - - describe("later deposit", function () { - beforeEach(async function () { - await depositIntoBond(bond, toFixedPtAmt("1000"), deployer); - }); - - describe("with no supply change", function () { - beforeEach(async function () { - await rebase(collateralToken, rebaseOracle, 0); - }); - it("should calculate the tranche balances after deposit", async function () { - const d = await bondHelpers.previewDeposit(bond.address, toFixedPtAmt("1000")); - expect(d[1][0]).to.eq(toFixedPtAmt("497.5")); - expect(d[1][1]).to.eq(toFixedPtAmt("497.5")); - expect(d[2][0]).to.eq(toFixedPtAmt("2.5")); - expect(d[2][1]).to.eq(toFixedPtAmt("2.5")); - }); - - it("should be consistent with deposit", async function () { - await depositIntoBond(bond, toFixedPtAmt("1000"), deployer); - const b = await getTrancheBalances(bond, deployerAddress); - expect(b[0]).to.eq(toFixedPtAmt("995")); // 497.5 + 497.5 - expect(b[1]).to.eq(toFixedPtAmt("995")); - const c = await getTrancheBalances(bond, bond.address); - expect(c[0]).to.eq(toFixedPtAmt("5")); // 2.5 + 2.5 - expect(c[1]).to.eq(toFixedPtAmt("5")); - }); - }); - - describe("with supply increase", function () { - beforeEach(async function () { - await rebase(collateralToken, rebaseOracle, +0.25); - }); - it("should calculate the tranche balances after deposit", async function () { - const d = await bondHelpers.previewDeposit(bond.address, toFixedPtAmt("1000")); - expect(d[1][0]).to.eq(toFixedPtAmt("398")); - expect(d[1][1]).to.eq(toFixedPtAmt("398")); - expect(d[2][0]).to.eq(toFixedPtAmt("2")); - expect(d[2][1]).to.eq(toFixedPtAmt("2")); - }); - - it("should be consistent with deposit", async function () { - await depositIntoBond(bond, toFixedPtAmt("1000"), deployer); - const b = await getTrancheBalances(bond, deployerAddress); - expect(b[0]).to.eq(toFixedPtAmt("895.5")); // 497.5 + 398 - expect(b[1]).to.eq(toFixedPtAmt("895.5")); - const c = await getTrancheBalances(bond, bond.address); - expect(c[0]).to.eq(toFixedPtAmt("4.5")); // 2.5 + 2 - expect(c[1]).to.eq(toFixedPtAmt("4.5")); - }); - }); - - describe("with supply decrease", function () { - beforeEach(async function () { - await rebase(collateralToken, rebaseOracle, -0.5); - }); - it("should calculate the tranche balances after deposit", async function () { - const d = await bondHelpers.previewDeposit(bond.address, toFixedPtAmt("1000")); - expect(d[1][0]).to.eq(toFixedPtAmt("995")); - expect(d[1][1]).to.eq(toFixedPtAmt("995")); - expect(d[2][0]).to.eq(toFixedPtAmt("5")); - expect(d[2][1]).to.eq(toFixedPtAmt("5")); - }); - it("should be consistent with deposit", async function () { - await depositIntoBond(bond, toFixedPtAmt("1000"), deployer); - const b = await getTrancheBalances(bond, deployerAddress); - expect(b[0]).to.eq(toFixedPtAmt("1492.5")); // 497.5 + 995 - expect(b[1]).to.eq(toFixedPtAmt("1492.5")); - const c = await getTrancheBalances(bond, bond.address); - expect(c[0]).to.eq(toFixedPtAmt("7.5")); // 2.5 + 5 - expect(c[1]).to.eq(toFixedPtAmt("7.5")); - }); - }); - }); - }); - }); - - describe("#getTrancheCollateralizations", 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 bond not mature", function () { - describe("when no change in supply", function () { - it("should calculate the balances", async function () { - const b = await bondHelpers.getTrancheCollateralizations(bond.address); - expect(b[1][0]).to.eq(toFixedPtAmt("200")); - expect(b[1][1]).to.eq(toFixedPtAmt("300")); - expect(b[1][2]).to.eq(toFixedPtAmt("500")); - expect(b[2][0]).to.eq(toFixedPtAmt("200")); - expect(b[2][1]).to.eq(toFixedPtAmt("300")); - expect(b[2][2]).to.eq(toFixedPtAmt("500")); - }); - }); - - describe("when supply increases above z threshold", function () { - it("should calculate the balances", async function () { - await rebase(collateralToken, rebaseOracle, 0.1); - const b = await bondHelpers.getTrancheCollateralizations(bond.address); - expect(b[1][0]).to.eq(toFixedPtAmt("200")); - expect(b[1][1]).to.eq(toFixedPtAmt("300")); - expect(b[1][2]).to.eq(toFixedPtAmt("600")); - expect(b[2][0]).to.eq(toFixedPtAmt("200")); - expect(b[2][1]).to.eq(toFixedPtAmt("300")); - expect(b[2][2]).to.eq(toFixedPtAmt("500")); - }); - }); - - describe("when supply decreases below z threshold", function () { - it("should calculate the balances", async function () { - await rebase(collateralToken, rebaseOracle, -0.1); - const b = await bondHelpers.getTrancheCollateralizations(bond.address); - expect(b[1][0]).to.eq(toFixedPtAmt("200")); - expect(b[1][1]).to.eq(toFixedPtAmt("300")); - expect(b[1][2]).to.eq(toFixedPtAmt("400")); - expect(b[2][0]).to.eq(toFixedPtAmt("200")); - expect(b[2][1]).to.eq(toFixedPtAmt("300")); - expect(b[2][2]).to.eq(toFixedPtAmt("500")); - }); - }); - - describe("when supply decreases below b threshold", function () { - it("should calculate the balances", async function () { - await rebase(collateralToken, rebaseOracle, -0.6); - const b = await bondHelpers.getTrancheCollateralizations(bond.address); - expect(b[1][0]).to.eq(toFixedPtAmt("200")); - expect(b[1][1]).to.eq(toFixedPtAmt("200")); - expect(b[1][2]).to.eq(toFixedPtAmt("0")); - expect(b[2][0]).to.eq(toFixedPtAmt("200")); - expect(b[2][1]).to.eq(toFixedPtAmt("300")); - expect(b[2][2]).to.eq(toFixedPtAmt("500")); - }); - }); - - describe("when supply decreases below a threshold", function () { - it("should calculate the balances", async function () { - await rebase(collateralToken, rebaseOracle, -0.85); - const b = await bondHelpers.getTrancheCollateralizations(bond.address); - expect(b[1][0]).to.eq(toFixedPtAmt("150")); - expect(b[1][1]).to.eq(toFixedPtAmt("0")); - expect(b[1][2]).to.eq(toFixedPtAmt("0")); - expect(b[2][0]).to.eq(toFixedPtAmt("200")); - expect(b[2][1]).to.eq(toFixedPtAmt("300")); - expect(b[2][2]).to.eq(toFixedPtAmt("500")); - }); - }); - }); - - describe("when bond is mature", function () { - beforeEach(async function () { - await TimeHelpers.increaseTime(bondLength); - await bond.mature(); // NOTE: Any rebase after maturity goes directly to the tranches - }); - - describe("when no change in supply", function () { - it("should calculate the balances", async function () { - const b = await bondHelpers.getTrancheCollateralizations(bond.address); - expect(b[1][0]).to.eq(toFixedPtAmt("200")); - expect(b[1][1]).to.eq(toFixedPtAmt("300")); - expect(b[1][2]).to.eq(toFixedPtAmt("500")); - expect(b[2][0]).to.eq(toFixedPtAmt("200")); - expect(b[2][1]).to.eq(toFixedPtAmt("300")); - expect(b[2][2]).to.eq(toFixedPtAmt("500")); - }); - }); - - describe("when supply increases", function () { - it("should calculate the balances", async function () { - await rebase(collateralToken, rebaseOracle, 0.1); - const b = await bondHelpers.getTrancheCollateralizations(bond.address); - expect(b[1][0]).to.eq(toFixedPtAmt("220")); - expect(b[1][1]).to.eq(toFixedPtAmt("330")); - expect(b[1][2]).to.eq(toFixedPtAmt("550")); - expect(b[2][0]).to.eq(toFixedPtAmt("200")); - expect(b[2][1]).to.eq(toFixedPtAmt("300")); - expect(b[2][2]).to.eq(toFixedPtAmt("500")); - }); - }); - - describe("when supply decreases", function () { - it("should calculate the balances", async function () { - await rebase(collateralToken, rebaseOracle, -0.1); - const b = await bondHelpers.getTrancheCollateralizations(bond.address); - expect(b[1][0]).to.eq(toFixedPtAmt("180")); - expect(b[1][1]).to.eq(toFixedPtAmt("270")); - expect(b[1][2]).to.eq(toFixedPtAmt("450")); - expect(b[2][0]).to.eq(toFixedPtAmt("200")); - expect(b[2][1]).to.eq(toFixedPtAmt("300")); - expect(b[2][2]).to.eq(toFixedPtAmt("500")); - }); - }); - }); - }); - - describe("#computeRedeemableTrancheAmounts", function () { - describe("when the user has all the tranches in the right proportions", 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 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( - trancheRatios: number[] = [], - amounts: string[] = [], - redemptionAmts: string[] = [], - ) { - const bond = await createBondWithFactory(bondFactory, collateralToken, trancheRatios, 86400); - const amt = amounts - .map((a, i) => toFixedPtAmt(a).mul("1000").div(trancheRatios[i])) - .reduce((m, a) => (m.gt(a) ? m : a), toFixedPtAmt("0")); - await depositIntoBond(bond, amt.add(toFixedPtAmt("1")), deployer); - - 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); - if (b[1][0].gt("0")) { - await bond.connect(user).redeem(b[1]); - } - for (const a in redemptionAmts) { - expect(b[1][a]).to.eq(toFixedPtAmt(redemptionAmts[a])); - } - } - - describe("[200,300,500]:[9, 15, 25]", async function () { - it("should calculate the amounts", async function () { - await checkRedeemableAmts([200, 300, 500], ["9", "15", "25"], ["9", "13.5", "22.5"]); - }); - }); - - describe("[200,300,500]:[10, 15, 250]", async function () { - it("should calculate the amounts", async function () { - await checkRedeemableAmts([200, 300, 500], ["10", "15", "250"], ["10", "15", "25"]); - }); - }); - - describe("[200,300,500]:[10, 12, 250]", async function () { - it("should calculate the amounts", async function () { - await checkRedeemableAmts([200, 300, 500], ["10", "12", "250"], ["8", "12", "20"]); - }); - }); - - describe("[200,300,500]:[10, 12, 5]", async function () { - it("should calculate the amounts", async function () { - await checkRedeemableAmts([200, 300, 500], ["10", "12", "5"], ["2", "3", "5"]); - }); - }); - - describe("[200,300,500]:[10, 12, 0.5]", async function () { - it("should calculate the amounts", async function () { - await checkRedeemableAmts([200, 300, 500], ["10", "12", "0.5"], ["0.2", "0.3", "0.5"]); - }); - }); - - describe("[200,300,500]:[10, 0, 25]", async function () { - it("should calculate the amounts", async function () { - await checkRedeemableAmts([200, 300, 500], ["10", "0", "25"], ["0", "0", "0"]); - }); - }); - - describe("[200,300,500]:[0, 15, 25]", async function () { - it("should calculate the amounts", async function () { - await checkRedeemableAmts([200, 300, 500], ["0", "15", "25"], ["0", "0", "0"]); - }); - }); - - describe("imperfect rounding", function () { - describe("[200,300,500]:[10, 15, 7.461048491123254231]", async function () { - it("should calculate the amounts", async function () { - await checkRedeemableAmts( - [200, 300, 500], - ["10", "15", "7.461048491123254230"], - ["2.984419396449301600", "4.476629094673952400", "7.461048491123254000"], - ); - }); - }); - - describe("[200,300,500]:[1000e-18,5001e-18,503e-18]", async function () { - it("should calculate the amounts", async function () { - await checkRedeemableAmts( - [200, 300, 500], - ["1000e-18", "5001e-18", "503e-18"], - ["200e-18", "300e-18", "500e-18"], - ); - }); - }); - - describe("[200,300,500]:[1000e-18,5001e-18,506e-18]", async function () { - it("should calculate the amounts", async function () { - await checkRedeemableAmts( - [200, 300, 500], - ["1000e-18", "5001e-18", "506e-18"], - ["200e-18", "300e-18", "500e-18"], - ); - }); - }); - - describe("[1,999]:[1000e-18,2001e-18]", async function () { - it("should calculate the amounts", async function () { - await checkRedeemableAmts([1, 999], ["1000e-18", "2001e-18"], ["2e-18", "1998e-18"]); - }); - }); - - describe("[1,999]:[5e-18,1]", async function () { - it("should calculate the amounts", async function () { - await checkRedeemableAmts([1, 999], ["5e-18", "1"], ["5e-18", "4995e-18"]); - }); - }); - - describe("[499,501]:[1232e-18,1]", async function () { - it("should calculate the amounts", async function () { - await checkRedeemableAmts([499, 501], ["1232e-18", "1"], ["998e-18", "1002e-18"]); - }); - }); - - describe("[499,501]:[1,499e-18]", async function () { - it("should calculate the amounts", async function () { - await checkRedeemableAmts([499, 501], ["1", "499e-18"], ["0", "0"]); - }); - }); - - describe("[499,501]:[13224e-18]", async function () { - it("should calculate the amounts", async function () { - await checkRedeemableAmts([499, 501], ["1", "1322e-18"], ["998e-18", "1002e-18"]); - }); - }); - }); - }); - }); -}); diff --git a/spot-contracts/test/_utils/HelpersTester.ts b/spot-contracts/test/_utils/HelpersTester.ts new file mode 100644 index 00000000..09cf7d7e --- /dev/null +++ b/spot-contracts/test/_utils/HelpersTester.ts @@ -0,0 +1,1013 @@ +import { expect, use } from "chai"; +import { network, ethers } from "hardhat"; +import { Contract, Signer } from "ethers"; +import { smock } from "@defi-wonderland/smock"; + +import { + TimeHelpers, + setupCollateralToken, + setupBondFactory, + createBondWithFactory, + toFixedPtAmt, + rebase, + depositIntoBond, + getTrancheBalances, + getTranches, + getContractFactoryFromExternalArtifacts, + mintCollteralToken, +} from "../helpers"; +use(smock.matchers); + +let bondFactory: Contract, + collateralToken: Contract, + rebaseOracle: Contract, + helper: Contract, + accounts: Signer[], + deployer: Signer, + deployerAddress: string, + user: Signer, + userAddress: string, + perp: Contract, + depositBond: Contract, + depositTranche: Contract; + +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")); + + const HelpersTester = await ethers.getContractFactory("HelpersTester"); + helper = await HelpersTester.deploy(); + await helper.deployed(); +} + +describe("HelpersTester", function () { + beforeEach(async () => { + await setupContracts(); + }); + + after(async function () { + await network.provider.send("hardhat_reset"); + }); + + describe("#timeToMatirity", function () { + let maturityDate: number, bondLength: number, bond: Contract; + beforeEach(async function () { + bondLength = 86400; + bond = await createBondWithFactory(bondFactory, collateralToken, [1000], bondLength); + maturityDate = (await bond.maturityDate()).toNumber(); + }); + + describe("when bond is NOT mature", function () { + it("should return the time to maturity", async function () { + await TimeHelpers.setNextBlockTimestamp(maturityDate - bondLength / 2); + expect(await helper.secondsToMaturity(bond.address)).to.eq(bondLength / 2); + }); + }); + + describe("when bond is mature", function () { + it("should return the time to maturity", async function () { + await TimeHelpers.setNextBlockTimestamp(maturityDate + 1); + expect(await helper.secondsToMaturity(bond.address)).to.eq(0); + }); + }); + }); + + describe("#getTranches", function () { + it("should revert if bond has more than 2 tranches", async function () { + const bond = await createBondWithFactory(bondFactory, collateralToken, [200, 300, 500], 86400); + await expect(helper.getTranches(bond.address)).to.be.revertedWithCustomError(helper, "UnacceptableTrancheLength"); + }); + + it("should return the tranche data", async function () { + const bond = await createBondWithFactory(bondFactory, collateralToken, [498, 502], 86400); + const td = await helper.getTranches(bond.address); + expect(td.tranches.length).to.eq(2); + expect(td.trancheRatios.length).to.eq(2); + expect(td.trancheRatios[0]).to.eq(498); + expect(td.trancheRatios[1]).to.eq(502); + expect(td.tranches[0]).to.eq((await bond.tranches(0))[0]); + expect(td.tranches[1]).to.eq((await bond.tranches(1))[0]); + }); + }); + + describe("#trancheAt", function () { + it("should return the tranche when given index", async function () { + const bond = await createBondWithFactory(bondFactory, collateralToken, [100, 100, 100, 100, 100, 500], 86400); + for (let i = 0; i < 6; i++) { + expect(await helper.trancheAt(bond.address, i)).to.eq((await bond.tranches(i))[0]); + } + await expect(helper.trancheAt(bond.address, 7)).to.be.reverted; + }); + }); + + describe("#getSeniorTranche", function () { + it("should return the tranche when given index", async function () { + const bond = await createBondWithFactory(bondFactory, collateralToken, [300, 700], 86400); + const td = await helper.getTranches(bond.address); + expect(await helper.getSeniorTranche(bond.address)).to.eq(td.tranches[0]); + }); + }); + + describe("#getSeniorTrancheRatio", function () { + it("should return the tranche when given index", async function () { + const bond = await createBondWithFactory(bondFactory, collateralToken, [50, 950], 86400); + const ratio = await helper.getSeniorTrancheRatio(bond.address); + expect(ratio).to.eq(50); + }); + }); + + describe("#previewDeposit", function () { + let bond: Contract; + beforeEach(async function () { + bond = await createBondWithFactory(bondFactory, collateralToken, [500, 500], 86400); + }); + + describe("if bond is mature", function () { + it("should revert", async function () { + await bond.mature(); + await expect(helper.previewDeposit(bond.address, toFixedPtAmt("1000"))).to.be.revertedWithCustomError( + helper, + "UnacceptableDeposit", + ); + }); + }); + + describe("first deposit", function () { + it("should calculate the tranche balances after deposit", async function () { + const d = await helper.previewDeposit(bond.address, toFixedPtAmt("1000")); + expect(d[0].amount).to.eq(toFixedPtAmt("500")); + expect(d[1].amount).to.eq(toFixedPtAmt("500")); + }); + + it("should be consistent with deposit", async function () { + await depositIntoBond(bond, toFixedPtAmt("1000"), deployer); + const b = await getTrancheBalances(bond, deployerAddress); + expect(b[0]).to.eq(toFixedPtAmt("500")); + expect(b[1]).to.eq(toFixedPtAmt("500")); + }); + }); + + describe("later deposit", function () { + beforeEach(async function () { + await depositIntoBond(bond, toFixedPtAmt("1000"), deployer); + }); + + describe("with no supply change", function () { + beforeEach(async function () { + await rebase(collateralToken, rebaseOracle, 0); + }); + it("should calculate the tranche balances after deposit", async function () { + const d = await helper.previewDeposit(bond.address, toFixedPtAmt("1000")); + expect(d[0].amount).to.eq(toFixedPtAmt("500")); + expect(d[1].amount).to.eq(toFixedPtAmt("500")); + }); + + it("should be consistent with deposit", async function () { + await depositIntoBond(bond, toFixedPtAmt("1000"), deployer); + const b = await getTrancheBalances(bond, deployerAddress); + expect(b[0]).to.eq(toFixedPtAmt("1000")); // 500 + 500 + expect(b[1]).to.eq(toFixedPtAmt("1000")); + }); + }); + + describe("with supply increase", function () { + beforeEach(async function () { + await rebase(collateralToken, rebaseOracle, +0.25); + }); + it("should calculate the tranche balances after deposit", async function () { + const d = await helper.previewDeposit(bond.address, toFixedPtAmt("1000")); + expect(d[0].amount).to.eq(toFixedPtAmt("400")); + expect(d[1].amount).to.eq(toFixedPtAmt("400")); + }); + + it("should be consistent with deposit", async function () { + await depositIntoBond(bond, toFixedPtAmt("1000"), deployer); + const b = await getTrancheBalances(bond, deployerAddress); + expect(b[0]).to.eq(toFixedPtAmt("900")); // 500 + 400 + expect(b[1]).to.eq(toFixedPtAmt("900")); + }); + }); + + describe("with supply decrease", function () { + beforeEach(async function () { + await rebase(collateralToken, rebaseOracle, -0.5); + }); + it("should calculate the tranche balances after deposit", async function () { + const d = await helper.previewDeposit(bond.address, toFixedPtAmt("1000")); + expect(d[0].amount).to.eq(toFixedPtAmt("1000")); + expect(d[1].amount).to.eq(toFixedPtAmt("1000")); + }); + it("should be consistent with deposit", async function () { + await depositIntoBond(bond, toFixedPtAmt("1000"), deployer); + const b = await getTrancheBalances(bond, deployerAddress); + expect(b[0]).to.eq(toFixedPtAmt("1500")); // 500 + 1000 + expect(b[1]).to.eq(toFixedPtAmt("1500")); + }); + }); + }); + }); +}); + +describe("BondTranchesHelpers", function () { + beforeEach(async () => { + await setupContracts(); + }); + + after(async function () { + await network.provider.send("hardhat_reset"); + }); + + describe("#computeRedeemableTrancheAmounts", function () { + describe("when the user has all the tranches in the right proportions", function () { + async function checkRedeemableAmts( + trancheRatios: number[] = [], + amounts: string[] = [], + redemptionAmts: string[] = [], + ) { + const bond = await createBondWithFactory(bondFactory, collateralToken, trancheRatios, 86400); + await depositIntoBond(bond, toFixedPtAmt("1000"), deployer); + + const tranches = await getTranches(bond); + for (const a in amounts) { + await tranches[a].transfer(userAddress, toFixedPtAmt(amounts[a])); + } + const b = await helper["computeRedeemableTrancheAmounts(address,address)"](bond.address, userAddress); + for (const a in redemptionAmts) { + expect(b[1][a]).to.eq(toFixedPtAmt(redemptionAmts[a])); + } + if (b[1][0].gt("0")) { + await bond.connect(user).redeem(b[1]); + } + } + + describe("when the user has the entire supply", function () { + describe("[200,800]:[200,800]", async function () { + it("should calculate the amounts", async function () { + await checkRedeemableAmts([200, 800], ["200", "800"], ["200", "800"]); + }); + }); + }); + + describe("when the user does not have the entire supply", function () { + describe("[200,800]:[10, 15, 25]", async function () { + it("should calculate the amounts", async function () { + await checkRedeemableAmts([200, 800], ["10", "40"], ["10", "40"]); + }); + }); + }); + }); + + describe("when the user does not have tranches right proportions", function () { + async function checkRedeemableAmts( + trancheRatios: number[] = [], + amounts: string[] = [], + redemptionAmts: string[] = [], + ) { + const bond = await createBondWithFactory(bondFactory, collateralToken, trancheRatios, 86400); + const amt = amounts + .map((a, i) => toFixedPtAmt(a).mul("1000").div(trancheRatios[i])) + .reduce((m, a) => (m.gt(a) ? m : a), toFixedPtAmt("0")); + await depositIntoBond(bond, amt.add(toFixedPtAmt("1")), deployer); + + const tranches = await getTranches(bond); + for (const a in amounts) { + await tranches[a].transfer(userAddress, toFixedPtAmt(amounts[a])); + } + const b = await helper["computeRedeemableTrancheAmounts(address,address)"](bond.address, userAddress); + for (const a in redemptionAmts) { + expect(b[1][a]).to.eq(toFixedPtAmt(redemptionAmts[a])); + } + if (b[1][0].gt("0")) { + await bond.connect(user).redeem(b[1]); + } + } + + describe("[200,800]:[9, 40]", async function () { + it("should calculate the amounts", async function () { + await checkRedeemableAmts([200, 800], ["9", "40"], ["9", "36"]); + }); + }); + + describe("[200,800]:[10, 265]", async function () { + it("should calculate the amounts", async function () { + await checkRedeemableAmts([200, 800], ["10", "265"], ["10", "40"]); + }); + }); + + describe("[200,800]:[10, 32]", async function () { + it("should calculate the amounts", async function () { + await checkRedeemableAmts([200, 800], ["10", "32"], ["8", "32"]); + }); + }); + + describe("[200,800]:[100, 9]", async function () { + it("should calculate the amounts", async function () { + await checkRedeemableAmts([200, 800], ["100", "9"], ["2.25", "9"]); + }); + }); + + describe("[200,800]:[10, 0.8]", async function () { + it("should calculate the amounts", async function () { + await checkRedeemableAmts([200, 800], ["10", "0.8"], ["0.2", "0.8"]); + }); + }); + + describe("[200,800]:[10, 0]", async function () { + it("should calculate the amounts", async function () { + await checkRedeemableAmts([200, 800], ["10", "0"], ["0", "0"]); + }); + }); + + describe("[200,800]:[0, 40]", async function () { + it("should calculate the amounts", async function () { + await checkRedeemableAmts([200, 800], ["0", "40"], ["0", "0"]); + }); + }); + + describe("imperfect rounding", function () { + describe("[200,800]:[10, 22.461048491123254231]", async function () { + it("should calculate the amounts", async function () { + await checkRedeemableAmts( + [200, 800], + ["10", "22.461048491123254231"], + ["5.6152621227808134", "22.4610484911232536"], + ); + }); + }); + + describe("[200,800]:[1000e-18,801e-18]", async function () { + it("should calculate the amounts", async function () { + await checkRedeemableAmts([200, 800], ["1000e-18", "801e-18"], ["200e-18", "800e-18"]); + }); + }); + + describe("[200,800]:[1000e-18,1001e-18]", async function () { + it("should calculate the amounts", async function () { + await checkRedeemableAmts([200, 800], ["1000e-18", "1001e-18"], ["200e-18", "800e-18"]); + }); + }); + + describe("[200,800]:[1000e-18,1601e-18]", async function () { + it("should calculate the amounts", async function () { + await checkRedeemableAmts([200, 800], ["1000e-18", "1601e-18"], ["400e-18", "1600e-18"]); + }); + }); + + describe("[1,999]:[1000e-18,2001e-18]", async function () { + it("should calculate the amounts", async function () { + await checkRedeemableAmts([1, 999], ["1000e-18", "2001e-18"], ["2e-18", "1998e-18"]); + }); + }); + + describe("[1,999]:[5e-18,1]", async function () { + it("should calculate the amounts", async function () { + await checkRedeemableAmts([1, 999], ["5e-18", "1"], ["5e-18", "4995e-18"]); + }); + }); + + describe("[499,501]:[1232e-18,1]", async function () { + it("should calculate the amounts", async function () { + await checkRedeemableAmts([499, 501], ["1232e-18", "1"], ["998e-18", "1002e-18"]); + }); + }); + + describe("[499,501]:[1,499e-18]", async function () { + it("should calculate the amounts", async function () { + await checkRedeemableAmts([499, 501], ["1", "499e-18"], ["0", "0"]); + }); + }); + + describe("[499,501]:[13224e-18]", async function () { + it("should calculate the amounts", async function () { + await checkRedeemableAmts([499, 501], ["1", "1322e-18"], ["998e-18", "1002e-18"]); + }); + }); + }); + }); + }); + + describe("#computeRedeemableTrancheAmounts", function () { + let bond: Contract; + describe("when balances are in the right proportions", function () { + async function checkRedeemableAmts( + trancheRatios: number[] = [], + amounts: string[] = [], + redemptionAmts: string[] = [], + ) { + bond = await createBondWithFactory(bondFactory, collateralToken, trancheRatios, 86400); + const b = await helper["computeRedeemableTrancheAmounts(address,uint256[])"]( + bond.address, + amounts.map(toFixedPtAmt), + ); + for (const a in redemptionAmts) { + expect(b[1][a]).to.eq(toFixedPtAmt(redemptionAmts[a])); + } + } + + describe("[200,800]:[200,800]", async function () { + it("should calculate the amounts", async function () { + await checkRedeemableAmts([200, 800], ["200", "800"], ["200", "800"]); + }); + }); + + describe("[200,800]:[6, 9, 15]", async function () { + it("should calculate the amounts", async function () { + await checkRedeemableAmts([200, 800], ["6", "24"], ["6", "24"]); + }); + }); + + describe("[200,800]:[202, 808]", async function () { + it("should calculate the amounts", async function () { + await checkRedeemableAmts([200, 800], ["202", "808"], ["202", "808"]); + }); + }); + + describe("when the bond has a balance", async function () { + await depositIntoBond(bond, toFixedPtAmt("1000"), deployer); + + describe("[200,800]:[202, 808]", async function () { + it("should calculate the amounts", async function () { + await checkRedeemableAmts([200, 800], ["202", "808"], ["202", "808"]); + }); + }); + }); + }); + + describe("when balances are not right proportions", function () { + async function checkRedeemableAmts( + trancheRatios: number[] = [], + amounts: string[] = [], + redemptionAmts: string[] = [], + ) { + const bond = await createBondWithFactory(bondFactory, collateralToken, trancheRatios, 86400); + const amt = amounts + .map((a, i) => toFixedPtAmt(a).mul("1000").div(trancheRatios[i])) + .reduce((m, a) => (m.gt(a) ? m : a), toFixedPtAmt("0")); + await depositIntoBond(bond, amt.add(toFixedPtAmt("1")), deployer); + + const b = await helper["computeRedeemableTrancheAmounts(address,uint256[])"]( + bond.address, + amounts.map(toFixedPtAmt), + ); + for (const a in redemptionAmts) { + expect(b[1][a]).to.eq(toFixedPtAmt(redemptionAmts[a])); + } + } + + describe("[200,800]:[9, 40]", async function () { + it("should calculate the amounts", async function () { + await checkRedeemableAmts([200, 800], ["9", "40"], ["9", "36"]); + }); + }); + + describe("[200,800]:[10, 265]", async function () { + it("should calculate the amounts", async function () { + await checkRedeemableAmts([200, 800], ["10", "265"], ["10", "40"]); + }); + }); + + describe("[200,800]:[10, 32]", async function () { + it("should calculate the amounts", async function () { + await checkRedeemableAmts([200, 800], ["10", "32"], ["8", "32"]); + }); + }); + + describe("[200,800]:[100, 9]", async function () { + it("should calculate the amounts", async function () { + await checkRedeemableAmts([200, 800], ["100", "9"], ["2.25", "9"]); + }); + }); + + describe("[200,800]:[10, 0.8]", async function () { + it("should calculate the amounts", async function () { + await checkRedeemableAmts([200, 800], ["10", "0.8"], ["0.2", "0.8"]); + }); + }); + + describe("[200,800]:[10, 0]", async function () { + it("should calculate the amounts", async function () { + await checkRedeemableAmts([200, 800], ["10", "0"], ["0", "0"]); + }); + }); + + describe("[200,800]:[0, 40]", async function () { + it("should calculate the amounts", async function () { + await checkRedeemableAmts([200, 800], ["0", "40"], ["0", "0"]); + }); + }); + + describe("imperfect rounding", function () { + describe("[200,800]:[10, 22.461048491123254231]", async function () { + it("should calculate the amounts", async function () { + await checkRedeemableAmts( + [200, 800], + ["10", "22.461048491123254231"], + ["5.6152621227808134", "22.4610484911232536"], + ); + }); + }); + + describe("[200,800]:[1000e-18,801e-18]", async function () { + it("should calculate the amounts", async function () { + await checkRedeemableAmts([200, 800], ["1000e-18", "801e-18"], ["200e-18", "800e-18"]); + }); + }); + + describe("[200,800]:[1000e-18,1001e-18]", async function () { + it("should calculate the amounts", async function () { + await checkRedeemableAmts([200, 800], ["1000e-18", "1001e-18"], ["200e-18", "800e-18"]); + }); + }); + + describe("[200,800]:[1000e-18,1601e-18]", async function () { + it("should calculate the amounts", async function () { + await checkRedeemableAmts([200, 800], ["1000e-18", "1601e-18"], ["400e-18", "1600e-18"]); + }); + }); + + describe("[1,999]:[1000e-18,2001e-18]", async function () { + it("should calculate the amounts", async function () { + await checkRedeemableAmts([1, 999], ["1000e-18", "2001e-18"], ["2e-18", "1998e-18"]); + }); + }); + + describe("[1,999]:[5e-18,1]", async function () { + it("should calculate the amounts", async function () { + await checkRedeemableAmts([1, 999], ["5e-18", "1"], ["5e-18", "4995e-18"]); + }); + }); + + describe("[499,501]:[1232e-18,1]", async function () { + it("should calculate the amounts", async function () { + await checkRedeemableAmts([499, 501], ["1232e-18", "1"], ["998e-18", "1002e-18"]); + }); + }); + + describe("[499,501]:[1,499e-18]", async function () { + it("should calculate the amounts", async function () { + await checkRedeemableAmts([499, 501], ["1", "499e-18"], ["0", "0"]); + }); + }); + + describe("[499,501]:[13224e-18]", async function () { + it("should calculate the amounts", async function () { + await checkRedeemableAmts([499, 501], ["1", "1322e-18"], ["998e-18", "1002e-18"]); + }); + }); + }); + }); + }); +}); + +describe("TrancheHelpers", function () { + beforeEach(async () => { + await setupContracts(); + }); + + after(async function () { + await network.provider.send("hardhat_reset"); + }); + + describe("#getTrancheCollateralizations", function () { + let bond: Contract, bondLength: number, tranches: Contract[]; + beforeEach(async function () { + bondLength = 86400; + bond = await createBondWithFactory(bondFactory, collateralToken, [250, 750], bondLength); + tranches = await getTranches(bond); + await depositIntoBond(bond, toFixedPtAmt("1000"), deployer); + }); + + describe("when bond has too few tranches", function () { + it("should return 0", async function () { + const bond = await createBondWithFactory(bondFactory, collateralToken, [1000], bondLength); + const tranches = await getTranches(bond); + await expect(helper.getTrancheCollateralizations(tranches[0].address)).to.be.revertedWithCustomError( + helper, + "UnacceptableTrancheLength", + ); + }); + }); + + describe("when bond has too few tranches", function () { + it("should return 0", async function () { + const bond = await createBondWithFactory(bondFactory, collateralToken, [100, 200, 700], bondLength); + const tranches = await getTranches(bond); + await expect(helper.getTrancheCollateralizations(tranches[0].address)).to.be.revertedWithCustomError( + helper, + "UnacceptableTrancheLength", + ); + }); + }); + + describe("when bond has no deposits", function () { + it("should return 0", async function () { + const bond = await createBondWithFactory(bondFactory, collateralToken, [333, 667], bondLength); + const tranches = await getTranches(bond); + + const t0 = await helper.getTrancheCollateralizations(tranches[0].address); + expect(t0[0]).to.eq("0"); + expect(t0[1]).to.eq("0"); + + const t1 = await helper.getTrancheCollateralizations(tranches[1].address); + expect(t1[0]).to.eq("0"); + expect(t1[1]).to.eq("0"); + }); + }); + + describe("when bond not mature", function () { + describe("when no change in supply", function () { + it("should calculate the balances", async function () { + const t0 = await helper.getTrancheCollateralizations(tranches[0].address); + expect(t0[0]).to.eq(toFixedPtAmt("250")); + expect(t0[1]).to.eq(toFixedPtAmt("250")); + + const t1 = await helper.getTrancheCollateralizations(tranches[1].address); + expect(t1[0]).to.eq(toFixedPtAmt("750")); + expect(t1[1]).to.eq(toFixedPtAmt("750")); + }); + }); + + describe("when supply increases above bond threshold", function () { + it("should calculate the balances", async function () { + await rebase(collateralToken, rebaseOracle, 0.1); + const t0 = await helper.getTrancheCollateralizations(tranches[0].address); + expect(t0[0]).to.eq(toFixedPtAmt("250")); + expect(t0[1]).to.eq(toFixedPtAmt("250")); + const t1 = await helper.getTrancheCollateralizations(tranches[1].address); + expect(t1[0]).to.eq(toFixedPtAmt("850")); + expect(t1[1]).to.eq(toFixedPtAmt("750")); + }); + }); + + describe("when supply decreases below bond threshold", function () { + it("should calculate the balances", async function () { + await rebase(collateralToken, rebaseOracle, -0.1); + const t0 = await helper.getTrancheCollateralizations(tranches[0].address); + expect(t0[0]).to.eq(toFixedPtAmt("250")); + expect(t0[1]).to.eq(toFixedPtAmt("250")); + const t1 = await helper.getTrancheCollateralizations(tranches[1].address); + expect(t1[0]).to.eq(toFixedPtAmt("650")); + expect(t1[1]).to.eq(toFixedPtAmt("750")); + }); + }); + + describe("when supply decreases below junior threshold", function () { + it("should calculate the balances", async function () { + await rebase(collateralToken, rebaseOracle, -0.8); + const t0 = await helper.getTrancheCollateralizations(tranches[0].address); + expect(t0[0]).to.eq(toFixedPtAmt("200")); + expect(t0[1]).to.eq(toFixedPtAmt("250")); + const t1 = await helper.getTrancheCollateralizations(tranches[1].address); + expect(t1[0]).to.eq("0"); + expect(t1[1]).to.eq(toFixedPtAmt("750")); + }); + }); + }); + + describe("when bond is mature", function () { + beforeEach(async function () { + await TimeHelpers.increaseTime(bondLength); + await bond.mature(); // NOTE: Any rebase after maturity goes directly to the tranches + }); + + describe("when no change in supply", function () { + it("should calculate the balances", async function () { + const t0 = await helper.getTrancheCollateralizations(tranches[0].address); + expect(t0[0]).to.eq(toFixedPtAmt("250")); + expect(t0[1]).to.eq(toFixedPtAmt("250")); + const t1 = await helper.getTrancheCollateralizations(tranches[1].address); + expect(t1[0]).to.eq(toFixedPtAmt("750")); + expect(t1[1]).to.eq(toFixedPtAmt("750")); + }); + }); + + describe("when supply increases", function () { + it("should calculate the balances", async function () { + await rebase(collateralToken, rebaseOracle, 0.1); + const t0 = await helper.getTrancheCollateralizations(tranches[0].address); + expect(t0[0]).to.eq(toFixedPtAmt("275")); + expect(t0[1]).to.eq(toFixedPtAmt("250")); + const t1 = await helper.getTrancheCollateralizations(tranches[1].address); + expect(t1[0]).to.eq(toFixedPtAmt("825")); + expect(t1[1]).to.eq(toFixedPtAmt("750")); + }); + }); + + describe("when supply decreases", function () { + it("should calculate the balances", async function () { + await rebase(collateralToken, rebaseOracle, -0.1); + const t0 = await helper.getTrancheCollateralizations(tranches[0].address); + expect(t0[0]).to.eq(toFixedPtAmt("225")); + expect(t0[1]).to.eq(toFixedPtAmt("250")); + const t1 = await helper.getTrancheCollateralizations(tranches[1].address); + expect(t1[0]).to.eq(toFixedPtAmt("675")); + expect(t1[1]).to.eq(toFixedPtAmt("750")); + }); + }); + }); + }); +}); + +describe("PerpHelpers", function () { + beforeEach(async () => { + await setupContracts(); + + const PerpetualTranche = await ethers.getContractFactory("PerpetualTranche"); + perp = await smock.fake(PerpetualTranche); + + const BondController = await getContractFactoryFromExternalArtifacts("BondController"); + depositBond = await smock.fake(BondController); + + const Tranche = await getContractFactoryFromExternalArtifacts("Tranche"); + depositTranche = await smock.fake(Tranche); + + await perp.getDepositBond.returns(depositBond.address); + await perp.totalSupply.returns(toFixedPtAmt("100")); + + await mintCollteralToken(collateralToken, toFixedPtAmt("500"), deployer); + await collateralToken.transfer(depositBond.address, toFixedPtAmt("500")); + await depositBond.collateralToken.returns(collateralToken.address); + await depositBond.tranches.whenCalledWith(0).returns([depositTranche.address, 200]); + await depositBond.totalDebt.returns(toFixedPtAmt("500")); + await depositTranche.totalSupply.returns(toFixedPtAmt("100")); + }); + + after(async function () { + await network.provider.send("hardhat_reset"); + }); + + describe("when perp price = 1", async function () { + describe("when bond cdr = 1", async function () { + it("should compute the underlying amount", async function () { + const r = await helper.callStatic.estimateUnderlyingAmtToTranche( + perp.address, + toFixedPtAmt("100"), + toFixedPtAmt("10"), + ); + expect(r[0]).to.eq(toFixedPtAmt("50")); + expect(r[1]).to.eq(toFixedPtAmt("10")); + }); + }); + + describe("when bond cdr > 1", async function () { + beforeEach(async function () { + await rebase(collateralToken, rebaseOracle, 0.1); + }); + it("should compute the underlying amount", async function () { + const r = await helper.callStatic.estimateUnderlyingAmtToTranche( + perp.address, + toFixedPtAmt("100"), + toFixedPtAmt("10"), + ); + expect(r[0]).to.eq(toFixedPtAmt("55")); + expect(r[1]).to.eq(toFixedPtAmt("10")); + }); + }); + + describe("when bond cdr < 1", async function () { + beforeEach(async function () { + await rebase(collateralToken, rebaseOracle, -0.1); + }); + it("should compute the underlying amount", async function () { + const r = await helper.callStatic.estimateUnderlyingAmtToTranche( + perp.address, + toFixedPtAmt("100"), + toFixedPtAmt("10"), + ); + expect(r[0]).to.eq(toFixedPtAmt("45")); + expect(r[1]).to.eq(toFixedPtAmt("10")); + }); + }); + + describe("when bond cdr < 1 and seniors are impaired", async function () { + beforeEach(async function () { + await rebase(collateralToken, rebaseOracle, -0.9); + }); + it("should compute the underlying amount", async function () { + const r = await helper.callStatic.estimateUnderlyingAmtToTranche( + perp.address, + toFixedPtAmt("100"), + toFixedPtAmt("10"), + ); + expect(r[0]).to.eq(toFixedPtAmt("10")); + expect(r[1]).to.eq(toFixedPtAmt("20")); + }); + }); + }); + + describe("when perp price > 1", async function () { + describe("when bond cdr = 1", async function () { + it("should compute the underlying amount", async function () { + const r = await helper.callStatic.estimateUnderlyingAmtToTranche( + perp.address, + toFixedPtAmt("200"), + toFixedPtAmt("10"), + ); + expect(r[0]).to.eq(toFixedPtAmt("100")); + expect(r[1]).to.eq(toFixedPtAmt("20")); + }); + }); + + describe("when bond cdr > 1", async function () { + beforeEach(async function () { + await rebase(collateralToken, rebaseOracle, 0.1); + }); + it("should compute the underlying amount", async function () { + const r = await helper.callStatic.estimateUnderlyingAmtToTranche( + perp.address, + toFixedPtAmt("200"), + toFixedPtAmt("10"), + ); + expect(r[0]).to.eq(toFixedPtAmt("110")); + expect(r[1]).to.eq(toFixedPtAmt("20")); + }); + }); + + describe("when bond cdr < 1", async function () { + beforeEach(async function () { + await rebase(collateralToken, rebaseOracle, -0.1); + }); + it("should compute the underlying amount", async function () { + const r = await helper.callStatic.estimateUnderlyingAmtToTranche( + perp.address, + toFixedPtAmt("200"), + toFixedPtAmt("10"), + ); + expect(r[0]).to.eq(toFixedPtAmt("90")); + expect(r[1]).to.eq(toFixedPtAmt("20")); + }); + }); + + describe("when bond cdr < 1 and seniors are impaired", async function () { + beforeEach(async function () { + await rebase(collateralToken, rebaseOracle, -0.9); + }); + it("should compute the underlying amount", async function () { + const r = await helper.callStatic.estimateUnderlyingAmtToTranche( + perp.address, + toFixedPtAmt("200"), + toFixedPtAmt("10"), + ); + expect(r[0]).to.eq(toFixedPtAmt("20")); + expect(r[1]).to.eq(toFixedPtAmt("40")); + }); + }); + }); + + describe("when perp price < 1", async function () { + describe("when bond cdr = 1", async function () { + it("should compute the underlying amount", async function () { + const r = await helper.callStatic.estimateUnderlyingAmtToTranche( + perp.address, + toFixedPtAmt("50"), + toFixedPtAmt("10"), + ); + expect(r[0]).to.eq(toFixedPtAmt("25")); + expect(r[1]).to.eq(toFixedPtAmt("5")); + }); + }); + + describe("when bond cdr > 1", async function () { + beforeEach(async function () { + await rebase(collateralToken, rebaseOracle, 0.1); + }); + it("should compute the underlying amount", async function () { + const r = await helper.callStatic.estimateUnderlyingAmtToTranche( + perp.address, + toFixedPtAmt("50"), + toFixedPtAmt("10"), + ); + expect(r[0]).to.eq(toFixedPtAmt("27.5")); + expect(r[1]).to.eq(toFixedPtAmt("5")); + }); + }); + + describe("when bond cdr < 1", async function () { + beforeEach(async function () { + await rebase(collateralToken, rebaseOracle, -0.1); + }); + it("should compute the underlying amount", async function () { + const r = await helper.callStatic.estimateUnderlyingAmtToTranche( + perp.address, + toFixedPtAmt("50"), + toFixedPtAmt("10"), + ); + expect(r[0]).to.eq(toFixedPtAmt("22.5")); + expect(r[1]).to.eq(toFixedPtAmt("5")); + }); + }); + + describe("when bond cdr < 1 and seniors are impaired", async function () { + beforeEach(async function () { + await rebase(collateralToken, rebaseOracle, -0.9); + }); + it("should compute the underlying amount", async function () { + const r = await helper.callStatic.estimateUnderlyingAmtToTranche( + perp.address, + toFixedPtAmt("50"), + toFixedPtAmt("10"), + ); + expect(r[0]).to.eq(toFixedPtAmt("5")); + expect(r[1]).to.eq(toFixedPtAmt("10")); + }); + }); + }); + + describe("imperfect rounding", async function () { + it("should compute the underlying amount", async function () { + const r = await helper.callStatic.estimateUnderlyingAmtToTranche( + perp.address, + toFixedPtAmt("100"), + toFixedPtAmt("0.999999999999999999"), + ); + expect(r[0]).to.eq(toFixedPtAmt("4.999999999999999995")); + expect(r[1]).to.eq(toFixedPtAmt("0.999999999999999999")); + }); + }); + + describe("when perp supply is zero", function () { + beforeEach(async function () { + await perp.totalSupply.returns("0"); + }); + + describe("when bond cdr = 1", async function () { + it("should compute the underlying amount", async function () { + const r = await helper.callStatic.estimateUnderlyingAmtToTranche( + perp.address, + toFixedPtAmt("100"), + toFixedPtAmt("10"), + ); + expect(r[0]).to.eq(toFixedPtAmt("50")); + expect(r[1]).to.eq(toFixedPtAmt("10")); + }); + }); + + describe("when bond cdr > 1", async function () { + beforeEach(async function () { + await rebase(collateralToken, rebaseOracle, 0.1); + }); + it("should compute the underlying amount", async function () { + const r = await helper.callStatic.estimateUnderlyingAmtToTranche( + perp.address, + toFixedPtAmt("100"), + toFixedPtAmt("10"), + ); + expect(r[0]).to.eq(toFixedPtAmt("55")); + expect(r[1]).to.eq(toFixedPtAmt("10")); + }); + }); + + describe("when bond cdr < 1", async function () { + beforeEach(async function () { + await rebase(collateralToken, rebaseOracle, -0.1); + }); + it("should compute the underlying amount", async function () { + const r = await helper.callStatic.estimateUnderlyingAmtToTranche( + perp.address, + toFixedPtAmt("100"), + toFixedPtAmt("10"), + ); + expect(r[0]).to.eq(toFixedPtAmt("45")); + expect(r[1]).to.eq(toFixedPtAmt("10")); + }); + }); + + describe("when bond cdr < 1 and seniors are impaired", async function () { + beforeEach(async function () { + await rebase(collateralToken, rebaseOracle, -0.9); + }); + it("should compute the underlying amount", async function () { + const r = await helper.callStatic.estimateUnderlyingAmtToTranche( + perp.address, + toFixedPtAmt("100"), + toFixedPtAmt("10"), + ); + expect(r[0]).to.eq(toFixedPtAmt("10")); + expect(r[1]).to.eq(toFixedPtAmt("20")); + }); + }); + }); + + describe("when deposit bond has no deposits yet", function () { + beforeEach(async function () { + await depositBond.totalDebt.returns("0"); + await depositTranche.totalSupply.returns("0"); + }); + + it("should compute the underlying amount", async function () { + const r = await helper.callStatic.estimateUnderlyingAmtToTranche( + perp.address, + toFixedPtAmt("100"), + toFixedPtAmt("10"), + ); + expect(r[0]).to.eq(toFixedPtAmt("50")); + expect(r[1]).to.eq(toFixedPtAmt("10")); + }); + }); +}); diff --git a/spot-contracts/test/_utils/Sigmoid.ts b/spot-contracts/test/_utils/Sigmoid.ts new file mode 100644 index 00000000..550e9f8a --- /dev/null +++ b/spot-contracts/test/_utils/Sigmoid.ts @@ -0,0 +1,130 @@ +import { expect } from "chai"; +import { ethers } from "hardhat"; +import { Contract, BigNumber } from "ethers"; + +import { toPercFixedPtAmt } from "../helpers"; + +describe("Sigmoid", function () { + let math: Contract; + async function cmp(x, y, lower, upper, growth) { + expect( + await math.compute( + toPercFixedPtAmt(`${x}`), + toPercFixedPtAmt(`${lower}`), + toPercFixedPtAmt(`${upper}`), + toPercFixedPtAmt(`${growth}`), + toPercFixedPtAmt("1.0"), + ), + ).to.eq(toPercFixedPtAmt(`${y}`)); + } + + describe("compute", function () { + before(async function () { + const MathTester = await ethers.getContractFactory("MathTester"); + math = await MathTester.deploy(); + await math.deployed(); + }); + it("should return sigmoid(x)", async function () { + await cmp(0, 0, -0.01, 0.05, 0); + await cmp(1, 0, -0.01, 0.05, 0); + await cmp(0, -0.00925926, -0.01, 0.05, 4); + await cmp(0.1, -0.00902227, -0.01, 0.05, 4); + await cmp(0.25, -0.00853659, -0.01, 0.05, 4); + await cmp(0.5, -0.00714286, -0.01, 0.05, 4); + await cmp(0.75, -0.00454546, -0.01, 0.05, 4); + await cmp(0.8, -0.00374551, -0.01, 0.05, 4); + await cmp(0.85, -0.00297903, -0.01, 0.05, 4); + await cmp(0.9, -0.00198311, -0.01, 0.05, 4); + await cmp(0.95, -0.00103668, -0.01, 0.05, 4); + await cmp(0.98, -0.00035583, -0.01, 0.05, 4); + await cmp(0.99, -0.00017921, -0.01, 0.05, 4); + await cmp(1, 0, -0.01, 0.05, 4); + await cmp(1.01, 0.00018181, -0.01, 0.05, 4); + await cmp(1.02, 0.00036624, -0.01, 0.05, 4); + await cmp(1.1, 0.00235705, -0.01, 0.05, 4); + await cmp(1.2, 0.00534796, -0.01, 0.05, 4); + await cmp(1.3, 0.00877749, -0.01, 0.05, 4); + await cmp(1.5, 0.01666666, -0.01, 0.05, 4); + await cmp(2, 0.03571428, -0.01, 0.05, 4); + await cmp(3, 0.04885057, -0.01, 0.05, 4); + await cmp(4, 0.04992684, -0.01, 0.05, 4); + await cmp(10, 0.05, -0.01, 0.05, 4); + await cmp(-10, -0.01, -0.01, 0.05, 1000); + await cmp(10, 0.05, -0.01, 0.05, 1000); + }); + }); + + describe("twoPower", function () { + before(async function () { + const MathTester = await ethers.getContractFactory("MathTester"); + math = await MathTester.deploy(); + await math.deployed(); + }); + + const decimals18 = BigNumber.from("1000000000000000000"); + const decimals10 = BigNumber.from("10000000000"); + it("2^0", async function () { + const e = BigNumber.from(0); + const one = BigNumber.from(1).mul(decimals18); + expect(await math.twoPower(e, one)).to.eq(one); + }); + it("2^1", async function () { + const e = BigNumber.from(1).mul(decimals18); + const one = BigNumber.from(1).mul(decimals18); + const result = BigNumber.from(2).mul(decimals18); + expect(await math.twoPower(e, one)).to.eq(result); + }); + it("2^30", async function () { + const e = BigNumber.from(30).mul(decimals18); + const one = BigNumber.from(1).mul(decimals18); + const result = BigNumber.from(2 ** 30).mul(decimals18); + expect(await math.twoPower(e, one)).to.eq(result); + }); + it("2^2.5", async function () { + const e = BigNumber.from("25000000000"); + const one = BigNumber.from(1).mul(decimals10); + const result = BigNumber.from("56568542494"); + expect(await math.twoPower(e, one)).to.eq(result); + }); + it("2^2.25", async function () { + const e = BigNumber.from("22500000000"); + const one = BigNumber.from(1).mul(decimals10); + const result = BigNumber.from("47568284600"); + expect(await math.twoPower(e, one)).to.eq(result); + }); + it("2^-2.25", async function () { + const e = BigNumber.from("-22500000000"); + const one = BigNumber.from(1).mul(decimals10); + const result = BigNumber.from("2102241038"); + expect(await math.twoPower(e, one)).to.eq(result); + }); + it("2^-0.6", async function () { + const e = BigNumber.from("-6000000000"); + const one = BigNumber.from(1).mul(decimals10); + const result = BigNumber.from("6626183216"); + expect(await math.twoPower(e, one)).to.eq(result); + }); + it("2^2.96875", async function () { + const e = BigNumber.from("29687500000"); + const one = BigNumber.from(1).mul(decimals10); + const result = BigNumber.from("78285764964"); + expect(await math.twoPower(e, one)).to.eq(result); + }); + it("2^2.99", async function () { + const e = BigNumber.from("29900000000"); + const one = BigNumber.from(1).mul(decimals10); + const result = BigNumber.from("78285764964"); + expect(await math.twoPower(e, one)).to.eq(result); + }); + it("should fail on too small exponents", async function () { + const e = BigNumber.from("-1011000000000"); + const one = BigNumber.from(1).mul(decimals10); + await expect(math.twoPower(e, one)).to.be.revertedWithCustomError(math, "ExpTooLarge"); + }); + it("should fail on too large exponents", async function () { + const e = BigNumber.from("1011000000000"); + const one = BigNumber.from(1).mul(decimals10); + await expect(math.twoPower(e, one)).to.be.revertedWithCustomError(math, "ExpTooLarge"); + }); + }); +}); diff --git a/spot-contracts/test/_utils/SignedMath.ts b/spot-contracts/test/_utils/SignedMath.ts deleted file mode 100644 index c4f722c7..00000000 --- a/spot-contracts/test/_utils/SignedMath.ts +++ /dev/null @@ -1,22 +0,0 @@ -import { expect } from "chai"; -import { ethers } from "hardhat"; -import { constants } from "ethers"; - -describe("SignedMath", function () { - describe("sign", function () { - it("should return sign", async function () { - const MathTester = await ethers.getContractFactory("MathTester"); - const math = await MathTester.deploy(); - await math.deployed(); - expect(await math.sign("0")).to.eq(0); - - expect(await math.sign("-1")).to.eq(-1); - expect(await math.sign(constants.MinInt256)).to.eq(-1); - expect(await math.sign("-1123213132112")).to.eq(-1); - - expect(await math.sign("1")).to.eq(1); - expect(await math.sign("1112433423242")).to.eq(1); - expect(await math.sign(constants.MaxInt256)).to.eq(1); - }); - }); -}); diff --git a/spot-contracts/test/helpers.ts b/spot-contracts/test/helpers.ts index 7b4bf8d9..52b40ed6 100644 --- a/spot-contracts/test/helpers.ts +++ b/spot-contracts/test/helpers.ts @@ -9,25 +9,25 @@ use(smock.matchers); const TOKEN_DECIMALS = 18; const PRICE_DECIMALS = 8; const DISCOUNT_DECIMALS = 18; +const PERC_DECIMALS = 8; const sciParseFloat = (a: string): BigNumber => (a.includes("e") ? parseFloat(a).toFixed(18) : a); export const toFixedPtAmt = (a: string): BigNumber => utils.parseUnits(sciParseFloat(a), TOKEN_DECIMALS); export const toPriceFixedPtAmt = (a: string): BigNumber => utils.parseUnits(sciParseFloat(a), PRICE_DECIMALS); export const toDiscountFixedPtAmt = (a: string): BigNumber => utils.parseUnits(sciParseFloat(a), DISCOUNT_DECIMALS); +export const toPercFixedPtAmt = (a: string): BigNumber => utils.parseUnits(sciParseFloat(a), PERC_DECIMALS); const ORACLE_BASE_PRICE = toPriceFixedPtAmt("1"); const EXTERNAL_ARTIFACTS_PATH = path.join(__dirname, "/../external-artifacts"); -async function getContractFactoryFromExternalArtifacts(name: string): Promise { +export const getContractFactoryFromExternalArtifacts = (name: string): Promise => { const artifact = JSON.parse(fs.readFileSync(`${EXTERNAL_ARTIFACTS_PATH}/${name}.json`).toString()); return ethers.getContractFactoryFromArtifact(artifact); -} +}; export const TimeHelpers = { secondsFromNow: async (secondsFromNow: number): Promise => { - const res = await hre.network.provider.send("eth_getBlockByNumber", ["latest", false]); - const timestamp = parseInt(res.timestamp, 16); - return timestamp + secondsFromNow; + return (await TimeHelpers.currentTime()) + secondsFromNow; }, increaseTime: async (seconds: number): Promise => { @@ -39,6 +39,12 @@ export const TimeHelpers = { await ethers.provider.send("evm_setNextBlockTimestamp", [timestamp]); await hre.network.provider.send("evm_mine"); }, + + currentTime: async (): Promise => { + const res = await hre.network.provider.send("eth_getBlockByNumber", ["latest", false]); + const timestamp = parseInt(res.timestamp, 16); + return timestamp; + }, }; // Rebasing collateral token (button tokens) @@ -171,10 +177,18 @@ export const getTrancheBalances = async (bond: Contract, user: string): Promise< return balances; }; +export const timeToMaturity = async (bond: Contract): Promise => { + return (await bond.maturityDate()).toNumber() - (await TimeHelpers.currentTime()); +}; + export const getDepositBond = async (perp: Contract): Contract => { return bondAt(await perp.callStatic.getDepositBond()); }; +export const advanceTime = async (time: number): Promise => { + return TimeHelpers.increaseTime(time); +}; + export const advancePerpQueue = async (perp: Contract, time: number): Promise => { await TimeHelpers.increaseTime(time); return perp.updateState(); @@ -208,13 +222,15 @@ export const logReserveComposition = async (perp: Contract) => { console.log("Reserve count", count); for (let i = 0; i < count; i++) { const token = await ERC20.attach(await perp.callStatic.getReserveAt(i)); + const ONE = BigNumber.from(`${10 ** (await perp.decimals())}`); + const tokenVal = await perp.callStatic.getReserveTokenValue(token.address); + const tokenBal = await perp.callStatic.getReserveTokenBalance(token.address); + const tokenPrice = tokenBal.gt("0") ? tokenVal.mul(ONE).div(tokenBal) : BigNumber.from(0); console.log( i, 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()), + utils.formatUnits(await token.balanceOf(perp.address), await perp.decimals()), + utils.formatUnits(tokenPrice, await perp.decimals()), ); } }; @@ -237,7 +253,7 @@ export const checkReserveComposition = async (perp: Contract, tokens: Contract[] const reserveToken = ERC20.attach(await perp.callStatic.getReserveAt(j)); expect(tokenMap[reserveToken.address]).to.eq(true); if (checkBalances) { - expect(await reserveToken.balanceOf(await perp.reserve())).to.eq(tokenBalanceMap[reserveToken.address]); + expect(await reserveToken.balanceOf(perp.address)).to.eq(tokenBalanceMap[reserveToken.address]); } } await expect(perp.callStatic.getReserveAt(tokens.length)).to.be.reverted; @@ -254,9 +270,8 @@ export const getReserveTokens = async (perp: Contract) => { 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; + const count = await vault.assetCount(); + const assetCount = await vault.assetCount(); console.log("Asset count", count); const underlying = await ERC20.attach(await vault.underlying()); @@ -265,26 +280,18 @@ export const logVaultAssets = async (vault: Contract) => { 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)); + for (let i = 1; i < assetCount; i++) { + const token = await ERC20.attach(await vault.assetAt(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); + expect(await vault.assetCount()).to.eq(tokens.length); for (const i in tokens) { expect(await vault.vaultAssetBalance(tokens[i].address)).to.eq(balances[i]); } @@ -293,12 +300,8 @@ export const checkVaultAssetComposition = async (vault: Contract, tokens: Contra 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))); + for (let i = 0; i < (await vault.assetCount()); i++) { + assets.push(await ERC20.attach(await vault.assetAt(i))); } return assets; }; diff --git a/spot-contracts/test/PerpetualTranche.ts b/spot-contracts/test/perp/PerpetualTranche.ts similarity index 56% rename from spot-contracts/test/PerpetualTranche.ts rename to spot-contracts/test/perp/PerpetualTranche.ts index b5832150..54909b3d 100644 --- a/spot-contracts/test/PerpetualTranche.ts +++ b/spot-contracts/test/perp/PerpetualTranche.ts @@ -9,24 +9,20 @@ import { depositIntoBond, getTranches, toFixedPtAmt, - toDiscountFixedPtAmt, - toPriceFixedPtAmt, advancePerpQueue, bondAt, checkReserveComposition, TimeHelpers, rebase, advancePerpQueueToRollover, -} from "./helpers"; +} from "../helpers"; use(smock.matchers); let perp: Contract, collateralToken: Contract, rebaseOracle: Contract, issuer: Contract, - feeStrategy: Contract, - pricingStrategy: Contract, - discountStrategy: Contract, + feePolicy: Contract, deployer: Signer, otherUser: Signer; describe("PerpetualTranche", function () { @@ -43,36 +39,23 @@ describe("PerpetualTranche", function () { issuer = await smock.fake(BondIssuer); await issuer.collateral.returns(collateralToken.address); - const FeeStrategy = await ethers.getContractFactory("BasicFeeStrategy"); - feeStrategy = await smock.fake(FeeStrategy); - - 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.returns(toDiscountFixedPtAmt("1")); + const FeePolicy = await ethers.getContractFactory("FeePolicy"); + feePolicy = await smock.fake(FeePolicy); + await feePolicy.decimals.returns(8); 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, - ], + ["PerpetualTranche", "PERP", collateralToken.address, issuer.address, feePolicy.address], { - initializer: "init(string,string,address,address,address,address,address)", + initializer: "init(string,string,address,address,address)", }, ); + + const RolloverVault = await ethers.getContractFactory("RolloverVault"); + const vault = await smock.fake(RolloverVault); + await vault.getTVL.returns("0"); + await perp.updateVault(vault.address); }); afterEach(async function () { @@ -92,40 +75,22 @@ describe("PerpetualTranche", function () { it("should set ext service references", async function () { expect(await perp.bondIssuer()).to.eq(issuer.address); - expect(await perp.feeStrategy()).to.eq(feeStrategy.address); - expect(await perp.pricingStrategy()).to.eq(pricingStrategy.address); - expect(await perp.discountStrategy()).to.eq(discountStrategy.address); - }); - - it("should set collateral reference", async function () { - expect(await perp.collateral()).to.eq(collateralToken.address); + expect(await perp.feePolicy()).to.eq(feePolicy.address); }); - it("should set collateral discount", async function () { - expect(await perp.computeDiscount(collateralToken.address)).to.eq(toDiscountFixedPtAmt("1")); - }); - - it("should set fund pool references", async function () { - expect(await perp.reserve()).to.eq(perp.address); - expect(await perp.protocolFeeCollector()).to.eq(await deployer.getAddress()); - expect(await perp.perpERC20()).to.eq(perp.address); + it("should set underlying collateral reference", async function () { + expect(await perp.underlying()).to.eq(collateralToken.address); }); it("should initialize lists", async function () { expect(await perp.callStatic.getReserveCount()).to.eq(1); }); - it("should initialize tranche balances", async function () { - expect(await perp.callStatic.getReserveTrancheBalance(collateralToken.address)).to.eq(0); - expect(await perp.callStatic.getMatureTrancheBalance()).to.eq(0); - }); - it("should set hyper parameters", async function () { expect(await perp.minTrancheMaturitySec()).to.eq(1); expect(await perp.maxTrancheMaturitySec()).to.eq(constants.MaxUint256); expect(await perp.maxSupply()).to.eq(constants.MaxUint256); expect(await perp.maxMintAmtPerTranche()).to.eq(constants.MaxUint256); - expect(await perp.matureValueTargetPerc()).to.eq(0); }); it("should NOT be paused", async function () { @@ -229,75 +194,30 @@ describe("PerpetualTranche", function () { it("should update reference", async function () { expect(await perp.keeper()).to.eq(await otherUser.getAddress()); }); - it("should emit event", async function () { - await expect(tx) - .to.emit(perp, "UpdatedKeeper") - .withArgs(constants.AddressZero, await otherUser.getAddress()); - }); }); }); - describe("#authorizeRoller", function () { - let tx: Transaction; - + describe("#updateVault", function () { describe("when triggered by non-owner", function () { it("should revert", async function () { - await expect(perp.connect(otherUser).authorizeRoller(constants.AddressZero, true)).to.be.revertedWith( + await expect(perp.connect(otherUser).updateVault(constants.AddressZero)).to.be.revertedWith( "Ownable: caller is not the owner", ); }); }); - describe("when roller is not authorized and is authorized", function () { - beforeEach(async function () { - expect(await perp.authorizedRollersCount()).to.eq(0); - tx = perp.authorizeRoller(await otherUser.getAddress(), true); - await tx; - }); - it("should authorize roller", async function () { - expect(await perp.authorizedRollersCount()).to.eq(1); - expect(await perp.authorizedRollerAt(0)).to.eq(await otherUser.getAddress()); - }); - it("should emit event", async function () { - await expect(tx) - .to.emit(perp, "UpdatedRollerAuthorization") - .withArgs(await otherUser.getAddress(), true); - }); - }); - - describe("when roller is already authorized and is authorized again", function () { + describe("when vault reference is set", function () { + let tx: Transaction, vault: Contract; beforeEach(async function () { - await perp.authorizeRoller(await otherUser.getAddress(), true); - }); - it("should NOT revert", async function () { - await expect(perp.authorizeRoller(await otherUser.getAddress(), true)).not.to.be.reverted; - expect(await perp.authorizedRollersCount()).to.eq(1); - expect(await perp.authorizedRollerAt(0)).to.eq(await otherUser.getAddress()); - }); - }); + const RolloverVault = await ethers.getContractFactory("RolloverVault"); + vault = await smock.fake(RolloverVault); + await vault.getTVL.returns(0); - describe("when roller is not authorized and is unauthorized", function () { - it("should NOT revert", async function () { - expect(await perp.authorizedRollersCount()).to.eq(0); - await expect(perp.authorizeRoller(await otherUser.getAddress(), false)).not.to.be.reverted; - expect(await perp.authorizedRollersCount()).to.eq(0); - }); - }); - - describe("when roller is authorized and is unauthorized", function () { - beforeEach(async function () { - await perp.authorizeRoller(await otherUser.getAddress(), true); - expect(await perp.authorizedRollersCount()).to.eq(1); - tx = perp.authorizeRoller(await otherUser.getAddress(), false); + tx = perp.connect(deployer).updateVault(vault.address); await tx; }); - it("should unauthorize roller", async function () { - expect(await perp.authorizedRollersCount()).to.eq(0); - }); - it("should emit event", async function () { - await expect(tx) - .to.emit(perp, "UpdatedRollerAuthorization") - .withArgs(await otherUser.getAddress(), false); + it("should update vault reference", async function () { + expect(await perp.vault()).to.eq(vault.address); }); }); }); @@ -313,15 +233,6 @@ describe("PerpetualTranche", function () { }); }); - describe("when set address is NOT valid", function () { - it("should revert", async function () { - 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("BondIssuer"); @@ -333,9 +244,6 @@ describe("PerpetualTranche", function () { it("should update reference", async function () { expect(await perp.bondIssuer()).to.eq(newIssuer.address); }); - it("should emit event", async function () { - await expect(tx).to.emit(perp, "UpdatedBondIssuer").withArgs(newIssuer.address); - }); }); describe("when collateral is NOT valid", function () { @@ -345,145 +253,44 @@ describe("PerpetualTranche", function () { await newIssuer.collateral.returns(constants.AddressZero); }); it("should revert", async function () { - await expect(perp.updateBondIssuer(newIssuer.address)).to.be.revertedWithCustomError(perp, "InvalidCollateral"); - }); - }); - }); - - describe("#updateFeeStrategy", function () { - let newFeeStrategy: Contract, tx: Transaction; - - describe("when triggered by non-owner", function () { - it("should revert", async function () { - await expect(perp.connect(otherUser).updateFeeStrategy(constants.AddressZero)).to.be.revertedWith( - "Ownable: caller is not the owner", - ); - }); - }); - - describe("when set address is NOT valid", function () { - it("should revert", async function () { - 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("BasicFeeStrategy"); - newFeeStrategy = await smock.fake(FeeStrategy); - tx = perp.updateFeeStrategy(newFeeStrategy.address); - await tx; - }); - it("should update reference", async function () { - expect(await perp.feeStrategy()).to.eq(newFeeStrategy.address); - }); - it("should emit event", async function () { - await expect(tx).to.emit(perp, "UpdatedFeeStrategy").withArgs(newFeeStrategy.address); - }); - }); - }); - - describe("#updatePricingStrategy", function () { - let newPricingStrategy: Contract, tx: Transaction; - - describe("when triggered by non-owner", function () { - it("should revert", async function () { - await expect(perp.connect(otherUser).updatePricingStrategy(constants.AddressZero)).to.be.revertedWith( - "Ownable: caller is not the owner", - ); - }); - }); - - describe("when set address is NOT valid", function () { - it("should revert", async function () { - 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("UnitPricingStrategy"); - newPricingStrategy = await smock.fake(PricingStrategy); - await newPricingStrategy.decimals.returns(18); - }); - it("should revert", async function () { - await expect(perp.updatePricingStrategy(newPricingStrategy.address)).to.be.revertedWithCustomError( - perp, - "InvalidStrategyDecimals", - ); - }); - }); - - describe("when set address is valid", function () { - beforeEach(async function () { - const PricingStrategy = await ethers.getContractFactory("UnitPricingStrategy"); - newPricingStrategy = await smock.fake(PricingStrategy); - await newPricingStrategy.decimals.returns(8); - tx = perp.updatePricingStrategy(newPricingStrategy.address); - await tx; - }); - it("should update reference", async function () { - expect(await perp.pricingStrategy()).to.eq(newPricingStrategy.address); - }); - it("should emit event", async function () { - await expect(tx).to.emit(perp, "UpdatedPricingStrategy").withArgs(newPricingStrategy.address); + await expect(perp.updateBondIssuer(newIssuer.address)).to.be.revertedWithCustomError(perp, "UnexpectedAsset"); }); }); }); - describe("#updateDiscountStrategy", function () { - let newDiscountStrategy: Contract, tx: Transaction; + describe("#updateFeePolicy", function () { + let newFeePolicy: Contract, tx: Transaction; describe("when triggered by non-owner", function () { it("should revert", async function () { - await expect(perp.connect(otherUser).updateDiscountStrategy(constants.AddressZero)).to.be.revertedWith( + await expect(perp.connect(otherUser).updateFeePolicy(constants.AddressZero)).to.be.revertedWith( "Ownable: caller is not the owner", ); }); }); - describe("when set address is NOT valid", function () { - it("should revert", async function () { - 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("TrancheClassDiscountStrategy"); - newDiscountStrategy = await smock.fake(DiscountStrategy); - await newDiscountStrategy.decimals.returns(8); - }); + describe("when set strategy decimals dont match", function () { it("should revert", async function () { - await expect(perp.updateDiscountStrategy(newDiscountStrategy.address)).to.be.revertedWithCustomError( + const FeePolicy = await ethers.getContractFactory("FeePolicy"); + newFeePolicy = await smock.fake(FeePolicy); + await newFeePolicy.decimals.returns(7); + await expect(perp.updateFeePolicy(newFeePolicy.address)).to.be.revertedWithCustomError( perp, - "InvalidStrategyDecimals", + "UnexpectedDecimals", ); }); }); describe("when set address is valid", function () { beforeEach(async function () { - const DiscountStrategy = await ethers.getContractFactory("TrancheClassDiscountStrategy"); - newDiscountStrategy = await smock.fake(DiscountStrategy); - await newDiscountStrategy.decimals.returns(18); - tx = perp.updateDiscountStrategy(newDiscountStrategy.address); + const FeePolicy = await ethers.getContractFactory("FeePolicy"); + newFeePolicy = await smock.fake(FeePolicy); + await newFeePolicy.decimals.returns(8); + tx = perp.updateFeePolicy(newFeePolicy.address); await tx; }); it("should update reference", async function () { - expect(await perp.discountStrategy()).to.eq(newDiscountStrategy.address); - }); - it("should emit event", async function () { - await expect(tx).to.emit(perp, "UpdatedDiscountStrategy").withArgs(newDiscountStrategy.address); + expect(await perp.feePolicy()).to.eq(newFeePolicy.address); }); }); }); @@ -503,7 +310,7 @@ describe("PerpetualTranche", function () { it("should revert", async function () { await expect(perp.updateTolerableTrancheMaturity(86400, 3600)).to.be.revertedWithCustomError( perp, - "InvalidTrancheMaturityBounds", + "UnacceptableParams", ); }); }); @@ -517,66 +324,30 @@ describe("PerpetualTranche", function () { expect(await perp.minTrancheMaturitySec()).to.eq(3600); expect(await perp.maxTrancheMaturitySec()).to.eq(86400); }); - it("should emit event", async function () { - await expect(tx).to.emit(perp, "UpdatedTolerableTrancheMaturity").withArgs(3600, 86400); - }); }); }); describe("#updateMintingLimits", function () { let tx: Transaction; - describe("when triggered by non-owner", function () { + describe("when triggered by non-keeper", function () { it("should revert", async function () { await expect( perp.connect(otherUser).updateMintingLimits(constants.MaxUint256, constants.MaxUint256), - ).to.be.revertedWith("Ownable: caller is not the owner"); + ).to.be.revertedWithCustomError(perp, "UnauthorizedCall"); }); }); describe("when triggered by owner", function () { beforeEach(async function () { - tx = perp.updateMintingLimits(toFixedPtAmt("100"), toFixedPtAmt("20")); + await perp.updateKeeper(await otherUser.getAddress()); + tx = perp.connect(otherUser).updateMintingLimits(toFixedPtAmt("100"), toFixedPtAmt("20")); await tx; }); it("should update reference", async function () { expect(await perp.maxSupply()).to.eq(toFixedPtAmt("100")); expect(await perp.maxMintAmtPerTranche()).to.eq(toFixedPtAmt("20")); }); - it("should emit event", async function () { - await expect(tx).to.emit(perp, "UpdatedMintingLimits").withArgs(toFixedPtAmt("100"), toFixedPtAmt("20")); - }); - }); - }); - - describe("#updateMatureValueTargetPerc", function () { - let tx: Transaction; - - describe("when triggered by non-owner", function () { - it("should revert", async function () { - await expect(perp.connect(otherUser).updateMatureValueTargetPerc("1000000")).to.be.revertedWith( - "Ownable: caller is not the owner", - ); - }); - }); - - describe("when NOT valid", function () { - it("should revert", async function () { - await expect(perp.updateMatureValueTargetPerc("100000001")).to.be.revertedWithCustomError(perp, "InvalidPerc"); - }); - }); - - describe("when triggered by owner", function () { - beforeEach(async function () { - tx = perp.updateMatureValueTargetPerc("1000000"); - await tx; - }); - it("should update reference", async function () { - expect(await perp.matureValueTargetPerc()).to.eq("1000000"); - }); - it("should emit event", async function () { - await expect(tx).to.emit(perp, "UpdatedMatureValueTargetPerc").withArgs("1000000"); - }); }); }); @@ -618,133 +389,21 @@ describe("PerpetualTranche", function () { }); }); - describe("when fee token", function () { - it("should revert", async function () { - await expect( - perp.transferERC20(await perp.feeToken(), toAddress, toFixedPtAmt("100")), - ).to.be.revertedWithCustomError(perp, "UnauthorizedTransferOut"); - }); - }); - }); - - describe("#computeDiscount", 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.getLatestBond.returns(bond.address); - await discountStrategy.computeTrancheDiscount - .whenCalledWith(tranches[0].address) - .returns(toDiscountFixedPtAmt("1")); - }); - - describe("when tranche instance is not in the system", function () { - it("should return defined discount", async 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")); - }); - }); - describe("when updated", function () { - beforeEach(async function () { - expect(await perp.computeDiscount(tranches[0].address)).to.eq(toDiscountFixedPtAmt("1")); - 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")); - }); - }); - }); - - describe("when tranche instance is already in system", function () { - beforeEach(async function () { - await depositIntoBond(bond, toFixedPtAmt("1000"), deployer); - await tranches[0].approve(perp.address, toFixedPtAmt("200")); - await perp.deposit(tranches[0].address, toFixedPtAmt("200")); - }); - it("should return applied discount", async function () { - expect(await perp.computeDiscount(tranches[0].address)).to.eq(toDiscountFixedPtAmt("1")); - }); - describe("when updated", function () { - beforeEach(async function () { - 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")); - }); - }); - }); - - describe("when a new tranche instance enters the system", function () { - let tranchesNext: Contract[]; - beforeEach(async function () { + describe("when withdrawing perp", function () { + it("should NOT revert", async function () { + const bondFactory = await setupBondFactory(); + const bond = await createBondWithFactory(bondFactory, collateralToken, [200, 800], 3600); + const tranches = await getTranches(bond); + await issuer.getLatestBond.returns(bond.address); await depositIntoBond(bond, toFixedPtAmt("1000"), deployer); - await tranches[0].approve(perp.address, toFixedPtAmt("200")); - await perp.deposit(tranches[0].address, toFixedPtAmt("200")); - - const bondNext = await createBondWithFactory(bondFactory, collateralToken, [200, 300, 500], 3600); - tranchesNext = await getTranches(bondNext); - await issuer.getLatestBond.returns(bondNext.address); - - 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.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")); - }); - it("should return applied discount for old tranche", async function () { - expect(await perp.computeDiscount(tranches[0].address)).to.eq(toDiscountFixedPtAmt("1")); - }); + await tranches[0].approve(perp.address, toFixedPtAmt("100")); + await perp.deposit(tranches[0].address, toFixedPtAmt("100")); + await perp.transfer(perp.address, toFixedPtAmt("100")); + await expect(perp.transferERC20(perp.address, toAddress, toFixedPtAmt("100"))).not.to.be.reverted; }); }); }); - describe("#computePrice", function () { - beforeEach(async function () { - expect(await perp.computePrice(constants.AddressZero)).not.to.eq(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")); - }); - }); - - describe("#feeToken", function () { - let feeToken: Contract; - beforeEach(async function () { - const ERC20 = await ethers.getContractFactory("MockERC20"); - feeToken = await ERC20.deploy(); - await feeToken.init("Mock token", "MOCK"); - expect(await perp.feeToken()).not.to.eq(feeToken.address); - await feeStrategy.feeToken.returns(feeToken.address); - }); - - it("should return the fee token from the strategy", async function () { - expect(await perp.feeToken()).to.eq(feeToken.address); - }); - }); - describe("#reserve", function () { let bondFactory: Contract, bond: Contract, tranches: Contract[], bondNext: Contract, tranchesNext: Contract[]; beforeEach(async function () { @@ -753,16 +412,16 @@ describe("PerpetualTranche", function () { describe("when reserve has no tranches", function () { it("should have expected reserve composition", async function () { - await checkReserveComposition(perp, [collateralToken], [toFixedPtAmt("0")]); + await checkReserveComposition(perp, [collateralToken], ["0"]); }); - it("should calculate the avg. perp price", async function () { - expect(await perp.callStatic.getAvgPrice()).to.eq(0); + it("should calculate the tvl", async function () { + expect(await perp.callStatic.getTVL()).to.eq(0); }); }); describe("when reserve has one tranche", function () { beforeEach(async function () { - bond = await createBondWithFactory(bondFactory, collateralToken, [200, 300, 500], 3600); + bond = await createBondWithFactory(bondFactory, collateralToken, [200, 800], 3600); tranches = await getTranches(bond); await issuer.getLatestBond.returns(bond.address); @@ -772,16 +431,16 @@ describe("PerpetualTranche", function () { }); it("should have expected reserve composition", async function () { - await checkReserveComposition(perp, [collateralToken, tranches[0]], [toFixedPtAmt("0"), toFixedPtAmt("200")]); + await checkReserveComposition(perp, [collateralToken, tranches[0]], ["0", toFixedPtAmt("200")]); }); - it("should calculate the avg. perp price", async function () { - expect(await perp.callStatic.getAvgPrice()).to.eq(toPriceFixedPtAmt("1")); + it("should calculate the tvl", async function () { + expect(await perp.callStatic.getTVL()).to.eq(toFixedPtAmt("200")); }); }); describe("when reserve has many tranches", function () { beforeEach(async function () { - bond = await createBondWithFactory(bondFactory, collateralToken, [200, 300, 500], 3600); + bond = await createBondWithFactory(bondFactory, collateralToken, [200, 800], 3600); tranches = await getTranches(bond); await issuer.getLatestBond.returns(bond.address); @@ -789,16 +448,12 @@ describe("PerpetualTranche", function () { await tranches[0].approve(perp.address, toFixedPtAmt("200")); await perp.deposit(tranches[0].address, toFixedPtAmt("200")); - bondNext = await createBondWithFactory(bondFactory, collateralToken, [200, 300, 500], 3600); + await rebase(collateralToken, rebaseOracle, -0.9); + + bondNext = await createBondWithFactory(bondFactory, collateralToken, [200, 800], 3600); tranchesNext = await getTranches(bondNext); 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")); @@ -808,11 +463,11 @@ describe("PerpetualTranche", function () { await checkReserveComposition( perp, [collateralToken, tranches[0], tranchesNext[0]], - [toFixedPtAmt("0"), toFixedPtAmt("200"), toFixedPtAmt("100")], + ["0", toFixedPtAmt("200"), toFixedPtAmt("100")], ); }); - it("should calculate the avg. perp price", async function () { - expect(await perp.callStatic.getAvgPrice()).to.eq(toPriceFixedPtAmt("1")); + it("should calculate the tvl", async function () { + expect(await perp.callStatic.getTVL()).to.eq(toFixedPtAmt("200")); }); }); @@ -820,8 +475,13 @@ describe("PerpetualTranche", function () { let issuer: Contract; beforeEach(async function () { const BondIssuer = await ethers.getContractFactory("BondIssuer"); - issuer = await BondIssuer.deploy(bondFactory.address, collateralToken.address); - await issuer.init(3600, [200, 300, 500], 1200, 0); + issuer = await upgrades.deployProxy( + BondIssuer.connect(deployer), + [bondFactory.address, collateralToken.address, 3600, [200, 800], 1200, 0], + { + initializer: "init(address,address,uint256,uint256[],uint256,uint256)", + }, + ); await perp.updateBondIssuer(issuer.address); await advancePerpQueue(perp, 1200); @@ -846,8 +506,8 @@ describe("PerpetualTranche", function () { it("should have expected reserve composition", async function () { await checkReserveComposition(perp, [collateralToken], [toFixedPtAmt("300")]); }); - it("should calculate the avg. perp price", async function () { - expect(await perp.callStatic.getAvgPrice()).to.eq(toPriceFixedPtAmt("1")); + it("should calculate the tvl", async function () { + expect(await perp.callStatic.getTVL()).to.eq(toFixedPtAmt("300")); }); }); @@ -855,8 +515,13 @@ describe("PerpetualTranche", function () { let issuer: Contract; beforeEach(async function () { const BondIssuer = await ethers.getContractFactory("BondIssuer"); - issuer = await BondIssuer.deploy(bondFactory.address, collateralToken.address); - await issuer.init(3600, [200, 300, 500], 1200, 0); + issuer = await upgrades.deployProxy( + BondIssuer.connect(deployer), + [bondFactory.address, collateralToken.address, 3600, [200, 800], 1200, 0], + { + initializer: "init(address,address,uint256,uint256[],uint256,uint256)", + }, + ); await perp.updateBondIssuer(issuer.address); await advancePerpQueue(perp, 3600); @@ -885,8 +550,8 @@ describe("PerpetualTranche", function () { [toFixedPtAmt("200"), toFixedPtAmt("100")], ); }); - it("should calculate the avg. perp price", async function () { - expect(await perp.callStatic.getAvgPrice()).to.eq(toPriceFixedPtAmt("1")); + it("should calculate the tvl", async function () { + expect(await perp.callStatic.getTVL()).to.eq(toFixedPtAmt("300")); }); }); @@ -894,8 +559,13 @@ describe("PerpetualTranche", function () { let issuer: Contract; beforeEach(async function () { const BondIssuer = await ethers.getContractFactory("BondIssuer"); - issuer = await BondIssuer.deploy(bondFactory.address, collateralToken.address); - await issuer.init(3600, [200, 300, 500], 1200, 0); + issuer = await upgrades.deployProxy( + BondIssuer.connect(deployer), + [bondFactory.address, collateralToken.address, 3600, [200, 800], 1200, 0], + { + initializer: "init(address,address,uint256,uint256[],uint256,uint256)", + }, + ); await perp.updateBondIssuer(issuer.address); await advancePerpQueue(perp, 3600); bond = await bondAt(await perp.callStatic.getDepositBond()); @@ -916,7 +586,6 @@ describe("PerpetualTranche", function () { await advancePerpQueue(perp, 2400); await rebase(collateralToken, rebaseOracle, 0.1); - await pricingStrategy.computeMatureTranchePrice.returns(toPriceFixedPtAmt("1.1")); }); it("should have expected reserve composition", async function () { @@ -926,8 +595,8 @@ describe("PerpetualTranche", function () { [toFixedPtAmt("220"), toFixedPtAmt("100")], ); }); - it("should calculate the avg. perp price", async function () { - expect(await perp.callStatic.getAvgPrice()).to.eq(toPriceFixedPtAmt("1.06666666")); + it("should calculate the tvl", async function () { + expect(await perp.callStatic.getTVL()).to.eq(toFixedPtAmt("320")); }); }); @@ -935,8 +604,13 @@ describe("PerpetualTranche", function () { let issuer: Contract; beforeEach(async function () { const BondIssuer = await ethers.getContractFactory("BondIssuer"); - issuer = await BondIssuer.deploy(bondFactory.address, collateralToken.address); - await issuer.init(3600, [200, 300, 500], 1200, 0); + issuer = await upgrades.deployProxy( + BondIssuer.connect(deployer), + [bondFactory.address, collateralToken.address, 3600, [200, 800], 1200, 0], + { + initializer: "init(address,address,uint256,uint256[],uint256,uint256)", + }, + ); await perp.updateBondIssuer(issuer.address); await advancePerpQueue(perp, 3600); @@ -958,7 +632,6 @@ describe("PerpetualTranche", function () { await advancePerpQueue(perp, 2400); await rebase(collateralToken, rebaseOracle, -0.1); - await pricingStrategy.computeMatureTranchePrice.returns(toPriceFixedPtAmt("0.9")); }); it("should have expected reserve composition", async function () { @@ -968,8 +641,8 @@ describe("PerpetualTranche", function () { [toFixedPtAmt("180"), toFixedPtAmt("100")], ); }); - it("should calculate the avg. perp price", async function () { - expect(await perp.callStatic.getAvgPrice()).to.eq(toPriceFixedPtAmt("0.93333333")); + it("should calculate the tvl", async function () { + expect(await perp.callStatic.getTVL()).to.eq(toFixedPtAmt("280")); }); }); }); @@ -983,22 +656,10 @@ describe("PerpetualTranche", function () { }); describe("when the deposit bond is not acceptable", function () { - describe("when deposit bond matures too soon", async function () { - beforeEach(async function () { - bond = await createBondWithFactory(bondFactory, collateralToken, [200, 300, 500], 600); - await issuer.getLatestBond.returns(bond.address); - await perp.updateState(); - }); - - it("should NOT update the deposit bond", async function () { - expect(await perp.callStatic.getDepositBond()).to.not.eq(bond.address); - }); - }); - describe("when deposit bond matures too late", async function () { beforeEach(async function () { await perp.updateTolerableTrancheMaturity(1200, 7200); - bond = await createBondWithFactory(bondFactory, collateralToken, [200, 300, 500], 7210); + bond = await createBondWithFactory(bondFactory, collateralToken, [200, 800], 7210); await issuer.getLatestBond.returns(bond.address); await perp.updateState(); }); @@ -1012,7 +673,7 @@ describe("PerpetualTranche", function () { beforeEach(async function () { await perp.updateTolerableTrancheMaturity(1200, 7200); const r = await setupCollateralToken("Ethereum", "ETH"); - bond = await createBondWithFactory(bondFactory, r.collateralToken, [200, 300, 500], 3600); + bond = await createBondWithFactory(bondFactory, r.collateralToken, [200, 800], 3600); await issuer.getLatestBond.returns(bond.address); await perp.updateState(); }); @@ -1026,7 +687,7 @@ describe("PerpetualTranche", function () { let tx: Transaction; beforeEach(async function () { await perp.updateTolerableTrancheMaturity(1200, 7200); - bond = await createBondWithFactory(bondFactory, collateralToken, [200, 300, 500], 3600); + bond = await createBondWithFactory(bondFactory, collateralToken, [200, 800], 3600); await issuer.getLatestBond.returns(bond.address); tx = perp.updateState(); await tx; @@ -1047,18 +708,20 @@ describe("PerpetualTranche", function () { const reserveTranches: Contract[] = []; beforeEach(async function () { const BondIssuer = await ethers.getContractFactory("BondIssuer"); - issuer = await BondIssuer.deploy(bondFactory.address, collateralToken.address); - await issuer.init(10800, [500, 500], 1200, 0); + issuer = await upgrades.deployProxy( + BondIssuer.connect(deployer), + [bondFactory.address, collateralToken.address, 10800, [500, 500], 1200, 0], + { + initializer: "init(address,address,uint256,uint256[],uint256,uint256)", + }, + ); + await perp.updateBondIssuer(issuer.address); await perp.updateTolerableTrancheMaturity(0, 10800); await advancePerpQueue(perp, 10900); for (let i = 0; i < 5; i++) { const depositBond = await bondAt(await perp.callStatic.getDepositBond()); const tranches = await getTranches(depositBond); - 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")); @@ -1070,7 +733,7 @@ describe("PerpetualTranche", function () { perp, [collateralToken, ...reserveTranches], [ - toFixedPtAmt("0"), + "0", toFixedPtAmt("500"), toFixedPtAmt("500"), toFixedPtAmt("500"), @@ -1080,8 +743,7 @@ describe("PerpetualTranche", function () { ); expect(await perp.callStatic.getReserveCount()).to.eq("6"); - expect(await perp.callStatic.getMatureTrancheBalance()).to.eq("0"); - expect(await collateralToken.balanceOf(await perp.reserve())).to.eq("0"); + expect(await collateralToken.balanceOf(perp.address)).to.eq("0"); await TimeHelpers.increaseTime(1200); tx = await perp.updateState(); @@ -1093,7 +755,7 @@ describe("PerpetualTranche", function () { perp, [collateralToken, ...reserveTranches], [ - toFixedPtAmt("0"), + "0", toFixedPtAmt("500"), toFixedPtAmt("500"), toFixedPtAmt("500"), @@ -1107,16 +769,14 @@ describe("PerpetualTranche", function () { expect(await perp.callStatic.getReserveCount()).to.eq("6"); }); - it("should NOT change tranche balances", async function () { - expect(await perp.callStatic.getMatureTrancheBalance()).to.eq("0"); - }); + it("should NOT change tranche balances", async function () {}); it("should emit ReserveSynced", async function () { - await expect(tx).to.emit(perp, "ReserveSynced").withArgs(collateralToken.address, toFixedPtAmt("0")); + await expect(tx).to.emit(perp, "ReserveSynced").withArgs(collateralToken.address, "0"); }); it("should NOT update the reserve balance", async function () { - expect(await collateralToken.balanceOf(await perp.reserve())).to.eq("0"); + expect(await collateralToken.balanceOf(perp.address)).to.eq("0"); }); }); @@ -1126,18 +786,19 @@ describe("PerpetualTranche", function () { const reserveTranches: Contract[] = []; beforeEach(async function () { const BondIssuer = await ethers.getContractFactory("BondIssuer"); - issuer = await BondIssuer.deploy(bondFactory.address, collateralToken.address); - await issuer.init(10800, [500, 500], 1200, 0); + issuer = await upgrades.deployProxy( + BondIssuer.connect(deployer), + [bondFactory.address, collateralToken.address, 10800, [500, 500], 1200, 0], + { + initializer: "init(address,address,uint256,uint256[],uint256,uint256)", + }, + ); await perp.updateBondIssuer(issuer.address); await perp.updateTolerableTrancheMaturity(0, 10800); await advancePerpQueue(perp, 10900); for (let i = 0; i < 5; i++) { const depositBond = await bondAt(await perp.callStatic.getDepositBond()); const tranches = await getTranches(depositBond); - 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")); @@ -1149,7 +810,7 @@ describe("PerpetualTranche", function () { perp, [collateralToken, ...reserveTranches], [ - toFixedPtAmt("0"), + "0", toFixedPtAmt("500"), toFixedPtAmt("500"), toFixedPtAmt("500"), @@ -1158,8 +819,7 @@ describe("PerpetualTranche", function () { ], ); expect(await perp.callStatic.getReserveCount()).to.eq("6"); - expect(await perp.callStatic.getMatureTrancheBalance()).to.eq("0"); - expect(await collateralToken.balanceOf(await perp.reserve())).to.eq("0"); + expect(await collateralToken.balanceOf(perp.address)).to.eq("0"); await TimeHelpers.increaseTime(6000); // NOTE: invoking mature on reserveTranches[0], @@ -1182,18 +842,8 @@ describe("PerpetualTranche", function () { expect(await perp.callStatic.getReserveCount()).to.eq("4"); }); - it("should emit tranche balance update", async function () { - await expect(tx).to.emit(perp, "UpdatedMatureTrancheBalance").withArgs(toFixedPtAmt("1000")); - }); - - it("should change mature tranche balances", async function () { - expect(await perp.callStatic.getMatureTrancheBalance()).to.eq(toFixedPtAmt("1000")); - }); - it("should call mature if not already called", async function () { await expect(tx) - .to.emit(await bondAt(await reserveTranches[0].bond()), "Mature") - .withArgs(perp.address) .to.emit(await bondAt(await reserveTranches[1].bond()), "Mature") .withArgs(perp.address); }); @@ -1209,7 +859,7 @@ describe("PerpetualTranche", function () { }); it("should update the reserve balance", async function () { - expect(await collateralToken.balanceOf(await perp.reserve())).to.eq(toFixedPtAmt("1000")); + expect(await collateralToken.balanceOf(perp.address)).to.eq(toFixedPtAmt("1000")); }); }); @@ -1219,18 +869,19 @@ describe("PerpetualTranche", function () { const reserveTranches: Contract[] = []; beforeEach(async function () { const BondIssuer = await ethers.getContractFactory("BondIssuer"); - issuer = await BondIssuer.deploy(bondFactory.address, collateralToken.address); - await issuer.init(10800, [500, 500], 1200, 0); + issuer = await upgrades.deployProxy( + BondIssuer.connect(deployer), + [bondFactory.address, collateralToken.address, 10800, [500, 500], 1200, 0], + { + initializer: "init(address,address,uint256,uint256[],uint256,uint256)", + }, + ); await perp.updateBondIssuer(issuer.address); await perp.updateTolerableTrancheMaturity(0, 10800); await advancePerpQueue(perp, 10900); for (let i = 0; i < 5; i++) { const depositBond = await bondAt(await perp.callStatic.getDepositBond()); const tranches = await getTranches(depositBond); - 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")); @@ -1243,7 +894,7 @@ describe("PerpetualTranche", function () { perp, [collateralToken, ...reserveTranches], [ - toFixedPtAmt("0"), + "0", toFixedPtAmt("500"), toFixedPtAmt("500"), toFixedPtAmt("500"), @@ -1252,8 +903,7 @@ describe("PerpetualTranche", function () { ], ); expect(await perp.callStatic.getReserveCount()).to.eq("6"); - expect(await perp.callStatic.getMatureTrancheBalance()).to.eq("0"); - expect(await collateralToken.balanceOf(await perp.reserve())).to.eq("0"); + expect(await collateralToken.balanceOf(perp.address)).to.eq("0"); await TimeHelpers.increaseTime(6000); tx = perp.updateState(); @@ -1272,14 +922,6 @@ describe("PerpetualTranche", function () { expect(await perp.callStatic.getReserveCount()).to.eq("4"); }); - it("should emit tranche balance update", async function () { - await expect(tx).to.emit(perp, "UpdatedMatureTrancheBalance").withArgs(toFixedPtAmt("1000")); - }); - - it("should change mature tranche balances", async function () { - expect(await perp.callStatic.getMatureTrancheBalance()).to.eq(toFixedPtAmt("1000")); - }); - it("should call mature if not already called", async function () { await expect(tx) .to.emit(await bondAt(await reserveTranches[0].bond()), "Mature") @@ -1299,34 +941,29 @@ describe("PerpetualTranche", function () { }); it("should update the reserve balance", async function () { - expect(await collateralToken.balanceOf(await perp.reserve())).to.eq("553710919999999999999"); + expect(await collateralToken.balanceOf(perp.address)).to.eq("553710919999999999999"); }); }); - describe("when some reserve tranches are mature and discounts are different", async function () { + describe("when some reserve tranches are mature", async function () { let issuer: Contract; let tx: Transaction; const reserveTranches: Contract[] = []; beforeEach(async function () { const BondIssuer = await ethers.getContractFactory("BondIssuer"); - issuer = await BondIssuer.deploy(bondFactory.address, collateralToken.address); - await issuer.init(10800, [500, 500], 1200, 0); + issuer = await upgrades.deployProxy( + BondIssuer.connect(deployer), + [bondFactory.address, collateralToken.address, 10800, [500, 500], 1200, 0], + { + initializer: "init(address,address,uint256,uint256[],uint256,uint256)", + }, + ); await perp.updateBondIssuer(issuer.address); await perp.updateTolerableTrancheMaturity(0, 10800); await advancePerpQueue(perp, 10900); for (let i = 0; i < 5; i++) { const depositBond = await bondAt(await perp.callStatic.getDepositBond()); const tranches = await getTranches(depositBond); - await pricingStrategy.computeTranchePrice.whenCalledWith(tranches[0].address).returns(toPriceFixedPtAmt("1")); - if (i === 0) { - await discountStrategy.computeTrancheDiscount - .whenCalledWith(tranches[0].address) - .returns(toDiscountFixedPtAmt("0.5")); - } else { - 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")); @@ -1338,7 +975,7 @@ describe("PerpetualTranche", function () { perp, [collateralToken, ...reserveTranches], [ - toFixedPtAmt("0"), + "0", toFixedPtAmt("500"), toFixedPtAmt("500"), toFixedPtAmt("500"), @@ -1347,8 +984,7 @@ describe("PerpetualTranche", function () { ], ); expect(await perp.callStatic.getReserveCount()).to.eq("6"); - expect(await perp.callStatic.getMatureTrancheBalance()).to.eq("0"); - expect(await collateralToken.balanceOf(await perp.reserve())).to.eq("0"); + expect(await collateralToken.balanceOf(perp.address)).to.eq("0"); await TimeHelpers.increaseTime(6000); tx = perp.updateState(); @@ -1367,13 +1003,7 @@ describe("PerpetualTranche", function () { expect(await perp.callStatic.getReserveCount()).to.eq("4"); }); - it("should emit tranche balance update", async function () { - await expect(tx).to.emit(perp, "UpdatedMatureTrancheBalance").withArgs(toFixedPtAmt("750")); - }); - - it("should change mature tranche balances", async function () { - expect(await perp.callStatic.getMatureTrancheBalance()).to.eq(toFixedPtAmt("750")); - }); + it("should change mature tranche balances", async function () {}); it("should call mature if not already called", async function () { await expect(tx) @@ -1394,7 +1024,71 @@ describe("PerpetualTranche", function () { }); it("should update the reserve balance", async function () { - expect(await collateralToken.balanceOf(await perp.reserve())).to.eq(toFixedPtAmt("1000")); + expect(await collateralToken.balanceOf(perp.address)).to.eq(toFixedPtAmt("1000")); + }); + }); + + describe("when paused", async function () { + let issuer: Contract; + let tx: Transaction; + const reserveTranches: Contract[] = []; + beforeEach(async function () { + const BondIssuer = await ethers.getContractFactory("BondIssuer"); + issuer = await upgrades.deployProxy( + BondIssuer.connect(deployer), + [bondFactory.address, collateralToken.address, 10800, [500, 500], 1200, 0], + { + initializer: "init(address,address,uint256,uint256[],uint256,uint256)", + }, + ); + await perp.updateBondIssuer(issuer.address); + await perp.updateTolerableTrancheMaturity(0, 10800); + await advancePerpQueue(perp, 10900); + for (let i = 0; i < 5; i++) { + const depositBond = await bondAt(await perp.callStatic.getDepositBond()); + const tranches = await getTranches(depositBond); + await depositIntoBond(depositBond, toFixedPtAmt("1000"), deployer); + await tranches[0].approve(perp.address, toFixedPtAmt("500")); + await perp.deposit(tranches[0].address, toFixedPtAmt("500")); + reserveTranches[i] = tranches[0]; + await advancePerpQueue(perp, 1200); + } + + await checkReserveComposition( + perp, + [collateralToken, ...reserveTranches], + [ + "0", + toFixedPtAmt("500"), + toFixedPtAmt("500"), + toFixedPtAmt("500"), + toFixedPtAmt("500"), + toFixedPtAmt("500"), + ], + ); + expect(await perp.callStatic.getReserveCount()).to.eq("6"); + expect(await collateralToken.balanceOf(perp.address)).to.eq("0"); + + await TimeHelpers.increaseTime(6000); + await perp.updateKeeper(await deployer.getAddress()); + await perp.pause(); + tx = perp.updateState(); + await tx; + }); + + it("should have not updated composition", async function () { + await checkReserveComposition( + perp, + [collateralToken, ...reserveTranches], + [ + "0", + toFixedPtAmt("500"), + toFixedPtAmt("500"), + toFixedPtAmt("500"), + toFixedPtAmt("500"), + toFixedPtAmt("500"), + ], + ); }); }); }); @@ -1404,18 +1098,19 @@ describe("PerpetualTranche", function () { beforeEach(async function () { const bondFactory = await setupBondFactory(); const BondIssuer = await ethers.getContractFactory("BondIssuer"); - const issuer = await BondIssuer.deploy(bondFactory.address, collateralToken.address); - await issuer.init(10800, [500, 500], 1200, 0); + issuer = await upgrades.deployProxy( + BondIssuer.connect(deployer), + [bondFactory.address, collateralToken.address, 10800, [500, 500], 1200, 0], + { + initializer: "init(address,address,uint256,uint256[],uint256,uint256)", + }, + ); await perp.updateBondIssuer(issuer.address); await perp.updateTolerableTrancheMaturity(600, 10800); await advancePerpQueue(perp, 10900); for (let i = 0; i < 5; i++) { const depositBond = await bondAt(await perp.callStatic.getDepositBond()); const tranches = await getTranches(depositBond); - 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")); @@ -1436,23 +1131,24 @@ describe("PerpetualTranche", function () { }); }); - describe("#getReserveTrancheBalance", async function () { + describe("#getReserveTokenBalance", async function () { const depositTranches: Contract[] = []; beforeEach(async function () { const bondFactory = await setupBondFactory(); const BondIssuer = await ethers.getContractFactory("BondIssuer"); - const issuer = await BondIssuer.deploy(bondFactory.address, collateralToken.address); - await issuer.init(10800, [500, 500], 1200, 0); + issuer = await upgrades.deployProxy( + BondIssuer.connect(deployer), + [bondFactory.address, collateralToken.address, 10800, [500, 500], 1200, 0], + { + initializer: "init(address,address,uint256,uint256[],uint256,uint256)", + }, + ); await perp.updateBondIssuer(issuer.address); await perp.updateTolerableTrancheMaturity(600, 10800); await advancePerpQueue(perp, 10900); for (let i = 0; i < 5; i++) { const depositBond = await bondAt(await perp.callStatic.getDepositBond()); const tranches = await getTranches(depositBond); - 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")); @@ -1462,52 +1158,53 @@ describe("PerpetualTranche", function () { await advancePerpQueueToRollover(perp, await bondAt(depositTranches[2].bond())); }); - it("should return the tranche balance", async function () { - expect(await perp.callStatic.getReserveTrancheBalance(perp.address)).to.eq("0"); - expect(await perp.callStatic.getReserveTrancheBalance(collateralToken.address)).to.eq(toFixedPtAmt("750")); - expect(await perp.callStatic.getReserveTrancheBalance(depositTranches[0].address)).to.eq("0"); - expect(await perp.callStatic.getReserveTrancheBalance(depositTranches[1].address)).to.eq("0"); - expect(await perp.callStatic.getReserveTrancheBalance(depositTranches[2].address)).to.eq(toFixedPtAmt("500")); - expect(await perp.callStatic.getReserveTrancheBalance(depositTranches[3].address)).to.eq(toFixedPtAmt("500")); - expect(await perp.callStatic.getReserveTrancheBalance(depositTranches[4].address)).to.eq(toFixedPtAmt("500")); + it("should return the token balance", async function () { + expect(await perp.callStatic.getReserveTokenBalance(perp.address)).to.eq("0"); + expect(await perp.callStatic.getReserveTokenBalance(collateralToken.address)).to.eq(toFixedPtAmt("1000")); + expect(await perp.callStatic.getReserveTokenBalance(depositTranches[0].address)).to.eq("0"); + expect(await perp.callStatic.getReserveTokenBalance(depositTranches[1].address)).to.eq("0"); + expect(await perp.callStatic.getReserveTokenBalance(depositTranches[2].address)).to.eq(toFixedPtAmt("500")); + expect(await perp.callStatic.getReserveTokenBalance(depositTranches[3].address)).to.eq(toFixedPtAmt("500")); + expect(await perp.callStatic.getReserveTokenBalance(depositTranches[4].address)).to.eq(toFixedPtAmt("500")); }); }); - describe("#getReserveTrancheValue", async function () { + describe("#getReserveTokenValue", async function () { const depositTranches: Contract[] = []; beforeEach(async function () { const bondFactory = await setupBondFactory(); const BondIssuer = await ethers.getContractFactory("BondIssuer"); - const issuer = await BondIssuer.deploy(bondFactory.address, collateralToken.address); - await issuer.init(10800, [500, 500], 1200, 0); + issuer = await upgrades.deployProxy( + BondIssuer.connect(deployer), + [bondFactory.address, collateralToken.address, 10800, [500, 500], 1200, 0], + { + initializer: "init(address,address,uint256,uint256[],uint256,uint256)", + }, + ); await perp.updateBondIssuer(issuer.address); await perp.updateTolerableTrancheMaturity(600, 10800); await advancePerpQueue(perp, 10900); for (let i = 0; i < 5; i++) { const depositBond = await bondAt(await perp.callStatic.getDepositBond()); const tranches = await getTranches(depositBond); - 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")); depositTranches[i] = tranches[0]; await advancePerpQueue(perp, 1200); + await rebase(collateralToken, rebaseOracle, -0.5); } await advancePerpQueueToRollover(perp, await bondAt(depositTranches[2].bond())); - await pricingStrategy.computeMatureTranchePrice.returns(toPriceFixedPtAmt("1.1")); }); it("should return the tranche value", async function () { - expect(await perp.callStatic.getReserveTrancheValue(perp.address)).to.eq("0"); - expect(await perp.callStatic.getReserveTrancheValue(collateralToken.address)).to.eq(toFixedPtAmt("880")); - expect(await perp.callStatic.getReserveTrancheValue(depositTranches[0].address)).to.eq("0"); - expect(await perp.callStatic.getReserveTrancheValue(depositTranches[1].address)).to.eq("0"); - expect(await perp.callStatic.getReserveTrancheValue(depositTranches[2].address)).to.eq(toFixedPtAmt("360")); - expect(await perp.callStatic.getReserveTrancheValue(depositTranches[3].address)).to.eq(toFixedPtAmt("360")); - expect(await perp.callStatic.getReserveTrancheValue(depositTranches[4].address)).to.eq(toFixedPtAmt("360")); + expect(await perp.callStatic.getReserveTokenValue(perp.address)).to.eq("0"); + expect(await perp.callStatic.getReserveTokenValue(collateralToken.address)).to.eq(toFixedPtAmt("93.75")); + expect(await perp.callStatic.getReserveTokenValue(depositTranches[0].address)).to.eq("0"); + expect(await perp.callStatic.getReserveTokenValue(depositTranches[1].address)).to.eq("0"); + expect(await perp.callStatic.getReserveTokenValue(depositTranches[2].address)).to.eq(toFixedPtAmt("125")); + expect(await perp.callStatic.getReserveTokenValue(depositTranches[3].address)).to.eq(toFixedPtAmt("250")); + expect(await perp.callStatic.getReserveTokenValue(depositTranches[4].address)).to.eq(toFixedPtAmt("500")); }); }); }); diff --git a/spot-contracts/test/perp/PerpetualTranche_deposit.ts b/spot-contracts/test/perp/PerpetualTranche_deposit.ts new file mode 100644 index 00000000..c6a860be --- /dev/null +++ b/spot-contracts/test/perp/PerpetualTranche_deposit.ts @@ -0,0 +1,481 @@ +import { expect, use } from "chai"; +import { network, ethers, upgrades } from "hardhat"; +import { Contract, Transaction, Signer } from "ethers"; +import { smock } from "@defi-wonderland/smock"; +import { + setupCollateralToken, + setupBondFactory, + createBondWithFactory, + depositIntoBond, + bondAt, + getTranches, + toPercFixedPtAmt, + toFixedPtAmt, + advancePerpQueue, + checkReserveComposition, + rebase, + mintCollteralToken, +} from "../helpers"; +use(smock.matchers); + +let perp: Contract, + bondFactory: Contract, + rebaseOracle: Contract, + collateralToken: Contract, + issuer: Contract, + feePolicy: Contract, + deployer: Signer, + deployerAddress: string, + depositBond: Contract, + depositTrancheA: Contract, + depositTrancheZ: Contract; +describe("PerpetualTranche", function () { + beforeEach(async function () { + await network.provider.send("hardhat_reset"); + + const accounts = await ethers.getSigners(); + deployer = accounts[0]; + deployerAddress = await deployer.getAddress(); + + bondFactory = await setupBondFactory(); + ({ collateralToken, rebaseOracle } = await setupCollateralToken("Bitcoin", "BTC")); + + const BondIssuer = await ethers.getContractFactory("BondIssuer"); + issuer = await upgrades.deployProxy( + BondIssuer.connect(deployer), + [bondFactory.address, collateralToken.address, 3600, [500, 500], 1200, 0], + { + initializer: "init(address,address,uint256,uint256[],uint256,uint256)", + }, + ); + + const FeePolicy = await ethers.getContractFactory("FeePolicy"); + feePolicy = await smock.fake(FeePolicy); + await feePolicy.decimals.returns(8); + + const PerpetualTranche = await ethers.getContractFactory("PerpetualTranche"); + perp = await upgrades.deployProxy( + PerpetualTranche.connect(deployer), + ["PerpetualTranche", "PERP", collateralToken.address, issuer.address, feePolicy.address], + { + initializer: "init(string,string,address,address,address)", + }, + ); + await advancePerpQueue(perp, 3600); + + const RolloverVault = await ethers.getContractFactory("RolloverVault"); + const vault = await smock.fake(RolloverVault); + await vault.getTVL.returns("0"); + await perp.updateVault(vault.address); + + depositBond = await bondAt(await perp.callStatic.getDepositBond()); + [depositTrancheA, depositTrancheZ] = await getTranches(depositBond); + + await depositIntoBond(depositBond, toFixedPtAmt("1000"), deployer); + await depositTrancheA.approve(perp.address, toFixedPtAmt("500")); + await depositTrancheZ.approve(perp.address, toFixedPtAmt("500")); + + await feePolicy.computePerpMintFeePerc.returns("0"); + }); + + afterEach(async function () { + await network.provider.send("hardhat_reset"); + }); + + describe("#deposit", function () { + describe("when paused", function () { + beforeEach(async function () { + await perp.updateKeeper(deployerAddress); + await perp.pause(); + }); + + it("should revert", async function () { + await expect(perp.deposit(depositTrancheA.address, toFixedPtAmt("500"))).to.revertedWith("Pausable: paused"); + }); + }); + + describe("when bond issuer is NOT set correctly", function () { + let bond: Contract; + beforeEach(async function () { + 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.getLatestBond.returns(bond.address); + }); + it("should not update the deposit bond", async function () { + await depositTrancheA.approve(perp.address, toFixedPtAmt("500")); + await expect(perp.deposit(depositTrancheA.address, toFixedPtAmt("500"))).to.not.be.reverted; + expect(await perp.callStatic.getDepositBond()).to.not.eq(bond.address); + }); + }); + + describe("when the trancheIn is not of deposit bond", function () { + beforeEach(async function () { + const bond = await createBondWithFactory(bondFactory, collateralToken, [500, 500], 3600); + await depositIntoBond(bond, toFixedPtAmt("1000"), deployer); + depositTrancheA = (await getTranches(bond))[0]; + }); + it("should revert", async function () { + await depositTrancheA.approve(perp.address, toFixedPtAmt("500")); + await expect(perp.deposit(depositTrancheA.address, toFixedPtAmt("500"))).to.revertedWithCustomError( + perp, + "UnexpectedAsset", + ); + }); + }); + + describe("when the malicious trancheIn is deposited which points to the deposit bond", function () { + it("should revert", async function () { + const ERC20 = await ethers.getContractFactory("MockTranche"); + const maliciousTranche = await ERC20.deploy(); + await maliciousTranche.init("Tranche", "TRA"); + 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.revertedWithCustomError( + perp, + "UnexpectedAsset", + ); + }); + }); + + describe("when user has not approved sufficient tranche tokens", function () { + beforeEach(async function () { + await depositTrancheA.approve(perp.address, "0"); + }); + it("should revert", async function () { + await expect(perp.deposit(depositTrancheA.address, toFixedPtAmt("500"))).to.revertedWith( + "ERC20: transfer amount exceeds allowance", + ); + }); + }); + + describe("when user has insufficient balance", function () { + beforeEach(async function () { + await depositTrancheA.transfer(perp.address, toFixedPtAmt("500")); + }); + it("should revert", async function () { + expect(await depositTrancheA.balanceOf(deployerAddress)).to.eq("0"); + await expect(perp.deposit(depositTrancheA.address, toFixedPtAmt("500"))).to.revertedWith( + "ERC20: transfer amount exceeds balance", + ); + }); + }); + + describe("when the supply cap is exceeded", function () { + beforeEach(async function () { + await perp.updateMintingLimits(toFixedPtAmt("499"), toFixedPtAmt("1000")); + }); + + it("should revert", async function () { + await expect(perp.deposit(depositTrancheA.address, toFixedPtAmt("500"))).to.revertedWithCustomError( + perp, + "ExceededMaxSupply", + ); + }); + }); + + describe("when the supply cap is exceeded and existing supply > 0", function () { + beforeEach(async function () { + await perp.deposit(depositTrancheA.address, toFixedPtAmt("400")); + await perp.updateMintingLimits(toFixedPtAmt("499"), toFixedPtAmt("1000")); + }); + + it("should revert", async function () { + await expect(perp.deposit(depositTrancheA.address, toFixedPtAmt("100"))).to.revertedWithCustomError( + perp, + "ExceededMaxSupply", + ); + }); + }); + + describe("when the tranche mint limit is exceeded", function () { + beforeEach(async function () { + await perp.updateMintingLimits(toFixedPtAmt("1000"), toFixedPtAmt("499")); + }); + + it("should revert", async function () { + await expect(perp.deposit(depositTrancheA.address, toFixedPtAmt("500"))).to.revertedWithCustomError( + perp, + "ExceededMaxMintPerTranche", + ); + }); + }); + + describe("when the tranche mint limit is exceeded and existing supply > 0", function () { + beforeEach(async function () { + await perp.deposit(depositTrancheA.address, toFixedPtAmt("400")); + await perp.updateMintingLimits(toFixedPtAmt("1000"), toFixedPtAmt("499")); + }); + + 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 return without minting", async function () { + expect(await perp.callStatic.deposit(depositTrancheA.address, "0")).to.eq("0"); + }); + }); + + describe("when depositing a junior", function () { + it("should return without minting", async function () { + await expect(perp.deposit(depositTrancheZ.address, toFixedPtAmt("500"))).to.revertedWithCustomError( + perp, + "UnexpectedAsset", + ); + }); + }); + + describe("when total supply is zero", function () { + describe("when tranche price is 1", function () { + it("should mint the correct amount", async function () { + const r = await perp.callStatic.computeMintAmt(depositTrancheA.address, toFixedPtAmt("500")); + expect(r).to.eq(toFixedPtAmt("500")); + }); + }); + + describe("when tranche price is 0.5", function () { + beforeEach(async function () { + await rebase(collateralToken, rebaseOracle, -0.75); + }); + + it("should mint the correct amount", async function () { + const r = await perp.callStatic.computeMintAmt(depositTrancheA.address, toFixedPtAmt("500")); + expect(r).to.eq(toFixedPtAmt("250")); + }); + }); + }); + + describe("when total supply > zero", function () { + let newBond: Contract, newTranche: Contract; + beforeEach(async function () { + await perp.deposit(depositTrancheA.address, toFixedPtAmt("200")); + + await advancePerpQueue(perp, 1200); + newBond = await bondAt(await perp.callStatic.getDepositBond()); + await depositIntoBond(newBond, toFixedPtAmt("1000"), deployer); + const tranches = await getTranches(newBond); + newTranche = tranches[0]; + await newTranche.approve(perp.address, toFixedPtAmt("250")); + }); + + describe("when price is eql to avg reserve price", function () { + it("should mint the correct amount", async function () { + const r = await perp.callStatic.computeMintAmt(newTranche.address, toFixedPtAmt("250")); + expect(r).to.eq(toFixedPtAmt("250")); + }); + }); + + describe("when price is < avg reserve price", function () { + beforeEach(async function () { + await mintCollteralToken(collateralToken, toFixedPtAmt("500"), deployer); + await collateralToken.transfer(perp.address, toFixedPtAmt("200")); + }); + + it("should mint the correct amount", async function () { + const r = await perp.callStatic.computeMintAmt(newTranche.address, toFixedPtAmt("250")); + expect(r).to.eq(toFixedPtAmt("125")); + }); + }); + + describe("when price is > avg reserve price", function () { + beforeEach(async function () { + await rebase(collateralToken, rebaseOracle, -0.75); + + await advancePerpQueue(perp, 1200); + newBond = await bondAt(await perp.callStatic.getDepositBond()); + await depositIntoBond(newBond, toFixedPtAmt("1000"), deployer); + const tranches = await getTranches(newBond); + newTranche = tranches[0]; + await newTranche.approve(perp.address, toFixedPtAmt("250")); + }); + + it("should mint the correct amount", async function () { + const r = await perp.callStatic.computeMintAmt(newTranche.address, toFixedPtAmt("250")); + expect(r).to.eq(toFixedPtAmt("500")); + }); + }); + }); + + describe("on successful deposit", function () { + it("should mint perp tokens", async function () { + await expect(() => perp.deposit(depositTrancheA.address, toFixedPtAmt("500"))).to.changeTokenBalance( + perp, + deployer, + toFixedPtAmt("500"), + ); + }); + it("should NOT withhold any fee amount", async function () { + await expect(() => perp.deposit(depositTrancheA.address, toFixedPtAmt("500"))).to.changeTokenBalance( + perp, + perp, + "0", + ); + }); + it("should transfer the tranches in", async function () { + await expect(() => perp.deposit(depositTrancheA.address, toFixedPtAmt("500"))).to.changeTokenBalances( + depositTrancheA, + [deployer, perp], + [toFixedPtAmt("-500"), toFixedPtAmt("500")], + ); + }); + it("should return the mintAmt", async function () { + const r = await perp.callStatic.computeMintAmt(depositTrancheA.address, toFixedPtAmt("500")); + expect(r).to.eq(toFixedPtAmt("500")); + }); + }); + + describe("when the reserve has no tranches", function () { + let tx: Transaction; + beforeEach(async function () { + expect(await perp.callStatic.getDepositBond()).to.eq(depositBond.address); + + await checkReserveComposition(perp, [collateralToken], ["0"]); + expect(await perp.totalSupply()).to.eq(0); + + tx = perp.deposit(depositTrancheA.address, toFixedPtAmt("500")); + await tx; + }); + + it("should NOT update the deposit bond", async function () { + expect(await perp.callStatic.getDepositBond()).to.eq(depositBond.address); + }); + + it("should emit reserve synced", async function () { + await expect(tx).to.emit(perp, "ReserveSynced").withArgs(depositTrancheA.address, toFixedPtAmt("500")); + }); + it("should update the reserve", async function () { + await checkReserveComposition(perp, [collateralToken, depositTrancheA], ["0", toFixedPtAmt("500")]); + }); + it("should update the total supply", async function () { + expect(await perp.totalSupply()).to.eq(toFixedPtAmt("500")); + }); + }); + + describe("when the reserve has tranches", function () { + beforeEach(async function () { + await perp.deposit(depositTrancheA.address, toFixedPtAmt("200")); + + expect(await perp.callStatic.getDepositBond()).to.eq(depositBond.address); + await checkReserveComposition(perp, [collateralToken, depositTrancheA], ["0", toFixedPtAmt("200")]); + + expect(await perp.totalSupply()).to.eq(toFixedPtAmt("200")); + }); + + describe("when inserting the an existing tranche", async function () { + let tx: Transaction; + beforeEach(async function () { + tx = perp.deposit(depositTrancheA.address, toFixedPtAmt("300")); + await tx; + }); + + it("should NOT update the deposit bond", async function () { + expect(await perp.callStatic.getDepositBond()).to.eq(depositBond.address); + }); + it("should emit reserve synced", async function () { + await expect(tx).to.emit(perp, "ReserveSynced").withArgs(depositTrancheA.address, toFixedPtAmt("500")); + }); + it("should update the reserve", async function () { + await checkReserveComposition(perp, [collateralToken, depositTrancheA], ["0", toFixedPtAmt("500")]); + }); + it("should update the total supply", async function () { + expect(await perp.totalSupply()).to.eq(toFixedPtAmt("500")); + }); + }); + + describe("when inserting a new tranche", function () { + let newBond: Contract, newTranche: Contract, tx: Transaction; + beforeEach(async function () { + await advancePerpQueue(perp, 1200); + + newBond = await bondAt(await perp.callStatic.getDepositBond()); + await depositIntoBond(newBond, toFixedPtAmt("1000"), deployer); + const tranches = await getTranches(newBond); + newTranche = tranches[0]; + await checkReserveComposition(perp, [collateralToken, depositTrancheA], ["0", toFixedPtAmt("200")]); + expect(await perp.totalSupply()).to.eq(toFixedPtAmt("200")); + await newTranche.approve(perp.address, toFixedPtAmt("250")); + tx = perp.deposit(newTranche.address, toFixedPtAmt("250")); + await tx; + }); + + it("should update the deposit bond", async function () { + expect(await perp.callStatic.getDepositBond()).to.eq(newBond.address); + }); + it("should emit reserve synced", async function () { + await expect(tx).to.emit(perp, "ReserveSynced").withArgs(newTranche.address, toFixedPtAmt("250")); + }); + it("should update the reserve", async function () { + await checkReserveComposition( + perp, + [collateralToken, depositTrancheA, newTranche], + ["0", toFixedPtAmt("200"), toFixedPtAmt("250")], + ); + }); + it("should update the total supply", async function () { + expect(await perp.totalSupply()).to.eq(toFixedPtAmt("450")); + }); + }); + }); + + describe("when fee is set", function () { + beforeEach(async function () { + await feePolicy.computePerpMintFeePerc.returns(toPercFixedPtAmt("0.01")); + }); + it("should mint perp tokens", async function () { + await expect(() => perp.deposit(depositTrancheA.address, toFixedPtAmt("500"))).to.changeTokenBalance( + perp, + deployer, + toFixedPtAmt("495"), + ); + }); + it("should transfer the tranches in", async function () { + await expect(() => perp.deposit(depositTrancheA.address, toFixedPtAmt("500"))).to.changeTokenBalances( + depositTrancheA, + [deployer, perp], + [toFixedPtAmt("-500"), toFixedPtAmt("500")], + ); + }); + it("should return the mintAmt", async function () { + const r = await perp.callStatic.computeMintAmt(depositTrancheA.address, toFixedPtAmt("500")); + expect(r).to.eq(toFixedPtAmt("495")); + }); + }); + + describe("when fee is set and caller is the vault", function () { + let mockVault: Contract; + beforeEach(async function () { + const MockVault = await ethers.getContractFactory("MockVault"); + mockVault = await MockVault.deploy(); + await perp.updateVault(mockVault.address); + await depositTrancheA.approve(mockVault.address, toFixedPtAmt("500")); + await feePolicy.computePerpMintFeePerc.returns(toPercFixedPtAmt("1")); + }); + it("should mint perp tokens", async function () { + await expect(() => + mockVault.mintPerps(perp.address, depositTrancheA.address, toFixedPtAmt("500")), + ).to.changeTokenBalance(perp, deployer, toFixedPtAmt("500")); + }); + it("should transfer the tranches in", async function () { + await expect(() => + mockVault.mintPerps(perp.address, depositTrancheA.address, toFixedPtAmt("500")), + ).to.changeTokenBalances(depositTrancheA, [deployer, perp], [toFixedPtAmt("-500"), toFixedPtAmt("500")]); + }); + it("should return the mintAmt", async function () { + const r = await mockVault.callStatic.computePerpMintAmt( + perp.address, + depositTrancheA.address, + toFixedPtAmt("500"), + ); + expect(r).to.eq(toFixedPtAmt("500")); + }); + }); + }); +}); diff --git a/spot-contracts/test/perp/PerpetualTranche_redeem.ts b/spot-contracts/test/perp/PerpetualTranche_redeem.ts new file mode 100644 index 00000000..61291431 --- /dev/null +++ b/spot-contracts/test/perp/PerpetualTranche_redeem.ts @@ -0,0 +1,708 @@ +import { expect, use } from "chai"; +import { network, ethers, upgrades } from "hardhat"; +import { Contract, Transaction, Signer } from "ethers"; +import { smock } from "@defi-wonderland/smock"; +import { + setupCollateralToken, + setupBondFactory, + depositIntoBond, + bondAt, + getTranches, + toPercFixedPtAmt, + toFixedPtAmt, + advancePerpQueue, + checkReserveComposition, + rebase, + mintCollteralToken, +} from "../helpers"; +use(smock.matchers); + +let perp: Contract, + bondFactory: Contract, + collateralToken: Contract, + rebaseOracle: Contract, + issuer: Contract, + feePolicy: Contract, + deployer: Signer, + otherUser: Signer, + deployerAddress: string, + depositBond: Contract, + initialDepositTranche: Contract; + +describe("PerpetualTranche", function () { + beforeEach(async function () { + await network.provider.send("hardhat_reset"); + + const accounts = await ethers.getSigners(); + deployer = accounts[0]; + otherUser = accounts[1]; + deployerAddress = await deployer.getAddress(); + + bondFactory = await setupBondFactory(); + ({ collateralToken, rebaseOracle } = await setupCollateralToken("Bitcoin", "BTC")); + + const BondIssuer = await ethers.getContractFactory("BondIssuer"); + issuer = await upgrades.deployProxy( + BondIssuer.connect(deployer), + [bondFactory.address, collateralToken.address, 3600, [500, 500], 1200, 0], + { + initializer: "init(address,address,uint256,uint256[],uint256,uint256)", + }, + ); + + const FeePolicy = await ethers.getContractFactory("FeePolicy"); + feePolicy = await smock.fake(FeePolicy); + await feePolicy.decimals.returns(8); + + const PerpetualTranche = await ethers.getContractFactory("PerpetualTranche"); + perp = await upgrades.deployProxy( + PerpetualTranche.connect(deployer), + ["PerpetualTranche", "PERP", collateralToken.address, issuer.address, feePolicy.address], + { + initializer: "init(string,string,address,address,address)", + }, + ); + await advancePerpQueue(perp, 3600); + + const RolloverVault = await ethers.getContractFactory("RolloverVault"); + const vault = await smock.fake(RolloverVault); + await vault.getTVL.returns("0"); + await perp.updateVault(vault.address); + + depositBond = await bondAt(await perp.callStatic.getDepositBond()); + [initialDepositTranche] = await getTranches(depositBond); + + await depositIntoBond(depositBond, toFixedPtAmt("1000"), deployer); + await initialDepositTranche.approve(perp.address, toFixedPtAmt("500")); + await perp.deposit(initialDepositTranche.address, toFixedPtAmt("500")); + await feePolicy.computePerpBurnFeePerc.returns("0"); + await mintCollteralToken(collateralToken, toFixedPtAmt("1000"), deployer); + }); + + afterEach(async function () { + await network.provider.send("hardhat_reset"); + }); + + describe("#burn", function () { + it("should burn tokens without redemption", async function () { + await checkReserveComposition(perp, [collateralToken, initialDepositTranche], ["0", toFixedPtAmt("500")]); + expect(await perp.balanceOf(deployerAddress)).to.eq(toFixedPtAmt("500")); + await perp.burn(toFixedPtAmt("500")); + expect(await perp.balanceOf(deployerAddress)).to.eq("0"); + await checkReserveComposition(perp, [collateralToken, initialDepositTranche], ["0", toFixedPtAmt("500")]); + }); + }); + + describe("#burnFrom", function () { + it("should burn tokens without redemption from authorized wallet", async function () { + await checkReserveComposition(perp, [collateralToken, initialDepositTranche], ["0", toFixedPtAmt("500")]); + expect(await perp.balanceOf(deployerAddress)).to.eq(toFixedPtAmt("500")); + await perp.approve(await otherUser.getAddress(), toFixedPtAmt("500")); + await perp.connect(otherUser).burnFrom(deployerAddress, toFixedPtAmt("200")); + expect(await perp.balanceOf(deployerAddress)).to.eq(toFixedPtAmt("300")); + expect(await perp.allowance(deployerAddress, await otherUser.getAddress())).to.eq(toFixedPtAmt("300")); + await checkReserveComposition(perp, [collateralToken, initialDepositTranche], ["0", toFixedPtAmt("500")]); + }); + }); + + describe("#redeem", function () { + describe("when paused", function () { + beforeEach(async function () { + await perp.updateKeeper(deployerAddress); + await perp.pause(); + }); + + it("should revert", async function () { + await expect(perp.redeem(toFixedPtAmt("500"))).to.be.revertedWith("Pausable: paused"); + }); + }); + + describe("when user has insufficient balance", function () { + beforeEach(async function () { + await perp.redeem(toFixedPtAmt("250")); + }); + + it("should revert", async function () { + expect(await perp.balanceOf(deployerAddress)).to.lte(toFixedPtAmt("500")); + await expect(perp.redeem(toFixedPtAmt("500"))).to.be.reverted; + }); + }); + + describe("when requested amount is zero", function () { + it("should return without redeeming", async function () { + expect(await perp.callStatic.redeem("0")).to.deep.eq([]); + }); + }); + + describe("when supply is zero", function () { + beforeEach(async function () { + await perp.burn(toFixedPtAmt("500")); + }); + + it("should revert", async function () { + await expect(perp.redeem(toFixedPtAmt("100"))).to.be.reverted; + }); + + it("should revert", async function () { + await expect(perp.callStatic.computeRedemptionAmts(toFixedPtAmt("100"))).to.be.reverted; + }); + }); + + describe("on successful redeem", function () { + it("should burn perp tokens", async function () { + await expect(() => perp.redeem(toFixedPtAmt("500"))).to.changeTokenBalance( + perp, + deployer, + toFixedPtAmt("-500"), + ); + }); + it("should transfer the tranches out", async function () { + await expect(() => perp.redeem(toFixedPtAmt("500"))).to.changeTokenBalances( + initialDepositTranche, + [deployer, perp], + [toFixedPtAmt("500"), toFixedPtAmt("-500")], + ); + }); + it("should emit reserve synced", async function () { + await expect(perp.redeem(toFixedPtAmt("500"))) + .to.emit(perp, "ReserveSynced") + .withArgs(collateralToken.address, "0") + .to.emit(perp, "ReserveSynced") + .withArgs(initialDepositTranche.address, "0"); + }); + it("should return the redemption amounts", async function () { + const r = await perp.callStatic.redeem(toFixedPtAmt("500")); + expect(r[0].token).to.eq(collateralToken.address); + expect(r[1].token).to.eq(initialDepositTranche.address); + expect(r[0].amount).to.eq("0"); + expect(r[1].amount).to.eq(toFixedPtAmt("500")); + }); + it("should return the redemption amounts", async function () { + const r = await perp.callStatic.computeRedemptionAmts(toFixedPtAmt("500")); + expect(r[0].token).to.eq(collateralToken.address); + expect(r[1].token).to.eq(initialDepositTranche.address); + expect(r[0].amount).to.eq("0"); + expect(r[1].amount).to.eq(toFixedPtAmt("500")); + }); + }); + + describe("when reserve has more than one tranche", function () { + let newRedemptionTranche: Contract, txFn: Promise; + beforeEach(async function () { + await perp.updateTolerableTrancheMaturity(1200, 3600); + + await advancePerpQueue(perp, 1200); + + const newBond = await bondAt(await perp.callStatic.getDepositBond()); + await depositIntoBond(newBond, toFixedPtAmt("1000"), deployer); + const tranches = await getTranches(newBond); + newRedemptionTranche = tranches[0]; + + await newRedemptionTranche.approve(perp.address, toFixedPtAmt("500")); + await perp.deposit(newRedemptionTranche.address, toFixedPtAmt("500")); + + await checkReserveComposition( + perp, + [collateralToken, initialDepositTranche, newRedemptionTranche], + ["0", toFixedPtAmt("500"), toFixedPtAmt("500")], + ); + expect(await perp.totalSupply()).to.eq(toFixedPtAmt("1000")); + + txFn = () => perp.redeem(toFixedPtAmt("375")); + }); + + it("should update the reserve composition", async function () { + await txFn(); + await checkReserveComposition( + perp, + [collateralToken, initialDepositTranche, newRedemptionTranche], + ["0", toFixedPtAmt("312.5"), toFixedPtAmt("312.5")], + ); + }); + + it("should transfer tokens out", async function () { + await expect(txFn).to.changeTokenBalances(collateralToken, [deployer, perp], ["0", "0"]); + }); + + it("should transfer tokens out", async function () { + await expect(txFn).to.changeTokenBalances( + initialDepositTranche, + [deployer, perp], + [toFixedPtAmt("187.5"), toFixedPtAmt("-187.5")], + ); + }); + + it("should transfer tokens out", async function () { + await expect(txFn).to.changeTokenBalances( + newRedemptionTranche, + [deployer, perp], + [toFixedPtAmt("187.5"), toFixedPtAmt("-187.5")], + ); + }); + + it("should update the total supply", async function () { + await txFn(); + expect(await perp.totalSupply()).to.eq(toFixedPtAmt("625")); + }); + + it("should burn perp tokens", async function () { + await expect(txFn).to.changeTokenBalances(perp, [deployer], [toFixedPtAmt("-375")]); + }); + }); + + describe("when reserve has mature collateral and tranches", function () { + let newRedemptionTranche: Contract, txFn: Promise; + beforeEach(async function () { + await perp.updateTolerableTrancheMaturity(1200, 3600); + await advancePerpQueue(perp, 1200); + + const newBond = await bondAt(await perp.callStatic.getDepositBond()); + await depositIntoBond(newBond, toFixedPtAmt("1000"), deployer); + const tranches = await getTranches(newBond); + newRedemptionTranche = tranches[0]; + + await newRedemptionTranche.approve(perp.address, toFixedPtAmt("500")); + await perp.deposit(newRedemptionTranche.address, toFixedPtAmt("500")); + + await collateralToken.transfer(perp.address, toFixedPtAmt("100")); + await checkReserveComposition( + perp, + [collateralToken, initialDepositTranche, newRedemptionTranche], + [toFixedPtAmt("100"), toFixedPtAmt("500"), toFixedPtAmt("500")], + ); + expect(await perp.totalSupply()).to.eq(toFixedPtAmt("1000")); + + txFn = () => perp.redeem(toFixedPtAmt("500")); + }); + + it("should update the reserve composition", async function () { + await txFn(); + await checkReserveComposition( + perp, + [collateralToken, initialDepositTranche, newRedemptionTranche], + [toFixedPtAmt("50"), toFixedPtAmt("250"), toFixedPtAmt("250")], + ); + }); + it("should transfer tokens out", async function () { + await expect(txFn).to.changeTokenBalances( + collateralToken, + [deployer, perp], + [toFixedPtAmt("50"), toFixedPtAmt("-50")], + ); + }); + + it("should transfer tokens out", async function () { + await expect(txFn).to.changeTokenBalances( + initialDepositTranche, + [deployer, perp], + [toFixedPtAmt("250"), toFixedPtAmt("-250")], + ); + }); + + it("should transfer tokens out", async function () { + await expect(txFn).to.changeTokenBalances( + newRedemptionTranche, + [deployer, perp], + [toFixedPtAmt("250"), toFixedPtAmt("-250")], + ); + }); + + it("should update the total supply", async function () { + await txFn(); + expect(await perp.totalSupply()).to.eq(toFixedPtAmt("500")); + }); + + it("should burn perp tokens", async function () { + await expect(txFn).to.changeTokenBalances(perp, [deployer], [toFixedPtAmt("-500")]); + }); + }); + + describe("when the collateralToken balance has rebased up", function () { + let newRedemptionTranche: Contract, txFn: Promise; + beforeEach(async function () { + await perp.updateTolerableTrancheMaturity(1200, 3600); + + await advancePerpQueue(perp, 1200); + + const newBond = await bondAt(await perp.callStatic.getDepositBond()); + await depositIntoBond(newBond, toFixedPtAmt("1000"), deployer); + const tranches = await getTranches(newBond); + newRedemptionTranche = tranches[0]; + + await newRedemptionTranche.approve(perp.address, toFixedPtAmt("500")); + await perp.deposit(newRedemptionTranche.address, toFixedPtAmt("500")); + + await checkReserveComposition( + perp, + [collateralToken, initialDepositTranche, newRedemptionTranche], + ["0", toFixedPtAmt("500"), toFixedPtAmt("500")], + ); + + await advancePerpQueue(perp, 2400); + await rebase(collateralToken, rebaseOracle, +0.5); + await checkReserveComposition( + perp, + [collateralToken, newRedemptionTranche], + [toFixedPtAmt("750"), toFixedPtAmt("500")], + ); + expect(await perp.totalSupply()).to.eq(toFixedPtAmt("1000")); + + txFn = () => perp.redeem(toFixedPtAmt("375")); + }); + + it("should update the reserve composition", async function () { + await txFn(); + await checkReserveComposition( + perp, + [collateralToken, newRedemptionTranche], + [toFixedPtAmt("468.75"), toFixedPtAmt("312.5")], + ); + }); + it("should transfer tokens out", async function () { + await expect(txFn).to.changeTokenBalances( + collateralToken, + [deployer, perp], + [toFixedPtAmt("281.25"), toFixedPtAmt("-281.25")], + ); + }); + it("should transfer tokens out", async function () { + await expect(txFn).to.changeTokenBalances( + newRedemptionTranche, + [deployer, perp], + [toFixedPtAmt("187.5"), toFixedPtAmt("-187.5")], + ); + }); + it("should update the total supply", async function () { + await txFn(); + expect(await perp.totalSupply()).to.eq(toFixedPtAmt("625")); + }); + it("should burn perp tokens", async function () { + await expect(txFn).to.changeTokenBalances(perp, [deployer], [toFixedPtAmt("-375")]); + }); + }); + + describe("when the collateralToken balance has rebased down", function () { + let newRedemptionTranche: Contract, txFn: Promise; + beforeEach(async function () { + await perp.updateTolerableTrancheMaturity(1200, 3600); + + await advancePerpQueue(perp, 1200); + + const newBond = await bondAt(await perp.callStatic.getDepositBond()); + await depositIntoBond(newBond, toFixedPtAmt("1000"), deployer); + const tranches = await getTranches(newBond); + newRedemptionTranche = tranches[0]; + + await newRedemptionTranche.approve(perp.address, toFixedPtAmt("500")); + await perp.deposit(newRedemptionTranche.address, toFixedPtAmt("500")); + + await checkReserveComposition( + perp, + [collateralToken, initialDepositTranche, newRedemptionTranche], + ["0", toFixedPtAmt("500"), toFixedPtAmt("500")], + ); + + await advancePerpQueue(perp, 2400); + await rebase(collateralToken, rebaseOracle, -0.5); + await checkReserveComposition( + perp, + [collateralToken, newRedemptionTranche], + [toFixedPtAmt("250"), toFixedPtAmt("500")], + ); + expect(await perp.totalSupply()).to.eq(toFixedPtAmt("1000")); + + txFn = () => perp.redeem(toFixedPtAmt("375")); + }); + + it("should update the reserve composition", async function () { + await txFn(); + await checkReserveComposition( + perp, + [collateralToken, newRedemptionTranche], + [toFixedPtAmt("156.25"), toFixedPtAmt("312.5")], + ); + }); + + it("should transfer tokens out", async function () { + await expect(txFn).to.changeTokenBalances( + collateralToken, + [deployer, perp], + [toFixedPtAmt("93.75"), toFixedPtAmt("-93.75")], + ); + }); + + it("should update the total supply", async function () { + await txFn(); + expect(await perp.totalSupply()).to.eq(toFixedPtAmt("625")); + }); + + it("should burn perp tokens", async function () { + await expect(txFn).to.changeTokenBalances(perp, [deployer], [toFixedPtAmt("-375")]); + }); + }); + + describe("when reserve has only mature collateral", function () { + let newRedemptionTranche: Contract, txFn: Promise; + beforeEach(async function () { + await perp.updateTolerableTrancheMaturity(1200, 3600); + + await advancePerpQueue(perp, 1200); + + const newBond = await bondAt(await perp.callStatic.getDepositBond()); + await depositIntoBond(newBond, toFixedPtAmt("1000"), deployer); + const tranches = await getTranches(newBond); + newRedemptionTranche = tranches[0]; + + await newRedemptionTranche.approve(perp.address, toFixedPtAmt("500")); + await perp.deposit(newRedemptionTranche.address, toFixedPtAmt("500")); + + await checkReserveComposition( + perp, + [collateralToken, initialDepositTranche, newRedemptionTranche], + ["0", toFixedPtAmt("500"), toFixedPtAmt("500")], + ); + await advancePerpQueue(perp, 7200); + + await checkReserveComposition(perp, [collateralToken], [toFixedPtAmt("1000")]); + expect(await perp.totalSupply()).to.eq(toFixedPtAmt("1000")); + + txFn = () => perp.redeem(toFixedPtAmt("375")); + }); + + it("should update the reserve composition", async function () { + await txFn(); + await checkReserveComposition(perp, [collateralToken], [toFixedPtAmt("625")]); + }); + + it("should transfer tokens out", async function () { + await expect(txFn).to.changeTokenBalances( + collateralToken, + [deployer, perp], + [toFixedPtAmt("375"), toFixedPtAmt("-375")], + ); + }); + + it("should update the total supply", async function () { + await txFn(); + expect(await perp.totalSupply()).to.eq(toFixedPtAmt("625")); + }); + + it("should burn perp tokens", async function () { + await expect(txFn).to.changeTokenBalances(perp, [deployer], [toFixedPtAmt("-375")]); + }); + }); + + describe("when redeeming entire supply", function () { + let newRedemptionTranche: Contract, txFn: Promise; + beforeEach(async function () { + await perp.updateTolerableTrancheMaturity(1200, 3600); + + await advancePerpQueue(perp, 1200); + + const newBond = await bondAt(await perp.callStatic.getDepositBond()); + await depositIntoBond(newBond, toFixedPtAmt("1000"), deployer); + const tranches = await getTranches(newBond); + newRedemptionTranche = tranches[0]; + + await newRedemptionTranche.approve(perp.address, toFixedPtAmt("500")); + await perp.deposit(newRedemptionTranche.address, toFixedPtAmt("500")); + + await checkReserveComposition( + perp, + [collateralToken, initialDepositTranche, newRedemptionTranche], + ["0", toFixedPtAmt("500"), toFixedPtAmt("500")], + ); + expect(await perp.totalSupply()).to.eq(toFixedPtAmt("1000")); + + const bal = await perp.balanceOf(deployerAddress); + txFn = () => perp.redeem(bal); + }); + + it("should update the reserve composition", async function () { + await txFn(); + await checkReserveComposition(perp, [collateralToken], ["0"]); + }); + + it("should transfer tokens out", async function () { + await expect(txFn).to.changeTokenBalances(collateralToken, [deployer, perp], ["0", "0"]); + }); + + it("should transfer tokens out", async function () { + await expect(txFn).to.changeTokenBalances( + initialDepositTranche, + [deployer, perp], + [toFixedPtAmt("500"), toFixedPtAmt("-500")], + ); + }); + + it("should transfer tokens out", async function () { + await expect(txFn).to.changeTokenBalances( + newRedemptionTranche, + [deployer, perp], + [toFixedPtAmt("500"), toFixedPtAmt("-500")], + ); + }); + + it("should update the total supply", async function () { + await txFn(); + expect(await perp.totalSupply()).to.eq("0"); + }); + + it("should burn perp tokens", async function () { + await expect(txFn).to.changeTokenBalances(perp, [deployer], [toFixedPtAmt("-1000")]); + }); + }); + + describe("when fee is set", function () { + let newRedemptionTranche: Contract, txFn: Promise; + beforeEach(async function () { + await perp.updateTolerableTrancheMaturity(1200, 3600); + await advancePerpQueue(perp, 1200); + + const newBond = await bondAt(await perp.callStatic.getDepositBond()); + await depositIntoBond(newBond, toFixedPtAmt("1000"), deployer); + const tranches = await getTranches(newBond); + newRedemptionTranche = tranches[0]; + + await newRedemptionTranche.approve(perp.address, toFixedPtAmt("500")); + await perp.deposit(newRedemptionTranche.address, toFixedPtAmt("500")); + + await collateralToken.transfer(perp.address, toFixedPtAmt("100")); + await checkReserveComposition( + perp, + [collateralToken, initialDepositTranche, newRedemptionTranche], + [toFixedPtAmt("100"), toFixedPtAmt("500"), toFixedPtAmt("500")], + ); + expect(await perp.totalSupply()).to.eq(toFixedPtAmt("1000")); + + await feePolicy.computePerpBurnFeePerc.returns(toPercFixedPtAmt("0.1")); + txFn = () => perp.redeem(toFixedPtAmt("500")); + }); + + it("should update the reserve composition", async function () { + await txFn(); + await checkReserveComposition( + perp, + [collateralToken, initialDepositTranche, newRedemptionTranche], + [toFixedPtAmt("55"), toFixedPtAmt("275"), toFixedPtAmt("275")], + ); + }); + + it("should transfer tokens out", async function () { + await expect(txFn).to.changeTokenBalances( + collateralToken, + [deployer, perp], + [toFixedPtAmt("45"), toFixedPtAmt("-45")], + ); + }); + + it("should transfer tokens out", async function () { + await expect(txFn).to.changeTokenBalances( + initialDepositTranche, + [deployer, perp], + [toFixedPtAmt("225"), toFixedPtAmt("-225")], + ); + }); + + it("should transfer tokens out", async function () { + await expect(txFn).to.changeTokenBalances( + newRedemptionTranche, + [deployer, perp], + [toFixedPtAmt("225"), toFixedPtAmt("-225")], + ); + }); + + it("should update the total supply", async function () { + await txFn(); + expect(await perp.totalSupply()).to.eq(toFixedPtAmt("500")); + }); + + it("should burn perp tokens", async function () { + await expect(txFn).to.changeTokenBalances(perp, [deployer], [toFixedPtAmt("-500")]); + }); + }); + + describe("when fee is set and caller is vault", function () { + let newRedemptionTranche: Contract, txFn: Promise, mockVault: Contract; + beforeEach(async function () { + await perp.updateTolerableTrancheMaturity(1200, 3600); + await advancePerpQueue(perp, 1200); + + const newBond = await bondAt(await perp.callStatic.getDepositBond()); + await depositIntoBond(newBond, toFixedPtAmt("1000"), deployer); + const tranches = await getTranches(newBond); + newRedemptionTranche = tranches[0]; + + await newRedemptionTranche.approve(perp.address, toFixedPtAmt("500")); + await perp.deposit(newRedemptionTranche.address, toFixedPtAmt("500")); + + await collateralToken.transfer(perp.address, toFixedPtAmt("100")); + await checkReserveComposition( + perp, + [collateralToken, initialDepositTranche, newRedemptionTranche], + [toFixedPtAmt("100"), toFixedPtAmt("500"), toFixedPtAmt("500")], + ); + expect(await perp.totalSupply()).to.eq(toFixedPtAmt("1000")); + await feePolicy.computePerpBurnFeePerc.returns(toPercFixedPtAmt("1")); + + const MockVault = await ethers.getContractFactory("MockVault"); + mockVault = await MockVault.deploy(); + await perp.updateVault(mockVault.address); + await perp.approve(mockVault.address, toFixedPtAmt("500")); + txFn = () => mockVault.redeemPerps(perp.address, toFixedPtAmt("500")); + }); + + it("should update the reserve composition", async function () { + await txFn(); + await checkReserveComposition( + perp, + [collateralToken, initialDepositTranche, newRedemptionTranche], + [toFixedPtAmt("50"), toFixedPtAmt("250"), toFixedPtAmt("250")], + ); + }); + + it("should transfer tokens out", async function () { + await expect(txFn).to.changeTokenBalances( + collateralToken, + [deployer, perp], + [toFixedPtAmt("50"), toFixedPtAmt("-50")], + ); + }); + + it("should transfer tokens out", async function () { + await expect(txFn).to.changeTokenBalances( + initialDepositTranche, + [deployer, perp], + [toFixedPtAmt("250"), toFixedPtAmt("-250")], + ); + }); + + it("should transfer tokens out", async function () { + await expect(txFn).to.changeTokenBalances( + newRedemptionTranche, + [deployer, perp], + [toFixedPtAmt("250"), toFixedPtAmt("-250")], + ); + }); + + it("should update the total supply", async function () { + await txFn(); + expect(await perp.totalSupply()).to.eq(toFixedPtAmt("500")); + }); + + it("should burn perp tokens", async function () { + await expect(txFn).to.changeTokenBalances(perp, [deployer], [toFixedPtAmt("-500")]); + }); + + it("should return the redemption amounts", async function () { + const r = await mockVault.callStatic.computePerpRedemptionAmts(perp.address, toFixedPtAmt("500")); + expect(r[0].token).to.eq(collateralToken.address); + expect(r[1].token).to.eq(initialDepositTranche.address); + expect(r[2].token).to.eq(newRedemptionTranche.address); + expect(r[0].amount).to.eq(toFixedPtAmt("50")); + expect(r[1].amount).to.eq(toFixedPtAmt("250")); + expect(r[2].amount).to.eq(toFixedPtAmt("250")); + }); + }); + }); +}); diff --git a/spot-contracts/test/perp/PerpetualTranche_rollover.ts b/spot-contracts/test/perp/PerpetualTranche_rollover.ts new file mode 100644 index 00000000..c6a2787d --- /dev/null +++ b/spot-contracts/test/perp/PerpetualTranche_rollover.ts @@ -0,0 +1,908 @@ +import { expect, use } from "chai"; +import { network, ethers, upgrades } from "hardhat"; +import { Contract, Transaction, Signer } from "ethers"; +import { smock } from "@defi-wonderland/smock"; +import { + setupCollateralToken, + setupBondFactory, + createBondWithFactory, + depositIntoBond, + bondAt, + getTranches, + toFixedPtAmt, + toPercFixedPtAmt, + advancePerpQueue, + advancePerpQueueToBondMaturity, + advancePerpQueueToRollover, + checkReserveComposition, + rebase, + mintCollteralToken, +} from "../helpers"; +use(smock.matchers); + +let perp: Contract, + bondFactory: Contract, + collateralToken: Contract, + rebaseOracle: Contract, + issuer: Contract, + feePolicy: Contract, + deployer: Signer, + deployerAddress: string, + holdingPenBond: Contract, + holdingPenTranche1: Contract, + reserveBond: Contract, + reserveTranche: Contract, + rolloverInBond: Contract, + rolloverInTranche: Contract, + mockVault: Contract; + +describe("PerpetualTranche", function () { + beforeEach(async function () { + await network.provider.send("hardhat_reset"); + + const accounts = await ethers.getSigners(); + deployer = accounts[0]; + deployerAddress = await deployer.getAddress(); + + bondFactory = await setupBondFactory(); + ({ collateralToken, rebaseOracle } = await setupCollateralToken("Bitcoin", "BTC")); + + const BondIssuer = await ethers.getContractFactory("BondIssuer"); + issuer = await upgrades.deployProxy( + BondIssuer.connect(deployer), + [bondFactory.address, collateralToken.address, 10800, [500, 500], 1200, 0], + { + initializer: "init(address,address,uint256,uint256[],uint256,uint256)", + }, + ); + + const FeePolicy = await ethers.getContractFactory("FeePolicy"); + feePolicy = await smock.fake(FeePolicy); + await feePolicy.decimals.returns(8); + + const MockVault = await ethers.getContractFactory("MockVault"); + mockVault = await MockVault.deploy(); + + const PerpetualTranche = await ethers.getContractFactory("PerpetualTranche"); + perp = await upgrades.deployProxy( + PerpetualTranche.connect(deployer), + ["PerpetualTranche", "PERP", collateralToken.address, issuer.address, feePolicy.address], + { + initializer: "init(string,string,address,address,address)", + }, + ); + + await perp.updateTolerableTrancheMaturity(1200, 10800); + await advancePerpQueue(perp, 10900); + await perp.updateVault(mockVault.address); + + holdingPenBond = await bondAt(await perp.callStatic.getDepositBond()); + [holdingPenTranche1] = await getTranches(holdingPenBond); + + await depositIntoBond(holdingPenBond, toFixedPtAmt("2000"), deployer); + + await holdingPenTranche1.approve(perp.address, toFixedPtAmt("500")); + await perp.deposit(holdingPenTranche1.address, toFixedPtAmt("500")); + + await advancePerpQueue(perp, 1200); + + reserveBond = await bondAt(await perp.callStatic.getDepositBond()); + [reserveTranche] = await getTranches(reserveBond); + + await depositIntoBond(reserveBond, toFixedPtAmt("2000"), deployer); + + await reserveTranche.approve(perp.address, toFixedPtAmt("500")); + await perp.deposit(reserveTranche.address, toFixedPtAmt("500")); + await advancePerpQueueToBondMaturity(perp, holdingPenBond); + + rolloverInBond = await bondAt(await perp.callStatic.getDepositBond()); + [rolloverInTranche] = await getTranches(rolloverInBond); + + await depositIntoBond(rolloverInBond, toFixedPtAmt("5000"), deployer); + await rolloverInTranche.approve(perp.address, toFixedPtAmt("5000")); + await perp.deposit(rolloverInTranche.address, toFixedPtAmt("500")); + + await checkReserveComposition( + perp, + [collateralToken, reserveTranche, rolloverInTranche], + [toFixedPtAmt("500"), toFixedPtAmt("500"), toFixedPtAmt("500")], + ); + + await rolloverInTranche.approve(mockVault.address, toFixedPtAmt("5000")); + await reserveTranche.approve(mockVault.address, toFixedPtAmt("5000")); + }); + + afterEach(async function () { + await network.provider.send("hardhat_reset"); + }); + + describe("#rollover", function () { + describe("when paused", function () { + beforeEach(async function () { + await perp.updateKeeper(deployerAddress); + await perp.pause(); + }); + + it("should revert", async function () { + await expect( + mockVault.rollover(perp.address, rolloverInTranche.address, reserveTranche.address, toFixedPtAmt("500")), + ).to.revertedWith("Pausable: paused"); + }); + }); + + describe("when rollover vault reference is set", function () { + beforeEach(async function () { + await perp.updateVault(mockVault.address); + }); + + it("should revert when invoked from other addresses", async function () { + await expect( + perp.rollover(rolloverInTranche.address, reserveTranche.address, toFixedPtAmt("500")), + ).to.revertedWithCustomError(perp, "UnauthorizedCall"); + }); + + it("should NOT revert when invoked from the vault ", async function () { + await rolloverInTranche.approve(mockVault.address, toFixedPtAmt("500")); + await expect( + mockVault.rollover(perp.address, rolloverInTranche.address, reserveTranche.address, toFixedPtAmt("500")), + ).not.to.be.reverted; + }); + }); + + describe("when trancheIn and tokenOut belong to the same bond", function () { + let tranches: Contract[]; + beforeEach(async function () { + tranches = await getTranches(rolloverInBond); + }); + it("should revert", async function () { + await expect( + mockVault.rollover(perp.address, rolloverInTranche.address, tranches[1].address, toFixedPtAmt("500")), + ).to.revertedWithCustomError(perp, "UnacceptableRollover"); + }); + }); + + describe("when trancheIn is NOT of deposit bond", function () { + it("should revert", async function () { + await expect( + mockVault.rollover(perp.address, reserveTranche.address, collateralToken.address, toFixedPtAmt("500")), + ).to.revertedWithCustomError(perp, "UnacceptableRollover"); + await expect( + mockVault.rollover(perp.address, reserveTranche.address, reserveTranche.address, toFixedPtAmt("500")), + ).to.revertedWithCustomError(perp, "UnacceptableRollover"); + await expect( + mockVault.rollover(perp.address, reserveTranche.address, reserveTranche.address, toFixedPtAmt("500")), + ).to.revertedWithCustomError(perp, "UnacceptableRollover"); + await expect( + mockVault.rollover(perp.address, reserveTranche.address, holdingPenTranche1.address, toFixedPtAmt("500")), + ).to.revertedWithCustomError(perp, "UnacceptableRollover"); + }); + }); + + describe("when tokenOut is NOT in the reserve", function () { + let maliciousTranche: Contract; + beforeEach(async function () { + const bond = await createBondWithFactory(bondFactory, collateralToken, [1, 999], 86400); + maliciousTranche = (await getTranches(bond))[0]; + }); + it("should revert", async function () { + await expect( + mockVault.rollover(perp.address, rolloverInTranche.address, maliciousTranche.address, toFixedPtAmt("500")), + ).to.revertedWithCustomError(perp, "UnacceptableRollover"); + }); + }); + + describe("when tokenOut is still isAcceptableForReserve", function () { + let newRotationInTranche: Contract; + beforeEach(async function () { + await advancePerpQueue(perp, 1200); + const newRotationInBond = await bondAt(await perp.callStatic.getDepositBond()); + [newRotationInTranche] = await getTranches(newRotationInBond); + await depositIntoBond(newRotationInBond, toFixedPtAmt("2000"), deployer); + await newRotationInTranche.approve(mockVault.address, toFixedPtAmt("500")); + }); + it("should revert", async function () { + await expect( + mockVault.rollover( + perp.address, + newRotationInTranche.address, + rolloverInTranche.address, + toFixedPtAmt("500"), + ), + ).to.revertedWithCustomError(perp, "UnacceptableRollover"); + }); + }); + + describe("when the malicious trancheIn which points to the deposit bond is rolled in", function () { + let maliciousTranche: Contract; + beforeEach(async function () { + const ERC20 = await ethers.getContractFactory("MockTranche"); + maliciousTranche = await ERC20.deploy(); + await maliciousTranche.init("Tranche", "TRA"); + await maliciousTranche.mint(deployerAddress, toFixedPtAmt("500")); + await maliciousTranche.setBond(await perp.callStatic.getDepositBond()); + await maliciousTranche.approve(mockVault.address, toFixedPtAmt("500")); + }); + it("should revert", async function () { + await expect( + mockVault.rollover(perp.address, maliciousTranche.address, reserveTranche.address, toFixedPtAmt("500")), + ).to.revertedWithCustomError(perp, "UnacceptableRollover"); + }); + }); + + describe("when user has insufficient tranche balance", function () { + beforeEach(async function () { + await rolloverInTranche.transfer("0x000000000000000000000000000000000000dead", toFixedPtAmt("1501")); + }); + + it("should revert", async function () { + expect(await rolloverInTranche.balanceOf(deployerAddress)).to.lt(toFixedPtAmt("500")); + await expect( + mockVault.rollover(perp.address, rolloverInTranche.address, reserveTranche.address, toFixedPtAmt("500")), + ).to.revertedWith("ERC20: transfer amount exceeds balance"); + }); + }); + + describe("when approval is insufficient", function () { + it("should return without rollover", async function () { + await rolloverInTranche.transfer(mockVault.address, toFixedPtAmt("500")); + await expect( + mockVault.callRollover(perp.address, rolloverInTranche.address, reserveTranche.address, toFixedPtAmt("500")), + ).to.revertedWith("ERC20: transfer amount exceeds allowance"); + }); + }); + + describe("when trancheInAmt is zero", function () { + it("should return without rollover", async function () { + const r = await mockVault.callStatic.rollover( + perp.address, + rolloverInTranche.address, + reserveTranche.address, + "0", + ); + expect(r.tokenOutAmt).to.eq("0"); + expect(r.trancheInAmt).to.eq("0"); + }); + }); + + describe("when trancheIn is not acceptable", function () { + let newRotationInTranche: Contract; + beforeEach(async function () { + const tranches = await getTranches(rolloverInBond); + newRotationInTranche = tranches[1]; + await newRotationInTranche.approve(mockVault.address, toFixedPtAmt("500")); + }); + + it("should revert", async function () { + await expect( + mockVault.rollover(perp.address, newRotationInTranche.address, reserveTranche.address, toFixedPtAmt("500")), + ).to.revertedWithCustomError(perp, "UnacceptableRollover"); + }); + }); + + describe("when trancheIn price is 0.5", function () { + beforeEach(async function () { + await rebase(collateralToken, rebaseOracle, -0.75); + await mintCollteralToken(collateralToken, toFixedPtAmt("1000"), deployer); + await collateralToken.transfer(perp.address, toFixedPtAmt("1000")); + }); + + it("should rollover the correct amount", async function () { + const r = await perp.callStatic.computeRolloverAmt( + rolloverInTranche.address, + collateralToken.address, + toFixedPtAmt("500"), + ); + expect(r.tokenOutAmt).to.eq(toFixedPtAmt("250")); + expect(r.trancheInAmt).to.eq(toFixedPtAmt("500")); + }); + }); + + describe("trancheOut price is 0.5", function () { + let newRotationInTranche: Contract, newReserveTranche: Contract; + beforeEach(async function () { + await rebase(collateralToken, rebaseOracle, -0.75); + await advancePerpQueueToRollover(perp, await bondAt(await rolloverInTranche.bond())); + + newReserveTranche = rolloverInTranche; + const newDepositBond = await bondAt(await perp.callStatic.getDepositBond()); + [newRotationInTranche] = await getTranches(newDepositBond); + }); + + it("should rollover the correct amount", async function () { + const r = await perp.callStatic.computeRolloverAmt( + newRotationInTranche.address, + newReserveTranche.address, + toFixedPtAmt("500"), + ); + expect(r.tokenOutAmt).to.eq(toFixedPtAmt("500")); + expect(r.trancheInAmt).to.eq(toFixedPtAmt("250")); + }); + }); + + describe("tokenOut is collateral which rebased up", function () { + beforeEach(async function () { + await rebase(collateralToken, rebaseOracle, +1); + }); + + it("should rollover the correct amount", async function () { + const r = await perp.callStatic.computeRolloverAmt( + rolloverInTranche.address, + collateralToken.address, + toFixedPtAmt("500"), + ); + expect(r.tokenOutAmt).to.eq(toFixedPtAmt("500")); + expect(r.trancheInAmt).to.eq(toFixedPtAmt("500")); + }); + }); + + describe("tokenOut is collateral which rebased down", function () { + beforeEach(async function () { + await rebase(collateralToken, rebaseOracle, -0.5); + }); + + it("should rollover the correct amount", async function () { + const r = await perp.callStatic.computeRolloverAmt( + rolloverInTranche.address, + collateralToken.address, + toFixedPtAmt("500"), + ); + expect(r.tokenOutAmt).to.eq(toFixedPtAmt("250")); + expect(r.trancheInAmt).to.eq(toFixedPtAmt("250")); + }); + }); + + describe("when trancheIn price is 0.5 and tokenOut is collateral which rebased up", function () { + beforeEach(async function () { + await rebase(collateralToken, rebaseOracle, -0.75); + // simulating collateral rebase up, by just transferring some tokens in + await mintCollteralToken(collateralToken, toFixedPtAmt("1000"), deployer); + await collateralToken.transfer(perp.address, toFixedPtAmt("1000")); + }); + + it("should rollover the correct amount", async function () { + const r = await perp.callStatic.computeRolloverAmt( + rolloverInTranche.address, + collateralToken.address, + toFixedPtAmt("500"), + ); + expect(r.tokenOutAmt).to.eq(toFixedPtAmt("250")); + expect(r.trancheInAmt).to.eq(toFixedPtAmt("500")); + }); + }); + + describe("when trancheIn price is 0.5 and tokenOut is collateral which rebased down", function () { + beforeEach(async function () { + await mintCollteralToken(collateralToken, toFixedPtAmt("1000"), deployer); + await collateralToken.transfer(perp.address, toFixedPtAmt("1000")); + await rebase(collateralToken, rebaseOracle, -0.75); + }); + + it("should rollover the correct amount", async function () { + const r = await perp.callStatic.computeRolloverAmt( + rolloverInTranche.address, + collateralToken.address, + toFixedPtAmt("500"), + ); + expect(r.tokenOutAmt).to.eq(toFixedPtAmt("250")); + expect(r.trancheInAmt).to.eq(toFixedPtAmt("500")); + }); + }); + + describe("when tokenOut is collateral has rebased down", function () { + beforeEach(async function () { + await rebase(collateralToken, rebaseOracle, -0.25); + }); + it("should rollover the correct amount", async function () { + const r = await perp.callStatic.computeRolloverAmt( + rolloverInTranche.address, + collateralToken.address, + toFixedPtAmt("500"), + ); + expect(r.tokenOutAmt).to.eq(toFixedPtAmt("375")); + expect(r.trancheInAmt).to.eq(toFixedPtAmt("375")); + }); + }); + + describe("when tokenOut is collateral has rebased up", function () { + beforeEach(async function () { + await rebase(collateralToken, rebaseOracle, 0.25); + }); + it("should rollover the correct amount", async function () { + const r = await perp.callStatic.computeRolloverAmt( + rolloverInTranche.address, + collateralToken.address, + toFixedPtAmt("500"), + ); + expect(r.tokenOutAmt).to.eq(toFixedPtAmt("500")); + expect(r.trancheInAmt).to.eq(toFixedPtAmt("500")); + }); + }); + + describe("when fee is zero", function () { + beforeEach(async function () { + await feePolicy.computePerpRolloverFeePerc.returns("0"); + }); + it("should transfer the tranches in", async function () { + await expect(() => + mockVault.rollover(perp.address, rolloverInTranche.address, reserveTranche.address, toFixedPtAmt("500")), + ).to.changeTokenBalances(rolloverInTranche, [deployer, perp], [toFixedPtAmt("-500"), toFixedPtAmt("500")]); + }); + it("should transfer the tranches out", async function () { + await expect(() => + mockVault.rollover(perp.address, rolloverInTranche.address, reserveTranche.address, toFixedPtAmt("500")), + ).to.changeTokenBalances(reserveTranche, [deployer, perp], [toFixedPtAmt("500"), toFixedPtAmt("-500")]); + }); + it("should charge fee", async function () { + await expect(() => + mockVault.rollover(perp.address, rolloverInTranche.address, reserveTranche.address, toFixedPtAmt("500")), + ).to.changeTokenBalance(perp, perp, "0"); + }); + it("should calculate rollover amt", async function () { + const r = await perp.callStatic.computeRolloverAmt( + rolloverInTranche.address, + reserveTranche.address, + toFixedPtAmt("500"), + ); + expect(r.tokenOutAmt).to.eq(toFixedPtAmt("500")); + expect(r.trancheInAmt).to.eq(toFixedPtAmt("500")); + }); + }); + + describe("when fee > 0", function () { + beforeEach(async function () { + await feePolicy.computePerpRolloverFeePerc.returns(toPercFixedPtAmt("0.01")); + }); + it("should transfer the tranches in", async function () { + await expect(() => + mockVault.rollover(perp.address, rolloverInTranche.address, reserveTranche.address, toFixedPtAmt("500")), + ).to.changeTokenBalances(rolloverInTranche, [deployer, perp], [toFixedPtAmt("-500"), toFixedPtAmt("500")]); + }); + it("should transfer the tranches out", async function () { + await expect(() => + mockVault.rollover(perp.address, rolloverInTranche.address, reserveTranche.address, toFixedPtAmt("500")), + ).to.changeTokenBalances(reserveTranche, [deployer, perp], [toFixedPtAmt("495"), toFixedPtAmt("-495")]); + }); + it("should calculate rollover amt", async function () { + const r = await perp.callStatic.computeRolloverAmt( + rolloverInTranche.address, + reserveTranche.address, + toFixedPtAmt("500"), + ); + expect(r.tokenOutAmt).to.eq(toFixedPtAmt("495")); + expect(r.trancheInAmt).to.eq(toFixedPtAmt("500")); + }); + }); + + describe("when fee < 0", function () { + beforeEach(async function () { + await feePolicy.computePerpRolloverFeePerc.returns(toPercFixedPtAmt("-0.01")); + }); + it("should transfer the tranches in", async function () { + await expect(() => + mockVault.rollover(perp.address, rolloverInTranche.address, reserveTranche.address, toFixedPtAmt("500")), + ).to.changeTokenBalances( + rolloverInTranche, + [deployer, perp], + [toFixedPtAmt("-495.049504950495049505"), toFixedPtAmt("495.049504950495049505")], + ); + }); + it("should transfer the tranches out", async function () { + await expect(() => + mockVault.rollover(perp.address, rolloverInTranche.address, reserveTranche.address, toFixedPtAmt("500")), + ).to.changeTokenBalances(reserveTranche, [deployer, perp], [toFixedPtAmt("500"), toFixedPtAmt("-500")]); + }); + it("should calculate rollover amt", async function () { + const r = await perp.callStatic.computeRolloverAmt( + rolloverInTranche.address, + reserveTranche.address, + toFixedPtAmt("500"), + ); + expect(r.tokenOutAmt).to.eq(toFixedPtAmt("500")); + expect(r.trancheInAmt).to.eq(toFixedPtAmt("495.049504950495049505")); + }); + }); + + describe("when trancheIn is NOT yet in the reserve", async function () { + let tx: Transaction, newRotationInTranche: Contract, r: any; + beforeEach(async function () { + await advancePerpQueueToBondMaturity(perp, rolloverInBond); // advancing to next issuance + const newRolloverInBond = await bondAt(await perp.callStatic.getDepositBond()); + await depositIntoBond(newRolloverInBond, toFixedPtAmt("1000"), deployer); + [newRotationInTranche] = await getTranches(newRolloverInBond); + await newRotationInTranche.approve(mockVault.address, toFixedPtAmt("250")); + r = await perp.callStatic.computeRolloverAmt( + newRotationInTranche.address, + collateralToken.address, + toFixedPtAmt("250"), + ); + tx = mockVault.rollover( + perp.address, + newRotationInTranche.address, + collateralToken.address, + toFixedPtAmt("250"), + ); + await tx; + }); + it("should update the reserve", async function () { + await checkReserveComposition( + perp, + [collateralToken, newRotationInTranche], + [toFixedPtAmt("1250"), toFixedPtAmt("250")], + ); + }); + it("should emit reserve synced", async function () { + await expect(tx) + .to.emit(perp, "ReserveSynced") + .withArgs(newRotationInTranche.address, toFixedPtAmt("250")) + .to.emit(perp, "ReserveSynced") + .withArgs(collateralToken.address, toFixedPtAmt("1250")); + }); + it("should compute the rollover amounts", async function () { + expect(r.tokenOutAmt).to.eq(toFixedPtAmt("250")); + expect(r.trancheInAmt).to.eq(toFixedPtAmt("250")); + }); + }); + + describe("when tokenOut is a reserve tranche", async function () { + let tx: Transaction, r: any; + beforeEach(async function () { + r = await perp.callStatic.computeRolloverAmt( + rolloverInTranche.address, + reserveTranche.address, + toFixedPtAmt("250"), + ); + tx = mockVault.rollover(perp.address, rolloverInTranche.address, reserveTranche.address, toFixedPtAmt("250")); + await tx; + }); + + it("should update the reserve", async function () { + await checkReserveComposition( + perp, + [collateralToken, reserveTranche, rolloverInTranche], + [toFixedPtAmt("500"), toFixedPtAmt("250"), toFixedPtAmt("750")], + ); + }); + it("should emit reserve synced", async function () { + await expect(tx) + .to.emit(perp, "ReserveSynced") + .withArgs(rolloverInTranche.address, toFixedPtAmt("750")) + .to.emit(perp, "ReserveSynced") + .withArgs(reserveTranche.address, toFixedPtAmt("250")); + }); + it("should compute the rollover amounts", async function () { + expect(r.tokenOutAmt).to.eq(toFixedPtAmt("250")); + expect(r.trancheInAmt).to.eq(toFixedPtAmt("250")); + }); + }); + + describe("when tokenOut is the mature collateral", async function () { + let tx: Transaction, r: any; + beforeEach(async function () { + r = await perp.callStatic.computeRolloverAmt( + rolloverInTranche.address, + collateralToken.address, + toFixedPtAmt("250"), + ); + tx = mockVault.rollover(perp.address, rolloverInTranche.address, collateralToken.address, toFixedPtAmt("250")); + await tx; + }); + + it("should update the reserve", async function () { + await checkReserveComposition( + perp, + [collateralToken, reserveTranche, rolloverInTranche], + [toFixedPtAmt("250"), toFixedPtAmt("500"), toFixedPtAmt("750")], + ); + }); + it("should emit reserve synced", async function () { + await expect(tx) + .to.emit(perp, "ReserveSynced") + .withArgs(rolloverInTranche.address, toFixedPtAmt("750")) + .to.emit(perp, "ReserveSynced") + .withArgs(collateralToken.address, toFixedPtAmt("250")); + }); + it("should compute the rollover amounts", async function () { + expect(r.tokenOutAmt).to.eq(toFixedPtAmt("250")); + expect(r.trancheInAmt).to.eq(toFixedPtAmt("250")); + }); + }); + + describe("when tokenOut is the mature collateral which has rebased up", async function () { + let tx: Transaction, r: any; + beforeEach(async function () { + await rebase(collateralToken, rebaseOracle, +0.5); + r = await perp.callStatic.computeRolloverAmt( + rolloverInTranche.address, + collateralToken.address, + toFixedPtAmt("250"), + ); + tx = mockVault.rollover(perp.address, rolloverInTranche.address, collateralToken.address, toFixedPtAmt("250")); + await tx; + }); + + it("should update the reserve", async function () { + await checkReserveComposition( + perp, + [collateralToken, reserveTranche, rolloverInTranche], + [toFixedPtAmt("500"), toFixedPtAmt("500"), toFixedPtAmt("750")], + ); + }); + it("should emit reserve synced", async function () { + await expect(tx) + .to.emit(perp, "ReserveSynced") + .withArgs(rolloverInTranche.address, toFixedPtAmt("750")) + .to.emit(perp, "ReserveSynced") + .withArgs(collateralToken.address, toFixedPtAmt("500")); + }); + it("should compute the rollover amounts", async function () { + expect(r.tokenOutAmt).to.eq(toFixedPtAmt("250")); + expect(r.trancheInAmt).to.eq(toFixedPtAmt("250")); + }); + }); + + describe("when tokenOut is the mature collateral which has rebased down", async function () { + let tx: Transaction, r: any; + beforeEach(async function () { + await rebase(collateralToken, rebaseOracle, -0.5); + r = await perp.callStatic.computeRolloverAmt( + rolloverInTranche.address, + collateralToken.address, + toFixedPtAmt("250"), + ); + tx = mockVault.rollover(perp.address, rolloverInTranche.address, collateralToken.address, toFixedPtAmt("250")); + await tx; + }); + + it("should update the reserve", async function () { + await checkReserveComposition( + perp, + [collateralToken, reserveTranche, rolloverInTranche], + ["0", toFixedPtAmt("500"), toFixedPtAmt("750")], + ); + }); + it("should emit reserve synced", async function () { + await expect(tx) + .to.emit(perp, "ReserveSynced") + .withArgs(rolloverInTranche.address, toFixedPtAmt("750")) + .to.emit(perp, "ReserveSynced") + .withArgs(collateralToken.address, "0"); + }); + it("should compute the rollover amounts", async function () { + expect(r.tokenOutAmt).to.eq(toFixedPtAmt("250")); + expect(r.trancheInAmt).to.eq(toFixedPtAmt("250")); + }); + }); + + describe("when tokenOut is tranche and fully withdrawn", async function () { + let tx: Transaction, r: any; + beforeEach(async function () { + r = await perp.callStatic.computeRolloverAmt( + rolloverInTranche.address, + reserveTranche.address, + toFixedPtAmt("500"), + ); + tx = mockVault.rollover(perp.address, rolloverInTranche.address, reserveTranche.address, toFixedPtAmt("500")); + await tx; + }); + + it("should update the reserve", async function () { + await checkReserveComposition( + perp, + [collateralToken, rolloverInTranche], + [toFixedPtAmt("500"), toFixedPtAmt("1000")], + ); + }); + + it("should emit reserve synced", async function () { + await expect(tx) + .to.emit(perp, "ReserveSynced") + .withArgs(rolloverInTranche.address, toFixedPtAmt("1000")) + .to.emit(perp, "ReserveSynced") + .withArgs(reserveTranche.address, "0"); + }); + it("should compute the rollover amounts", async function () { + expect(r.tokenOutAmt).to.eq(toFixedPtAmt("500")); + expect(r.trancheInAmt).to.eq(toFixedPtAmt("500")); + }); + }); + + describe("when tokenOut is collateral and fully withdrawn", async function () { + let tx: Transaction, r: any; + beforeEach(async function () { + r = await perp.callStatic.computeRolloverAmt( + rolloverInTranche.address, + collateralToken.address, + toFixedPtAmt("500"), + ); + tx = mockVault.rollover(perp.address, rolloverInTranche.address, collateralToken.address, toFixedPtAmt("500")); + await tx; + }); + + it("should update the reserve", async function () { + await checkReserveComposition( + perp, + [collateralToken, reserveTranche, rolloverInTranche], + ["0", toFixedPtAmt("500"), toFixedPtAmt("1000")], + ); + }); + it("should emit reserve synced", async function () { + await expect(tx) + .to.emit(perp, "ReserveSynced") + .withArgs(rolloverInTranche.address, toFixedPtAmt("1000")) + .to.emit(perp, "ReserveSynced") + .withArgs(collateralToken.address, "0"); + }); + it("should compute the rollover amounts", async function () { + expect(r.tokenOutAmt).to.eq(toFixedPtAmt("500")); + expect(r.trancheInAmt).to.eq(toFixedPtAmt("500")); + }); + }); + + describe("when tokenOut is partially redeemed", async function () { + let tx: Transaction, r: any; + beforeEach(async function () { + r = await perp.callStatic.computeRolloverAmt( + rolloverInTranche.address, + reserveTranche.address, + toFixedPtAmt("100"), + ); + tx = mockVault.rollover(perp.address, rolloverInTranche.address, reserveTranche.address, toFixedPtAmt("100")); + await tx; + }); + + it("should update the reserve", async function () { + await checkReserveComposition( + perp, + [collateralToken, reserveTranche, rolloverInTranche], + [toFixedPtAmt("500"), toFixedPtAmt("400"), toFixedPtAmt("600")], + ); + }); + + it("should emit reserve synced", async function () { + await expect(tx) + .to.emit(perp, "ReserveSynced") + .withArgs(rolloverInTranche.address, toFixedPtAmt("600")) + .to.emit(perp, "ReserveSynced") + .withArgs(reserveTranche.address, toFixedPtAmt("400")); + }); + it("should compute the rollover amounts", async function () { + expect(r.tokenOutAmt).to.eq(toFixedPtAmt("100")); + expect(r.trancheInAmt).to.eq(toFixedPtAmt("100")); + }); + }); + + describe("when tokenOut is NOT covered", async function () { + let tx: Transaction, r: any; + beforeEach(async function () { + r = await perp.callStatic.computeRolloverAmt( + rolloverInTranche.address, + reserveTranche.address, + toFixedPtAmt("2000"), + ); + tx = mockVault.rollover(perp.address, rolloverInTranche.address, reserveTranche.address, toFixedPtAmt("2000")); + await tx; + }); + + it("should update the reserve (only transfers covered amount)", async function () { + await checkReserveComposition( + perp, + [collateralToken, rolloverInTranche], + [toFixedPtAmt("500"), toFixedPtAmt("1000")], + ); + }); + + it("should emit reserve synced", async function () { + await expect(tx) + .to.emit(perp, "ReserveSynced") + .withArgs(rolloverInTranche.address, toFixedPtAmt("1000")) + .to.emit(perp, "ReserveSynced") + .withArgs(reserveTranche.address, "0"); + }); + it("should compute the rollover amounts", async function () { + expect(r.tokenOutAmt).to.eq(toFixedPtAmt("500")); + expect(r.trancheInAmt).to.eq(toFixedPtAmt("500")); + }); + }); + + describe("when tokenOut is NOT covered and fee > 0", async function () { + let tx: Transaction, r: any; + beforeEach(async function () { + await feePolicy.computePerpRolloverFeePerc.returns(toPercFixedPtAmt("0.01")); + r = await perp.callStatic.computeRolloverAmt( + rolloverInTranche.address, + reserveTranche.address, + toFixedPtAmt("2000"), + ); + tx = mockVault.rollover(perp.address, rolloverInTranche.address, reserveTranche.address, toFixedPtAmt("2000")); + await tx; + }); + + it("should update the reserve (only transfers covered amount)", async function () { + await checkReserveComposition( + perp, + [collateralToken, rolloverInTranche], + [toFixedPtAmt("500"), toFixedPtAmt("1005.050505050505050506")], + ); + }); + + it("should emit reserve synced", async function () { + await expect(tx) + .to.emit(perp, "ReserveSynced") + .withArgs(rolloverInTranche.address, toFixedPtAmt("1005.050505050505050506")) + .to.emit(perp, "ReserveSynced") + .withArgs(reserveTranche.address, "0"); + }); + it("should compute the rollover amounts", async function () { + expect(r.tokenOutAmt).to.eq(toFixedPtAmt("500")); + expect(r.trancheInAmt).to.eq(toFixedPtAmt("505.050505050505050506")); + }); + }); + + describe("when tokenOut is NOT covered and fee < 0", async function () { + let tx: Transaction, r: any; + beforeEach(async function () { + await feePolicy.computePerpRolloverFeePerc.returns(toPercFixedPtAmt("-0.01")); + r = await perp.callStatic.computeRolloverAmt( + rolloverInTranche.address, + reserveTranche.address, + toFixedPtAmt("2000"), + ); + tx = mockVault.rollover(perp.address, rolloverInTranche.address, reserveTranche.address, toFixedPtAmt("2000")); + await tx; + }); + + it("should update the reserve (only transfers covered amount)", async function () { + await checkReserveComposition( + perp, + [collateralToken, rolloverInTranche], + [toFixedPtAmt("500"), toFixedPtAmt("995.049504950495049505")], + ); + }); + + it("should emit reserve synced", async function () { + await expect(tx) + .to.emit(perp, "ReserveSynced") + .withArgs(rolloverInTranche.address, toFixedPtAmt("995.049504950495049505")) + .to.emit(perp, "ReserveSynced") + .withArgs(reserveTranche.address, "0"); + }); + it("should compute the rollover amounts", async function () { + expect(r.tokenOutAmt).to.eq(toFixedPtAmt("500")); + expect(r.trancheInAmt).to.eq(toFixedPtAmt("495.049504950495049505")); + }); + }); + + describe("when valid rollover", async function () { + let tx: Transaction, r: any; + beforeEach(async function () { + r = await perp.callStatic.computeRolloverAmt( + rolloverInTranche.address, + collateralToken.address, + toFixedPtAmt("100"), + ); + tx = mockVault.rollover(perp.address, rolloverInTranche.address, collateralToken.address, toFixedPtAmt("100")); + await tx; + }); + + it("should update the reserve", async function () { + await checkReserveComposition( + perp, + [collateralToken, reserveTranche, rolloverInTranche], + [toFixedPtAmt("400"), toFixedPtAmt("500"), toFixedPtAmt("600")], + ); + }); + + it("should emit reserve synced", async function () { + await expect(tx) + .to.emit(perp, "ReserveSynced") + .withArgs(rolloverInTranche.address, toFixedPtAmt("600")) + .to.emit(perp, "ReserveSynced") + .withArgs(collateralToken.address, toFixedPtAmt("400")); + }); + it("should compute the rollover amounts", async function () { + expect(r.tokenOutAmt).to.eq(toFixedPtAmt("100")); + expect(r.trancheInAmt).to.eq(toFixedPtAmt("100")); + }); + }); + }); +}); diff --git a/spot-contracts/test/vaults/RolloverVault.ts b/spot-contracts/test/rollover-vault/RolloverVault.ts similarity index 59% rename from spot-contracts/test/vaults/RolloverVault.ts rename to spot-contracts/test/rollover-vault/RolloverVault.ts index 37b3707c..c811e225 100644 --- a/spot-contracts/test/vaults/RolloverVault.ts +++ b/spot-contracts/test/rollover-vault/RolloverVault.ts @@ -1,15 +1,14 @@ import { expect, use } from "chai"; import { network, ethers, upgrades } from "hardhat"; -import { Contract, Transaction, Signer } from "ethers"; +import { Contract, Transaction, Signer, constants } from "ethers"; import { setupCollateralToken, mintCollteralToken, toFixedPtAmt, + toPercFixedPtAmt, setupBondFactory, depositIntoBond, getTranches, - toDiscountFixedPtAmt, - toPriceFixedPtAmt, getDepositBond, advancePerpQueueToBondMaturity, } from "../helpers"; @@ -17,7 +16,12 @@ import { smock, FakeContract } from "@defi-wonderland/smock"; use(smock.matchers); -let vault: Contract, perp: FakeContract, collateralToken: Contract, deployer: Signer, otherUser: Signer; +let vault: Contract, + perp: FakeContract, + feePolicy: FakeContract, + collateralToken: Contract, + deployer: Signer, + otherUser: Signer; describe("RolloverVault", function () { beforeEach(async function () { await network.provider.send("hardhat_reset"); @@ -31,14 +35,17 @@ describe("RolloverVault", function () { const PerpetualTranche = await ethers.getContractFactory("PerpetualTranche"); perp = await smock.fake(PerpetualTranche); + await perp.underlying.returns(collateralToken.address); - await perp.collateral.returns(collateralToken.address); - await perp.feeToken.returns(perp.address); + const FeePolicy = await ethers.getContractFactory("FeePolicy"); + feePolicy = await smock.fake(FeePolicy); + await feePolicy.decimals.returns(8); 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 vault.init("RolloverVault", "VSHARE", perp.address, feePolicy.address); + await perp.vault.returns(vault.address); }); afterEach(async function () { @@ -64,13 +71,17 @@ describe("RolloverVault", function () { expect(await vault.underlying()).to.eq(collateralToken.address); }); + it("should set initial param values", async function () { + expect(await vault.minUnderlyingPerc()).to.eq(toPercFixedPtAmt("0.33333333")); + expect(await vault.minDeploymentAmt()).to.eq("0"); + expect(await vault.minUnderlyingBal()).to.eq("0"); + }); + 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.assetCount()).to.eq(1); + expect(await vault.assetAt(0)).to.eq(collateralToken.address); expect(await vault.isVaultAsset(collateralToken.address)).to.eq(true); - expect(await vault.isVaultAsset(perp.address)).to.eq(true); + expect(await vault.isVaultAsset(perp.address)).to.eq(false); }); it("should NOT be paused", async function () { @@ -81,12 +92,12 @@ describe("RolloverVault", function () { describe("#pause", function () { let tx: Transaction; beforeEach(async function () { - await vault.connect(deployer).transferOwnership(await otherUser.getAddress()); + await vault.updateKeeper(await otherUser.getAddress()); }); - describe("when triggered by non-owner", function () { + describe("when triggered by non-keeper", function () { it("should revert", async function () { - await expect(vault.connect(deployer).pause()).to.be.revertedWith("Ownable: caller is not the owner"); + await expect(vault.connect(deployer).pause()).to.be.revertedWithCustomError(vault, "UnauthorizedCall"); }); }); @@ -94,6 +105,7 @@ describe("RolloverVault", 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"); }); @@ -118,7 +130,7 @@ describe("RolloverVault", function () { describe("#unpause", function () { let tx: Transaction; beforeEach(async function () { - await vault.connect(deployer).transferOwnership(await otherUser.getAddress()); + await vault.updateKeeper(await otherUser.getAddress()); }); describe("when triggered by non-owner", function () { @@ -127,7 +139,7 @@ describe("RolloverVault", function () { }); it("should revert", async function () { - await expect(vault.connect(deployer).unpause()).to.be.revertedWith("Ownable: caller is not the owner"); + await expect(vault.connect(deployer).unpause()).to.be.revertedWithCustomError(vault, "UnauthorizedCall"); }); }); @@ -192,11 +204,10 @@ describe("RolloverVault", function () { }); }); - 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 perp", function () { + it("should not revert", async function () { + await perp.transfer.returns(() => true); + await expect(vault.transferERC20(perp.address, toAddress, toFixedPtAmt("100"))).not.to.be.reverted; }); }); @@ -204,52 +215,34 @@ describe("RolloverVault", 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 issuer = await upgrades.deployProxy( + BondIssuer.connect(deployer), + [bondFactory.address, collateralToken.address, 4800, [200, 800], 1200, 0], + { + initializer: "init(address,address,uint256,uint256[],uint256,uint256)", + }, + ); 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, - ], + ["PerpetualTranche", "PERP", collateralToken.address, issuer.address, feePolicy.address], { - initializer: "init(string,string,address,address,address,address,address)", + initializer: "init(string,string,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 RolloverVault = await ethers.getContractFactory("RolloverVault"); + vault = await upgrades.deployProxy(RolloverVault.connect(deployer)); + await vault.init("RolloverVault", "VSHARE", perp.address, feePolicy.address); + await perp.updateVault(vault.address); + + await mintCollteralToken(collateralToken, toFixedPtAmt("100000"), deployer); const bond = await getDepositBond(perp); const tranches = await getTranches(bond); await depositIntoBond(bond, toFixedPtAmt("1000"), deployer); @@ -257,26 +250,19 @@ describe("RolloverVault", function () { 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); + expect(await vault.assetCount()).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")), + vault.transferERC20(await vault.assetAt(1), toAddress, toFixedPtAmt("100")), ).to.be.revertedWithCustomError(vault, "UnauthorizedTransferOut"); }); }); }); - describe("#updateMinDeploymentAmt", function () { + describe("#updateFeePolicy", function () { let tx: Transaction; beforeEach(async function () { await vault.connect(deployer).transferOwnership(await otherUser.getAddress()); @@ -284,13 +270,43 @@ describe("RolloverVault", function () { describe("when triggered by non-owner", function () { it("should revert", async function () { - await expect(vault.connect(deployer).updateMinDeploymentAmt(0)).to.be.revertedWith( + await expect(vault.connect(deployer).updateFeePolicy(constants.AddressZero)).to.be.revertedWith( "Ownable: caller is not the owner", ); }); }); describe("when triggered by owner", function () { + let newFeePolicy: Contract; + beforeEach(async function () { + const FeePolicy = await ethers.getContractFactory("FeePolicy"); + newFeePolicy = await smock.fake(FeePolicy); + await newFeePolicy.decimals.returns(8); + tx = await vault.connect(otherUser).updateFeePolicy(newFeePolicy.address); + await tx; + }); + it("should update the fee policy", async function () { + expect(await vault.feePolicy()).to.eq(newFeePolicy.address); + }); + }); + }); + + describe("#updateMinDeploymentAmt", function () { + let tx: Transaction; + beforeEach(async function () { + await vault.connect(deployer).updateKeeper(await otherUser.getAddress()); + }); + + describe("when triggered by non-keeper", function () { + it("should revert", async function () { + await expect(vault.connect(deployer).updateMinDeploymentAmt(0)).to.be.revertedWithCustomError( + vault, + "UnauthorizedCall", + ); + }); + }); + + describe("when triggered by keeper", function () { beforeEach(async function () { tx = await vault.connect(otherUser).updateMinDeploymentAmt(toFixedPtAmt("1000")); await tx; @@ -300,4 +316,81 @@ describe("RolloverVault", function () { }); }); }); + + describe("#updateMinUnderlyingBal", function () { + let tx: Transaction; + beforeEach(async function () { + await vault.connect(deployer).updateKeeper(await otherUser.getAddress()); + }); + + describe("when triggered by non-keeper", function () { + it("should revert", async function () { + await expect(vault.connect(deployer).updateMinUnderlyingBal(0)).to.be.revertedWithCustomError( + vault, + "UnauthorizedCall", + ); + }); + }); + + describe("when triggered by keeper", function () { + beforeEach(async function () { + tx = await vault.connect(otherUser).updateMinUnderlyingBal(toFixedPtAmt("1000")); + await tx; + }); + it("should update the min underlying balance", async function () { + expect(await vault.minUnderlyingBal()).to.eq(toFixedPtAmt("1000")); + }); + }); + }); + + describe("#updateMinUnderlyingPerc", function () { + let tx: Transaction; + beforeEach(async function () { + await vault.connect(deployer).updateKeeper(await otherUser.getAddress()); + }); + + describe("when triggered by non-keeper", function () { + it("should revert", async function () { + await expect(vault.connect(deployer).updateMinUnderlyingPerc(0)).to.be.revertedWithCustomError( + vault, + "UnauthorizedCall", + ); + }); + }); + + describe("when triggered by keeper", function () { + beforeEach(async function () { + tx = await vault.connect(otherUser).updateMinUnderlyingPerc(toPercFixedPtAmt("0.1")); + await tx; + }); + it("should update the min underlying balance", async function () { + expect(await vault.minUnderlyingPerc()).to.eq(toPercFixedPtAmt("0.1")); + }); + }); + }); + + describe("#updateKeeper", 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).updateKeeper(constants.AddressZero)).to.be.revertedWith( + "Ownable: caller is not the owner", + ); + }); + }); + + describe("when triggered by owner", function () { + beforeEach(async function () { + tx = await vault.connect(otherUser).updateKeeper(await otherUser.getAddress()); + await tx; + }); + it("should update the keeper", async function () { + expect(await vault.keeper()).to.eq(await otherUser.getAddress()); + }); + }); + }); }); diff --git a/spot-contracts/test/rollover-vault/RolloverVault_deploy.ts b/spot-contracts/test/rollover-vault/RolloverVault_deploy.ts new file mode 100644 index 00000000..c2e356c4 --- /dev/null +++ b/spot-contracts/test/rollover-vault/RolloverVault_deploy.ts @@ -0,0 +1,481 @@ +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, + toPercFixedPtAmt, + getDepositBond, + advancePerpQueue, + advancePerpQueueToBondMaturity, + advancePerpQueueToRollover, + checkReserveComposition, + checkVaultAssetComposition, + rebase, +} from "../helpers"; +use(smock.matchers); + +let vault: Contract; +let perp: Contract; +let bondFactory: Contract; +let collateralToken: Contract; +let issuer: Contract; +let feePolicy: Contract; +let deployer: Signer; +let reserveTranches: Contract[][] = []; +let rolloverInBond: Contract; +let rolloverInTranches: Contract; +let rebaseOracle: 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, rebaseOracle } = await setupCollateralToken("Bitcoin", "BTC")); + + const BondIssuer = await ethers.getContractFactory("BondIssuer"); + issuer = await upgrades.deployProxy( + BondIssuer.connect(deployer), + [bondFactory.address, collateralToken.address, 4800, [200, 800], 1200, 0], + { + initializer: "init(address,address,uint256,uint256[],uint256,uint256)", + }, + ); + + const FeePolicy = await ethers.getContractFactory("FeePolicy"); + feePolicy = await smock.fake(FeePolicy); + await feePolicy.decimals.returns(8); + await feePolicy.computePerpRolloverFeePerc.returns("0"); + + const PerpetualTranche = await ethers.getContractFactory("PerpetualTranche"); + perp = await upgrades.deployProxy( + PerpetualTranche.connect(deployer), + ["PerpetualTranche", "PERP", collateralToken.address, issuer.address, feePolicy.address], + { + initializer: "init(string,string,address,address,address)", + }, + ); + + await perp.updateTolerableTrancheMaturity(1200, 4800); + await advancePerpQueueToBondMaturity(perp, await getDepositBond(perp)); + + const RolloverVault = await ethers.getContractFactory("RolloverVault"); + vault = await upgrades.deployProxy(RolloverVault.connect(deployer)); + await vault.init("RolloverVault", "VSHARE", perp.address, feePolicy.address); + await perp.updateVault(vault.address); + + 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 tranches[0].approve(perp.address, toFixedPtAmt("200")); + await perp.deposit(tranches[0].address, toFixedPtAmt("200")); + + reserveTranches.push(tranches[0]); + await advancePerpQueue(perp, 1200); + } + + await checkReserveComposition( + perp, + [collateralToken, ...reserveTranches.slice(-3)], + [toFixedPtAmt("200"), toFixedPtAmt("200"), toFixedPtAmt("200"), toFixedPtAmt("200")], + ); + + rolloverInBond = await bondAt(await perp.callStatic.getDepositBond()); + rolloverInTranches = await getTranches(rolloverInBond); + await mintCollteralToken(collateralToken, toFixedPtAmt("100000"), deployer); + await collateralToken.approve(vault.address, toFixedPtAmt("1")); + + await checkVaultAssetComposition(vault, [collateralToken], ["0"]); + expect(await vault.assetCount()).to.eq(1); + }); + + 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, "InsufficientLiquidity"); + }); + }); + + describe("when minUnderlyingBal is not set", function () { + beforeEach(async function () { + await vault.updateMinUnderlyingBal(toFixedPtAmt("0")); + }); + + describe("when usable balance is lower than the min deployment", function () { + beforeEach(async function () { + await collateralToken.transfer(vault.address, toFixedPtAmt("999")); + await vault.updateMinDeploymentAmt(toFixedPtAmt("1000")); + }); + it("should revert", async function () { + await expect(vault.deploy()).to.be.revertedWithCustomError(vault, "InsufficientDeployment"); + }); + }); + + describe("when usable balance is higher than the min deployment", function () { + beforeEach(async function () { + await collateralToken.transfer(vault.address, toFixedPtAmt("1000")); + await vault.updateMinDeploymentAmt(toFixedPtAmt("100")); + }); + it("should not revert", async function () { + await expect(vault.deploy()).not.to.be.reverted; + }); + }); + }); + + describe("when minUnderlyingBal is set", function () { + beforeEach(async function () { + await vault.updateMinUnderlyingBal(toFixedPtAmt("25")); + }); + + describe("when usable balance is lower than the minUnderlyingBal", function () { + beforeEach(async function () { + await collateralToken.transfer(vault.address, toFixedPtAmt("20")); + await vault.updateMinDeploymentAmt(toFixedPtAmt("1")); + }); + it("should revert", async function () { + await expect(vault.deploy()).to.be.revertedWithCustomError(vault, "InsufficientLiquidity"); + }); + }); + + describe("when usable balance is lower than the min deployment", function () { + beforeEach(async function () { + await collateralToken.transfer(vault.address, toFixedPtAmt("125")); + await vault.updateMinDeploymentAmt(toFixedPtAmt("100")); + }); + it("should revert", async function () { + await expect(vault.deploy()).to.be.revertedWithCustomError(vault, "InsufficientDeployment"); + }); + }); + + describe("when usable balance is higher than the min deployment", function () { + beforeEach(async function () { + await collateralToken.transfer(vault.address, toFixedPtAmt("126")); + await vault.updateMinDeploymentAmt(toFixedPtAmt("100")); + }); + it("should not revert", async function () { + await expect(vault.deploy()).not.to.be.reverted; + }); + }); + }); + + 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 checkReserveComposition(perp, [collateralToken], [toFixedPtAmt("800")]); + }); + + 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]], + [toFixedPtAmt("2"), toFixedPtAmt("8")], + ); + await checkReserveComposition( + perp, + [collateralToken, newTranchesIn[0]], + [toFixedPtAmt("798"), 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]], + [toFixedPtAmt("6800"), toFixedPtAmt("3200")], + ); + await checkReserveComposition(perp, [collateralToken, newTranchesIn[0]], ["0", toFixedPtAmt("800")]); + }); + }); + }); + + describe("when one trancheIn one tokenOut (near mature tranche)", function () { + let curTranchesIn, newTranchesIn; + beforeEach(async function () { + await advancePerpQueueToBondMaturity(perp, await bondAt(reserveTranches[2].bond())); + const curBondIn = await bondAt(await perp.callStatic.getDepositBond()); + curTranchesIn = await getTranches(curBondIn); + 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 checkVaultAssetComposition( + vault, + [collateralToken, reserveTranches[3], curTranchesIn[1]], + [toFixedPtAmt("6600"), toFixedPtAmt("200"), toFixedPtAmt("3200")], + ); + await checkReserveComposition(perp, [collateralToken, curTranchesIn[0]], ["0", toFixedPtAmt("800")]); + }); + + 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, newTranchesIn[1], reserveTranches[3]], + [toFixedPtAmt("6600"), toFixedPtAmt("3200"), toFixedPtAmt("200")], + ); + await checkReserveComposition(perp, [collateralToken, newTranchesIn[0]], ["0", toFixedPtAmt("800")]); + }); + }); + + 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[3], newTranchesIn[1]], + [toFixedPtAmt("15100"), toFixedPtAmt("200"), toFixedPtAmt("3200")], + ); + await checkReserveComposition(perp, [collateralToken, newTranchesIn[0]], ["0", toFixedPtAmt("800")]); + }); + }); + }); + + 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]], + [toFixedPtAmt("2"), toFixedPtAmt("8")], + ); + await checkReserveComposition( + perp, + [collateralToken, ...reserveTranches.slice(-3), rolloverInTranches[0]], + [toFixedPtAmt("198"), toFixedPtAmt("200"), toFixedPtAmt("200"), toFixedPtAmt("200"), toFixedPtAmt("2")], + ); + }); + }); + + describe("when balance covers just 1 token exactly", 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[1]], + [toFixedPtAmt("200"), toFixedPtAmt("800")], + ); + await checkReserveComposition( + perp, + [collateralToken, ...reserveTranches.slice(-3), rolloverInTranches[0]], + ["0", toFixedPtAmt("200"), toFixedPtAmt("200"), toFixedPtAmt("200"), toFixedPtAmt("200")], + ); + }); + }); + + 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[1], rolloverInTranches[1]], + [toFixedPtAmt("2200"), toFixedPtAmt("200"), toFixedPtAmt("1600")], + ); + await checkReserveComposition( + perp, + [collateralToken, ...reserveTranches.slice(-2), rolloverInTranches[0]], + ["0", toFixedPtAmt("200"), toFixedPtAmt("200"), toFixedPtAmt("400")], + ); + }); + }); + }); + + describe("when one trancheIn many tokenOut with different prices", function () { + describe("when balance covers many tokens", async function () { + beforeEach(async function () { + await rebase(collateralToken, rebaseOracle, -0.9); + await collateralToken.transfer(vault.address, toFixedPtAmt("600")); + }); + it("should rollover", async function () { + await expect(vault.deploy()).not.to.be.reverted; + await checkVaultAssetComposition( + vault, + [collateralToken, reserveTranches[1], rolloverInTranches[1]], + [toFixedPtAmt("20"), toFixedPtAmt("200"), toFixedPtAmt("480")], + ); + await checkReserveComposition( + perp, + [collateralToken, ...reserveTranches.slice(-2), rolloverInTranches[0]], + ["0", toFixedPtAmt("200"), toFixedPtAmt("200"), toFixedPtAmt("120")], + ); + }); + }); + }); + + describe("when rollover fee is +ve", function () { + beforeEach(async function () { + await feePolicy.computePerpRolloverFeePerc.returns(toPercFixedPtAmt("0.01")); + 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[1], rolloverInTranches[1]], + [toFixedPtAmt("200"), toFixedPtAmt("96.999999999999999999"), toFixedPtAmt("1200")], + ); + await checkReserveComposition( + perp, + [collateralToken, reserveTranches[1], reserveTranches[2], reserveTranches[3], rolloverInTranches[0]], + ["0", toFixedPtAmt("103.000000000000000001"), toFixedPtAmt("200"), toFixedPtAmt("200"), toFixedPtAmt("300")], + ); + }); + }); + + describe("when rollover fee is -ve", function () { + beforeEach(async function () { + await feePolicy.computePerpRolloverFeePerc.returns(toPercFixedPtAmt("-0.01")); + 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[1], rolloverInTranches[1]], + [toFixedPtAmt("200"), toFixedPtAmt("102.999999999999999999"), toFixedPtAmt("1200")], + ); + await checkReserveComposition( + perp, + [collateralToken, reserveTranches[1], reserveTranches[2], reserveTranches[3], rolloverInTranches[0]], + ["0", toFixedPtAmt("97.000000000000000001"), toFixedPtAmt("200"), toFixedPtAmt("200"), toFixedPtAmt("300")], + ); + }); + }); + + describe("typical deploy", function () { + let txFn: Promise; + beforeEach(async function () { + await rebase(collateralToken, rebaseOracle, -0.9); + await feePolicy.computePerpRolloverFeePerc.returns(toPercFixedPtAmt("-0.01")); + await collateralToken.transfer(vault.address, toFixedPtAmt("1000")); + + txFn = () => vault.deploy(); + }); + + it("should tranche and rollover", async function () { + const tx = txFn(); + + // Tranche + await expect(tx).to.emit(vault, "AssetSynced").withArgs(rolloverInTranches[0].address, toFixedPtAmt("200")); + await expect(tx).to.emit(vault, "AssetSynced").withArgs(rolloverInTranches[1].address, toFixedPtAmt("800")); + + // Rollover + await expect(tx).to.emit(vault, "AssetSynced").withArgs(reserveTranches[1].address, toFixedPtAmt("200")); + + // Recover + await expect(tx) + .to.emit(vault, "AssetSynced") + .withArgs(rolloverInTranches[0].address, toFixedPtAmt("0.000000000000000118")); + await expect(tx) + .to.emit(vault, "AssetSynced") + .withArgs(rolloverInTranches[1].address, toFixedPtAmt("475.247524752475248")); + + // Final + await expect(tx) + .to.emit(vault, "AssetSynced") + .withArgs(collateralToken.address, toFixedPtAmt("425.940594059405940000")); + }); + + it("should update the list of deployed assets", async function () { + await txFn(); + + await checkVaultAssetComposition( + vault, + [collateralToken, reserveTranches[1], rolloverInTranches[0], rolloverInTranches[1]], + [ + toFixedPtAmt("425.940594059405940000"), + toFixedPtAmt("200"), + toFixedPtAmt("0.000000000000000118"), + toFixedPtAmt("475.247524752475248"), + ], + ); + + await checkReserveComposition( + perp, + [collateralToken, reserveTranches[2], reserveTranches[3], rolloverInTranches[0]], + ["0", toFixedPtAmt("200"), toFixedPtAmt("200"), toFixedPtAmt("118.811881188118811882")], + ); + }); + }); + }); + + describe("deploy limit", function () { + async function setupDeployment() { + const curBondIn = await bondAt(await perp.callStatic.getDepositBond()); + await advancePerpQueueToRollover(perp, curBondIn); + await collateralToken.transfer(vault.address, toFixedPtAmt("10")); + } + + beforeEach(async function () { + for (let i = 0; i < 46; i++) { + await setupDeployment(); + await vault.deploy(); + } + }); + + it("should revert after limit is reached", async function () { + expect(await vault.assetCount()).to.eq(47); + await setupDeployment(); + await expect(vault.deploy()).to.be.revertedWithCustomError(vault, "DeployedCountOverLimit"); + }); + it("redemption should be within gas limit", async function () { + await collateralToken.approve(vault.address, toFixedPtAmt("10")); + await vault.deposit(toFixedPtAmt("10")); + await expect(vault.redeem(await vault.balanceOf(await deployer.getAddress()))).not.to.be.reverted; + }); + + it("recovery should be within gas limit", async function () { + await expect(vault["recover()"]()).not.to.be.reverted; + }); + }); +}); diff --git a/spot-contracts/test/rollover-vault/RolloverVault_deposit_redeem.ts b/spot-contracts/test/rollover-vault/RolloverVault_deposit_redeem.ts new file mode 100644 index 00000000..80ec756e --- /dev/null +++ b/spot-contracts/test/rollover-vault/RolloverVault_deposit_redeem.ts @@ -0,0 +1,907 @@ +import { expect, use } from "chai"; +import { network, ethers, upgrades } from "hardhat"; +import { Contract, Signer, BigNumber } from "ethers"; +import { smock } from "@defi-wonderland/smock"; + +import { + setupCollateralToken, + mintCollteralToken, + setupBondFactory, + depositIntoBond, + bondAt, + getTranches, + toFixedPtAmt, + getDepositBond, + advancePerpQueue, + advancePerpQueueToBondMaturity, + checkReserveComposition, + checkVaultAssetComposition, + rebase, + toPercFixedPtAmt, + advancePerpQueueUpToBondMaturity, +} 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 feePolicy: 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 upgrades.deployProxy( + BondIssuer.connect(deployer), + [bondFactory.address, collateralToken.address, 4800, [200, 800], 1200, 0], + { + initializer: "init(address,address,uint256,uint256[],uint256,uint256)", + }, + ); + + const FeePolicy = await ethers.getContractFactory("FeePolicy"); + feePolicy = await smock.fake(FeePolicy); + await feePolicy.decimals.returns(8); + await feePolicy.computePerpRolloverFeePerc.returns("0"); + + const PerpetualTranche = await ethers.getContractFactory("PerpetualTranche"); + perp = await upgrades.deployProxy( + PerpetualTranche.connect(deployer), + ["PerpetualTranche", "PERP", collateralToken.address, issuer.address, feePolicy.address], + { + initializer: "init(string,string,address,address,address)", + }, + ); + + await perp.updateTolerableTrancheMaturity(1200, 4800); + await advancePerpQueueToBondMaturity(perp, await getDepositBond(perp)); + + const RolloverVault = await ethers.getContractFactory("RolloverVault"); + vault = await upgrades.deployProxy(RolloverVault.connect(deployer)); + await vault.init("RolloverVault", "VSHARE", perp.address, feePolicy.address); + await perp.updateVault(vault.address); + + 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 tranches[0].approve(perp.address, toFixedPtAmt("200")); + await perp.deposit(tranches[0].address, toFixedPtAmt("200")); + + reserveTranches.push(tranches[0]); + await advancePerpQueue(perp, 1200); + } + + await checkReserveComposition( + perp, + [collateralToken, ...reserveTranches], + ["0", toFixedPtAmt("200"), toFixedPtAmt("200"), toFixedPtAmt("200")], + ); + + rolloverInBond = await bondAt(await perp.callStatic.getDepositBond()); + rolloverInTranches = await getTranches(rolloverInBond); + + await mintCollteralToken(collateralToken, toFixedPtAmt("100000"), deployer); + await collateralToken.approve(vault.address, toFixedPtAmt("1")); + + expect(await vault.assetCount()).to.eq(1); + expect(await vault.vaultAssetBalance(await vault.underlying())).to.eq(0); + }); + + describe("#getTVL", function () { + describe("when vault is empty", function () { + it("should return 0 vaule", async function () { + expect(await vault.callStatic.getTVL()).to.eq(0); + expect(await vault.callStatic.getVaultAssetValue(collateralToken.address)).to.eq(0); + expect(await vault.callStatic.getVaultAssetValue(perp.address)).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")); + }); + it("should return asset value", async function () { + expect(await vault.callStatic.getVaultAssetValue(collateralToken.address)).to.eq(toFixedPtAmt("100")); + expect(await vault.callStatic.getVaultAssetValue(perp.address)).to.eq(0); + }); + }); + + describe("when vault has only deployed balance", function () { + beforeEach(async function () { + await collateralToken.transfer(vault.address, toFixedPtAmt("100")); + await vault.deploy(); + expect(await vault.vaultAssetBalance(await vault.underlying())).to.eq(0); + expect(await vault.assetCount()).to.eq(3); + }); + it("should return tvl", async function () { + expect(await vault.callStatic.getTVL()).to.eq(toFixedPtAmt("100")); + }); + it("should return asset value", async function () { + expect(await vault.callStatic.getVaultAssetValue(collateralToken.address)).to.eq(0); + expect(await vault.callStatic.getVaultAssetValue(reserveTranches[0].address)).to.eq(toFixedPtAmt("20")); + expect(await vault.callStatic.getVaultAssetValue(rolloverInTranches[1].address)).to.eq(toFixedPtAmt("80")); + expect(await vault.callStatic.getVaultAssetValue(perp.address)).to.eq(0); + }); + }); + + 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("2100")); + }); + it("should return asset value", async function () { + expect(await vault.callStatic.getVaultAssetValue(perp.address)).to.eq("0"); + expect(await vault.callStatic.getVaultAssetValue(collateralToken.address)).to.eq(toFixedPtAmt("1100")); + expect(await vault.callStatic.getVaultAssetValue(reserveTranches[0].address)).to.eq(toFixedPtAmt("200")); + expect(await vault.callStatic.getVaultAssetValue(rolloverInTranches[1].address)).to.eq(toFixedPtAmt("800")); + }); + }); + + describe("when vault has many balances and rebases up", function () { + beforeEach(async function () { + 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("2310")); + }); + it("should return asset value", async function () { + expect(await vault.callStatic.getVaultAssetValue(collateralToken.address)).to.eq(toFixedPtAmt("1210")); + expect(await vault.callStatic.getVaultAssetValue(reserveTranches[0].address)).to.eq(toFixedPtAmt("200")); + expect(await vault.callStatic.getVaultAssetValue(rolloverInTranches[1].address)).to.eq(toFixedPtAmt("900")); + }); + }); + + describe("when vault has many balances and rebases down", function () { + beforeEach(async function () { + 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("1890")); + }); + it("should return asset value", async function () { + expect(await vault.callStatic.getVaultAssetValue(collateralToken.address)).to.eq(toFixedPtAmt("990")); + expect(await vault.callStatic.getVaultAssetValue(reserveTranches[0].address)).to.eq(toFixedPtAmt("200")); + expect(await vault.callStatic.getVaultAssetValue(rolloverInTranches[1].address)).to.eq(toFixedPtAmt("700")); + }); + }); + + describe("when vault has many balances and rebases down below threshold", function () { + beforeEach(async function () { + await collateralToken.transfer(vault.address, toFixedPtAmt("5000")); + await vault.deploy(); + await collateralToken.transfer(vault.address, toFixedPtAmt("100")); + await rebase(collateralToken, rebaseOracle, -0.9); + }); + + it("should return tvl", async function () { + expect(await vault.callStatic.getTVL()).to.eq(toFixedPtAmt("510")); + }); + + it("should return asset value", async function () { + expect(await vault.callStatic.getVaultAssetValue(collateralToken.address)).to.eq(toFixedPtAmt("410")); + expect(await vault.callStatic.getVaultAssetValue(reserveTranches[0].address)).to.eq(toFixedPtAmt("100")); + expect(await vault.callStatic.getVaultAssetValue(reserveTranches[1].address)).to.eq("0"); + expect(await vault.callStatic.getVaultAssetValue(reserveTranches[2].address)).to.eq("0"); + expect(await vault.callStatic.getVaultAssetValue(rolloverInTranches[0].address)).to.eq(toFixedPtAmt("0")); + expect(await vault.callStatic.getVaultAssetValue(rolloverInTranches[1].address)).to.eq("0"); + }); + }); + + describe("when vault has some dust balances", function () { + beforeEach(async function () { + await perp.transfer(vault.address, toFixedPtAmt("100")); + await collateralToken.transfer(vault.address, toFixedPtAmt("2000")); + await vault.deploy(); + await vault["recover(address)"](perp.address); + await collateralToken.transfer(vault.address, toFixedPtAmt("100")); + await checkVaultAssetComposition( + vault, + [ + collateralToken, + reserveTranches[0], + reserveTranches[1], + reserveTranches[2], + rolloverInTranches[0], + rolloverInTranches[1], + ], + [ + toFixedPtAmt("1266.666666666666666"), + toFixedPtAmt("200"), + toFixedPtAmt("33.333333333333333333"), + toFixedPtAmt("33.333333333333333333"), + toFixedPtAmt("0.000000000000000133"), + toFixedPtAmt("666.6666666666666672"), + ], + ); + }); + it("should return tvl excluding the dust", async function () { + // balances sum up to 2200 but tvl will exclude 0.000000000000000133 + expect(await vault.callStatic.getTVL()).to.eq(toFixedPtAmt("2199.999999999999999866")); + }); + it("should return asset value", async function () { + expect(await vault.callStatic.getVaultAssetValue(perp.address)).to.eq("0"); + expect(await vault.callStatic.getVaultAssetValue(collateralToken.address)).to.eq( + toFixedPtAmt("1266.666666666666666"), + ); + expect(await vault.callStatic.getVaultAssetValue(reserveTranches[0].address)).to.eq(toFixedPtAmt("200")); + expect(await vault.callStatic.getVaultAssetValue(reserveTranches[1].address)).to.eq( + toFixedPtAmt("33.333333333333333333"), + ); + expect(await vault.callStatic.getVaultAssetValue(reserveTranches[2].address)).to.eq( + toFixedPtAmt("33.333333333333333333"), + ); + expect(await vault.callStatic.getVaultAssetValue(rolloverInTranches[1].address)).to.eq( + toFixedPtAmt("666.6666666666666672"), + ); + }); + it("should return no asset value for dust", async function () { + expect(await rolloverInTranches[0].balanceOf(vault.address)).eq("133"); + expect(await vault.callStatic.getVaultAssetValue(rolloverInTranches[0].address)).to.eq("0"); + }); + }); + }); + + describe("#deposit", function () { + let noteAmt: BigNumber; + + describe("when deposit amount is zero", async function () { + it("should return zero", async function () { + expect(await vault.callStatic.deposit("0")).to.eq("0"); + }); + }); + + describe("when total supply = 0", async function () { + beforeEach(async function () { + await collateralToken.approve(vault.address, toFixedPtAmt("100")); + noteAmt = await vault.callStatic.deposit(toFixedPtAmt("100")); + }); + it("should transfer underlying", async function () { + await expect(() => vault.deposit(toFixedPtAmt("100"))).to.changeTokenBalances( + collateralToken, + [deployer, vault], + [toFixedPtAmt("-100"), toFixedPtAmt("100")], + ); + }); + it("should update vault", async function () { + await checkVaultAssetComposition(vault, [collateralToken], ["0"]); + await vault.deposit(toFixedPtAmt("100")); + await checkVaultAssetComposition(vault, [collateralToken], [toFixedPtAmt("100")]); + }); + it("should mint notes", async function () { + await expect(() => vault.deposit(toFixedPtAmt("100"))).to.changeTokenBalances(vault, [deployer], [noteAmt]); + }); + 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")); + }); + it("should transfer underlying", async function () { + await expect(() => vault.connect(otherUser).deposit(toFixedPtAmt("100"))).to.changeTokenBalances( + collateralToken, + [otherUser, vault], + [toFixedPtAmt("-100"), toFixedPtAmt("100")], + ); + }); + + it("should update vault", async function () { + await checkVaultAssetComposition(vault, [collateralToken], [toFixedPtAmt("100")]); + await vault.connect(otherUser).deposit(toFixedPtAmt("100")); + await checkVaultAssetComposition(vault, [collateralToken], [toFixedPtAmt("200")]); + }); + + it("should mint notes", async function () { + await expect(() => vault.connect(otherUser).deposit(toFixedPtAmt("100"))).to.changeTokenBalances( + vault, + [otherUser, deployer], + [noteAmt, "0"], + ); + }); + 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")); + }); + + it("should transfer underlying", async function () { + await expect(() => vault.connect(otherUser).deposit(toFixedPtAmt("100"))).to.changeTokenBalances( + collateralToken, + [otherUser, vault], + [toFixedPtAmt("-100"), toFixedPtAmt("100")], + ); + }); + + it("should update vault", async function () { + await checkVaultAssetComposition(vault, [collateralToken], [toFixedPtAmt("200")]); + await vault.connect(otherUser).deposit(toFixedPtAmt("100")); + await checkVaultAssetComposition(vault, [collateralToken], [toFixedPtAmt("300")]); + }); + + it("should mint notes", async function () { + await expect(() => vault.connect(otherUser).deposit(toFixedPtAmt("100"))).to.changeTokenBalances( + vault, + [otherUser, deployer], + [noteAmt, "0"], + ); + }); + 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")); + }); + + it("should transfer underlying", async function () { + await expect(() => vault.connect(otherUser).deposit(toFixedPtAmt("100"))).to.changeTokenBalances( + collateralToken, + [otherUser, vault], + [toFixedPtAmt("-100"), toFixedPtAmt("100")], + ); + }); + + it("should update vault", async function () { + await checkVaultAssetComposition(vault, [collateralToken], [toFixedPtAmt("50")]); + await vault.connect(otherUser).deposit(toFixedPtAmt("100")); + await checkVaultAssetComposition(vault, [collateralToken], [toFixedPtAmt("150")]); + }); + + it("should mint notes", async function () { + await expect(() => vault.connect(otherUser).deposit(toFixedPtAmt("100"))).to.changeTokenBalances( + vault, + [otherUser, deployer], + [noteAmt, "0"], + ); + }); + + it("should return the note amount", async function () { + expect(noteAmt).to.eq(toFixedPtAmt("100").mul("2000000")); + }); + }); + + describe("when total supply > 0 and vault has deployed assets", 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")); + }); + it("should transfer underlying", async function () { + await expect(() => vault.connect(otherUser).deposit(toFixedPtAmt("100"))).to.changeTokenBalances( + collateralToken, + [otherUser, vault], + [toFixedPtAmt("-100"), toFixedPtAmt("100")], + ); + }); + + it("should update vault", async function () { + await checkVaultAssetComposition(vault, [collateralToken], [toFixedPtAmt("100")]); + await vault.connect(otherUser).deposit(toFixedPtAmt("100")); + await checkVaultAssetComposition(vault, [collateralToken], [toFixedPtAmt("200")]); + }); + + it("should mint notes", async function () { + await expect(() => vault.connect(otherUser).deposit(toFixedPtAmt("100"))).to.changeTokenBalances( + vault, + [otherUser, deployer], + [noteAmt, "0"], + ); + }); + it("should return the note amount", async function () { + expect(noteAmt).to.eq(toFixedPtAmt("100").mul("1000000")); + }); + }); + + describe("fee > 0", async function () { + beforeEach(async function () { + await collateralToken.approve(vault.address, toFixedPtAmt("100")); + await vault.deposit(toFixedPtAmt("100")); + + await feePolicy.computeVaultMintFeePerc.returns(toPercFixedPtAmt("0.05")); + await collateralToken.transfer(otherUserAddress, toFixedPtAmt("100")); + await collateralToken.connect(otherUser).approve(vault.address, toFixedPtAmt("100")); + noteAmt = await vault.connect(otherUser).callStatic.deposit(toFixedPtAmt("100")); + }); + it("should transfer underlying", async function () { + await expect(() => vault.connect(otherUser).deposit(toFixedPtAmt("100"))).to.changeTokenBalances( + collateralToken, + [otherUser, vault], + [toFixedPtAmt("-100"), toFixedPtAmt("100")], + ); + }); + it("should update vault", async function () { + await checkVaultAssetComposition(vault, [collateralToken], [toFixedPtAmt("100")]); + await vault.connect(otherUser).deposit(toFixedPtAmt("100")); + await checkVaultAssetComposition(vault, [collateralToken], [toFixedPtAmt("200")]); + }); + + it("should mint notes", async function () { + await expect(() => vault.connect(otherUser).deposit(toFixedPtAmt("100"))).to.changeTokenBalances( + vault, + [otherUser, deployer], + [noteAmt, "0"], + ); + }); + it("should return the note amount", async function () { + expect(noteAmt).to.eq(toFixedPtAmt("95").mul("1000000")); + }); + }); + }); + + describe("#redeem", function () { + let bal: BigNumber; + + describe("when vault is empty", function () { + it("should revert", async function () { + await expect(vault.redeem("1")).to.be.reverted; + }); + }); + + describe("when redeem amount is zero", async function () { + beforeEach(async function () { + await collateralToken.approve(vault.address, toFixedPtAmt("100")); + await vault.deposit(toFixedPtAmt("100")); + }); + + it("should return []", async function () { + expect(await vault.callStatic.redeem("0")).to.deep.eq([]); + }); + }); + + 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 underlying 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")); + + bal = await vault.balanceOf(deployerAddress); + }); + + it("should transfer assets", async function () { + await expect(() => vault.redeem(bal)).to.changeTokenBalances( + collateralToken, + [deployer, vault], + [toFixedPtAmt("100"), toFixedPtAmt("-100")], + ); + }); + + it("should update vault", async function () { + await checkVaultAssetComposition(vault, [collateralToken], [toFixedPtAmt("200")]); + await vault.redeem(bal); + await checkVaultAssetComposition(vault, [collateralToken], [toFixedPtAmt("100")]); + }); + + it("should burn users notes", async function () { + await expect(() => vault.redeem(bal)).to.changeTokenBalances(vault, [deployer], [bal.mul("-1")]); + expect(await vault.balanceOf(otherUserAddress)).to.eq(toFixedPtAmt("100").mul("1000000")); + }); + + it("should return redemption amounts", async function () { + const redemptionAmts = await vault.callStatic.redeem(bal); + expect(redemptionAmts.length).to.eq(1); + expect(redemptionAmts[0].token).to.eq(collateralToken.address); + expect(redemptionAmts[0].amount).to.eq(toFixedPtAmt("100")); + }); + }); + + describe("when vault has only deployed balance", function () { + let bal: 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(); + + bal = await vault.balanceOf(deployerAddress); + + expect(await vault.vaultAssetBalance(await vault.underlying())).to.eq(0); + expect(await vault.assetCount()).to.eq(3); + }); + + it("should transfer assets", async function () { + await expect(() => vault.redeem(bal)).to.changeTokenBalances( + reserveTranches[0], + [deployer, vault], + [toFixedPtAmt("20"), toFixedPtAmt("-20")], + ); + }); + + it("should transfer assets", async function () { + await expect(() => vault.redeem(bal)).to.changeTokenBalances( + rolloverInTranches[1], + [deployer, vault], + [toFixedPtAmt("80"), toFixedPtAmt("-80")], + ); + }); + + it("should update vault", async function () { + await checkVaultAssetComposition( + vault, + [collateralToken, reserveTranches[0], rolloverInTranches[1]], + ["0", toFixedPtAmt("40"), toFixedPtAmt("160")], + ); + await vault.redeem(bal); + await checkVaultAssetComposition( + vault, + [collateralToken, reserveTranches[0], rolloverInTranches[1]], + ["0", toFixedPtAmt("20"), toFixedPtAmt("80")], + ); + }); + + it("should burn users notes", async function () { + await expect(() => vault.redeem(bal)).to.changeTokenBalances(vault, [deployer], [bal.mul("-1")]); + expect(await vault.balanceOf(otherUserAddress)).to.eq(toFixedPtAmt("100").mul("1000000")); + }); + + it("should return redemption amounts", async function () { + const redemptionAmts = await vault.callStatic.redeem(bal); + 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("20")); + expect(redemptionAmts[2].token).to.eq(rolloverInTranches[1].address); + expect(redemptionAmts[2].amount).to.eq(toFixedPtAmt("80")); + }); + }); + + 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")); + + redemptionAmts = await vault.callStatic.redeem(await vault.balanceOf(deployerAddress)); + bal = await vault.balanceOf(deployerAddress); + }); + + it("should transfer assets", async function () { + await expect(() => vault.redeem(bal)).to.changeTokenBalances( + collateralToken, + [deployer, vault], + [toFixedPtAmt("10"), toFixedPtAmt("-10")], + ); + }); + + it("should transfer assets", async function () { + await expect(() => vault.redeem(bal)).to.changeTokenBalances( + reserveTranches[0], + [deployer, vault], + [toFixedPtAmt("20"), toFixedPtAmt("-20")], + ); + }); + + it("should transfer assets", async function () { + await expect(() => vault.redeem(bal)).to.changeTokenBalances( + rolloverInTranches[1], + [deployer, vault], + [toFixedPtAmt("80"), toFixedPtAmt("-80")], + ); + }); + + it("should update vault", async function () { + await checkVaultAssetComposition( + vault, + [collateralToken, reserveTranches[0], rolloverInTranches[1]], + [toFixedPtAmt("20"), toFixedPtAmt("40"), toFixedPtAmt("160")], + ); + await vault.redeem(bal); + await checkVaultAssetComposition( + vault, + [collateralToken, reserveTranches[0], rolloverInTranches[1]], + [toFixedPtAmt("10"), toFixedPtAmt("20"), toFixedPtAmt("80")], + ); + }); + + it("should burn users notes", async function () { + await expect(() => vault.redeem(bal)).to.changeTokenBalances(vault, [deployer], [bal.mul("-1")]); + 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(toFixedPtAmt("10")); + expect(redemptionAmts[1].token).to.eq(reserveTranches[0].address); + expect(redemptionAmts[1].amount).to.eq(toFixedPtAmt("20")); + expect(redemptionAmts[2].token).to.eq(rolloverInTranches[1].address); + expect(redemptionAmts[2].amount).to.eq(toFixedPtAmt("80")); + }); + }); + + describe("when vault has a combination of balances (partial balance redemption)", 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 vault.deploy(); + await collateralToken.transfer(vault.address, toFixedPtAmt("20")); + + bal = toFixedPtAmt("50").mul("1000000"); + }); + + it("should transfer assets", async function () { + await expect(() => vault.redeem(bal)).to.changeTokenBalances( + collateralToken, + [deployer, vault], + [toFixedPtAmt("5"), toFixedPtAmt("-5")], + ); + }); + + it("should transfer assets", async function () { + await expect(() => vault.redeem(bal)).to.changeTokenBalances( + reserveTranches[0], + [deployer, vault], + [toFixedPtAmt("10"), toFixedPtAmt("-10")], + ); + }); + + it("should transfer assets", async function () { + await expect(() => vault.redeem(bal)).to.changeTokenBalances( + rolloverInTranches[1], + [deployer, vault], + [toFixedPtAmt("40"), toFixedPtAmt("-40")], + ); + }); + + it("should update vault", async function () { + await checkVaultAssetComposition( + vault, + [collateralToken, reserveTranches[0], rolloverInTranches[1]], + [toFixedPtAmt("20"), toFixedPtAmt("40"), toFixedPtAmt("160")], + ); + await vault.redeem(bal); + await checkVaultAssetComposition( + vault, + [collateralToken, reserveTranches[0], rolloverInTranches[1]], + [toFixedPtAmt("15"), toFixedPtAmt("30"), toFixedPtAmt("120")], + ); + }); + + it("should burn users notes", async function () { + await expect(() => vault.redeem(bal)).to.changeTokenBalances(vault, [deployer], [bal.mul("-1")]); + expect(await vault.balanceOf(otherUserAddress)).to.eq(toFixedPtAmt("100").mul("1000000")); + }); + + it("should return redemption amounts", async function () { + const redemptionAmts = await vault.callStatic.redeem(bal); + expect(redemptionAmts.length).to.eq(3); + expect(redemptionAmts[0].token).to.eq(collateralToken.address); + expect(redemptionAmts[0].amount).to.eq(toFixedPtAmt("5")); + expect(redemptionAmts[1].token).to.eq(reserveTranches[0].address); + expect(redemptionAmts[1].amount).to.eq(toFixedPtAmt("10")); + expect(redemptionAmts[2].token).to.eq(rolloverInTranches[1].address); + expect(redemptionAmts[2].amount).to.eq(toFixedPtAmt("40")); + }); + }); + + describe("when fee > 0", 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 vault.deploy(); + await collateralToken.transfer(vault.address, toFixedPtAmt("20")); + + bal = toFixedPtAmt("50").mul("1000000"); + + await feePolicy.computeVaultBurnFeePerc.returns(toPercFixedPtAmt("0.1")); + }); + + it("should transfer assets", async function () { + await expect(() => vault.redeem(bal)).to.changeTokenBalances( + collateralToken, + [deployer, vault], + [toFixedPtAmt("4.5"), toFixedPtAmt("-4.5")], + ); + }); + + it("should transfer assets", async function () { + await expect(() => vault.redeem(bal)).to.changeTokenBalances( + reserveTranches[0], + [deployer, vault], + [toFixedPtAmt("9"), toFixedPtAmt("-9")], + ); + }); + + it("should transfer assets", async function () { + await expect(() => vault.redeem(bal)).to.changeTokenBalances( + rolloverInTranches[1], + [deployer, vault], + [toFixedPtAmt("36"), toFixedPtAmt("-36")], + ); + }); + + it("should update vault", async function () { + await checkVaultAssetComposition( + vault, + [collateralToken, reserveTranches[0], rolloverInTranches[1]], + [toFixedPtAmt("20"), toFixedPtAmt("40"), toFixedPtAmt("160")], + ); + await vault.redeem(bal); + await checkVaultAssetComposition( + vault, + [collateralToken, reserveTranches[0], rolloverInTranches[1]], + [toFixedPtAmt("15.5"), toFixedPtAmt("31"), toFixedPtAmt("124")], + ); + }); + + it("should burn users notes", async function () { + await expect(() => vault.redeem(bal)).to.changeTokenBalances(vault, [deployer], [bal.mul("-1")]); + expect(await vault.balanceOf(otherUserAddress)).to.eq(toFixedPtAmt("100").mul("1000000")); + }); + + it("should return redemption amounts", async function () { + const redemptionAmts = await vault.callStatic.redeem(bal); + expect(redemptionAmts.length).to.eq(3); + expect(redemptionAmts[0].token).to.eq(collateralToken.address); + expect(redemptionAmts[0].amount).to.eq(toFixedPtAmt("4.5")); + expect(redemptionAmts[1].token).to.eq(reserveTranches[0].address); + expect(redemptionAmts[1].amount).to.eq(toFixedPtAmt("9")); + expect(redemptionAmts[2].token).to.eq(rolloverInTranches[1].address); + expect(redemptionAmts[2].amount).to.eq(toFixedPtAmt("36")); + }); + }); + }); + + describe("#recoverAndRedeem", function () { + let bal: 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 advancePerpQueueUpToBondMaturity(perp, await bondAt(await perp.callStatic.getDepositBond())); + bal = toFixedPtAmt("50").mul("1000000"); + }); + + it("should transfer assets", async function () { + await expect(() => vault.recoverAndRedeem(bal)).to.changeTokenBalances( + collateralToken, + [deployer, vault], + [toFixedPtAmt("55"), toFixedPtAmt("145")], + ); + }); + + it("should transfer assets", async function () { + await expect(() => vault.recoverAndRedeem(bal)).to.changeTokenBalances( + reserveTranches[0], + [deployer, vault], + [toFixedPtAmt("0"), toFixedPtAmt("-40")], + ); + }); + + it("should transfer assets", async function () { + await expect(() => vault.recoverAndRedeem(bal)).to.changeTokenBalances( + rolloverInTranches[1], + [deployer, vault], + [toFixedPtAmt("0"), toFixedPtAmt("-160")], + ); + }); + + it("should update vault", async function () { + await checkVaultAssetComposition( + vault, + [collateralToken, reserveTranches[0], rolloverInTranches[1]], + [toFixedPtAmt("20"), toFixedPtAmt("40"), toFixedPtAmt("160")], + ); + await vault.recoverAndRedeem(bal); + await checkVaultAssetComposition(vault, [collateralToken], [toFixedPtAmt("165")]); + }); + + it("should burn users notes", async function () { + await expect(() => vault.recoverAndRedeem(bal)).to.changeTokenBalances(vault, [deployer], [bal.mul("-1")]); + expect(await vault.balanceOf(otherUserAddress)).to.eq(toFixedPtAmt("100").mul("1000000")); + }); + + it("should return redemption amounts", async function () { + await perp.updateState(); + const redemptionAmts = await vault.callStatic.recoverAndRedeem(bal); + expect(redemptionAmts.length).to.eq(1); + expect(redemptionAmts[0].token).to.eq(collateralToken.address); + expect(redemptionAmts[0].amount).to.eq(toFixedPtAmt("55")); + }); + }); +}); diff --git a/spot-contracts/test/rollover-vault/RolloverVault_recover.ts b/spot-contracts/test/rollover-vault/RolloverVault_recover.ts new file mode 100644 index 00000000..e3d97785 --- /dev/null +++ b/spot-contracts/test/rollover-vault/RolloverVault_recover.ts @@ -0,0 +1,610 @@ +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, + createBondWithFactory, + setupBondFactory, + depositIntoBond, + bondAt, + getTranches, + toFixedPtAmt, + getDepositBond, + advancePerpQueue, + advancePerpQueueUpToBondMaturity, + 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 feePolicy: Contract; +let deployer: Signer; +let reserveTranches: Contract[][] = []; +let rolloverInBond: 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 upgrades.deployProxy( + BondIssuer.connect(deployer), + [bondFactory.address, collateralToken.address, 4800, [200, 800], 1200, 0], + { + initializer: "init(address,address,uint256,uint256[],uint256,uint256)", + }, + ); + + const FeePolicy = await ethers.getContractFactory("FeePolicy"); + feePolicy = await smock.fake(FeePolicy); + await feePolicy.computePerpRolloverFeePerc.returns("0"); + await feePolicy.decimals.returns(8); + + const PerpetualTranche = await ethers.getContractFactory("PerpetualTranche"); + perp = await upgrades.deployProxy( + PerpetualTranche.connect(deployer), + ["PerpetualTranche", "PERP", collateralToken.address, issuer.address, feePolicy.address], + { + initializer: "init(string,string,address,address,address)", + }, + ); + + await perp.updateTolerableTrancheMaturity(1200, 4800); + await advancePerpQueueToBondMaturity(perp, await getDepositBond(perp)); + + const RolloverVault = await ethers.getContractFactory("RolloverVault"); + vault = await upgrades.deployProxy(RolloverVault.connect(deployer)); + await vault.init("RolloverVault", "VSHARE", perp.address, feePolicy.address); + await perp.updateVault(vault.address); + + 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 tranches[0].approve(perp.address, toFixedPtAmt("200")); + await perp.deposit(tranches[0].address, toFixedPtAmt("200")); + + reserveTranches.push(tranches[0]); + await advancePerpQueue(perp, 1200); + } + + await checkReserveComposition( + perp, + [collateralToken, ...reserveTranches.slice(-3)], + [toFixedPtAmt("200"), toFixedPtAmt("200"), toFixedPtAmt("200"), toFixedPtAmt("200")], + ); + + rolloverInBond = await bondAt(await perp.callStatic.getDepositBond()); + + await mintCollteralToken(collateralToken, toFixedPtAmt("100000"), deployer); + await collateralToken.approve(vault.address, toFixedPtAmt("1")); + + await checkVaultAssetComposition(vault, [collateralToken], ["0"]); + expect(await vault.assetCount()).to.eq(1); + }); + + 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.assetCount()).to.eq(1); + }); + }); + + 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 collateralToken.transfer(vault.address, toFixedPtAmt("10")); + + await vault.deploy(); + await checkVaultAssetComposition( + vault, + [collateralToken, currentTranchesIn[1]], + [toFixedPtAmt("2"), toFixedPtAmt("8")], + ); + expect(await vault.assetCount()).to.eq(2); + }); + 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[1]], + [toFixedPtAmt("2"), toFixedPtAmt("8")], + ); + expect(await vault.assetCount()).to.eq(2); + }); + }); + 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.assetCount()).to.eq(1); + await checkVaultAssetComposition(vault, [collateralToken], [toFixedPtAmt("10")]); + }); + 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, "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 collateralToken.transfer(vault.address, toFixedPtAmt("10")); + await vault.deploy(); + + await checkVaultAssetComposition( + vault, + [collateralToken, currentTranchesIn[1]], + [toFixedPtAmt("2"), toFixedPtAmt("8")], + ); + expect(await vault.assetCount()).to.eq(2); + }); + + 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]], + [toFixedPtAmt("2"), toFixedPtAmt("8")], + ); + expect(await vault.assetCount()).to.eq(2); + }); + }); + + 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.assetCount()).to.eq(1); + await checkVaultAssetComposition(vault, [collateralToken], [toFixedPtAmt("10")]); + }); + 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, "0"); + }); + }); + + describe("when immature redemption", function () { + beforeEach(async function () { + await depositIntoBond(currentBondIn, toFixedPtAmt("1000"), deployer); + await currentTranchesIn[0].transfer(vault.address, toFixedPtAmt("100")); + + await advancePerpQueueToRollover(perp, currentBondIn); + + newBondIn = await bondAt(await perp.callStatic.getDepositBond()); + newTranchesIn = await getTranches(newBondIn); + + await collateralToken.transfer(vault.address, toFixedPtAmt("9998")); + await vault.deploy(); + + expect(await vault.assetCount()).to.eq(3); + await checkVaultAssetComposition( + vault, + [collateralToken, currentTranchesIn[0], newTranchesIn[1]], + [toFixedPtAmt("6808"), toFixedPtAmt("100"), toFixedPtAmt("3200")], + ); + }); + + describe("without reminder", function () { + beforeEach(async function () { + await currentTranchesIn[1].transfer(vault.address, toFixedPtAmt("400")); + }); + it("should recover", async function () { + await expect(vault["recover()"]()).not.to.be.reverted; + expect(await vault.assetCount()).to.eq(2); + await checkVaultAssetComposition( + vault, + [collateralToken, newTranchesIn[1]], + [toFixedPtAmt("7308"), toFixedPtAmt("3200")], + ); + }); + it("should sync assets", async function () { + const tx = vault["recover()"](); + await expect(tx).to.emit(vault, "AssetSynced").withArgs(collateralToken.address, toFixedPtAmt("7308")); + await expect(tx).to.emit(vault, "AssetSynced").withArgs(newTranchesIn[1].address, toFixedPtAmt("3200")); + await expect(tx).to.emit(vault, "AssetSynced").withArgs(currentTranchesIn[0].address, toFixedPtAmt("0")); + }); + }); + + describe("with reminder", function () { + beforeEach(async function () { + await currentTranchesIn[1].transfer(vault.address, toFixedPtAmt("100")); + }); + it("should recover", async function () { + await expect(vault["recover()"]()).not.to.be.reverted; + expect(await vault.assetCount()).to.eq(3); + await checkVaultAssetComposition( + vault, + [collateralToken, currentTranchesIn[0], newTranchesIn[1]], + [toFixedPtAmt("6933"), toFixedPtAmt("75"), toFixedPtAmt("3200")], + ); + }); + it("should sync assets", async function () { + const tx = vault["recover()"](); + await expect(tx).to.emit(vault, "AssetSynced").withArgs(collateralToken.address, toFixedPtAmt("6933")); + await expect(tx).to.emit(vault, "AssetSynced").withArgs(currentTranchesIn[0].address, toFixedPtAmt("75")); + await expect(tx).to.emit(vault, "AssetSynced").withArgs(newTranchesIn[1].address, toFixedPtAmt("3200")); + }); + }); + }); + }); + }); + + describe("#recover(address)", function () { + describe("when no asset is deployed", function () { + it("should revert", async function () { + await vault["recover()"](); + await expect(vault["recover(address)"](collateralToken.address)).to.be.revertedWithCustomError( + vault, + "UnexpectedAsset", + ); + expect(await vault.assetCount()).to.eq(1); + }); + }); + + 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 collateralToken.transfer(vault.address, toFixedPtAmt("10")); + + await vault.deploy(); + await checkVaultAssetComposition( + vault, + [collateralToken, currentTranchesIn[1]], + [toFixedPtAmt("2"), toFixedPtAmt("8")], + ); + expect(await vault.assetCount()).to.eq(2); + }); + + describe("when address is not valid", function () { + it("should be reverted", async function () { + await expect(vault["recover(address)"](collateralToken.address)).to.be.revertedWithCustomError( + vault, + "UnexpectedAsset", + ); + }); + }); + + describe("when belongs to a malicious tranche", function () { + it("should be reverted", async function () { + const maliciousBond = await createBondWithFactory(bondFactory, collateralToken, [1, 999], 100000000000); + await collateralToken.approve(maliciousBond.address, toFixedPtAmt("1")); + await maliciousBond.deposit(toFixedPtAmt("1")); + const maliciousTranches = await getTranches(maliciousBond); + await maliciousTranches[1].transfer( + vault.address, + maliciousTranches[1].balanceOf(await deployer.getAddress()), + ); + await expect(vault["recover(address)"](maliciousTranches[1].address)).to.be.revertedWithCustomError( + vault, + "UnexpectedAsset", + ); + }); + }); + + describe("when its not mature", function () { + it("should be a no-op", async function () { + await expect(vault["recover(address)"](currentTranchesIn[1].address)).not.to.be.reverted; + await checkVaultAssetComposition( + vault, + [collateralToken, currentTranchesIn[1]], + [toFixedPtAmt("2"), toFixedPtAmt("8")], + ); + expect(await vault.assetCount()).to.eq(2); + }); + }); + + describe("when its mature", function () { + beforeEach(async function () { + await advancePerpQueueUpToBondMaturity(perp, currentBondIn); + }); + it("should recover", async function () { + await expect(vault["recover(address)"](currentTranchesIn[1].address)).not.to.be.reverted; + expect(await vault.assetCount()).to.eq(1); + await checkVaultAssetComposition(vault, [collateralToken], [toFixedPtAmt("10")]); + }); + it("should sync assets", async function () { + const tx = vault["recover(address)"](currentTranchesIn[1].address); + await expect(tx).to.emit(vault, "AssetSynced").withArgs(collateralToken.address, toFixedPtAmt("10")); + await expect(tx).to.emit(vault, "AssetSynced").withArgs(currentTranchesIn[1].address, "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 collateralToken.transfer(vault.address, toFixedPtAmt("10")); + await vault.deploy(); + + await checkVaultAssetComposition( + vault, + [collateralToken, currentTranchesIn[1]], + [toFixedPtAmt("2"), toFixedPtAmt("8")], + ); + expect(await vault.assetCount()).to.eq(2); + }); + + describe("when no redemption", function () { + it("should be a no-op", async function () { + await expect(vault["recover(address)"](currentTranchesIn[1].address)).not.to.be.reverted; + await checkVaultAssetComposition( + vault, + [collateralToken, currentTranchesIn[1]], + [toFixedPtAmt("2"), toFixedPtAmt("8")], + ); + expect(await vault.assetCount()).to.eq(2); + }); + }); + + describe("when mature redemption", function () { + beforeEach(async function () { + await advancePerpQueueToBondMaturity(perp, currentBondIn); + }); + it("should recover", async function () { + await expect(vault["recover(address)"](currentTranchesIn[1].address)).not.to.be.reverted; + await checkVaultAssetComposition(vault, [collateralToken], [toFixedPtAmt("10")]); + expect(await vault.assetCount()).to.eq(1); + }); + it("should sync assets", async function () { + const tx = vault["recover(address)"](currentTranchesIn[1].address); + await expect(tx).to.emit(vault, "AssetSynced").withArgs(collateralToken.address, toFixedPtAmt("10")); + await expect(tx).to.emit(vault, "AssetSynced").withArgs(currentTranchesIn[1].address, "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 collateralToken.transfer(vault.address, toFixedPtAmt("9998")); + await vault.deploy(); + + expect(await vault.assetCount()).to.eq(2); + await checkVaultAssetComposition( + vault, + [collateralToken, newTranchesIn[1]], + [toFixedPtAmt("6808"), toFixedPtAmt("3200")], + ); + }); + + describe("without reminder", function () { + beforeEach(async function () { + await depositIntoBond(newBondIn, toFixedPtAmt("4000"), deployer); + await newTranchesIn[0].transfer(vault.address, toFixedPtAmt("800")); + }); + it("should recover", async function () { + await expect(vault["recover(address)"](newTranchesIn[1].address)).not.to.be.reverted; + expect(await vault.assetCount()).to.eq(1); + await checkVaultAssetComposition(vault, [collateralToken], [toFixedPtAmt("10808")]); + }); + it("should sync assets", async function () { + const tx = vault["recover(address)"](newTranchesIn[1].address); + await expect(tx).to.emit(vault, "AssetSynced").withArgs(collateralToken.address, toFixedPtAmt("10808")); + await expect(tx).to.emit(vault, "AssetSynced").withArgs(newTranchesIn[0].address, "0"); + await expect(tx).to.emit(vault, "AssetSynced").withArgs(newTranchesIn[1].address, "0"); + }); + }); + + describe("with reminder", function () { + beforeEach(async function () { + await depositIntoBond(newBondIn, toFixedPtAmt("1000"), deployer); + await newTranchesIn[0].transfer(vault.address, toFixedPtAmt("200")); + }); + + it("should recover", async function () { + await expect(vault["recover(address)"](newTranchesIn[1].address)).not.to.be.reverted; + expect(await vault.assetCount()).to.eq(2); + await checkVaultAssetComposition( + vault, + [collateralToken, newTranchesIn[1]], + [toFixedPtAmt("7808"), toFixedPtAmt("2400")], + ); + }); + + it("should sync assets", async function () { + const tx = vault["recover(address)"](newTranchesIn[1].address); + await expect(tx).to.emit(vault, "AssetSynced").withArgs(collateralToken.address, toFixedPtAmt("7808")); + await expect(tx).to.emit(vault, "AssetSynced").withArgs(newTranchesIn[0].address, toFixedPtAmt("0")); + await expect(tx).to.emit(vault, "AssetSynced").withArgs(newTranchesIn[1].address, toFixedPtAmt("2400")); + }); + }); + + describe("with dust reminder", function () { + beforeEach(async function () { + await depositIntoBond(newBondIn, toFixedPtAmt("1000"), deployer); + await newTranchesIn[0].transfer(vault.address, "200000"); + }); + + it("should recover", async function () { + await expect(vault["recover(address)"](newTranchesIn[1].address)).not.to.be.reverted; + expect(await vault.assetCount()).to.eq(2); + await checkVaultAssetComposition( + vault, + [collateralToken, newTranchesIn[1]], + [toFixedPtAmt("6808.000000000001"), toFixedPtAmt("3199.9999999999992")], + ); + }); + + it("should sync assets", async function () { + const tx = vault["recover(address)"](newTranchesIn[1].address); + await expect(tx) + .to.emit(vault, "AssetSynced") + .withArgs(collateralToken.address, toFixedPtAmt("6808.000000000001")); + await expect(tx).to.emit(vault, "AssetSynced").withArgs(newTranchesIn[0].address, toFixedPtAmt("0")); + await expect(tx) + .to.emit(vault, "AssetSynced") + .withArgs(newTranchesIn[1].address, toFixedPtAmt("3199.9999999999992")); + }); + + it("should not recover dust when triggered again", async function () { + await depositIntoBond(newBondIn, toFixedPtAmt("10000"), deployer); + await newTranchesIn[0].transfer(vault.address, toFixedPtAmt("799.999999999999")); + await vault["recover(address)"](newTranchesIn[1].address); + const tx = vault["recover(address)"](newTranchesIn[1].address); + await tx; + await checkVaultAssetComposition( + vault, + [collateralToken, newTranchesIn[1]], + [toFixedPtAmt("10807.999999999996"), "3200000"], + ); + await expect(tx) + .to.emit(vault, "AssetSynced") + .withArgs(collateralToken.address, toFixedPtAmt("10807.999999999996")); + await expect(tx).to.emit(vault, "AssetSynced").withArgs(newTranchesIn[1].address, "3200000"); + }); + }); + }); + }); + + describe("recovering perp", function () { + let currentBondIn: Contract, currentTranchesIn: Contract[]; + beforeEach(async function () { + currentBondIn = await bondAt(await perp.callStatic.getDepositBond()); + currentTranchesIn = await getTranches(currentBondIn); + + await collateralToken.transfer(vault.address, toFixedPtAmt("125")); + await vault.deploy(); + + await checkVaultAssetComposition( + vault, + [collateralToken, currentTranchesIn[1]], + [toFixedPtAmt("25"), toFixedPtAmt("100")], + ); + expect(await vault.assetCount()).to.eq(2); + }); + + describe("when vault has no perps", function () { + it("should be a no-op", async function () { + await expect(vault["recover(address)"](perp.address)).not.to.be.reverted; + await checkVaultAssetComposition( + vault, + [collateralToken, currentTranchesIn[1]], + [toFixedPtAmt("25"), toFixedPtAmt("100")], + ); + expect(await vault.assetCount()).to.eq(2); + }); + }); + + describe("when vault has perps", function () { + beforeEach(async function () { + await perp.transfer(vault.address, toFixedPtAmt("100")); + }); + it("should recover", async function () { + await expect(vault["recover(address)"](perp.address)).not.to.be.reverted; + await checkVaultAssetComposition( + vault, + [collateralToken, currentTranchesIn[1], ...reserveTranches.slice(-3)], + [toFixedPtAmt("62.5"), toFixedPtAmt("87.5"), toFixedPtAmt("25"), toFixedPtAmt("25"), toFixedPtAmt("25")], + ); + expect(await vault.assetCount()).to.eq(5); + }); + it("should sync assets", async function () { + const tx = vault["recover(address)"](perp.address); + await expect(tx).to.emit(vault, "AssetSynced").withArgs(collateralToken.address, toFixedPtAmt("62.5")); + await expect(tx).to.emit(vault, "AssetSynced").withArgs(currentTranchesIn[1].address, toFixedPtAmt("87.5")); + await expect(tx).to.emit(vault, "AssetSynced").withArgs(currentTranchesIn[0].address, "0"); + await expect(tx).to.emit(vault, "AssetSynced").withArgs(reserveTranches[1].address, toFixedPtAmt("25")); + await expect(tx).to.emit(vault, "AssetSynced").withArgs(reserveTranches[2].address, toFixedPtAmt("25")); + await expect(tx).to.emit(vault, "AssetSynced").withArgs(reserveTranches[3].address, toFixedPtAmt("25")); + }); + }); + }); + }); + + 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 collateralToken.transfer(vault.address, toFixedPtAmt("10")); + await vault.deploy(); + + await checkVaultAssetComposition( + vault, + [collateralToken, currentTranchesIn[1]], + [toFixedPtAmt("2"), toFixedPtAmt("8")], + ); + expect(await vault.assetCount()).to.eq(2); + + await advancePerpQueueToBondMaturity(perp, currentBondIn); + + newBondIn = await bondAt(await perp.callStatic.getDepositBond()); + newTranchesIn = await getTranches(newBondIn); + }); + + it("should recover", async function () { + await expect(vault.recoverAndRedeploy()).not.to.be.reverted; + expect(await vault.assetCount()).to.eq(2); + await checkVaultAssetComposition( + vault, + [collateralToken, newTranchesIn[1]], + [toFixedPtAmt("2"), toFixedPtAmt("8")], + ); + }); + + 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, "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("8")); + await expect(tx).to.emit(vault, "AssetSynced").withArgs(newTranchesIn[0].address, "0"); + await expect(tx).to.emit(vault, "AssetSynced").withArgs(collateralToken.address, toFixedPtAmt("2")); + }); + }); +}); diff --git a/spot-contracts/test/rollover-vault/RolloverVault_swap.ts b/spot-contracts/test/rollover-vault/RolloverVault_swap.ts new file mode 100644 index 00000000..e2eb17b7 --- /dev/null +++ b/spot-contracts/test/rollover-vault/RolloverVault_swap.ts @@ -0,0 +1,1264 @@ +import { expect, use } from "chai"; +import { network, ethers, upgrades } from "hardhat"; +import { Contract, Signer, constants, Transaction } from "ethers"; +import { smock } from "@defi-wonderland/smock"; + +import { + setupCollateralToken, + mintCollteralToken, + setupBondFactory, + depositIntoBond, + bondAt, + getTranches, + toFixedPtAmt, + toPercFixedPtAmt, + getDepositBond, + advancePerpQueue, + advancePerpQueueToBondMaturity, + checkReserveComposition, + checkVaultAssetComposition, + rebase, +} from "../helpers"; +use(smock.matchers); + +let vault: Contract; +let perp: Contract; +let bondFactory: Contract; +let collateralToken: Contract; +let rebaseOracle: Contract; +let issuer: Contract; +let feePolicy: Contract; +let deployer: Signer; +let reserveTranches: Contract[][] = []; +let remainingJuniorTranches: Contract[][] = []; +let currentBondIn: Contract; +let currentTranchesIn: 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, rebaseOracle } = await setupCollateralToken("Bitcoin", "BTC")); + + const BondIssuer = await ethers.getContractFactory("BondIssuer"); + issuer = await upgrades.deployProxy( + BondIssuer.connect(deployer), + [bondFactory.address, collateralToken.address, 4800, [200, 800], 1200, 0], + { + initializer: "init(address,address,uint256,uint256[],uint256,uint256)", + }, + ); + + const FeePolicy = await ethers.getContractFactory("FeePolicy"); + feePolicy = await smock.fake(FeePolicy); + await feePolicy.decimals.returns(8); + await feePolicy.computePerpMintFeePerc.returns(0); + await feePolicy.computePerpBurnFeePerc.returns(0); + await feePolicy.computePerpRolloverFeePerc.returns(0); + + const PerpetualTranche = await ethers.getContractFactory("PerpetualTranche"); + perp = await upgrades.deployProxy( + PerpetualTranche.connect(deployer), + ["PerpetualTranche", "PERP", collateralToken.address, issuer.address, feePolicy.address], + { + initializer: "init(string,string,address,address,address)", + }, + ); + await perp.updateTolerableTrancheMaturity(1200, 4800); + await advancePerpQueueToBondMaturity(perp, await getDepositBond(perp)); + + 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, feePolicy.address); + await perp.updateVault(vault.address); + + reserveTranches = []; + remainingJuniorTranches = []; + for (let i = 0; i < 4; i++) { + 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")); + + reserveTranches.push(tranches[0]); + remainingJuniorTranches.push(tranches[1]); + await advancePerpQueue(perp, 1200); + } + + await checkReserveComposition( + perp, + [collateralToken, ...reserveTranches.slice(-3)], + [toFixedPtAmt("200"), toFixedPtAmt("200"), toFixedPtAmt("200"), toFixedPtAmt("200")], + ); + await checkVaultAssetComposition(vault, [collateralToken], [0]); + expect(await vault.assetCount()).to.eq(1); + + await mintCollteralToken(collateralToken, toFixedPtAmt("100000"), deployer); + currentBondIn = await bondAt(await perp.callStatic.getDepositBond()); + currentTranchesIn = await getTranches(currentBondIn); + await collateralToken.transfer(vault.address, toFixedPtAmt("1000")); + await vault.deploy(); + await collateralToken.transfer(vault.address, toFixedPtAmt("1000")); + + await checkVaultAssetComposition( + vault, + [collateralToken, currentTranchesIn[1]], + [toFixedPtAmt("1200"), toFixedPtAmt("800")], + ); + expect(await vault.assetCount()).to.eq(2); + + await collateralToken.approve(vault.address, toFixedPtAmt("1000")); + await perp.approve(vault.address, toFixedPtAmt("1000")); + }); + + afterEach(async function () { + await network.provider.send("hardhat_reset"); + }); + + describe("#swapUnderlyingForPerps", function () { + describe("when fee is zero", function () { + beforeEach(async function () { + await feePolicy.computePerpMintFeePerc.returns(0); + await feePolicy.computeUnderlyingToPerpVaultSwapFeePerc.returns(0); + }); + + describe("when perp price is 1", function () { + it("should compute swap amount", async function () { + const s = await vault.callStatic.computeUnderlyingToPerpSwapAmt(toFixedPtAmt("100")); + expect(s[0]).to.eq(toFixedPtAmt("100")); + expect(s[1]).to.eq(0); + expect(s[2].perpTVL).to.eq(toFixedPtAmt("800")); + expect(s[2].vaultTVL).to.eq(toFixedPtAmt("2000")); + expect(s[2].seniorTR).to.eq("200"); + }); + + it("should update vault after swap", async function () { + await checkVaultAssetComposition( + vault, + [collateralToken, currentTranchesIn[1]], + [toFixedPtAmt("1200"), toFixedPtAmt("800")], + ); + + await vault.swapUnderlyingForPerps(toFixedPtAmt("100")); + + await checkVaultAssetComposition( + vault, + [collateralToken, currentTranchesIn[1]], + [toFixedPtAmt("800"), toFixedPtAmt("1200")], + ); + }); + }); + + describe("when perp price > 1", function () { + beforeEach(async function () { + await collateralToken.transfer(perp.address, toFixedPtAmt("800")); + }); + + it("should compute swap amount", async function () { + const s = await vault.callStatic.computeUnderlyingToPerpSwapAmt(toFixedPtAmt("100")); + expect(s[0]).to.eq(toFixedPtAmt("50")); + expect(s[1]).to.eq(0); + expect(s[2].perpTVL).to.eq(toFixedPtAmt("1600")); + expect(s[2].vaultTVL).to.eq(toFixedPtAmt("2000")); + expect(s[2].seniorTR).to.eq("200"); + }); + + it("should update vault after swap", async function () { + await checkVaultAssetComposition( + vault, + [collateralToken, currentTranchesIn[1]], + [toFixedPtAmt("1200"), toFixedPtAmt("800")], + ); + + await vault.swapUnderlyingForPerps(toFixedPtAmt("100")); + + await checkVaultAssetComposition( + vault, + [collateralToken, currentTranchesIn[1]], + [toFixedPtAmt("800"), toFixedPtAmt("1200")], + ); + }); + }); + + describe("when perp price < 1", function () { + beforeEach(async function () { + await rebase(collateralToken, rebaseOracle, -0.9); + }); + it("should compute swap amount", async function () { + const s = await vault.callStatic.computeUnderlyingToPerpSwapAmt(toFixedPtAmt("100")); + expect(s[0]).to.eq(toFixedPtAmt("200")); + expect(s[1]).to.eq(0); + expect(s[2].perpTVL).to.eq(toFixedPtAmt("400")); + expect(s[2].vaultTVL).to.eq(toFixedPtAmt("120")); + expect(s[2].seniorTR).to.eq("200"); + }); + it("should update vault after swap", async function () { + await checkVaultAssetComposition( + vault, + [collateralToken, currentTranchesIn[1]], + [toFixedPtAmt("120"), toFixedPtAmt("800")], + ); + + await vault.swapUnderlyingForPerps(toFixedPtAmt("100")); + + await checkVaultAssetComposition( + vault, + [collateralToken, currentTranchesIn[1]], + [toFixedPtAmt("120"), toFixedPtAmt("1600")], + ); + }); + }); + + describe("when perp price is 1 but deposit bond has rebased down", function () { + beforeEach(async function () { + await rebase(collateralToken, rebaseOracle, -0.1); + }); + + it("should compute swap amount", async function () { + const s = await vault.callStatic.computeUnderlyingToPerpSwapAmt(toFixedPtAmt("100")); + expect(s[0]).to.eq(toFixedPtAmt("100")); + expect(s[1]).to.eq(0); + expect(s[2].perpTVL).to.eq(toFixedPtAmt("800")); + expect(s[2].vaultTVL).to.eq(toFixedPtAmt("1780")); + expect(s[2].seniorTR).to.eq("200"); + }); + + it("should update vault after swap", async function () { + await checkVaultAssetComposition( + vault, + [collateralToken, currentTranchesIn[1]], + [toFixedPtAmt("1080"), toFixedPtAmt("800")], + ); + + await vault.swapUnderlyingForPerps(toFixedPtAmt("100")); + + await checkVaultAssetComposition( + vault, + [collateralToken, currentTranchesIn[1]], + [toFixedPtAmt("730"), toFixedPtAmt("1200")], + ); + }); + }); + + describe("when perp price is 1 but deposit bond has rebased up", function () { + beforeEach(async function () { + await rebase(collateralToken, rebaseOracle, 0.1); + }); + + it("should compute swap amount", async function () { + const s = await vault.callStatic.computeUnderlyingToPerpSwapAmt(toFixedPtAmt("100")); + expect(s[0]).to.eq(toFixedPtAmt("100")); + expect(s[1]).to.eq(0); + expect(s[2].perpTVL).to.eq(toFixedPtAmt("800")); + expect(s[2].vaultTVL).to.eq(toFixedPtAmt("2220")); + expect(s[2].seniorTR).to.eq("200"); + }); + + it("should update vault after swap", async function () { + await checkVaultAssetComposition( + vault, + [collateralToken, currentTranchesIn[1]], + [toFixedPtAmt("1320"), toFixedPtAmt("800")], + ); + + await vault.swapUnderlyingForPerps(toFixedPtAmt("100")); + + await checkVaultAssetComposition( + vault, + [collateralToken, currentTranchesIn[1]], + [toFixedPtAmt("870"), toFixedPtAmt("1200")], + ); + }); + }); + }); + + describe("when fee is not zero", function () { + beforeEach(async function () { + await feePolicy.computePerpMintFeePerc.returns(toPercFixedPtAmt("0.05")); + await feePolicy.computeUnderlyingToPerpVaultSwapFeePerc.returns(toPercFixedPtAmt("0.1")); + }); + + it("should compute swap amount", async function () { + const s = await vault.callStatic.computeUnderlyingToPerpSwapAmt(toFixedPtAmt("100")); + expect(s[0]).to.eq(toFixedPtAmt("85")); + expect(s[1]).to.eq(toFixedPtAmt("5")); + expect(s[2].perpTVL).to.eq(toFixedPtAmt("800")); + expect(s[2].vaultTVL).to.eq(toFixedPtAmt("2000")); + expect(s[2].seniorTR).to.eq("200"); + }); + }); + + describe("when swap amount is zero", function () { + it("should be reverted", async function () { + await expect(vault.swapUnderlyingForPerps(0)).to.be.revertedWithCustomError(vault, "UnacceptableSwap"); + }); + }); + + describe("when absolute liquidity is too low", function () { + beforeEach(async function () { + await vault.updateMinUnderlyingBal(toFixedPtAmt("1000")); + await vault.updateMinUnderlyingPerc(0); + }); + it("should be reverted", async function () { + await expect(vault.swapUnderlyingForPerps(toFixedPtAmt("50"))).to.be.revertedWithCustomError( + vault, + "InsufficientLiquidity", + ); + await expect(vault.swapUnderlyingForPerps(toFixedPtAmt("1"))).not.to.be.reverted; + }); + }); + + describe("when percentage of liquidity is too low", function () { + beforeEach(async function () { + await vault.updateMinUnderlyingBal(0); + await vault.updateMinUnderlyingPerc(toPercFixedPtAmt("0.40")); + }); + it("should be reverted", async function () { + await expect(vault.swapUnderlyingForPerps(toFixedPtAmt("100"))).to.be.revertedWithCustomError( + vault, + "InsufficientLiquidity", + ); + await expect(vault.swapUnderlyingForPerps(toFixedPtAmt("99"))).not.to.be.reverted; + }); + }); + + describe("when fee is 100%", function () { + it("should be reverted", async function () { + await feePolicy.computePerpMintFeePerc.returns(0); + await feePolicy.computeUnderlyingToPerpVaultSwapFeePerc.returns(toPercFixedPtAmt("1")); + await expect(vault.swapUnderlyingForPerps(toFixedPtAmt("100"))).to.be.revertedWithCustomError( + vault, + "UnacceptableSwap", + ); + }); + }); + + describe("when fee is greater than 100%", function () { + it("should be reverted", async function () { + await feePolicy.computePerpMintFeePerc.returns(toPercFixedPtAmt("0.05")); + await feePolicy.computeUnderlyingToPerpVaultSwapFeePerc.returns(toPercFixedPtAmt("1")); + await expect(vault.swapUnderlyingForPerps(toFixedPtAmt("100"))).to.be.reverted; + }); + }); + + describe("on successful swap with zero fees", function () { + let txFn: Promise; + beforeEach(async function () { + await feePolicy.computePerpMintFeePerc.returns(0); + await feePolicy.computeUnderlyingToPerpVaultSwapFeePerc.returns(0); + txFn = () => vault.swapUnderlyingForPerps(toFixedPtAmt("100")); + }); + + it("should mint perps for swap and leave none left over", async function () { + expect(await perp.totalSupply()).to.eq(toFixedPtAmt("800")); + await expect(txFn()) + .to.emit(perp, "Transfer") + .withArgs(constants.AddressZero, vault.address, toFixedPtAmt("100")); + expect(await perp.totalSupply()).to.eq(toFixedPtAmt("900")); + expect(await perp.balanceOf(vault.address)).to.eq(0); + }); + + it("should return the perp amt", async function () { + expect(await vault.callStatic.swapUnderlyingForPerps(toFixedPtAmt("100"))).to.eq(toFixedPtAmt("100")); + }); + + it("should transfer underlying from the user", async function () { + await expect(txFn).to.changeTokenBalances(collateralToken, [deployer], [toFixedPtAmt("-100")]); + }); + + it("should transfer back perps to the user", async function () { + await expect(txFn).to.changeTokenBalances(perp, [deployer], [toFixedPtAmt("100")]); + }); + + it("should update the vault assets", async function () { + await checkReserveComposition( + perp, + [collateralToken, ...reserveTranches.slice(-3), currentTranchesIn[0]], + [0, toFixedPtAmt("200"), toFixedPtAmt("200"), toFixedPtAmt("200"), toFixedPtAmt("200")], + ); + + await checkVaultAssetComposition( + vault, + [collateralToken, currentTranchesIn[1]], + [toFixedPtAmt("1200"), toFixedPtAmt("800")], + ); + + await txFn(); + + await checkReserveComposition( + perp, + [collateralToken, ...reserveTranches.slice(-3), currentTranchesIn[0]], + [0, toFixedPtAmt("200"), toFixedPtAmt("200"), toFixedPtAmt("200"), toFixedPtAmt("300")], + ); + + await checkVaultAssetComposition( + vault, + [collateralToken, currentTranchesIn[1]], + [toFixedPtAmt("800"), toFixedPtAmt("1200")], + ); + }); + + it("should update the vault tvl", async function () { + expect(await vault.callStatic.getTVL()).to.eq(toFixedPtAmt("2000")); + await txFn(); + expect(await vault.callStatic.getTVL()).to.eq(toFixedPtAmt("2000")); + }); + }); + + describe("on successful swap with zero perp fees", function () { + let txFn: Promise; + beforeEach(async function () { + await feePolicy.computePerpMintFeePerc.returns(0); + await feePolicy.computeUnderlyingToPerpVaultSwapFeePerc.returns(toPercFixedPtAmt("0.1")); + txFn = () => vault.swapUnderlyingForPerps(toFixedPtAmt("100")); + }); + + it("should mint perps for swap and leave none left over", async function () { + expect(await perp.totalSupply()).to.eq(toFixedPtAmt("800")); + await expect(txFn()) + .to.emit(perp, "Transfer") + .withArgs(constants.AddressZero, vault.address, toFixedPtAmt("90")); + expect(await perp.totalSupply()).to.eq(toFixedPtAmt("890")); + expect(await perp.balanceOf(vault.address)).to.eq(0); + }); + + it("should return the perp amt", async function () { + expect(await vault.callStatic.swapUnderlyingForPerps(toFixedPtAmt("100"))).to.eq(toFixedPtAmt("90")); + }); + + it("should transfer underlying from the user", async function () { + await expect(txFn).to.changeTokenBalances(collateralToken, [deployer], [toFixedPtAmt("-100")]); + }); + + it("should transfer back perps to the user", async function () { + await expect(txFn).to.changeTokenBalances(perp, [deployer], [toFixedPtAmt("90")]); + }); + + it("should update the vault assets", async function () { + await checkReserveComposition( + perp, + [collateralToken, ...reserveTranches.slice(-3), currentTranchesIn[0]], + [0, toFixedPtAmt("200"), toFixedPtAmt("200"), toFixedPtAmt("200"), toFixedPtAmt("200")], + ); + + await checkVaultAssetComposition( + vault, + [collateralToken, currentTranchesIn[1]], + [toFixedPtAmt("1200"), toFixedPtAmt("800")], + ); + + await txFn(); + + await checkReserveComposition( + perp, + [collateralToken, ...reserveTranches.slice(-3), currentTranchesIn[0]], + [0, toFixedPtAmt("200"), toFixedPtAmt("200"), toFixedPtAmt("200"), toFixedPtAmt("290")], + ); + + await checkVaultAssetComposition( + vault, + [collateralToken, currentTranchesIn[1]], + [toFixedPtAmt("850"), toFixedPtAmt("1160")], + ); + }); + + it("should update the vault tvl", async function () { + expect(await vault.callStatic.getTVL()).to.eq(toFixedPtAmt("2000")); + await txFn(); + expect(await vault.callStatic.getTVL()).to.eq(toFixedPtAmt("2010")); + }); + }); + + describe("on successful swap", function () { + let txFn: Promise; + beforeEach(async function () { + await feePolicy.computePerpMintFeePerc.returns(toPercFixedPtAmt("0.05")); + await feePolicy.computeUnderlyingToPerpVaultSwapFeePerc.returns(toPercFixedPtAmt("0.1")); + txFn = () => vault.swapUnderlyingForPerps(toFixedPtAmt("100")); + }); + + it("should mint perps for swap and leave none left over", async function () { + expect(await perp.totalSupply()).to.eq(toFixedPtAmt("800")); + await expect(txFn()) + .to.emit(perp, "Transfer") + .withArgs(constants.AddressZero, vault.address, toFixedPtAmt("90")); + expect(await perp.totalSupply()).to.eq(toFixedPtAmt("885")); + expect(await perp.balanceOf(vault.address)).to.eq(0); + }); + + it("should burn perps as fee", async function () { + await expect(txFn()) + .to.emit(perp, "Transfer") + .withArgs(vault.address, constants.AddressZero, toFixedPtAmt("5")); + }); + + it("should return the perp amt", async function () { + expect(await vault.callStatic.swapUnderlyingForPerps(toFixedPtAmt("100"))).to.eq(toFixedPtAmt("85")); + }); + + it("should transfer underlying from the user", async function () { + await expect(txFn).to.changeTokenBalances(collateralToken, [deployer], [toFixedPtAmt("-100")]); + }); + + it("should transfer back perps to the user", async function () { + await expect(txFn).to.changeTokenBalances(perp, [deployer], [toFixedPtAmt("85")]); + }); + + it("should update the vault assets", async function () { + await checkReserveComposition( + perp, + [collateralToken, ...reserveTranches.slice(-3), currentTranchesIn[0]], + [0, toFixedPtAmt("200"), toFixedPtAmt("200"), toFixedPtAmt("200"), toFixedPtAmt("200")], + ); + + await checkVaultAssetComposition( + vault, + [collateralToken, currentTranchesIn[1]], + [toFixedPtAmt("1200"), toFixedPtAmt("800")], + ); + + await txFn(); + + await checkReserveComposition( + perp, + [collateralToken, ...reserveTranches.slice(-3), currentTranchesIn[0]], + [0, toFixedPtAmt("200"), toFixedPtAmt("200"), toFixedPtAmt("200"), toFixedPtAmt("290")], + ); + + await checkVaultAssetComposition( + vault, + [collateralToken, currentTranchesIn[1]], + [toFixedPtAmt("850"), toFixedPtAmt("1160")], + ); + }); + + it("should update the vault tvl", async function () { + expect(await vault.callStatic.getTVL()).to.eq(toFixedPtAmt("2000")); + await txFn(); + expect(await vault.callStatic.getTVL()).to.eq(toFixedPtAmt("2010")); + }); + }); + + describe("on successful swap with imperfect rounding", function () { + let txFn: Promise; + beforeEach(async function () { + await feePolicy.computePerpMintFeePerc.returns(toPercFixedPtAmt("0.1")); + await feePolicy.computeUnderlyingToPerpVaultSwapFeePerc.returns(toPercFixedPtAmt("0.1")); + txFn = () => vault.swapUnderlyingForPerps(toFixedPtAmt("100.999999999999999999")); + }); + + it("should mint perps for swap and leave none left over", async function () { + expect(await perp.totalSupply()).to.eq(toFixedPtAmt("800")); + await expect(txFn()) + .to.emit(perp, "Transfer") + .withArgs(constants.AddressZero, vault.address, toFixedPtAmt("90.899999999999999999")); + expect(await perp.totalSupply()).to.eq(toFixedPtAmt("880.799999999999999999")); + expect(await perp.balanceOf(vault.address)).to.eq(0); + }); + + it("should burn perps as fee", async function () { + await expect(txFn()) + .to.emit(perp, "Transfer") + .withArgs(vault.address, constants.AddressZero, toFixedPtAmt("10.1")); + }); + + it("should return the perp amt", async function () { + expect(await vault.callStatic.swapUnderlyingForPerps(toFixedPtAmt("100.999999999999999999"))).to.eq( + toFixedPtAmt("80.799999999999999999"), + ); + }); + + it("should transfer underlying from the user", async function () { + await expect(txFn).to.changeTokenBalances( + collateralToken, + [deployer], + [toFixedPtAmt("-100.999999999999999999")], + ); + }); + + it("should transfer back perps to the user", async function () { + await expect(txFn).to.changeTokenBalances(perp, [deployer], [toFixedPtAmt("80.799999999999999999")]); + }); + + it("should update the vault assets", async function () { + await checkReserveComposition( + perp, + [collateralToken, ...reserveTranches.slice(-3), currentTranchesIn[0]], + [0, toFixedPtAmt("200"), toFixedPtAmt("200"), toFixedPtAmt("200"), toFixedPtAmt("200")], + ); + + await checkVaultAssetComposition( + vault, + [collateralToken, currentTranchesIn[1]], + [toFixedPtAmt("1200"), toFixedPtAmt("800")], + ); + + await txFn(); + + await checkReserveComposition( + perp, + [collateralToken, ...reserveTranches.slice(-3), currentTranchesIn[0]], + [0, toFixedPtAmt("200"), toFixedPtAmt("200"), toFixedPtAmt("200"), toFixedPtAmt("290.899999999999999999")], + ); + + await checkVaultAssetComposition( + vault, + [collateralToken, currentTranchesIn[1]], + [toFixedPtAmt("846.500000000000000004"), toFixedPtAmt("1163.599999999999999996")], + ); + }); + + it("should update the vault tvl", async function () { + expect(await vault.callStatic.getTVL()).to.eq(toFixedPtAmt("2000")); + await txFn(); + expect(await vault.callStatic.getTVL()).to.eq(toFixedPtAmt("2010.10")); + }); + }); + + describe("on successful swap when deposit bond is fresh", function () { + let txFn: Promise; + beforeEach(async function () { + await advancePerpQueueToBondMaturity(perp, await getDepositBond(perp)); + await feePolicy.computePerpMintFeePerc.returns(toPercFixedPtAmt("0.05")); + await feePolicy.computeUnderlyingToPerpVaultSwapFeePerc.returns(toPercFixedPtAmt("0.1")); + txFn = () => vault.swapUnderlyingForPerps(toFixedPtAmt("100")); + }); + + it("should mint perps for swap and leave none left over", async function () { + expect(await perp.totalSupply()).to.eq(toFixedPtAmt("800")); + await expect(txFn()) + .to.emit(perp, "Transfer") + .withArgs(constants.AddressZero, vault.address, toFixedPtAmt("90")); + expect(await perp.totalSupply()).to.eq(toFixedPtAmt("885")); + expect(await perp.balanceOf(vault.address)).to.eq(0); + }); + + it("should burn perps as fee", async function () { + await expect(txFn()) + .to.emit(perp, "Transfer") + .withArgs(vault.address, constants.AddressZero, toFixedPtAmt("5")); + }); + + it("should return the perp amt", async function () { + expect(await vault.callStatic.swapUnderlyingForPerps(toFixedPtAmt("100"))).to.eq(toFixedPtAmt("85")); + }); + + it("should transfer underlying from the user", async function () { + await expect(txFn).to.changeTokenBalances(collateralToken, [deployer], [toFixedPtAmt("-100")]); + }); + + it("should transfer back perps to the user", async function () { + await expect(txFn).to.changeTokenBalances(perp, [deployer], [toFixedPtAmt("85")]); + }); + + it("should update the vault assets", async function () { + const depositBond = await bondAt(perp.callStatic.getDepositBond()); + const depositTranches = await getTranches(depositBond); + await checkReserveComposition(perp, [collateralToken], [toFixedPtAmt("800")]); + + await checkVaultAssetComposition( + vault, + [collateralToken, currentTranchesIn[1]], + [toFixedPtAmt("1200"), toFixedPtAmt("800")], + ); + + await txFn(); + + await checkReserveComposition( + perp, + [collateralToken, depositTranches[0]], + [toFixedPtAmt("800"), toFixedPtAmt("90")], + ); + + await checkVaultAssetComposition( + vault, + [collateralToken, currentTranchesIn[1], depositTranches[1]], + [toFixedPtAmt("850"), toFixedPtAmt("800"), toFixedPtAmt("360")], + ); + }); + + it("should update the vault tvl", async function () { + expect(await vault.callStatic.getTVL()).to.eq(toFixedPtAmt("2000")); + await txFn(); + expect(await vault.callStatic.getTVL()).to.eq(toFixedPtAmt("2010")); + }); + }); + }); + + describe("#swapPerpsForUnderlying", function () { + describe("when fee is zero", function () { + beforeEach(async function () { + await feePolicy.computePerpBurnFeePerc.returns(0); + await feePolicy.computePerpToUnderlyingVaultSwapFeePerc.returns(0); + }); + + describe("when perp price is 1", function () { + it("should compute swap amount", async function () { + const s = await vault.callStatic.computePerpToUnderlyingSwapAmt(toFixedPtAmt("100")); + expect(s[0]).to.eq(toFixedPtAmt("100")); + expect(s[1]).to.eq(0); + expect(s[2].perpTVL).to.eq(toFixedPtAmt("800")); + expect(s[2].vaultTVL).to.eq(toFixedPtAmt("2000")); + expect(s[2].seniorTR).to.eq("200"); + }); + }); + + describe("when perp price > 1", function () { + beforeEach(async function () { + await collateralToken.transfer(perp.address, toFixedPtAmt("800")); + }); + it("should compute swap amount", async function () { + const s = await vault.callStatic.computePerpToUnderlyingSwapAmt(toFixedPtAmt("100")); + expect(s[0]).to.eq(toFixedPtAmt("200")); + expect(s[1]).to.eq(0); + expect(s[2].perpTVL).to.eq(toFixedPtAmt("1600")); + expect(s[2].vaultTVL).to.eq(toFixedPtAmt("2000")); + expect(s[2].seniorTR).to.eq("200"); + }); + }); + + describe("when perp price < 1", function () { + beforeEach(async function () { + await rebase(collateralToken, rebaseOracle, -0.9); + }); + it("should compute swap amount", async function () { + const s = await vault.callStatic.computePerpToUnderlyingSwapAmt(toFixedPtAmt("100")); + expect(s[0]).to.eq(toFixedPtAmt("50")); + expect(s[1]).to.eq(0); + expect(s[2].perpTVL).to.eq(toFixedPtAmt("400")); + expect(s[2].vaultTVL).to.eq(toFixedPtAmt("120")); + expect(s[2].seniorTR).to.eq("200"); + }); + }); + }); + + describe("when fee is not zero", function () { + beforeEach(async function () { + await feePolicy.computePerpBurnFeePerc.returns(toPercFixedPtAmt("0.05")); + await feePolicy.computePerpToUnderlyingVaultSwapFeePerc.returns(toPercFixedPtAmt("0.15")); + }); + + it("should compute swap amount", async function () { + const s = await vault.callStatic.computePerpToUnderlyingSwapAmt(toFixedPtAmt("100")); + expect(s[0]).to.eq(toFixedPtAmt("80")); + expect(s[1]).to.eq(toFixedPtAmt("5")); + expect(s[2].perpTVL).to.eq(toFixedPtAmt("800")); + expect(s[2].vaultTVL).to.eq(toFixedPtAmt("2000")); + expect(s[2].seniorTR).to.eq("200"); + }); + }); + + describe("when swap amount is zero", function () { + it("should be reverted", async function () { + await expect(vault.swapPerpsForUnderlying(0)).to.be.revertedWithCustomError(vault, "UnacceptableSwap"); + }); + }); + + describe("when fee is 100%", function () { + it("should be reverted", async function () { + await feePolicy.computePerpBurnFeePerc.returns(0); + await feePolicy.computePerpToUnderlyingVaultSwapFeePerc.returns(toPercFixedPtAmt("1")); + await expect(vault.swapPerpsForUnderlying(toFixedPtAmt("100"))).to.be.revertedWithCustomError( + vault, + "UnacceptableSwap", + ); + }); + }); + + describe("when fee is greater than 100%", function () { + it("should be reverted", async function () { + await feePolicy.computePerpBurnFeePerc.returns(toPercFixedPtAmt("0.05")); + await feePolicy.computePerpToUnderlyingVaultSwapFeePerc.returns(toPercFixedPtAmt("1")); + await expect(vault.swapPerpsForUnderlying(toFixedPtAmt("100"))).to.be.reverted; + }); + }); + + describe("on successful swap with zero fees", function () { + let txFn: Promise; + beforeEach(async function () { + await feePolicy.computePerpBurnFeePerc.returns(0); + await feePolicy.computePerpToUnderlyingVaultSwapFeePerc.returns(0); + txFn = () => vault.swapPerpsForUnderlying(toFixedPtAmt("100")); + }); + + it("should redeem perps for swap and leave none left over", async function () { + expect(await perp.totalSupply()).to.eq(toFixedPtAmt("800")); + await expect(txFn()) + .to.emit(perp, "Transfer") + .withArgs(vault.address, constants.AddressZero, toFixedPtAmt("100")); + expect(await perp.totalSupply()).to.eq(toFixedPtAmt("700")); + expect(await perp.balanceOf(vault.address)).to.eq(0); + }); + + it("should return the underlying amt", async function () { + expect(await vault.callStatic.swapPerpsForUnderlying(toFixedPtAmt("100"))).to.eq(toFixedPtAmt("100")); + }); + + it("should transfer perps from the user", async function () { + await expect(txFn).to.changeTokenBalances(perp, [deployer], [toFixedPtAmt("-100")]); + }); + + it("should transfer back underlying to the user", async function () { + await expect(txFn).to.changeTokenBalances(collateralToken, [deployer], [toFixedPtAmt("100")]); + }); + + it("should update the vault assets", async function () { + await checkReserveComposition( + perp, + [collateralToken, ...reserveTranches.slice(-3), currentTranchesIn[0]], + [0, toFixedPtAmt("200"), toFixedPtAmt("200"), toFixedPtAmt("200"), toFixedPtAmt("200")], + ); + + await checkVaultAssetComposition( + vault, + [collateralToken, currentTranchesIn[1]], + [toFixedPtAmt("1200"), toFixedPtAmt("800")], + ); + + await txFn(); + + await checkReserveComposition( + perp, + [collateralToken, ...reserveTranches.slice(-3), currentTranchesIn[0]], + [0, toFixedPtAmt("175"), toFixedPtAmt("175"), toFixedPtAmt("175"), toFixedPtAmt("175")], + ); + + await checkVaultAssetComposition( + vault, + [collateralToken, currentTranchesIn[1], ...reserveTranches.slice(-3)], + [toFixedPtAmt("1225"), toFixedPtAmt("700"), toFixedPtAmt("25"), toFixedPtAmt("25"), toFixedPtAmt("25")], + ); + }); + + it("should update the vault tvl", async function () { + expect(await vault.callStatic.getTVL()).to.eq(toFixedPtAmt("2000")); + await txFn(); + expect(await vault.callStatic.getTVL()).to.eq(toFixedPtAmt("2000")); + }); + }); + + describe("on successful swap with zero perp fees", function () { + let txFn: Promise; + beforeEach(async function () { + await feePolicy.computePerpBurnFeePerc.returns(0); + await feePolicy.computePerpToUnderlyingVaultSwapFeePerc.returns(toPercFixedPtAmt("0.1")); + txFn = () => vault.swapPerpsForUnderlying(toFixedPtAmt("100")); + }); + + it("should redeem perps for swap and leave none left over", async function () { + expect(await perp.totalSupply()).to.eq(toFixedPtAmt("800")); + await expect(txFn()) + .to.emit(perp, "Transfer") + .withArgs(vault.address, constants.AddressZero, toFixedPtAmt("100")); + expect(await perp.totalSupply()).to.eq(toFixedPtAmt("700")); + expect(await perp.balanceOf(vault.address)).to.eq(0); + }); + + it("should return the underlying amt", async function () { + expect(await vault.callStatic.swapPerpsForUnderlying(toFixedPtAmt("100"))).to.eq(toFixedPtAmt("90")); + }); + + it("should transfer perps from the user", async function () { + await expect(txFn).to.changeTokenBalances(perp, [deployer], [toFixedPtAmt("-100")]); + }); + + it("should transfer back underlying to the user", async function () { + await expect(txFn).to.changeTokenBalances(collateralToken, [deployer], [toFixedPtAmt("90")]); + }); + + it("should update the vault assets", async function () { + await checkReserveComposition( + perp, + [collateralToken, ...reserveTranches.slice(-3), currentTranchesIn[0]], + [0, toFixedPtAmt("200"), toFixedPtAmt("200"), toFixedPtAmt("200"), toFixedPtAmt("200")], + ); + + await checkVaultAssetComposition( + vault, + [collateralToken, currentTranchesIn[1]], + [toFixedPtAmt("1200"), toFixedPtAmt("800")], + ); + + await txFn(); + + await checkReserveComposition( + perp, + [collateralToken, ...reserveTranches.slice(-3), currentTranchesIn[0]], + [0, toFixedPtAmt("175"), toFixedPtAmt("175"), toFixedPtAmt("175"), toFixedPtAmt("175")], + ); + + await checkVaultAssetComposition( + vault, + [collateralToken, currentTranchesIn[1], ...reserveTranches.slice(-3)], + [toFixedPtAmt("1235"), toFixedPtAmt("700"), toFixedPtAmt("25"), toFixedPtAmt("25"), toFixedPtAmt("25")], + ); + }); + + it("should update the vault tvl", async function () { + expect(await vault.callStatic.getTVL()).to.eq(toFixedPtAmt("2000")); + await txFn(); + expect(await vault.callStatic.getTVL()).to.eq(toFixedPtAmt("2010")); + }); + }); + + describe("on successful swap", function () { + let txFn: Promise; + beforeEach(async function () { + await feePolicy.computePerpBurnFeePerc.returns(toPercFixedPtAmt("0.1")); + await feePolicy.computePerpToUnderlyingVaultSwapFeePerc.returns(toPercFixedPtAmt("0.15")); + txFn = () => vault.swapPerpsForUnderlying(toFixedPtAmt("100")); + }); + + it("should redeem perps for swap and leave none left over", async function () { + expect(await perp.totalSupply()).to.eq(toFixedPtAmt("800")); + await expect(txFn()) + .to.emit(perp, "Transfer") + .withArgs(vault.address, constants.AddressZero, toFixedPtAmt("90")); + expect(await perp.totalSupply()).to.eq(toFixedPtAmt("700")); + expect(await perp.balanceOf(vault.address)).to.eq(0); + }); + + it("should burn perps as fee", async function () { + await expect(txFn()) + .to.emit(perp, "Transfer") + .withArgs(vault.address, constants.AddressZero, toFixedPtAmt("10")); + }); + + it("should return the underlying amt", async function () { + expect(await vault.callStatic.swapPerpsForUnderlying(toFixedPtAmt("100"))).to.eq(toFixedPtAmt("75")); + }); + + it("should transfer perps from the user", async function () { + await expect(txFn).to.changeTokenBalances(perp, [deployer], [toFixedPtAmt("-100")]); + }); + + it("should transfer back underlying to the user", async function () { + await expect(txFn).to.changeTokenBalances(collateralToken, [deployer], [toFixedPtAmt("75")]); + }); + + it("should update the vault assets", async function () { + await checkReserveComposition( + perp, + [collateralToken, ...reserveTranches.slice(-3), currentTranchesIn[0]], + [0, toFixedPtAmt("200"), toFixedPtAmt("200"), toFixedPtAmt("200"), toFixedPtAmt("200")], + ); + + await checkVaultAssetComposition( + vault, + [collateralToken, currentTranchesIn[1]], + [toFixedPtAmt("1200"), toFixedPtAmt("800")], + ); + + await txFn(); + + await checkReserveComposition( + perp, + [collateralToken, ...reserveTranches.slice(-3), currentTranchesIn[0]], + [ + 0, + toFixedPtAmt("177.215189873417721519"), + toFixedPtAmt("177.215189873417721519"), + toFixedPtAmt("177.215189873417721519"), + toFixedPtAmt("177.215189873417721519"), + ], + ); + + await checkVaultAssetComposition( + vault, + [collateralToken, currentTranchesIn[1], ...reserveTranches.slice(-3), currentTranchesIn[0]], + [ + toFixedPtAmt("1238.924050632911392"), + toFixedPtAmt("708.8607594936708864"), + toFixedPtAmt("22.784810126582278481"), + toFixedPtAmt("22.784810126582278481"), + toFixedPtAmt("22.784810126582278481"), + toFixedPtAmt("0.000000000000000081"), + ], + ); + }); + + it("should update the vault tvl", async function () { + expect(await vault.callStatic.getTVL()).to.eq(toFixedPtAmt("2000")); + await txFn(); + expect(await vault.callStatic.getTVL()).to.eq(toFixedPtAmt("2016.139240506329113843")); + }); + }); + + describe("on successful swap with imperfect rounding", function () { + let txFn: Promise; + beforeEach(async function () { + await feePolicy.computePerpBurnFeePerc.returns(toPercFixedPtAmt("0.1")); + await feePolicy.computePerpToUnderlyingVaultSwapFeePerc.returns(toPercFixedPtAmt("0.1")); + txFn = () => vault.swapPerpsForUnderlying(toFixedPtAmt("100.999999999999999999")); + }); + + it("should redeem perps for swap and leave none left over", async function () { + expect(await perp.totalSupply()).to.eq(toFixedPtAmt("800")); + await expect(txFn()) + .to.emit(perp, "Transfer") + .withArgs(vault.address, constants.AddressZero, toFixedPtAmt("90.899999999999999999")); + expect(await perp.totalSupply()).to.eq(toFixedPtAmt("699.000000000000000001")); + expect(await perp.balanceOf(vault.address)).to.eq(0); + }); + + it("should burn perps as fee", async function () { + await expect(txFn()) + .to.emit(perp, "Transfer") + .withArgs(vault.address, constants.AddressZero, toFixedPtAmt("10.1")); + }); + + it("should return the underlying amt", async function () { + expect(await vault.callStatic.swapPerpsForUnderlying(toFixedPtAmt("100.999999999999999999"))).to.eq( + toFixedPtAmt("80.799999999999999999"), + ); + }); + + it("should transfer perps from the user", async function () { + await expect(txFn).to.changeTokenBalances(perp, [deployer], [toFixedPtAmt("-100.999999999999999999")]); + }); + + it("should transfer back underlying to the user", async function () { + await expect(txFn).to.changeTokenBalances(collateralToken, [deployer], [toFixedPtAmt("80.799999999999999999")]); + }); + + it("should update the vault assets", async function () { + await checkReserveComposition( + perp, + [collateralToken, ...reserveTranches.slice(-3), currentTranchesIn[0]], + [0, toFixedPtAmt("200"), toFixedPtAmt("200"), toFixedPtAmt("200"), toFixedPtAmt("200")], + ); + + await checkVaultAssetComposition( + vault, + [collateralToken, currentTranchesIn[1]], + [toFixedPtAmt("1200"), toFixedPtAmt("800")], + ); + + await txFn(); + + await checkReserveComposition( + perp, + [collateralToken, ...reserveTranches.slice(-3), currentTranchesIn[0]], + [ + 0, + toFixedPtAmt("176.984428408659323966"), + toFixedPtAmt("176.984428408659323966"), + toFixedPtAmt("176.984428408659323966"), + toFixedPtAmt("176.984428408659323966"), + ], + ); + + await checkVaultAssetComposition( + vault, + [collateralToken, currentTranchesIn[1], ...reserveTranches.slice(-3), currentTranchesIn[0]], + [ + toFixedPtAmt("1234.277857956703380001"), + toFixedPtAmt("707.937713634637296000"), + toFixedPtAmt("23.015571591340676034"), + toFixedPtAmt("23.015571591340676034"), + toFixedPtAmt("23.015571591340676034"), + toFixedPtAmt("0.000000000000000034"), + ], + ); + }); + + it("should update the vault tvl", async function () { + expect(await vault.callStatic.getTVL()).to.eq(toFixedPtAmt("2000")); + await txFn(); + expect(await vault.callStatic.getTVL()).to.eq(toFixedPtAmt("2011.262286365362704103")); + }); + }); + + describe("on successful swap with some the juniors in the vault", function () { + let txFn: Promise; + beforeEach(async function () { + await feePolicy.computePerpBurnFeePerc.returns(toPercFixedPtAmt("0.1")); + await feePolicy.computePerpToUnderlyingVaultSwapFeePerc.returns(toPercFixedPtAmt("0.15")); + remainingJuniorTranches[1].transfer(vault.address, toFixedPtAmt("100")); + remainingJuniorTranches[2].transfer(vault.address, toFixedPtAmt("100")); + remainingJuniorTranches[3].transfer(vault.address, toFixedPtAmt("100")); + txFn = () => vault.swapPerpsForUnderlying(toFixedPtAmt("200")); + }); + + it("should redeem perps for swap and leave none left over", async function () { + expect(await perp.totalSupply()).to.eq(toFixedPtAmt("800")); + await expect(txFn()) + .to.emit(perp, "Transfer") + .withArgs(vault.address, constants.AddressZero, toFixedPtAmt("180")); + expect(await perp.totalSupply()).to.eq(toFixedPtAmt("600")); + expect(await perp.balanceOf(vault.address)).to.eq(0); + }); + + it("should burn perps as fee", async function () { + await expect(txFn()) + .to.emit(perp, "Transfer") + .withArgs(vault.address, constants.AddressZero, toFixedPtAmt("20")); + }); + + it("should return the underlying amt", async function () { + expect(await vault.callStatic.swapPerpsForUnderlying(toFixedPtAmt("200"))).to.eq(toFixedPtAmt("150")); + }); + + it("should transfer perps from the user", async function () { + await expect(txFn).to.changeTokenBalances(perp, [deployer], [toFixedPtAmt("-200")]); + }); + + it("should transfer back underlying to the user", async function () { + await expect(txFn).to.changeTokenBalances(collateralToken, [deployer], [toFixedPtAmt("150")]); + }); + + it("should update the vault assets", async function () { + await checkReserveComposition( + perp, + [collateralToken, ...reserveTranches.slice(-3), currentTranchesIn[0]], + [0, toFixedPtAmt("200"), toFixedPtAmt("200"), toFixedPtAmt("200"), toFixedPtAmt("200")], + ); + + await checkVaultAssetComposition( + vault, + [collateralToken, currentTranchesIn[1]], + [toFixedPtAmt("1200"), toFixedPtAmt("800")], + ); + + await txFn(); + + await checkReserveComposition( + perp, + [collateralToken, ...reserveTranches.slice(-3), currentTranchesIn[0]], + [ + 0, + toFixedPtAmt("153.846153846153846154"), + toFixedPtAmt("153.846153846153846154"), + toFixedPtAmt("153.846153846153846154"), + toFixedPtAmt("153.846153846153846154"), + ], + ); + + await checkVaultAssetComposition( + vault, + [collateralToken, currentTranchesIn[1], ...reserveTranches.slice(-3), currentTranchesIn[0]], + [ + toFixedPtAmt("1655.769230769230769000"), + toFixedPtAmt("615.384615384615384800"), + toFixedPtAmt("21.153846153846153846"), + toFixedPtAmt("21.153846153846153846"), + toFixedPtAmt("21.153846153846153846"), + toFixedPtAmt("0.000000000000000046"), + ], + ); + }); + + it("should update the vault tvl", async function () { + expect(await vault.callStatic.getTVL()).to.eq(toFixedPtAmt("2000")); + await txFn(); + expect(await vault.callStatic.getTVL()).to.eq(toFixedPtAmt("2334.615384615384615338")); + }); + }); + + describe("on successful swap with all the juniors in the vault", function () { + let txFn: Promise; + beforeEach(async function () { + await feePolicy.computePerpBurnFeePerc.returns(toPercFixedPtAmt("0.1")); + await feePolicy.computePerpToUnderlyingVaultSwapFeePerc.returns(toPercFixedPtAmt("0.15")); + + remainingJuniorTranches[1].transfer(vault.address, toFixedPtAmt("800")); + remainingJuniorTranches[2].transfer(vault.address, toFixedPtAmt("800")); + remainingJuniorTranches[3].transfer(vault.address, toFixedPtAmt("800")); + + txFn = () => vault.swapPerpsForUnderlying(toFixedPtAmt("200")); + }); + + it("should redeem perps for swap and leave none left over", async function () { + expect(await perp.totalSupply()).to.eq(toFixedPtAmt("800")); + await expect(txFn()) + .to.emit(perp, "Transfer") + .withArgs(vault.address, constants.AddressZero, toFixedPtAmt("180")); + expect(await perp.totalSupply()).to.eq(toFixedPtAmt("600")); + expect(await perp.balanceOf(vault.address)).to.eq(0); + }); + + it("should burn perps as fee", async function () { + await expect(txFn()) + .to.emit(perp, "Transfer") + .withArgs(vault.address, constants.AddressZero, toFixedPtAmt("20")); + }); + + it("should return the underlying amt", async function () { + expect(await vault.callStatic.swapPerpsForUnderlying(toFixedPtAmt("200"))).to.eq(toFixedPtAmt("150")); + }); + + it("should transfer perps from the user", async function () { + await expect(txFn).to.changeTokenBalances(perp, [deployer], [toFixedPtAmt("-200")]); + }); + + it("should transfer back underlying to the user", async function () { + await expect(txFn).to.changeTokenBalances(collateralToken, [deployer], [toFixedPtAmt("150")]); + }); + + it("should update the vault assets", async function () { + await checkReserveComposition( + perp, + [collateralToken, ...reserveTranches.slice(-3), currentTranchesIn[0]], + [0, toFixedPtAmt("200"), toFixedPtAmt("200"), toFixedPtAmt("200"), toFixedPtAmt("200")], + ); + + await checkVaultAssetComposition( + vault, + [collateralToken, currentTranchesIn[1]], + [toFixedPtAmt("1200"), toFixedPtAmt("800")], + ); + + await txFn(); + + await checkReserveComposition( + perp, + [collateralToken, ...reserveTranches.slice(-3), currentTranchesIn[0]], + [ + 0, + toFixedPtAmt("153.846153846153846154"), + toFixedPtAmt("153.846153846153846154"), + toFixedPtAmt("153.846153846153846154"), + toFixedPtAmt("153.846153846153846154"), + ], + ); + + await checkVaultAssetComposition( + vault, + [ + collateralToken, + currentTranchesIn[1], + remainingJuniorTranches[1], + remainingJuniorTranches[2], + remainingJuniorTranches[3], + ...reserveTranches.slice(-3), + currentTranchesIn[0], + ], + [ + toFixedPtAmt("1973.076923076923076"), + toFixedPtAmt("615.3846153846153848"), + toFixedPtAmt("615.3846153846153848"), + toFixedPtAmt("615.3846153846153848"), + toFixedPtAmt("615.3846153846153848"), + toFixedPtAmt("0.000000000000000046"), + toFixedPtAmt("0.000000000000000046"), + toFixedPtAmt("0.000000000000000046"), + toFixedPtAmt("0.000000000000000046"), + ], + ); + }); + + it("should update the vault tvl", async function () { + expect(await vault.callStatic.getTVL()).to.eq(toFixedPtAmt("2000")); // + 2400 transferred in + await txFn(); + expect(await vault.callStatic.getTVL()).to.eq(toFixedPtAmt("4434.6153846153846152")); + }); + }); + }); +}); diff --git a/spot-contracts/test/strategies/BasicFeeStrategy.ts b/spot-contracts/test/strategies/BasicFeeStrategy.ts deleted file mode 100644 index 4091d96b..00000000 --- a/spot-contracts/test/strategies/BasicFeeStrategy.ts +++ /dev/null @@ -1,204 +0,0 @@ -import { expect } from "chai"; -import { ethers } from "hardhat"; -import { Contract, Transaction, Signer } from "ethers"; - -import { toFixedPtAmt } from "../helpers"; - -let feeStrategy: Contract, deployer: Signer, otherUser: Signer, feeToken: Contract; - -describe("BasicFeeStrategy", function () { - beforeEach(async function () { - const accounts = await ethers.getSigners(); - deployer = accounts[0]; - otherUser = accounts[1]; - - const ERC20 = await ethers.getContractFactory("MockERC20"); - feeToken = await ERC20.deploy(); - - const factory = await ethers.getContractFactory("BasicFeeStrategy"); - feeStrategy = await factory.deploy(feeToken.address); - }); - - 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(feeToken.address); - }); - it("should return owner", async function () { - expect(await feeStrategy.owner()).to.eq(await deployer.getAddress()); - }); - }); - - describe("#updateMintFeePerc", 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).updateMintFeePerc("1")).to.be.revertedWith( - "Ownable: caller is not the owner", - ); - }); - }); - - describe("when set mint fee perc is valid", function () { - beforeEach(async function () { - tx = feeStrategy.connect(deployer).updateMintFeePerc("50000000"); - await tx; - }); - it("should update reference", async function () { - expect(await feeStrategy.mintFeePerc()).to.eq("50000000"); - }); - it("should emit event", async function () { - await expect(tx).to.emit(feeStrategy, "UpdatedMintPerc").withArgs("50000000"); - }); - }); - }); - - describe("#updateBurnFeePerc", 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).updateBurnFeePerc("1")).to.be.revertedWith( - "Ownable: caller is not the owner", - ); - }); - }); - - describe("when set burn fee perc is valid", function () { - beforeEach(async function () { - tx = feeStrategy.connect(deployer).updateBurnFeePerc("50000000"); - await tx; - }); - it("should update reference", async function () { - expect(await feeStrategy.burnFeePerc()).to.eq("50000000"); - }); - it("should emit event", async function () { - await expect(tx).to.emit(feeStrategy, "UpdatedBurnPerc").withArgs("50000000"); - }); - }); - }); - - describe("#updateRolloverFeePerc", 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).updateRolloverFeePerc("1")).to.be.revertedWith( - "Ownable: caller is not the owner", - ); - }); - }); - - describe("when set rollover fee perc is valid", function () { - beforeEach(async function () { - tx = feeStrategy.connect(deployer).updateRolloverFeePerc("50000000"); - await tx; - }); - it("should update reference", async function () { - expect(await feeStrategy.rolloverFeePerc()).to.eq("50000000"); - }); - it("should emit event", async function () { - await expect(tx).to.emit(feeStrategy, "UpdatedRolloverPerc").withArgs("50000000"); - }); - }); - }); - - 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("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 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("0"); - 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"); - 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"); - 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"); - const r = await feeStrategy.computeBurnFees(toFixedPtAmt("1000")); - 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 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("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("0"); - expect(r[1]).to.eq("0"); - }); - }); - }); -}); diff --git a/spot-contracts/test/strategies/CDRPricingStrategy.ts b/spot-contracts/test/strategies/CDRPricingStrategy.ts deleted file mode 100644 index ada3d362..00000000 --- a/spot-contracts/test/strategies/CDRPricingStrategy.ts +++ /dev/null @@ -1,163 +0,0 @@ -import { expect, use } from "chai"; -import { network, ethers } from "hardhat"; -import { Contract, Signer } from "ethers"; -import { smock } from "@defi-wonderland/smock"; - -import { - TimeHelpers, - setupCollateralToken, - setupBondFactory, - createBondWithFactory, - toFixedPtAmt, - rebase, - depositIntoBond, - getTranches, -} from "../helpers"; -use(smock.matchers); - -let bondFactory: Contract, - collateralToken: Contract, - rebaseOracle: Contract, - pricingStrategy: Contract, - perp: Contract, - deployer: Signer; - -async function setupContracts() { - const accounts = await ethers.getSigners(); - deployer = accounts[0]; - - bondFactory = await setupBondFactory(); - ({ collateralToken, rebaseOracle } = await setupCollateralToken("Bitcoin", "BTC")); - - 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(); -} - -describe("CDRPricingStrategy", function () { - beforeEach(async () => { - await setupContracts(); - }); - - afterEach(async function () { - await network.provider.send("hardhat_reset"); - }); - - describe("decimals", function () { - it("should be set", async function () { - expect(await pricingStrategy.decimals()).to.eq(8); - }); - }); - - describe("computeTranchePrice", function () { - let bond: Contract, tranches: Contract[]; - beforeEach(async function () { - bond = await createBondWithFactory(bondFactory, collateralToken, [500, 500], 86400); - tranches = await getTranches(bond); - }); - - describe("when bond is empty", function () { - it("should return zero", async function () { - expect(await pricingStrategy.computeTranchePrice(tranches[0].address)).to.eq("100000000"); - }); - }); - - describe("when bond has assets", function () { - beforeEach(async function () { - await depositIntoBond(bond, toFixedPtAmt("1000"), deployer); - }); - - describe("when pricing the tranche", function () { - describe("when bond not mature", function () { - it("should return the price", async function () { - expect(await pricingStrategy.computeTranchePrice(tranches[0].address)).to.eq("100000000"); - }); - }); - - describe("when bond is mature", function () { - beforeEach(async function () { - await TimeHelpers.increaseTime(86400); - await bond.mature(); // NOTE: Any rebase after maturity goes directly to the tranches - }); - - describe("when cdr = 1", async function () { - it("should return the price", async function () { - expect(await pricingStrategy.computeTranchePrice(tranches[0].address)).to.eq("100000000"); - }); - }); - - describe("when cdr > 1", async function () { - beforeEach(async function () { - await rebase(collateralToken, rebaseOracle, 0.1); - }); - it("should return the price", async function () { - expect(await pricingStrategy.computeTranchePrice(tranches[0].address)).to.eq("110000000"); - }); - }); - - describe("when cdr < 1", async function () { - beforeEach(async function () { - await rebase(collateralToken, rebaseOracle, -0.1); - }); - it("should return the price", async function () { - expect(await pricingStrategy.computeTranchePrice(tranches[0].address)).to.eq("90000000"); - }); - }); - }); - }); - }); - }); - - describe("computeMatureTranchePrice", function () { - describe("when debt is zero", function () { - it("should return zero", async function () { - expect( - await pricingStrategy.computeMatureTranchePrice( - collateralToken.address, - toFixedPtAmt("0"), - toFixedPtAmt("0"), - ), - ).to.eq("100000000"); - }); - }); - - describe("when cdr = 1", async function () { - it("should return the price", async function () { - expect( - await pricingStrategy.computeMatureTranchePrice( - collateralToken.address, - toFixedPtAmt("100"), - toFixedPtAmt("100"), - ), - ).to.eq("100000000"); - }); - }); - - describe("when cdr > 1", async function () { - it("should return the price", async function () { - expect( - await pricingStrategy.computeMatureTranchePrice( - collateralToken.address, - toFixedPtAmt("110"), - toFixedPtAmt("100"), - ), - ).to.eq("110000000"); - }); - }); - - describe("when cdr < 1", async function () { - it("should return the price", async function () { - expect( - await pricingStrategy.computeMatureTranchePrice( - collateralToken.address, - toFixedPtAmt("90"), - toFixedPtAmt("100"), - ), - ).to.eq("90000000"); - }); - }); - }); -}); diff --git a/spot-contracts/test/strategies/NonEquityCDRLBPricingStrategy.ts b/spot-contracts/test/strategies/NonEquityCDRLBPricingStrategy.ts deleted file mode 100644 index 7c684a00..00000000 --- a/spot-contracts/test/strategies/NonEquityCDRLBPricingStrategy.ts +++ /dev/null @@ -1,153 +0,0 @@ -import { expect } from "chai"; -import { network, ethers } from "hardhat"; -import { Contract, Signer } from "ethers"; - -import { - TimeHelpers, - setupCollateralToken, - setupBondFactory, - createBondWithFactory, - toFixedPtAmt, - rebase, - depositIntoBond, - getTranches, -} from "../helpers"; - -let bondFactory: Contract, - collateralToken: Contract, - rebaseOracle: Contract, - pricingStrategy: Contract, - deployer: Signer; - -async function setupContracts() { - const accounts = await ethers.getSigners(); - deployer = accounts[0]; - - bondFactory = await setupBondFactory(); - ({ collateralToken, rebaseOracle } = await setupCollateralToken("Bitcoin", "BTC")); - - const NonEquityCDRLBPricingStrategy = await ethers.getContractFactory("NonEquityCDRLBPricingStrategy"); - pricingStrategy = await NonEquityCDRLBPricingStrategy.deploy(); -} - -describe("NonEquityCDRLBPricingStrategy", function () { - beforeEach(async () => { - await setupContracts(); - }); - - afterEach(async function () { - await network.provider.send("hardhat_reset"); - }); - - describe("decimals", function () { - it("should be set", async function () { - expect(await pricingStrategy.decimals()).to.eq(8); - }); - }); - - describe("computeTranchePrice", function () { - let bond: Contract, tranches: Contract[]; - beforeEach(async function () { - bond = await createBondWithFactory(bondFactory, collateralToken, [500, 500], 86400); - tranches = await getTranches(bond); - }); - - describe("when bond has no assets", function () { - it("should return the price", async function () { - expect(await pricingStrategy.computeTranchePrice(tranches[0].address)).to.eq("100000000"); - }); - }); - - describe("when bond has assets", function () { - beforeEach(async function () { - await depositIntoBond(bond, toFixedPtAmt("1000"), deployer); - }); - describe("when bond not mature", function () { - it("should return the price", async function () { - expect(await pricingStrategy.computeTranchePrice(tranches[0].address)).to.eq("100000000"); - }); - }); - - describe("when bond is mature", function () { - beforeEach(async function () { - await TimeHelpers.increaseTime(86400); - await bond.mature(); // NOTE: Any rebase after maturity goes directly to the tranches - }); - - describe("when cdr = 1", async function () { - it("should return the price", async function () { - expect(await pricingStrategy.computeTranchePrice(tranches[0].address)).to.eq("100000000"); - }); - }); - - describe("when cdr > 1", async function () { - beforeEach(async function () { - await rebase(collateralToken, rebaseOracle, 0.1); - }); - it("should return the price", async function () { - expect(await pricingStrategy.computeTranchePrice(tranches[0].address)).to.eq("100000000"); - }); - }); - - describe("when cdr < 1", async function () { - beforeEach(async function () { - await rebase(collateralToken, rebaseOracle, -0.1); - }); - it("should return the price", async function () { - expect(await pricingStrategy.computeTranchePrice(tranches[0].address)).to.eq("100000000"); - }); - }); - }); - }); - }); - - describe("computeMatureTranchePrice", function () { - describe("when debt is zero", async function () { - it("should return the price", async function () { - expect( - await pricingStrategy.computeMatureTranchePrice( - collateralToken.address, - toFixedPtAmt("100"), - toFixedPtAmt("0"), - ), - ).to.eq("100000000"); - }); - }); - - describe("when cdr = 1", async function () { - it("should return the price", async function () { - expect( - await pricingStrategy.computeMatureTranchePrice( - collateralToken.address, - toFixedPtAmt("100"), - toFixedPtAmt("100"), - ), - ).to.eq("100000000"); - }); - }); - - describe("when cdr > 1", async function () { - it("should return the price", async function () { - expect( - await pricingStrategy.computeMatureTranchePrice( - collateralToken.address, - toFixedPtAmt("110"), - toFixedPtAmt("100"), - ), - ).to.eq("110000000"); - }); - }); - - describe("when cdr < 1", async function () { - it("should return the price", async function () { - expect( - await pricingStrategy.computeMatureTranchePrice( - collateralToken.address, - toFixedPtAmt("90"), - toFixedPtAmt("100"), - ), - ).to.eq("100000000"); - }); - }); - }); -}); diff --git a/spot-contracts/test/strategies/TrancheClassDiscountStrategy.ts b/spot-contracts/test/strategies/TrancheClassDiscountStrategy.ts deleted file mode 100644 index a9145cac..00000000 --- a/spot-contracts/test/strategies/TrancheClassDiscountStrategy.ts +++ /dev/null @@ -1,200 +0,0 @@ -import { expect } from "chai"; -import { ethers } from "hardhat"; -import { Signer, Contract, constants, Transaction } from "ethers"; - -import { - setupCollateralToken, - setupBondFactory, - createBondWithFactory, - depositIntoBond, - getTranches, - toFixedPtAmt, - toDiscountFixedPtAmt, -} from "../helpers"; - -let discountStrategy: Contract, deployer: Signer, otherUser: Signer; - -describe("TrancheClassDiscountStrategy", function () { - beforeEach(async function () { - const accounts = await ethers.getSigners(); - deployer = accounts[0]; - otherUser = accounts[1]; - - const TrancheClassDiscountStrategy = await ethers.getContractFactory("TrancheClassDiscountStrategy"); - discountStrategy = await TrancheClassDiscountStrategy.deploy(); - await discountStrategy.init(); - }); - - describe("decimals", function () { - it("should be set", async function () { - expect(await discountStrategy.decimals()).to.eq(18); - }); - }); - - describe("#updateDefinedDiscount", function () { - let tx: Transaction, tranche: Contract, classHash: string; - - describe("when triggered by non-owner", function () { - it("should revert", async function () { - await expect( - discountStrategy.connect(otherUser).updateDefinedDiscount(constants.HashZero, 0), - ).to.be.revertedWith("Ownable: caller is not the owner"); - }); - }); - - describe("when triggered by owner", function () { - beforeEach(async function () { - const bondFactory = await setupBondFactory(); - const { collateralToken } = await setupCollateralToken("Bitcoin", "BTC"); - const bond = await createBondWithFactory(bondFactory, collateralToken, [1000], 86400); - const tranches = await getTranches(bond); - tranche = tranches[0]; - - classHash = await discountStrategy.trancheClass(tranche.address); - tx = discountStrategy.updateDefinedDiscount(classHash, toDiscountFixedPtAmt("1")); - await tx; - }); - it("should update reference", async function () { - expect(await discountStrategy.computeTrancheDiscount(tranche.address)).to.eq(toDiscountFixedPtAmt("1")); - }); - it("should emit event", async function () { - await expect(tx) - .to.emit(discountStrategy, "UpdatedDefinedTrancheDiscounts") - .withArgs(classHash, toDiscountFixedPtAmt("1")); - }); - it("should delete discount when set to zero", async function () { - await discountStrategy.updateDefinedDiscount(classHash, "0"); - expect(await discountStrategy.computeTrancheDiscount(tranche.address)).to.eq("0"); - }); - }); - }); - - describe("#trancheClass", function () { - let bondFactory: Contract, collateralToken: Contract, tranches: Contract[]; - beforeEach(async function () { - bondFactory = await setupBondFactory(); - ({ collateralToken } = await setupCollateralToken("Bitcoin", "BTC")); - const bond = await createBondWithFactory(bondFactory, collateralToken, [200, 300, 500], 3600); - tranches = await getTranches(bond); - }); - describe("given a tranche", function () { - it("should compute the tranche hash", async function () { - const types = ["address", "uint256[]", "uint256"]; - const abiCoder = ethers.utils.defaultAbiCoder; - const c0 = await ethers.utils.keccak256(abiCoder.encode(types, [collateralToken.address, [200, 300, 500], 0])); - const c1 = await ethers.utils.keccak256(abiCoder.encode(types, [collateralToken.address, [200, 300, 500], 1])); - const c2 = await ethers.utils.keccak256(abiCoder.encode(types, [collateralToken.address, [200, 300, 500], 2])); - expect(await discountStrategy.trancheClass(tranches[0].address)).to.eq(c0); - expect(await discountStrategy.trancheClass(tranches[1].address)).to.eq(c1); - expect(await discountStrategy.trancheClass(tranches[2].address)).to.eq(c2); - }); - }); - - describe("when 2 tranches from same class", function () { - let tranchesOther: Contract[]; - beforeEach(async function () { - const bondOther = await createBondWithFactory(bondFactory, collateralToken, [200, 300, 500], 3600); - tranchesOther = await getTranches(bondOther); - }); - it("should have the same class hash", async function () { - expect(await discountStrategy.trancheClass(tranches[0].address)).to.eq( - await discountStrategy.trancheClass(tranchesOther[0].address), - ); - expect(await discountStrategy.trancheClass(tranches[1].address)).to.eq( - await discountStrategy.trancheClass(tranchesOther[1].address), - ); - expect(await discountStrategy.trancheClass(tranches[2].address)).to.eq( - await discountStrategy.trancheClass(tranchesOther[2].address), - ); - }); - }); - - describe("when 2 tranches from different classes", function () { - let tranchesOther: Contract[]; - beforeEach(async function () { - const bondOther = await createBondWithFactory(bondFactory, collateralToken, [201, 300, 499], 3600); - tranchesOther = await getTranches(bondOther); - }); - it("should NOT have the same class hash", async function () { - expect(await discountStrategy.trancheClass(tranches[0].address)).not.to.eq( - await discountStrategy.trancheClass(tranchesOther[0].address), - ); - expect(await discountStrategy.trancheClass(tranches[1].address)).not.to.eq( - await discountStrategy.trancheClass(tranchesOther[1].address), - ); - expect(await discountStrategy.trancheClass(tranches[2].address)).not.to.eq( - await discountStrategy.trancheClass(tranchesOther[2].address), - ); - }); - }); - }); - - describe("#computeTrancheDiscount", function () { - let bondFactory: Contract, collateralToken: Contract, bond: Contract, tranches: Contract[]; - beforeEach(async function () { - bondFactory = await setupBondFactory(); - ({ collateralToken } = await setupCollateralToken("Bitcoin", "BTC")); - - bond = await createBondWithFactory(bondFactory, collateralToken, [200, 300, 500], 3600); - tranches = await getTranches(bond); - - await discountStrategy.updateDefinedDiscount( - await discountStrategy.trancheClass(tranches[0].address), - toDiscountFixedPtAmt("1"), - ); - }); - - describe("when tranche instance is not in the system", function () { - it("should return defined discount", async function () { - expect(await discountStrategy.computeTrancheDiscount(tranches[0].address)).to.eq(toDiscountFixedPtAmt("1")); - }); - describe("when not defined", function () { - it("should return 0", async function () { - expect(await discountStrategy.computeTrancheDiscount(tranches[1].address)).to.eq(toDiscountFixedPtAmt("0")); - expect(await discountStrategy.computeTrancheDiscount(tranches[2].address)).to.eq(toDiscountFixedPtAmt("0")); - }); - }); - describe("when updated", function () { - beforeEach(async function () { - await discountStrategy.updateDefinedDiscount( - await discountStrategy.trancheClass(tranches[0].address), - toDiscountFixedPtAmt("0.5"), - ); - }); - it("should return defined discount", async function () { - expect(await discountStrategy.computeTrancheDiscount(tranches[0].address)).to.eq(toDiscountFixedPtAmt("0.5")); - }); - }); - }); - - describe("when a new tranche instance enters the system", function () { - let tranchesNext: Contract[]; - beforeEach(async function () { - await depositIntoBond(bond, toFixedPtAmt("1000"), deployer); - await tranches[0].approve(discountStrategy.address, toFixedPtAmt("200")); - - const bondNext = await createBondWithFactory(bondFactory, collateralToken, [200, 300, 500], 3600); - tranchesNext = await getTranches(bondNext); - }); - it("should return defined discount", async function () { - expect(await discountStrategy.computeTrancheDiscount(tranchesNext[0].address)).to.eq(toDiscountFixedPtAmt("1")); - }); - describe("when updated", function () { - beforeEach(async function () { - await discountStrategy.updateDefinedDiscount( - await discountStrategy.trancheClass(tranches[0].address), - toDiscountFixedPtAmt("0.5"), - ); - }); - it("should return the updated discount", async function () { - expect(await discountStrategy.computeTrancheDiscount(tranchesNext[0].address)).to.eq( - toDiscountFixedPtAmt("0.5"), - ); - }); - it("should return the updated discount", async function () { - expect(await discountStrategy.computeTrancheDiscount(tranches[0].address)).to.eq(toDiscountFixedPtAmt("0.5")); - }); - }); - }); - }); -}); diff --git a/spot-contracts/test/strategies/UnitPricingStrategy.ts b/spot-contracts/test/strategies/UnitPricingStrategy.ts deleted file mode 100644 index 67364b23..00000000 --- a/spot-contracts/test/strategies/UnitPricingStrategy.ts +++ /dev/null @@ -1,30 +0,0 @@ -import { expect } from "chai"; -import { ethers } from "hardhat"; -import { Contract, constants } from "ethers"; - -let pricingStrategy: Contract; - -describe("UnitPricingStrategy", function () { - beforeEach(async () => { - const UnitPricingStrategy = await ethers.getContractFactory("UnitPricingStrategy"); - pricingStrategy = await UnitPricingStrategy.deploy(); - }); - - describe("decimals", function () { - it("should be set", async function () { - expect(await pricingStrategy.decimals()).to.eq(8); - }); - }); - - describe("computeTranchePrice", function () { - it("should be return one", async function () { - expect(await pricingStrategy.computeTranchePrice(constants.AddressZero)).to.eq("100000000"); - }); - }); - - describe("computeMatureTranchePrice", function () { - it("should be return one", async function () { - expect(await pricingStrategy.computeMatureTranchePrice(constants.AddressZero, 0, 0)).to.eq("100000000"); - }); - }); -}); diff --git a/spot-contracts/test/vaults/RolloverVault_deploy.ts b/spot-contracts/test/vaults/RolloverVault_deploy.ts deleted file mode 100644 index 063d3250..00000000 --- a/spot-contracts/test/vaults/RolloverVault_deploy.ts +++ /dev/null @@ -1,1113 +0,0 @@ -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, "InsufficientDeployment"); - }); - }); - - describe("when usable balance is lower than the min deployment", function () { - beforeEach(async function () { - await collateralToken.transfer(vault.address, toFixedPtAmt("999")); - await vault.updateMinDeploymentAmt(toFixedPtAmt("1000")); - }); - it("should revert", async function () { - await expect(vault.deploy()).to.be.revertedWithCustomError(vault, "InsufficientDeployment"); - }); - }); - - describe("when usable balance is higher than the min deployment", function () { - beforeEach(async function () { - await collateralToken.transfer(vault.address, toFixedPtAmt("1000")); - await vault.updateMinDeploymentAmt(toFixedPtAmt("100")); - }); - it("should not revert", async function () { - await expect(vault.deploy()).not.to.be.reverted; - }); - }); - - 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, "InsufficientDeployment"); - }); - }); - - 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")); - await expect(tx).to.emit(vault, "AssetSynced").withArgs(collateralToken.address, toFixedPtAmt("0")); - - // Rollover - await expect(tx).to.emit(vault, "AssetSynced").withArgs(rolloverInTranches[0].address, toFixedPtAmt("0")); - await expect(tx) - .to.emit(vault, "AssetSynced") - .withArgs(rolloverInTranches[1].address, toFixedPtAmt("183.333333333333333334")); - 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")); - await expect(tx).to.emit(vault, "AssetSynced").withArgs(collateralToken.address, toFixedPtAmt("500")); - - // 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); - }); - }); - }); - - describe("deploy limit", function () { - async function setupDeployment() { - const curBondIn = await bondAt(await perp.callStatic.getDepositBond()); - await advancePerpQueueToRollover(perp, curBondIn); - const newBondIn = await bondAt(await perp.callStatic.getDepositBond()); - const 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("10")); - } - - beforeEach(async function () { - for (let i = 0; i < 23; i++) { - await setupDeployment(); - await vault.deploy(); - } - }); - - it("should revert after limit is reached", async function () { - expect(await vault.deployedCount()).to.eq(46); - await setupDeployment(); - await expect(vault.deploy()).to.be.revertedWith("DeployedCountOverLimit"); - }); - it("redemption should be within gas limit", async function () { - await collateralToken.approve(vault.address, toFixedPtAmt("10")); - await vault.deposit(toFixedPtAmt("10")); - await expect(vault.redeem(await vault.balanceOf(await deployer.getAddress()))).not.to.be.reverted; - }); - - it("recovery should be within gas limit", async function () { - await expect(vault["recover()"]()).not.to.be.reverted; - }); - }); -}); diff --git a/spot-contracts/test/vaults/RolloverVault_deposit_redeem.ts b/spot-contracts/test/vaults/RolloverVault_deposit_redeem.ts deleted file mode 100644 index 10dfdae4..00000000 --- a/spot-contracts/test/vaults/RolloverVault_deposit_redeem.ts +++ /dev/null @@ -1,741 +0,0 @@ -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("get asset value", function () { - describe("when vault is empty", function () { - it("should return 0 vaule", async function () { - expect(await vault.callStatic.getTVL()).to.eq(0); - expect(await vault.callStatic.getVaultAssetValue(collateralToken.address)).to.eq(0); - expect(await vault.callStatic.getVaultAssetValue(perp.address)).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")); - }); - it("should return asset value", async function () { - expect(await vault.callStatic.getVaultAssetValue(collateralToken.address)).to.eq(toFixedPtAmt("100")); - expect(await vault.callStatic.getVaultAssetValue(perp.address)).to.eq(0); - }); - }); - - 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")); - }); - it("should return asset value", async function () { - expect(await vault.callStatic.getVaultAssetValue(collateralToken.address)).to.eq(0); - expect(await vault.callStatic.getVaultAssetValue(reserveTranches[0].address)).to.eq(toFixedPtAmt("100")); - expect(await vault.callStatic.getVaultAssetValue(perp.address)).to.eq(0); - }); - }); - - 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")); - }); - it("should return asset value", async function () { - expect(await vault.callStatic.getVaultAssetValue(collateralToken.address)).to.eq(0); - expect(await vault.callStatic.getVaultAssetValue(perp.address)).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")); - }); - it("should return asset value", async function () { - expect(await vault.callStatic.getVaultAssetValue(collateralToken.address)).to.eq(toFixedPtAmt("100")); - expect(await vault.callStatic.getVaultAssetValue(reserveTranches[0].address)).to.eq(toFixedPtAmt("200")); - expect(await vault.callStatic.getVaultAssetValue(reserveTranches[1].address)).to.eq(toFixedPtAmt("200")); - expect(await vault.callStatic.getVaultAssetValue(rolloverInTranches[1].address)).to.eq(toFixedPtAmt("600")); - expect(await vault.callStatic.getVaultAssetValue(rolloverInTranches[2].address)).to.eq(toFixedPtAmt("1000")); - expect(await vault.callStatic.getVaultAssetValue(perp.address)).to.eq(toFixedPtAmt("100")); - }); - }); - - 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")); - }); - it("should return asset value", async function () { - expect(await vault.callStatic.getVaultAssetValue(collateralToken.address)).to.eq(toFixedPtAmt("110")); - expect(await vault.callStatic.getVaultAssetValue(reserveTranches[0].address)).to.eq(toFixedPtAmt("200")); - expect(await vault.callStatic.getVaultAssetValue(reserveTranches[1].address)).to.eq(toFixedPtAmt("200")); - expect(await vault.callStatic.getVaultAssetValue(rolloverInTranches[1].address)).to.eq(toFixedPtAmt("600")); - expect(await vault.callStatic.getVaultAssetValue(rolloverInTranches[2].address)).to.eq(toFixedPtAmt("1200")); - expect(await vault.callStatic.getVaultAssetValue(perp.address)).to.eq(toFixedPtAmt("100")); - }); - }); - - 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")); - }); - it("should return asset value", async function () { - expect(await vault.callStatic.getVaultAssetValue(collateralToken.address)).to.eq(toFixedPtAmt("90")); - expect(await vault.callStatic.getVaultAssetValue(reserveTranches[0].address)).to.eq(toFixedPtAmt("200")); - expect(await vault.callStatic.getVaultAssetValue(reserveTranches[1].address)).to.eq(toFixedPtAmt("200")); - expect(await vault.callStatic.getVaultAssetValue(rolloverInTranches[1].address)).to.eq(toFixedPtAmt("600")); - expect(await vault.callStatic.getVaultAssetValue(rolloverInTranches[2].address)).to.eq(toFixedPtAmt("800")); - expect(await vault.callStatic.getVaultAssetValue(perp.address)).to.eq(toFixedPtAmt("100")); - }); - }); - - 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")); - }); - - it("should return asset value", async function () { - expect(await vault.callStatic.getVaultAssetValue(collateralToken.address)).to.eq(toFixedPtAmt("10")); - expect(await vault.callStatic.getVaultAssetValue(reserveTranches[0].address)).to.eq(toFixedPtAmt("100")); - expect(await vault.callStatic.getVaultAssetValue(reserveTranches[1].address)).to.eq(toFixedPtAmt("0")); - expect(await vault.callStatic.getVaultAssetValue(reserveTranches[2].address)).to.eq(toFixedPtAmt("0")); - expect(await vault.callStatic.getVaultAssetValue(rolloverInTranches[0].address)).to.eq(toFixedPtAmt("250")); - expect(await vault.callStatic.getVaultAssetValue(rolloverInTranches[1].address)).to.eq(toFixedPtAmt("0")); - expect(await vault.callStatic.getVaultAssetValue(rolloverInTranches[2].address)).to.eq(toFixedPtAmt("0")); - expect(await vault.callStatic.getVaultAssetValue(perp.address)).to.eq(toFixedPtAmt("30")); - }); - }); - }); - - 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 deleted file mode 100644 index a33f32e5..00000000 --- a/spot-contracts/test/vaults/RolloverVault_recover.ts +++ /dev/null @@ -1,730 +0,0 @@ -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, - createBondWithFactory, - setupBondFactory, - depositIntoBond, - bondAt, - getTranches, - toFixedPtAmt, - toDiscountFixedPtAmt, - toPriceFixedPtAmt, - getDepositBond, - advancePerpQueue, - advancePerpQueueUpToBondMaturity, - 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("#recover(address)", function () { - describe("when no asset is deployed", function () { - it("should revert", async function () { - await vault["recover()"](); - await expect(vault["recover(address)"](collateralToken.address)).to.be.revertedWith("UnexpectedAsset"); - 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 address is not valid", function () { - it("should be reverted", async function () { - await expect(vault["recover(address)"](collateralToken.address)).to.be.revertedWith("UnexpectedAsset"); - }); - }); - describe("when belongs to a malicious tranche", function () { - it("should be reverted", async function () { - const maliciousBond = await createBondWithFactory(bondFactory, collateralToken, [1, 999], 100000000000); - await collateralToken.approve(maliciousBond.address, toFixedPtAmt("1")); - await maliciousBond.deposit(toFixedPtAmt("1")); - const maliciousTranches = await getTranches(maliciousBond); - await maliciousTranches[1].transfer( - vault.address, - maliciousTranches[1].balanceOf(await deployer.getAddress()), - ); - await expect(vault["recover(address)"](maliciousTranches[1].address)).to.be.revertedWith("UnexpectedAsset"); - }); - }); - describe("when its not mature", function () { - it("should be a no-op", async function () { - await expect(vault["recover(address)"](currentTranchesIn[2].address)).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 advancePerpQueueUpToBondMaturity(perp, currentBondIn); - }); - it("should recover", async function () { - await expect(vault["recover(address)"](currentTranchesIn[2].address)).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(address)"](currentTranchesIn[1].address)).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(address)"](currentTranchesIn[1].address)).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); - }); - it("should sync assets", async function () { - const tx = vault["recover(address)"](currentTranchesIn[1].address); - await expect(tx).to.emit(vault, "AssetSynced").withArgs(collateralToken.address, toFixedPtAmt("5")); - await expect(tx).to.emit(vault, "AssetSynced").withArgs(currentTranchesIn[1].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(address)"](currentTranchesIn[1].address)).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(address)"](currentTranchesIn[0].address)).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 3f9efdf2..b576ca51 100644 --- a/yarn.lock +++ b/yarn.lock @@ -5,17 +5,26 @@ __metadata: version: 6 cacheKey: 8 +"@aashutoshrathi/word-wrap@npm:^1.2.3": + version: 1.2.6 + resolution: "@aashutoshrathi/word-wrap@npm:1.2.6" + checksum: ada901b9e7c680d190f1d012c84217ce0063d8f5c5a7725bb91ec3c5ed99bb7572680eb2d2938a531ccbaec39a95422fcd8a6b4a13110c7d98dd75402f66a0cd + languageName: node + linkType: hard + "@ampleforthorg/spot-contracts@workspace:spot-contracts": 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/abstract-provider": ^5.7.0 + "@ethersproject/abstract-signer": ^5.7.0 "@ethersproject/bytes": ^5.6.1 "@ethersproject/providers": ^5.6.8 "@nomicfoundation/hardhat-chai-matchers": ^1.0.6 + "@nomicfoundation/hardhat-verify": ^1.1.0 "@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 @@ -25,6 +34,7 @@ __metadata: "@types/chai": ^4.3.1 "@types/mocha": ^9.1.1 "@types/node": ^18.6.1 + "@types/sinon-chai": ^3.2.12 "@typescript-eslint/eslint-plugin": ^5.0.0 "@typescript-eslint/parser": ^5.0.0 chai: ^4.3.6 @@ -38,16 +48,17 @@ __metadata: eslint-plugin-node: ^11.1.0 eslint-plugin-prettier: ^4.2.1 eslint-plugin-promise: ^6.0.0 + eslint-plugin-unused-imports: ^3.0.0 ethereum-waffle: ^3.4.4 ethers: ^5.6.9 ganache-cli: ^6.12.2 - hardhat: ^2.12.6 + hardhat: ^2.19.4 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.8.2 + solidity-coverage: ^0.8.5 ts-node: ^10.9.1 typechain: ^8.1.0 typescript: ^4.7.4 @@ -76,30 +87,118 @@ __metadata: languageName: unknown linkType: soft +"@aws-crypto/sha256-js@npm:1.2.2": + version: 1.2.2 + resolution: "@aws-crypto/sha256-js@npm:1.2.2" + dependencies: + "@aws-crypto/util": ^1.2.2 + "@aws-sdk/types": ^3.1.0 + tslib: ^1.11.1 + checksum: b6aeb71f88ecc219c5473803345bb15150ecd056a337582638dd60fb2344e0ff63908c684ef55268b249290fe0776e8e6fc830605f0aad850ff325b9cfe0dc6a + languageName: node + linkType: hard + +"@aws-crypto/util@npm:^1.2.2": + version: 1.2.2 + resolution: "@aws-crypto/util@npm:1.2.2" + dependencies: + "@aws-sdk/types": ^3.1.0 + "@aws-sdk/util-utf8-browser": ^3.0.0 + tslib: ^1.11.1 + checksum: 54d72ce4945b52f3fcbcb62574a55bc038cc3ff165742f340cabca1bdc979faf69c97709cf56daf434e4ad69e33582a04a64da33b4e4e13b25c6ff67f8abe5ae + languageName: node + linkType: hard + +"@aws-sdk/types@npm:^3.1.0": + version: 3.502.0 + resolution: "@aws-sdk/types@npm:3.502.0" + dependencies: + "@smithy/types": ^2.9.1 + tslib: ^2.5.0 + checksum: 11dddc2c1fb7b601adba1df6339b68367a3c77fcdd298c7ceb42541c813ba777ffc9ddfbb68633f9fd9082c883edf1b2fe3139acdf822d7d423c0b5f76ce78dd + languageName: node + linkType: hard + +"@aws-sdk/util-utf8-browser@npm:^3.0.0": + version: 3.259.0 + resolution: "@aws-sdk/util-utf8-browser@npm:3.259.0" + dependencies: + tslib: ^2.3.1 + checksum: b6a1e580da1c9b62c749814182a7649a748ca4253edb4063aa521df97d25b76eae3359eb1680b86f71aac668e05cc05c514379bca39ebf4ba998ae4348412da8 + languageName: node + linkType: hard + "@babel/code-frame@npm:^7.0.0": - version: 7.18.6 - resolution: "@babel/code-frame@npm:7.18.6" + version: 7.23.5 + resolution: "@babel/code-frame@npm:7.23.5" dependencies: - "@babel/highlight": ^7.18.6 - checksum: 195e2be3172d7684bf95cff69ae3b7a15a9841ea9d27d3c843662d50cdd7d6470fd9c8e64be84d031117e4a4083486effba39f9aef6bbb2c89f7f21bcfba33ba + "@babel/highlight": ^7.23.4 + chalk: ^2.4.2 + checksum: d90981fdf56a2824a9b14d19a4c0e8db93633fd488c772624b4e83e0ceac6039a27cd298a247c3214faa952bf803ba23696172ae7e7235f3b97f43ba278c569a languageName: node linkType: hard -"@babel/helper-validator-identifier@npm:^7.18.6": - version: 7.18.6 - resolution: "@babel/helper-validator-identifier@npm:7.18.6" - checksum: e295254d616bbe26e48c196a198476ab4d42a73b90478c9842536cf910ead887f5af6b5c4df544d3052a25ccb3614866fa808dc1e3a5a4291acd444e243c0648 +"@babel/helper-validator-identifier@npm:^7.22.20": + version: 7.22.20 + resolution: "@babel/helper-validator-identifier@npm:7.22.20" + checksum: 136412784d9428266bcdd4d91c32bcf9ff0e8d25534a9d94b044f77fe76bc50f941a90319b05aafd1ec04f7d127cd57a179a3716009ff7f3412ef835ada95bdc languageName: node linkType: hard -"@babel/highlight@npm:^7.18.6": - version: 7.18.6 - resolution: "@babel/highlight@npm:7.18.6" +"@babel/highlight@npm:^7.23.4": + version: 7.23.4 + resolution: "@babel/highlight@npm:7.23.4" dependencies: - "@babel/helper-validator-identifier": ^7.18.6 - chalk: ^2.0.0 + "@babel/helper-validator-identifier": ^7.22.20 + chalk: ^2.4.2 js-tokens: ^4.0.0 - checksum: 92d8ee61549de5ff5120e945e774728e5ccd57fd3b2ed6eace020ec744823d4a98e242be1453d21764a30a14769ecd62170fba28539b211799bbaf232bbb2789 + checksum: 643acecdc235f87d925979a979b539a5d7d1f31ae7db8d89047269082694122d11aa85351304c9c978ceeb6d250591ccadb06c366f358ccee08bb9c122476b89 + languageName: node + linkType: hard + +"@chainsafe/as-sha256@npm:^0.3.1": + version: 0.3.1 + resolution: "@chainsafe/as-sha256@npm:0.3.1" + checksum: 58ea733be1657b0e31dbf48b0dba862da0833df34a81c1460c7352f04ce90874f70003cbf34d0afb9e5e53a33ee2d63a261a8b12462be85b2ba0a6f7f13d6150 + languageName: node + linkType: hard + +"@chainsafe/persistent-merkle-tree@npm:^0.4.2": + version: 0.4.2 + resolution: "@chainsafe/persistent-merkle-tree@npm:0.4.2" + dependencies: + "@chainsafe/as-sha256": ^0.3.1 + checksum: f9cfcb2132a243992709715dbd28186ab48c7c0c696f29d30857693cca5526bf753974a505ef68ffd5623bbdbcaa10f9083f4dd40bf99eb6408e451cc26a1a9e + languageName: node + linkType: hard + +"@chainsafe/persistent-merkle-tree@npm:^0.5.0": + version: 0.5.0 + resolution: "@chainsafe/persistent-merkle-tree@npm:0.5.0" + dependencies: + "@chainsafe/as-sha256": ^0.3.1 + checksum: 2c67203da776c79cd3a6132e2d672fe132393b2e63dc71604e3134acc8c0ec25cc5e431051545939ea0f7c5ff2066fb806b9e5cab974ca085d046226a1671f7d + languageName: node + linkType: hard + +"@chainsafe/ssz@npm:^0.10.0": + version: 0.10.2 + resolution: "@chainsafe/ssz@npm:0.10.2" + dependencies: + "@chainsafe/as-sha256": ^0.3.1 + "@chainsafe/persistent-merkle-tree": ^0.5.0 + checksum: 6bb70cf741d0a19dd0b28b3f6f067b96fa39f556e2eefa6ac745b21db9c3b3a8393dc3cca8ff4a6ce065ed71ddc3fb1b2b390a92004b9d01067c26e2558e5503 + languageName: node + linkType: hard + +"@chainsafe/ssz@npm:^0.9.2": + version: 0.9.4 + resolution: "@chainsafe/ssz@npm:0.9.4" + dependencies: + "@chainsafe/as-sha256": ^0.3.1 + "@chainsafe/persistent-merkle-tree": ^0.4.2 + case: ^1.6.3 + checksum: c6eaedeae9e5618b3c666ff4507a27647f665a8dcf17d5ca86da4ed4788c5a93868f256d0005467d184fdf35ec03f323517ec2e55ec42492d769540a2ec396bc languageName: node linkType: hard @@ -113,8 +212,8 @@ __metadata: linkType: hard "@defi-wonderland/smock@npm:^2.3.4": - version: 2.3.4 - resolution: "@defi-wonderland/smock@npm:2.3.4" + version: 2.3.5 + resolution: "@defi-wonderland/smock@npm:2.3.5" dependencies: "@nomicfoundation/ethereumjs-evm": ^1.0.0-rc.3 "@nomicfoundation/ethereumjs-util": ^8.0.0-rc.3 @@ -131,7 +230,7 @@ __metadata: "@nomiclabs/hardhat-ethers": ^2 ethers: ^5 hardhat: ^2 - checksum: 316026d672364a02c5d83c15110b2d5df4358768a6f645e9fd0786fbb230c0c7983a39b928521ee7d0b917a478e07c454b7d7bdf22ff10ed140f520340c28267 + checksum: b3c408fb43cd7b02bf6f3b3a392758944ee4d4ad9d92a5bcb595b2bdf7ebe702d052b8631afba0b408e80185b1db22d655dc63feba82365f5f1f6786eb98d859 languageName: node linkType: hard @@ -155,20 +254,45 @@ __metadata: languageName: node linkType: hard -"@eslint/eslintrc@npm:^1.3.0": - version: 1.3.0 - resolution: "@eslint/eslintrc@npm:1.3.0" +"@eslint-community/eslint-utils@npm:^4.2.0": + version: 4.4.0 + resolution: "@eslint-community/eslint-utils@npm:4.4.0" + dependencies: + eslint-visitor-keys: ^3.3.0 + peerDependencies: + eslint: ^6.0.0 || ^7.0.0 || >=8.0.0 + checksum: cdfe3ae42b4f572cbfb46d20edafe6f36fc5fb52bf2d90875c58aefe226892b9677fef60820e2832caf864a326fe4fc225714c46e8389ccca04d5f9288aabd22 + languageName: node + linkType: hard + +"@eslint-community/regexpp@npm:^4.4.0, @eslint-community/regexpp@npm:^4.6.1": + version: 4.10.0 + resolution: "@eslint-community/regexpp@npm:4.10.0" + checksum: 2a6e345429ea8382aaaf3a61f865cae16ed44d31ca917910033c02dc00d505d939f10b81e079fa14d43b51499c640138e153b7e40743c4c094d9df97d4e56f7b + languageName: node + linkType: hard + +"@eslint/eslintrc@npm:^2.1.4": + version: 2.1.4 + resolution: "@eslint/eslintrc@npm:2.1.4" dependencies: ajv: ^6.12.4 debug: ^4.3.2 - espree: ^9.3.2 - globals: ^13.15.0 + espree: ^9.6.0 + globals: ^13.19.0 ignore: ^5.2.0 import-fresh: ^3.2.1 js-yaml: ^4.1.0 minimatch: ^3.1.2 strip-json-comments: ^3.1.1 - checksum: a1e734ad31a8b5328dce9f479f185fd4fc83dd7f06c538e1fa457fd8226b89602a55cc6458cd52b29573b01cdfaf42331be8cfc1fec732570086b591f4ed6515 + checksum: 10957c7592b20ca0089262d8c2a8accbad14b4f6507e35416c32ee6b4dbf9cad67dfb77096bbd405405e9ada2b107f3797fe94362e1c55e0b09d6e90dd149127 + languageName: node + linkType: hard + +"@eslint/js@npm:8.56.0": + version: 8.56.0 + resolution: "@eslint/js@npm:8.56.0" + checksum: 5804130574ef810207bdf321c265437814e7a26f4e6fac9b496de3206afd52f533e09ec002a3be06cd9adcc9da63e727f1883938e663c4e4751c007d5b58e539 languageName: node linkType: hard @@ -235,6 +359,26 @@ __metadata: languageName: node linkType: hard +"@ethereumjs/rlp@npm:^4.0.1": + version: 4.0.1 + resolution: "@ethereumjs/rlp@npm:4.0.1" + bin: + rlp: bin/rlp + checksum: 30db19c78faa2b6ff27275ab767646929207bb207f903f09eb3e4c273ce2738b45f3c82169ddacd67468b4f063d8d96035f2bf36f02b6b7e4d928eefe2e3ecbc + languageName: node + linkType: hard + +"@ethereumjs/util@npm:^8.1.0": + version: 8.1.0 + resolution: "@ethereumjs/util@npm:8.1.0" + dependencies: + "@ethereumjs/rlp": ^4.0.1 + ethereum-cryptography: ^2.0.0 + micro-ftch: ^0.3.1 + checksum: 9ae5dee8f12b0faf81cd83f06a41560e79b0ba96a48262771d897a510ecae605eb6d84f687da001ab8ccffd50f612ae50f988ef76e6312c752897f462f3ac08d + 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" @@ -252,24 +396,7 @@ __metadata: 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" - dependencies: - "@ethersproject/address": ^5.6.1 - "@ethersproject/bignumber": ^5.6.2 - "@ethersproject/bytes": ^5.6.1 - "@ethersproject/constants": ^5.6.1 - "@ethersproject/hash": ^5.6.1 - "@ethersproject/keccak256": ^5.6.1 - "@ethersproject/logger": ^5.6.0 - "@ethersproject/properties": ^5.6.0 - "@ethersproject/strings": ^5.6.1 - checksum: b5e70fa13a29e1143131a0ed25053a3d355c07353e13d436f42add33f40753b5541a088cf31a1ccca6448bb1d773a41ece0bf8367490d3f2ad394a4c26f4876f - languageName: node - linkType: hard - -"@ethersproject/abi@npm:^5.0.9": +"@ethersproject/abi@npm:5.7.0, @ethersproject/abi@npm:^5.0.9, @ethersproject/abi@npm:^5.1.2, @ethersproject/abi@npm:^5.5.0, @ethersproject/abi@npm:^5.6.3, @ethersproject/abi@npm:^5.6.4, @ethersproject/abi@npm:^5.7.0": version: 5.7.0 resolution: "@ethersproject/abi@npm:5.7.0" dependencies: @@ -286,22 +413,7 @@ __metadata: 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" - dependencies: - "@ethersproject/bignumber": ^5.6.2 - "@ethersproject/bytes": ^5.6.1 - "@ethersproject/logger": ^5.6.0 - "@ethersproject/networks": ^5.6.3 - "@ethersproject/properties": ^5.6.0 - "@ethersproject/transactions": ^5.6.2 - "@ethersproject/web": ^5.6.1 - checksum: a1be8035d9e67fd41a336e2d38f5cf03b7a2590243749b4cf807ad73906b5a298e177ebe291cb5b54262ded4825169bf82968e0e5b09fbea17444b903faeeab0 - languageName: node - linkType: hard - -"@ethersproject/abstract-provider@npm:^5.7.0": +"@ethersproject/abstract-provider@npm:5.7.0, @ethersproject/abstract-provider@npm:^5.7.0": version: 5.7.0 resolution: "@ethersproject/abstract-provider@npm:5.7.0" dependencies: @@ -316,20 +428,7 @@ __metadata: 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" - dependencies: - "@ethersproject/abstract-provider": ^5.6.1 - "@ethersproject/bignumber": ^5.6.2 - "@ethersproject/bytes": ^5.6.1 - "@ethersproject/logger": ^5.6.0 - "@ethersproject/properties": ^5.6.0 - checksum: 09f3dd1309b37bb3803057d618e4a831668e010e22047f52f1719f2b6f50b63805f1bec112b1603880d6c6b7d403ed187611ff1b14ae1f151141ede186a04996 - languageName: node - linkType: hard - -"@ethersproject/abstract-signer@npm:^5.7.0": +"@ethersproject/abstract-signer@npm:5.7.0, @ethersproject/abstract-signer@npm:^5.7.0": version: 5.7.0 resolution: "@ethersproject/abstract-signer@npm:5.7.0" dependencies: @@ -342,20 +441,7 @@ __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.6.1": - version: 5.6.1 - resolution: "@ethersproject/address@npm:5.6.1" - dependencies: - "@ethersproject/bignumber": ^5.6.2 - "@ethersproject/bytes": ^5.6.1 - "@ethersproject/keccak256": ^5.6.1 - "@ethersproject/logger": ^5.6.0 - "@ethersproject/rlp": ^5.6.1 - checksum: 262096ef05a1b626c161a72698a5d8b06aebf821fe01a1651ab40f80c29ca2481b96be7f972745785fd6399906509458c4c9a38f3bc1c1cb5afa7d2f76f7309a - languageName: node - linkType: hard - -"@ethersproject/address@npm:^5.7.0": +"@ethersproject/address@npm:5.7.0, @ethersproject/address@npm:>=5.0.0-beta.128, @ethersproject/address@npm:^5.0.2, @ethersproject/address@npm:^5.7.0": version: 5.7.0 resolution: "@ethersproject/address@npm:5.7.0" dependencies: @@ -368,16 +454,7 @@ __metadata: 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" - dependencies: - "@ethersproject/bytes": ^5.6.1 - checksum: d21c5c297e1b8bc48fe59012c0cd70a90df7772fac07d9cc3da499d71d174d9f48edfd83495d4a1496cb70e8d1b33fb5b549a9529c5c2f97bb3a07d3f33a3fe8 - languageName: node - linkType: hard - -"@ethersproject/base64@npm:^5.7.0": +"@ethersproject/base64@npm:5.7.0, @ethersproject/base64@npm:^5.7.0": version: 5.7.0 resolution: "@ethersproject/base64@npm:5.7.0" dependencies: @@ -386,28 +463,17 @@ __metadata: 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" - dependencies: - "@ethersproject/bytes": ^5.6.1 - "@ethersproject/properties": ^5.6.0 - checksum: a14b75d2c25d0ac00ce0098e5bd338d4cce7a68c583839b2bc4e3512ffcb14498b18cbcb4e05b695d216d2a23814d0c335385f35b3118735cc4895234db5ae1c - languageName: node - linkType: hard - -"@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" +"@ethersproject/basex@npm:5.7.0, @ethersproject/basex@npm:^5.7.0": + version: 5.7.0 + resolution: "@ethersproject/basex@npm:5.7.0" dependencies: - "@ethersproject/bytes": ^5.6.1 - "@ethersproject/logger": ^5.6.0 - bn.js: ^5.2.1 - checksum: 9cf31c10274f1b6d45b16aed29f43729e8f5edec38c8ec8bb90d6b44f0eae14fda6519536228d23916a375ce11e71a77279a912d653ea02503959910b6bf9de7 + "@ethersproject/bytes": ^5.7.0 + "@ethersproject/properties": ^5.7.0 + checksum: 326087b7e1f3787b5fe6cd1cf2b4b5abfafbc355a45e88e22e5e9d6c845b613ffc5301d629b28d5c4d5e2bfe9ec424e6782c804956dff79be05f0098cb5817de languageName: node linkType: hard -"@ethersproject/bignumber@npm:^5.7.0": +"@ethersproject/bignumber@npm:5.7.0, @ethersproject/bignumber@npm:>=5.0.0-beta.130, @ethersproject/bignumber@npm:^5.7.0": version: 5.7.0 resolution: "@ethersproject/bignumber@npm:5.7.0" dependencies: @@ -418,16 +484,7 @@ __metadata: 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: - "@ethersproject/logger": ^5.6.0 - checksum: d06ffe3bf12aa8a6588d99b82e40b46a2cbb8b057fc650aad836e3e8c95d4559773254eeeb8fed652066dcf8082e527e37cd2b9fff7ac8cabc4de7c49459a7eb - languageName: node - linkType: hard - -"@ethersproject/bytes@npm:^5.7.0": +"@ethersproject/bytes@npm:5.7.0, @ethersproject/bytes@npm:>=5.0.0-beta.129, @ethersproject/bytes@npm:^5.6.1, @ethersproject/bytes@npm:^5.7.0": version: 5.7.0 resolution: "@ethersproject/bytes@npm:5.7.0" dependencies: @@ -436,16 +493,7 @@ __metadata: 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: - "@ethersproject/bignumber": ^5.6.2 - checksum: 3c6abcee60f1620796dc40210a638b601ad8a2d3f6668a69c42a5ca361044f21296b16d1d43b8a00f7c28b385de4165983a8adf671e0983f5ef07459dfa84997 - languageName: node - linkType: hard - -"@ethersproject/constants@npm:^5.7.0": +"@ethersproject/constants@npm:5.7.0, @ethersproject/constants@npm:>=5.0.0-beta.128, @ethersproject/constants@npm:^5.7.0": version: 5.7.0 resolution: "@ethersproject/constants@npm:5.7.0" dependencies: @@ -454,41 +502,25 @@ __metadata: languageName: node linkType: hard -"@ethersproject/contracts@npm:5.6.2": - version: 5.6.2 - resolution: "@ethersproject/contracts@npm:5.6.2" - dependencies: - "@ethersproject/abi": ^5.6.3 - "@ethersproject/abstract-provider": ^5.6.1 - "@ethersproject/abstract-signer": ^5.6.2 - "@ethersproject/address": ^5.6.1 - "@ethersproject/bignumber": ^5.6.2 - "@ethersproject/bytes": ^5.6.1 - "@ethersproject/constants": ^5.6.1 - "@ethersproject/logger": ^5.6.0 - "@ethersproject/properties": ^5.6.0 - "@ethersproject/transactions": ^5.6.2 - checksum: c5a36ce3d0b88dc80db0135aaf39a71c0f14e262fd14172ae557d8943e69d3a2ba52c8f73f67639db0c235ea51155a97ff3584d431b92686f4c711b1004e6f87 - languageName: node - linkType: hard - -"@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" +"@ethersproject/contracts@npm:5.7.0": + version: 5.7.0 + resolution: "@ethersproject/contracts@npm:5.7.0" dependencies: - "@ethersproject/abstract-signer": ^5.6.2 - "@ethersproject/address": ^5.6.1 - "@ethersproject/bignumber": ^5.6.2 - "@ethersproject/bytes": ^5.6.1 - "@ethersproject/keccak256": ^5.6.1 - "@ethersproject/logger": ^5.6.0 - "@ethersproject/properties": ^5.6.0 - "@ethersproject/strings": ^5.6.1 - checksum: 1338b578a51bc5cb692c17b1cabc51e484e9e3e009c4ffec13032332fc7e746c115968de1c259133cdcdad55fa96c5c8a5144170190c62b968a3fedb5b1d2cdb + "@ethersproject/abi": ^5.7.0 + "@ethersproject/abstract-provider": ^5.7.0 + "@ethersproject/abstract-signer": ^5.7.0 + "@ethersproject/address": ^5.7.0 + "@ethersproject/bignumber": ^5.7.0 + "@ethersproject/bytes": ^5.7.0 + "@ethersproject/constants": ^5.7.0 + "@ethersproject/logger": ^5.7.0 + "@ethersproject/properties": ^5.7.0 + "@ethersproject/transactions": ^5.7.0 + checksum: 6ccf1121cba01b31e02f8c507cb971ab6bfed85706484a9ec09878ef1594a62215f43c4fdef8f4a4875b99c4a800bc95e3be69b1803f8ce479e07634b5a740c0 languageName: node linkType: hard -"@ethersproject/hash@npm:^5.7.0": +"@ethersproject/hash@npm:5.7.0, @ethersproject/hash@npm:>=5.0.0-beta.128, @ethersproject/hash@npm:^5.7.0": version: 5.7.0 resolution: "@ethersproject/hash@npm:5.7.0" dependencies: @@ -505,58 +537,48 @@ __metadata: 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" +"@ethersproject/hdnode@npm:5.7.0, @ethersproject/hdnode@npm:^5.7.0": + version: 5.7.0 + resolution: "@ethersproject/hdnode@npm:5.7.0" dependencies: - "@ethersproject/abstract-signer": ^5.6.2 - "@ethersproject/basex": ^5.6.1 - "@ethersproject/bignumber": ^5.6.2 - "@ethersproject/bytes": ^5.6.1 - "@ethersproject/logger": ^5.6.0 - "@ethersproject/pbkdf2": ^5.6.1 - "@ethersproject/properties": ^5.6.0 - "@ethersproject/sha2": ^5.6.1 - "@ethersproject/signing-key": ^5.6.2 - "@ethersproject/strings": ^5.6.1 - "@ethersproject/transactions": ^5.6.2 - "@ethersproject/wordlists": ^5.6.1 - checksum: b096882ac75d6738c085bf7cdaaf06b6b89055b8e98469df4abf00d600a6131299ec25ca3bc71986cc79d70ddf09ec00258e7ce7e94c45d5ffb83aa616eaaaae + "@ethersproject/abstract-signer": ^5.7.0 + "@ethersproject/basex": ^5.7.0 + "@ethersproject/bignumber": ^5.7.0 + "@ethersproject/bytes": ^5.7.0 + "@ethersproject/logger": ^5.7.0 + "@ethersproject/pbkdf2": ^5.7.0 + "@ethersproject/properties": ^5.7.0 + "@ethersproject/sha2": ^5.7.0 + "@ethersproject/signing-key": ^5.7.0 + "@ethersproject/strings": ^5.7.0 + "@ethersproject/transactions": ^5.7.0 + "@ethersproject/wordlists": ^5.7.0 + checksum: bfe5ca2d89a42de73655f853170ef4766b933c5f481cddad709b3aca18823275b096e572f92d1602a052f80b426edde44ad6b9d028799775a7dad4a5bbed2133 languageName: node linkType: hard -"@ethersproject/json-wallets@npm:5.6.1, @ethersproject/json-wallets@npm:^5.6.1": - version: 5.6.1 - resolution: "@ethersproject/json-wallets@npm:5.6.1" +"@ethersproject/json-wallets@npm:5.7.0, @ethersproject/json-wallets@npm:^5.7.0": + version: 5.7.0 + resolution: "@ethersproject/json-wallets@npm:5.7.0" dependencies: - "@ethersproject/abstract-signer": ^5.6.2 - "@ethersproject/address": ^5.6.1 - "@ethersproject/bytes": ^5.6.1 - "@ethersproject/hdnode": ^5.6.2 - "@ethersproject/keccak256": ^5.6.1 - "@ethersproject/logger": ^5.6.0 - "@ethersproject/pbkdf2": ^5.6.1 - "@ethersproject/properties": ^5.6.0 - "@ethersproject/random": ^5.6.1 - "@ethersproject/strings": ^5.6.1 - "@ethersproject/transactions": ^5.6.2 + "@ethersproject/abstract-signer": ^5.7.0 + "@ethersproject/address": ^5.7.0 + "@ethersproject/bytes": ^5.7.0 + "@ethersproject/hdnode": ^5.7.0 + "@ethersproject/keccak256": ^5.7.0 + "@ethersproject/logger": ^5.7.0 + "@ethersproject/pbkdf2": ^5.7.0 + "@ethersproject/properties": ^5.7.0 + "@ethersproject/random": ^5.7.0 + "@ethersproject/strings": ^5.7.0 + "@ethersproject/transactions": ^5.7.0 aes-js: 3.0.0 scrypt-js: 3.0.1 - checksum: 811b3596aaf1c1a64a8acef0c4fe0123a660349e6cbd5e970b1f9461966fd06858be0f154543bbd962a0ef0d369db52c6254c6b5264c172d44315085a2a6c454 + checksum: f583458d22db62efaaf94d38dd243482776a45bf90f9f3882fbad5aa0b8fd288b41eb7c1ff8ec0b99c9b751088e43d6173530db64dd33c59f9d8daa8d7ad5aa2 languageName: node linkType: hard -"@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: - "@ethersproject/bytes": ^5.6.1 - js-sha3: 0.8.0 - checksum: fdc950e22a1aafc92fdf749cdc5b8952b85e8cee8872d807c5f40be31f58675d30e0eca5e676876b93f2cd22ac63a344d384d116827ee80928c24b7c299991f5 - languageName: node - linkType: hard - -"@ethersproject/keccak256@npm:^5.7.0": +"@ethersproject/keccak256@npm:5.7.0, @ethersproject/keccak256@npm:>=5.0.0-beta.127, @ethersproject/keccak256@npm:^5.7.0": version: 5.7.0 resolution: "@ethersproject/keccak256@npm:5.7.0" dependencies: @@ -566,30 +588,14 @@ __metadata: 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": +"@ethersproject/logger@npm:5.7.0, @ethersproject/logger@npm:>=5.0.0-beta.129, @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" - dependencies: - "@ethersproject/logger": ^5.6.0 - checksum: d41c07497de4ace3f57e972428685a8703a867600cf01f2bc15a21fcb7f99afb3f05b3d8dbb29ac206473368f30d60b98dc445cc38403be4cbe6f804f70e5173 - languageName: node - linkType: hard - -"@ethersproject/networks@npm:^5.7.0": +"@ethersproject/networks@npm:5.7.1, @ethersproject/networks@npm:^5.7.0": version: 5.7.1 resolution: "@ethersproject/networks@npm:5.7.1" dependencies: @@ -598,26 +604,17 @@ __metadata: 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" - dependencies: - "@ethersproject/bytes": ^5.6.1 - "@ethersproject/sha2": ^5.6.1 - checksum: 316006373828a189bf22b7a08df7dd7ffe24e5f2c83e6d09d922ce663892cc14c7d27524dc4e51993d51e4464a7b7ce5e7b23453bdc85e3c6d4d5c41aa7227cf - languageName: node - linkType: hard - -"@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" +"@ethersproject/pbkdf2@npm:5.7.0, @ethersproject/pbkdf2@npm:^5.7.0": + version: 5.7.0 + resolution: "@ethersproject/pbkdf2@npm:5.7.0" dependencies: - "@ethersproject/logger": ^5.6.0 - checksum: adcb6a843dcdf809262d77d6fbe52acdd48703327b298f78e698b76784e89564fb81791d27eaee72b1a6aaaf5688ea2ae7a95faabdef8b4aecc99989fec55901 + "@ethersproject/bytes": ^5.7.0 + "@ethersproject/sha2": ^5.7.0 + checksum: b895adb9e35a8a127e794f7aadc31a2424ef355a70e51cde10d457e3e888bb8102373199a540cf61f2d6b9a32e47358f9c65b47d559f42bf8e596b5fd67901e9 languageName: node linkType: hard -"@ethersproject/properties@npm:^5.7.0": +"@ethersproject/properties@npm:5.7.0, @ethersproject/properties@npm:>=5.0.0-beta.131, @ethersproject/properties@npm:^5.7.0": version: 5.7.0 resolution: "@ethersproject/properties@npm:5.7.0" dependencies: @@ -626,55 +623,45 @@ __metadata: 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" +"@ethersproject/providers@npm:5.7.2, @ethersproject/providers@npm:^5.6.8, @ethersproject/providers@npm:^5.7.1, @ethersproject/providers@npm:^5.7.2": + version: 5.7.2 + resolution: "@ethersproject/providers@npm:5.7.2" dependencies: - "@ethersproject/abstract-provider": ^5.6.1 - "@ethersproject/abstract-signer": ^5.6.2 - "@ethersproject/address": ^5.6.1 - "@ethersproject/base64": ^5.6.1 - "@ethersproject/basex": ^5.6.1 - "@ethersproject/bignumber": ^5.6.2 - "@ethersproject/bytes": ^5.6.1 - "@ethersproject/constants": ^5.6.1 - "@ethersproject/hash": ^5.6.1 - "@ethersproject/logger": ^5.6.0 - "@ethersproject/networks": ^5.6.3 - "@ethersproject/properties": ^5.6.0 - "@ethersproject/random": ^5.6.1 - "@ethersproject/rlp": ^5.6.1 - "@ethersproject/sha2": ^5.6.1 - "@ethersproject/strings": ^5.6.1 - "@ethersproject/transactions": ^5.6.2 - "@ethersproject/web": ^5.6.1 + "@ethersproject/abstract-provider": ^5.7.0 + "@ethersproject/abstract-signer": ^5.7.0 + "@ethersproject/address": ^5.7.0 + "@ethersproject/base64": ^5.7.0 + "@ethersproject/basex": ^5.7.0 + "@ethersproject/bignumber": ^5.7.0 + "@ethersproject/bytes": ^5.7.0 + "@ethersproject/constants": ^5.7.0 + "@ethersproject/hash": ^5.7.0 + "@ethersproject/logger": ^5.7.0 + "@ethersproject/networks": ^5.7.0 + "@ethersproject/properties": ^5.7.0 + "@ethersproject/random": ^5.7.0 + "@ethersproject/rlp": ^5.7.0 + "@ethersproject/sha2": ^5.7.0 + "@ethersproject/strings": ^5.7.0 + "@ethersproject/transactions": ^5.7.0 + "@ethersproject/web": ^5.7.0 bech32: 1.1.4 ws: 7.4.6 - checksum: 27dc2005e1ae7a6d498bb0bbacc6ad1f7164a599cf5aaad7c51cfd7c4d36d0cc5c7c40ba504f9017c746e8a0f008f15ad24e9961816793b49755dcb5c01540c0 - languageName: node - linkType: hard - -"@ethersproject/random@npm:5.6.1, @ethersproject/random@npm:^5.6.1": - version: 5.6.1 - resolution: "@ethersproject/random@npm:5.6.1" - dependencies: - "@ethersproject/bytes": ^5.6.1 - "@ethersproject/logger": ^5.6.0 - checksum: 55517d65eee6dcc0848ef10a825245d61553a6c1bec15d2f69d9430ce4568d9af32013e2aa96c8336545465a24a1fd04defbe9e9f76a5ee110dc5128d4111c11 + checksum: 1754c731a5ca6782ae9677f4a9cd8b6246c4ef21a966c9a01b133750f3c578431ec43ec254e699969c4a0f87e84463ded50f96b415600aabd37d2056aee58c19 languageName: node linkType: hard -"@ethersproject/rlp@npm:5.6.1, @ethersproject/rlp@npm:^5.6.1": - version: 5.6.1 - resolution: "@ethersproject/rlp@npm:5.6.1" +"@ethersproject/random@npm:5.7.0, @ethersproject/random@npm:^5.7.0": + version: 5.7.0 + resolution: "@ethersproject/random@npm:5.7.0" dependencies: - "@ethersproject/bytes": ^5.6.1 - "@ethersproject/logger": ^5.6.0 - checksum: 43a281d0e7842606e2337b5552c13f4b5dad209dce173de39ef6866e02c9d7b974f1cae945782f4c4b74a8e22d8272bfd0348c1cd1bfeb2c278078ef95565488 + "@ethersproject/bytes": ^5.7.0 + "@ethersproject/logger": ^5.7.0 + checksum: 017829c91cff6c76470852855108115b0b52c611b6be817ed1948d56ba42d6677803ec2012aa5ae298a7660024156a64c11fcf544e235e239ab3f89f0fff7345 languageName: node linkType: hard -"@ethersproject/rlp@npm:^5.7.0": +"@ethersproject/rlp@npm:5.7.0, @ethersproject/rlp@npm:^5.7.0": version: 5.7.0 resolution: "@ethersproject/rlp@npm:5.7.0" dependencies: @@ -684,32 +671,18 @@ __metadata: 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" - dependencies: - "@ethersproject/bytes": ^5.6.1 - "@ethersproject/logger": ^5.6.0 - hash.js: 1.1.7 - checksum: 04313cb4a8e24ce8b5736f9d08906764fbfdab19bc64adef363cf570defa72926d8faae19aed805e1caee737f5efecdc60a4c89fd2b1ee2b3ba0eb9555cae3ae - languageName: node - linkType: hard - -"@ethersproject/signing-key@npm:5.6.2, @ethersproject/signing-key@npm:^5.6.2": - version: 5.6.2 - resolution: "@ethersproject/signing-key@npm:5.6.2" +"@ethersproject/sha2@npm:5.7.0, @ethersproject/sha2@npm:^5.7.0": + version: 5.7.0 + resolution: "@ethersproject/sha2@npm:5.7.0" dependencies: - "@ethersproject/bytes": ^5.6.1 - "@ethersproject/logger": ^5.6.0 - "@ethersproject/properties": ^5.6.0 - bn.js: ^5.2.1 - elliptic: 6.5.4 + "@ethersproject/bytes": ^5.7.0 + "@ethersproject/logger": ^5.7.0 hash.js: 1.1.7 - checksum: 7889d0934c9664f87e7b7e021794e2d2ddb2e81c1392498e154cf2d5909b922d74d3df78cec44187f63dc700eddad8f8ea5ded47d2082a212a591818014ca636 + checksum: 09321057c022effbff4cc2d9b9558228690b5dd916329d75c4b1ffe32ba3d24b480a367a7cc92d0f0c0b1c896814d03351ae4630e2f1f7160be2bcfbde435dbc languageName: node linkType: hard -"@ethersproject/signing-key@npm:^5.7.0": +"@ethersproject/signing-key@npm:5.7.0, @ethersproject/signing-key@npm:^5.7.0": version: 5.7.0 resolution: "@ethersproject/signing-key@npm:5.7.0" dependencies: @@ -723,32 +696,21 @@ __metadata: languageName: node linkType: hard -"@ethersproject/solidity@npm:5.6.1": - version: 5.6.1 - resolution: "@ethersproject/solidity@npm:5.6.1" - dependencies: - "@ethersproject/bignumber": ^5.6.2 - "@ethersproject/bytes": ^5.6.1 - "@ethersproject/keccak256": ^5.6.1 - "@ethersproject/logger": ^5.6.0 - "@ethersproject/sha2": ^5.6.1 - "@ethersproject/strings": ^5.6.1 - checksum: a31bd7b98314824d15e28350ee1a21c10e32d2f71579b46c72eab06b895dba147efe966874444a30b17846f9c2ad74043152ec49d4401148262afffb30727087 - languageName: node - linkType: hard - -"@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" +"@ethersproject/solidity@npm:5.7.0": + version: 5.7.0 + resolution: "@ethersproject/solidity@npm:5.7.0" dependencies: - "@ethersproject/bytes": ^5.6.1 - "@ethersproject/constants": ^5.6.1 - "@ethersproject/logger": ^5.6.0 - checksum: dcf33c2ddb22a48c3d7afc151a5f37e5a4da62a742a298988d517dc9adfaff9c5a0ebd8f476ec9792704cfc8142abd541e97432bc47cb121093edac7a5cfaf22 + "@ethersproject/bignumber": ^5.7.0 + "@ethersproject/bytes": ^5.7.0 + "@ethersproject/keccak256": ^5.7.0 + "@ethersproject/logger": ^5.7.0 + "@ethersproject/sha2": ^5.7.0 + "@ethersproject/strings": ^5.7.0 + checksum: 9a02f37f801c96068c3e7721f83719d060175bc4e80439fe060e92bd7acfcb6ac1330c7e71c49f4c2535ca1308f2acdcb01e00133129aac00581724c2d6293f3 languageName: node linkType: hard -"@ethersproject/strings@npm:^5.7.0": +"@ethersproject/strings@npm:5.7.0, @ethersproject/strings@npm:>=5.0.0-beta.130, @ethersproject/strings@npm:^5.7.0": version: 5.7.0 resolution: "@ethersproject/strings@npm:5.7.0" dependencies: @@ -759,24 +721,7 @@ __metadata: 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" - dependencies: - "@ethersproject/address": ^5.6.1 - "@ethersproject/bignumber": ^5.6.2 - "@ethersproject/bytes": ^5.6.1 - "@ethersproject/constants": ^5.6.1 - "@ethersproject/keccak256": ^5.6.1 - "@ethersproject/logger": ^5.6.0 - "@ethersproject/properties": ^5.6.0 - "@ethersproject/rlp": ^5.6.1 - "@ethersproject/signing-key": ^5.6.2 - checksum: 5cf13936ce406f97b71fc1e99090698c2e4276dcb17c5a022aa3c3f55825961edcb53d4a59166acab797275afa45fb93f1b9b602ebc709da6afa66853f849609 - languageName: node - linkType: hard - -"@ethersproject/transactions@npm:^5.7.0": +"@ethersproject/transactions@npm:5.7.0, @ethersproject/transactions@npm:^5.0.0-beta.135, @ethersproject/transactions@npm:^5.7.0": version: 5.7.0 resolution: "@ethersproject/transactions@npm:5.7.0" dependencies: @@ -793,54 +738,41 @@ __metadata: languageName: node linkType: hard -"@ethersproject/units@npm:5.6.1": - version: 5.6.1 - resolution: "@ethersproject/units@npm:5.6.1" - dependencies: - "@ethersproject/bignumber": ^5.6.2 - "@ethersproject/constants": ^5.6.1 - "@ethersproject/logger": ^5.6.0 - checksum: 79cc7c35181fc3bd76fc33d95f1c8d2a20a6339dfc22745184967481b66e0782ee12bbf75b4269119152cbd23bf7980b900978d885b5da72cfb74cf897411065 - languageName: node - linkType: hard - -"@ethersproject/wallet@npm:5.6.2": - version: 5.6.2 - resolution: "@ethersproject/wallet@npm:5.6.2" +"@ethersproject/units@npm:5.7.0": + version: 5.7.0 + resolution: "@ethersproject/units@npm:5.7.0" dependencies: - "@ethersproject/abstract-provider": ^5.6.1 - "@ethersproject/abstract-signer": ^5.6.2 - "@ethersproject/address": ^5.6.1 - "@ethersproject/bignumber": ^5.6.2 - "@ethersproject/bytes": ^5.6.1 - "@ethersproject/hash": ^5.6.1 - "@ethersproject/hdnode": ^5.6.2 - "@ethersproject/json-wallets": ^5.6.1 - "@ethersproject/keccak256": ^5.6.1 - "@ethersproject/logger": ^5.6.0 - "@ethersproject/properties": ^5.6.0 - "@ethersproject/random": ^5.6.1 - "@ethersproject/signing-key": ^5.6.2 - "@ethersproject/transactions": ^5.6.2 - "@ethersproject/wordlists": ^5.6.1 - checksum: 88603a4797b8f489c76671ff096ad3630ad1226640032594cfb3376398b41c1c4875076f1cf6521854c42e4496cafd2171e6dc301669cbf6c972ba13e97be5b0 + "@ethersproject/bignumber": ^5.7.0 + "@ethersproject/constants": ^5.7.0 + "@ethersproject/logger": ^5.7.0 + checksum: 304714f848cd32e57df31bf545f7ad35c2a72adae957198b28cbc62166daa929322a07bff6e9c9ac4577ab6aa0de0546b065ed1b2d20b19e25748b7d475cb0fc languageName: node linkType: hard -"@ethersproject/web@npm:5.6.1, @ethersproject/web@npm:^5.6.1": - version: 5.6.1 - resolution: "@ethersproject/web@npm:5.6.1" +"@ethersproject/wallet@npm:5.7.0": + version: 5.7.0 + resolution: "@ethersproject/wallet@npm:5.7.0" dependencies: - "@ethersproject/base64": ^5.6.1 - "@ethersproject/bytes": ^5.6.1 - "@ethersproject/logger": ^5.6.0 - "@ethersproject/properties": ^5.6.0 - "@ethersproject/strings": ^5.6.1 - checksum: 4acb62bb04431f5a1b1ec27e88847087676dd2fd72ba40c789f2885493e5eed6b6d387d5b47d4cdfc2775bcbe714e04bfaf0d04a6f30e929310384362e6be429 + "@ethersproject/abstract-provider": ^5.7.0 + "@ethersproject/abstract-signer": ^5.7.0 + "@ethersproject/address": ^5.7.0 + "@ethersproject/bignumber": ^5.7.0 + "@ethersproject/bytes": ^5.7.0 + "@ethersproject/hash": ^5.7.0 + "@ethersproject/hdnode": ^5.7.0 + "@ethersproject/json-wallets": ^5.7.0 + "@ethersproject/keccak256": ^5.7.0 + "@ethersproject/logger": ^5.7.0 + "@ethersproject/properties": ^5.7.0 + "@ethersproject/random": ^5.7.0 + "@ethersproject/signing-key": ^5.7.0 + "@ethersproject/transactions": ^5.7.0 + "@ethersproject/wordlists": ^5.7.0 + checksum: a4009bf7331eddab38e3015b5e9101ef92de7f705b00a6196b997db0e5635b6d83561674d46c90c6f77b87c0500fe4a6b0183ba13749efc22db59c99deb82fbd languageName: node linkType: hard -"@ethersproject/web@npm:^5.7.0": +"@ethersproject/web@npm:5.7.1, @ethersproject/web@npm:^5.7.0": version: 5.7.1 resolution: "@ethersproject/web@npm:5.7.1" dependencies: @@ -853,23 +785,23 @@ __metadata: 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" +"@ethersproject/wordlists@npm:5.7.0, @ethersproject/wordlists@npm:^5.7.0": + version: 5.7.0 + resolution: "@ethersproject/wordlists@npm:5.7.0" dependencies: - "@ethersproject/bytes": ^5.6.1 - "@ethersproject/hash": ^5.6.1 - "@ethersproject/logger": ^5.6.0 - "@ethersproject/properties": ^5.6.0 - "@ethersproject/strings": ^5.6.1 - checksum: 3be4f300705b3f4f2b1dfa3948aac2e5030ab6216086578ec5cd2fad130b6b30d2a6a3c54d94c6669601ed62b56e7052232bc0a934a451ef3320fd6513734729 + "@ethersproject/bytes": ^5.7.0 + "@ethersproject/hash": ^5.7.0 + "@ethersproject/logger": ^5.7.0 + "@ethersproject/properties": ^5.7.0 + "@ethersproject/strings": ^5.7.0 + checksum: 30eb6eb0731f9ef5faa44bf9c0c6e950bcaaef61e4d2d9ce0ae6d341f4e2d6d1f4ab4f8880bfce03b7aac4b862fb740e1421170cfbf8e2aafc359277d49e6e97 languageName: node linkType: hard -"@gar/promisify@npm:^1.1.3": - version: 1.1.3 - resolution: "@gar/promisify@npm:1.1.3" - checksum: 4059f790e2d07bf3c3ff3e0fec0daa8144fe35c1f6e0111c9921bd32106adaa97a4ab096ad7dab1e28ee6a9060083c4d1a4ada42a7f5f3f7a96b8812e2b757c1 +"@fastify/busboy@npm:^2.0.0": + version: 2.1.0 + resolution: "@fastify/busboy@npm:2.1.0" + checksum: 3233abd10f73e50668cb4bb278a79b7b3fadd30215ac6458299b0e5a09a29c3586ec07597aae6bd93f5cbedfcef43a8aeea51829cd28fc13850cdbcd324c28d5 languageName: node linkType: hard @@ -912,21 +844,28 @@ __metadata: languageName: node linkType: hard -"@humanwhocodes/config-array@npm:^0.9.2": - version: 0.9.5 - resolution: "@humanwhocodes/config-array@npm:0.9.5" +"@humanwhocodes/config-array@npm:^0.11.13": + version: 0.11.14 + resolution: "@humanwhocodes/config-array@npm:0.11.14" dependencies: - "@humanwhocodes/object-schema": ^1.2.1 - debug: ^4.1.1 - minimatch: ^3.0.4 - checksum: 8ba6281bc0590f6c6eadeefc14244b5a3e3f5903445aadd1a32099ed80e753037674026ce1b3c945ab93561bea5eb29e3c5bff67060e230c295595ba517a3492 + "@humanwhocodes/object-schema": ^2.0.2 + debug: ^4.3.1 + minimatch: ^3.0.5 + checksum: 861ccce9eaea5de19546653bccf75bf09fe878bc39c3aab00aeee2d2a0e654516adad38dd1098aab5e3af0145bbcbf3f309bdf4d964f8dab9dcd5834ae4c02f2 languageName: node linkType: hard -"@humanwhocodes/object-schema@npm:^1.2.1": - version: 1.2.1 - resolution: "@humanwhocodes/object-schema@npm:1.2.1" - checksum: a824a1ec31591231e4bad5787641f59e9633827d0a2eaae131a288d33c9ef0290bd16fda8da6f7c0fcb014147865d12118df10db57f27f41e20da92369fcb3f1 +"@humanwhocodes/module-importer@npm:^1.0.1": + version: 1.0.1 + resolution: "@humanwhocodes/module-importer@npm:1.0.1" + checksum: 0fd22007db8034a2cdf2c764b140d37d9020bbfce8a49d3ec5c05290e77d4b0263b1b972b752df8c89e5eaa94073408f2b7d977aed131faf6cf396ebb5d7fb61 + languageName: node + linkType: hard + +"@humanwhocodes/object-schema@npm:^2.0.2": + version: 2.0.2 + resolution: "@humanwhocodes/object-schema@npm:2.0.2" + checksum: 2fc11503361b5fb4f14714c700c02a3f4c7c93e9acd6b87a29f62c522d90470f364d6161b03d1cc618b979f2ae02aed1106fd29d302695d8927e2fc8165ba8ee languageName: node linkType: hard @@ -959,17 +898,31 @@ __metadata: languageName: node linkType: hard +"@isaacs/cliui@npm:^8.0.2": + version: 8.0.2 + resolution: "@isaacs/cliui@npm:8.0.2" + dependencies: + string-width: ^5.1.2 + string-width-cjs: "npm:string-width@^4.2.0" + strip-ansi: ^7.0.1 + strip-ansi-cjs: "npm:strip-ansi@^6.0.1" + wrap-ansi: ^8.1.0 + wrap-ansi-cjs: "npm:wrap-ansi@^7.0.0" + checksum: 4a473b9b32a7d4d3cfb7a614226e555091ff0c5a29a1734c28c72a182c2f6699b26fc6b5c2131dfd841e86b185aea714c72201d7c98c2fba5f17709333a67aeb + languageName: node + linkType: hard + "@jridgewell/resolve-uri@npm:^3.0.3": - version: 3.0.8 - resolution: "@jridgewell/resolve-uri@npm:3.0.8" - checksum: 28d739f49b4a52a95843b15669dcb2daaab48f0eaef8f457b9aacd0bdebeb60468d0684f73244f613b786e9d871c25abdbe6f55991bba36814cdadc399dbb3a8 + version: 3.1.1 + resolution: "@jridgewell/resolve-uri@npm:3.1.1" + checksum: f5b441fe7900eab4f9155b3b93f9800a916257f4e8563afbcd3b5a5337b55e52bd8ae6735453b1b745457d9f6cdb16d74cd6220bbdd98cf153239e13f6cbb653 languageName: node linkType: hard "@jridgewell/sourcemap-codec@npm:^1.4.10": - version: 1.4.14 - resolution: "@jridgewell/sourcemap-codec@npm:1.4.14" - checksum: 61100637b6d173d3ba786a5dff019e1a74b1f394f323c1fee337ff390239f053b87266c7a948777f4b1ee68c01a8ad0ab61e5ff4abb5a012a0b091bec391ab97 + version: 1.4.15 + resolution: "@jridgewell/sourcemap-codec@npm:1.4.15" + checksum: b881c7e503db3fc7f3c1f35a1dd2655a188cc51a3612d76efc8a6eb74728bef5606e6758ee77423e564092b4a518aba569bbb21c9bac5ab7a35b0c6ae7e344c8 languageName: node linkType: hard @@ -983,6 +936,24 @@ __metadata: languageName: node linkType: hard +"@ljharb/resumer@npm:~0.0.1": + version: 0.0.1 + resolution: "@ljharb/resumer@npm:0.0.1" + dependencies: + "@ljharb/through": ^2.3.9 + checksum: 1cff0a485cb857933d2921cb05a349f8fe894fa2bb6b31a347010ecccc4a2b369e43ebe5383a32a60ee6c9572d2c83fcab383eb01727e1507bf29c59f312dae6 + languageName: node + linkType: hard + +"@ljharb/through@npm:^2.3.9, @ljharb/through@npm:~2.3.9": + version: 2.3.12 + resolution: "@ljharb/through@npm:2.3.12" + dependencies: + call-bind: ^1.0.5 + checksum: d5a78568cd3025c03264a9f9c61b30511d27cb9611fae7575cb1339a1baa1a263b6af03e28505b821324f3c6285086ee5add612b8b0155d1f253ed5159cd3f56 + languageName: node + linkType: hard + "@metamask/eth-sig-util@npm:^4.0.0": version: 4.0.1 resolution: "@metamask/eth-sig-util@npm:4.0.1" @@ -996,24 +967,33 @@ __metadata: languageName: node linkType: hard -"@noble/hashes@npm:1.1.1": - version: 1.1.1 - resolution: "@noble/hashes@npm:1.1.1" - checksum: 3bd98d7a6dcc01c5e72478975073e12c79639636f4eb5710b665dd8ac462fcdff5b235d0c3b113ac83e7e56c43eee5ccba3759f9262964edc123bd1713dd2180 +"@noble/curves@npm:1.3.0, @noble/curves@npm:~1.3.0": + version: 1.3.0 + resolution: "@noble/curves@npm:1.3.0" + dependencies: + "@noble/hashes": 1.3.3 + checksum: b65342ee66c4a440eee2978524412eabba9a9efdd16d6370e15218c6a7d80bddf35e66bb57ed52c0dfd32cb9a717b439ab3a72db618f1a0066dfebe3fd12a421 languageName: node linkType: hard -"@noble/hashes@npm:~1.1.1": - version: 1.1.2 - resolution: "@noble/hashes@npm:1.1.2" - checksum: 3c2a8cb7c2e053811032f242155d870c5eb98844d924d69702244d48804cb03b42d4a666c49c2b71164420d8229cb9a6f242b972d50d5bb2f1d673b98b041de2 +"@noble/hashes@npm:1.2.0, @noble/hashes@npm:~1.2.0": + version: 1.2.0 + resolution: "@noble/hashes@npm:1.2.0" + checksum: 8ca080ce557b8f40fb2f78d3aedffd95825a415ac8e13d7ffe3643f8626a8c2d99a3e5975b555027ac24316d8b3c02a35b8358567c0c23af681e6573602aa434 languageName: node linkType: hard -"@noble/secp256k1@npm:1.6.0, @noble/secp256k1@npm:~1.6.0": - version: 1.6.0 - resolution: "@noble/secp256k1@npm:1.6.0" - checksum: e99df3b776515e6a8b3193870e69ff3a7d22c6a4733245dceb9d1d229d5b0859bd478b7213f31d556ba3745647ec07262d0f9df845d79204b7ce4ae1648b27c7 +"@noble/hashes@npm:1.3.3, @noble/hashes@npm:~1.3.2": + version: 1.3.3 + resolution: "@noble/hashes@npm:1.3.3" + checksum: 8a6496d1c0c64797339bc694ad06cdfaa0f9e56cd0c3f68ae3666cfb153a791a55deb0af9c653c7ed2db64d537aa3e3054629740d2f2338bb1dcb7ab60cd205b + languageName: node + linkType: hard + +"@noble/secp256k1@npm:1.7.1, @noble/secp256k1@npm:~1.7.0": + version: 1.7.1 + resolution: "@noble/secp256k1@npm:1.7.1" + checksum: d2301f1f7690368d8409a3152450458f27e54df47e3f917292de3de82c298770890c2de7c967d237eff9c95b70af485389a9695f73eb05a43e2bd562d18b18cb languageName: node linkType: hard @@ -1034,7 +1014,7 @@ __metadata: languageName: node linkType: hard -"@nodelib/fs.walk@npm:^1.2.3": +"@nodelib/fs.walk@npm:^1.2.3, @nodelib/fs.walk@npm:^1.2.8": version: 1.2.8 resolution: "@nodelib/fs.walk@npm:1.2.8" dependencies: @@ -1044,151 +1024,309 @@ __metadata: languageName: node linkType: hard -"@nomicfoundation/ethereumjs-block@npm:^4.0.0": - version: 4.0.0 - resolution: "@nomicfoundation/ethereumjs-block@npm:4.0.0" +"@nomicfoundation/ethereumjs-block@npm:4.2.2": + version: 4.2.2 + resolution: "@nomicfoundation/ethereumjs-block@npm:4.2.2" 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 + "@nomicfoundation/ethereumjs-common": 3.1.2 + "@nomicfoundation/ethereumjs-rlp": 4.0.3 + "@nomicfoundation/ethereumjs-trie": 5.0.5 + "@nomicfoundation/ethereumjs-tx": 4.1.2 + "@nomicfoundation/ethereumjs-util": 8.0.6 ethereum-cryptography: 0.1.3 - checksum: a57a33dda7724f0a46ef2e0ca0dbb1b427268f4135e8c23eee9ab5730a79369d52122faba7a010d71bca3046f7ce644ed95e4a34d5f2221ecaa5d94886d84b11 + checksum: 174a251d9c4e0bb9c1a7a6e77c52f1b2b4708d8135dba55c1025776248258ce905e4383a79da0ce7ac4e67e03b6c56351ca634a771b5eae976ed97498fc163f9 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 +"@nomicfoundation/ethereumjs-block@npm:5.0.2": + version: 5.0.2 + resolution: "@nomicfoundation/ethereumjs-block@npm:5.0.2" + dependencies: + "@nomicfoundation/ethereumjs-common": 4.0.2 + "@nomicfoundation/ethereumjs-rlp": 5.0.2 + "@nomicfoundation/ethereumjs-trie": 6.0.2 + "@nomicfoundation/ethereumjs-tx": 5.0.2 + "@nomicfoundation/ethereumjs-util": 9.0.2 + ethereum-cryptography: 0.1.3 + ethers: ^5.7.1 + checksum: 7ff744f44a01f1c059ca7812a1cfc8089f87aa506af6cb39c78331dca71b32993cbd6fa05ad03f8c4f4fab73bb998a927af69e0d8ff01ae192ee5931606e09f5 + languageName: node + linkType: hard + +"@nomicfoundation/ethereumjs-blockchain@npm:6.2.2": + version: 6.2.2 + resolution: "@nomicfoundation/ethereumjs-blockchain@npm:6.2.2" + dependencies: + "@nomicfoundation/ethereumjs-block": 4.2.2 + "@nomicfoundation/ethereumjs-common": 3.1.2 + "@nomicfoundation/ethereumjs-ethash": 2.0.5 + "@nomicfoundation/ethereumjs-rlp": 4.0.3 + "@nomicfoundation/ethereumjs-trie": 5.0.5 + "@nomicfoundation/ethereumjs-util": 8.0.6 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 + checksum: 5933600bf005ec3e33f6fdd0b3582b80ed7eac8fa776fc86f21de8a6ac3614e3262c48ad3737015c19558165aecd7b13a8056e96afd61511d0605411e0264871 languageName: node linkType: hard -"@nomicfoundation/ethereumjs-common@npm:^3.0.0": - version: 3.0.0 - resolution: "@nomicfoundation/ethereumjs-common@npm:3.0.0" +"@nomicfoundation/ethereumjs-blockchain@npm:7.0.2": + version: 7.0.2 + resolution: "@nomicfoundation/ethereumjs-blockchain@npm:7.0.2" + dependencies: + "@nomicfoundation/ethereumjs-block": 5.0.2 + "@nomicfoundation/ethereumjs-common": 4.0.2 + "@nomicfoundation/ethereumjs-ethash": 3.0.2 + "@nomicfoundation/ethereumjs-rlp": 5.0.2 + "@nomicfoundation/ethereumjs-trie": 6.0.2 + "@nomicfoundation/ethereumjs-tx": 5.0.2 + "@nomicfoundation/ethereumjs-util": 9.0.2 + 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: b7e440dcd73e32aa72d13bfd28cb472773c9c60ea808a884131bf7eb3f42286ad594a0864215f599332d800f3fe1f772fff4b138d2dcaa8f41e4d8389bff33e7 + languageName: node + linkType: hard + +"@nomicfoundation/ethereumjs-common@npm:3.1.2": + version: 3.1.2 + resolution: "@nomicfoundation/ethereumjs-common@npm:3.1.2" dependencies: - "@nomicfoundation/ethereumjs-util": ^8.0.0 + "@nomicfoundation/ethereumjs-util": 8.0.6 crc-32: ^1.2.0 - checksum: 6a62908e5ccd8a4f56b841bd6ba9eef21dffafdd505f18b6b886d86ba4287cd12a2c632d521c5fddf2c6fca5a840f580d7601d89820098f6c1f8311db41e496b + checksum: b886e47bb4da26b42bf9e905c5f073db62d2ad1b740d50898012580b501868839fcf08430debe3fca927b4d73e01628c1b0b2e84401feb551245dacfac045404 languageName: node linkType: hard -"@nomicfoundation/ethereumjs-ethash@npm:^2.0.0": - version: 2.0.0 - resolution: "@nomicfoundation/ethereumjs-ethash@npm:2.0.0" +"@nomicfoundation/ethereumjs-common@npm:4.0.2": + version: 4.0.2 + resolution: "@nomicfoundation/ethereumjs-common@npm:4.0.2" dependencies: - "@nomicfoundation/ethereumjs-block": ^4.0.0 - "@nomicfoundation/ethereumjs-rlp": ^4.0.0 - "@nomicfoundation/ethereumjs-util": ^8.0.0 + "@nomicfoundation/ethereumjs-util": 9.0.2 + crc-32: ^1.2.0 + checksum: f0d84704d6254d374299c19884312bd5666974b4b6f342d3f10bc76e549de78d20e45a53d25fbdc146268a52335497127e4f069126da7c60ac933a158e704887 + languageName: node + linkType: hard + +"@nomicfoundation/ethereumjs-ethash@npm:2.0.5": + version: 2.0.5 + resolution: "@nomicfoundation/ethereumjs-ethash@npm:2.0.5" + dependencies: + "@nomicfoundation/ethereumjs-block": 4.2.2 + "@nomicfoundation/ethereumjs-rlp": 4.0.3 + "@nomicfoundation/ethereumjs-util": 8.0.6 abstract-level: ^1.0.3 bigint-crypto-utils: ^3.0.23 ethereum-cryptography: 0.1.3 - checksum: 60133df2d450179f2ab26e8784b1bd79b37411bb047a7dace655499749893750f0f8d6d573f182ebcf4dba35f2da6301b0ad1b80dbe7637bb0d5155ccb189fda + checksum: 0b03c8771602cfa64c9d35e5686326d0bfecb7dc0874cd9ff737cae0ec401396187d8499c103b8858fed5b9bd930e132b8fd09d19b3f0649df36d7d0fdf4d27c 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" +"@nomicfoundation/ethereumjs-ethash@npm:3.0.2": + version: 3.0.2 + resolution: "@nomicfoundation/ethereumjs-ethash@npm:3.0.2" dependencies: - "@nomicfoundation/ethereumjs-common": ^3.0.0 - "@nomicfoundation/ethereumjs-util": ^8.0.0 + "@nomicfoundation/ethereumjs-block": 5.0.2 + "@nomicfoundation/ethereumjs-rlp": 5.0.2 + "@nomicfoundation/ethereumjs-util": 9.0.2 + abstract-level: ^1.0.3 + bigint-crypto-utils: ^3.0.23 + ethereum-cryptography: 0.1.3 + checksum: e4011e4019dd9b92f7eeebfc1e6c9a9685c52d8fd0ee4f28f03e50048a23b600c714490827f59fdce497b3afb503b3fd2ebf6815ff307e9949c3efeff1403278 + languageName: node + linkType: hard + +"@nomicfoundation/ethereumjs-evm@npm:1.3.2, @nomicfoundation/ethereumjs-evm@npm:^1.0.0-rc.3": + version: 1.3.2 + resolution: "@nomicfoundation/ethereumjs-evm@npm:1.3.2" + dependencies: + "@nomicfoundation/ethereumjs-common": 3.1.2 + "@nomicfoundation/ethereumjs-util": 8.0.6 "@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 + checksum: 4a051f36968574ffbee5d3c401ebf1c81899b69a0692c372fced67691fe18f26741f26d1781e79dfa52136af888e561d80de4fd7dd59000d640c51bd8b130023 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" +"@nomicfoundation/ethereumjs-evm@npm:2.0.2": + version: 2.0.2 + resolution: "@nomicfoundation/ethereumjs-evm@npm:2.0.2" + dependencies: + "@ethersproject/providers": ^5.7.1 + "@nomicfoundation/ethereumjs-common": 4.0.2 + "@nomicfoundation/ethereumjs-tx": 5.0.2 + "@nomicfoundation/ethereumjs-util": 9.0.2 + debug: ^4.3.3 + ethereum-cryptography: 0.1.3 + mcl-wasm: ^0.7.1 + rustbn.js: ~0.2.0 + checksum: a23cf570836ddc147606b02df568069de946108e640f902358fef67e589f6b371d856056ee44299d9b4e3497f8ae25faa45e6b18fefd90e9b222dc6a761d85f0 + languageName: node + linkType: hard + +"@nomicfoundation/ethereumjs-rlp@npm:4.0.3": + version: 4.0.3 + resolution: "@nomicfoundation/ethereumjs-rlp@npm:4.0.3" bin: rlp: bin/rlp - checksum: b358d239e5a24884f0446d52159c8115b0eb1d6907179dc968df5054dccea7eff72f2d12522c911b6e08bb4b5d3f5f8e1d86a45cb1a24a4831cbb109743d4407 + checksum: 14fc83701dd52323fae705786549ab07482ace315de69a586bb948b6f21ec529794cef8248af0b5c7e8f8b05fbadfbe222754b305841fa2189bfbc8f1eb064a2 languageName: node linkType: hard -"@nomicfoundation/ethereumjs-statemanager@npm:^1.0.0": - version: 1.0.0 - resolution: "@nomicfoundation/ethereumjs-statemanager@npm:1.0.0" +"@nomicfoundation/ethereumjs-rlp@npm:5.0.2": + version: 5.0.2 + resolution: "@nomicfoundation/ethereumjs-rlp@npm:5.0.2" + bin: + rlp: bin/rlp + checksum: a74434cadefca9aa8754607cc1ad7bb4bbea4ee61c6214918e60a5bbee83206850346eb64e39fd1fe97f854c7ec0163e01148c0c881dda23881938f0645a0ef2 + languageName: node + linkType: hard + +"@nomicfoundation/ethereumjs-statemanager@npm:1.0.5": + version: 1.0.5 + resolution: "@nomicfoundation/ethereumjs-statemanager@npm:1.0.5" 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 + "@nomicfoundation/ethereumjs-common": 3.1.2 + "@nomicfoundation/ethereumjs-rlp": 4.0.3 + "@nomicfoundation/ethereumjs-trie": 5.0.5 + "@nomicfoundation/ethereumjs-util": 8.0.6 debug: ^4.3.3 ethereum-cryptography: 0.1.3 functional-red-black-tree: ^1.0.1 - checksum: fad02ea922fbe25328186ea2eb43bdba63def57822f373ce213be26125ee8d3c90cf3b6f626e6876637cdb842e3c2b788fb8891fcf1aca3fd655e1c0d9a7e936 + checksum: 0f88743900b2211deb5d2393bf111ef63411ce533387a6d06c48cc9ac1f4fc38f968cdecc4712ebdafdebc3c4c2ce6bd1abd82989f4f4f515d3f571981d38f9f languageName: node linkType: hard -"@nomicfoundation/ethereumjs-trie@npm:^5.0.0": - version: 5.0.0 - resolution: "@nomicfoundation/ethereumjs-trie@npm:5.0.0" +"@nomicfoundation/ethereumjs-statemanager@npm:2.0.2": + version: 2.0.2 + resolution: "@nomicfoundation/ethereumjs-statemanager@npm:2.0.2" dependencies: - "@nomicfoundation/ethereumjs-rlp": ^4.0.0 - "@nomicfoundation/ethereumjs-util": ^8.0.0 + "@nomicfoundation/ethereumjs-common": 4.0.2 + "@nomicfoundation/ethereumjs-rlp": 5.0.2 + debug: ^4.3.3 + ethereum-cryptography: 0.1.3 + ethers: ^5.7.1 + js-sdsl: ^4.1.4 + checksum: 3ab6578e252e53609afd98d8ba42a99f182dcf80252f23ed9a5e0471023ffb2502130f85fc47fa7c94cd149f9be799ed9a0942ca52a143405be9267f4ad94e64 + languageName: node + linkType: hard + +"@nomicfoundation/ethereumjs-trie@npm:5.0.5": + version: 5.0.5 + resolution: "@nomicfoundation/ethereumjs-trie@npm:5.0.5" + dependencies: + "@nomicfoundation/ethereumjs-rlp": 4.0.3 + "@nomicfoundation/ethereumjs-util": 8.0.6 ethereum-cryptography: 0.1.3 readable-stream: ^3.6.0 - checksum: 468de7ffe05473f0f05940e74bba01652dd9a4ff155a13e0a5395551e53557afde47d98f496f6323824bccfaeee8de4e22fef9b7f88d3bbd4e97cadc54e2e4f9 + checksum: bed56b55093275166c40d0aa097b32d348b3795cbfdc3797d48d136a578161431e70f30bcf453b74b52f77b897d79b61a3fb9d1abd10187c0cb7f25e40dea9c5 languageName: node linkType: hard -"@nomicfoundation/ethereumjs-tx@npm:^4.0.0": - version: 4.0.0 - resolution: "@nomicfoundation/ethereumjs-tx@npm:4.0.0" +"@nomicfoundation/ethereumjs-trie@npm:6.0.2": + version: 6.0.2 + resolution: "@nomicfoundation/ethereumjs-trie@npm:6.0.2" dependencies: - "@nomicfoundation/ethereumjs-common": ^3.0.0 - "@nomicfoundation/ethereumjs-rlp": ^4.0.0 - "@nomicfoundation/ethereumjs-util": ^8.0.0 + "@nomicfoundation/ethereumjs-rlp": 5.0.2 + "@nomicfoundation/ethereumjs-util": 9.0.2 + "@types/readable-stream": ^2.3.13 ethereum-cryptography: 0.1.3 - checksum: d2c0e3384aaa9f3b58232c531a4efd524be257e7257f23c3beed6ec9cf5fba6345cb632b3a464ae0a2aa99fd9e4a2d3e2d5c501593c5466e6ab629f05255791e + readable-stream: ^3.6.0 + checksum: d4da918d333851b9f2cce7dbd25ab5753e0accd43d562d98fd991b168b6a08d1794528f0ade40fe5617c84900378376fe6256cdbe52c8d66bf4c53293bbc7c40 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" +"@nomicfoundation/ethereumjs-tx@npm:4.1.2": + version: 4.1.2 + resolution: "@nomicfoundation/ethereumjs-tx@npm:4.1.2" dependencies: - "@nomicfoundation/ethereumjs-rlp": ^4.0.0-beta.2 + "@nomicfoundation/ethereumjs-common": 3.1.2 + "@nomicfoundation/ethereumjs-rlp": 4.0.3 + "@nomicfoundation/ethereumjs-util": 8.0.6 ethereum-cryptography: 0.1.3 - checksum: a39be4c8d3dea4fae1e969b47138d718cac31bf248bb517766a42c97ca5850ca3ddf16c66d8e404fa0a0363fd6898ae2e716d75da2ed4113e610d26026e4cefb + checksum: 209622bdc56e5f1267e5d2de69ed18388b141edc568f739f0ed865aecfe96e07c381aab779ed0adacefeae4da5be64fa1110a02e481e9a7c343bf0d53f4fd1b9 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 +"@nomicfoundation/ethereumjs-tx@npm:5.0.2": + version: 5.0.2 + resolution: "@nomicfoundation/ethereumjs-tx@npm:5.0.2" + dependencies: + "@chainsafe/ssz": ^0.9.2 + "@ethersproject/providers": ^5.7.2 + "@nomicfoundation/ethereumjs-common": 4.0.2 + "@nomicfoundation/ethereumjs-rlp": 5.0.2 + "@nomicfoundation/ethereumjs-util": 9.0.2 + ethereum-cryptography: 0.1.3 + checksum: 0bbcea75786b2ccb559afe2ecc9866fb4566a9f157b6ffba4f50960d14f4b3da2e86e273f6fadda9b860e67cfcabf589970fb951b328cb5f900a585cd21842a2 + languageName: node + linkType: hard + +"@nomicfoundation/ethereumjs-util@npm:8.0.6, @nomicfoundation/ethereumjs-util@npm:^8.0.0-rc.3": + version: 8.0.6 + resolution: "@nomicfoundation/ethereumjs-util@npm:8.0.6" + dependencies: + "@nomicfoundation/ethereumjs-rlp": 4.0.3 + ethereum-cryptography: 0.1.3 + checksum: 7a51c2069702750d94bf6bc5afd4a26c50321fe42504339d5275b60974941451eb41232f8a08c307797bcd498f20a3b27074351a76abdfc36a5e74473a7eda01 + languageName: node + linkType: hard + +"@nomicfoundation/ethereumjs-util@npm:9.0.2": + version: 9.0.2 + resolution: "@nomicfoundation/ethereumjs-util@npm:9.0.2" + dependencies: + "@chainsafe/ssz": ^0.10.0 + "@nomicfoundation/ethereumjs-rlp": 5.0.2 + ethereum-cryptography: 0.1.3 + checksum: 3a08f7b88079ef9f53b43da9bdcb8195498fd3d3911c2feee2571f4d1204656053f058b2f650471c86f7d2d0ba2f814768c7cfb0f266eede41c848356afc4900 + languageName: node + linkType: hard + +"@nomicfoundation/ethereumjs-vm@npm:7.0.2": + version: 7.0.2 + resolution: "@nomicfoundation/ethereumjs-vm@npm:7.0.2" + dependencies: + "@nomicfoundation/ethereumjs-block": 5.0.2 + "@nomicfoundation/ethereumjs-blockchain": 7.0.2 + "@nomicfoundation/ethereumjs-common": 4.0.2 + "@nomicfoundation/ethereumjs-evm": 2.0.2 + "@nomicfoundation/ethereumjs-rlp": 5.0.2 + "@nomicfoundation/ethereumjs-statemanager": 2.0.2 + "@nomicfoundation/ethereumjs-trie": 6.0.2 + "@nomicfoundation/ethereumjs-tx": 5.0.2 + "@nomicfoundation/ethereumjs-util": 9.0.2 + debug: ^4.3.3 + ethereum-cryptography: 0.1.3 + mcl-wasm: ^0.7.1 + rustbn.js: ~0.2.0 + checksum: 1c25ba4d0644cadb8a2b0241a4bb02e578bfd7f70e3492b855c2ab5c120cb159cb8f7486f84dc1597884bd1697feedbfb5feb66e91352afb51f3694fd8e4a043 + languageName: node + linkType: hard + +"@nomicfoundation/ethereumjs-vm@npm:^6.0.0-rc.3": + version: 6.4.2 + resolution: "@nomicfoundation/ethereumjs-vm@npm:6.4.2" + dependencies: + "@nomicfoundation/ethereumjs-block": 4.2.2 + "@nomicfoundation/ethereumjs-blockchain": 6.2.2 + "@nomicfoundation/ethereumjs-common": 3.1.2 + "@nomicfoundation/ethereumjs-evm": 1.3.2 + "@nomicfoundation/ethereumjs-rlp": 4.0.3 + "@nomicfoundation/ethereumjs-statemanager": 1.0.5 + "@nomicfoundation/ethereumjs-trie": 5.0.5 + "@nomicfoundation/ethereumjs-tx": 4.1.2 + "@nomicfoundation/ethereumjs-util": 8.0.6 "@types/async-eventemitter": ^0.2.1 async-eventemitter: ^0.2.4 debug: ^4.3.3 @@ -1196,7 +1334,7 @@ __metadata: functional-red-black-tree: ^1.0.1 mcl-wasm: ^0.7.1 rustbn.js: ~0.2.0 - checksum: 3c0e10b377579d74bfdcfd056d5545b605f767982e41038d036c8219a50fe3564c7f146fdd04385d64f48f94b9d95c378d7a37955c5100c46c568a29f54ea737 + checksum: 9138b8cce872a51fe2e378942c52fc6c54d8126ff094ba6bb78cbb630cafa20d7fbaa2b08bdcf7cad6de78e19ce68493ddbcc2e02acb7c803b866dc121274ea7 languageName: node linkType: hard @@ -1218,90 +1356,109 @@ __metadata: 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" +"@nomicfoundation/hardhat-verify@npm:^1.1.0": + version: 1.1.1 + resolution: "@nomicfoundation/hardhat-verify@npm:1.1.1" + dependencies: + "@ethersproject/abi": ^5.1.2 + "@ethersproject/address": ^5.0.2 + cbor: ^8.1.0 + chalk: ^2.4.2 + debug: ^4.1.1 + lodash.clonedeep: ^4.5.0 + semver: ^6.3.0 + table: ^6.8.0 + undici: ^5.14.0 + peerDependencies: + hardhat: ^2.0.4 + checksum: 2d83d32d6833f23fb62c30c68c9a2ab3956098030edcf459e69639960f059c72572d203bcf92f191c69c9cb0fbbf011a1113bacde1e3cbb28d5e812334f04f32 + languageName: node + linkType: hard + +"@nomicfoundation/solidity-analyzer-darwin-arm64@npm:0.1.1": + version: 0.1.1 + resolution: "@nomicfoundation/solidity-analyzer-darwin-arm64@npm:0.1.1" 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" +"@nomicfoundation/solidity-analyzer-darwin-x64@npm:0.1.1": + version: 0.1.1 + resolution: "@nomicfoundation/solidity-analyzer-darwin-x64@npm:0.1.1" 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" +"@nomicfoundation/solidity-analyzer-freebsd-x64@npm:0.1.1": + version: 0.1.1 + resolution: "@nomicfoundation/solidity-analyzer-freebsd-x64@npm:0.1.1" 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" +"@nomicfoundation/solidity-analyzer-linux-arm64-gnu@npm:0.1.1": + version: 0.1.1 + resolution: "@nomicfoundation/solidity-analyzer-linux-arm64-gnu@npm:0.1.1" 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" +"@nomicfoundation/solidity-analyzer-linux-arm64-musl@npm:0.1.1": + version: 0.1.1 + resolution: "@nomicfoundation/solidity-analyzer-linux-arm64-musl@npm:0.1.1" 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" +"@nomicfoundation/solidity-analyzer-linux-x64-gnu@npm:0.1.1": + version: 0.1.1 + resolution: "@nomicfoundation/solidity-analyzer-linux-x64-gnu@npm:0.1.1" 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" +"@nomicfoundation/solidity-analyzer-linux-x64-musl@npm:0.1.1": + version: 0.1.1 + resolution: "@nomicfoundation/solidity-analyzer-linux-x64-musl@npm:0.1.1" 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" +"@nomicfoundation/solidity-analyzer-win32-arm64-msvc@npm:0.1.1": + version: 0.1.1 + resolution: "@nomicfoundation/solidity-analyzer-win32-arm64-msvc@npm:0.1.1" 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" +"@nomicfoundation/solidity-analyzer-win32-ia32-msvc@npm:0.1.1": + version: 0.1.1 + resolution: "@nomicfoundation/solidity-analyzer-win32-ia32-msvc@npm:0.1.1" 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" +"@nomicfoundation/solidity-analyzer-win32-x64-msvc@npm:0.1.1": + version: 0.1.1 + resolution: "@nomicfoundation/solidity-analyzer-win32-x64-msvc@npm:0.1.1" 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 + version: 0.1.1 + resolution: "@nomicfoundation/solidity-analyzer@npm:0.1.1" + dependencies: + "@nomicfoundation/solidity-analyzer-darwin-arm64": 0.1.1 + "@nomicfoundation/solidity-analyzer-darwin-x64": 0.1.1 + "@nomicfoundation/solidity-analyzer-freebsd-x64": 0.1.1 + "@nomicfoundation/solidity-analyzer-linux-arm64-gnu": 0.1.1 + "@nomicfoundation/solidity-analyzer-linux-arm64-musl": 0.1.1 + "@nomicfoundation/solidity-analyzer-linux-x64-gnu": 0.1.1 + "@nomicfoundation/solidity-analyzer-linux-x64-musl": 0.1.1 + "@nomicfoundation/solidity-analyzer-win32-arm64-msvc": 0.1.1 + "@nomicfoundation/solidity-analyzer-win32-ia32-msvc": 0.1.1 + "@nomicfoundation/solidity-analyzer-win32-x64-msvc": 0.1.1 dependenciesMeta: "@nomicfoundation/solidity-analyzer-darwin-arm64": optional: true @@ -1323,133 +1480,134 @@ __metadata: optional: true "@nomicfoundation/solidity-analyzer-win32-x64-msvc": optional: true - checksum: 42dc5ba40e76bf14945fb6a423554bbbc6c99596675065d7d6f3c9a49ec39e37f3f77ecfedcf906fdb1bb33b033a5d92a90c645c886d6ff23334c8af8b14ff67 + checksum: 038cffafd5769e25256b5b8bef88d95cc1c021274a65c020cf84aceb3237752a3b51645fdb0687f5516a2bdfebf166fcf50b08ab64857925100213e0654b266b languageName: node linkType: hard "@nomiclabs/hardhat-ethers@npm:^2.2.1": - version: 2.2.1 - resolution: "@nomiclabs/hardhat-ethers@npm:2.2.1" + version: 2.2.3 + resolution: "@nomiclabs/hardhat-ethers@npm:2.2.3" peerDependencies: ethers: ^5.0.0 hardhat: ^2.0.0 - checksum: 8cdbf7068f15ee993142ab600074938d05b42af73392a5b12c8eb607a5bca2fac977a6a85955e0b0285541415ad520626e7fb3d33ca7cc112d61ee928358e2f6 - languageName: node - linkType: hard - -"@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 - cbor: ^5.0.2 - chalk: ^2.4.2 - debug: ^4.1.1 - fs-extra: ^7.0.1 - lodash: ^4.17.11 - semver: ^6.3.0 - table: ^6.8.0 - undici: ^5.4.0 - peerDependencies: - hardhat: ^2.0.4 - checksum: 7f225d05fe4d8549472bed2a5c8b8d02e1be652c6c1e00ac83416c048919a2a665d3ad7833c8d6f4513bb6f444e2a165d32a99051b179da7d35aaa1247c32947 + checksum: 72321317e55eb510306e04c42353c5f7ceb42d086fc76cc740120da6e1635b7ad5bbf23a8d6b02bd590754adcf646618933111624085ab249b1ff3482e773226 languageName: node linkType: hard "@nomiclabs/hardhat-waffle@npm:^2.0.3": - version: 2.0.3 - resolution: "@nomiclabs/hardhat-waffle@npm:2.0.3" - dependencies: - "@types/sinon-chai": ^3.2.3 - "@types/web3": 1.0.19 + version: 2.0.6 + resolution: "@nomiclabs/hardhat-waffle@npm:2.0.6" peerDependencies: "@nomiclabs/hardhat-ethers": ^2.0.0 - ethereum-waffle: ^3.2.0 + "@types/sinon-chai": ^3.2.3 + ethereum-waffle: "*" ethers: ^5.0.0 hardhat: ^2.0.0 - checksum: e68c9e13905caa54a10fa68d9ad7dc905c86469c879f91e999a1c4d039dd2f9fa43a8ed90ab975afb14a79cd6badeb60a7cf50dc6564660edd192614c55875ca + checksum: e43592b135739c7f077a9d0a38a479a5512000e58f91d684e6a0d4f0894f8f826821d0b637e2cd7b646669ba12300fcb5e180bcc2473f5cc67d55f44ab809770 languageName: node linkType: hard -"@npmcli/fs@npm:^2.1.0": - version: 2.1.0 - resolution: "@npmcli/fs@npm:2.1.0" +"@npmcli/agent@npm:^2.0.0": + version: 2.2.0 + resolution: "@npmcli/agent@npm:2.2.0" dependencies: - "@gar/promisify": ^1.1.3 - semver: ^7.3.5 - checksum: 6ec6d678af6da49f9dac50cd882d7f661934dd278972ffbaacde40d9eaa2871292d634000a0cca9510f6fc29855fbd4af433e1adbff90a524ec3eaf140f1219b + agent-base: ^7.1.0 + http-proxy-agent: ^7.0.0 + https-proxy-agent: ^7.0.1 + lru-cache: ^10.0.1 + socks-proxy-agent: ^8.0.1 + checksum: 3b25312edbdfaa4089af28e2d423b6f19838b945e47765b0c8174c1395c79d43c3ad6d23cb364b43f59fd3acb02c93e3b493f72ddbe3dfea04c86843a7311fc4 languageName: node linkType: hard -"@npmcli/move-file@npm:^2.0.0": - version: 2.0.0 - resolution: "@npmcli/move-file@npm:2.0.0" +"@npmcli/fs@npm:^3.1.0": + version: 3.1.0 + resolution: "@npmcli/fs@npm:3.1.0" dependencies: - mkdirp: ^1.0.4 - rimraf: ^3.0.2 - checksum: 1388777b507b0c592d53f41b9d182e1a8de7763bc625fc07999b8edbc22325f074e5b3ec90af79c89d6987fdb2325bc66d59f483258543c14a43661621f841b0 + semver: ^7.3.5 + checksum: a50a6818de5fc557d0b0e6f50ec780a7a02ab8ad07e5ac8b16bf519e0ad60a144ac64f97d05c443c3367235d337182e1d012bbac0eb8dbae8dc7b40b193efd0e languageName: node linkType: hard "@openzeppelin/contracts-upgradeable@npm:^4.7.3": - version: 4.7.3 - resolution: "@openzeppelin/contracts-upgradeable@npm:4.7.3" - checksum: c9ffb40cb847a975d440204fc6a811f43af960050242f707332b984d29bd16dc242ffa0935de61867aeb9e0357fadedb16b09b276deda5e9775582face831021 + version: 4.9.5 + resolution: "@openzeppelin/contracts-upgradeable@npm:4.9.5" + checksum: d5d9bad93fe2a88a8336060901d45f185ebfe1fed9844b1fe50e8641d946588c6e69a5a8f1694f0bd073c913b4b9a5c34316acfc17d8f75228adecbcb635ea62 + languageName: node + linkType: hard + +"@openzeppelin/defender-base-client@npm:^1.46.0": + version: 1.54.1 + resolution: "@openzeppelin/defender-base-client@npm:1.54.1" + dependencies: + amazon-cognito-identity-js: ^6.0.1 + async-retry: ^1.3.3 + axios: ^1.4.0 + lodash: ^4.17.19 + node-fetch: ^2.6.0 + checksum: bb44d305b3a7b20ce765bef4c8385c1187f9eca8f2647a99ad55111034da471709e946472ca5f641eb4bd26e1c7ebd19fb9832c29919c36d353a355c009ae98c languageName: node linkType: hard "@openzeppelin/hardhat-upgrades@npm:^1.19.0": - version: 1.19.0 - resolution: "@openzeppelin/hardhat-upgrades@npm:1.19.0" + version: 1.28.0 + resolution: "@openzeppelin/hardhat-upgrades@npm:1.28.0" dependencies: - "@openzeppelin/upgrades-core": ^1.16.0 + "@openzeppelin/defender-base-client": ^1.46.0 + "@openzeppelin/platform-deploy-client": ^0.8.0 + "@openzeppelin/upgrades-core": ^1.27.0 chalk: ^4.1.0 + debug: ^4.1.1 proper-lockfile: ^4.1.1 peerDependencies: "@nomiclabs/hardhat-ethers": ^2.0.0 "@nomiclabs/hardhat-etherscan": ^3.1.0 + ethers: ^5.0.5 hardhat: ^2.0.2 peerDependenciesMeta: "@nomiclabs/harhdat-etherscan": optional: true bin: migrate-oz-cli-project: dist/scripts/migrate-oz-cli-project.js - checksum: eba1149e3164f162b733af40710c99b0ccbe8aad7d5e7fe000b509e946cdee9a42f6ae58eaf868f0af0094e2a83902a7f67bd3801b6e9d1f52fd212949d84a68 + checksum: b37a5eb7c3a5c1fb4ae6754f5fe1d6e93eb6bc143861f57babf5c7d66706ee3e44ca7d57db17ce2ec6c7014f09c269d506f62b3b116897407fdb0d1ff68f4925 languageName: node linkType: hard -"@openzeppelin/upgrades-core@npm:^1.16.0": - version: 1.16.1 - resolution: "@openzeppelin/upgrades-core@npm:1.16.1" +"@openzeppelin/platform-deploy-client@npm:^0.8.0": + version: 0.8.0 + resolution: "@openzeppelin/platform-deploy-client@npm:0.8.0" dependencies: - bn.js: ^5.1.2 - cbor: ^8.0.0 - chalk: ^4.1.0 - compare-versions: ^4.0.0 - debug: ^4.1.1 - ethereumjs-util: ^7.0.3 - proper-lockfile: ^4.1.1 - solidity-ast: ^0.4.15 - checksum: 2cf0bca09f6f0924a1872cb4d97df50382d1defb5770bcdf755cf069efed27727a6ca47206c868cf56411ec5addf63f7811aa5ee4db146ec8308ae400fbf944b + "@ethersproject/abi": ^5.6.3 + "@openzeppelin/defender-base-client": ^1.46.0 + axios: ^0.21.2 + lodash: ^4.17.19 + node-fetch: ^2.6.0 + checksum: 0ce050e185a812c366ceef7dcfce526815babab9396275d9724f324a548ddfdca92ea9913ce61356dcd8c014fc495890c8e21afab4a197e0e14e761c698cce68 languageName: node linkType: hard -"@openzeppelin/upgrades-core@npm:latest": - version: 1.27.1 - resolution: "@openzeppelin/upgrades-core@npm:1.27.1" +"@openzeppelin/upgrades-core@npm:^1.27.0, @openzeppelin/upgrades-core@npm:latest": + version: 1.32.4 + resolution: "@openzeppelin/upgrades-core@npm:1.32.4" dependencies: - cbor: ^8.0.0 + cbor: ^9.0.0 chalk: ^4.1.0 - compare-versions: ^5.0.0 + compare-versions: ^6.0.0 debug: ^4.1.1 ethereumjs-util: ^7.0.3 minimist: ^1.2.7 proper-lockfile: ^4.1.1 - solidity-ast: ^0.4.15 + solidity-ast: ^0.4.51 bin: openzeppelin-upgrades-core: dist/cli/cli.js - checksum: 144d871410e981af106b9f75cf4ff9ab45083b72084c312fcea4acfdd25f06a5ba5ab4709702cad00aa2008f6b1b00533726197c402f067819b8d81d81bb0e8e + checksum: 3efb07adbc8b1c92ca64817f2c48a7312f6c9338d0e79f7a640756aad472be023b201962d79704eb8e1f470a2c50a6c920f0cb860dd2a04c8bc6ea09a0b451c0 + languageName: node + linkType: hard + +"@pkgjs/parseargs@npm:^0.11.0": + version: 0.11.0 + resolution: "@pkgjs/parseargs@npm:0.11.0" + checksum: 6ad6a00fc4f2f2cfc6bff76fb1d88b8ee20bc0601e18ebb01b6d4be583733a860239a521a7fbca73b612e66705078809483549d2b18f370eb346c5155c8e4a0f languageName: node linkType: hard @@ -1571,31 +1729,52 @@ __metadata: languageName: node linkType: hard -"@scure/base@npm:~1.1.0": - version: 1.1.1 - resolution: "@scure/base@npm:1.1.1" - checksum: b4fc810b492693e7e8d0107313ac74c3646970c198bbe26d7332820886fa4f09441991023ec9aa3a2a51246b74409ab5ebae2e8ef148bbc253da79ac49130309 +"@scure/base@npm:~1.1.0, @scure/base@npm:~1.1.4": + version: 1.1.5 + resolution: "@scure/base@npm:1.1.5" + checksum: 9e9ee6088cb3aa0fb91f5a48497d26682c7829df3019b1251d088d166d7a8c0f941c68aaa8e7b96bbad20c71eb210397cb1099062cde3e29d4bad6b975c18519 languageName: node linkType: hard -"@scure/bip32@npm:1.1.0": - version: 1.1.0 - resolution: "@scure/bip32@npm:1.1.0" +"@scure/bip32@npm:1.1.5": + version: 1.1.5 + resolution: "@scure/bip32@npm:1.1.5" dependencies: - "@noble/hashes": ~1.1.1 - "@noble/secp256k1": ~1.6.0 + "@noble/hashes": ~1.2.0 + "@noble/secp256k1": ~1.7.0 "@scure/base": ~1.1.0 - checksum: e6102ab9038896861fca5628b8a97f3c4cb24a073cc9f333c71c747037d82e4423d1d111fd282ba212efaf73cbc5875702567fb4cf13b5f0eb23a5bab402e37e + checksum: b08494ab0d2b1efee7226d1b5100db5157ebea22a78bb87126982a76a186cb3048413e8be0ba2622d00d048a20acbba527af730de86c132a77de616eb9907a3b languageName: node linkType: hard -"@scure/bip39@npm:1.1.0": - version: 1.1.0 - resolution: "@scure/bip39@npm:1.1.0" +"@scure/bip32@npm:1.3.3": + version: 1.3.3 + resolution: "@scure/bip32@npm:1.3.3" + dependencies: + "@noble/curves": ~1.3.0 + "@noble/hashes": ~1.3.2 + "@scure/base": ~1.1.4 + checksum: f939ca733972622fcc1e61d4fdf170a0ad294b24ddb7ed7cdd4c467e1ef283b970154cb101cf5f1a7b64cf5337e917ad31135911dfc36b1d76625320167df2fa + languageName: node + linkType: hard + +"@scure/bip39@npm:1.1.1": + version: 1.1.1 + resolution: "@scure/bip39@npm:1.1.1" dependencies: - "@noble/hashes": ~1.1.1 + "@noble/hashes": ~1.2.0 "@scure/base": ~1.1.0 - checksum: c4361406f092a45e511dc572c89f497af6665ad81cb3fd7bf78e6772f357f7ae885e129ef0b985cb3496a460b4811318f77bc61634d9b0a8446079a801b6003c + checksum: fbb594c50696fa9c14e891d872f382e50a3f919b6c96c55ef2fb10c7102c546dafb8f099a62bd114c12a00525b595dcf7381846f383f0ddcedeaa6e210747d2f + languageName: node + linkType: hard + +"@scure/bip39@npm:1.2.2": + version: 1.2.2 + resolution: "@scure/bip39@npm:1.2.2" + dependencies: + "@noble/hashes": ~1.3.2 + "@scure/base": ~1.1.4 + checksum: cb99505e6d2deef8e55e81df8c563ce8dbfdf1595596dc912bceadcf366c91b05a98130e928ecb090df74efdb20150b64acc4be55bc42768cab4d39a2833d234 languageName: node linkType: hard @@ -1688,21 +1867,51 @@ __metadata: languageName: node linkType: hard -"@solidity-parser/parser@npm:^0.14.0, @solidity-parser/parser@npm:^0.14.1": - version: 0.14.2 - resolution: "@solidity-parser/parser@npm:0.14.2" +"@sindresorhus/is@npm:^4.0.0": + version: 4.6.0 + resolution: "@sindresorhus/is@npm:4.6.0" + checksum: 83839f13da2c29d55c97abc3bc2c55b250d33a0447554997a85c539e058e57b8da092da396e252b11ec24a0279a0bed1f537fa26302209327060643e327f81d2 + languageName: node + linkType: hard + +"@smithy/types@npm:^2.9.1": + version: 2.9.1 + resolution: "@smithy/types@npm:2.9.1" + dependencies: + tslib: ^2.5.0 + checksum: 8570affb4abb5d0ead57293977fc915d44be481120defcabb87a3fb1c7b5d2501b117835eca357b5d54ea4bbee08032f9dc3d909ecbf0abb0cec2ca9678ae7bd + languageName: node + linkType: hard + +"@solidity-parser/parser@npm:^0.14.0": + version: 0.14.5 + resolution: "@solidity-parser/parser@npm:0.14.5" dependencies: antlr4ts: ^0.5.0-alpha.4 - checksum: 7d1dd44beb27e8a6ad2cca6f99d2536eaafd9ec156b6bfff64475e1c85e2e1d3cd84f697e275d7c0742aa365ecade6f68b15ed8cd8b0f339ee239a91fdbda37c + checksum: 9e85a0d4f8a05a11db6022444b70b2f353e2358467b1cce44cdda703ae1e3c7337e1b8cbc2eec8e14a8f34f9c60b42f325e5fe9b3c934cc980e35091e292d7ee languageName: node linkType: hard -"@solidity-parser/parser@npm:^0.14.3": - version: 0.14.3 - resolution: "@solidity-parser/parser@npm:0.14.3" +"@solidity-parser/parser@npm:^0.16.0": + version: 0.16.2 + resolution: "@solidity-parser/parser@npm:0.16.2" dependencies: antlr4ts: ^0.5.0-alpha.4 - checksum: 9a27eb961c22a3b8732bd9782ce3b7912e67b2e2183acada552116dd0bbe637c33265177ab3db9610063da48aa57299d67afdeb0616450b631a0e3da865c4e88 + checksum: 109f7bec5de943c63e444fdde179d9bba6a592c18c836f585753798f428424cfcca72c715e7a345e4b79b83d6548543c9e56cb4b263e9b1e8352af2efcfd224a + languageName: node + linkType: hard + +"@solidity-parser/parser@npm:^0.17.0": + version: 0.17.0 + resolution: "@solidity-parser/parser@npm:0.17.0" + checksum: 2f47732c9a4f6b264ce6c8a0544bd5a0805f824d3c40a8a253e59d5dbe9a98163f55c06460232f57a6b389bb5235c18d0563f94425202ec2f859d88f2378e0ac + languageName: node + linkType: hard + +"@solidity-parser/parser@npm:^0.18.0": + version: 0.18.0 + resolution: "@solidity-parser/parser@npm:0.18.0" + checksum: 970d991529d632862fa88e107531339d84df35bf0374e31e8215ce301b19a01ede33fccf4d374402649814263f8bc278a8e6d62a0129bb877539fbdd16a604cc languageName: node linkType: hard @@ -1715,10 +1924,12 @@ __metadata: languageName: node linkType: hard -"@tootallnate/once@npm:2": - version: 2.0.0 - resolution: "@tootallnate/once@npm:2.0.0" - checksum: ad87447820dd3f24825d2d947ebc03072b20a42bfc96cbafec16bff8bbda6c1a81fcb0be56d5b21968560c5359a0af4038a68ba150c3e1694fe4c109a063bed8 +"@szmarczak/http-timer@npm:^4.0.5": + version: 4.0.6 + resolution: "@szmarczak/http-timer@npm:4.0.6" + dependencies: + defer-to-connect: ^2.0.0 + checksum: c29df3bcec6fc3bdec2b17981d89d9c9fc9bd7d0c9bcfe92821dc533f4440bc890ccde79971838b4ceed1921d456973c4180d7175ee1d0023ad0562240a58d95 languageName: node linkType: hard @@ -1744,26 +1955,25 @@ __metadata: linkType: hard "@tsconfig/node16@npm:^1.0.2": - version: 1.0.3 - resolution: "@tsconfig/node16@npm:1.0.3" - checksum: 3a8b657dd047495b7ad23437d6afd20297ce90380ff0bdee93fc7d39a900dbd8d9e26e53ff6b465e7967ce2adf0b218782590ce9013285121e6a5928fbd6819f + version: 1.0.4 + resolution: "@tsconfig/node16@npm:1.0.4" + checksum: 202319785901f942a6e1e476b872d421baec20cf09f4b266a1854060efbf78cde16a4d256e8bc949d31e6cd9a90f1e8ef8fb06af96a65e98338a2b6b0de0a0ff languageName: node linkType: hard "@typechain/ethers-v5@npm:^10.1.0": - version: 10.1.0 - resolution: "@typechain/ethers-v5@npm:10.1.0" + version: 10.2.1 + resolution: "@typechain/ethers-v5@npm:10.2.1" dependencies: lodash: ^4.17.15 ts-essentials: ^7.0.1 peerDependencies: "@ethersproject/abi": ^5.0.0 - "@ethersproject/bytes": ^5.0.0 "@ethersproject/providers": ^5.0.0 ethers: ^5.1.3 - typechain: ^8.1.0 + typechain: ^8.1.1 typescript: ">=4.3.0" - checksum: 452ad685ce792ddefebdcb8ae72f4343ed1411f91701250e273c1a0dd79753d0d40f311cff19ec0c4fd6a9b9cf23e55eb9ae1b94cb388fbc54c122ae4d0e6f47 + checksum: 852da4b1ff368ef87251111a5d50077de3d0fc12c519529269a74223740f8bda89297e67a5eb6c1f5b04ee23119566d6cbccf58264d32a83132be0f328a58d22 languageName: node linkType: hard @@ -1780,35 +1990,27 @@ __metadata: linkType: hard "@typechain/hardhat@npm:^6.1.2": - version: 6.1.2 - resolution: "@typechain/hardhat@npm:6.1.2" + version: 6.1.6 + resolution: "@typechain/hardhat@npm:6.1.6" dependencies: fs-extra: ^9.1.0 - lodash: ^4.17.15 peerDependencies: "@ethersproject/abi": ^5.4.7 "@ethersproject/providers": ^5.4.7 - "@typechain/ethers-v5": ^10.1.0 + "@typechain/ethers-v5": ^10.2.1 ethers: ^5.4.7 hardhat: ^2.9.9 - typechain: ^8.1.0 - checksum: 875753de831825c95c30e6792949f38f69fcee888c1936042414c8d030ecc181817cc0f5cbf6f1764f30779b5f840201eb29addda45713fd0b58a71693102a2c + typechain: ^8.1.1 + checksum: f214bebf7860956230478cb92696ba757829cfd9dc65ac99c3bc7e539378310318d92b009054186f446595c8ffc1a81e9c6d028da0eb04253253049ea1b6e8d3 languageName: node linkType: hard "@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 - -"@types/bn.js@npm:*, @types/bn.js@npm:^5.1.0": - version: 5.1.0 - resolution: "@types/bn.js@npm:5.1.0" + version: 0.2.4 + resolution: "@types/async-eventemitter@npm:0.2.4" dependencies: - "@types/node": "*" - checksum: 1dc1cbbd7a1e8bf3614752e9602f558762a901031f499f3055828b5e3e2bba16e5b88c27b3c4152ad795248fbe4086c731a5c4b0f29bb243f1875beeeabee59c + "@types/events": "*" + checksum: cee62e258cf02a45688a3f6f517b623270a9c2779dfd2f53b52e0efbcb0282d7078c3ce1fafb2af257aefdb892acc09ba51d93647930885414ec719437430bf7 languageName: node linkType: hard @@ -1821,19 +2023,40 @@ __metadata: languageName: node linkType: hard +"@types/bn.js@npm:^5.1.0": + version: 5.1.5 + resolution: "@types/bn.js@npm:5.1.5" + dependencies: + "@types/node": "*" + checksum: c87b28c4af74545624f8a3dae5294b16aa190c222626e8d4b2e327b33b1a3f1eeb43e7a24d914a9774bca43d8cd6e1cb0325c1f4b3a244af6693a024e1d918e6 + languageName: node + linkType: hard + +"@types/cacheable-request@npm:^6.0.1": + version: 6.0.3 + resolution: "@types/cacheable-request@npm:6.0.3" + dependencies: + "@types/http-cache-semantics": "*" + "@types/keyv": ^3.1.4 + "@types/node": "*" + "@types/responselike": ^1.0.0 + checksum: d9b26403fe65ce6b0cb3720b7030104c352bcb37e4fac2a7089a25a97de59c355fa08940658751f2f347a8512aa9d18fdb66ab3ade835975b2f454f2d5befbd9 + 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" + version: 7.1.8 + resolution: "@types/chai-as-promised@npm:7.1.8" dependencies: "@types/chai": "*" - checksum: 7c1345c6e32513d52d8e562ec173c23161648d6b792046525f18803a9932d7b3ad3dca8f0181e3c529ec42b106099f174e34edeb184d61dc93e32c98b5132fd4 + checksum: f0e5eab451b91bc1e289ed89519faf6591932e8a28d2ec9bbe95826eb73d28fe43713633e0c18706f3baa560a7d97e7c7c20dc53ce639e5d75bac46b2a50bf21 languageName: node linkType: hard "@types/chai@npm:*, @types/chai@npm:^4.3.1": - version: 4.3.1 - resolution: "@types/chai@npm:4.3.1" - checksum: 2ee246b76c469cd620a7a1876a73bc597074361b67d547b4bd96a0c1adb43597ede2d8589ab626192e14349d83cbb646cc11e2c179eeeb43ff11596de94d82c4 + version: 4.3.11 + resolution: "@types/chai@npm:4.3.11" + checksum: d0c05fe5d02b2e6bbca2bd4866a2ab20a59cf729bc04af0060e7a3277eaf2fb65651b90d4c74b0ebf1d152b4b1d49fa8e44143acef276a2bbaa7785fbe5642d3 languageName: node linkType: hard @@ -1847,11 +2070,11 @@ __metadata: linkType: hard "@types/connect@npm:^3.4.33": - version: 3.4.35 - resolution: "@types/connect@npm:3.4.35" + version: 3.4.38 + resolution: "@types/connect@npm:3.4.38" dependencies: "@types/node": "*" - checksum: fe81351470f2d3165e8b12ce33542eef89ea893e36dd62e8f7d72566dfb7e448376ae962f9f3ea888547ce8b55a40020ca0e01d637fab5d99567673084542641 + checksum: 7eb1bc5342a9604facd57598a6c62621e244822442976c443efb84ff745246b10d06e8b309b6e80130026a396f19bf6793b7cecd7380169f369dac3bfc46fb99 languageName: node linkType: hard @@ -1862,6 +2085,13 @@ __metadata: languageName: node linkType: hard +"@types/events@npm:*": + version: 3.0.3 + resolution: "@types/events@npm:3.0.3" + checksum: 50af9312fab001fd6bd4bb3ff65830f940877e6778de140a92481a0d9bf5f4853d44ec758a8800ef60e0598ac43ed1b5688116a3c65906ae54e989278d6c7c82 + languageName: node + linkType: hard + "@types/form-data@npm:0.0.33": version: 0.0.33 resolution: "@types/form-data@npm:0.0.33" @@ -1881,17 +2111,17 @@ __metadata: languageName: node linkType: hard -"@types/json-schema@npm:^7.0.3": - version: 7.0.12 - resolution: "@types/json-schema@npm:7.0.12" - checksum: 00239e97234eeb5ceefb0c1875d98ade6e922bfec39dd365ec6bd360b5c2f825e612ac4f6e5f1d13601b8b30f378f15e6faa805a3a732f4a1bbe61915163d293 +"@types/http-cache-semantics@npm:*": + version: 4.0.4 + resolution: "@types/http-cache-semantics@npm:4.0.4" + checksum: 7f4dd832e618bc1e271be49717d7b4066d77c2d4eed5b81198eb987e532bb3e1c7e02f45d77918185bad936f884b700c10cebe06305f50400f382ab75055f9e8 languageName: node linkType: hard -"@types/json-schema@npm:^7.0.9": - version: 7.0.11 - resolution: "@types/json-schema@npm:7.0.11" - checksum: 527bddfe62db9012fccd7627794bd4c71beb77601861055d87e3ee464f2217c85fca7a4b56ae677478367bbd248dbde13553312b7d4dbc702a2f2bbf60c4018d +"@types/json-schema@npm:^7.0.3, @types/json-schema@npm:^7.0.9": + version: 7.0.15 + resolution: "@types/json-schema@npm:7.0.15" + checksum: 97ed0cb44d4070aecea772b7b2e2ed971e10c81ec87dd4ecc160322ffa55ff330dace1793489540e3e318d90942064bb697cc0f8989391797792d919737b3b98 languageName: node linkType: hard @@ -1902,7 +2132,7 @@ __metadata: languageName: node linkType: hard -"@types/keyv@npm:^3.1.1": +"@types/keyv@npm:^3.1.1, @types/keyv@npm:^3.1.4": version: 3.1.4 resolution: "@types/keyv@npm:3.1.4" dependencies: @@ -1925,7 +2155,14 @@ __metadata: languageName: node linkType: hard -"@types/minimatch@npm:*, @types/minimatch@npm:^3.0.4": +"@types/minimatch@npm:*": + version: 5.1.2 + resolution: "@types/minimatch@npm:5.1.2" + checksum: 0391a282860c7cb6fe262c12b99564732401bdaa5e395bee9ca323c312c1a0f45efbf34dce974682036e857db59a5c9b1da522f3d6055aeead7097264c8705a8 + languageName: node + linkType: hard + +"@types/minimatch@npm:^3.0.4": version: 3.0.5 resolution: "@types/minimatch@npm:3.0.5" checksum: c41d136f67231c3131cf1d4ca0b06687f4a322918a3a5adddc87ce90ed9dbd175a3610adee36b106ae68c0b92c637c35e02b58c8a56c424f71d30993ea220b92 @@ -1949,26 +2186,21 @@ __metadata: linkType: hard "@types/node-fetch@npm:^2.5.5": - version: 2.6.2 - resolution: "@types/node-fetch@npm:2.6.2" + version: 2.6.11 + resolution: "@types/node-fetch@npm:2.6.11" dependencies: "@types/node": "*" - form-data: ^3.0.0 - checksum: 6f73b1470000d303d25a6fb92875ea837a216656cb7474f66cdd67bb014aa81a5a11e7ac9c21fe19bee9ecb2ef87c1962bceeaec31386119d1ac86e4c30ad7a6 + form-data: ^4.0.0 + checksum: 180e4d44c432839bdf8a25251ef8c47d51e37355ddd78c64695225de8bc5dc2b50b7bb855956d471c026bb84bd7295688a0960085e7158cbbba803053492568b languageName: node linkType: hard -"@types/node@npm:*": - version: 18.0.0 - resolution: "@types/node@npm:18.0.0" - checksum: aab2b325727a2599f6d25ebe0dedf58c40fb66a51ce4ca9c0226ceb70fcda2d3afccdca29db5942eb48b158ee8585a274a1e3750c718bbd5399d7f41d62dfdcc - languageName: node - linkType: hard - -"@types/node@npm:>=13.7.0": - version: 20.5.6 - resolution: "@types/node@npm:20.5.6" - checksum: d2ce44f1cfa3fd00fe7426f7cf9a46d680cd57802b874ed5618e7d9101a9c6b8de37f08c0e7185ee06fb363ad492549c3ea69665c7e8e31c7813210ed8e89005 +"@types/node@npm:*, @types/node@npm:>=13.7.0": + version: 20.11.16 + resolution: "@types/node@npm:20.11.16" + dependencies: + undici-types: ~5.26.4 + checksum: 51f0831c1219bf4698e7430aeb9892237bd851deeb25ce23c5bb0ceefcc77c3b114e48f4e98d9fc26def5a87ba9d8079f0281dd37bee691140a93f133812c152 languageName: node linkType: hard @@ -1987,9 +2219,11 @@ __metadata: linkType: hard "@types/node@npm:^18.6.1": - version: 18.6.1 - resolution: "@types/node@npm:18.6.1" - checksum: 06331f68bc73d93b73977ae19ca87818e004bc7b8e70b0d4f73b0ea5e341a4a1ba6f0e79db8fb75a841af2b8d74552d6b9a8f846b267954689f57effedcf5d77 + version: 18.19.14 + resolution: "@types/node@npm:18.19.14" + dependencies: + undici-types: ~5.26.4 + checksum: 3d42b50e649f18c6ca7044714eaeb51ba5fda463c845eeb1973bcbbfcab8e93179501fbf865e675cb0c7a5e59f7ea18eca8296b52c2455c856aa45c77ae815dc languageName: node linkType: hard @@ -2001,32 +2235,42 @@ __metadata: linkType: hard "@types/parse-json@npm:^4.0.0": - version: 4.0.0 - resolution: "@types/parse-json@npm:4.0.0" - checksum: fd6bce2b674b6efc3db4c7c3d336bd70c90838e8439de639b909ce22f3720d21344f52427f1d9e57b265fcb7f6c018699b99e5e0c208a1a4823014269a6bf35b + version: 4.0.2 + resolution: "@types/parse-json@npm:4.0.2" + checksum: 5bf62eec37c332ad10059252fc0dab7e7da730764869c980b0714777ad3d065e490627be9f40fc52f238ffa3ac4199b19de4127196910576c2fe34dd47c7a470 languageName: node linkType: hard "@types/pbkdf2@npm:^3.0.0": - version: 3.1.0 - resolution: "@types/pbkdf2@npm:3.1.0" + version: 3.1.2 + resolution: "@types/pbkdf2@npm:3.1.2" dependencies: "@types/node": "*" - checksum: d15024b1957c21cf3b8887329d9bd8dfde754cf13a09d76ae25f1391cfc62bb8b8d7b760773c5dbaa748172fba8b3e0c3dbe962af6ccbd69b76df12a48dfba40 + checksum: bebe1e596cbbe5f7d2726a58859e61986c5a42459048e29cb7f2d4d764be6bbb0844572fd5d70ca8955a8a17e8b4ed80984fc4903e165d9efb8807a3fbb051aa languageName: node linkType: hard "@types/prettier@npm:^2.1.1": - version: 2.6.3 - resolution: "@types/prettier@npm:2.6.3" - checksum: e1836699ca189fff6d2a73dc22e028b6a6f693ed1180d5998ac29fa197caf8f85aa92cb38db642e4a370e616b451cb5722ad2395dab11c78e025a1455f37d1f0 + version: 2.7.3 + resolution: "@types/prettier@npm:2.7.3" + checksum: 705384209cea6d1433ff6c187c80dcc0b95d99d5c5ce21a46a9a58060c527973506822e428789d842761e0280d25e3359300f017fbe77b9755bc772ab3dc2f83 languageName: node linkType: hard "@types/qs@npm:^6.2.31": - version: 6.9.7 - resolution: "@types/qs@npm:6.9.7" - checksum: 7fd6f9c25053e9b5bb6bc9f9f76c1d89e6c04f7707a7ba0e44cc01f17ef5284adb82f230f542c2d5557d69407c9a40f0f3515e8319afd14e1e16b5543ac6cdba + version: 6.9.11 + resolution: "@types/qs@npm:6.9.11" + checksum: 620ca1628bf3da65662c54ed6ebb120b18a3da477d0bfcc872b696685a9bb1893c3c92b53a1190a8f54d52eaddb6af8b2157755699ac83164604329935e8a7f2 + languageName: node + linkType: hard + +"@types/readable-stream@npm:^2.3.13": + version: 2.3.15 + resolution: "@types/readable-stream@npm:2.3.15" + dependencies: + "@types/node": "*" + safe-buffer: ~5.1.1 + checksum: ec36f525cad09b6c65a1dafcb5ad99b9e2ed824ec49b7aa23180ac427e5d35b8a0706193ecd79ab4253a283ad485ba03d5917a98daaaa144f0ea34f4823e9d82 languageName: node linkType: hard @@ -2040,63 +2284,53 @@ __metadata: linkType: hard "@types/responselike@npm:^1.0.0": - version: 1.0.0 - resolution: "@types/responselike@npm:1.0.0" + version: 1.0.3 + resolution: "@types/responselike@npm:1.0.3" dependencies: "@types/node": "*" - checksum: e99fc7cc6265407987b30deda54c1c24bb1478803faf6037557a774b2f034c5b097ffd65847daa87e82a61a250d919f35c3588654b0fdaa816906650f596d1b0 + checksum: 6ac4b35723429b11b117e813c7acc42c3af8b5554caaf1fc750404c1ae59f9b7376bc69b9e9e194a5a97357a597c2228b7173d317320f0360d617b6425212f58 languageName: node linkType: hard "@types/secp256k1@npm:^4.0.1": - version: 4.0.3 - resolution: "@types/secp256k1@npm:4.0.3" + version: 4.0.6 + resolution: "@types/secp256k1@npm:4.0.6" dependencies: "@types/node": "*" - checksum: 1bd10b9afa724084b655dc81b7b315def3d2d0e272014ef16009fa76e17537411c07c0695fdea412bc7b36d2a02687f5fea33522d55b8ef29eda42992f812913 + checksum: 984494caf49a4ce99fda2b9ea1840eb47af946b8c2737314108949bcc0c06b4880e871296bd49ed6ea4c8423e3a302ad79fec43abfc987330e7eb98f0c4e8ba4 languageName: node linkType: hard -"@types/sinon-chai@npm:^3.2.3": - version: 3.2.8 - resolution: "@types/sinon-chai@npm:3.2.8" +"@types/semver@npm:^7.3.12": + version: 7.5.6 + resolution: "@types/semver@npm:7.5.6" + checksum: 563a0120ec0efcc326567db2ed920d5d98346f3638b6324ea6b50222b96f02a8add3c51a916b6897b51523aad8ac227d21d3dcf8913559f1bfc6c15b14d23037 + languageName: node + linkType: hard + +"@types/sinon-chai@npm:^3.2.12": + version: 3.2.12 + resolution: "@types/sinon-chai@npm:3.2.12" dependencies: "@types/chai": "*" "@types/sinon": "*" - checksum: a0f7a8cef24904db25a695f3c3adcc03ae72bab89a954c9b6e23fe7e541228e67fe4119cec069e8b36c80e9af33102b626129ff538efade9391cc0f65f1d4933 + checksum: d906f2f766613534c5e9fe1437ec740fb6a9a550f02d1a0abe180c5f18fe73a99f0c12935195404d42f079f5f72a371e16b81e2aef963a6ef0ee0ed9d5d7f391 languageName: node linkType: hard "@types/sinon@npm:*": - version: 10.0.12 - resolution: "@types/sinon@npm:10.0.12" + version: 17.0.3 + resolution: "@types/sinon@npm:17.0.3" dependencies: "@types/sinonjs__fake-timers": "*" - checksum: a37cac3f53d989140c1655488d7afa8f04ea1977337f5475671f5bce1eb0530fced7927ca0493a7ba3b79696ea3dd19e6e8918c8676180386c32a2bb2d1ae0b5 + checksum: c8e9956d9c90fe1ec1cc43085ae48897f93f9ea86e909ab47f255ea71f5229651faa070393950fb6923aef426c84e92b375503f9f8886ef44668b82a8ee49e9a languageName: node linkType: hard "@types/sinonjs__fake-timers@npm:*": - version: 8.1.2 - resolution: "@types/sinonjs__fake-timers@npm:8.1.2" - checksum: bbc73a5ab6c0ec974929392f3d6e1e8db4ebad97ec506d785301e1c3d8a4f98a35b1aa95b97035daef02886fd8efd7788a2fa3ced2ec7105988bfd8dce61eedd - languageName: node - linkType: hard - -"@types/underscore@npm:*": - version: 1.11.4 - resolution: "@types/underscore@npm:1.11.4" - checksum: db9f8486bc851b732259e51f42d62aad1ae2158be5724612dc125ece5f5d61c51447f9dea28284c2a0f79cb95e788d01cb5ce97709880019213e69fab0dd1696 - languageName: node - linkType: hard - -"@types/web3@npm:1.0.19": - version: 1.0.19 - resolution: "@types/web3@npm:1.0.19" - dependencies: - "@types/bn.js": "*" - "@types/underscore": "*" - checksum: 25a78e80052cca8abe5edf15c0ae92854d00d1bec15283486a2535ab673345b0be090e39cc9a86822be17ddd812fa76cbfd869be21bb944d2faaf2922e2a836a + version: 8.1.5 + resolution: "@types/sinonjs__fake-timers@npm:8.1.5" + checksum: 7e3c08f6c13df44f3ea7d9a5155ddf77e3f7314c156fa1c5a829a4f3763bafe2f75b1283b887f06e6b4296996a2f299b70f64ff82625f9af5885436e2524d10c languageName: node linkType: hard @@ -2128,16 +2362,17 @@ __metadata: linkType: hard "@typescript-eslint/eslint-plugin@npm:^5.0.0": - version: 5.31.0 - resolution: "@typescript-eslint/eslint-plugin@npm:5.31.0" + version: 5.62.0 + resolution: "@typescript-eslint/eslint-plugin@npm:5.62.0" dependencies: - "@typescript-eslint/scope-manager": 5.31.0 - "@typescript-eslint/type-utils": 5.31.0 - "@typescript-eslint/utils": 5.31.0 + "@eslint-community/regexpp": ^4.4.0 + "@typescript-eslint/scope-manager": 5.62.0 + "@typescript-eslint/type-utils": 5.62.0 + "@typescript-eslint/utils": 5.62.0 debug: ^4.3.4 - functional-red-black-tree: ^1.0.1 + graphemer: ^1.4.0 ignore: ^5.2.0 - regexpp: ^3.2.0 + natural-compare-lite: ^1.4.0 semver: ^7.3.7 tsutils: ^3.21.0 peerDependencies: @@ -2146,7 +2381,7 @@ __metadata: peerDependenciesMeta: typescript: optional: true - checksum: a6d007e6cc6c7204b9ce09dd6670a5a29f8b75417a84c8238d1dd7fc3bfa4a7294beb961a0ba76e610b695a0c80edd4186803429e3605a21562c23e47b8efa37 + checksum: fc104b389c768f9fa7d45a48c86d5c1ad522c1d0512943e782a56b1e3096b2cbcc1eea3fcc590647bf0658eef61aac35120a9c6daf979bf629ad2956deb516a1 languageName: node linkType: hard @@ -2182,37 +2417,38 @@ __metadata: linkType: hard "@typescript-eslint/parser@npm:^5.0.0": - version: 5.31.0 - resolution: "@typescript-eslint/parser@npm:5.31.0" + version: 5.62.0 + resolution: "@typescript-eslint/parser@npm:5.62.0" dependencies: - "@typescript-eslint/scope-manager": 5.31.0 - "@typescript-eslint/types": 5.31.0 - "@typescript-eslint/typescript-estree": 5.31.0 + "@typescript-eslint/scope-manager": 5.62.0 + "@typescript-eslint/types": 5.62.0 + "@typescript-eslint/typescript-estree": 5.62.0 debug: ^4.3.4 peerDependencies: eslint: ^6.0.0 || ^7.0.0 || ^8.0.0 peerDependenciesMeta: typescript: optional: true - checksum: ae842105ff0e5811d54c9c020ee0568170c13f401de293eb4caa2106f3060558773b496b5647f2b80b2969a2890135c054f50e2443a13c3705d5965aa12896c0 + checksum: d168f4c7f21a7a63f47002e2d319bcbb6173597af5c60c1cf2de046b46c76b4930a093619e69faf2d30214c29ab27b54dcf1efc7046a6a6bd6f37f59a990e752 languageName: node linkType: hard -"@typescript-eslint/scope-manager@npm:5.31.0": - version: 5.31.0 - resolution: "@typescript-eslint/scope-manager@npm:5.31.0" +"@typescript-eslint/scope-manager@npm:5.62.0": + version: 5.62.0 + resolution: "@typescript-eslint/scope-manager@npm:5.62.0" dependencies: - "@typescript-eslint/types": 5.31.0 - "@typescript-eslint/visitor-keys": 5.31.0 - checksum: f771adf54a7cf6387bb201a0d4bef598425818c38832cabbf33c369b3fb650932cbb81a28f198727f3ffae5e21445dde710c41c624bd10b3b7283249333b625b + "@typescript-eslint/types": 5.62.0 + "@typescript-eslint/visitor-keys": 5.62.0 + checksum: 6062d6b797fe1ce4d275bb0d17204c827494af59b5eaf09d8a78cdd39dadddb31074dded4297aaf5d0f839016d601032857698b0e4516c86a41207de606e9573 languageName: node linkType: hard -"@typescript-eslint/type-utils@npm:5.31.0": - version: 5.31.0 - resolution: "@typescript-eslint/type-utils@npm:5.31.0" +"@typescript-eslint/type-utils@npm:5.62.0": + version: 5.62.0 + resolution: "@typescript-eslint/type-utils@npm:5.62.0" dependencies: - "@typescript-eslint/utils": 5.31.0 + "@typescript-eslint/typescript-estree": 5.62.0 + "@typescript-eslint/utils": 5.62.0 debug: ^4.3.4 tsutils: ^3.21.0 peerDependencies: @@ -2220,14 +2456,14 @@ __metadata: peerDependenciesMeta: typescript: optional: true - checksum: 1e98a6952207cf7d19cdac375a69bcfed953a29746fa1f2b3c7a8c9376c6984c0bb52506539b76d6a9bebc33966c825f032a27859e545447890562dd3c05ef31 + checksum: fc41eece5f315dfda14320be0da78d3a971d650ea41300be7196934b9715f3fe1120a80207551eb71d39568275dbbcf359bde540d1ca1439d8be15e9885d2739 languageName: node linkType: hard -"@typescript-eslint/types@npm:5.31.0": - version: 5.31.0 - resolution: "@typescript-eslint/types@npm:5.31.0" - checksum: 1c4223a7dcbeb2fb52dc723ac366e2cc75549b21d71f5de8515e86e48d13324e4e136e75804e0f71aff56c9936ef494fa4d1e3eb2f189ed60cf8e2c7401ce372 +"@typescript-eslint/types@npm:5.62.0": + version: 5.62.0 + resolution: "@typescript-eslint/types@npm:5.62.0" + checksum: 48c87117383d1864766486f24de34086155532b070f6264e09d0e6139449270f8a9559cfef3c56d16e3bcfb52d83d42105d61b36743626399c7c2b5e0ac3b670 languageName: node linkType: hard @@ -2249,12 +2485,12 @@ __metadata: languageName: node linkType: hard -"@typescript-eslint/typescript-estree@npm:5.31.0": - version: 5.31.0 - resolution: "@typescript-eslint/typescript-estree@npm:5.31.0" +"@typescript-eslint/typescript-estree@npm:5.62.0": + version: 5.62.0 + resolution: "@typescript-eslint/typescript-estree@npm:5.62.0" dependencies: - "@typescript-eslint/types": 5.31.0 - "@typescript-eslint/visitor-keys": 5.31.0 + "@typescript-eslint/types": 5.62.0 + "@typescript-eslint/visitor-keys": 5.62.0 debug: ^4.3.4 globby: ^11.1.0 is-glob: ^4.0.3 @@ -2263,40 +2499,42 @@ __metadata: peerDependenciesMeta: typescript: optional: true - checksum: 921c502ac4c93df9342d29636b384e154c3ac714e2be0308a4c9d3337d24d8b4721b76cbe700f70c7ceef06b50dfc404e4d4d734e446fe319bac030cb653d7b4 + checksum: 3624520abb5807ed8f57b1197e61c7b1ed770c56dfcaca66372d584ff50175225798bccb701f7ef129d62c5989070e1ee3a0aa2d84e56d9524dcf011a2bb1a52 languageName: node linkType: hard -"@typescript-eslint/utils@npm:5.31.0": - version: 5.31.0 - resolution: "@typescript-eslint/utils@npm:5.31.0" +"@typescript-eslint/utils@npm:5.62.0": + version: 5.62.0 + resolution: "@typescript-eslint/utils@npm:5.62.0" dependencies: + "@eslint-community/eslint-utils": ^4.2.0 "@types/json-schema": ^7.0.9 - "@typescript-eslint/scope-manager": 5.31.0 - "@typescript-eslint/types": 5.31.0 - "@typescript-eslint/typescript-estree": 5.31.0 + "@types/semver": ^7.3.12 + "@typescript-eslint/scope-manager": 5.62.0 + "@typescript-eslint/types": 5.62.0 + "@typescript-eslint/typescript-estree": 5.62.0 eslint-scope: ^5.1.1 - eslint-utils: ^3.0.0 + semver: ^7.3.7 peerDependencies: eslint: ^6.0.0 || ^7.0.0 || ^8.0.0 - checksum: 2a4200fd8812f7d7dfbe381d856e97da3606f0c59de78829edd297cc76b4851316bf8362b65e66c7db399e9ea31ec71943626ec12022a552bcb7bb591259ec49 + checksum: ee9398c8c5db6d1da09463ca7bf36ed134361e20131ea354b2da16a5fdb6df9ba70c62a388d19f6eebb421af1786dbbd79ba95ddd6ab287324fc171c3e28d931 languageName: node linkType: hard -"@typescript-eslint/visitor-keys@npm:5.31.0": - version: 5.31.0 - resolution: "@typescript-eslint/visitor-keys@npm:5.31.0" +"@typescript-eslint/visitor-keys@npm:5.62.0": + version: 5.62.0 + resolution: "@typescript-eslint/visitor-keys@npm:5.62.0" dependencies: - "@typescript-eslint/types": 5.31.0 + "@typescript-eslint/types": 5.62.0 eslint-visitor-keys: ^3.3.0 - checksum: 24ff3b9037b8fafe4f240b1c8a91981d658cd12a019f7961c9fe2f1d4dc84cf64e4071d865073191181b46652f4bd8f8cfc8e053ed8737ba1b9aede3e3252b3d + checksum: 976b05d103fe8335bef5c93ad3f76d781e3ce50329c0243ee0f00c0fcfb186c81df50e64bfdd34970148113f8ade90887f53e3c4938183afba830b4ba8e30a35 languageName: node linkType: hard -"@ungap/promise-all-settled@npm:1.1.2": - version: 1.1.2 - resolution: "@ungap/promise-all-settled@npm:1.1.2" - checksum: 08d37fdfa23a6fe8139f1305313562ebad973f3fac01bcce2773b2bda5bcb0146dfdcf3cb6a722cf0a5f2ca0bc56a827eac8f1e7b3beddc548f654addf1fc34c +"@ungap/structured-clone@npm:^1.2.0": + version: 1.2.0 + resolution: "@ungap/structured-clone@npm:1.2.0" + checksum: 4f656b7b4672f2ce6e272f2427d8b0824ed11546a601d8d5412b9d7704e83db38a8d9f402ecdf2b9063fc164af842ad0ec4a55819f621ed7e7ea4d1efcc74524 languageName: node linkType: hard @@ -2345,6 +2583,13 @@ __metadata: languageName: node linkType: hard +"abbrev@npm:^2.0.0": + version: 2.0.0 + resolution: "abbrev@npm:2.0.0" + checksum: 0e994ad2aa6575f94670d8a2149afe94465de9cedaaaac364e7fb43a40c3691c980ff74899f682f4ca58fa96b4cbd7421a015d3a6defe43a442117d7821a2f36 + languageName: node + linkType: hard + "abort-controller@npm:^3.0.0": version: 3.0.0 resolution: "abort-controller@npm:3.0.0" @@ -2354,9 +2599,9 @@ __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" +"abstract-level@npm:^1.0.0, abstract-level@npm:^1.0.2, abstract-level@npm:^1.0.3, abstract-level@npm:^1.0.4": + version: 1.0.4 + resolution: "abstract-level@npm:1.0.4" dependencies: buffer: ^6.0.3 catering: ^2.1.0 @@ -2365,7 +2610,7 @@ __metadata: level-transcoder: ^1.0.1 module-error: ^1.0.1 queue-microtask: ^1.2.3 - checksum: 70d61a3924526ebc257b138992052f9ff571a6cee5a7660836e37a1cc7081273c3acf465dd2f5e1897b38dc743a6fd9dba14a5d8a2a9d39e5787cd3da99f301d + checksum: 5b70d08926f5234c3c23ffca848542af4def780dc96d6ca35a81659af80e6439c46f5106bd14a5ea37465173d7dcc8294317048b0194fdf6480103edc4aa9e63 languageName: node linkType: hard @@ -2415,7 +2660,7 @@ __metadata: languageName: node linkType: hard -"acorn-jsx@npm:^5.0.0, acorn-jsx@npm:^5.2.0, acorn-jsx@npm:^5.3.2": +"acorn-jsx@npm:^5.2.0, acorn-jsx@npm:^5.3.2": version: 5.3.2 resolution: "acorn-jsx@npm:5.3.2" peerDependencies: @@ -2425,18 +2670,9 @@ __metadata: linkType: hard "acorn-walk@npm:^8.1.1": - version: 8.2.0 - resolution: "acorn-walk@npm:8.2.0" - checksum: 1715e76c01dd7b2d4ca472f9c58968516a4899378a63ad5b6c2d668bba8da21a71976c14ec5f5b75f887b6317c4ae0b897ab141c831d741dc76024d8745f1ad1 - languageName: node - linkType: hard - -"acorn@npm:^6.0.7": - version: 6.4.2 - resolution: "acorn@npm:6.4.2" - bin: - acorn: bin/acorn - checksum: 44b07053729db7f44d28343eed32247ed56dc4a6ec6dff2b743141ecd6b861406bbc1c20bf9d4f143ea7dd08add5dc8c290582756539bc03a8db605050ce2fb4 + version: 8.3.2 + resolution: "acorn-walk@npm:8.3.2" + checksum: 3626b9d26a37b1b427796feaa5261faf712307a8920392c8dce9a5739fb31077667f4ad2ec71c7ac6aaf9f61f04a9d3d67ff56f459587206fc04aa31c27ef392 languageName: node linkType: hard @@ -2449,28 +2685,19 @@ __metadata: languageName: node linkType: hard -"acorn@npm:^8.4.1": - version: 8.7.1 - resolution: "acorn@npm:8.7.1" - bin: - acorn: bin/acorn - checksum: aca0aabf98826717920ac2583fdcad0a6fbe4e583fdb6e843af2594e907455aeafe30b1e14f1757cd83ce1776773cf8296ffc3a4acf13f0bd3dfebcf1db6ae80 - languageName: node - linkType: hard - -"acorn@npm:^8.7.1": - version: 8.8.0 - resolution: "acorn@npm:8.8.0" +"acorn@npm:^8.4.1, acorn@npm:^8.9.0": + version: 8.11.3 + resolution: "acorn@npm:8.11.3" bin: acorn: bin/acorn - checksum: 7270ca82b242eafe5687a11fea6e088c960af712683756abf0791b68855ea9cace3057bd5e998ffcef50c944810c1e0ca1da526d02b32110e13c722aa959afdc + checksum: 76d8e7d559512566b43ab4aadc374f11f563f0a9e21626dd59cb2888444e9445923ae9f3699972767f18af61df89cd89f5eaaf772d1327b055b45cb829b4a88c languageName: node linkType: hard "address@npm:^1.0.1": - version: 1.2.0 - resolution: "address@npm:1.2.0" - checksum: 2ef3aa9d23bbe0f9f2745a634b16f3a2f2b18c43146c0913c7b26c8be410e20d59b8c3808d0bb7fe94d50fc2448b4b91e65dd9f33deb4aed53c14f0dedc3ddd8 + version: 1.2.2 + resolution: "address@npm:1.2.2" + checksum: ace439960c1e3564d8f523aff23a841904bf33a2a7c2e064f7f60a064194075758b9690e65bd9785692a4ef698a998c57eb74d145881a1cecab8ba658ddb1607 languageName: node linkType: hard @@ -2495,7 +2722,7 @@ __metadata: languageName: node linkType: hard -"agent-base@npm:6, agent-base@npm:^6.0.2": +"agent-base@npm:6": version: 6.0.2 resolution: "agent-base@npm:6.0.2" dependencies: @@ -2504,14 +2731,12 @@ __metadata: languageName: node linkType: hard -"agentkeepalive@npm:^4.2.1": - version: 4.2.1 - resolution: "agentkeepalive@npm:4.2.1" +"agent-base@npm:^7.0.2, agent-base@npm:^7.1.0": + version: 7.1.0 + resolution: "agent-base@npm:7.1.0" dependencies: - debug: ^4.1.0 - depd: ^1.1.2 - humanize-ms: ^1.2.1 - checksum: 39cb49ed8cf217fd6da058a92828a0a84e0b74c35550f82ee0a10e1ee403c4b78ade7948be2279b188b7a7303f5d396ea2738b134731e464bf28de00a4f72a18 + debug: ^4.3.4 + checksum: f7828f991470a0cc22cb579c86a18cbae83d8a3cbed39992ab34fc7217c4d126017f1c74d0ab66be87f71455318a8ea3e757d6a37881b8d0f2a2c6aa55e5418f languageName: node linkType: hard @@ -2525,7 +2750,7 @@ __metadata: languageName: node linkType: hard -"ajv@npm:^6.10.0, ajv@npm:^6.10.2, ajv@npm:^6.12.3, ajv@npm:^6.12.4, ajv@npm:^6.6.1, ajv@npm:^6.9.1": +"ajv@npm:^6.10.0, ajv@npm:^6.10.2, ajv@npm:^6.12.3, ajv@npm:^6.12.4, ajv@npm:^6.12.6": version: 6.12.6 resolution: "ajv@npm:6.12.6" dependencies: @@ -2538,14 +2763,27 @@ __metadata: linkType: hard "ajv@npm:^8.0.1": - version: 8.11.0 - resolution: "ajv@npm:8.11.0" + version: 8.12.0 + resolution: "ajv@npm:8.12.0" dependencies: fast-deep-equal: ^3.1.1 json-schema-traverse: ^1.0.0 require-from-string: ^2.0.2 uri-js: ^4.2.2 - checksum: 5e0ff226806763be73e93dd7805b634f6f5921e3e90ca04acdf8db81eed9d8d3f0d4c5f1213047f45ebbf8047ffe0c840fa1ef2ec42c3a644899f69aa72b5bef + checksum: 4dc13714e316e67537c8b31bc063f99a1d9d9a497eb4bbd55191ac0dcd5e4985bbb71570352ad6f1e76684fb6d790928f96ba3b2d4fd6e10024be9612fe3f001 + languageName: node + linkType: hard + +"amazon-cognito-identity-js@npm:^6.0.1": + version: 6.3.10 + resolution: "amazon-cognito-identity-js@npm:6.3.10" + dependencies: + "@aws-crypto/sha256-js": 1.2.2 + buffer: 4.9.2 + fast-base64-decode: ^1.0.0 + isomorphic-unfetch: ^3.0.0 + js-cookie: ^2.2.1 + checksum: c82aaaff8a0e9485d00c3c2541d5bfdea0112c2eced27bc9cbdf9a92c31aa6bcc2e389e429582e6aa115f5fd0394843f47c09bae84ec0c649ddcac16d6fa6756 languageName: node linkType: hard @@ -2556,10 +2794,12 @@ __metadata: languageName: node linkType: hard -"ansi-colors@npm:3.2.3": - version: 3.2.3 - resolution: "ansi-colors@npm:3.2.3" - checksum: 018a92fbf8b143feb9e00559655072598902ff2cdfa07dbe24b933c70ae04845e3dda2c091ab128920fc50b3db06c3f09947f49fcb287d53beb6c5869b8bb32b +"ansi-align@npm:^3.0.0": + version: 3.0.1 + resolution: "ansi-align@npm:3.0.1" + dependencies: + string-width: ^4.1.0 + checksum: 6abfa08f2141d231c257162b15292467081fa49a208593e055c866aa0455b57f3a86b5a678c190c618faa79b4c59e254493099cb700dd9cf2293c6be2c8f5d8d languageName: node linkType: hard @@ -2584,13 +2824,6 @@ __metadata: languageName: node linkType: hard -"ansi-escapes@npm:^3.2.0": - version: 3.2.0 - resolution: "ansi-escapes@npm:3.2.0" - checksum: 0f94695b677ea742f7f1eed961f7fd8d05670f744c6ad1f8f635362f6681dcfbc1575cb05b43abc7bb6d67e25a75fb8c7ea8f2a57330eb2c76b33f18cb2cef0a - languageName: node - linkType: hard - "ansi-escapes@npm:^4.2.1, ansi-escapes@npm:^4.3.0": version: 4.3.2 resolution: "ansi-escapes@npm:4.3.2" @@ -2628,6 +2861,13 @@ __metadata: languageName: node linkType: hard +"ansi-regex@npm:^6.0.1": + version: 6.0.1 + resolution: "ansi-regex@npm:6.0.1" + checksum: 1ff8b7667cded1de4fa2c9ae283e979fc87036864317da86a2e546725f96406746411d0d85e87a2d12fa5abd715d90006de7fa4fa0477c92321ad3b4c7d4e169 + languageName: node + linkType: hard + "ansi-styles@npm:^2.2.1": version: 2.2.1 resolution: "ansi-styles@npm:2.2.1" @@ -2653,10 +2893,17 @@ __metadata: languageName: node linkType: hard -"antlr4@npm:4.7.1": - version: 4.7.1 - resolution: "antlr4@npm:4.7.1" - checksum: ac8644a82aa4031bf466b9dbde40c2f8b20cd944d41649332f01d4ecd384531f76fcefb1cfd044bdbad3d1fd3e2457e792aa72241aaffa225f3a1fe1aca7b9d5 +"ansi-styles@npm:^6.1.0": + version: 6.2.1 + resolution: "ansi-styles@npm:6.2.1" + checksum: ef940f2f0ced1a6347398da88a91da7930c33ecac3c77b72c5905f8b8fe402c52e6fde304ff5347f616e27a742da3f1dc76de98f6866c69251ad0b07a66776d9 + languageName: node + linkType: hard + +"antlr4@npm:^4.11.0": + version: 4.13.1 + resolution: "antlr4@npm:4.13.1" + checksum: 76dcb0c8ed2d0b83a16641579668088919f51d1288551604e3cdff46c67955b3eef25892aead9de1eca203b3968536fa952e6931f35ba877780af37971c055f1 languageName: node linkType: hard @@ -2686,13 +2933,13 @@ __metadata: languageName: node linkType: hard -"anymatch@npm:~3.1.1, anymatch@npm:~3.1.2": - version: 3.1.2 - resolution: "anymatch@npm:3.1.2" +"anymatch@npm:~3.1.2": + version: 3.1.3 + resolution: "anymatch@npm:3.1.3" dependencies: normalize-path: ^3.0.0 picomatch: ^2.0.4 - checksum: 985163db2292fac9e5a1e072bf99f1b5baccf196e4de25a0b0b81865ebddeb3b3eb4480734ef0a2ac8c002845396b91aa89121f5b84f93981a4658164a9ec6e9 + checksum: 3e044fd6d1d26545f235a9fe4d7a534e2029d8e59fa7fd9f2a6eb21230f6b5380ea1eaf55136e60cbf8e613544b3b766e7a6fa2102e2a3a117505466e3025dc2 languageName: node linkType: hard @@ -2712,23 +2959,6 @@ __metadata: languageName: node linkType: hard -"aproba@npm:^1.0.3 || ^2.0.0": - version: 2.0.0 - resolution: "aproba@npm:2.0.0" - checksum: 5615cadcfb45289eea63f8afd064ab656006361020e1735112e346593856f87435e02d8dcc7ff0d11928bc7d425f27bc7c2a84f6c0b35ab0ff659c814c138a24 - languageName: node - linkType: hard - -"are-we-there-yet@npm:^3.0.0": - version: 3.0.0 - resolution: "are-we-there-yet@npm:3.0.0" - dependencies: - delegates: ^1.0.0 - readable-stream: ^3.6.0 - checksum: 348edfdd931b0b50868b55402c01c3f64df1d4c229ab6f063539a5025fd6c5f5bb8a0cab409bbed8d75d34762d22aa91b7c20b4204eb8177063158d9ba792981 - languageName: node - linkType: hard - "arg@npm:^4.1.0": version: 4.1.3 resolution: "arg@npm:4.1.3" @@ -2805,6 +3035,16 @@ __metadata: languageName: node linkType: hard +"array-buffer-byte-length@npm:^1.0.0, array-buffer-byte-length@npm:^1.0.1": + version: 1.0.1 + resolution: "array-buffer-byte-length@npm:1.0.1" + dependencies: + call-bind: ^1.0.5 + is-array-buffer: ^3.0.4 + checksum: 53524e08f40867f6a9f35318fafe467c32e45e9c682ba67b11943e167344d2febc0f6977a17e699b05699e805c3e8f073d876f8bbf1b559ed494ad2cd0fae09e + languageName: node + linkType: hard + "array-flatten@npm:1.1.1": version: 1.1.1 resolution: "array-flatten@npm:1.1.1" @@ -2812,16 +3052,16 @@ __metadata: languageName: node linkType: hard -"array-includes@npm:^3.1.4": - version: 3.1.5 - resolution: "array-includes@npm:3.1.5" +"array-includes@npm:^3.1.7": + version: 3.1.7 + resolution: "array-includes@npm:3.1.7" dependencies: call-bind: ^1.0.2 - define-properties: ^1.1.4 - es-abstract: ^1.19.5 - get-intrinsic: ^1.1.1 + define-properties: ^1.2.0 + es-abstract: ^1.22.1 + get-intrinsic: ^1.2.1 is-string: ^1.0.7 - checksum: f6f24d834179604656b7bec3e047251d5cc87e9e87fab7c175c61af48e80e75acd296017abcde21fb52292ab6a2a449ab2ee37213ee48c8709f004d75983f9c5 + checksum: 06f9e4598fac12a919f7c59a3f04f010ea07f0b7f0585465ed12ef528a60e45f374e79d1bddbb34cdd4338357d00023ddbd0ac18b0be36964f5e726e8965d7fc languageName: node linkType: hard @@ -2846,28 +3086,95 @@ __metadata: languageName: node linkType: hard -"array.prototype.flat@npm:^1.2.5": - version: 1.3.0 - resolution: "array.prototype.flat@npm:1.3.0" +"array.prototype.filter@npm:^1.0.3": + version: 1.0.3 + resolution: "array.prototype.filter@npm:1.0.3" dependencies: call-bind: ^1.0.2 - define-properties: ^1.1.3 - es-abstract: ^1.19.2 + define-properties: ^1.2.0 + es-abstract: ^1.22.1 + es-array-method-boxes-properly: ^1.0.0 + is-string: ^1.0.7 + checksum: 5443cde6ad64596649e5751252b1b2f5242b41052980c2fb2506ba485e3ffd7607e8f6f2f1aefa0cb1cfb9b8623b2b2be103579cb367a161a3426400619b6e73 + languageName: node + linkType: hard + +"array.prototype.findlast@npm:^1.2.2": + version: 1.2.3 + resolution: "array.prototype.findlast@npm:1.2.3" + dependencies: + call-bind: ^1.0.2 + define-properties: ^1.2.0 + es-abstract: ^1.22.1 es-shim-unscopables: ^1.0.0 - checksum: 2a652b3e8dc0bebb6117e42a5ab5738af0203a14c27341d7bb2431467bdb4b348e2c5dc555dfcda8af0a5e4075c400b85311ded73861c87290a71a17c3e0a257 + get-intrinsic: ^1.2.1 + checksum: 853d359bac5c4ce7354e5f2f2930f09eb47eeed18b6d6fb630cf6fa1fe4f32217fd27d07ee4aeeb3d1fd710538cf7bc00d4efc34df64da49fff0b13f57b0d66f languageName: node linkType: hard -"array.prototype.reduce@npm:^1.0.4": - version: 1.0.4 - resolution: "array.prototype.reduce@npm:1.0.4" +"array.prototype.findlastindex@npm:^1.2.3": + version: 1.2.3 + resolution: "array.prototype.findlastindex@npm:1.2.3" dependencies: call-bind: ^1.0.2 - define-properties: ^1.1.3 - es-abstract: ^1.19.2 + define-properties: ^1.2.0 + es-abstract: ^1.22.1 + es-shim-unscopables: ^1.0.0 + get-intrinsic: ^1.2.1 + checksum: 31f35d7b370c84db56484618132041a9af401b338f51899c2e78ef7690fbba5909ee7ca3c59a7192085b328cc0c68c6fd1f6d1553db01a689a589ae510f3966e + languageName: node + linkType: hard + +"array.prototype.flat@npm:^1.3.2": + version: 1.3.2 + resolution: "array.prototype.flat@npm:1.3.2" + dependencies: + call-bind: ^1.0.2 + define-properties: ^1.2.0 + es-abstract: ^1.22.1 + es-shim-unscopables: ^1.0.0 + checksum: 5d6b4bf102065fb3f43764bfff6feb3295d372ce89591e6005df3d0ce388527a9f03c909af6f2a973969a4d178ab232ffc9236654149173e0e187ec3a1a6b87b + languageName: node + linkType: hard + +"array.prototype.flatmap@npm:^1.3.2": + version: 1.3.2 + resolution: "array.prototype.flatmap@npm:1.3.2" + dependencies: + call-bind: ^1.0.2 + define-properties: ^1.2.0 + es-abstract: ^1.22.1 + es-shim-unscopables: ^1.0.0 + checksum: ce09fe21dc0bcd4f30271f8144083aa8c13d4639074d6c8dc82054b847c7fc9a0c97f857491f4da19d4003e507172a78f4bcd12903098adac8b9cd374f734be3 + languageName: node + linkType: hard + +"array.prototype.reduce@npm:^1.0.6": + version: 1.0.6 + resolution: "array.prototype.reduce@npm:1.0.6" + dependencies: + call-bind: ^1.0.2 + define-properties: ^1.2.0 + es-abstract: ^1.22.1 es-array-method-boxes-properly: ^1.0.0 is-string: ^1.0.7 - checksum: 6a57a1a2d3b77a9543db139cd52211f43a5af8e8271cb3c173be802076e3a6f71204ba8f090f5937ebc0842d5876db282f0f63dffd0e86b153e6e5a45681e4a5 + checksum: c709c3f5caa2aac4fb10e0c6c1982cca50328a2a48658d53b1da8ee3a78069ad67cdac21296d6285521aa3a932a8178c0e192b5fc831fae2977b69a5a8a64ad7 + languageName: node + linkType: hard + +"arraybuffer.prototype.slice@npm:^1.0.2": + version: 1.0.3 + resolution: "arraybuffer.prototype.slice@npm:1.0.3" + dependencies: + array-buffer-byte-length: ^1.0.1 + call-bind: ^1.0.5 + define-properties: ^1.2.1 + es-abstract: ^1.22.3 + es-errors: ^1.2.1 + get-intrinsic: ^1.2.3 + is-array-buffer: ^3.0.4 + is-shared-array-buffer: ^1.0.2 + checksum: 352259cba534dcdd969c92ab002efd2ba5025b2e3b9bead3973150edbdf0696c629d7f4b3f061c5931511e8207bdc2306da614703c820b45dabce39e3daf7e3e languageName: node linkType: hard @@ -2937,7 +3244,7 @@ __metadata: languageName: node linkType: hard -"ast-parents@npm:0.0.1": +"ast-parents@npm:^0.0.1": version: 0.0.1 resolution: "ast-parents@npm:0.0.1" checksum: 51360afb9f7b939eb0330fdd0d5d855d0242f273f63478d30d9053069120492173719fb3c03ba372bccf1a7c1a9041c3c6bf2ab700de8c0f8c14792b045c3b23 @@ -2974,6 +3281,15 @@ __metadata: languageName: node linkType: hard +"async-retry@npm:^1.3.3": + version: 1.3.3 + resolution: "async-retry@npm:1.3.3" + dependencies: + retry: 0.13.1 + checksum: 38a7152ff7265a9321ea214b9c69e8224ab1febbdec98efbbde6e562f17ff68405569b796b1c5271f354aef8783665d29953f051f68c1fc45306e61aec82fdc4 + languageName: node + linkType: hard + "async@npm:1.x, async@npm:^1.4.2": version: 1.5.2 resolution: "async@npm:1.5.2" @@ -3022,6 +3338,13 @@ __metadata: languageName: node linkType: hard +"available-typed-arrays@npm:^1.0.5, available-typed-arrays@npm:^1.0.6": + version: 1.0.6 + resolution: "available-typed-arrays@npm:1.0.6" + checksum: 8295571eb86447138adf64a0df0c08ae61250b17190bba30e1fae8c80a816077a6d028e5506f602c382c0197d3080bae131e92e331139d55460989580eeae659 + languageName: node + linkType: hard + "aws-sign2@npm:~0.7.0": version: 0.7.0 resolution: "aws-sign2@npm:0.7.0" @@ -3030,13 +3353,13 @@ __metadata: linkType: hard "aws4@npm:^1.8.0": - version: 1.11.0 - resolution: "aws4@npm:1.11.0" - checksum: 5a00d045fd0385926d20ebebcfba5ec79d4482fe706f63c27b324d489a04c68edb0db99ed991e19eda09cb8c97dc2452059a34d97545cebf591d7a2b5a10999f + version: 1.12.0 + resolution: "aws4@npm:1.12.0" + checksum: 68f79708ac7c335992730bf638286a3ee0a645cf12575d557860100767c500c08b30e24726b9f03265d74116417f628af78509e1333575e9f8d52a80edfe8cbc languageName: node linkType: hard -"axios@npm:^0.21.4": +"axios@npm:^0.21.2, axios@npm:^0.21.4": version: 0.21.4 resolution: "axios@npm:0.21.4" dependencies: @@ -3045,6 +3368,17 @@ __metadata: languageName: node linkType: hard +"axios@npm:^1.4.0, axios@npm:^1.5.1": + version: 1.6.7 + resolution: "axios@npm:1.6.7" + dependencies: + follow-redirects: ^1.15.4 + form-data: ^4.0.0 + proxy-from-env: ^1.1.0 + checksum: 87d4d429927d09942771f3b3a6c13580c183e31d7be0ee12f09be6d5655304996bb033d85e54be81606f4e89684df43be7bf52d14becb73a12727bf33298a082 + languageName: node + linkType: hard + "babel-code-frame@npm:^6.26.0": version: 6.26.0 resolution: "babel-code-frame@npm:6.26.0" @@ -3699,7 +4033,7 @@ __metadata: languageName: node linkType: hard -"base64-js@npm:^1.3.1": +"base64-js@npm:^1.0.2, base64-js@npm:^1.3.1": version: 1.5.1 resolution: "base64-js@npm:1.5.1" checksum: 669632eb3745404c2f822a18fc3a0122d2f9a7a13f7fb8b5823ee19d1d2ff9ee5b52c53367176ea4ad093c332fd5ab4bd0ebae5a8e27917a4105a4cfc86b1005 @@ -3738,25 +4072,16 @@ __metadata: 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 + version: 3.3.0 + resolution: "bigint-crypto-utils@npm:3.3.0" + checksum: 9598ce57b23f776c8936d44114c9f051e62b5fa654915b664784cbcbacc5aa0485f4479571c51ff58008abb1210c0d6a234853742f07cf84bda890f2a1e01000 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" - checksum: 8637b71d0a99104b20413c47578953970006fec6b4df796b9dcfd9835ea9c402ea0e727eba9a5ca9f9a393c1d88b6168c5bbe0887598b708d4f8b4870ad62e1f +"bignumber.js@npm:^9.0.0": + version: 9.1.2 + resolution: "bignumber.js@npm:9.1.2" + checksum: 582c03af77ec9cb0ebd682a373ee6c66475db94a4325f92299621d544aa4bd45cb45fd60001610e94aef8ae98a0905fa538241d9638d4422d57abbeeac6fadaf languageName: node linkType: hard @@ -3838,16 +4163,16 @@ __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.2.0, bn.js@npm:^5.2.1": +"bn.js@npm:^5.0.0, 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 languageName: node linkType: hard -"body-parser@npm:1.20.0, body-parser@npm:^1.16.0": - version: 1.20.0 - resolution: "body-parser@npm:1.20.0" +"body-parser@npm:1.20.1": + version: 1.20.1 + resolution: "body-parser@npm:1.20.1" dependencies: bytes: 3.1.2 content-type: ~1.0.4 @@ -3857,11 +4182,47 @@ __metadata: http-errors: 2.0.0 iconv-lite: 0.4.24 on-finished: 2.4.1 - qs: 6.10.3 + qs: 6.11.0 raw-body: 2.5.1 type-is: ~1.6.18 unpipe: 1.0.0 - checksum: 12fffdeac82fe20dddcab7074215d5156e7d02a69ae90cbe9fee1ca3efa2f28ef52097cbea76685ee0a1509c71d85abd0056a08e612c09077cad6277a644cf88 + checksum: f1050dbac3bede6a78f0b87947a8d548ce43f91ccc718a50dd774f3c81f2d8b04693e52acf62659fad23101827dd318da1fb1363444ff9a8482b886a3e4a5266 + languageName: node + linkType: hard + +"body-parser@npm:^1.16.0": + version: 1.20.2 + resolution: "body-parser@npm:1.20.2" + dependencies: + bytes: 3.1.2 + content-type: ~1.0.5 + debug: 2.6.9 + depd: 2.0.0 + destroy: 1.2.0 + http-errors: 2.0.0 + iconv-lite: 0.4.24 + on-finished: 2.4.1 + qs: 6.11.0 + raw-body: 2.5.2 + type-is: ~1.6.18 + unpipe: 1.0.0 + checksum: 14d37ec638ab5c93f6099ecaed7f28f890d222c650c69306872e00b9efa081ff6c596cd9afb9930656aae4d6c4e1c17537bea12bb73c87a217cb3cfea8896737 + languageName: node + linkType: hard + +"boxen@npm:^5.1.2": + version: 5.1.2 + resolution: "boxen@npm:5.1.2" + dependencies: + ansi-align: ^3.0.0 + camelcase: ^6.2.0 + chalk: ^4.1.0 + cli-boxes: ^2.2.1 + string-width: ^4.2.2 + type-fest: ^0.20.2 + widest-line: ^3.1.0 + wrap-ansi: ^7.0.0 + checksum: 82d03e42a72576ff235123f17b7c505372fe05c83f75f61e7d4fa4bcb393897ec95ce766fecb8f26b915f0f7a7227d66e5ec7cef43f5b2bd9d3aeed47ec55877 languageName: node linkType: hard @@ -3981,7 +4342,7 @@ __metadata: languageName: node linkType: hard -"browserify-rsa@npm:^4.0.0, browserify-rsa@npm:^4.0.1": +"browserify-rsa@npm:^4.0.0, browserify-rsa@npm:^4.1.0": version: 4.1.0 resolution: "browserify-rsa@npm:4.1.0" dependencies: @@ -3992,19 +4353,19 @@ __metadata: linkType: hard "browserify-sign@npm:^4.0.0": - version: 4.2.1 - resolution: "browserify-sign@npm:4.2.1" + version: 4.2.2 + resolution: "browserify-sign@npm:4.2.2" dependencies: - bn.js: ^5.1.1 - browserify-rsa: ^4.0.1 + bn.js: ^5.2.1 + browserify-rsa: ^4.1.0 create-hash: ^1.2.0 create-hmac: ^1.1.7 - elliptic: ^6.5.3 + elliptic: ^6.5.4 inherits: ^2.0.4 - parse-asn1: ^5.1.5 - readable-stream: ^3.6.0 - safe-buffer: ^5.2.0 - checksum: 0221f190e3f5b2d40183fa51621be7e838d9caa329fe1ba773406b7637855f37b30f5d83e52ff8f244ed12ffe6278dd9983638609ed88c841ce547e603855707 + parse-asn1: ^5.1.6 + readable-stream: ^3.6.2 + safe-buffer: ^5.2.1 + checksum: b622730c0fc183328c3a1c9fdaaaa5118821ed6822b266fa6b0375db7e20061ebec87301d61931d79b9da9a96ada1cab317fce3c68f233e5e93ed02dbb35544c languageName: node linkType: hard @@ -4094,6 +4455,17 @@ __metadata: languageName: node linkType: hard +"buffer@npm:4.9.2": + version: 4.9.2 + resolution: "buffer@npm:4.9.2" + dependencies: + base64-js: ^1.0.2 + ieee754: ^1.1.4 + isarray: ^1.0.0 + checksum: 8801bc1ba08539f3be70eee307a8b9db3d40f6afbfd3cf623ab7ef41dffff1d0a31de0addbe1e66e0ca5f7193eeb667bfb1ecad3647f8f1b0750de07c13295c3 + languageName: node + linkType: hard + "buffer@npm:^5.0.5, buffer@npm:^5.2.1, buffer@npm:^5.5.0, buffer@npm:^5.6.0": version: 5.7.1 resolution: "buffer@npm:5.7.1" @@ -4115,12 +4487,12 @@ __metadata: linkType: hard "bufferutil@npm:^4.0.1": - version: 4.0.6 - resolution: "bufferutil@npm:4.0.6" + version: 4.0.8 + resolution: "bufferutil@npm:4.0.8" dependencies: node-gyp: latest node-gyp-build: ^4.3.0 - checksum: dd107560947445280af7820c3d0534127b911577d85d537e1d7e0aa30fd634853cef8a994d6e8aed3d81388ab1a20257de776164afe6a6af8e78f5f17968ebd6 + checksum: 7e9a46f1867dca72fda350966eb468eca77f4d623407b0650913fadf73d5750d883147d6e5e21c56f9d3b0bdc35d5474e80a600b9f31ec781315b4d2469ef087 languageName: node linkType: hard @@ -4129,16 +4501,7 @@ __metadata: resolution: "builtins@npm:5.0.1" dependencies: semver: ^7.0.0 - checksum: 66d204657fe36522822a95b288943ad11b58f5eaede235b11d8c4edaa28ce4800087d44a2681524c340494aadb120a0068011acabe99d30e8f11a7d826d83515 - languageName: node - linkType: hard - -"busboy@npm:^1.6.0": - version: 1.6.0 - resolution: "busboy@npm:1.6.0" - dependencies: - streamsearch: ^1.1.0 - checksum: 32801e2c0164e12106bf236291a00795c3c4e4b709ae02132883fe8478ba2ae23743b11c5735a0aae8afe65ac4b6ca4568b91f0d9fed1fdbc32ede824a73746e + checksum: 66d204657fe36522822a95b288943ad11b58f5eaede235b11d8c4edaa28ce4800087d44a2681524c340494aadb120a0068011acabe99d30e8f11a7d826d83515 languageName: node linkType: hard @@ -4168,29 +4531,23 @@ __metadata: languageName: node linkType: hard -"cacache@npm:^16.1.0": - version: 16.1.1 - resolution: "cacache@npm:16.1.1" +"cacache@npm:^18.0.0": + version: 18.0.2 + resolution: "cacache@npm:18.0.2" dependencies: - "@npmcli/fs": ^2.1.0 - "@npmcli/move-file": ^2.0.0 - chownr: ^2.0.0 - fs-minipass: ^2.1.0 - glob: ^8.0.1 - infer-owner: ^1.0.4 - lru-cache: ^7.7.1 - minipass: ^3.1.6 - minipass-collect: ^1.0.2 + "@npmcli/fs": ^3.1.0 + fs-minipass: ^3.0.0 + glob: ^10.2.2 + lru-cache: ^10.0.1 + minipass: ^7.0.3 + minipass-collect: ^2.0.1 minipass-flush: ^1.0.5 minipass-pipeline: ^1.2.4 - mkdirp: ^1.0.4 p-map: ^4.0.0 - promise-inflight: ^1.0.1 - rimraf: ^3.0.2 - ssri: ^9.0.0 + ssri: ^10.0.0 tar: ^6.1.11 - unique-filename: ^1.1.1 - checksum: 488524617008b793f0249b0c4ea2c330c710ca997921376e15650cc2415a8054491ae2dee9f01382c2015602c0641f3f977faf2fa7361aa33d2637dcfb03907a + unique-filename: ^3.0.0 + checksum: 0250df80e1ad0c828c956744850c5f742c24244e9deb5b7dc81bca90f8c10e011e132ecc58b64497cc1cad9a98968676147fb6575f4f94722f7619757b17a11b languageName: node linkType: hard @@ -4211,6 +4568,13 @@ __metadata: languageName: node linkType: hard +"cacheable-lookup@npm:^5.0.3": + version: 5.0.4 + resolution: "cacheable-lookup@npm:5.0.4" + checksum: 763e02cf9196bc9afccacd8c418d942fc2677f22261969a4c2c2e760fa44a2351a81557bd908291c3921fe9beb10b976ba8fa50c5ca837c5a0dd945f16468f2d + languageName: node + linkType: hard + "cacheable-request@npm:^6.0.0": version: 6.1.0 resolution: "cacheable-request@npm:6.1.0" @@ -4226,6 +4590,21 @@ __metadata: languageName: node linkType: hard +"cacheable-request@npm:^7.0.2": + version: 7.0.4 + resolution: "cacheable-request@npm:7.0.4" + dependencies: + clone-response: ^1.0.2 + get-stream: ^5.1.0 + http-cache-semantics: ^4.0.0 + keyv: ^4.0.0 + lowercase-keys: ^2.0.0 + normalize-url: ^6.0.1 + responselike: ^2.0.0 + checksum: 0de9df773fd4e7dd9bd118959878f8f2163867e2e1ab3575ffbecbe6e75e80513dd0c68ba30005e5e5a7b377cc6162bbc00ab1db019bb4e9cb3c2f3f7a6f1ee4 + languageName: node + linkType: hard + "cachedown@npm:1.0.0": version: 1.0.0 resolution: "cachedown@npm:1.0.0" @@ -4236,38 +4615,14 @@ __metadata: languageName: node linkType: hard -"call-bind@npm:^1.0.0, call-bind@npm:^1.0.2, call-bind@npm:~1.0.2": - version: 1.0.2 - resolution: "call-bind@npm:1.0.2" - dependencies: - function-bind: ^1.1.1 - get-intrinsic: ^1.0.2 - checksum: f8e31de9d19988a4b80f3e704788c4a2d6b6f3d17cfec4f57dc29ced450c53a49270dc66bf0fbd693329ee948dd33e6c90a329519aef17474a4d961e8d6426b0 - languageName: node - linkType: hard - -"caller-callsite@npm:^2.0.0": - version: 2.0.0 - resolution: "caller-callsite@npm:2.0.0" - dependencies: - callsites: ^2.0.0 - checksum: b685e9d126d9247b320cfdfeb3bc8da0c4be28d8fb98c471a96bc51aab3130099898a2fe3bf0308f0fe048d64c37d6d09f563958b9afce1a1e5e63d879c128a2 - languageName: node - linkType: hard - -"caller-path@npm:^2.0.0": - version: 2.0.0 - resolution: "caller-path@npm:2.0.0" +"call-bind@npm:^1.0.0, call-bind@npm:^1.0.2, call-bind@npm:^1.0.5, call-bind@npm:~1.0.2": + version: 1.0.5 + resolution: "call-bind@npm:1.0.5" dependencies: - caller-callsite: ^2.0.0 - checksum: 3e12ccd0c71ec10a057aac69e3ec175b721ca858c640df021ef0d25999e22f7c1d864934b596b7d47038e9b56b7ec315add042abbd15caac882998b50102fb12 - languageName: node - linkType: hard - -"callsites@npm:^2.0.0": - version: 2.0.0 - resolution: "callsites@npm:2.0.0" - checksum: be2f67b247df913732b7dec1ec0bbfcdbaea263e5a95968b19ec7965affae9496b970e3024317e6d4baa8e28dc6ba0cec03f46fdddc2fdcc51396600e53c2623 + function-bind: ^1.1.2 + get-intrinsic: ^1.2.1 + set-function-length: ^1.1.1 + checksum: 449e83ecbd4ba48e7eaac5af26fea3b50f8f6072202c2dd7c5a6e7a6308f2421abe5e13a3bbd55221087f76320c5e09f25a8fdad1bab2b77c68ae74d92234ea5 languageName: node linkType: hard @@ -4292,7 +4647,7 @@ __metadata: languageName: node linkType: hard -"camelcase@npm:^6.0.0": +"camelcase@npm:^6.0.0, camelcase@npm:^6.2.0": version: 6.3.0 resolution: "camelcase@npm:6.3.0" checksum: 8c96818a9076434998511251dcb2761a94817ea17dbdc37f47ac080bd088fc62c7369429a19e2178b993497132c8cbcf5cc1f44ba963e76782ba469c0474938d @@ -4300,9 +4655,16 @@ __metadata: linkType: hard "caniuse-lite@npm:^1.0.30000844": - version: 1.0.30001359 - resolution: "caniuse-lite@npm:1.0.30001359" - checksum: e15dbf4ea445367e998ad525c54620275ae382446d0bc289d14bf014a159074a2207881b46406b011a72676686f8a734457ea8e8d4856dc3f700c3391e89b43e + version: 1.0.30001584 + resolution: "caniuse-lite@npm:1.0.30001584" + checksum: de7018759561795ef31864b0d1584735eef267033d4e9b5f046b976756e06c43e85afd46705c5d63c63e3c36484c26794c259b9748eefffa582750b4ad0822ce + languageName: node + linkType: hard + +"case@npm:^1.6.3": + version: 1.6.3 + resolution: "case@npm:1.6.3" + checksum: febe73278f910b0d28aab7efd6f51c235f9aa9e296148edb56dfb83fd58faa88308c30ce9a0122b6e53e0362c44f4407105bd5ef89c46860fc2b184e540fd68d languageName: node linkType: hard @@ -4320,22 +4682,21 @@ __metadata: languageName: node linkType: hard -"cbor@npm:^5.0.2": - version: 5.2.0 - resolution: "cbor@npm:5.2.0" +"cbor@npm:^8.1.0": + version: 8.1.0 + resolution: "cbor@npm:8.1.0" dependencies: - bignumber.js: ^9.0.1 - nofilter: ^1.0.4 - checksum: b3c39dae64370f361526dbec88f51d0f1b47027224cdd21dbd64c228f0fe7eaa945932d349ec5324068a6c6dcdbb1e3b46242852524fd53c526d14cb60514bdc + nofilter: ^3.1.0 + checksum: a90338435dc7b45cc01461af979e3bb6ddd4f2a08584c437586039cd5f2235014c06e49d664295debbfb3514d87b2f06728092ab6aa6175e2e85e9cd7dc0c1fd languageName: node linkType: hard -"cbor@npm:^8.0.0": - version: 8.1.0 - resolution: "cbor@npm:8.1.0" +"cbor@npm:^9.0.0": + version: 9.0.2 + resolution: "cbor@npm:9.0.2" dependencies: nofilter: ^3.1.0 - checksum: a90338435dc7b45cc01461af979e3bb6ddd4f2a08584c437586039cd5f2235014c06e49d664295debbfb3514d87b2f06728092ab6aa6175e2e85e9cd7dc0c1fd + checksum: 925edae7bf964be5a26dba1b7ba6311ac12b6a66234dc958958997a0576cdc740632dc19852a5b84d8a75101936bea1fe122dc22539d6e11f4539c731853ba2e languageName: node linkType: hard @@ -4360,17 +4721,17 @@ __metadata: linkType: hard "chai@npm:^4.3.6": - version: 4.3.6 - resolution: "chai@npm:4.3.6" + version: 4.4.1 + resolution: "chai@npm:4.4.1" dependencies: assertion-error: ^1.1.0 - check-error: ^1.0.2 - deep-eql: ^3.0.1 - get-func-name: ^2.0.0 - loupe: ^2.3.1 + check-error: ^1.0.3 + deep-eql: ^4.1.3 + get-func-name: ^2.0.2 + loupe: ^2.3.6 pathval: ^1.1.1 - type-detect: ^4.0.5 - checksum: acff93fd537f96d4a4d62dd83810285dffcfccb5089e1bf2a1205b28ec82d93dff551368722893cf85004282df10ee68802737c33c90c5493957ed449ed7ce71 + type-detect: ^4.0.8 + checksum: 9ab84f36eb8e0b280c56c6c21ca4da5933132cd8a0c89c384f1497f77953640db0bc151edd47f81748240a9fab57b78f7d925edfeedc8e8fc98016d71f40c36e languageName: node linkType: hard @@ -4387,7 +4748,7 @@ __metadata: languageName: node linkType: hard -"chalk@npm:^2.0.0, chalk@npm:^2.1.0, chalk@npm:^2.4.1, chalk@npm:^2.4.2": +"chalk@npm:^2.1.0, chalk@npm:^2.4.1, chalk@npm:^2.4.2": version: 2.4.2 resolution: "chalk@npm:2.4.2" dependencies: @@ -4408,7 +4769,7 @@ __metadata: languageName: node linkType: hard -"chalk@npm:^4.0.0, chalk@npm:^4.1.0": +"chalk@npm:^4.0.0, chalk@npm:^4.1.0, chalk@npm:^4.1.2": version: 4.1.2 resolution: "chalk@npm:4.1.2" dependencies: @@ -4432,10 +4793,12 @@ __metadata: languageName: node linkType: hard -"check-error@npm:^1.0.2": - version: 1.0.2 - resolution: "check-error@npm:1.0.2" - checksum: d9d106504404b8addd1ee3f63f8c0eaa7cd962a1a28eb9c519b1c4a1dc7098be38007fc0060f045ee00f075fbb7a2a4f42abcf61d68323677e11ab98dc16042e +"check-error@npm:^1.0.2, check-error@npm:^1.0.3": + version: 1.0.3 + resolution: "check-error@npm:1.0.3" + dependencies: + get-func-name: ^2.0.2 + checksum: e2131025cf059b21080f4813e55b3c480419256914601750b0fee3bd9b2b8315b531e551ef12560419b8b6d92a3636511322752b1ce905703239e7cc451b6399 languageName: node linkType: hard @@ -4448,25 +4811,6 @@ __metadata: languageName: node linkType: hard -"chokidar@npm:3.3.0": - version: 3.3.0 - resolution: "chokidar@npm:3.3.0" - dependencies: - anymatch: ~3.1.1 - braces: ~3.0.2 - fsevents: ~2.1.1 - glob-parent: ~5.1.0 - is-binary-path: ~2.1.0 - is-glob: ~4.0.1 - normalize-path: ~3.0.0 - readdirp: ~3.2.0 - dependenciesMeta: - fsevents: - optional: true - checksum: e9863256ebb29dbc5e58a7e2637439814beb63b772686cb9e94478312c24dcaf3d0570220c5e75ea29029f43b664f9956d87b716120d38cf755f32124f047e8e - languageName: node - linkType: hard - "chokidar@npm:3.5.3, chokidar@npm:^3.0.2, chokidar@npm:^3.4.0": version: 3.5.3 resolution: "chokidar@npm:3.5.3" @@ -4550,16 +4894,16 @@ __metadata: linkType: hard "classic-level@npm:^1.2.0": - version: 1.2.0 - resolution: "classic-level@npm:1.2.0" + version: 1.4.1 + resolution: "classic-level@npm:1.4.1" dependencies: abstract-level: ^1.0.2 catering: ^2.1.0 module-error: ^1.0.1 - napi-macros: ~2.0.0 + napi-macros: ^2.2.2 node-gyp: latest node-gyp-build: ^4.3.0 - checksum: 88ddd12f2192c2775107d5e462998ac01095cb0222ca01dc2be77d8dcbbf9883c4c0a0248529cceee40a2f1232c68027b1aca731da9f767ad8e9483cbd61dd37 + checksum: 62e7e07297fcd656941eb88f92b91df0046ebb2b34987e98ec870cb736f096e212ef109a25541deba2f30866b9d5df550594ed04948614815dd5964933da50a9 languageName: node linkType: hard @@ -4570,12 +4914,10 @@ __metadata: languageName: node linkType: hard -"cli-cursor@npm:^2.1.0": - version: 2.1.0 - resolution: "cli-cursor@npm:2.1.0" - dependencies: - restore-cursor: ^2.0.0 - checksum: d88e97bfdac01046a3ffe7d49f06757b3126559d7e44aa2122637eb179284dc6cd49fca2fac4f67c19faaf7e6dab716b6fe1dfcd309977407d8c7578ec2d044d +"cli-boxes@npm:^2.2.1": + version: 2.2.1 + resolution: "cli-boxes@npm:2.2.1" + checksum: be79f8ec23a558b49e01311b39a1ea01243ecee30539c880cf14bf518a12e223ef40c57ead0cb44f509bffdffc5c129c746cd50d863ab879385370112af4f585 languageName: node linkType: hard @@ -4589,9 +4931,9 @@ __metadata: linkType: hard "cli-spinners@npm:^2.2.0": - version: 2.9.0 - resolution: "cli-spinners@npm:2.9.0" - checksum: a9c56e1f44457d4a9f4f535364e729cb8726198efa9e98990cfd9eda9e220dfa4ba12f92808d1be5e29029cdfead781db82dc8549b97b31c907d55f96aa9b0e2 + version: 2.9.2 + resolution: "cli-spinners@npm:2.9.2" + checksum: 1bd588289b28432e4676cb5d40505cfe3e53f2e4e10fbe05c8a710a154d6fe0ce7836844b00d6858f740f2ffe67cdc36e0fce9c7b6a8430e80e6388d5aa4956c languageName: node linkType: hard @@ -4609,13 +4951,6 @@ __metadata: languageName: node linkType: hard -"cli-width@npm:^2.0.0": - version: 2.2.1 - resolution: "cli-width@npm:2.2.1" - checksum: 3c21b897a2ff551ae5b3c3ab32c866ed2965dcf7fb442f81adf0e27f4a397925c8f84619af7bcc6354821303f6ee9b2aa31d248306174f32c287986158cf4eed - languageName: node - linkType: hard - "cli-width@npm:^3.0.0": version: 3.0.0 resolution: "cli-width@npm:3.0.0" @@ -4657,11 +4992,11 @@ __metadata: linkType: hard "clone-response@npm:^1.0.2": - version: 1.0.2 - resolution: "clone-response@npm:1.0.2" + version: 1.0.3 + resolution: "clone-response@npm:1.0.3" dependencies: mimic-response: ^1.0.0 - checksum: 2d0e61547fc66276e0903be9654ada422515f5a15741691352000d47e8c00c226061221074ce2c0064d12e975e84a8687cfd35d8b405750cb4e772f87b256eda + checksum: 4e671cac39b11c60aa8ba0a450657194a5d6504df51bca3fac5b3bd0145c4f8e8464898f87c8406b83232e3bc5cca555f51c1f9c8ac023969ebfbf7f6bdabb2e languageName: node linkType: hard @@ -4728,15 +5063,6 @@ __metadata: languageName: node linkType: hard -"color-support@npm:^1.1.3": - version: 1.1.3 - resolution: "color-support@npm:1.1.3" - bin: - color-support: bin.js - checksum: 9b7356817670b9a13a26ca5af1c21615463b500783b739b7634a0c2047c16cef4b2865d7576875c31c3cddf9dd621fa19285e628f20198b233a5cfdda6d0793b - languageName: node - linkType: hard - "colors@npm:1.4.0, colors@npm:^1.1.2, colors@npm:^1.3.3": version: 1.4.0 resolution: "colors@npm:1.4.0" @@ -4797,13 +5123,6 @@ __metadata: languageName: node linkType: hard -"commander@npm:2.18.0": - version: 2.18.0 - resolution: "commander@npm:2.18.0" - checksum: 3a31585348a5000bbdc457c9839aabbdf0bb0020e5dfaa1c9f9903680073d67c06911b55368e4c8df2ed166e0d4468f9a668585c1667c321804034a2819a819f - languageName: node - linkType: hard - "commander@npm:3.0.2": version: 3.0.2 resolution: "commander@npm:3.0.2" @@ -4811,6 +5130,13 @@ __metadata: languageName: node linkType: hard +"commander@npm:^10.0.0": + version: 10.0.1 + resolution: "commander@npm:10.0.1" + checksum: 436901d64a818295803c1996cd856621a74f30b9f9e28a588e726b2b1670665bccd7c1a77007ebf328729f0139838a88a19265858a0fa7a8728c4656796db948 + languageName: node + linkType: hard + "commander@npm:^2.20.3": version: 2.20.3 resolution: "commander@npm:2.20.3" @@ -4818,24 +5144,17 @@ __metadata: languageName: node linkType: hard -"compare-versions@npm:^4.0.0": - version: 4.1.3 - resolution: "compare-versions@npm:4.1.3" - checksum: 54460756ab2d62f8a9d672db249b248fec7ca41c3e8ed242925e2f2257793ad3e83cecb2cdfd60b46a3aabc962a3a4cbf37a4b928c8f30517822d2bde937a3d1 - languageName: node - linkType: hard - -"compare-versions@npm:^5.0.0": - version: 5.0.3 - resolution: "compare-versions@npm:5.0.3" - checksum: f66a4bb6ef8ff32031cc92c04dea4bbead039e72a7f6c7df7ef05f5a42ddca9202f8875b7449add54181e73b89f039662a8760c8db0ab036c4e8f653a7cd29c1 +"compare-versions@npm:^6.0.0": + version: 6.1.0 + resolution: "compare-versions@npm:6.1.0" + checksum: d4e2a45706a023d8d0b6680338b66b79e20bd02d1947f0ac6531dab634cbed89fa373b3f03d503c5e489761194258d6e1bae67a07f88b1efc61648454f2d47e7 languageName: node linkType: hard "component-emitter@npm:^1.2.1": - version: 1.3.0 - resolution: "component-emitter@npm:1.3.0" - checksum: b3c46de38ffd35c57d1c02488355be9f218e582aec72d72d1b8bbec95a3ac1b38c96cd6e03ff015577e68f550fbb361a3bfdbd9bb248be9390b7b3745691be6b + version: 1.3.1 + resolution: "component-emitter@npm:1.3.1" + checksum: 94550aa462c7bd5a61c1bc480e28554aa306066930152d1b1844a0dd3845d4e5db7e261ddec62ae184913b3e59b55a2ad84093b9d3596a8f17c341514d6c483d languageName: node linkType: hard @@ -4858,13 +5177,6 @@ __metadata: languageName: node linkType: hard -"console-control-strings@npm:^1.1.0": - version: 1.1.0 - resolution: "console-control-strings@npm:1.1.0" - checksum: 8755d76787f94e6cf79ce4666f0c5519906d7f5b02d4b884cf41e11dcd759ed69c57da0670afd9236d229a46e0f9cf519db0cd829c6dca820bb5a5c3def584ed - languageName: node - linkType: hard - "content-disposition@npm:0.5.4": version: 0.5.4 resolution: "content-disposition@npm:0.5.4" @@ -4885,19 +5197,17 @@ __metadata: languageName: node linkType: hard -"content-type@npm:~1.0.4": - version: 1.0.4 - resolution: "content-type@npm:1.0.4" - checksum: 3d93585fda985d1554eca5ebd251994327608d2e200978fdbfba21c0c679914d5faf266d17027de44b34a72c7b0745b18584ecccaa7e1fdfb6a68ac7114f12e0 +"content-type@npm:~1.0.4, content-type@npm:~1.0.5": + version: 1.0.5 + resolution: "content-type@npm:1.0.5" + checksum: 566271e0a251642254cde0f845f9dd4f9856e52d988f4eb0d0dcffbb7a1f8ec98de7a5215fc628f3bce30fe2fb6fd2bc064b562d721658c59b544e2d34ea2766 languageName: node linkType: hard "convert-source-map@npm:^1.5.1": - version: 1.8.0 - resolution: "convert-source-map@npm:1.8.0" - dependencies: - safe-buffer: ~5.1.1 - checksum: 985d974a2d33e1a2543ada51c93e1ba2f73eaed608dc39f229afc78f71dcc4c8b7d7c684aa647e3c6a3a204027444d69e53e169ce94e8d1fa8d7dee80c9c8fed + version: 1.9.0 + resolution: "convert-source-map@npm:1.9.0" + checksum: dc55a1f28ddd0e9485ef13565f8f756b342f9a46c4ae18b843fe3c30c675d058d6a4823eff86d472f187b176f0adf51ea7b69ea38be34be4a63cbbf91b0593c8 languageName: node linkType: hard @@ -4937,9 +5247,9 @@ __metadata: linkType: hard "core-js-pure@npm:^3.0.1": - version: 3.23.3 - resolution: "core-js-pure@npm:3.23.3" - checksum: 09a477a56963ca4409ca383d36429ea3b51b658ff85e94331a510543c77c4d1b44cb6b305b0f185d729eb059c71f1289c62fdec6371ff46ce838a16988cdcb2e + version: 3.35.1 + resolution: "core-js-pure@npm:3.35.1" + checksum: 2fb360757c403b1487e746bb3648c7f0be45c196640552767f4e2a55a962411a33093cd8babf5e0416de7f4c38d1b05bbaf576c0a3bf2d6565935bab749d3fb5 languageName: node linkType: hard @@ -4987,15 +5297,20 @@ __metadata: languageName: node linkType: hard -"cosmiconfig@npm:^5.0.7": - version: 5.2.1 - resolution: "cosmiconfig@npm:5.2.1" +"cosmiconfig@npm:^8.0.0": + version: 8.3.6 + resolution: "cosmiconfig@npm:8.3.6" dependencies: - import-fresh: ^2.0.0 - is-directory: ^0.3.1 - js-yaml: ^3.13.1 - parse-json: ^4.0.0 - checksum: 8b6f1d3c8a5ffdf663a952f17af0761adf210b7a5933d0fe8988f3ca3a1f0e1e5cbbb74d5b419c15933dd2fdcaec31dbc5cc85cb8259a822342b93b529eff89c + import-fresh: ^3.3.0 + js-yaml: ^4.1.0 + parse-json: ^5.2.0 + path-type: ^4.0.0 + peerDependencies: + typescript: ">=4.9.5" + peerDependenciesMeta: + typescript: + optional: true + checksum: dc339ebea427898c9e03bf01b56ba7afbac07fc7d2a2d5a15d6e9c14de98275a9565da949375aee1809591c152c0a3877bb86dbeaf74d5bd5aaa79955ad9e7a0 languageName: node linkType: hard @@ -5138,7 +5453,7 @@ __metadata: languageName: node linkType: hard -"debug@npm:2.6.9, debug@npm:^2.2.0, debug@npm:^2.3.3, debug@npm:^2.6.0, debug@npm:^2.6.8, debug@npm:^2.6.9": +"debug@npm:2.6.9, debug@npm:^2.2.0, debug@npm:^2.3.3, debug@npm:^2.6.8, debug@npm:^2.6.9": version: 2.6.9 resolution: "debug@npm:2.6.9" dependencies: @@ -5156,7 +5471,7 @@ __metadata: languageName: node linkType: hard -"debug@npm:4, debug@npm:4.3.4, debug@npm:^4.0.1, debug@npm:^4.1.0, debug@npm:^4.1.1, debug@npm:^4.3.1, debug@npm:^4.3.2, debug@npm:^4.3.3, debug@npm:^4.3.4": +"debug@npm:4, debug@npm:4.3.4, debug@npm:^4.0.1, debug@npm:^4.1.1, debug@npm:^4.3.1, debug@npm:^4.3.2, debug@npm:^4.3.3, debug@npm:^4.3.4": version: 4.3.4 resolution: "debug@npm:4.3.4" dependencies: @@ -5198,7 +5513,7 @@ __metadata: languageName: node linkType: hard -"decompress-response@npm:^3.2.0, decompress-response@npm:^3.3.0": +"decompress-response@npm:^3.3.0": version: 3.3.0 resolution: "decompress-response@npm:3.3.0" dependencies: @@ -5207,16 +5522,16 @@ __metadata: languageName: node linkType: hard -"deep-eql@npm:^3.0.1": - version: 3.0.1 - resolution: "deep-eql@npm:3.0.1" +"decompress-response@npm:^6.0.0": + version: 6.0.0 + resolution: "decompress-response@npm:6.0.0" dependencies: - type-detect: ^4.0.0 - checksum: 4f4c9fb79eb994fb6e81d4aa8b063adc40c00f831588aa65e20857d5d52f15fb23034a6576ecf886f7ff6222d5ae42e71e9b7d57113e0715b1df7ea1e812b125 + mimic-response: ^3.1.0 + checksum: d377cf47e02d805e283866c3f50d3d21578b779731e8c5072d6ce8c13cc31493db1c2f6784da9d1d5250822120cefa44f1deab112d5981015f2e17444b763812 languageName: node linkType: hard -"deep-eql@npm:^4.0.1": +"deep-eql@npm:^4.0.1, deep-eql@npm:^4.1.3": version: 4.1.3 resolution: "deep-eql@npm:4.1.3" dependencies: @@ -5226,16 +5541,16 @@ __metadata: linkType: hard "deep-equal@npm:~1.1.1": - version: 1.1.1 - resolution: "deep-equal@npm:1.1.1" + version: 1.1.2 + resolution: "deep-equal@npm:1.1.2" dependencies: - is-arguments: ^1.0.4 - is-date-object: ^1.0.1 - is-regex: ^1.0.4 - object-is: ^1.0.1 + is-arguments: ^1.1.1 + is-date-object: ^1.0.5 + is-regex: ^1.1.4 + object-is: ^1.1.5 object-keys: ^1.1.1 - regexp.prototype.flags: ^1.2.0 - checksum: f92686f2c5bcdf714a75a5fa7a9e47cb374a8ec9307e717b8d1ce61f56a75aaebf5619c2a12b8087a705b5a2f60d0292c35f8b58cb1f72e3268a3a15cab9f78d + regexp.prototype.flags: ^1.5.1 + checksum: 2d50f27fff785fb272cdef038ee5365ee5a30ab1aab053976e6a6add44cc60abd99b38179a46a01ac52c5e54ebb220e8f1a3a1954da20678b79c46ef4d97c9db languageName: node linkType: hard @@ -5269,6 +5584,13 @@ __metadata: languageName: node linkType: hard +"defer-to-connect@npm:^2.0.0": + version: 2.0.1 + resolution: "defer-to-connect@npm:2.0.1" + checksum: 8a9b50d2f25446c0bfefb55a48e90afd58f85b21bcf78e9207cd7b804354f6409032a1705c2491686e202e64fc05f147aa5aa45f9aa82627563f045937f5791b + languageName: node + linkType: hard + "deferred-leveldown@npm:~1.2.1": version: 1.2.2 resolution: "deferred-leveldown@npm:1.2.2" @@ -5288,13 +5610,25 @@ __metadata: 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" +"define-data-property@npm:^1.0.1, define-data-property@npm:^1.1.1": + version: 1.1.1 + resolution: "define-data-property@npm:1.1.1" + dependencies: + get-intrinsic: ^1.2.1 + gopd: ^1.0.1 + has-property-descriptors: ^1.0.0 + checksum: a29855ad3f0630ea82e3c5012c812efa6ca3078d5c2aa8df06b5f597c1cde6f7254692df41945851d903e05a1668607b6d34e778f402b9ff9ffb38111f1a3f0d + languageName: node + linkType: hard + +"define-properties@npm:^1.1.3, define-properties@npm:^1.2.0, define-properties@npm:^1.2.1": + version: 1.2.1 + resolution: "define-properties@npm:1.2.1" dependencies: + define-data-property: ^1.0.1 has-property-descriptors: ^1.0.0 object-keys: ^1.1.1 - checksum: ce0aef3f9eb193562b5cfb79b2d2c86b6a109dfc9fdcb5f45d680631a1a908c06824ddcdb72b7573b54e26ace07f0a23420aaba0d5c627b34d2c1de8ef527e2b + checksum: b4ccd00597dd46cb2d4a379398f5b19fca84a16f3374e2249201992f36b30f6835949a9429669ee6b41b6e837205a163eadd745e472069e70dfc10f03e5fcc12 languageName: node linkType: hard @@ -5326,10 +5660,10 @@ __metadata: languageName: node linkType: hard -"defined@npm:~1.0.0": - version: 1.0.0 - resolution: "defined@npm:1.0.0" - checksum: 77672997c5001773371c4dbcce98da0b3dc43089d6da2ad87c4b800adb727633cea8723ea3889fe0c2112a2404e2fd07e3bfd0e55f7426aa6441d8992045dbd5 +"defined@npm:~1.0.1": + version: 1.0.1 + resolution: "defined@npm:1.0.1" + checksum: b1a852300bdb57f297289b55eafdd0c517afaa3ec8190e78fce91b9d8d0c0369d4505ecbdacfd3d98372e664f4a267d9bd793938d4a8c76209c9d9516fbe2101 languageName: node linkType: hard @@ -5347,13 +5681,6 @@ __metadata: languageName: node linkType: hard -"delegates@npm:^1.0.0": - version: 1.0.0 - resolution: "delegates@npm:1.0.0" - checksum: a51744d9b53c164ba9c0492471a1a2ffa0b6727451bdc89e31627fdf4adda9d51277cfcbfb20f0a6f08ccb3c436f341df3e92631a3440226d93a8971724771fd - languageName: node - linkType: hard - "depd@npm:2.0.0": version: 2.0.0 resolution: "depd@npm:2.0.0" @@ -5361,20 +5688,13 @@ __metadata: languageName: node linkType: hard -"depd@npm:^1.1.2": - version: 1.1.2 - resolution: "depd@npm:1.1.2" - checksum: 6b406620d269619852885ce15965272b829df6f409724415e0002c8632ab6a8c0a08ec1f0bd2add05dc7bd7507606f7e2cc034fa24224ab829580040b835ecd9 - languageName: node - linkType: hard - "des.js@npm:^1.0.0": - version: 1.0.1 - resolution: "des.js@npm:1.0.1" + version: 1.1.0 + resolution: "des.js@npm:1.1.0" dependencies: inherits: ^2.0.1 minimalistic-assert: ^1.0.0 - checksum: 1ec2eedd7ed6bd61dd5e0519fd4c96124e93bb22de8a9d211b02d63e5dd152824853d919bb2090f965cc0e3eb9c515950a9836b332020d810f9c71feb0fd7df4 + checksum: 0e9c1584b70d31e20f20a613fc9ef60fbc6a147dfec9e448a168794a4b97ac04d8dc47ea008f1fa93b0f8aaf7c1ead632a5e59ce1913a6079d2d244c9f5ebe33 languageName: node linkType: hard @@ -5395,22 +5715,15 @@ __metadata: linkType: hard "detect-port@npm:^1.3.0": - version: 1.3.0 - resolution: "detect-port@npm:1.3.0" + version: 1.5.1 + resolution: "detect-port@npm:1.5.1" dependencies: address: ^1.0.1 - debug: ^2.6.0 + debug: 4 bin: - detect: ./bin/detect-port - detect-port: ./bin/detect-port - checksum: 93c40febe714f56711d1fedc2b7a9cc4cbaa0fcddec0509876c46b9dd6099ed6bfd6662a4f35e5fa0301660f48ed516829253ab0fc90b9e79b823dd77786b379 - languageName: node - linkType: hard - -"diff@npm:3.5.0": - version: 3.5.0 - resolution: "diff@npm:3.5.0" - checksum: 00842950a6551e26ce495bdbce11047e31667deea546527902661f25cc2e73358967ebc78cf86b1a9736ec3e14286433225f9970678155753a6291c3bca5227b + detect: bin/detect-port.js + detect-port: bin/detect-port.js + checksum: b48da9340481742547263d5d985e65d078592557863402ecf538511735e83575867e94f91fe74405ea19b61351feb99efccae7e55de9a151d5654e3417cea05b languageName: node linkType: hard @@ -5533,9 +5846,9 @@ __metadata: linkType: hard "dotenv@npm:^16.0.1": - version: 16.0.1 - resolution: "dotenv@npm:16.0.1" - checksum: f459ffce07b977b7f15d8cc4ee69cdff77d4dd8c5dc8c85d2d485ee84655352c2415f9dd09d42b5b5985ced3be186130871b34e2f3e2569ebc72fbc2e8096792 + version: 16.4.1 + resolution: "dotenv@npm:16.4.1" + checksum: a343f0a1d156deef8c60034f797969867af4dbccfacedd4ac15fad04547e7ffe0553b58fc3b27a5837950f0d977e38e9234943fbcec4aeced4e3d044309a76ab languageName: node linkType: hard @@ -5551,9 +5864,16 @@ __metadata: linkType: hard "duplexer3@npm:^0.1.4": - version: 0.1.4 - resolution: "duplexer3@npm:0.1.4" - checksum: c2fd6969314607d23439c583699aaa43c4100d66b3e161df55dccd731acc57d5c81a64bb4f250805fbe434ddb1d2623fee2386fb890f5886ca1298690ec53415 + version: 0.1.5 + resolution: "duplexer3@npm:0.1.5" + checksum: e677cb4c48f031ca728601d6a20bf6aed4c629d69ef9643cb89c67583d673c4ec9317cc6427501f38bd8c368d3a18f173987cc02bd99d8cf8fe3d94259a22a20 + languageName: node + linkType: hard + +"eastasianwidth@npm:^0.2.0": + version: 0.2.0 + resolution: "eastasianwidth@npm:0.2.0" + checksum: 7d00d7cd8e49b9afa762a813faac332dee781932d6f2c848dc348939c4253f1d4564341b7af1d041853bc3f32c2ef141b58e0a4d9862c17a7f08f68df1e0f1ed languageName: node linkType: hard @@ -5591,9 +5911,9 @@ __metadata: linkType: hard "electron-to-chromium@npm:^1.3.47": - version: 1.4.172 - resolution: "electron-to-chromium@npm:1.4.172" - checksum: fe1db1c41cea55d700871f051f4662c5d988db9eca5f3f234294b73ccbaa2eabfd8349fa9c68433f5d6160988c604eff6bbf41f0600694ec03e758c0eb010355 + version: 1.4.656 + resolution: "electron-to-chromium@npm:1.4.656" + checksum: b9e00c81e74ee307141a216ef971efeff8784232a9eaa9074a65687b631029725f8f4ddeefd98c43287339cdadb6c7aba92c01806122f9cae813a95735fcd432 languageName: node linkType: hard @@ -5612,13 +5932,6 @@ __metadata: languageName: node linkType: hard -"emoji-regex@npm:^10.1.0": - version: 10.1.0 - resolution: "emoji-regex@npm:10.1.0" - checksum: 5bc780fc4d75f89369155a87c55f7e83a0bf72bcccda7df7f2c570cde4738d8b17d112d12afdadfec16647d1faef6501307b4304f81d35c823a938fe6547df0f - languageName: node - linkType: hard - "emoji-regex@npm:^7.0.1": version: 7.0.3 resolution: "emoji-regex@npm:7.0.3" @@ -5633,6 +5946,13 @@ __metadata: languageName: node linkType: hard +"emoji-regex@npm:^9.2.2": + version: 9.2.2 + resolution: "emoji-regex@npm:9.2.2" + checksum: 8487182da74aabd810ac6d6f1994111dfc0e331b01271ae01ec1eb0ad7b5ecc2bbbbd2f053c05cb55a1ac30449527d819bbfbf0e3de1023db308cbcb47f86601 + languageName: node + linkType: hard + "encodeurl@npm:~1.0.2": version: 1.0.2 resolution: "encodeurl@npm:1.0.2" @@ -5681,11 +6001,12 @@ __metadata: linkType: hard "enquirer@npm:^2.3.0": - version: 2.3.6 - resolution: "enquirer@npm:2.3.6" + version: 2.4.1 + resolution: "enquirer@npm:2.4.1" dependencies: ansi-colors: ^4.1.1 - checksum: 1c0911e14a6f8d26721c91e01db06092a5f7675159f0261d69c403396a385afd13dd76825e7678f66daffa930cfaa8d45f506fb35f818a2788463d022af1b884 + strip-ansi: ^6.0.1 + checksum: f080f11a74209647dbf347a7c6a83c8a47ae1ebf1e75073a808bc1088eb780aa54075bfecd1bcdb3e3c724520edb8e6ee05da031529436b421b71066fcc48cb5 languageName: node linkType: hard @@ -5730,34 +6051,50 @@ __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.1": - version: 1.20.1 - resolution: "es-abstract@npm:1.20.1" +"es-abstract@npm:^1.22.1, es-abstract@npm:^1.22.3": + version: 1.22.3 + resolution: "es-abstract@npm:1.22.3" dependencies: - call-bind: ^1.0.2 + array-buffer-byte-length: ^1.0.0 + arraybuffer.prototype.slice: ^1.0.2 + available-typed-arrays: ^1.0.5 + call-bind: ^1.0.5 + es-set-tostringtag: ^2.0.1 es-to-primitive: ^1.2.1 - function-bind: ^1.1.1 - function.prototype.name: ^1.1.5 - get-intrinsic: ^1.1.1 + function.prototype.name: ^1.1.6 + get-intrinsic: ^1.2.2 get-symbol-description: ^1.0.0 - has: ^1.0.3 + globalthis: ^1.0.3 + gopd: ^1.0.1 has-property-descriptors: ^1.0.0 + has-proto: ^1.0.1 has-symbols: ^1.0.3 - internal-slot: ^1.0.3 - is-callable: ^1.2.4 + hasown: ^2.0.0 + internal-slot: ^1.0.5 + is-array-buffer: ^3.0.2 + is-callable: ^1.2.7 is-negative-zero: ^2.0.2 is-regex: ^1.1.4 is-shared-array-buffer: ^1.0.2 is-string: ^1.0.7 + is-typed-array: ^1.1.12 is-weakref: ^1.0.2 - object-inspect: ^1.12.0 + object-inspect: ^1.13.1 object-keys: ^1.1.1 - object.assign: ^4.1.2 - regexp.prototype.flags: ^1.4.3 - string.prototype.trimend: ^1.0.5 - string.prototype.trimstart: ^1.0.5 + object.assign: ^4.1.4 + regexp.prototype.flags: ^1.5.1 + safe-array-concat: ^1.0.1 + safe-regex-test: ^1.0.0 + string.prototype.trim: ^1.2.8 + string.prototype.trimend: ^1.0.7 + string.prototype.trimstart: ^1.0.7 + typed-array-buffer: ^1.0.0 + typed-array-byte-length: ^1.0.0 + typed-array-byte-offset: ^1.0.0 + typed-array-length: ^1.0.4 unbox-primitive: ^1.0.2 - checksum: 28da27ae0ed9c76df7ee8ef5c278df79dcfdb554415faf7068bb7c58f8ba8e2a16bfb59e586844be6429ab4c302ca7748979d48442224cb1140b051866d74b7f + which-typed-array: ^1.1.13 + checksum: b1bdc962856836f6e72be10b58dc128282bdf33771c7a38ae90419d920fc3b36cc5d2b70a222ad8016e3fc322c367bf4e9e89fc2bc79b7e933c05b218e83d79a languageName: node linkType: hard @@ -5768,12 +6105,30 @@ __metadata: languageName: node linkType: hard +"es-errors@npm:^1.0.0, es-errors@npm:^1.2.1": + version: 1.3.0 + resolution: "es-errors@npm:1.3.0" + checksum: ec1414527a0ccacd7f15f4a3bc66e215f04f595ba23ca75cdae0927af099b5ec865f9f4d33e9d7e86f512f252876ac77d4281a7871531a50678132429b1271b5 + languageName: node + linkType: hard + +"es-set-tostringtag@npm:^2.0.1": + version: 2.0.2 + resolution: "es-set-tostringtag@npm:2.0.2" + dependencies: + get-intrinsic: ^1.2.2 + has-tostringtag: ^1.0.0 + hasown: ^2.0.0 + checksum: afcec3a4c9890ae14d7ec606204858441c801ff84f312538e1d1ccf1e5493c8b17bd672235df785f803756472cb4f2d49b87bde5237aef33411e74c22f194e07 + languageName: node + linkType: hard + "es-shim-unscopables@npm:^1.0.0": - version: 1.0.0 - resolution: "es-shim-unscopables@npm:1.0.0" + version: 1.0.2 + resolution: "es-shim-unscopables@npm:1.0.2" dependencies: - has: ^1.0.3 - checksum: 83e95cadbb6ee44d3644dfad60dcad7929edbc42c85e66c3e99aefd68a3a5c5665f2686885cddb47dfeabfd77bd5ea5a7060f2092a955a729bbd8834f0d86fa1 + hasown: ^2.0.0 + checksum: 432bd527c62065da09ed1d37a3f8e623c423683285e6188108286f4a1e8e164a5bcbfbc0051557c7d14633cd2a41ce24c7048e6bbb66a985413fd32f1be72626 languageName: node linkType: hard @@ -5789,13 +6144,13 @@ __metadata: linkType: hard "es5-ext@npm:^0.10.35, es5-ext@npm:^0.10.50": - version: 0.10.61 - resolution: "es5-ext@npm:0.10.61" + version: 0.10.62 + resolution: "es5-ext@npm:0.10.62" dependencies: es6-iterator: ^2.0.3 es6-symbol: ^3.1.3 next-tick: ^1.1.0 - checksum: 2f2034e91e77fe247d94f0fd13a94bcf113273b7cc4650794d6795e377267ffb2425d3a891bd8c4d9c8b990e16e17dd7c28f12dbd3fa4b0909d0874892f491bf + checksum: 25f42f6068cfc6e393cf670bc5bba249132c5f5ec2dd0ed6e200e6274aca2fed8e9aec8a31c76031744c78ca283c57f0b41c7e737804c6328c7b8d3fbcba7983 languageName: node linkType: hard @@ -5850,13 +6205,6 @@ __metadata: languageName: node linkType: hard -"escape-string-regexp@npm:1.0.5, escape-string-regexp@npm:^1.0.2, escape-string-regexp@npm:^1.0.5": - version: 1.0.5 - resolution: "escape-string-regexp@npm:1.0.5" - checksum: 6092fda75c63b110c706b6a9bfde8a612ad595b628f0bd2147eea1d3406723020810e591effc7db1da91d80a71a737a313567c5abb3813e8d9c71f4aa595b410 - languageName: node - linkType: hard - "escape-string-regexp@npm:4.0.0, escape-string-regexp@npm:^4.0.0": version: 4.0.0 resolution: "escape-string-regexp@npm:4.0.0" @@ -5864,6 +6212,13 @@ __metadata: languageName: node linkType: hard +"escape-string-regexp@npm:^1.0.2, escape-string-regexp@npm:^1.0.5": + version: 1.0.5 + resolution: "escape-string-regexp@npm:1.0.5" + checksum: 6092fda75c63b110c706b6a9bfde8a612ad595b628f0bd2147eea1d3406723020810e591effc7db1da91d80a71a737a313567c5abb3813e8d9c71f4aa595b410 + languageName: node + linkType: hard + "escodegen@npm:1.8.x": version: 1.8.1 resolution: "escodegen@npm:1.8.1" @@ -5897,45 +6252,48 @@ __metadata: linkType: hard "eslint-config-prettier@npm:^8.5.0": - version: 8.5.0 - resolution: "eslint-config-prettier@npm:8.5.0" + version: 8.10.0 + resolution: "eslint-config-prettier@npm:8.10.0" peerDependencies: eslint: ">=7.0.0" bin: eslint-config-prettier: bin/cli.js - checksum: 0d0f5c32e7a0ad91249467ce71ca92394ccd343178277d318baf32063b79ea90216f4c81d1065d60f96366fdc60f151d4d68ae7811a58bd37228b84c2083f893 + checksum: 153266badd477e49b0759816246b2132f1dbdb6c7f313ca60a9af5822fd1071c2bc5684a3720d78b725452bbac04bb130878b2513aea5e72b1b792de5a69fec8 languageName: node linkType: hard "eslint-config-standard@npm:^17.0.0": - version: 17.0.0 - resolution: "eslint-config-standard@npm:17.0.0" + version: 17.1.0 + resolution: "eslint-config-standard@npm:17.1.0" peerDependencies: eslint: ^8.0.1 eslint-plugin-import: ^2.25.2 - eslint-plugin-n: ^15.0.0 + eslint-plugin-n: "^15.0.0 || ^16.0.0 " eslint-plugin-promise: ^6.0.0 - checksum: dc0ed51e186fd963ff2c0819d33ef580afce11b11036cbcf5e74427e26e514c2b1be96b8ffe74fd2fd00263554a0d49cc873fcf76f17c3dfdba614b45d7fd7da + checksum: 8ed14ffe424b8a7e67b85e44f75c46dc4c6954f7c474c871c56fb0daf40b6b2a7af2db55102b12a440158b2be898e1fb8333b05e3dbeaeaef066fdbc863eaa88 languageName: node linkType: hard -"eslint-import-resolver-node@npm:^0.3.6": - version: 0.3.6 - resolution: "eslint-import-resolver-node@npm:0.3.6" +"eslint-import-resolver-node@npm:^0.3.9": + version: 0.3.9 + resolution: "eslint-import-resolver-node@npm:0.3.9" dependencies: debug: ^3.2.7 - resolve: ^1.20.0 - checksum: 6266733af1e112970e855a5bcc2d2058fb5ae16ad2a6d400705a86b29552b36131ffc5581b744c23d550de844206fb55e9193691619ee4dbf225c4bde526b1c8 + is-core-module: ^2.13.0 + resolve: ^1.22.4 + checksum: 439b91271236b452d478d0522a44482e8c8540bf9df9bd744062ebb89ab45727a3acd03366a6ba2bdbcde8f9f718bab7fe8db64688aca75acf37e04eafd25e22 languageName: node linkType: hard -"eslint-module-utils@npm:^2.7.3": - version: 2.7.3 - resolution: "eslint-module-utils@npm:2.7.3" +"eslint-module-utils@npm:^2.8.0": + version: 2.8.0 + resolution: "eslint-module-utils@npm:2.8.0" dependencies: debug: ^3.2.7 - find-up: ^2.1.0 - checksum: 77048263f309167a1e6a1e1b896bfb5ddd1d3859b2e2abbd9c32c432aee13d610d46e6820b1ca81b37fba437cf423a404bc6649be64ace9148a3062d1886a678 + peerDependenciesMeta: + eslint: + optional: true + checksum: 74c6dfea7641ebcfe174be61168541a11a14aa8d72e515f5f09af55cd0d0862686104b0524aa4b8e0ce66418a44aa38a94d2588743db5fd07a6b49ffd16921d2 languageName: node linkType: hard @@ -5964,43 +6322,47 @@ __metadata: linkType: hard "eslint-plugin-import@npm:^2.26.0": - version: 2.26.0 - resolution: "eslint-plugin-import@npm:2.26.0" + version: 2.29.1 + resolution: "eslint-plugin-import@npm:2.29.1" dependencies: - array-includes: ^3.1.4 - array.prototype.flat: ^1.2.5 - debug: ^2.6.9 + array-includes: ^3.1.7 + array.prototype.findlastindex: ^1.2.3 + array.prototype.flat: ^1.3.2 + array.prototype.flatmap: ^1.3.2 + debug: ^3.2.7 doctrine: ^2.1.0 - eslint-import-resolver-node: ^0.3.6 - eslint-module-utils: ^2.7.3 - has: ^1.0.3 - is-core-module: ^2.8.1 + eslint-import-resolver-node: ^0.3.9 + eslint-module-utils: ^2.8.0 + hasown: ^2.0.0 + is-core-module: ^2.13.1 is-glob: ^4.0.3 minimatch: ^3.1.2 - object.values: ^1.1.5 - resolve: ^1.22.0 - tsconfig-paths: ^3.14.1 + object.fromentries: ^2.0.7 + object.groupby: ^1.0.1 + object.values: ^1.1.7 + semver: ^6.3.1 + tsconfig-paths: ^3.15.0 peerDependencies: eslint: ^2 || ^3 || ^4 || ^5 || ^6 || ^7.2.0 || ^8 - checksum: 0bf77ad80339554481eafa2b1967449e1f816b94c7a6f9614ce33fb4083c4e6c050f10d241dd50b4975d47922880a34de1e42ea9d8e6fd663ebb768baa67e655 + checksum: e65159aef808136d26d029b71c8c6e4cb5c628e65e5de77f1eb4c13a379315ae55c9c3afa847f43f4ff9df7e54515c77ffc6489c6a6f81f7dd7359267577468c languageName: node linkType: hard "eslint-plugin-n@npm:^15.2.4": - version: 15.2.4 - resolution: "eslint-plugin-n@npm:15.2.4" + version: 15.7.0 + resolution: "eslint-plugin-n@npm:15.7.0" dependencies: builtins: ^5.0.1 eslint-plugin-es: ^4.1.0 eslint-utils: ^3.0.0 ignore: ^5.1.1 - is-core-module: ^2.9.0 + is-core-module: ^2.11.0 minimatch: ^3.1.2 - resolve: ^1.10.1 - semver: ^7.3.7 + resolve: ^1.22.1 + semver: ^7.3.8 peerDependencies: eslint: ">=7.0.0" - checksum: dd651651ab76120e45707ee968d846e3ffffb42d1035792fdef6d3b0dcfddf3673bc6a09cb2fac8c5f1d081f14f2a67fc52295d5ed1d2edfb5beead93284eaac + checksum: cfbcc67e62adf27712afdeadf13223cb9717f95d4af8442056d9d4c97a8b88af76b7969f75deaac26fa98481023d6b7c9e43a28909e7f0468f40b3024b7bcfae languageName: node linkType: hard @@ -6043,21 +6405,33 @@ __metadata: linkType: hard "eslint-plugin-promise@npm:^6.0.0": - version: 6.0.0 - resolution: "eslint-plugin-promise@npm:6.0.0" + version: 6.1.1 + resolution: "eslint-plugin-promise@npm:6.1.1" peerDependencies: eslint: ^7.0.0 || ^8.0.0 - checksum: 7e761507c51267b77e4ad710e7c8938aa4f8f69b975886034e57497a1816e9527eda364e25aac03d1b4e0df2e738ba98e49ad075d028824fcfea533a1419751c + checksum: 46b9a4f79dae5539987922afc27cc17cbccdecf4f0ba19c0ccbf911b0e31853e9f39d9959eefb9637461b52772afa1a482f1f87ff16c1ba38bdb6fcf21897e9a languageName: node linkType: hard -"eslint-scope@npm:^4.0.3": - version: 4.0.3 - resolution: "eslint-scope@npm:4.0.3" +"eslint-plugin-unused-imports@npm:^3.0.0": + version: 3.0.0 + resolution: "eslint-plugin-unused-imports@npm:3.0.0" dependencies: - esrecurse: ^4.1.0 - estraverse: ^4.1.1 - checksum: c5f835f681884469991fe58d76a554688d9c9e50811299ccd4a8f79993a039f5bcb0ee6e8de2b0017d97c794b5832ef3b21c9aac66228e3aa0f7a0485bcfb65b + eslint-rule-composer: ^0.3.0 + peerDependencies: + "@typescript-eslint/eslint-plugin": ^6.0.0 + eslint: ^8.0.0 + peerDependenciesMeta: + "@typescript-eslint/eslint-plugin": + optional: true + checksum: 51666f62cc8dccba2895ced83f3c1e0b78b68c357e17360e156c4db548bfdeda34cbd8725192fb4903f22d5069400fb22ded6039631df01ee82fd618dc307247 + languageName: node + linkType: hard + +"eslint-rule-composer@npm:^0.3.0": + version: 0.3.0 + resolution: "eslint-rule-composer@npm:0.3.0" + checksum: c2f57cded8d1c8f82483e0ce28861214347e24fd79fd4144667974cd334d718f4ba05080aaef2399e3bbe36f7d6632865110227e6b176ed6daa2d676df9281b1 languageName: node linkType: hard @@ -6071,17 +6445,17 @@ __metadata: languageName: node linkType: hard -"eslint-scope@npm:^7.1.1": - version: 7.1.1 - resolution: "eslint-scope@npm:7.1.1" +"eslint-scope@npm:^7.2.2": + version: 7.2.2 + resolution: "eslint-scope@npm:7.2.2" dependencies: esrecurse: ^4.3.0 estraverse: ^5.2.0 - checksum: 9f6e974ab2db641ca8ab13508c405b7b859e72afe9f254e8131ff154d2f40c99ad4545ce326fd9fde3212ff29707102562a4834f1c48617b35d98c71a97fbf3e + checksum: ec97dbf5fb04b94e8f4c5a91a7f0a6dd3c55e46bfc7bbcd0e3138c3a76977570e02ed89a1810c778dcd72072ff0e9621ba1379b4babe53921d71e2e4486fda3e languageName: node linkType: hard -"eslint-utils@npm:^1.3.1, eslint-utils@npm:^1.4.3": +"eslint-utils@npm:^1.4.3": version: 1.4.3 resolution: "eslint-utils@npm:1.4.3" dependencies: @@ -6110,70 +6484,24 @@ __metadata: languageName: node linkType: hard -"eslint-visitor-keys@npm:^1.0.0, eslint-visitor-keys@npm:^1.1.0": +"eslint-visitor-keys@npm:^1.1.0": version: 1.3.0 - resolution: "eslint-visitor-keys@npm:1.3.0" - checksum: 37a19b712f42f4c9027e8ba98c2b06031c17e0c0a4c696cd429bd9ee04eb43889c446f2cd545e1ff51bef9593fcec94ecd2c2ef89129fcbbf3adadbef520376a - languageName: node - linkType: hard - -"eslint-visitor-keys@npm:^2.0.0": - version: 2.1.0 - resolution: "eslint-visitor-keys@npm:2.1.0" - checksum: e3081d7dd2611a35f0388bbdc2f5da60b3a3c5b8b6e928daffff7391146b434d691577aa95064c8b7faad0b8a680266bcda0a42439c18c717b80e6718d7e267d - languageName: node - linkType: hard - -"eslint-visitor-keys@npm:^3.3.0": - version: 3.3.0 - resolution: "eslint-visitor-keys@npm:3.3.0" - checksum: d59e68a7c5a6d0146526b0eec16ce87fbf97fe46b8281e0d41384224375c4e52f5ffb9e16d48f4ea50785cde93f766b0c898e31ab89978d88b0e1720fbfb7808 - languageName: node - linkType: hard - -"eslint@npm:^5.6.0": - version: 5.16.0 - resolution: "eslint@npm:5.16.0" - dependencies: - "@babel/code-frame": ^7.0.0 - ajv: ^6.9.1 - chalk: ^2.1.0 - cross-spawn: ^6.0.5 - debug: ^4.0.1 - doctrine: ^3.0.0 - eslint-scope: ^4.0.3 - eslint-utils: ^1.3.1 - eslint-visitor-keys: ^1.0.0 - espree: ^5.0.1 - esquery: ^1.0.1 - esutils: ^2.0.2 - file-entry-cache: ^5.0.1 - functional-red-black-tree: ^1.0.1 - glob: ^7.1.2 - globals: ^11.7.0 - ignore: ^4.0.6 - import-fresh: ^3.0.0 - imurmurhash: ^0.1.4 - inquirer: ^6.2.2 - js-yaml: ^3.13.0 - json-stable-stringify-without-jsonify: ^1.0.1 - levn: ^0.3.0 - lodash: ^4.17.11 - minimatch: ^3.0.4 - mkdirp: ^0.5.1 - natural-compare: ^1.4.0 - optionator: ^0.8.2 - path-is-inside: ^1.0.2 - progress: ^2.0.0 - regexpp: ^2.0.1 - semver: ^5.5.1 - strip-ansi: ^4.0.0 - strip-json-comments: ^2.0.1 - table: ^5.2.3 - text-table: ^0.2.0 - bin: - eslint: ./bin/eslint.js - checksum: 53c6b9420992df95f986dc031f76949edbea14bdeed4e40d8cda8970fbf0fc013c6d91b98f469b6477753e50c9af133c1a768e421a1c160ec2cac7a246e05494 + resolution: "eslint-visitor-keys@npm:1.3.0" + checksum: 37a19b712f42f4c9027e8ba98c2b06031c17e0c0a4c696cd429bd9ee04eb43889c446f2cd545e1ff51bef9593fcec94ecd2c2ef89129fcbbf3adadbef520376a + languageName: node + linkType: hard + +"eslint-visitor-keys@npm:^2.0.0": + version: 2.1.0 + resolution: "eslint-visitor-keys@npm:2.1.0" + checksum: e3081d7dd2611a35f0388bbdc2f5da60b3a3c5b8b6e928daffff7391146b434d691577aa95064c8b7faad0b8a680266bcda0a42439c18c717b80e6718d7e267d + languageName: node + linkType: hard + +"eslint-visitor-keys@npm:^3.3.0, eslint-visitor-keys@npm:^3.4.1, eslint-visitor-keys@npm:^3.4.3": + version: 3.4.3 + resolution: "eslint-visitor-keys@npm:3.4.3" + checksum: 36e9ef87fca698b6fd7ca5ca35d7b2b6eeaaf106572e2f7fd31c12d3bfdaccdb587bba6d3621067e5aece31c8c3a348b93922ab8f7b2cbc6aaab5e1d89040c60 languageName: node linkType: hard @@ -6225,58 +6553,50 @@ __metadata: linkType: hard "eslint@npm:^8.20.0": - version: 8.20.0 - resolution: "eslint@npm:8.20.0" - dependencies: - "@eslint/eslintrc": ^1.3.0 - "@humanwhocodes/config-array": ^0.9.2 - ajv: ^6.10.0 + version: 8.56.0 + resolution: "eslint@npm:8.56.0" + dependencies: + "@eslint-community/eslint-utils": ^4.2.0 + "@eslint-community/regexpp": ^4.6.1 + "@eslint/eslintrc": ^2.1.4 + "@eslint/js": 8.56.0 + "@humanwhocodes/config-array": ^0.11.13 + "@humanwhocodes/module-importer": ^1.0.1 + "@nodelib/fs.walk": ^1.2.8 + "@ungap/structured-clone": ^1.2.0 + ajv: ^6.12.4 chalk: ^4.0.0 cross-spawn: ^7.0.2 debug: ^4.3.2 doctrine: ^3.0.0 escape-string-regexp: ^4.0.0 - eslint-scope: ^7.1.1 - eslint-utils: ^3.0.0 - eslint-visitor-keys: ^3.3.0 - espree: ^9.3.2 - esquery: ^1.4.0 + eslint-scope: ^7.2.2 + eslint-visitor-keys: ^3.4.3 + espree: ^9.6.1 + esquery: ^1.4.2 esutils: ^2.0.2 fast-deep-equal: ^3.1.3 file-entry-cache: ^6.0.1 - functional-red-black-tree: ^1.0.1 - glob-parent: ^6.0.1 - globals: ^13.15.0 + find-up: ^5.0.0 + glob-parent: ^6.0.2 + globals: ^13.19.0 + graphemer: ^1.4.0 ignore: ^5.2.0 - import-fresh: ^3.0.0 imurmurhash: ^0.1.4 is-glob: ^4.0.0 + is-path-inside: ^3.0.3 js-yaml: ^4.1.0 json-stable-stringify-without-jsonify: ^1.0.1 levn: ^0.4.1 lodash.merge: ^4.6.2 minimatch: ^3.1.2 natural-compare: ^1.4.0 - optionator: ^0.9.1 - regexpp: ^3.2.0 + optionator: ^0.9.3 strip-ansi: ^6.0.1 - strip-json-comments: ^3.1.0 text-table: ^0.2.0 - v8-compile-cache: ^2.0.3 bin: eslint: bin/eslint.js - checksum: a31adf390d71d916925586bc8467b48f620e93dd0416bc1e897d99265af88b48d4eba3985b5ff4653ae5cc46311a360d373574002277e159bb38a4363abf9228 - languageName: node - linkType: hard - -"espree@npm:^5.0.1": - version: 5.0.1 - resolution: "espree@npm:5.0.1" - dependencies: - acorn: ^6.0.7 - acorn-jsx: ^5.0.0 - eslint-visitor-keys: ^1.0.0 - checksum: a091aac2bddf872484b0a7e779e3a1ffab32d1c55a6c4f99e483613a0149443531272c191eda1c7c827e32a9e10f6ce7ea6b131c7b3f4e12471fe618ebbc5b7e + checksum: 883436d1e809b4a25d9eb03d42f584b84c408dbac28b0019f6ea07b5177940bf3cca86208f749a6a1e0039b63e085ee47aca1236c30721e91f0deef5cc5a5136 languageName: node linkType: hard @@ -6291,14 +6611,14 @@ __metadata: languageName: node linkType: hard -"espree@npm:^9.3.2": - version: 9.3.2 - resolution: "espree@npm:9.3.2" +"espree@npm:^9.6.0, espree@npm:^9.6.1": + version: 9.6.1 + resolution: "espree@npm:9.6.1" dependencies: - acorn: ^8.7.1 + acorn: ^8.9.0 acorn-jsx: ^5.3.2 - eslint-visitor-keys: ^3.3.0 - checksum: 9a790d6779847051e87f70d720a0f6981899a722419e80c92ab6dee01e1ab83b8ce52d11b4dc96c2c490182efb5a4c138b8b0d569205bfe1cd4629e658e58c30 + eslint-visitor-keys: ^3.4.1 + checksum: eb8c149c7a2a77b3f33a5af80c10875c3abd65450f60b8af6db1bfcfa8f101e21c1e56a561c6dc13b848e18148d43469e7cd208506238554fb5395a9ea5a1ab9 languageName: node linkType: hard @@ -6322,16 +6642,16 @@ __metadata: languageName: node linkType: hard -"esquery@npm:^1.0.1, esquery@npm:^1.4.0": - version: 1.4.0 - resolution: "esquery@npm:1.4.0" +"esquery@npm:^1.0.1, esquery@npm:^1.4.2": + version: 1.5.0 + resolution: "esquery@npm:1.5.0" dependencies: estraverse: ^5.1.0 - checksum: a0807e17abd7fbe5fbd4fab673038d6d8a50675cdae6b04fbaa520c34581be0c5fa24582990e8acd8854f671dd291c78bb2efb9e0ed5b62f33bac4f9cf820210 + checksum: aefb0d2596c230118656cd4ec7532d447333a410a48834d80ea648b1e7b5c9bc9ed8b5e33a89cb04e487b60d622f44cf5713bf4abed7c97343edefdc84a35900 languageName: node linkType: hard -"esrecurse@npm:^4.1.0, esrecurse@npm:^4.3.0": +"esrecurse@npm:^4.3.0": version: 4.3.0 resolution: "esrecurse@npm:4.3.0" dependencies: @@ -6401,22 +6721,20 @@ __metadata: linkType: hard "eth-gas-reporter@npm:^0.2.25": - version: 0.2.25 - resolution: "eth-gas-reporter@npm:0.2.25" + version: 0.2.27 + resolution: "eth-gas-reporter@npm:0.2.27" dependencies: - "@ethersproject/abi": ^5.0.0-beta.146 "@solidity-parser/parser": ^0.14.0 + axios: ^1.5.1 cli-table3: ^0.5.0 colors: 1.4.0 ethereum-cryptography: ^1.0.3 - ethers: ^4.0.40 + ethers: ^5.7.2 fs-readdir-recursive: ^1.1.0 lodash: ^4.17.14 markdown-table: ^1.1.3 - mocha: ^7.1.1 + mocha: ^10.2.0 req-cwd: ^2.0.0 - request: ^2.88.0 - request-promise-native: ^1.0.5 sha1: ^1.1.1 sync-request: ^6.0.0 peerDependencies: @@ -6424,7 +6742,7 @@ __metadata: peerDependenciesMeta: "@codechecks/client": optional: true - checksum: 3bfa81e554b069bb817f2a073a601a0429e6b582c56ad99db0727dc2a102ab00fc27888820b8a042a194a8fb7d40954d10cd7b011ede6b8170285d2d5a88666c + checksum: 9a26a4936693de6dbe633a9e6f9d69eb93c9d45c61ecbc20702a72f15ade424785e29ae8e62ea3a2afc49ea22a4777a71914dc8da1b8587e9d47d085a3246784 languageName: node linkType: hard @@ -6597,14 +6915,26 @@ __metadata: linkType: hard "ethereum-cryptography@npm:^1.0.3": - version: 1.1.0 - resolution: "ethereum-cryptography@npm:1.1.0" + version: 1.2.0 + resolution: "ethereum-cryptography@npm:1.2.0" + dependencies: + "@noble/hashes": 1.2.0 + "@noble/secp256k1": 1.7.1 + "@scure/bip32": 1.1.5 + "@scure/bip39": 1.1.1 + checksum: 97e8e8253cb9f5a9271bd0201c37609c451c890eb85883b9c564f14743c3d7c673287406c93bf5604307593ee298ad9a03983388b85c11ca61461b9fc1a4f2c7 + languageName: node + linkType: hard + +"ethereum-cryptography@npm:^2.0.0, ethereum-cryptography@npm:^2.1.2": + version: 2.1.3 + resolution: "ethereum-cryptography@npm:2.1.3" dependencies: - "@noble/hashes": 1.1.1 - "@noble/secp256k1": 1.6.0 - "@scure/bip32": 1.1.0 - "@scure/bip39": 1.1.0 - checksum: cba0bc58272ccc9eca4cf045bd4b6edb083486069ae0a62fba0fea5385a8c4257ea0faf135868440044fb37047bc0d7f39090c21ea409be106a9f9004a3792b5 + "@noble/curves": 1.3.0 + "@noble/hashes": 1.3.3 + "@scure/bip32": 1.3.3 + "@scure/bip39": 1.2.2 + checksum: 7f9c14f868a588641179cace3eb86c332c4743290865db699870710253cabc4dc74bd4bce5e7bc6db667482e032e94d6f79521219eb6be5dc422059d279a27b7 languageName: node linkType: hard @@ -6796,7 +7126,7 @@ __metadata: languageName: node linkType: hard -"ethereumjs-util@npm:^7.0.2, ethereumjs-util@npm:^7.0.3, ethereumjs-util@npm:^7.1.0": +"ethereumjs-util@npm:^7.0.2, ethereumjs-util@npm:^7.0.3": version: 7.1.5 resolution: "ethereumjs-util@npm:7.1.5" dependencies: @@ -6868,58 +7198,41 @@ __metadata: languageName: node linkType: hard -"ethers@npm:^4.0.40": - version: 4.0.49 - resolution: "ethers@npm:4.0.49" - dependencies: - aes-js: 3.0.0 - bn.js: ^4.11.9 - elliptic: 6.5.4 - hash.js: 1.1.3 - js-sha3: 0.5.7 - scrypt-js: 2.0.4 - setimmediate: 1.0.4 - uuid: 2.0.1 - xmlhttprequest: 1.8.0 - checksum: 357115348a5f1484c7745fae1d852876788216c7d94c072c80132192f1800c4d388433ea2456750856641d6d4eed8a3b41847eb44f5e1c42139963864e3bcc38 - languageName: node - linkType: hard - -"ethers@npm:^5.0.1, ethers@npm:^5.0.2, ethers@npm:^5.5.2, ethers@npm:^5.6.9": - version: 5.6.9 - resolution: "ethers@npm:5.6.9" - dependencies: - "@ethersproject/abi": 5.6.4 - "@ethersproject/abstract-provider": 5.6.1 - "@ethersproject/abstract-signer": 5.6.2 - "@ethersproject/address": 5.6.1 - "@ethersproject/base64": 5.6.1 - "@ethersproject/basex": 5.6.1 - "@ethersproject/bignumber": 5.6.2 - "@ethersproject/bytes": 5.6.1 - "@ethersproject/constants": 5.6.1 - "@ethersproject/contracts": 5.6.2 - "@ethersproject/hash": 5.6.1 - "@ethersproject/hdnode": 5.6.2 - "@ethersproject/json-wallets": 5.6.1 - "@ethersproject/keccak256": 5.6.1 - "@ethersproject/logger": 5.6.0 - "@ethersproject/networks": 5.6.4 - "@ethersproject/pbkdf2": 5.6.1 - "@ethersproject/properties": 5.6.0 - "@ethersproject/providers": 5.6.8 - "@ethersproject/random": 5.6.1 - "@ethersproject/rlp": 5.6.1 - "@ethersproject/sha2": 5.6.1 - "@ethersproject/signing-key": 5.6.2 - "@ethersproject/solidity": 5.6.1 - "@ethersproject/strings": 5.6.1 - "@ethersproject/transactions": 5.6.2 - "@ethersproject/units": 5.6.1 - "@ethersproject/wallet": 5.6.2 - "@ethersproject/web": 5.6.1 - "@ethersproject/wordlists": 5.6.1 - checksum: e4a029ad55da2355cb7b0ff178b38b0df27f9013604b0600c246dba297223ac2ce8ef0380758fa535cd82ea46bceb4a71aeb29949e1693f3a9c60d4cdaceb208 +"ethers@npm:^5.0.1, ethers@npm:^5.0.2, ethers@npm:^5.5.2, ethers@npm:^5.6.9, ethers@npm:^5.7.1, ethers@npm:^5.7.2": + version: 5.7.2 + resolution: "ethers@npm:5.7.2" + dependencies: + "@ethersproject/abi": 5.7.0 + "@ethersproject/abstract-provider": 5.7.0 + "@ethersproject/abstract-signer": 5.7.0 + "@ethersproject/address": 5.7.0 + "@ethersproject/base64": 5.7.0 + "@ethersproject/basex": 5.7.0 + "@ethersproject/bignumber": 5.7.0 + "@ethersproject/bytes": 5.7.0 + "@ethersproject/constants": 5.7.0 + "@ethersproject/contracts": 5.7.0 + "@ethersproject/hash": 5.7.0 + "@ethersproject/hdnode": 5.7.0 + "@ethersproject/json-wallets": 5.7.0 + "@ethersproject/keccak256": 5.7.0 + "@ethersproject/logger": 5.7.0 + "@ethersproject/networks": 5.7.1 + "@ethersproject/pbkdf2": 5.7.0 + "@ethersproject/properties": 5.7.0 + "@ethersproject/providers": 5.7.2 + "@ethersproject/random": 5.7.0 + "@ethersproject/rlp": 5.7.0 + "@ethersproject/sha2": 5.7.0 + "@ethersproject/signing-key": 5.7.0 + "@ethersproject/solidity": 5.7.0 + "@ethersproject/strings": 5.7.0 + "@ethersproject/transactions": 5.7.0 + "@ethersproject/units": 5.7.0 + "@ethersproject/wallet": 5.7.0 + "@ethersproject/web": 5.7.1 + "@ethersproject/wordlists": 5.7.0 + checksum: b7c08cf3e257185a7946117dbbf764433b7ba0e77c27298dec6088b3bc871aff711462b0621930c56880ff0a7ceb8b1d3a361ffa259f93377b48e34107f62553 languageName: node linkType: hard @@ -7023,13 +7336,20 @@ __metadata: languageName: node linkType: hard +"exponential-backoff@npm:^3.1.1": + version: 3.1.1 + resolution: "exponential-backoff@npm:3.1.1" + checksum: 3d21519a4f8207c99f7457287291316306255a328770d320b401114ec8481986e4e467e854cb9914dd965e0a1ca810a23ccb559c642c88f4c7f55c55778a9b48 + languageName: node + linkType: hard + "express@npm:^4.14.0": - version: 4.18.1 - resolution: "express@npm:4.18.1" + version: 4.18.2 + resolution: "express@npm:4.18.2" dependencies: accepts: ~1.3.8 array-flatten: 1.1.1 - body-parser: 1.20.0 + body-parser: 1.20.1 content-disposition: 0.5.4 content-type: ~1.0.4 cookie: 0.5.0 @@ -7048,7 +7368,7 @@ __metadata: parseurl: ~1.3.3 path-to-regexp: 0.1.7 proxy-addr: ~2.0.7 - qs: 6.10.3 + qs: 6.11.0 range-parser: ~1.2.1 safe-buffer: 5.2.1 send: 0.18.0 @@ -7058,16 +7378,16 @@ __metadata: type-is: ~1.6.18 utils-merge: 1.0.1 vary: ~1.1.2 - checksum: c3d44c92e48226ef32ec978becfedb0ecf0ca21316bfd33674b3c5d20459840584f2325726a4f17f33d9c99f769636f728982d1c5433a5b6fe6eb95b8cf0c854 + checksum: 3c4b9b076879442f6b968fe53d85d9f1eeacbb4f4c41e5f16cc36d77ce39a2b0d81b3f250514982110d815b2f7173f5561367f9110fcc541f9371948e8c8b037 languageName: node linkType: hard "ext@npm:^1.1.2": - version: 1.6.0 - resolution: "ext@npm:1.6.0" + version: 1.7.0 + resolution: "ext@npm:1.7.0" dependencies: - type: ^2.5.0 - checksum: ca3ef4619e838f441a92238a98b77ac873da2175ace746c64303ffe2c3208e79a3acf3bf7004e40b720f3c2a83bf0143e6dd4a7cdfae6e73f54a3bfc7a14b5c2 + type: ^2.7.2 + checksum: ef481f9ef45434d8c867cfd09d0393b60945b7c8a1798bedc4514cb35aac342ccb8d8ecb66a513e6a2b4ec1e294a338e3124c49b29736f8e7c735721af352c31 languageName: node linkType: hard @@ -7154,6 +7474,13 @@ __metadata: languageName: node linkType: hard +"fast-base64-decode@npm:^1.0.0": + version: 1.0.0 + resolution: "fast-base64-decode@npm:1.0.0" + checksum: 4c59eb1775a7f132333f296c5082476fdcc8f58d023c42ed6d378d2e2da4c328c7a71562f271181a725dd17cdaa8f2805346cc330cdbad3b8e4b9751508bd0a3 + languageName: node + linkType: hard + "fast-deep-equal@npm:^3.1.1, fast-deep-equal@npm:^3.1.3": version: 3.1.3 resolution: "fast-deep-equal@npm:3.1.3" @@ -7161,10 +7488,10 @@ __metadata: languageName: node linkType: hard -"fast-diff@npm:^1.1.2": - version: 1.2.0 - resolution: "fast-diff@npm:1.2.0" - checksum: 1b5306eaa9e826564d9e5ffcd6ebd881eb5f770b3f977fcbf38f05c824e42172b53c79920e8429c54eb742ce15a0caf268b0fdd5b38f6de52234c4a8368131ae +"fast-diff@npm:^1.1.2, fast-diff@npm:^1.2.0": + version: 1.3.0 + resolution: "fast-diff@npm:1.3.0" + checksum: d22d371b994fdc8cce9ff510d7b8dc4da70ac327bcba20df607dd5b9cae9f908f4d1028f5fe467650f058d1e7270235ae0b8230809a262b4df587a3b3aa216c3 languageName: node linkType: hard @@ -7176,15 +7503,15 @@ __metadata: linkType: hard "fast-glob@npm:^3.0.3, fast-glob@npm:^3.2.9": - version: 3.2.11 - resolution: "fast-glob@npm:3.2.11" + version: 3.3.2 + resolution: "fast-glob@npm:3.3.2" dependencies: "@nodelib/fs.stat": ^2.0.2 "@nodelib/fs.walk": ^1.2.3 glob-parent: ^5.1.2 merge2: ^1.3.0 micromatch: ^4.0.4 - checksum: f473105324a7780a20c06de842e15ddbb41d3cb7e71d1e4fe6e8373204f22245d54f5ab9e2061e6a1c613047345954d29b022e0e76f5c28b1df9858179a0e6d7 + checksum: 900e4979f4dbc3313840078419245621259f349950411ca2fa445a2f9a1a6d98c3b5e7e0660c5ccd563aa61abe133a21765c6c0dec8e57da1ba71d8000b05ec1 languageName: node linkType: hard @@ -7203,11 +7530,11 @@ __metadata: linkType: hard "fastq@npm:^1.6.0": - version: 1.13.0 - resolution: "fastq@npm:1.13.0" + version: 1.17.1 + resolution: "fastq@npm:1.17.1" dependencies: reusify: ^1.0.4 - checksum: 32cf15c29afe622af187d12fc9cd93e160a0cb7c31a3bb6ace86b7dea3b28e7b72acde89c882663f307b2184e14782c6c664fa315973c03626c7d4bff070bb0b + checksum: a8c5b26788d5a1763f88bae56a8ddeee579f935a831c5fe7a8268cea5b0a91fbfe705f612209e02d639b881d7b48e461a50da4a10cfaa40da5ca7cc9da098d88 languageName: node linkType: hard @@ -7220,15 +7547,6 @@ __metadata: languageName: node linkType: hard -"figures@npm:^2.0.0": - version: 2.0.0 - resolution: "figures@npm:2.0.0" - dependencies: - escape-string-regexp: ^1.0.5 - checksum: 081beb16ea57d1716f8447c694f637668322398b57017b20929376aaf5def9823b35245b734cdd87e4832dc96e9c6f46274833cada77bfe15e5f980fea1fd21f - languageName: node - linkType: hard - "figures@npm:^3.0.0": version: 3.2.0 resolution: "figures@npm:3.2.0" @@ -7311,16 +7629,7 @@ __metadata: languageName: node linkType: hard -"find-up@npm:3.0.0, find-up@npm:^3.0.0": - version: 3.0.0 - resolution: "find-up@npm:3.0.0" - dependencies: - locate-path: ^3.0.0 - checksum: 38eba3fe7a66e4bc7f0f5a1366dc25508b7cfc349f852640e3678d26ad9a6d7e2c43eff0a472287de4a9753ef58f066a0ea892a256fa3636ad51b3fe1e17fae9 - languageName: node - linkType: hard - -"find-up@npm:5.0.0": +"find-up@npm:5.0.0, find-up@npm:^5.0.0": version: 5.0.0 resolution: "find-up@npm:5.0.0" dependencies: @@ -7349,6 +7658,15 @@ __metadata: languageName: node linkType: hard +"find-up@npm:^3.0.0": + version: 3.0.0 + resolution: "find-up@npm:3.0.0" + dependencies: + locate-path: ^3.0.0 + checksum: 38eba3fe7a66e4bc7f0f5a1366dc25508b7cfc349f852640e3678d26ad9a6d7e2c43eff0a472287de4a9753ef58f066a0ea892a256fa3636ad51b3fe1e17fae9 + languageName: node + linkType: hard + "find-yarn-workspace-root@npm:^1.2.1": version: 1.2.1 resolution: "find-yarn-workspace-root@npm:1.2.1" @@ -7380,23 +7698,13 @@ __metadata: linkType: hard "flat-cache@npm:^3.0.4": - version: 3.0.4 - resolution: "flat-cache@npm:3.0.4" + version: 3.2.0 + resolution: "flat-cache@npm:3.2.0" dependencies: - flatted: ^3.1.0 + flatted: ^3.2.9 + keyv: ^4.5.3 rimraf: ^3.0.2 - checksum: 4fdd10ecbcbf7d520f9040dd1340eb5dfe951e6f0ecf2252edeec03ee68d989ec8b9a20f4434270e71bcfd57800dc09b3344fca3966b2eb8f613072c7d9a2365 - languageName: node - linkType: hard - -"flat@npm:^4.1.0": - version: 4.1.1 - resolution: "flat@npm:4.1.1" - dependencies: - is-buffer: ~2.0.3 - bin: - flat: cli.js - checksum: 398be12185eb0f3c59797c3670a8c35d07020b673363175676afbaf53d6b213660e060488554cf82c25504986e1a6059bdbcc5d562e87ca3e972e8a33148e3ae + checksum: e7e0f59801e288b54bee5cb9681e9ee21ee28ef309f886b312c9d08415b79fc0f24ac842f84356ce80f47d6a53de62197ce0e6e148dc42d5db005992e2a756ec languageName: node linkType: hard @@ -7416,10 +7724,10 @@ __metadata: languageName: node linkType: hard -"flatted@npm:^3.1.0": - version: 3.2.6 - resolution: "flatted@npm:3.2.6" - checksum: 33b87aa88dfa40ca6ee31d7df61712bbbad3d3c05c132c23e59b9b61d34631b337a18ff2b8dc5553acdc871ec72b741e485f78969cf006124a3f57174de29a0e +"flatted@npm:^3.2.9": + version: 3.2.9 + resolution: "flatted@npm:3.2.9" + checksum: f14167fbe26a9d20f6fca8d998e8f1f41df72c8e81f9f2c9d61ed2bea058248f5e1cbd05e7f88c0e5087a6a0b822a1e5e2b446e879f3cfbe0b07ba2d7f80b026 languageName: node linkType: hard @@ -7430,23 +7738,13 @@ __metadata: languageName: node linkType: hard -"follow-redirects@npm:^1.12.1": - version: 1.15.1 - resolution: "follow-redirects@npm:1.15.1" - peerDependenciesMeta: - debug: - optional: true - checksum: 6aa4e3e3cdfa3b9314801a1cd192ba756a53479d9d8cca65bf4db3a3e8834e62139245cd2f9566147c8dfe2efff1700d3e6aefd103de4004a7b99985e71dd533 - languageName: node - linkType: hard - -"follow-redirects@npm:^1.14.0": - version: 1.15.2 - resolution: "follow-redirects@npm:1.15.2" +"follow-redirects@npm:^1.12.1, follow-redirects@npm:^1.14.0, follow-redirects@npm:^1.15.4": + version: 1.15.5 + resolution: "follow-redirects@npm:1.15.5" peerDependenciesMeta: debug: optional: true - checksum: faa66059b66358ba65c234c2f2a37fcec029dc22775f35d9ad6abac56003268baf41e55f9ee645957b32c7d9f62baf1f0b906e68267276f54ec4b4c597c2b190 + checksum: 5ca49b5ce6f44338cbfc3546823357e7a70813cecc9b7b768158a1d32c1e62e7407c944402a918ea8c38ae2e78266312d617dc68783fac502cbb55e1047b34ec languageName: node linkType: hard @@ -7466,6 +7764,16 @@ __metadata: languageName: node linkType: hard +"foreground-child@npm:^3.1.0": + version: 3.1.1 + resolution: "foreground-child@npm:3.1.1" + dependencies: + cross-spawn: ^7.0.0 + signal-exit: ^4.0.1 + checksum: 139d270bc82dc9e6f8bc045fe2aae4001dc2472157044fdfad376d0a3457f77857fa883c1c8b21b491c6caade9a926a4bed3d3d2e8d3c9202b151a4cbbd0bcd5 + languageName: node + linkType: hard + "forever-agent@npm:~0.6.1": version: 0.6.1 resolution: "forever-agent@npm:0.6.1" @@ -7484,14 +7792,14 @@ __metadata: languageName: node linkType: hard -"form-data@npm:^3.0.0": - version: 3.0.1 - resolution: "form-data@npm:3.0.1" +"form-data@npm:^4.0.0": + version: 4.0.0 + resolution: "form-data@npm:4.0.0" dependencies: asynckit: ^0.4.0 combined-stream: ^1.0.8 mime-types: ^2.1.12 - checksum: b019e8d35c8afc14a2bd8a7a92fa4f525a4726b6d5a9740e8d2623c30e308fbb58dc8469f90415a856698933c8479b01646a9dff33c87cc4e76d72aedbbf860d + checksum: 01135bf8675f9d5c61ff18e2e2932f719ca4de964e3be90ef4c36aacfc7b9cb2fceb5eca0b7e0190e3383fe51c5b37f4cb80b62ca06a99aaabfcfd6ac7c9328c languageName: node linkType: hard @@ -7627,7 +7935,7 @@ __metadata: languageName: node linkType: hard -"fs-minipass@npm:^2.0.0, fs-minipass@npm:^2.1.0": +"fs-minipass@npm:^2.0.0": version: 2.1.0 resolution: "fs-minipass@npm:2.1.0" dependencies: @@ -7636,6 +7944,15 @@ __metadata: languageName: node linkType: hard +"fs-minipass@npm:^3.0.0": + version: 3.0.3 + resolution: "fs-minipass@npm:3.0.3" + dependencies: + minipass: ^7.0.3 + checksum: 8722a41109130851d979222d3ec88aabaceeaaf8f57b2a8f744ef8bd2d1ce95453b04a61daa0078822bc5cd21e008814f06fe6586f56fef511e71b8d2394d802 + languageName: node + linkType: hard + "fs-readdir-recursive@npm:^1.1.0": version: 1.1.0 resolution: "fs-readdir-recursive@npm:1.1.0" @@ -7650,60 +7967,41 @@ __metadata: languageName: node linkType: hard -"fsevents@npm:~2.1.1": - version: 2.1.3 - resolution: "fsevents@npm:2.1.3" - dependencies: - node-gyp: latest - checksum: b5ec0516b44d75b60af5c01ff80a80cd995d175e4640d2a92fbabd02991dd664d76b241b65feef0775c23d531c3c74742c0fbacd6205af812a9c3cef59f04292 - conditions: os=darwin - languageName: node - linkType: hard - "fsevents@npm:~2.3.2": - version: 2.3.2 - resolution: "fsevents@npm:2.3.2" - dependencies: - node-gyp: latest - checksum: 97ade64e75091afee5265e6956cb72ba34db7819b4c3e94c431d4be2b19b8bb7a2d4116da417950c3425f17c8fe693d25e20212cac583ac1521ad066b77ae31f - conditions: os=darwin - languageName: node - linkType: hard - -"fsevents@patch:fsevents@~2.1.1#~builtin": - version: 2.1.3 - resolution: "fsevents@patch:fsevents@npm%3A2.1.3#~builtin::version=2.1.3&hash=18f3a7" + version: 2.3.3 + resolution: "fsevents@npm:2.3.3" dependencies: node-gyp: latest + checksum: 11e6ea6fea15e42461fc55b4b0e4a0a3c654faa567f1877dbd353f39156f69def97a69936d1746619d656c4b93de2238bf731f6085a03a50cabf287c9d024317 conditions: os=darwin languageName: node linkType: hard "fsevents@patch:fsevents@~2.3.2#~builtin": - version: 2.3.2 - resolution: "fsevents@patch:fsevents@npm%3A2.3.2#~builtin::version=2.3.2&hash=18f3a7" + version: 2.3.3 + resolution: "fsevents@patch:fsevents@npm%3A2.3.3#~builtin::version=2.3.3&hash=18f3a7" dependencies: node-gyp: latest conditions: os=darwin languageName: node linkType: hard -"function-bind@npm:^1.1.1": - version: 1.1.1 - resolution: "function-bind@npm:1.1.1" - checksum: b32fbaebb3f8ec4969f033073b43f5c8befbb58f1a79e12f1d7490358150359ebd92f49e72ff0144f65f2c48ea2a605bff2d07965f548f6474fd8efd95bf361a +"function-bind@npm:^1.1.2": + version: 1.1.2 + resolution: "function-bind@npm:1.1.2" + checksum: 2b0ff4ce708d99715ad14a6d1f894e2a83242e4a52ccfcefaee5e40050562e5f6dafc1adbb4ce2d4ab47279a45dc736ab91ea5042d843c3c092820dfe032efb1 languageName: node linkType: hard -"function.prototype.name@npm:^1.1.5": - version: 1.1.5 - resolution: "function.prototype.name@npm:1.1.5" +"function.prototype.name@npm:^1.1.6": + version: 1.1.6 + resolution: "function.prototype.name@npm:1.1.6" dependencies: call-bind: ^1.0.2 - define-properties: ^1.1.3 - es-abstract: ^1.19.0 - functions-have-names: ^1.2.2 - checksum: acd21d733a9b649c2c442f067567743214af5fa248dbeee69d8278ce7df3329ea5abac572be9f7470b4ec1cd4d8f1040e3c5caccf98ebf2bf861a0deab735c27 + define-properties: ^1.2.0 + es-abstract: ^1.22.1 + functions-have-names: ^1.2.3 + checksum: 7a3f9bd98adab09a07f6e1f03da03d3f7c26abbdeaeee15223f6c04a9fb5674792bdf5e689dac19b97ac71de6aad2027ba3048a9b883aa1b3173eed6ab07f479 languageName: node linkType: hard @@ -7714,7 +8012,7 @@ __metadata: languageName: node linkType: hard -"functions-have-names@npm:^1.2.2": +"functions-have-names@npm:^1.2.3": version: 1.2.3 resolution: "functions-have-names@npm:1.2.3" checksum: c3f1f5ba20f4e962efb71344ce0a40722163e85bee2101ce25f88214e78182d2d2476aa85ef37950c579eb6cf6ee811c17b3101bb84004bb75655f3e33f3fdb5 @@ -7777,22 +8075,6 @@ __metadata: languageName: node linkType: hard -"gauge@npm:^4.0.3": - version: 4.0.4 - resolution: "gauge@npm:4.0.4" - dependencies: - aproba: ^1.0.3 || ^2.0.0 - color-support: ^1.1.3 - console-control-strings: ^1.1.0 - has-unicode: ^2.0.1 - signal-exit: ^3.0.7 - string-width: ^4.2.3 - strip-ansi: ^6.0.1 - wide-align: ^1.1.5 - checksum: 788b6bfe52f1dd8e263cda800c26ac0ca2ff6de0b6eee2fe0d9e3abf15e149b651bd27bf5226be10e6e3edb5c4e5d5985a5a1a98137e7a892f75eff76467ad2d - languageName: node - linkType: hard - "get-caller-file@npm:^1.0.1": version: 1.0.3 resolution: "get-caller-file@npm:1.0.3" @@ -7807,21 +8089,23 @@ __metadata: languageName: node linkType: hard -"get-func-name@npm:^2.0.0": - version: 2.0.0 - resolution: "get-func-name@npm:2.0.0" - checksum: 8d82e69f3e7fab9e27c547945dfe5cc0c57fc0adf08ce135dddb01081d75684a03e7a0487466f478872b341d52ac763ae49e660d01ab83741f74932085f693c3 +"get-func-name@npm:^2.0.1, get-func-name@npm:^2.0.2": + version: 2.0.2 + resolution: "get-func-name@npm:2.0.2" + checksum: 3f62f4c23647de9d46e6f76d2b3eafe58933a9b3830c60669e4180d6c601ce1b4aa310ba8366143f55e52b139f992087a9f0647274e8745621fa2af7e0acf13b languageName: node linkType: hard -"get-intrinsic@npm:^1.0.2, get-intrinsic@npm:^1.1.0, get-intrinsic@npm:^1.1.1": - version: 1.1.2 - resolution: "get-intrinsic@npm:1.1.2" +"get-intrinsic@npm:^1.0.2, get-intrinsic@npm:^1.1.1, get-intrinsic@npm:^1.1.3, get-intrinsic@npm:^1.2.1, get-intrinsic@npm:^1.2.2, get-intrinsic@npm:^1.2.3": + version: 1.2.3 + resolution: "get-intrinsic@npm:1.2.3" dependencies: - function-bind: ^1.1.1 - has: ^1.0.3 + es-errors: ^1.0.0 + function-bind: ^1.1.2 + has-proto: ^1.0.1 has-symbols: ^1.0.3 - checksum: 252f45491f2ba88ebf5b38018020c7cc3279de54b1d67ffb70c0cdf1dfa8ab31cd56467b5d117a8b4275b7a4dde91f86766b163a17a850f036528a7b2faafb2b + hasown: ^2.0.0 + checksum: 497fe6e444a2c570d174486c2450fc2b8c5020ae33a945a4b3d06c88bd69fb02a4611163e7a5bd9a5f61162e85ee83e55f06400dd3de108d6407eea32c6330b7 languageName: node linkType: hard @@ -7846,13 +8130,6 @@ __metadata: languageName: node linkType: hard -"get-stream@npm:^3.0.0": - version: 3.0.0 - resolution: "get-stream@npm:3.0.0" - checksum: 36142f46005ed74ce3a45c55545ec4e7da8e243554179e345a786baf144e5c4a35fb7bdc49fadfa9f18bd08000589b6fe364abdadfc4e1eb0e1b9914a6bb9c56 - languageName: node - linkType: hard - "get-stream@npm:^4.0.0, get-stream@npm:^4.1.0": version: 4.1.0 resolution: "get-stream@npm:4.1.0" @@ -7909,7 +8186,7 @@ __metadata: languageName: node linkType: hard -"glob-parent@npm:^5.0.0, glob-parent@npm:^5.1.2, glob-parent@npm:~5.1.0, glob-parent@npm:~5.1.2": +"glob-parent@npm:^5.0.0, glob-parent@npm:^5.1.2, glob-parent@npm:~5.1.2": version: 5.1.2 resolution: "glob-parent@npm:5.1.2" dependencies: @@ -7918,7 +8195,7 @@ __metadata: languageName: node linkType: hard -"glob-parent@npm:^6.0.1": +"glob-parent@npm:^6.0.2": version: 6.0.2 resolution: "glob-parent@npm:6.0.2" dependencies: @@ -7927,20 +8204,6 @@ __metadata: languageName: node linkType: hard -"glob@npm:7.1.3": - version: 7.1.3 - resolution: "glob@npm:7.1.3" - dependencies: - fs.realpath: ^1.0.0 - inflight: ^1.0.4 - inherits: 2 - minimatch: ^3.0.4 - once: ^1.3.0 - path-is-absolute: ^1.0.0 - checksum: d72a834a393948d6c4a5cacc6a29fe5fe190e1cd134e55dfba09aee0be6fe15be343e96d8ec43558ab67ff8af28e4420c7f63a4d4db1c779e515015e9c318616 - languageName: node - linkType: hard - "glob@npm:7.1.7": version: 7.1.7 resolution: "glob@npm:7.1.7" @@ -7969,6 +8232,21 @@ __metadata: languageName: node linkType: hard +"glob@npm:^10.2.2, glob@npm:^10.3.10": + version: 10.3.10 + resolution: "glob@npm:10.3.10" + dependencies: + foreground-child: ^3.1.0 + jackspeak: ^2.3.5 + minimatch: ^9.0.1 + minipass: ^5.0.0 || ^6.0.2 || ^7.0.0 + path-scurry: ^1.10.1 + bin: + glob: dist/esm/bin.mjs + checksum: 4f2fe2511e157b5a3f525a54092169a5f92405f24d2aed3142f4411df328baca13059f4182f1db1bf933e2c69c0bd89e57ae87edd8950cba8c7ccbe84f721cf3 + languageName: node + linkType: hard + "glob@npm:^5.0.15": version: 5.0.15 resolution: "glob@npm:5.0.15" @@ -7982,7 +8260,7 @@ __metadata: languageName: node linkType: hard -"glob@npm:^7.0.0, glob@npm:^7.1.2, glob@npm:^7.1.3, glob@npm:^7.1.4, glob@npm:^7.1.6, glob@npm:~7.2.0": +"glob@npm:^7.0.0, glob@npm:^7.1.2, glob@npm:^7.1.3, glob@npm:^7.1.6, glob@npm:~7.2.3": version: 7.2.3 resolution: "glob@npm:7.2.3" dependencies: @@ -7996,16 +8274,16 @@ __metadata: languageName: node linkType: hard -"glob@npm:^8.0.1": - version: 8.0.3 - resolution: "glob@npm:8.0.3" +"glob@npm:^8.0.3": + version: 8.1.0 + resolution: "glob@npm:8.1.0" dependencies: fs.realpath: ^1.0.0 inflight: ^1.0.4 inherits: 2 minimatch: ^5.0.1 once: ^1.3.0 - checksum: 50bcdea19d8e79d8de5f460b1939ffc2b3299eac28deb502093fdca22a78efebc03e66bf54f0abc3d3d07d8134d19a32850288b7440d77e072aa55f9d33b18c5 + checksum: 92fbea3221a7d12075f26f0227abac435de868dd0736a17170663783296d0dd8d3d532a5672b4488a439bf5d7fb85cdd07c11185d6cd39184f0385cbdfb86a47 languageName: node linkType: hard @@ -8039,13 +8317,6 @@ __metadata: languageName: node linkType: hard -"globals@npm:^11.7.0": - version: 11.12.0 - resolution: "globals@npm:11.12.0" - checksum: 67051a45eca3db904aee189dfc7cd53c20c7d881679c93f6146ddd4c9f4ab2268e68a919df740d39c71f4445d2b38ee360fc234428baea1dbdfe68bbcb46979e - languageName: node - linkType: hard - "globals@npm:^12.1.0": version: 12.4.0 resolution: "globals@npm:12.4.0" @@ -8055,12 +8326,12 @@ __metadata: languageName: node linkType: hard -"globals@npm:^13.15.0": - version: 13.17.0 - resolution: "globals@npm:13.17.0" +"globals@npm:^13.19.0": + version: 13.24.0 + resolution: "globals@npm:13.24.0" dependencies: type-fest: ^0.20.2 - checksum: fbaf4112e59b92c9f5575e85ce65e9e17c0b82711196ec5f58beb08599bbd92fd72703d6dfc9b080381fd35b644e1b11dcf25b38cc2341ec21df942594cbc8ce + checksum: 56066ef058f6867c04ff203b8a44c15b038346a62efbc3060052a1016be9f56f4cf0b2cd45b74b22b81e521a889fc7786c73691b0549c2f3a6e825b3d394f43c languageName: node linkType: hard @@ -8071,6 +8342,15 @@ __metadata: languageName: node linkType: hard +"globalthis@npm:^1.0.3": + version: 1.0.3 + resolution: "globalthis@npm:1.0.3" + dependencies: + define-properties: ^1.1.3 + checksum: fbd7d760dc464c886d0196166d92e5ffb4c84d0730846d6621a39fbbc068aeeb9c8d1421ad330e94b7bca4bb4ea092f5f21f3d36077812af5d098b4dc006c998 + languageName: node + linkType: hard + "globby@npm:^10.0.1": version: 10.0.2 resolution: "globby@npm:10.0.2" @@ -8141,6 +8421,15 @@ __metadata: languageName: node linkType: hard +"gopd@npm:^1.0.1": + version: 1.0.1 + resolution: "gopd@npm:1.0.1" + dependencies: + get-intrinsic: ^1.1.3 + checksum: a5ccfb8806e0917a94e0b3de2af2ea4979c1da920bc381667c260e00e7cafdbe844e2cb9c5bcfef4e5412e8bf73bab837285bc35c7ba73aaaf0134d4583393a6 + languageName: node + linkType: hard + "got@npm:9.6.0": version: 9.6.0 resolution: "got@npm:9.6.0" @@ -8160,32 +8449,36 @@ __metadata: languageName: node linkType: hard -"got@npm:^7.1.0": - version: 7.1.0 - resolution: "got@npm:7.1.0" +"got@npm:^11.8.5": + version: 11.8.6 + resolution: "got@npm:11.8.6" dependencies: - decompress-response: ^3.2.0 - duplexer3: ^0.1.4 - get-stream: ^3.0.0 - is-plain-obj: ^1.1.0 - is-retry-allowed: ^1.0.0 - is-stream: ^1.0.0 - isurl: ^1.0.0-alpha5 - lowercase-keys: ^1.0.0 - p-cancelable: ^0.3.0 - p-timeout: ^1.1.1 - safe-buffer: ^5.0.1 - timed-out: ^4.0.0 - url-parse-lax: ^1.0.0 - url-to-options: ^1.0.1 - checksum: 0270472a389bdca67e60d36cccd014e502d1797d925c06ea2ef372fb41ae99c9e25ac4f187cc422760b4a66abb5478f8821b8134b4eaefe0bf5183daeded5e2f + "@sindresorhus/is": ^4.0.0 + "@szmarczak/http-timer": ^4.0.5 + "@types/cacheable-request": ^6.0.1 + "@types/responselike": ^1.0.0 + cacheable-lookup: ^5.0.3 + cacheable-request: ^7.0.2 + decompress-response: ^6.0.0 + http2-wrapper: ^1.0.0-beta.5.2 + lowercase-keys: ^2.0.0 + p-cancelable: ^2.0.0 + responselike: ^2.0.0 + checksum: bbc783578a8d5030c8164ef7f57ce41b5ad7db2ed13371e1944bef157eeca5a7475530e07c0aaa71610d7085474d0d96222c9f4268d41db333a17e39b463f45d languageName: node linkType: hard "graceful-fs@npm:^4.1.11, graceful-fs@npm:^4.1.2, graceful-fs@npm:^4.1.6, graceful-fs@npm:^4.1.9, graceful-fs@npm:^4.2.0, graceful-fs@npm:^4.2.4, graceful-fs@npm:^4.2.6": - version: 4.2.10 - resolution: "graceful-fs@npm:4.2.10" - checksum: 3f109d70ae123951905d85032ebeae3c2a5a7a997430df00ea30df0e3a6c60cf6689b109654d6fdacd28810a053348c4d14642da1d075049e6be1ba5216218da + version: 4.2.11 + resolution: "graceful-fs@npm:4.2.11" + checksum: ac85f94da92d8eb6b7f5a8b20ce65e43d66761c55ce85ac96df6865308390da45a8d3f0296dd3a663de65d30ba497bd46c696cc1e248c72b13d6d567138a4fc7 + languageName: node + linkType: hard + +"graphemer@npm:^1.4.0": + version: 1.4.0 + resolution: "graphemer@npm:1.4.0" + checksum: bab8f0be9b568857c7bec9fda95a89f87b783546d02951c40c33f84d05bb7da3fd10f863a9beb901463669b6583173a8c8cc6d6b306ea2b9b9d5d3d943c3a673 languageName: node linkType: hard @@ -8196,19 +8489,12 @@ __metadata: languageName: node linkType: hard -"growl@npm:1.10.5": - version: 1.10.5 - resolution: "growl@npm:1.10.5" - checksum: 4b86685de6831cebcbb19f93870bea624afee61124b0a20c49017013987cd129e73a8c4baeca295728f41d21265e1f859d25ef36731b142ca59c655fea94bb1a - languageName: node - linkType: hard - "handlebars@npm:^4.0.1": - version: 4.7.7 - resolution: "handlebars@npm:4.7.7" + version: 4.7.8 + resolution: "handlebars@npm:4.7.8" dependencies: minimist: ^1.2.5 - neo-async: ^2.6.0 + neo-async: ^2.6.2 source-map: ^0.6.1 uglify-js: ^3.1.4 wordwrap: ^1.0.0 @@ -8217,7 +8503,7 @@ __metadata: optional: true bin: handlebars: bin/handlebars - checksum: 1e79a43f5e18d15742977cb987923eab3e2a8f44f2d9d340982bcb69e1735ed049226e534d7c1074eaddaf37e4fb4f471a8adb71cddd5bc8cf3f894241df5cee + checksum: 00e68bb5c183fd7b8b63322e6234b5ac8fbb960d712cb3f25587d559c2951d9642df83c04a1172c918c41bcfc81bfbd7a7718bbce93b893e0135fc99edea93ff languageName: node linkType: hard @@ -8239,42 +8525,42 @@ __metadata: linkType: hard "hardhat-gas-reporter@npm:^1.0.9": - version: 1.0.9 - resolution: "hardhat-gas-reporter@npm:1.0.9" + version: 1.0.10 + resolution: "hardhat-gas-reporter@npm:1.0.10" dependencies: array-uniq: 1.0.3 eth-gas-reporter: ^0.2.25 sha1: ^1.1.1 peerDependencies: hardhat: ^2.0.2 - checksum: 77f8f8d085ff3d9d7787f0227e5355e1800f7d6707bc70171e0567bf69706703ae7f6f53dce1be1d409e7e71e3629a434c94b546bdbbc1e4c1af47cd5d0c6776 + checksum: caaec13ab3fcda47b8768257e4416b5fd0e8ef3aca5369aa8195419d3d4a948cc182075333651df44215cfc629d088f5ed9f762c8c14ae5a4b4a4f2613e583d0 languageName: node linkType: hard -"hardhat@npm:^2.12.6": - version: 2.12.7 - resolution: "hardhat@npm:2.12.7" +"hardhat@npm:^2.19.4": + version: 2.19.5 + resolution: "hardhat@npm:2.19.5" dependencies: "@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/ethereumjs-block": 5.0.2 + "@nomicfoundation/ethereumjs-blockchain": 7.0.2 + "@nomicfoundation/ethereumjs-common": 4.0.2 + "@nomicfoundation/ethereumjs-evm": 2.0.2 + "@nomicfoundation/ethereumjs-rlp": 5.0.2 + "@nomicfoundation/ethereumjs-statemanager": 2.0.2 + "@nomicfoundation/ethereumjs-trie": 6.0.2 + "@nomicfoundation/ethereumjs-tx": 5.0.2 + "@nomicfoundation/ethereumjs-util": 9.0.2 + "@nomicfoundation/ethereumjs-vm": 7.0.2 "@nomicfoundation/solidity-analyzer": ^0.1.0 "@sentry/node": ^5.18.1 "@types/bn.js": ^5.1.0 "@types/lru-cache": ^5.1.0 - abort-controller: ^3.0.0 adm-zip: ^0.4.16 aggregate-error: ^3.0.0 ansi-escapes: ^4.3.0 + boxen: ^5.1.2 chalk: ^2.4.2 chokidar: ^3.4.0 ci-info: ^2.0.0 @@ -8294,7 +8580,6 @@ __metadata: mnemonist: ^0.38.0 mocha: ^10.0.0 p-map: ^4.0.0 - qs: ^6.7.0 raw-body: ^2.4.1 resolve: 1.17.0 semver: ^6.3.0 @@ -8314,8 +8599,8 @@ __metadata: typescript: optional: true bin: - hardhat: internal/cli/cli.js - checksum: d1f86c09f3db1cc67a448214ebf67a15fc980304ad892ca90792bba679c887a23d8bc2006daf301f4106d638230229dc1ddbc5ba363b1c6aa6b361064f0d7aec + hardhat: internal/cli/bootstrap.js + checksum: 316b03a1d090360e6ed471fe125360ec0c66c5bb62e29492898932b1a9a5227c12d7a18343877c59725f321647a01fde0841649bf7d8a4a746148a0d38b0ee27 languageName: node linkType: hard @@ -8356,51 +8641,35 @@ __metadata: languageName: node linkType: hard -"has-property-descriptors@npm:^1.0.0": - version: 1.0.0 - resolution: "has-property-descriptors@npm:1.0.0" +"has-property-descriptors@npm:^1.0.0, has-property-descriptors@npm:^1.0.1": + version: 1.0.1 + resolution: "has-property-descriptors@npm:1.0.1" dependencies: - get-intrinsic: ^1.1.1 - checksum: a6d3f0a266d0294d972e354782e872e2fe1b6495b321e6ef678c9b7a06a40408a6891817350c62e752adced73a94ac903c54734fee05bf65b1905ee1368194bb + get-intrinsic: ^1.2.2 + checksum: 2bcc6bf6ec6af375add4e4b4ef586e43674850a91ad4d46666d0b28ba8e1fd69e424c7677d24d60f69470ad0afaa2f3197f508b20b0bb7dd99a8ab77ffc4b7c4 languageName: node linkType: hard -"has-symbol-support-x@npm:^1.4.1": - version: 1.4.2 - resolution: "has-symbol-support-x@npm:1.4.2" - checksum: ff06631d556d897424c00e8e79c10093ad34c93e88bb0563932d7837f148a4c90a4377abc5d8da000cb6637c0ecdb4acc9ae836c7cfd0ffc919986db32097609 +"has-proto@npm:^1.0.1": + version: 1.0.1 + resolution: "has-proto@npm:1.0.1" + checksum: febc5b5b531de8022806ad7407935e2135f1cc9e64636c3916c6842bd7995994ca3b29871ecd7954bd35f9e2986c17b3b227880484d22259e2f8e6ce63fd383e languageName: node linkType: hard -"has-symbols@npm:^1.0.0, has-symbols@npm:^1.0.1, has-symbols@npm:^1.0.2, has-symbols@npm:^1.0.3": +"has-symbols@npm:^1.0.2, has-symbols@npm:^1.0.3": version: 1.0.3 resolution: "has-symbols@npm:1.0.3" checksum: a054c40c631c0d5741a8285010a0777ea0c068f99ed43e5d6eb12972da223f8af553a455132fdb0801bdcfa0e0f443c0c03a68d8555aa529b3144b446c3f2410 languageName: node linkType: hard -"has-to-string-tag-x@npm:^1.2.0": - version: 1.4.1 - resolution: "has-to-string-tag-x@npm:1.4.1" - dependencies: - has-symbol-support-x: ^1.4.1 - checksum: 804c4505727be7770f8b2f5e727ce31c9affc5b83df4ce12344f44b68d557fefb31f77751dbd739de900653126bcd71f8842fac06f97a3fae5422685ab0ce6f0 - languageName: node - linkType: hard - -"has-tostringtag@npm:^1.0.0": - version: 1.0.0 - resolution: "has-tostringtag@npm:1.0.0" +"has-tostringtag@npm:^1.0.0, has-tostringtag@npm:^1.0.1": + version: 1.0.2 + resolution: "has-tostringtag@npm:1.0.2" dependencies: - has-symbols: ^1.0.2 - checksum: cc12eb28cb6ae22369ebaad3a8ab0799ed61270991be88f208d508076a1e99abe4198c965935ce85ea90b60c94ddda73693b0920b58e7ead048b4a391b502c1c - languageName: node - linkType: hard - -"has-unicode@npm:^2.0.1": - version: 2.0.1 - resolution: "has-unicode@npm:2.0.1" - checksum: 1eab07a7436512db0be40a710b29b5dc21fa04880b7f63c9980b706683127e3c1b57cb80ea96d47991bdae2dfe479604f6a1ba410106ee1046a41d1bd0814400 + has-symbols: ^1.0.3 + checksum: 999d60bb753ad714356b2c6c87b7fb74f32463b8426e159397da4bde5bca7e598ab1073f4d8d4deafac297f2eb311484cd177af242776bf05f0d11565680468d languageName: node linkType: hard @@ -8443,12 +8712,10 @@ __metadata: languageName: node linkType: hard -"has@npm:^1.0.3, has@npm:~1.0.3": - version: 1.0.3 - resolution: "has@npm:1.0.3" - dependencies: - function-bind: ^1.1.1 - checksum: b9ad53d53be4af90ce5d1c38331e712522417d017d5ef1ebd0507e07c2fbad8686fffb8e12ddecd4c39ca9b9b47431afbb975b8abf7f3c3b82c98e9aad052792 +"has@npm:~1.0.3": + version: 1.0.4 + resolution: "has@npm:1.0.4" + checksum: 8a11ba062e0627c9578a1d08285401e39f1d071a9692ddf793199070edb5648b21c774dd733e2a181edd635bf6862731885f476f4ccf67c998d7a5ff7cef2550 languageName: node linkType: hard @@ -8463,16 +8730,6 @@ __metadata: languageName: node linkType: hard -"hash.js@npm:1.1.3": - version: 1.1.3 - resolution: "hash.js@npm:1.1.3" - dependencies: - inherits: ^2.0.3 - minimalistic-assert: ^1.0.0 - checksum: 93de6f178bf71feee38f66868a57ecb5602d937c1ccd69951b0bfec1488813b6afdbb4a81ddb2c62488c419b4a35af352298b006f14c9cfbf5b872c4191b657f - languageName: node - linkType: hard - "hash.js@npm:1.1.7, hash.js@npm:^1.0.0, hash.js@npm:^1.0.3, hash.js@npm:^1.1.7": version: 1.1.7 resolution: "hash.js@npm:1.1.7" @@ -8483,6 +8740,15 @@ __metadata: languageName: node linkType: hard +"hasown@npm:^2.0.0": + version: 2.0.0 + resolution: "hasown@npm:2.0.0" + dependencies: + function-bind: ^1.1.2 + checksum: 6151c75ca12554565098641c98a40f4cc86b85b0fd5b6fe92360967e4605a4f9610f7757260b4e8098dd1c2ce7f4b095f2006fe72a570e3b6d2d28de0298c176 + languageName: node + linkType: hard + "he@npm:1.2.0": version: 1.2.0 resolution: "he@npm:1.2.0" @@ -8546,7 +8812,7 @@ __metadata: languageName: node linkType: hard -"http-cache-semantics@npm:^4.0.0, http-cache-semantics@npm:^4.1.0": +"http-cache-semantics@npm:^4.0.0, http-cache-semantics@npm:^4.1.1": version: 4.1.1 resolution: "http-cache-semantics@npm:4.1.1" checksum: 83ac0bc60b17a3a36f9953e7be55e5c8f41acc61b22583060e8dedc9dd5e3607c823a88d0926f9150e571f90946835c7fe150732801010845c72cd8bbff1a236 @@ -8573,14 +8839,13 @@ __metadata: languageName: node linkType: hard -"http-proxy-agent@npm:^5.0.0": - version: 5.0.0 - resolution: "http-proxy-agent@npm:5.0.0" +"http-proxy-agent@npm:^7.0.0": + version: 7.0.0 + resolution: "http-proxy-agent@npm:7.0.0" dependencies: - "@tootallnate/once": 2 - agent-base: 6 - debug: 4 - checksum: e2ee1ff1656a131953839b2a19cd1f3a52d97c25ba87bd2559af6ae87114abf60971e498021f9b73f9fd78aea8876d1fb0d4656aac8a03c6caa9fc175f22b786 + agent-base: ^7.1.0 + debug: ^4.3.4 + checksum: 48d4fac997917e15f45094852b63b62a46d0c8a4f0b9c6c23ca26d27b8df8d178bed88389e604745e748bd9a01f5023e25093722777f0593c3f052009ff438b6 languageName: node linkType: hard @@ -8604,6 +8869,16 @@ __metadata: languageName: node linkType: hard +"http2-wrapper@npm:^1.0.0-beta.5.2": + version: 1.0.3 + resolution: "http2-wrapper@npm:1.0.3" + dependencies: + quick-lru: ^5.1.1 + resolve-alpn: ^1.0.0 + checksum: 74160b862ec699e3f859739101ff592d52ce1cb207b7950295bf7962e4aa1597ef709b4292c673bece9c9b300efad0559fc86c71b1409c7a1e02b7229456003e + languageName: node + linkType: hard + "https-proxy-agent@npm:^5.0.0": version: 5.0.1 resolution: "https-proxy-agent@npm:5.0.1" @@ -8614,6 +8889,16 @@ __metadata: languageName: node linkType: hard +"https-proxy-agent@npm:^7.0.1": + version: 7.0.2 + resolution: "https-proxy-agent@npm:7.0.2" + dependencies: + agent-base: ^7.0.2 + debug: 4 + checksum: 088969a0dd476ea7a0ed0a2cf1283013682b08f874c3bc6696c83fa061d2c157d29ef0ad3eb70a2046010bb7665573b2388d10fdcb3e410a66995e5248444292 + languageName: node + linkType: hard + "human-signals@npm:^1.1.1": version: 1.1.1 resolution: "human-signals@npm:1.1.1" @@ -8621,15 +8906,6 @@ __metadata: languageName: node linkType: hard -"humanize-ms@npm:^1.2.1": - version: 1.2.1 - resolution: "humanize-ms@npm:1.2.1" - dependencies: - ms: ^2.0.0 - checksum: 9c7a74a2827f9294c009266c82031030eae811ca87b0da3dceb8d6071b9bde22c9f3daef0469c3c533cc67a97d8a167cd9fc0389350e5f415f61a79b171ded16 - languageName: node - linkType: hard - "iconv-lite@npm:0.4.24, iconv-lite@npm:^0.4.24": version: 0.4.24 resolution: "iconv-lite@npm:0.4.24" @@ -8657,7 +8933,7 @@ __metadata: languageName: node linkType: hard -"ieee754@npm:^1.1.13, ieee754@npm:^1.2.1": +"ieee754@npm:^1.1.13, ieee754@npm:^1.1.4, ieee754@npm:^1.2.1": version: 1.2.1 resolution: "ieee754@npm:1.2.1" checksum: 5144c0c9815e54ada181d80a0b810221a253562422e7c6c3a60b1901154184f49326ec239d618c416c1c5945a2e197107aee8d986a3dd836b53dffefd99b5e7e @@ -8671,10 +8947,10 @@ __metadata: languageName: node linkType: hard -"ignore@npm:^5.1.1, ignore@npm:^5.2.0": - version: 5.2.0 - resolution: "ignore@npm:5.2.0" - checksum: 6b1f926792d614f64c6c83da3a1f9c83f6196c2839aa41e1e32dd7b8d174cef2e329d75caabb62cb61ce9dc432f75e67d07d122a037312db7caa73166a1bdb77 +"ignore@npm:^5.1.1, ignore@npm:^5.2.0, ignore@npm:^5.2.4": + version: 5.3.1 + resolution: "ignore@npm:5.3.1" + checksum: 71d7bb4c1dbe020f915fd881108cbe85a0db3d636a0ea3ba911393c53946711d13a9b1143c7e70db06d571a5822c0a324a6bcde5c9904e7ca5047f01f1bf8cd3 languageName: node linkType: hard @@ -8700,23 +8976,13 @@ __metadata: linkType: hard "immutable@npm:^4.0.0-rc.12": - version: 4.1.0 - resolution: "immutable@npm:4.1.0" - checksum: b9bc1f14fb18eb382d48339c064b24a1f97ae4cf43102e0906c0a6e186a27afcd18b55ca4a0b63c98eefb58143e2b5ebc7755a5fb4da4a7ad84b7a6096ac5b13 - languageName: node - linkType: hard - -"import-fresh@npm:^2.0.0": - version: 2.0.0 - resolution: "import-fresh@npm:2.0.0" - dependencies: - caller-path: ^2.0.0 - resolve-from: ^3.0.0 - checksum: 610255f9753cc6775df00be08e9f43691aa39f7703e3636c45afe22346b8b545e600ccfe100c554607546fc8e861fa149a0d1da078c8adedeea30fff326eef79 + version: 4.3.5 + resolution: "immutable@npm:4.3.5" + checksum: 0e25dd5c314421faede9e1122ab26cdb638cc3edc8678c4a75dee104279b12621a30c80a480fae7f68bc7e81672f1e672e454dc0fdc7e6cf0af10809348387b8 languageName: node linkType: hard -"import-fresh@npm:^3.0.0, import-fresh@npm:^3.1.0, import-fresh@npm:^3.2.1": +"import-fresh@npm:^3.0.0, import-fresh@npm:^3.1.0, import-fresh@npm:^3.2.1, import-fresh@npm:^3.3.0": version: 3.3.0 resolution: "import-fresh@npm:3.3.0" dependencies: @@ -8734,16 +9000,9 @@ __metadata: linkType: hard "indent-string@npm:^4.0.0": - version: 4.0.0 - resolution: "indent-string@npm:4.0.0" - checksum: 824cfb9929d031dabf059bebfe08cf3137365e112019086ed3dcff6a0a7b698cb80cf67ccccde0e25b9e2d7527aa6cc1fed1ac490c752162496caba3e6699612 - languageName: node - linkType: hard - -"infer-owner@npm:^1.0.4": - version: 1.0.4 - resolution: "infer-owner@npm:1.0.4" - checksum: 181e732764e4a0611576466b4b87dac338972b839920b2a8cde43642e4ed6bd54dc1fb0b40874728f2a2df9a1b097b8ff83b56d5f8f8e3927f837fdcb47d8a89 + version: 4.0.0 + resolution: "indent-string@npm:4.0.0" + checksum: 824cfb9929d031dabf059bebfe08cf3137365e112019086ed3dcff6a0a7b698cb80cf67ccccde0e25b9e2d7527aa6cc1fed1ac490c752162496caba3e6699612 languageName: node linkType: hard @@ -8771,27 +9030,6 @@ __metadata: languageName: node linkType: hard -"inquirer@npm:^6.2.2": - version: 6.5.2 - resolution: "inquirer@npm:6.5.2" - dependencies: - ansi-escapes: ^3.2.0 - chalk: ^2.4.2 - cli-cursor: ^2.1.0 - cli-width: ^2.0.0 - external-editor: ^3.0.3 - figures: ^2.0.0 - lodash: ^4.17.12 - mute-stream: 0.0.7 - run-async: ^2.2.0 - rxjs: ^6.4.0 - string-width: ^2.1.0 - strip-ansi: ^5.1.0 - through: ^2.3.6 - checksum: 175ad4cd1ebed493b231b240185f1da5afeace5f4e8811dfa83cf55dcae59c3255eaed990aa71871b0fd31aa9dc212f43c44c50ed04fb529364405e72f484d28 - languageName: node - linkType: hard - "inquirer@npm:^7.0.0": version: 7.3.3 resolution: "inquirer@npm:7.3.3" @@ -8831,14 +9069,14 @@ __metadata: languageName: node linkType: hard -"internal-slot@npm:^1.0.3": - version: 1.0.3 - resolution: "internal-slot@npm:1.0.3" +"internal-slot@npm:^1.0.5": + version: 1.0.6 + resolution: "internal-slot@npm:1.0.6" dependencies: - get-intrinsic: ^1.1.0 - has: ^1.0.3 + get-intrinsic: ^1.2.2 + hasown: ^2.0.0 side-channel: ^1.0.4 - checksum: 1944f92e981e47aebc98a88ff0db579fd90543d937806104d0b96557b10c1f170c51fb777b97740a8b6ddeec585fca8c39ae99fd08a8e058dfc8ab70937238bf + checksum: 7872454888047553ce97a3fa1da7cc054a28ec5400a9c2e9f4dbe4fe7c1d041cb8e8301467614b80d4246d50377aad2fb58860b294ed74d6700cc346b6f89549 languageName: node linkType: hard @@ -8888,10 +9126,10 @@ __metadata: languageName: node linkType: hard -"ip@npm:^1.1.5": - version: 1.1.8 - resolution: "ip@npm:1.1.8" - checksum: a2ade53eb339fb0cbe9e69a44caab10d6e3784662285eb5d2677117ee4facc33a64679051c35e0dfdb1a3983a51ce2f5d2cb36446d52e10d01881789b76e28fb +"ip@npm:^2.0.0": + version: 2.0.0 + resolution: "ip@npm:2.0.0" + checksum: cfcfac6b873b701996d71ec82a7dd27ba92450afdb421e356f44044ed688df04567344c36cbacea7d01b1c39a4c732dc012570ebe9bebfb06f27314bca625349 languageName: node linkType: hard @@ -9002,25 +9240,16 @@ __metadata: languageName: node linkType: hard -"is-accessor-descriptor@npm:^0.1.6": - version: 0.1.6 - resolution: "is-accessor-descriptor@npm:0.1.6" - dependencies: - kind-of: ^3.0.2 - checksum: 3d629a086a9585bc16a83a8e8a3416f400023301855cafb7ccc9a1d63145b7480f0ad28877dcc2cce09492c4ec1c39ef4c071996f24ee6ac626be4217b8ffc8a - languageName: node - linkType: hard - -"is-accessor-descriptor@npm:^1.0.0": - version: 1.0.0 - resolution: "is-accessor-descriptor@npm:1.0.0" +"is-accessor-descriptor@npm:^1.0.1": + version: 1.0.1 + resolution: "is-accessor-descriptor@npm:1.0.1" dependencies: - kind-of: ^6.0.0 - checksum: 8e475968e9b22f9849343c25854fa24492dbe8ba0dea1a818978f9f1b887339190b022c9300d08c47fe36f1b913d70ce8cbaca00369c55a56705fdb7caed37fe + hasown: ^2.0.0 + checksum: 8db44c02230a5e9b9dec390a343178791f073d5d5556a400527d2fd67a72d93b226abab2bd4123305c268f5dc22831bfdbd38430441fda82ea9e0b95ddc6b267 languageName: node linkType: hard -"is-arguments@npm:^1.0.4": +"is-arguments@npm:^1.1.1": version: 1.1.1 resolution: "is-arguments@npm:1.1.1" dependencies: @@ -9030,6 +9259,16 @@ __metadata: languageName: node linkType: hard +"is-array-buffer@npm:^3.0.2, is-array-buffer@npm:^3.0.4": + version: 3.0.4 + resolution: "is-array-buffer@npm:3.0.4" + dependencies: + call-bind: ^1.0.2 + get-intrinsic: ^1.2.1 + checksum: e4e3e6ef0ff2239e75371d221f74bc3c26a03564a22efb39f6bb02609b598917ddeecef4e8c877df2a25888f247a98198959842a5e73236bc7f22cabdf6351a7 + languageName: node + linkType: hard + "is-arrayish@npm:^0.2.1": version: 0.2.1 resolution: "is-arrayish@npm:0.2.1" @@ -9072,17 +9311,17 @@ __metadata: languageName: node linkType: hard -"is-buffer@npm:^2.0.5, is-buffer@npm:~2.0.3": +"is-buffer@npm:^2.0.5": version: 2.0.5 resolution: "is-buffer@npm:2.0.5" checksum: 764c9ad8b523a9f5a32af29bdf772b08eb48c04d2ad0a7240916ac2688c983bf5f8504bf25b35e66240edeb9d9085461f9b5dae1f3d2861c6b06a65fe983de42 languageName: node linkType: hard -"is-callable@npm:^1.1.3, is-callable@npm:^1.1.4, is-callable@npm:^1.2.4": - version: 1.2.4 - resolution: "is-callable@npm:1.2.4" - checksum: 1a28d57dc435797dae04b173b65d6d1e77d4f16276e9eff973f994eadcfdc30a017e6a597f092752a083c1103cceb56c91e3dadc6692fedb9898dfaba701575f +"is-callable@npm:^1.1.3, is-callable@npm:^1.1.4, is-callable@npm:^1.2.7": + version: 1.2.7 + resolution: "is-callable@npm:1.2.7" + checksum: 61fd57d03b0d984e2ed3720fb1c7a897827ea174bd44402878e059542ea8c4aeedee0ea0985998aa5cc2736b2fa6e271c08587addb5b3959ac52cf665173d1ac languageName: node linkType: hard @@ -9097,34 +9336,25 @@ __metadata: languageName: node linkType: hard -"is-core-module@npm:^2.8.1, is-core-module@npm:^2.9.0": - version: 2.9.0 - resolution: "is-core-module@npm:2.9.0" - dependencies: - has: ^1.0.3 - checksum: b27034318b4b462f1c8f1dfb1b32baecd651d891a4e2d1922135daeff4141dfced2b82b07aef83ef54275c4a3526aa38da859223664d0868ca24182badb784ce - languageName: node - linkType: hard - -"is-data-descriptor@npm:^0.1.4": - version: 0.1.4 - resolution: "is-data-descriptor@npm:0.1.4" +"is-core-module@npm:^2.11.0, is-core-module@npm:^2.13.0, is-core-module@npm:^2.13.1": + version: 2.13.1 + resolution: "is-core-module@npm:2.13.1" dependencies: - kind-of: ^3.0.2 - checksum: 5c622e078ba933a78338ae398a3d1fc5c23332b395312daf4f74bab4afb10d061cea74821add726cb4db8b946ba36217ee71a24fe71dd5bca4632edb7f6aad87 + hasown: ^2.0.0 + checksum: 256559ee8a9488af90e4bad16f5583c6d59e92f0742e9e8bb4331e758521ee86b810b93bae44f390766ffbc518a0488b18d9dab7da9a5ff997d499efc9403f7c languageName: node linkType: hard -"is-data-descriptor@npm:^1.0.0": - version: 1.0.0 - resolution: "is-data-descriptor@npm:1.0.0" +"is-data-descriptor@npm:^1.0.1": + version: 1.0.1 + resolution: "is-data-descriptor@npm:1.0.1" dependencies: - kind-of: ^6.0.0 - checksum: e705e6816241c013b05a65dc452244ee378d1c3e3842bd140beabe6e12c0d700ef23c91803f971aa7b091fb0573c5da8963af34a2b573337d87bc3e1f53a4e6d + hasown: ^2.0.0 + checksum: fc6da5be5177149d554c5612cc382e9549418ed72f2d3ed5a3e6511b03dd119ae1b2258320ca94931df50b7e9ee012894eccd4ca45bbcadf0d5b27da6faeb15a languageName: node linkType: hard -"is-date-object@npm:^1.0.1": +"is-date-object@npm:^1.0.1, is-date-object@npm:^1.0.5": version: 1.0.5 resolution: "is-date-object@npm:1.0.5" dependencies: @@ -9134,31 +9364,22 @@ __metadata: linkType: hard "is-descriptor@npm:^0.1.0": - version: 0.1.6 - resolution: "is-descriptor@npm:0.1.6" + version: 0.1.7 + resolution: "is-descriptor@npm:0.1.7" dependencies: - is-accessor-descriptor: ^0.1.6 - is-data-descriptor: ^0.1.4 - kind-of: ^5.0.0 - checksum: 0f780c1b46b465f71d970fd7754096ffdb7b69fd8797ca1f5069c163eaedcd6a20ec4a50af669075c9ebcfb5266d2e53c8b227e485eefdb0d1fee09aa1dd8ab6 + is-accessor-descriptor: ^1.0.1 + is-data-descriptor: ^1.0.1 + checksum: 45743109f0bb03f9fa989c34d31ece87cc15792649f147b896a7c4db2906a02fca685867619f4d312e024d7bbd53b945a47c6830d01f5e73efcc6388ac211963 languageName: node linkType: hard "is-descriptor@npm:^1.0.0, is-descriptor@npm:^1.0.2": - version: 1.0.2 - resolution: "is-descriptor@npm:1.0.2" + version: 1.0.3 + resolution: "is-descriptor@npm:1.0.3" dependencies: - is-accessor-descriptor: ^1.0.0 - is-data-descriptor: ^1.0.0 - kind-of: ^6.0.2 - checksum: 2ed623560bee035fb67b23e32ce885700bef8abe3fbf8c909907d86507b91a2c89a9d3a4d835a4d7334dd5db0237a0aeae9ca109c1e4ef1c0e7b577c0846ab5a - languageName: node - linkType: hard - -"is-directory@npm:^0.3.1": - version: 0.3.1 - resolution: "is-directory@npm:0.3.1" - checksum: dce9a9d3981e38f2ded2a80848734824c50ee8680cd09aa477bef617949715cfc987197a2ca0176c58a9fb192a1a0d69b535c397140d241996a609d5906ae524 + is-accessor-descriptor: ^1.0.1 + is-data-descriptor: ^1.0.1 + checksum: 316153b2fd86ac23b0a2f28b77744ae0a4e3c7a54fe52fa70b125d0971eb0a3bcfb562fa8e74537af0dad5bc405cc606726eb501fc748a241c10910deea89cfb languageName: node linkType: hard @@ -9316,17 +9537,10 @@ __metadata: languageName: node linkType: hard -"is-object@npm:^1.0.1": - version: 1.0.2 - resolution: "is-object@npm:1.0.2" - checksum: 971219c4b1985b9751f65e4c8296d3104f0457b0e8a70849e848a4a2208bc47317d73b3b85d4a369619cb2df8284dc22584cb2695a7d99aca5e8d0aa64fc075a - languageName: node - linkType: hard - -"is-plain-obj@npm:^1.1.0": - version: 1.1.0 - resolution: "is-plain-obj@npm:1.1.0" - checksum: 0ee04807797aad50859652a7467481816cbb57e5cc97d813a7dcd8915da8195dc68c436010bf39d195226cde6a2d352f4b815f16f26b7bf486a5754290629931 +"is-path-inside@npm:^3.0.3": + version: 3.0.3 + resolution: "is-path-inside@npm:3.0.3" + checksum: abd50f06186a052b349c15e55b182326f1936c89a78bf6c8f2b707412517c097ce04bc49a0ca221787bc44e1049f51f09a2ffb63d22899051988d3a618ba13e9 languageName: node linkType: hard @@ -9346,7 +9560,7 @@ __metadata: languageName: node linkType: hard -"is-regex@npm:^1.0.4, is-regex@npm:^1.1.4, is-regex@npm:~1.1.4": +"is-regex@npm:^1.1.4, is-regex@npm:~1.1.4": version: 1.1.4 resolution: "is-regex@npm:1.1.4" dependencies: @@ -9356,13 +9570,6 @@ __metadata: languageName: node linkType: hard -"is-retry-allowed@npm:^1.0.0": - version: 1.2.0 - resolution: "is-retry-allowed@npm:1.2.0" - checksum: 50d700a89ae31926b1c91b3eb0104dbceeac8790d8b80d02f5c76d9a75c2056f1bb24b5268a8a018dead606bddf116b2262e5ac07401eb8b8783b266ed22558d - languageName: node - linkType: hard - "is-shared-array-buffer@npm:^1.0.2": version: 1.0.2 resolution: "is-shared-array-buffer@npm:1.0.2" @@ -9372,7 +9579,7 @@ __metadata: languageName: node linkType: hard -"is-stream@npm:^1.0.0, is-stream@npm:^1.0.1, is-stream@npm:^1.1.0": +"is-stream@npm:^1.0.1, is-stream@npm:^1.1.0": version: 1.1.0 resolution: "is-stream@npm:1.1.0" checksum: 063c6bec9d5647aa6d42108d4c59723d2bd4ae42135a2d4db6eadbd49b7ea05b750fd69d279e5c7c45cf9da753ad2c00d8978be354d65aa9f6bb434969c6a2ae @@ -9404,6 +9611,15 @@ __metadata: languageName: node linkType: hard +"is-typed-array@npm:^1.1.10, is-typed-array@npm:^1.1.12, is-typed-array@npm:^1.1.9": + version: 1.1.13 + resolution: "is-typed-array@npm:1.1.13" + dependencies: + which-typed-array: ^1.1.14 + checksum: 150f9ada183a61554c91e1c4290086d2c100b0dff45f60b028519be72a8db964da403c48760723bf5253979b8dffe7b544246e0e5351dcd05c5fdb1dcc1dc0f0 + 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" @@ -9464,13 +9680,20 @@ __metadata: languageName: node linkType: hard -"isarray@npm:1.0.0, isarray@npm:~1.0.0": +"isarray@npm:1.0.0, isarray@npm:^1.0.0, isarray@npm:~1.0.0": version: 1.0.0 resolution: "isarray@npm:1.0.0" checksum: f032df8e02dce8ec565cf2eb605ea939bdccea528dbcf565cdf92bfa2da9110461159d86a537388ef1acef8815a330642d7885b29010e8f7eac967c9993b65ab languageName: node linkType: hard +"isarray@npm:^2.0.5": + version: 2.0.5 + resolution: "isarray@npm:2.0.5" + checksum: bd5bbe4104438c4196ba58a54650116007fa0262eccef13a4c55b2e09a5b36b59f1e75b9fcc49883dd9d4953892e6fc007eef9e9155648ceea036e184b0f930a + languageName: node + linkType: hard + "isexe@npm:^2.0.0": version: 2.0.0 resolution: "isexe@npm:2.0.0" @@ -9478,6 +9701,13 @@ __metadata: languageName: node linkType: hard +"isexe@npm:^3.1.1": + version: 3.1.1 + resolution: "isexe@npm:3.1.1" + checksum: 7fe1931ee4e88eb5aa524cd3ceb8c882537bc3a81b02e438b240e47012eef49c86904d0f0e593ea7c3a9996d18d0f1f3be8d3eaa92333977b0c3a9d353d5563e + languageName: node + linkType: hard + "iso-url@npm:^1.1.5": version: 1.2.1 resolution: "iso-url@npm:1.2.1" @@ -9501,6 +9731,16 @@ __metadata: languageName: node linkType: hard +"isomorphic-unfetch@npm:^3.0.0": + version: 3.1.0 + resolution: "isomorphic-unfetch@npm:3.1.0" + dependencies: + node-fetch: ^2.6.1 + unfetch: ^4.2.0 + checksum: 82b92fe4ec2823a81ab0fc0d11bd94d710e6f9a940d56b3cba31896d4345ec9ffc7949f4ff31ebcae84f6b95f7ebf3474c4c7452b834eb4078ea3f2c37e459c5 + languageName: node + linkType: hard + "isomorphic-ws@npm:^4.0.1": version: 4.0.1 resolution: "isomorphic-ws@npm:4.0.1" @@ -9517,16 +9757,6 @@ __metadata: languageName: node linkType: hard -"isurl@npm:^1.0.0-alpha5": - version: 1.0.0 - resolution: "isurl@npm:1.0.0" - dependencies: - has-to-string-tag-x: ^1.2.0 - is-object: ^1.0.1 - checksum: 28a96e019269d57015fa5869f19dda5a3ed1f7b21e3e0c4ff695419bd0541547db352aa32ee4a3659e811a177b0e37a5bc1a036731e71939dd16b59808ab92bd - languageName: node - linkType: hard - "it-all@npm:^1.0.4": version: 1.0.6 resolution: "it-all@npm:1.0.6" @@ -9586,6 +9816,19 @@ __metadata: languageName: node linkType: hard +"jackspeak@npm:^2.3.5": + version: 2.3.6 + resolution: "jackspeak@npm:2.3.6" + dependencies: + "@isaacs/cliui": ^8.0.2 + "@pkgjs/parseargs": ^0.11.0 + dependenciesMeta: + "@pkgjs/parseargs": + optional: true + checksum: 57d43ad11eadc98cdfe7496612f6bbb5255ea69fe51ea431162db302c2a11011642f50cfad57288bd0aea78384a0612b16e131944ad8ecd09d619041c8531b54 + languageName: node + linkType: hard + "jayson@npm:4.0.0": version: 4.0.0 resolution: "jayson@npm:4.0.0" @@ -9608,10 +9851,17 @@ __metadata: languageName: node linkType: hard -"js-sha3@npm:0.5.7, js-sha3@npm:^0.5.7": - version: 0.5.7 - resolution: "js-sha3@npm:0.5.7" - checksum: 973a28ea4b26cc7f12d2ab24f796e24ee4a71eef45a6634a052f6eb38cf8b2333db798e896e6e094ea6fa4dfe8e42a2a7942b425cf40da3f866623fd05bb91ea +"js-cookie@npm:^2.2.1": + version: 2.2.1 + resolution: "js-cookie@npm:2.2.1" + checksum: 9b1fb980a1c5e624fd4b28ea4867bb30c71e04c4484bb3a42766344c533faa684de9498e443425479ec68609e96e27b60614bfe354877c449c631529b6d932f2 + languageName: node + linkType: hard + +"js-sdsl@npm:^4.1.4": + version: 4.4.2 + resolution: "js-sdsl@npm:4.4.2" + checksum: ba705adc1788bf3c6f6c8e5077824f2bb4f0acab5a984420ce5cc492c7fff3daddc26335ad2c9a67d4f5e3241ec790f9e5b72a625adcf20cf321d2fd85e62b8b languageName: node linkType: hard @@ -9622,6 +9872,13 @@ __metadata: languageName: node linkType: hard +"js-sha3@npm:^0.5.7": + version: 0.5.7 + resolution: "js-sha3@npm:0.5.7" + checksum: 973a28ea4b26cc7f12d2ab24f796e24ee4a71eef45a6634a052f6eb38cf8b2333db798e896e6e094ea6fa4dfe8e42a2a7942b425cf40da3f866623fd05bb91ea + languageName: node + linkType: hard + "js-tokens@npm:^3.0.0 || ^4.0.0, js-tokens@npm:^4.0.0": version: 4.0.0 resolution: "js-tokens@npm:4.0.0" @@ -9636,19 +9893,7 @@ __metadata: languageName: node linkType: hard -"js-yaml@npm:3.13.1": - version: 3.13.1 - resolution: "js-yaml@npm:3.13.1" - dependencies: - argparse: ^1.0.7 - esprima: ^4.0.0 - bin: - js-yaml: bin/js-yaml.js - checksum: 7511b764abb66d8aa963379f7d2a404f078457d106552d05a7b556d204f7932384e8477513c124749fa2de52eb328961834562bd09924902c6432e40daa408bc - languageName: node - linkType: hard - -"js-yaml@npm:3.x, js-yaml@npm:^3.12.0, js-yaml@npm:^3.13.0, js-yaml@npm:^3.13.1": +"js-yaml@npm:3.x, js-yaml@npm:^3.13.1": version: 3.14.1 resolution: "js-yaml@npm:3.14.1" dependencies: @@ -9703,10 +9948,10 @@ __metadata: languageName: node linkType: hard -"json-parse-better-errors@npm:^1.0.1": - version: 1.0.2 - resolution: "json-parse-better-errors@npm:1.0.2" - checksum: ff2b5ba2a70e88fd97a3cb28c1840144c5ce8fae9cbeeddba15afa333a5c407cf0e42300cd0a2885dbb055227fe68d405070faad941beeffbfde9cf3b2c78c5d +"json-buffer@npm:3.0.1": + version: 3.0.1 + resolution: "json-buffer@npm:3.0.1" + checksum: 9026b03edc2847eefa2e37646c579300a1f3a4586cfb62bf857832b60c852042d0d6ae55d1afb8926163fa54c2b01d83ae24705f34990348bdac6273a29d4581 languageName: node linkType: hard @@ -9776,11 +10021,14 @@ __metadata: linkType: hard "json-stable-stringify@npm:^1.0.1": - version: 1.0.1 - resolution: "json-stable-stringify@npm:1.0.1" + version: 1.1.1 + resolution: "json-stable-stringify@npm:1.1.1" dependencies: - jsonify: ~0.0.0 - checksum: 65d6cbf0fca72a4136999f65f4401cf39a129f7aeff0fdd987ac3d3423a2113659294045fb8377e6e20d865cac32b1b8d70f3d87346c9786adcee60661d96ca5 + call-bind: ^1.0.5 + isarray: ^2.0.5 + jsonify: ^0.0.1 + object-keys: ^1.1.1 + checksum: e1ba06600fd278767eeff53f28e408e29c867e79abf564e7aadc3ce8f31f667258f8db278ef28831e45884dd687388fa1910f46e599fc19fb94c9afbbe3a4de8 languageName: node linkType: hard @@ -9800,14 +10048,14 @@ __metadata: languageName: node linkType: hard -"json5@npm:^1.0.1": - version: 1.0.1 - resolution: "json5@npm:1.0.1" +"json5@npm:^1.0.2": + version: 1.0.2 + resolution: "json5@npm:1.0.2" dependencies: minimist: ^1.2.0 bin: json5: lib/cli.js - checksum: e76ea23dbb8fc1348c143da628134a98adf4c5a4e8ea2adaa74a80c455fc2cdf0e2e13e6398ef819bfe92306b610ebb2002668ed9fc1af386d593691ef346fc3 + checksum: 866458a8c58a95a49bef3adba929c625e82532bcff1fe93f01d29cb02cac7c3fe1f4b79951b7792c2da9de0b32871a8401a6e3c5b36778ad852bf5b8a61165d7 languageName: node linkType: hard @@ -9848,10 +10096,10 @@ __metadata: languageName: node linkType: hard -"jsonify@npm:~0.0.0": - version: 0.0.0 - resolution: "jsonify@npm:0.0.0" - checksum: d8d4ed476c116e6987a460dcb82f22284686caae9f498ac87b0502c1765ac1522f4f450a4cad4cc368d202fd3b27a3860735140a82867fc6d558f5f199c38bce +"jsonify@npm:^0.0.1": + version: 0.0.1 + resolution: "jsonify@npm:0.0.1" + checksum: 027287e1c0294fce15f18c0ff990cfc2318e7f01fb76515f784d5cd0784abfec6fc5c2355c3a2f2cb0ad7f4aa2f5b74ebbfe4e80476c35b2d13cabdb572e1134 languageName: node linkType: hard @@ -9892,27 +10140,15 @@ __metadata: languageName: node linkType: hard -"keccak@npm:^3.0.0": - version: 3.0.2 - resolution: "keccak@npm:3.0.2" - dependencies: - node-addon-api: ^2.0.0 - node-gyp: latest - node-gyp-build: ^4.2.0 - readable-stream: ^3.6.0 - checksum: 39a7d6128b8ee4cb7dcd186fc7e20c6087cc39f573a0f81b147c323f688f1f7c2b34f62c4ae189fe9b81c6730b2d1228d8a399cdc1f3d8a4c8f030cdc4f20272 - languageName: node - linkType: hard - -"keccak@npm:^3.0.2": - version: 3.0.3 - resolution: "keccak@npm:3.0.3" +"keccak@npm:^3.0.0, keccak@npm:^3.0.2": + version: 3.0.4 + resolution: "keccak@npm:3.0.4" dependencies: node-addon-api: ^2.0.0 node-gyp: latest node-gyp-build: ^4.2.0 readable-stream: ^3.6.0 - checksum: f08f04f5cc87013a3fc9e87262f761daff38945c86dd09c01a7f7930a15ae3e14f93b310ef821dcc83675a7b814eb1c983222399a2f263ad980251201d1b9a99 + checksum: 2bf27b97b2f24225b1b44027de62be547f5c7326d87d249605665abd0c8c599d774671c35504c62c9b922cae02758504c6f76a73a84234d23af8a2211afaaa11 languageName: node linkType: hard @@ -9925,6 +10161,15 @@ __metadata: languageName: node linkType: hard +"keyv@npm:^4.0.0, keyv@npm:^4.5.3": + version: 4.5.4 + resolution: "keyv@npm:4.5.4" + dependencies: + json-buffer: 3.0.1 + checksum: 74a24395b1c34bd44ad5cb2b49140d087553e170625240b86755a6604cd65aa16efdbdeae5cdb17ba1284a0fbb25ad06263755dbc71b8d8b06f74232ce3cdd72 + languageName: node + linkType: hard + "kind-of@npm:^3.0.2, kind-of@npm:^3.0.3, kind-of@npm:^3.2.0": version: 3.2.2 resolution: "kind-of@npm:3.2.2" @@ -9943,14 +10188,7 @@ __metadata: languageName: node linkType: hard -"kind-of@npm:^5.0.0": - version: 5.1.0 - resolution: "kind-of@npm:5.1.0" - checksum: f2a0102ae0cf19c4a953397e552571bad2b588b53282874f25fca7236396e650e2db50d41f9f516bd402536e4df968dbb51b8e69e4d5d4a7173def78448f7bab - languageName: node - linkType: hard - -"kind-of@npm:^6.0.0, kind-of@npm:^6.0.2": +"kind-of@npm:^6.0.2": version: 6.0.3 resolution: "kind-of@npm:6.0.3" checksum: 3ab01e7b1d440b22fe4c31f23d8d38b4d9b91d9f291df683476576493d5dfd2e03848a8b05813dd0c3f0e835bc63f433007ddeceb71f05cb25c45ae1b19c6d3b @@ -10159,12 +10397,13 @@ __metadata: linkType: hard "level@npm:^8.0.0": - version: 8.0.0 - resolution: "level@npm:8.0.0" + version: 8.0.1 + resolution: "level@npm:8.0.1" dependencies: + abstract-level: ^1.0.4 browser-level: ^1.0.1 classic-level: ^1.2.0 - checksum: 13eb25bd71bfdca6cd714d1233adf9da97de9a8a4bf9f28d62a390b5c96d0250abaf983eb90eb8c4e89c7a985bb330750683d106f12670e5ea8fba1d7e608a1f + checksum: c5641cbba666ef9eb0292aad01d86a4f1af18e637d1fc097c65bf0109ab8d7e6fba8c8faf6c74ae4e48edac4310f7dd87def26ffeebfe395c7afd9bd2461ab97 languageName: node linkType: hard @@ -10278,6 +10517,13 @@ __metadata: languageName: node linkType: hard +"lodash.clonedeep@npm:^4.5.0": + version: 4.5.0 + resolution: "lodash.clonedeep@npm:4.5.0" + checksum: 92c46f094b064e876a23c97f57f81fbffd5d760bf2d8a1c61d85db6d1e488c66b0384c943abee4f6af7debf5ad4e4282e74ff83177c9e63d8ff081a4837c3489 + languageName: node + linkType: hard + "lodash.isequal@npm:^4.5.0": version: 4.5.0 resolution: "lodash.isequal@npm:4.5.0" @@ -10411,22 +10657,13 @@ __metadata: languageName: node linkType: hard -"lodash@npm:^4.17.11, lodash@npm:^4.17.12, lodash@npm:^4.17.14, lodash@npm:^4.17.15, lodash@npm:^4.17.19, lodash@npm:^4.17.21, lodash@npm:^4.17.4": +"lodash@npm:^4.17.11, lodash@npm:^4.17.14, lodash@npm:^4.17.15, lodash@npm:^4.17.19, lodash@npm:^4.17.21, lodash@npm:^4.17.4": version: 4.17.21 resolution: "lodash@npm:4.17.21" checksum: eb835a2e51d381e561e508ce932ea50a8e5a68f4ebdd771ea240d3048244a8d13658acbd502cd4829768c56f2e16bdd4340b9ea141297d472517b83868e677f7 languageName: node linkType: hard -"log-symbols@npm:3.0.0, log-symbols@npm:^3.0.0": - version: 3.0.0 - resolution: "log-symbols@npm:3.0.0" - dependencies: - chalk: ^2.4.2 - checksum: f2322e1452d819050b11aad247660e1494f8b2219d40a964af91d5f9af1a90636f1b3d93f2952090e42af07cc5550aecabf6c1d8ec1181207e95cb66ba112361 - languageName: node - linkType: hard - "log-symbols@npm:4.1.0": version: 4.1.0 resolution: "log-symbols@npm:4.1.0" @@ -10437,6 +10674,15 @@ __metadata: languageName: node linkType: hard +"log-symbols@npm:^3.0.0": + version: 3.0.0 + resolution: "log-symbols@npm:3.0.0" + dependencies: + chalk: ^2.4.2 + checksum: f2322e1452d819050b11aad247660e1494f8b2219d40a964af91d5f9af1a90636f1b3d93f2952090e42af07cc5550aecabf6c1d8ec1181207e95cb66ba112361 + languageName: node + linkType: hard + "long@npm:^4.0.0": version: 4.0.0 resolution: "long@npm:4.0.0" @@ -10469,12 +10715,12 @@ __metadata: languageName: node linkType: hard -"loupe@npm:^2.3.1": - version: 2.3.4 - resolution: "loupe@npm:2.3.4" +"loupe@npm:^2.3.6": + version: 2.3.7 + resolution: "loupe@npm:2.3.7" dependencies: - get-func-name: ^2.0.0 - checksum: 5af91db61aa18530f1749a64735ee194ac263e65e9f4d1562bf3036c591f1baa948289c193e0e34c7b5e2c1b75d3c1dc4fce87f5edb3cee10b0c0df46bc9ffb3 + get-func-name: ^2.0.1 + checksum: 96c058ec7167598e238bb7fb9def2f9339215e97d6685d9c1e3e4bdb33d14600e11fe7a812cf0c003dfb73ca2df374f146280b2287cae9e8d989e9d7a69a203b languageName: node linkType: hard @@ -10501,6 +10747,13 @@ __metadata: languageName: node linkType: hard +"lru-cache@npm:^10.0.1, lru-cache@npm:^9.1.1 || ^10.0.0": + version: 10.2.0 + resolution: "lru-cache@npm:10.2.0" + checksum: eee7ddda4a7475deac51ac81d7dd78709095c6fa46e8350dc2d22462559a1faa3b81ed931d5464b13d48cbd7e08b46100b6f768c76833912bc444b99c37e25db + languageName: node + linkType: hard + "lru-cache@npm:^3.2.0": version: 3.2.0 resolution: "lru-cache@npm:3.2.0" @@ -10519,13 +10772,6 @@ __metadata: languageName: node linkType: hard -"lru-cache@npm:^7.7.1": - version: 7.10.2 - resolution: "lru-cache@npm:7.10.2" - checksum: b8f98064c981ce98e2a3b82903726dd28d842f54b7f53e1d6029c6836546243d4157b8495a405c062209715c612050150bdf30b5fa8200d5a3559fb0fd4072c1 - languageName: node - linkType: hard - "lru_map@npm:^0.3.3": version: 0.3.3 resolution: "lru_map@npm:0.3.3" @@ -10554,27 +10800,22 @@ __metadata: languageName: node linkType: hard -"make-fetch-happen@npm:^10.0.3": - version: 10.1.8 - resolution: "make-fetch-happen@npm:10.1.8" +"make-fetch-happen@npm:^13.0.0": + version: 13.0.0 + resolution: "make-fetch-happen@npm:13.0.0" dependencies: - agentkeepalive: ^4.2.1 - cacache: ^16.1.0 - http-cache-semantics: ^4.1.0 - http-proxy-agent: ^5.0.0 - https-proxy-agent: ^5.0.0 + "@npmcli/agent": ^2.0.0 + cacache: ^18.0.0 + http-cache-semantics: ^4.1.1 is-lambda: ^1.0.1 - lru-cache: ^7.7.1 - minipass: ^3.1.6 - minipass-collect: ^1.0.2 - minipass-fetch: ^2.0.3 + minipass: ^7.0.2 + minipass-fetch: ^3.0.0 minipass-flush: ^1.0.5 minipass-pipeline: ^1.2.4 negotiator: ^0.6.3 promise-retry: ^2.0.1 - socks-proxy-agent: ^7.0.0 - ssri: ^9.0.0 - checksum: 5fe9fd9da5368a8a4fe9a3ea5b9aa15f1e91c9ab703cd9027a6b33840ecc8a57c182fbe1c767c139330a88c46a448b1f00da5e32065cec373aff2450b3da54ee + ssri: ^10.0.0 + checksum: 7c7a6d381ce919dd83af398b66459a10e2fe8f4504f340d1d090d3fa3d1b0c93750220e1d898114c64467223504bd258612ba83efbc16f31b075cd56de24b4af languageName: node linkType: hard @@ -10760,6 +11001,13 @@ __metadata: languageName: node linkType: hard +"micro-ftch@npm:^0.3.1": + version: 0.3.1 + resolution: "micro-ftch@npm:0.3.1" + checksum: 0e496547253a36e98a83fb00c628c53c3fb540fa5aaeaf718438873785afd193244988c09d219bb1802984ff227d04938d9571ef90fe82b48bd282262586aaff + languageName: node + linkType: hard + "micromatch@npm:^3.1.4": version: 3.1.10 resolution: "micromatch@npm:3.1.10" @@ -10828,13 +11076,6 @@ __metadata: languageName: node linkType: hard -"mimic-fn@npm:^1.0.0": - version: 1.2.0 - resolution: "mimic-fn@npm:1.2.0" - checksum: 69c08205156a1f4906d9c46f9b4dc08d18a50176352e77fdeb645cedfe9f20c0b19865d465bd2dec27a5c432347f24dc07fc3695e11159d193f892834233e939 - languageName: node - linkType: hard - "mimic-fn@npm:^2.0.0, mimic-fn@npm:^2.1.0": version: 2.1.0 resolution: "mimic-fn@npm:2.1.0" @@ -10849,6 +11090,13 @@ __metadata: languageName: node linkType: hard +"mimic-response@npm:^3.1.0": + version: 3.1.0 + resolution: "mimic-response@npm:3.1.0" + checksum: 25739fee32c17f433626bf19f016df9036b75b3d84a3046c7d156e72ec963dd29d7fc8a302f55a3d6c5a4ff24259676b15d915aad6480815a969ff2ec0836867 + languageName: node + linkType: hard + "min-document@npm:^2.19.0": version: 2.19.0 resolution: "min-document@npm:2.19.0" @@ -10872,7 +11120,7 @@ __metadata: languageName: node linkType: hard -"minimatch@npm:2 || 3, minimatch@npm:^3.0.2, minimatch@npm:^3.0.4, minimatch@npm:^3.1.1, minimatch@npm:^3.1.2": +"minimatch@npm:2 || 3, minimatch@npm:^3.0.2, minimatch@npm:^3.0.4, minimatch@npm:^3.0.5, minimatch@npm:^3.1.1, minimatch@npm:^3.1.2": version: 3.1.2 resolution: "minimatch@npm:3.1.2" dependencies: @@ -10881,15 +11129,6 @@ __metadata: languageName: node linkType: hard -"minimatch@npm:3.0.4": - version: 3.0.4 - resolution: "minimatch@npm:3.0.4" - dependencies: - brace-expansion: ^1.1.7 - checksum: 66ac295f8a7b59788000ea3749938b0970344c841750abd96694f80269b926ebcafad3deeb3f1da2522978b119e6ae3a5869b63b13a7859a456b3408bd18a078 - languageName: node - linkType: hard - "minimatch@npm:5.0.1": version: 5.0.1 resolution: "minimatch@npm:5.0.1" @@ -10900,49 +11139,51 @@ __metadata: linkType: hard "minimatch@npm:^5.0.1": - version: 5.1.0 - resolution: "minimatch@npm:5.1.0" + version: 5.1.6 + resolution: "minimatch@npm:5.1.6" dependencies: brace-expansion: ^2.0.1 - checksum: 15ce53d31a06361e8b7a629501b5c75491bc2b59712d53e802b1987121d91b433d73fcc5be92974fde66b2b51d8fb28d75a9ae900d249feb792bb1ba2a4f0a90 + checksum: 7564208ef81d7065a370f788d337cd80a689e981042cb9a1d0e6580b6c6a8c9279eba80010516e258835a988363f99f54a6f711a315089b8b42694f5da9d0d77 languageName: node linkType: hard -"minimist@npm:^1.2.0, minimist@npm:^1.2.5, minimist@npm:^1.2.6, minimist@npm:~1.2.6": - version: 1.2.6 - resolution: "minimist@npm:1.2.6" - checksum: d15428cd1e11eb14e1233bcfb88ae07ed7a147de251441d61158619dfb32c4d7e9061d09cab4825fdee18ecd6fce323228c8c47b5ba7cd20af378ca4048fb3fb +"minimatch@npm:^9.0.1": + version: 9.0.3 + resolution: "minimatch@npm:9.0.3" + dependencies: + brace-expansion: ^2.0.1 + checksum: 253487976bf485b612f16bf57463520a14f512662e592e95c571afdab1442a6a6864b6c88f248ce6fc4ff0b6de04ac7aa6c8bb51e868e99d1d65eb0658a708b5 languageName: node linkType: hard -"minimist@npm:^1.2.7": +"minimist@npm:^1.2.0, minimist@npm:^1.2.5, minimist@npm:^1.2.6, minimist@npm:^1.2.7, minimist@npm:~1.2.8": version: 1.2.8 resolution: "minimist@npm:1.2.8" checksum: 75a6d645fb122dad29c06a7597bddea977258957ed88d7a6df59b5cd3fe4a527e253e9bbf2e783e4b73657f9098b96a5fe96ab8a113655d4109108577ecf85b0 languageName: node linkType: hard -"minipass-collect@npm:^1.0.2": - version: 1.0.2 - resolution: "minipass-collect@npm:1.0.2" +"minipass-collect@npm:^2.0.1": + version: 2.0.1 + resolution: "minipass-collect@npm:2.0.1" dependencies: - minipass: ^3.0.0 - checksum: 14df761028f3e47293aee72888f2657695ec66bd7d09cae7ad558da30415fdc4752bbfee66287dcc6fd5e6a2fa3466d6c484dc1cbd986525d9393b9523d97f10 + minipass: ^7.0.3 + checksum: b251bceea62090f67a6cced7a446a36f4cd61ee2d5cea9aee7fff79ba8030e416327a1c5aa2908dc22629d06214b46d88fdab8c51ac76bacbf5703851b5ad342 languageName: node linkType: hard -"minipass-fetch@npm:^2.0.3": - version: 2.1.0 - resolution: "minipass-fetch@npm:2.1.0" +"minipass-fetch@npm:^3.0.0": + version: 3.0.4 + resolution: "minipass-fetch@npm:3.0.4" dependencies: encoding: ^0.1.13 - minipass: ^3.1.6 + minipass: ^7.0.3 minipass-sized: ^1.0.3 minizlib: ^2.1.2 dependenciesMeta: encoding: optional: true - checksum: 1334732859a3f7959ed22589bafd9c40384b885aebb5932328071c33f86b3eb181d54c86919675d1825ab5f1c8e4f328878c863873258d113c29d79a4b0c9c9f + checksum: af7aad15d5c128ab1ebe52e043bdf7d62c3c6f0cecb9285b40d7b395e1375b45dcdfd40e63e93d26a0e8249c9efd5c325c65575aceee192883970ff8cb11364a languageName: node linkType: hard @@ -10983,12 +11224,26 @@ __metadata: languageName: node linkType: hard -"minipass@npm:^3.0.0, minipass@npm:^3.1.1, minipass@npm:^3.1.6": - version: 3.3.4 - resolution: "minipass@npm:3.3.4" +"minipass@npm:^3.0.0": + version: 3.3.6 + resolution: "minipass@npm:3.3.6" dependencies: yallist: ^4.0.0 - checksum: 5d95a7738c54852ba78d484141e850c792e062666a2d0c681a5ac1021275beb7e1acb077e59f9523ff1defb80901aea4e30fac10ded9a20a25d819a42916ef1b + checksum: a30d083c8054cee83cdcdc97f97e4641a3f58ae743970457b1489ce38ee1167b3aaf7d815cd39ec7a99b9c40397fd4f686e83750e73e652b21cb516f6d845e48 + languageName: node + linkType: hard + +"minipass@npm:^5.0.0": + version: 5.0.0 + resolution: "minipass@npm:5.0.0" + checksum: 425dab288738853fded43da3314a0b5c035844d6f3097a8e3b5b29b328da8f3c1af6fc70618b32c29ff906284cf6406b6841376f21caaadd0793c1d5a6a620ea + languageName: node + linkType: hard + +"minipass@npm:^5.0.0 || ^6.0.2 || ^7.0.0, minipass@npm:^7.0.2, minipass@npm:^7.0.3": + version: 7.0.4 + resolution: "minipass@npm:7.0.4" + checksum: 87585e258b9488caf2e7acea242fd7856bbe9a2c84a7807643513a338d66f368c7d518200ad7b70a508664d408aa000517647b2930c259a8b1f9f0984f344a21 languageName: node linkType: hard @@ -11030,23 +11285,12 @@ __metadata: languageName: node linkType: hard -"mkdirp@npm:*, mkdirp@npm:^1.0.3, mkdirp@npm:^1.0.4": - version: 1.0.4 - resolution: "mkdirp@npm:1.0.4" - bin: - mkdirp: bin/cmd.js - checksum: a96865108c6c3b1b8e1d5e9f11843de1e077e57737602de1b82030815f311be11f96f09cce59bd5b903d0b29834733e5313f9301e3ed6d6f6fba2eae0df4298f - languageName: node - linkType: hard - -"mkdirp@npm:0.5.5": - version: 0.5.5 - resolution: "mkdirp@npm:0.5.5" - dependencies: - minimist: ^1.2.5 +"mkdirp@npm:*": + version: 3.0.1 + resolution: "mkdirp@npm:3.0.1" bin: - mkdirp: bin/cmd.js - checksum: 3bce20ea525f9477befe458ab85284b0b66c8dc3812f94155af07c827175948cdd8114852ac6c6d82009b13c1048c37f6d98743eb019651ee25c39acc8aabe7d + mkdirp: dist/cjs/src/bin.js + checksum: 972deb188e8fb55547f1e58d66bd6b4a3623bf0c7137802582602d73e6480c1c2268dcbafbfb1be466e00cc7e56ac514d7fd9334b7cf33e3e2ab547c16f83a8d languageName: node linkType: hard @@ -11061,6 +11305,15 @@ __metadata: languageName: node linkType: hard +"mkdirp@npm:^1.0.3, mkdirp@npm:^1.0.4": + version: 1.0.4 + resolution: "mkdirp@npm:1.0.4" + bin: + mkdirp: bin/cmd.js + checksum: a96865108c6c3b1b8e1d5e9f11843de1e077e57737602de1b82030815f311be11f96f09cce59bd5b903d0b29834733e5313f9301e3ed6d6f6fba2eae0df4298f + languageName: node + linkType: hard + "mnemonist@npm:^0.38.0": version: 0.38.5 resolution: "mnemonist@npm:0.38.5" @@ -11070,46 +11323,10 @@ __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" +"mocha@npm:^10.0.0, mocha@npm:^10.2.0": + version: 10.2.0 + resolution: "mocha@npm:10.2.0" dependencies: - "@ungap/promise-all-settled": 1.1.2 ansi-colors: 4.1.1 browser-stdout: 1.3.1 chokidar: 3.5.3 @@ -11134,42 +11351,7 @@ __metadata: bin: _mocha: bin/_mocha mocha: bin/mocha.js - checksum: ba49ddcf8015a467e744b06c396aab361b1281302e38e7c1269af25ba51ff9ab681a9c36e9046bb7491e751cd7d5ce85e276a00ce7e204f96b2c418e4595edfe - languageName: node - linkType: hard - -"mocha@npm:^7.1.1": - version: 7.2.0 - resolution: "mocha@npm:7.2.0" - 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: d098484fe1b165bb964fdbf6b88b256c71fead47575ca7c5bcf8ed07db0dcff41905f6d2f0a05111a0441efaef9d09241a8cc1ddf7961056b28984ec63ba2874 + checksum: 406c45eab122ffd6ea2003c2f108b2bc35ba036225eee78e0c784b6fa2c7f34e2b13f1dbacef55a4fdf523255d76e4f22d1b5aacda2394bd11666febec17c719 languageName: node linkType: hard @@ -11180,6 +11362,20 @@ __metadata: languageName: node linkType: hard +"mock-property@npm:~1.0.0": + version: 1.0.3 + resolution: "mock-property@npm:1.0.3" + dependencies: + define-data-property: ^1.1.1 + functions-have-names: ^1.2.3 + gopd: ^1.0.1 + has-property-descriptors: ^1.0.0 + hasown: ^2.0.0 + isarray: ^2.0.5 + checksum: 835b106e38580c929def6803dad58fc8299d77ed876faed0098f9eb2076e30a2ef36fb5098adac87a4901c13532de86a859e63c8b6769fb7527e1dbbb6430cce + 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" @@ -11189,15 +11385,8 @@ __metadata: "ms@npm:2.0.0": version: 2.0.0 - resolution: "ms@npm:2.0.0" - checksum: 0e6a22b8b746d2e0b65a430519934fefd41b6db0682e3477c10f60c76e947c4c0ad06f63ffdf1d78d335f83edee8c0aa928aa66a36c7cd95b69b26f468d527f4 - languageName: node - linkType: hard - -"ms@npm:2.1.1": - version: 2.1.1 - resolution: "ms@npm:2.1.1" - checksum: 0078a23cd916a9a7435c413caa14c57d4b4f6e2470e0ab554b6964163c8a4436448ac7ae020e883685475da6b6796cc396b670f579cb275db288a21e3e57721e + resolution: "ms@npm:2.0.0" + checksum: 0e6a22b8b746d2e0b65a430519934fefd41b6db0682e3477c10f60c76e947c4c0ad06f63ffdf1d78d335f83edee8c0aa928aa66a36c7cd95b69b26f468d527f4 languageName: node linkType: hard @@ -11208,7 +11397,7 @@ __metadata: languageName: node linkType: hard -"ms@npm:2.1.3, ms@npm:^2.0.0, ms@npm:^2.1.1": +"ms@npm:2.1.3, ms@npm:^2.1.1": version: 2.1.3 resolution: "ms@npm:2.1.3" checksum: aa92de608021b242401676e35cfa5aa42dd70cbdc082b916da7fb925c542173e36bce97ea3e804923fe92c0ad991434e4a38327e15a1b5b5f945d66df615ae6d @@ -11304,13 +11493,6 @@ __metadata: languageName: node linkType: hard -"mute-stream@npm:0.0.7": - version: 0.0.7 - resolution: "mute-stream@npm:0.0.7" - checksum: a9d4772c1c84206aa37c218ed4751cd060239bf1d678893124f51e037f6f22f4a159b2918c030236c93252638a74beb29c9b1fd3267c9f24d4b3253cf1eaa86f - languageName: node - linkType: hard - "mute-stream@npm:0.0.8": version: 0.0.8 resolution: "mute-stream@npm:0.0.8" @@ -11335,11 +11517,11 @@ __metadata: linkType: hard "nanoid@npm:^3.0.2, nanoid@npm:^3.1.20, nanoid@npm:^3.1.23": - version: 3.3.6 - resolution: "nanoid@npm:3.3.6" + version: 3.3.7 + resolution: "nanoid@npm:3.3.7" bin: nanoid: bin/nanoid.cjs - checksum: 7d0eda657002738aa5206107bd0580aead6c95c460ef1bdd0b1a87a9c7ae6277ac2e9b945306aaa5b32c6dcb7feaf462d0f552e7f8b5718abfc6ead5c94a71b3 + checksum: d36c427e530713e4ac6567d488b489a36582ef89da1d6d4e3b87eded11eb10d7042a877958c6f104929809b2ab0bafa17652b076cdf84324aa75b30b722204f2 languageName: node linkType: hard @@ -11362,10 +11544,10 @@ __metadata: languageName: node linkType: hard -"napi-macros@npm:~2.0.0": - version: 2.0.0 - resolution: "napi-macros@npm:2.0.0" - checksum: 30384819386977c1f82034757014163fa60ab3c5a538094f778d38788bebb52534966279956f796a92ea771c7f8ae072b975df65de910d051ffbdc927f62320c +"napi-macros@npm:^2.2.2": + version: 2.2.2 + resolution: "napi-macros@npm:2.2.2" + checksum: c6f9bd71cdbbc37ddc3535aa5be481238641d89585b8a3f4d301cb89abf459e2d294810432bb7d12056d1f9350b1a0899a5afcf460237a3da6c398cf0fec7629 languageName: node linkType: hard @@ -11387,6 +11569,13 @@ __metadata: languageName: node linkType: hard +"natural-compare-lite@npm:^1.4.0": + version: 1.4.0 + resolution: "natural-compare-lite@npm:1.4.0" + checksum: 5222ac3986a2b78dd6069ac62cbb52a7bf8ffc90d972ab76dfe7b01892485d229530ed20d0c62e79a6b363a663b273db3bde195a1358ce9e5f779d4453887225 + languageName: node + linkType: hard + "natural-compare@npm:^1.4.0": version: 1.4.0 resolution: "natural-compare@npm:1.4.0" @@ -11401,7 +11590,7 @@ __metadata: languageName: node linkType: hard -"neo-async@npm:^2.6.0": +"neo-async@npm:^2.6.2": version: 2.6.2 resolution: "neo-async@npm:2.6.2" checksum: deac9f8d00eda7b2e5cd1b2549e26e10a0faa70adaa6fdadca701cc55f49ee9018e427f424bac0c790b7c7e2d3068db97f3093f1093975f2acb8f8818b936ed9 @@ -11440,17 +11629,7 @@ __metadata: languageName: node linkType: hard -"node-environment-flags@npm:1.0.6": - version: 1.0.6 - resolution: "node-environment-flags@npm:1.0.6" - dependencies: - object.getownpropertydescriptors: ^2.0.3 - semver: ^5.7.0 - checksum: 268139ed0f7fabdca346dcb26931300ec7a1dc54a58085a849e5c78a82b94967f55df40177a69d4e819da278d98686d5c4fd49ab0d7bcff16fda25b6fffc4ca3 - languageName: node - linkType: hard - -"node-fetch@npm:^2.3.0, node-fetch@npm:^2.6.8": +"node-fetch@npm:^2.3.0, node-fetch@npm:^2.6.0, node-fetch@npm:^2.6.1, node-fetch@npm:^2.6.7, node-fetch@npm:^2.6.8": version: 2.7.0 resolution: "node-fetch@npm:2.7.0" dependencies: @@ -11464,20 +11643,6 @@ __metadata: languageName: node linkType: hard -"node-fetch@npm:^2.6.1, node-fetch@npm:^2.6.7": - version: 2.6.7 - resolution: "node-fetch@npm:2.6.7" - dependencies: - whatwg-url: ^5.0.0 - peerDependencies: - encoding: ^0.1.0 - peerDependenciesMeta: - encoding: - optional: true - checksum: 8d816ffd1ee22cab8301c7756ef04f3437f18dace86a1dae22cf81db8ef29c0bf6655f3215cb0cdb22b420b6fe141e64b26905e7f33f9377a7fa59135ea3e10b - languageName: node - linkType: hard - "node-fetch@npm:~1.7.1": version: 1.7.3 resolution: "node-fetch@npm:1.7.3" @@ -11489,40 +11654,33 @@ __metadata: linkType: hard "node-gyp-build@npm:^4.2.0, node-gyp-build@npm:^4.3.0": - version: 4.4.0 - resolution: "node-gyp-build@npm:4.4.0" + version: 4.8.0 + resolution: "node-gyp-build@npm:4.8.0" bin: node-gyp-build: bin.js node-gyp-build-optional: optional.js node-gyp-build-test: build-test.js - checksum: 972a059f960253d254e0b23ce10f54c8982236fc0edcab85166d0b7f87443b2ce98391c877cfb2f6eeafcf03c538c5f4dd3e0bfff03828eb48634f58f4c64343 + checksum: b82a56f866034b559dd3ed1ad04f55b04ae381b22ec2affe74b488d1582473ca6e7f85fccf52da085812d3de2b0bf23109e752a57709ac7b9963951c710fea40 languageName: node linkType: hard "node-gyp@npm:latest": - version: 9.0.0 - resolution: "node-gyp@npm:9.0.0" + version: 10.0.1 + resolution: "node-gyp@npm:10.0.1" dependencies: env-paths: ^2.2.0 - glob: ^7.1.4 + exponential-backoff: ^3.1.1 + glob: ^10.3.10 graceful-fs: ^4.2.6 - make-fetch-happen: ^10.0.3 - nopt: ^5.0.0 - npmlog: ^6.0.0 - rimraf: ^3.0.2 + make-fetch-happen: ^13.0.0 + nopt: ^7.0.0 + proc-log: ^3.0.0 semver: ^7.3.5 tar: ^6.1.2 - which: ^2.0.2 + which: ^4.0.0 bin: node-gyp: bin/node-gyp.js - checksum: 4d8ef8860f7e4f4d86c91db3f519d26ed5cc23b48fe54543e2afd86162b4acbd14f21de42a5db344525efb69a991e021b96a68c70c6e2d5f4a5cb770793da6d3 - languageName: node - linkType: hard - -"nofilter@npm:^1.0.4": - version: 1.0.4 - resolution: "nofilter@npm:1.0.4" - checksum: 54d864f745de5c3312994e880cf2d4f55e34830d6adc8275dce3731507ca380d21040336e4a277a4901551c07f04c452fbeffd57fad1dc8f68a2943eaf894a04 + checksum: 60a74e66d364903ce02049966303a57f898521d139860ac82744a5fdd9f7b7b3b61f75f284f3bfe6e6add3b8f1871ce305a1d41f775c7482de837b50c792223f languageName: node linkType: hard @@ -11544,14 +11702,14 @@ __metadata: languageName: node linkType: hard -"nopt@npm:^5.0.0": - version: 5.0.0 - resolution: "nopt@npm:5.0.0" +"nopt@npm:^7.0.0": + version: 7.2.0 + resolution: "nopt@npm:7.2.0" dependencies: - abbrev: 1 + abbrev: ^2.0.0 bin: nopt: bin/nopt.js - checksum: d35fdec187269503843924e0114c0c6533fb54bbf1620d0f28b4b60ba01712d6687f62565c55cc20a504eff0fbe5c63e22340c3fad549ad40469ffb611b04f2f + checksum: a9c0f57fb8cb9cc82ae47192ca2b7ef00e199b9480eed202482c962d61b59a7fbe7541920b2a5839a97b42ee39e288c0aed770e38057a608d7f579389dfde410 languageName: node linkType: hard @@ -11581,6 +11739,13 @@ __metadata: languageName: node linkType: hard +"normalize-url@npm:^6.0.1": + version: 6.1.0 + resolution: "normalize-url@npm:6.1.0" + checksum: 4a4944631173e7d521d6b80e4c85ccaeceb2870f315584fa30121f505a6dfd86439c5e3fdd8cd9e0e291290c41d0c3599f0cb12ab356722ed242584c30348e50 + languageName: node + linkType: hard + "npm-run-path@npm:^2.0.0": version: 2.0.2 resolution: "npm-run-path@npm:2.0.2" @@ -11599,18 +11764,6 @@ __metadata: languageName: node linkType: hard -"npmlog@npm:^6.0.0": - version: 6.0.2 - resolution: "npmlog@npm:6.0.2" - dependencies: - are-we-there-yet: ^3.0.0 - console-control-strings: ^1.1.0 - gauge: ^4.0.3 - set-blocking: ^2.0.0 - checksum: ae238cd264a1c3f22091cdd9e2b106f684297d3c184f1146984ecbe18aaa86343953f26b9520dedd1b1372bc0316905b736c1932d778dbeb1fcf5a1001390e2a - languageName: node - linkType: hard - "number-is-nan@npm:^1.0.0": version: 1.0.1 resolution: "number-is-nan@npm:1.0.1" @@ -11653,14 +11806,21 @@ __metadata: languageName: node linkType: hard -"object-inspect@npm:^1.12.0, object-inspect@npm:^1.9.0, object-inspect@npm:~1.12.0": - version: 1.12.2 - resolution: "object-inspect@npm:1.12.2" - checksum: a534fc1b8534284ed71f25ce3a496013b7ea030f3d1b77118f6b7b1713829262be9e6243acbcb3ef8c626e2b64186112cb7f6db74e37b2789b9c789ca23048b2 +"object-inspect@npm:^1.13.1, object-inspect@npm:^1.9.0": + version: 1.13.1 + resolution: "object-inspect@npm:1.13.1" + checksum: 7d9fa9221de3311dcb5c7c307ee5dc011cdd31dc43624b7c184b3840514e118e05ef0002be5388304c416c0eb592feb46e983db12577fc47e47d5752fbbfb61f languageName: node linkType: hard -"object-is@npm:^1.0.1": +"object-inspect@npm:~1.12.3": + version: 1.12.3 + resolution: "object-inspect@npm:1.12.3" + checksum: dabfd824d97a5f407e6d5d24810d888859f6be394d8b733a77442b277e0808860555176719c5905e765e3743a7cada6b8b0a3b85e5331c530fd418cc8ae991db + languageName: node + linkType: hard + +"object-is@npm:^1.1.5": version: 1.1.5 resolution: "object-is@npm:1.1.5" dependencies: @@ -11670,7 +11830,7 @@ __metadata: languageName: node linkType: hard -"object-keys@npm:^1.0.11, object-keys@npm:^1.1.1": +"object-keys@npm:^1.1.1": version: 1.1.1 resolution: "object-keys@npm:1.1.1" checksum: b363c5e7644b1e1b04aa507e88dcb8e3a2f52b6ffd0ea801e4c7a62d5aa559affe21c55a07fd4b1fd55fc03a33c610d73426664b20032405d7b92a1414c34d6a @@ -11693,39 +11853,52 @@ __metadata: languageName: node linkType: hard -"object.assign@npm:4.1.0": - version: 4.1.0 - resolution: "object.assign@npm:4.1.0" +"object.assign@npm:^4.1.4": + version: 4.1.5 + resolution: "object.assign@npm:4.1.5" dependencies: - define-properties: ^1.1.2 - function-bind: ^1.1.1 - has-symbols: ^1.0.0 - object-keys: ^1.0.11 - checksum: 648a9a463580bf48332d9a49a76fede2660ab1ee7104d9459b8a240562246da790b4151c3c073f28fda31c1fdc555d25a1d871e72be403e997e4468c91f4801f + call-bind: ^1.0.5 + define-properties: ^1.2.1 + has-symbols: ^1.0.3 + object-keys: ^1.1.1 + checksum: f9aeac0541661370a1fc86e6a8065eb1668d3e771f7dbb33ee54578201336c057b21ee61207a186dd42db0c62201d91aac703d20d12a79fc79c353eed44d4e25 languageName: node linkType: hard -"object.assign@npm:^4.1.2": - version: 4.1.2 - resolution: "object.assign@npm:4.1.2" +"object.fromentries@npm:^2.0.7": + version: 2.0.7 + resolution: "object.fromentries@npm:2.0.7" dependencies: - call-bind: ^1.0.0 - define-properties: ^1.1.3 - has-symbols: ^1.0.1 - object-keys: ^1.1.1 - checksum: d621d832ed7b16ac74027adb87196804a500d80d9aca536fccb7ba48d33a7e9306a75f94c1d29cbfa324bc091bfc530bc24789568efdaee6a47fcfa298993814 + call-bind: ^1.0.2 + define-properties: ^1.2.0 + es-abstract: ^1.22.1 + checksum: 7341ce246e248b39a431b87a9ddd331ff52a454deb79afebc95609f94b1f8238966cf21f52188f2a353f0fdf83294f32f1ebf1f7826aae915ebad21fd0678065 languageName: node linkType: hard -"object.getownpropertydescriptors@npm:^2.0.3, object.getownpropertydescriptors@npm:^2.1.1": - version: 2.1.4 - resolution: "object.getownpropertydescriptors@npm:2.1.4" +"object.getownpropertydescriptors@npm:^2.1.6": + version: 2.1.7 + resolution: "object.getownpropertydescriptors@npm:2.1.7" dependencies: - array.prototype.reduce: ^1.0.4 + array.prototype.reduce: ^1.0.6 call-bind: ^1.0.2 - define-properties: ^1.1.4 - es-abstract: ^1.20.1 - checksum: 988c466fe49fc4f19a28d2d1d894c95c6abfe33c94674ec0b14d96eed71f453c7ad16873d430dc2acbb1760de6d3d2affac4b81237a306012cc4dc49f7539e7f + define-properties: ^1.2.0 + es-abstract: ^1.22.1 + safe-array-concat: ^1.0.0 + checksum: 8e7ae1d522a3874d2d23a3d0fb75828cbcee60958b65c2ad8e58ce227f4efba8cc2b59c7431a0fd48b20d9e04ec075bc0e0d694b1d2c2296e534daf558beb10b + languageName: node + linkType: hard + +"object.groupby@npm:^1.0.1": + version: 1.0.2 + resolution: "object.groupby@npm:1.0.2" + dependencies: + array.prototype.filter: ^1.0.3 + call-bind: ^1.0.5 + define-properties: ^1.2.1 + es-abstract: ^1.22.3 + es-errors: ^1.0.0 + checksum: 5f95c2a3a5f60a1a8c05fdd71455110bd3d5e6af0350a20b133d8cd70f9c3385d5c7fceb6a17b940c3c61752d9c202d10d5e2eb5ce73b89002656a87e7bf767a languageName: node linkType: hard @@ -11738,14 +11911,14 @@ __metadata: languageName: node linkType: hard -"object.values@npm:^1.1.5": - version: 1.1.5 - resolution: "object.values@npm:1.1.5" +"object.values@npm:^1.1.7": + version: 1.1.7 + resolution: "object.values@npm:1.1.7" dependencies: call-bind: ^1.0.2 - define-properties: ^1.1.3 - es-abstract: ^1.19.1 - checksum: 0f17e99741ebfbd0fa55ce942f6184743d3070c61bd39221afc929c8422c4907618c8da694c6915bc04a83ab3224260c779ba37fc07bb668bdc5f33b66a902a4 + define-properties: ^1.2.0 + es-abstract: ^1.22.1 + checksum: f3e4ae4f21eb1cc7cebb6ce036d4c67b36e1c750428d7b7623c56a0db90edced63d08af8a316d81dfb7c41a3a5fa81b05b7cc9426e98d7da986b1682460f0777 languageName: node linkType: hard @@ -11783,15 +11956,6 @@ __metadata: languageName: node linkType: hard -"onetime@npm:^2.0.0": - version: 2.0.1 - resolution: "onetime@npm:2.0.1" - dependencies: - mimic-fn: ^1.0.0 - checksum: bb44015ac7a525d0fb43b029a583d4ad359834632b4424ca209b438aacf6d669dda81b5edfbdb42c22636e607b276ba5589f46694a729e3bc27948ce26f4cc1a - languageName: node - linkType: hard - "onetime@npm:^5.1.0": version: 5.1.2 resolution: "onetime@npm:5.1.2" @@ -11820,7 +11984,7 @@ __metadata: languageName: node linkType: hard -"optionator@npm:^0.8.1, optionator@npm:^0.8.2, optionator@npm:^0.8.3": +"optionator@npm:^0.8.1, optionator@npm:^0.8.3": version: 0.8.3 resolution: "optionator@npm:0.8.3" dependencies: @@ -11834,17 +11998,17 @@ __metadata: languageName: node linkType: hard -"optionator@npm:^0.9.1": - version: 0.9.1 - resolution: "optionator@npm:0.9.1" +"optionator@npm:^0.9.3": + version: 0.9.3 + resolution: "optionator@npm:0.9.3" dependencies: + "@aashutoshrathi/word-wrap": ^1.2.3 deep-is: ^0.1.3 fast-levenshtein: ^2.0.6 levn: ^0.4.1 prelude-ls: ^1.2.1 type-check: ^0.4.0 - word-wrap: ^1.2.3 - checksum: dbc6fa065604b24ea57d734261914e697bd73b69eff7f18e967e8912aa2a40a19a9f599a507fa805be6c13c24c4eae8c71306c239d517d42d4c041c942f508a0 + checksum: 09281999441f2fe9c33a5eeab76700795365a061563d66b098923eb719251a42bdbe432790d35064d0816ead9296dbeb1ad51a733edf4167c96bd5d0882e428a languageName: node linkType: hard @@ -11905,13 +12069,6 @@ __metadata: languageName: node linkType: hard -"p-cancelable@npm:^0.3.0": - version: 0.3.0 - resolution: "p-cancelable@npm:0.3.0" - checksum: 2b27639be8f7f8718f2854c1711f713c296db00acc4675975b1531ecb6253da197304b4a211a330a8e54e754d28d4b3f7feecb48f0566dd265e3ba6745cd4148 - languageName: node - linkType: hard - "p-cancelable@npm:^1.0.0": version: 1.1.0 resolution: "p-cancelable@npm:1.1.0" @@ -11919,6 +12076,13 @@ __metadata: languageName: node linkType: hard +"p-cancelable@npm:^2.0.0": + version: 2.1.1 + resolution: "p-cancelable@npm:2.1.1" + checksum: 3dba12b4fb4a1e3e34524535c7858fc82381bbbd0f247cc32dedc4018592a3950ce66b106d0880b4ec4c2d8d6576f98ca885dc1d7d0f274d1370be20e9523ddf + languageName: node + linkType: hard + "p-defer@npm:^1.0.0": version: 1.0.0 resolution: "p-defer@npm:1.0.0" @@ -12027,15 +12191,6 @@ __metadata: languageName: node linkType: hard -"p-timeout@npm:^1.1.1": - version: 1.2.1 - resolution: "p-timeout@npm:1.2.1" - dependencies: - p-finally: ^1.0.0 - checksum: 65a456f49cca1328774a6bfba61aac98d854b36df9153c2887f82f078d4399e9a30463be8a479871c22ed350a23b34a66ff303ca652b9d81ed4ff5260ac660d2 - languageName: node - linkType: hard - "p-try@npm:^1.0.0": version: 1.0.0 resolution: "p-try@npm:1.0.0" @@ -12059,7 +12214,7 @@ __metadata: languageName: node linkType: hard -"parse-asn1@npm:^5.0.0, parse-asn1@npm:^5.1.5": +"parse-asn1@npm:^5.0.0, parse-asn1@npm:^5.1.6": version: 5.1.6 resolution: "parse-asn1@npm:5.1.6" dependencies: @@ -12102,17 +12257,7 @@ __metadata: languageName: node linkType: hard -"parse-json@npm:^4.0.0": - version: 4.0.0 - resolution: "parse-json@npm:4.0.0" - dependencies: - error-ex: ^1.3.1 - json-parse-better-errors: ^1.0.1 - checksum: 0fe227d410a61090c247e34fa210552b834613c006c2c64d9a05cfe9e89cf8b4246d1246b1a99524b53b313e9ac024438d0680f67e33eaed7e6f38db64cfe7b5 - languageName: node - linkType: hard - -"parse-json@npm:^5.0.0": +"parse-json@npm:^5.0.0, parse-json@npm:^5.2.0": version: 5.2.0 resolution: "parse-json@npm:5.2.0" dependencies: @@ -12161,25 +12306,26 @@ __metadata: linkType: hard "patch-package@npm:^6.2.2": - version: 6.4.7 - resolution: "patch-package@npm:6.4.7" + version: 6.5.1 + resolution: "patch-package@npm:6.5.1" dependencies: "@yarnpkg/lockfile": ^1.1.0 - chalk: ^2.4.2 + chalk: ^4.1.2 cross-spawn: ^6.0.5 find-yarn-workspace-root: ^2.0.0 - fs-extra: ^7.0.1 + fs-extra: ^9.0.0 is-ci: ^2.0.0 klaw-sync: ^6.0.0 - minimist: ^1.2.0 + minimist: ^1.2.6 open: ^7.4.2 rimraf: ^2.6.3 semver: ^5.6.0 slash: ^2.0.0 tmp: ^0.0.33 + yaml: ^1.10.2 bin: patch-package: index.js - checksum: f36d5324da3b69ee635e7cd2c68f4d3dd89dc91d60ffdaad3b602fd953277f4da901c91033683bf6ff31c14799bc049849af3a389455c25d0435fe9cfb0d4088 + checksum: 8530ffa30f11136b527c6eddf6da48fa12856ee510a47edb1f9cdf8a025636adb82968f5fae778b5e04ce8c87915ebdf5911422b54add59a5a42e372a8f30eb2 languageName: node linkType: hard @@ -12220,13 +12366,6 @@ __metadata: languageName: node linkType: hard -"path-is-inside@npm:^1.0.2": - version: 1.0.2 - resolution: "path-is-inside@npm:1.0.2" - checksum: 0b5b6c92d3018b82afb1f74fe6de6338c4c654de4a96123cb343f2b747d5606590ac0c890f956ed38220a4ab59baddfd7b713d78a62d240b20b14ab801fa02cb - languageName: node - linkType: hard - "path-key@npm:^2.0.0, path-key@npm:^2.0.1": version: 2.0.1 resolution: "path-key@npm:2.0.1" @@ -12248,6 +12387,16 @@ __metadata: languageName: node linkType: hard +"path-scurry@npm:^1.10.1": + version: 1.10.1 + resolution: "path-scurry@npm:1.10.1" + dependencies: + lru-cache: ^9.1.1 || ^10.0.0 + minipass: ^5.0.0 || ^6.0.2 || ^7.0.0 + checksum: e2557cff3a8fb8bc07afdd6ab163a92587884f9969b05bbbaf6fe7379348bfb09af9ed292af12ed32398b15fb443e81692047b786d1eeb6d898a51eb17ed7d90 + languageName: node + linkType: hard + "path-to-regexp@npm:0.1.7": version: 0.1.7 resolution: "path-to-regexp@npm:0.1.7" @@ -12386,13 +12535,6 @@ __metadata: languageName: node linkType: hard -"prepend-http@npm:^1.0.1": - version: 1.0.4 - resolution: "prepend-http@npm:1.0.4" - checksum: 01e7baf4ad38af02257b99098543469332fc42ae50df33d97a124bf8172295907352fa6138c9b1610c10c6dd0847ca736e53fda736387cc5cf8fcffe96b47f29 - languageName: node - linkType: hard - "prepend-http@npm:^2.0.0": version: 2.0.0 resolution: "prepend-http@npm:2.0.0" @@ -12410,22 +12552,19 @@ __metadata: linkType: hard "prettier-plugin-solidity@npm:^1.0.0-dev.23": - version: 1.0.0-dev.23 - resolution: "prettier-plugin-solidity@npm:1.0.0-dev.23" + version: 1.3.1 + resolution: "prettier-plugin-solidity@npm:1.3.1" dependencies: - "@solidity-parser/parser": ^0.14.3 - emoji-regex: ^10.1.0 - escape-string-regexp: ^4.0.0 - semver: ^7.3.7 - solidity-comments-extractor: ^0.0.7 - string-width: ^4.2.3 + "@solidity-parser/parser": ^0.17.0 + semver: ^7.5.4 + solidity-comments-extractor: ^0.0.8 peerDependencies: - prettier: ^2.3.0 - checksum: bf068bcd763b2c2be48859abca5f2aee9543c38e875039470c8c0a850de4218292c7ec46fb63ef6866f51e8c87e80f8de015446ebd9f407712b26a9e30c41050 + prettier: ">=2.3.0" + checksum: 286bf3b5899d7fad66e49c78ebac164bacfbf419f874a932ed99e491d97d77e91fa03ca068197939d3696ba7991db9e5258390dd42dee8d2184fa8c2e11921e4 languageName: node linkType: hard -"prettier@npm:^1.13.5, prettier@npm:^1.14.3, prettier@npm:^1.18.2": +"prettier@npm:^1.13.5, prettier@npm:^1.18.2": version: 1.19.1 resolution: "prettier@npm:1.19.1" bin: @@ -12434,12 +12573,12 @@ __metadata: languageName: node linkType: hard -"prettier@npm:^2.1.2, prettier@npm:^2.3.1, prettier@npm:^2.7.1": - version: 2.7.1 - resolution: "prettier@npm:2.7.1" +"prettier@npm:^2.1.2, prettier@npm:^2.3.1, prettier@npm:^2.7.1, prettier@npm:^2.8.3": + version: 2.8.8 + resolution: "prettier@npm:2.8.8" bin: prettier: bin-prettier.js - checksum: 55a4409182260866ab31284d929b3cb961e5fdb91fe0d2e099dac92eaecec890f36e524b4c19e6ceae839c99c6d7195817579cdffc8e2c80da0cb794463a748b + checksum: b49e409431bf129dd89238d64299ba80717b57ff5a6d1c1a8b1a28b590d998a34e083fa13573bc732bb8d2305becb4c9a4407f8486c81fa7d55100eb08263cf8 languageName: node linkType: hard @@ -12450,6 +12589,13 @@ __metadata: languageName: node linkType: hard +"proc-log@npm:^3.0.0": + version: 3.0.0 + resolution: "proc-log@npm:3.0.0" + checksum: 02b64e1b3919e63df06f836b98d3af002b5cd92655cab18b5746e37374bfb73e03b84fe305454614b34c25b485cc687a9eebdccf0242cda8fda2475dd2c97e02 + languageName: node + linkType: hard + "process-nextick-args@npm:~2.0.0": version: 2.0.1 resolution: "process-nextick-args@npm:2.0.1" @@ -12471,13 +12617,6 @@ __metadata: languageName: node linkType: hard -"promise-inflight@npm:^1.0.1": - version: 1.0.1 - resolution: "promise-inflight@npm:1.0.1" - checksum: 22749483091d2c594261517f4f80e05226d4d5ecc1fc917e1886929da56e22b5718b7f2a75f3807e7a7d471bc3be2907fe92e6e8f373ddf5c64bae35b5af3981 - languageName: node - linkType: hard - "promise-retry@npm:^2.0.1": version: 2.0.1 resolution: "promise-retry@npm:2.0.1" @@ -12499,11 +12638,11 @@ __metadata: linkType: hard "promise@npm:^8.0.0": - version: 8.1.0 - resolution: "promise@npm:8.1.0" + version: 8.3.0 + resolution: "promise@npm:8.3.0" dependencies: asap: ~2.0.6 - checksum: 89b71a56154ed7d66a73236d8e8351a9c59adddba3929ecc845f75421ff37fc08ea0c67ad76cd5c0b0d81812c7d07a32bed27e7df5fcc960c6d68b0c1cd771f7 + checksum: a69f0ddbddf78ffc529cffee7ad950d307347615970564b17988ce43fbe767af5c738a9439660b24a9a8cbea106c0dcbb6c2b20e23b7e96a8e89e5c2679e94d5 languageName: node linkType: hard @@ -12552,6 +12691,13 @@ __metadata: languageName: node linkType: hard +"proxy-from-env@npm:^1.1.0": + version: 1.1.0 + resolution: "proxy-from-env@npm:1.1.0" + checksum: ed7fcc2ba0a33404958e34d95d18638249a68c430e30fcb6c478497d72739ba64ce9810a24f53a7d921d0c065e5b78e3822759800698167256b04659366ca4d4 + languageName: node + linkType: hard + "prr@npm:~1.0.1": version: 1.0.1 resolution: "prr@npm:1.0.1" @@ -12567,9 +12713,9 @@ __metadata: linkType: hard "psl@npm:^1.1.28": - version: 1.8.0 - resolution: "psl@npm:1.8.0" - checksum: 6150048ed2da3f919478bee8a82f3828303bc0fc730fb015a48f83c9977682c7b28c60ab01425a72d82a2891a1681627aa530a991d50c086b48a3be27744bde7 + version: 1.9.0 + resolution: "psl@npm:1.9.0" + checksum: 20c4277f640c93d393130673f392618e9a8044c6c7bf61c53917a0fddb4952790f5f362c6c730a9c32b124813e173733f9895add8d26f566ed0ea0654b2e711d languageName: node linkType: hard @@ -12634,9 +12780,9 @@ __metadata: linkType: hard "pull-stream@npm:^3.2.3, pull-stream@npm:^3.4.0, pull-stream@npm:^3.6.8": - version: 3.6.14 - resolution: "pull-stream@npm:3.6.14" - checksum: fc3d86d488894cdf1f980848886be54d8c9cf16a982e9f651098e673bf0134dd1be9b02435f59afe5b48d479c6bafb828348f7fac95fd4593633bffefdfb7503 + version: 3.7.0 + resolution: "pull-stream@npm:3.7.0" + checksum: df0b864fd92bb61e84d02764a064bf023188c1c917d854029a5b8e543e163f9aaf1a9553067d4fdf5e248b0d96338e0a23fac9257e86cf740e7d03e05b7a77a3 languageName: node linkType: hard @@ -12669,13 +12815,6 @@ __metadata: languageName: node linkType: hard -"punycode@npm:1.3.2": - version: 1.3.2 - resolution: "punycode@npm:1.3.2" - checksum: b8807fd594b1db33335692d1f03e8beeddde6fda7fbb4a2e32925d88d20a3aa4cd8dcc0c109ccaccbd2ba761c208dfaaada83007087ea8bfb0129c9ef1b99ed6 - languageName: node - linkType: hard - "punycode@npm:2.1.0": version: 2.1.0 resolution: "punycode@npm:2.1.0" @@ -12683,23 +12822,21 @@ __metadata: languageName: node linkType: hard -"punycode@npm:^2.1.0, punycode@npm:^2.1.1": - version: 2.1.1 - resolution: "punycode@npm:2.1.1" - checksum: 823bf443c6dd14f669984dea25757b37993f67e8d94698996064035edd43bed8a5a17a9f12e439c2b35df1078c6bec05a6c86e336209eb1061e8025c481168e8 +"punycode@npm:^1.4.1": + version: 1.4.1 + resolution: "punycode@npm:1.4.1" + checksum: fa6e698cb53db45e4628559e557ddaf554103d2a96a1d62892c8f4032cd3bc8871796cae9eabc1bc700e2b6677611521ce5bb1d9a27700086039965d0cf34518 languageName: node linkType: hard -"qs@npm:6.10.3": - version: 6.10.3 - resolution: "qs@npm:6.10.3" - dependencies: - side-channel: ^1.0.4 - checksum: 0fac5e6c7191d0295a96d0e83c851aeb015df7e990e4d3b093897d3ac6c94e555dbd0a599739c84d7fa46d7fee282d94ba76943983935cf33bba6769539b8019 +"punycode@npm:^2.1.0, punycode@npm:^2.1.1": + version: 2.3.1 + resolution: "punycode@npm:2.3.1" + checksum: bb0a0ceedca4c3c57a9b981b90601579058903c62be23c5e8e843d2c2d4148a3ecf029d5133486fb0e1822b098ba8bba09e89d6b21742d02fa26bda6441a6fb2 languageName: node linkType: hard -"qs@npm:^6.4.0, qs@npm:^6.7.0": +"qs@npm:6.11.0": version: 6.11.0 resolution: "qs@npm:6.11.0" dependencies: @@ -12708,6 +12845,15 @@ __metadata: languageName: node linkType: hard +"qs@npm:^6.11.2, qs@npm:^6.4.0": + version: 6.11.2 + resolution: "qs@npm:6.11.2" + dependencies: + side-channel: ^1.0.4 + checksum: e812f3c590b2262548647d62f1637b6989cc56656dc960b893fe2098d96e1bd633f36576f4cd7564dfbff9db42e17775884db96d846bebe4f37420d073ecdc0b + languageName: node + linkType: hard + "qs@npm:~6.5.2": version: 6.5.3 resolution: "qs@npm:6.5.3" @@ -12726,13 +12872,6 @@ __metadata: languageName: node linkType: hard -"querystring@npm:0.2.0": - version: 0.2.0 - resolution: "querystring@npm:0.2.0" - checksum: 8258d6734f19be27e93f601758858c299bdebe71147909e367101ba459b95446fbe5b975bf9beb76390156a592b6f4ac3a68b6087cea165c259705b8b4e56a69 - languageName: node - linkType: hard - "queue-microtask@npm:^1.2.2, queue-microtask@npm:^1.2.3": version: 1.2.3 resolution: "queue-microtask@npm:1.2.3" @@ -12740,6 +12879,13 @@ __metadata: languageName: node linkType: hard +"quick-lru@npm:^5.1.1": + version: 5.1.1 + resolution: "quick-lru@npm:5.1.1" + checksum: a516faa25574be7947969883e6068dbe4aa19e8ef8e8e0fd96cddd6d36485e9106d85c0041a27153286b0770b381328f4072aa40d3b18a19f5f7d2b78b94b5ed + languageName: node + linkType: hard + "randombytes@npm:^2.0.0, randombytes@npm:^2.0.1, randombytes@npm:^2.0.5, randombytes@npm:^2.0.6, randombytes@npm:^2.1.0": version: 2.1.0 resolution: "randombytes@npm:2.1.0" @@ -12766,7 +12912,7 @@ __metadata: languageName: node linkType: hard -"raw-body@npm:2.5.1, raw-body@npm:^2.4.1": +"raw-body@npm:2.5.1": version: 2.5.1 resolution: "raw-body@npm:2.5.1" dependencies: @@ -12778,6 +12924,18 @@ __metadata: languageName: node linkType: hard +"raw-body@npm:2.5.2, raw-body@npm:^2.4.1": + version: 2.5.2 + resolution: "raw-body@npm:2.5.2" + dependencies: + bytes: 3.1.2 + http-errors: 2.0.0 + iconv-lite: 0.4.24 + unpipe: 1.0.0 + checksum: ba1583c8d8a48e8fbb7a873fdbb2df66ea4ff83775421bfe21ee120140949ab048200668c47d9ae3880012f6e217052690628cf679ddfbd82c9fc9358d574676 + languageName: node + linkType: hard + "react-native-fetch-api@npm:^3.0.0": version: 3.0.0 resolution: "react-native-fetch-api@npm:3.0.0" @@ -12820,22 +12978,7 @@ __metadata: languageName: node linkType: hard -"readable-stream@npm:^2.0.0, readable-stream@npm:^2.0.5, readable-stream@npm:^2.2.2, readable-stream@npm:^2.2.8, readable-stream@npm:^2.2.9, readable-stream@npm:^2.3.6, readable-stream@npm:~2.3.6": - version: 2.3.7 - resolution: "readable-stream@npm:2.3.7" - dependencies: - core-util-is: ~1.0.0 - inherits: ~2.0.3 - isarray: ~1.0.0 - process-nextick-args: ~2.0.0 - safe-buffer: ~5.1.1 - string_decoder: ~1.1.1 - util-deprecate: ~1.0.1 - checksum: e4920cf7549a60f8aaf694d483a0e61b2a878b969d224f89b3bc788b8d920075132c4b55a7494ee944c7b6a9a0eada28a7f6220d80b0312ece70bbf08eeca755 - languageName: node - linkType: hard - -"readable-stream@npm:^2.3.0, readable-stream@npm:^2.3.5": +"readable-stream@npm:^2.0.0, readable-stream@npm:^2.0.5, readable-stream@npm:^2.2.2, readable-stream@npm:^2.2.8, readable-stream@npm:^2.2.9, readable-stream@npm:^2.3.0, readable-stream@npm:^2.3.5, readable-stream@npm:^2.3.6, readable-stream@npm:~2.3.6": version: 2.3.8 resolution: "readable-stream@npm:2.3.8" dependencies: @@ -12850,14 +12993,14 @@ __metadata: languageName: node linkType: hard -"readable-stream@npm:^3.0.6, readable-stream@npm:^3.6.0": - version: 3.6.0 - resolution: "readable-stream@npm:3.6.0" +"readable-stream@npm:^3.0.6, readable-stream@npm:^3.6.0, readable-stream@npm:^3.6.2": + version: 3.6.2 + resolution: "readable-stream@npm:3.6.2" dependencies: inherits: ^2.0.3 string_decoder: ^1.1.1 util-deprecate: ^1.0.1 - checksum: d4ea81502d3799439bb955a3a5d1d808592cf3133350ed352aeaa499647858b27b1c4013984900238b0873ec8d0d8defce72469fb7a83e61d53f5ad61cb80dc8 + checksum: bdcbe6c22e846b6af075e32cf8f4751c2576238c5043169a1c221c92ee2878458a816a4ea33f4c67623c0b6827c8a400409bfb3cf0bf3381392d0b1dfb52ac8d languageName: node linkType: hard @@ -12873,15 +13016,6 @@ __metadata: languageName: node linkType: hard -"readdirp@npm:~3.2.0": - version: 3.2.0 - resolution: "readdirp@npm:3.2.0" - dependencies: - picomatch: ^2.0.4 - checksum: 0456a4465a13eb5eaf40f0e0836b1bc6b9ebe479b48ba6f63a738b127a1990fb7b38f3ec4b4b6052f9230f976bc0558f12812347dc6b42ce4d548cfe82a9b6f3 - languageName: node - linkType: hard - "readdirp@npm:~3.6.0": version: 3.6.0 resolution: "readdirp@npm:3.6.0" @@ -12910,11 +13044,11 @@ __metadata: linkType: hard "recursive-readdir@npm:^2.2.2": - version: 2.2.2 - resolution: "recursive-readdir@npm:2.2.2" + version: 2.2.3 + resolution: "recursive-readdir@npm:2.2.3" dependencies: - minimatch: 3.0.4 - checksum: a6b22994d76458443d4a27f5fd7147ac63ad31bba972666a291d511d4d819ee40ff71ba7524c14f6a565b8cfaf7f48b318f971804b913cf538d58f04e25d1fee + minimatch: ^3.0.5 + checksum: 88ec96e276237290607edc0872b4f9842837b95cfde0cdbb1e00ba9623dfdf3514d44cdd14496ab60a0c2dd180a6ef8a3f1c34599e6cf2273afac9b72a6fb2b5 languageName: node linkType: hard @@ -12960,14 +13094,14 @@ __metadata: languageName: node linkType: hard -"regexp.prototype.flags@npm:^1.2.0, regexp.prototype.flags@npm:^1.4.3": - version: 1.4.3 - resolution: "regexp.prototype.flags@npm:1.4.3" +"regexp.prototype.flags@npm:^1.5.1": + version: 1.5.1 + resolution: "regexp.prototype.flags@npm:1.5.1" dependencies: call-bind: ^1.0.2 - define-properties: ^1.1.3 - functions-have-names: ^1.2.2 - checksum: 51228bae732592adb3ededd5e15426be25f289e9c4ef15212f4da73f4ec3919b6140806374b8894036a86020d054a8d2657d3fee6bb9b4d35d8939c20030b7a6 + define-properties: ^1.2.0 + set-function-name: ^2.0.0 + checksum: 869edff00288442f8d7fa4c9327f91d85f3b3acf8cbbef9ea7a220345cf23e9241b6def9263d2c1ebcf3a316b0aa52ad26a43a84aa02baca3381717b3e307f47 languageName: node linkType: hard @@ -12978,7 +13112,7 @@ __metadata: languageName: node linkType: hard -"regexpp@npm:^3.0.0, regexpp@npm:^3.2.0": +"regexpp@npm:^3.0.0": version: 3.2.0 resolution: "regexpp@npm:3.2.0" checksum: a78dc5c7158ad9ddcfe01aa9144f46e192ddbfa7b263895a70a5c6c73edd9ce85faf7c0430e59ac38839e1734e275b9c3de5c57ee3ab6edc0e0b1bdebefccef8 @@ -13055,30 +13189,6 @@ __metadata: languageName: node linkType: hard -"request-promise-core@npm:1.1.4": - version: 1.1.4 - resolution: "request-promise-core@npm:1.1.4" - dependencies: - lodash: ^4.17.19 - peerDependencies: - request: ^2.34 - checksum: c798bafd552961e36fbf5023b1d081e81c3995ab390f1bc8ef38a711ba3fe4312eb94dbd61887073d7356c3499b9380947d7f62faa805797c0dc50f039425699 - languageName: node - linkType: hard - -"request-promise-native@npm:^1.0.5": - version: 1.0.9 - resolution: "request-promise-native@npm:1.0.9" - dependencies: - request-promise-core: 1.1.4 - stealthy-require: ^1.1.1 - tough-cookie: ^2.3.3 - peerDependencies: - request: ^2.34 - checksum: 3e2c694eefac88cb20beef8911ad57a275ab3ccbae0c4ca6c679fffb09d5fd502458aab08791f0814ca914b157adab2d4e472597c97a73be702918e41725ed69 - languageName: node - linkType: hard - "request@npm:^2.79.0, request@npm:^2.85.0, request@npm:^2.88.0": version: 2.88.2 resolution: "request@npm:2.88.2" @@ -13142,6 +13252,13 @@ __metadata: languageName: node linkType: hard +"resolve-alpn@npm:^1.0.0": + version: 1.2.1 + resolution: "resolve-alpn@npm:1.2.1" + checksum: f558071fcb2c60b04054c99aebd572a2af97ef64128d59bef7ab73bd50d896a222a056de40ffc545b633d99b304c259ea9d0c06830d5c867c34f0bfa60b8eae0 + languageName: node + linkType: hard + "resolve-from@npm:^3.0.0": version: 3.0.0 resolution: "resolve-from@npm:3.0.0" @@ -13179,16 +13296,16 @@ __metadata: languageName: node linkType: hard -"resolve@npm:^1.1.6, resolve@npm:^1.10.0, resolve@npm:^1.10.1, resolve@npm:^1.20.0, resolve@npm:^1.22.0, resolve@npm:^1.8.1, resolve@npm:~1.22.0": - version: 1.22.1 - resolution: "resolve@npm:1.22.1" +"resolve@npm:^1.1.6, resolve@npm:^1.10.0, resolve@npm:^1.10.1, resolve@npm:^1.22.1, resolve@npm:^1.22.4, resolve@npm:^1.8.1, resolve@npm:~1.22.6": + version: 1.22.8 + resolution: "resolve@npm:1.22.8" dependencies: - is-core-module: ^2.9.0 + is-core-module: ^2.13.0 path-parse: ^1.0.7 supports-preserve-symlinks-flag: ^1.0.0 bin: resolve: bin/resolve - checksum: 07af5fc1e81aa1d866cbc9e9460fbb67318a10fa3c4deadc35c3ad8a898ee9a71a86a65e4755ac3195e0ea0cfbe201eb323ebe655ce90526fd61917313a34e4e + checksum: f8a26958aa572c9b064562750b52131a37c29d072478ea32e129063e2da7f83e31f7f11e7087a18225a8561cfe8d2f0df9dbea7c9d331a897571c0a2527dbb4c languageName: node linkType: hard @@ -13208,16 +13325,16 @@ __metadata: languageName: node linkType: hard -"resolve@patch:resolve@^1.1.6#~builtin, resolve@patch:resolve@^1.10.0#~builtin, resolve@patch:resolve@^1.10.1#~builtin, resolve@patch:resolve@^1.20.0#~builtin, resolve@patch:resolve@^1.22.0#~builtin, resolve@patch:resolve@^1.8.1#~builtin, resolve@patch:resolve@~1.22.0#~builtin": - version: 1.22.1 - resolution: "resolve@patch:resolve@npm%3A1.22.1#~builtin::version=1.22.1&hash=07638b" +"resolve@patch:resolve@^1.1.6#~builtin, resolve@patch:resolve@^1.10.0#~builtin, resolve@patch:resolve@^1.10.1#~builtin, resolve@patch:resolve@^1.22.1#~builtin, resolve@patch:resolve@^1.22.4#~builtin, resolve@patch:resolve@^1.8.1#~builtin, resolve@patch:resolve@~1.22.6#~builtin": + version: 1.22.8 + resolution: "resolve@patch:resolve@npm%3A1.22.8#~builtin::version=1.22.8&hash=07638b" dependencies: - is-core-module: ^2.9.0 + is-core-module: ^2.13.0 path-parse: ^1.0.7 supports-preserve-symlinks-flag: ^1.0.0 bin: resolve: bin/resolve - checksum: 5656f4d0bedcf8eb52685c1abdf8fbe73a1603bb1160a24d716e27a57f6cecbe2432ff9c89c2bd57542c3a7b9d14b1882b73bfe2e9d7849c9a4c0b8b39f02b8b + checksum: 5479b7d431cacd5185f8db64bfcb7286ae5e31eb299f4c4f404ad8aa6098b77599563ac4257cb2c37a42f59dfc06a1bec2bcf283bb448f319e37f0feb9a09847 languageName: node linkType: hard @@ -13230,13 +13347,12 @@ __metadata: languageName: node linkType: hard -"restore-cursor@npm:^2.0.0": - version: 2.0.0 - resolution: "restore-cursor@npm:2.0.0" +"responselike@npm:^2.0.0": + version: 2.0.1 + resolution: "responselike@npm:2.0.1" dependencies: - onetime: ^2.0.0 - signal-exit: ^3.0.2 - checksum: 482e13d02d834b6e5e3aa90304a8b5e840775d6f06916cc92a50038adf9f098dcc72405b567da8a37e137ae40ad3e31896fa3136ae62f7a426c2fbf53d036536 + lowercase-keys: ^2.0.0 + checksum: b122535466e9c97b55e69c7f18e2be0ce3823c5d47ee8de0d9c0b114aa55741c6db8bfbfce3766a94d1272e61bfb1ebf0a15e9310ac5629fbb7446a861b4fd3a languageName: node linkType: hard @@ -13250,15 +13366,6 @@ __metadata: languageName: node linkType: hard -"resumer@npm:~0.0.0": - version: 0.0.0 - resolution: "resumer@npm:0.0.0" - dependencies: - through: ~2.3.4 - checksum: 21b1c257aac24840643fae9bc99ca6447a71a0039e7c6dcf64d0ead447ce511eff158d529f1b6258ad12668e66ee3e49ff14932d2b88a3bd578f483e79708104 - languageName: node - linkType: hard - "ret@npm:~0.1.10": version: 0.1.15 resolution: "ret@npm:0.1.15" @@ -13273,6 +13380,13 @@ __metadata: languageName: node linkType: hard +"retry@npm:0.13.1": + version: 0.13.1 + resolution: "retry@npm:0.13.1" + checksum: 47c4d5be674f7c13eee4cfe927345023972197dbbdfba5d3af7e461d13b44de1bfd663bfc80d2f601f8ef3fc8164c16dd99655a221921954a65d044a2fc1233b + languageName: node + linkType: hard + "retry@npm:^0.12.0": version: 0.12.0 resolution: "retry@npm:0.12.0" @@ -13341,7 +13455,7 @@ __metadata: languageName: node linkType: hard -"run-async@npm:^2.2.0, run-async@npm:^2.4.0": +"run-async@npm:^2.4.0": version: 2.4.1 resolution: "run-async@npm:2.4.1" checksum: a2c88aa15df176f091a2878eb840e68d0bdee319d8d97bbb89112223259cebecb94bc0defd735662b83c2f7a30bed8cddb7d1674eb48ae7322dc602b22d03797 @@ -13373,7 +13487,7 @@ __metadata: languageName: node linkType: hard -"rxjs@npm:^6.4.0, rxjs@npm:^6.6.0": +"rxjs@npm:^6.6.0": version: 6.6.7 resolution: "rxjs@npm:6.6.7" dependencies: @@ -13383,11 +13497,23 @@ __metadata: linkType: hard "rxjs@npm:^7.2.0": - version: 7.5.7 - resolution: "rxjs@npm:7.5.7" + version: 7.8.1 + resolution: "rxjs@npm:7.8.1" dependencies: tslib: ^2.1.0 - checksum: edabcdb73b0f7e0f5f6e05c2077aff8c52222ac939069729704357d6406438acca831c24210db320aba269e86dbe1a400f3769c89101791885121a342fb15d9c + checksum: de4b53db1063e618ec2eca0f7965d9137cabe98cf6be9272efe6c86b47c17b987383df8574861bcced18ebd590764125a901d5506082be84a8b8e364bf05f119 + languageName: node + linkType: hard + +"safe-array-concat@npm:^1.0.0, safe-array-concat@npm:^1.0.1": + version: 1.1.0 + resolution: "safe-array-concat@npm:1.1.0" + dependencies: + call-bind: ^1.0.5 + get-intrinsic: ^1.2.2 + has-symbols: ^1.0.3 + isarray: ^2.0.5 + checksum: 5c71eaa999168ee7474929f1cd3aae80f486353a651a094d9968936692cf90aa065224929a6486dcda66334a27dce4250a83612f9e0fef6dced1a925d3ac7296 languageName: node linkType: hard @@ -13414,6 +13540,17 @@ __metadata: languageName: node linkType: hard +"safe-regex-test@npm:^1.0.0": + version: 1.0.2 + resolution: "safe-regex-test@npm:1.0.2" + dependencies: + call-bind: ^1.0.5 + get-intrinsic: ^1.2.2 + is-regex: ^1.1.4 + checksum: 4af5ce05a2daa4f6d4bfd5a3c64fc33d6b886f6592122e93c0efad52f7147b9b605e5ffc03c269a1e3d1f8db2a23bc636628a961c9fd65bafdc09503330673fd + languageName: node + linkType: hard + "safe-regex@npm:^1.1.0": version: 1.1.0 resolution: "safe-regex@npm:1.1.0" @@ -13454,13 +13591,6 @@ __metadata: languageName: node linkType: hard -"scrypt-js@npm:2.0.4": - version: 2.0.4 - resolution: "scrypt-js@npm:2.0.4" - checksum: 679e8940953ebbef40863bfcc58f1d3058d4b7af0ca9bd8062d8213c30e14db59c6ebfc82a85fbd3b90b6d46b708be4c53b9c4bb200b6f50767dc08a846315a9 - languageName: node - linkType: hard - "scrypt-js@npm:3.0.1, scrypt-js@npm:^3.0.0, scrypt-js@npm:^3.0.1": version: 3.0.1 resolution: "scrypt-js@npm:3.0.1" @@ -13503,25 +13633,16 @@ __metadata: languageName: node linkType: hard -"semver@npm:2 || 3 || 4 || 5, semver@npm:^5.3.0, semver@npm:^5.5.0, semver@npm:^5.5.1, semver@npm:^5.6.0, semver@npm:^5.7.0": - version: 5.7.1 - resolution: "semver@npm:5.7.1" - bin: - semver: ./bin/semver - checksum: 57fd0acfd0bac382ee87cd52cd0aaa5af086a7dc8d60379dfe65fea491fb2489b6016400813930ecd61fd0952dae75c115287a1b16c234b1550887117744dfaf - languageName: node - linkType: hard - -"semver@npm:^6.1.0, semver@npm:^6.3.0": - version: 6.3.0 - resolution: "semver@npm:6.3.0" +"semver@npm:2 || 3 || 4 || 5, semver@npm:^5.3.0, semver@npm:^5.5.0, semver@npm:^5.6.0": + version: 5.7.2 + resolution: "semver@npm:5.7.2" bin: - semver: ./bin/semver.js - checksum: 1b26ecf6db9e8292dd90df4e781d91875c0dcc1b1909e70f5d12959a23c7eebb8f01ea581c00783bbee72ceeaad9505797c381756326073850dc36ed284b21b9 + semver: bin/semver + checksum: fb4ab5e0dd1c22ce0c937ea390b4a822147a9c53dbd2a9a0132f12fe382902beef4fbf12cf51bb955248d8d15874ce8cd89532569756384f994309825f10b686 languageName: node linkType: hard -"semver@npm:^6.1.2": +"semver@npm:^6.1.0, semver@npm:^6.1.2, semver@npm:^6.3.0, semver@npm:^6.3.1": version: 6.3.1 resolution: "semver@npm:6.3.1" bin: @@ -13530,18 +13651,7 @@ __metadata: languageName: node linkType: hard -"semver@npm:^7.0.0, semver@npm:^7.3.4, semver@npm:^7.3.5, semver@npm:^7.3.7": - version: 7.3.7 - resolution: "semver@npm:7.3.7" - dependencies: - lru-cache: ^6.0.0 - bin: - semver: bin/semver.js - checksum: 2fa3e877568cd6ce769c75c211beaed1f9fce80b28338cadd9d0b6c40f2e2862bafd62c19a6cff42f3d54292b7c623277bcab8816a2b5521cf15210d43e75232 - languageName: node - linkType: hard - -"semver@npm:^7.3.2": +"semver@npm:^7.0.0, semver@npm:^7.3.2, semver@npm:^7.3.4, semver@npm:^7.3.5, semver@npm:^7.3.7, semver@npm:^7.3.8, semver@npm:^7.5.2, semver@npm:^7.5.4": version: 7.5.4 resolution: "semver@npm:7.5.4" dependencies: @@ -13623,6 +13733,30 @@ __metadata: languageName: node linkType: hard +"set-function-length@npm:^1.1.1": + version: 1.2.0 + resolution: "set-function-length@npm:1.2.0" + dependencies: + define-data-property: ^1.1.1 + function-bind: ^1.1.2 + get-intrinsic: ^1.2.2 + gopd: ^1.0.1 + has-property-descriptors: ^1.0.1 + checksum: 63e34b45a2ff9abb419f52583481bf8ba597d33c0c85e56999085eb6078a0f7fbb4222051981c287feceeb358aa7789e7803cea2c82ac94c0ab37059596aff79 + languageName: node + linkType: hard + +"set-function-name@npm:^2.0.0": + version: 2.0.1 + resolution: "set-function-name@npm:2.0.1" + dependencies: + define-data-property: ^1.0.1 + functions-have-names: ^1.2.3 + has-property-descriptors: ^1.0.0 + checksum: 4975d17d90c40168eee2c7c9c59d023429f0a1690a89d75656306481ece0c3c1fb1ebcc0150ea546d1913e35fbd037bace91372c69e543e51fc5d1f31a9fa126 + languageName: node + linkType: hard + "set-immediate-shim@npm:^1.0.1": version: 1.0.1 resolution: "set-immediate-shim@npm:1.0.1" @@ -13642,13 +13776,6 @@ __metadata: languageName: node linkType: hard -"setimmediate@npm:1.0.4": - version: 1.0.4 - resolution: "setimmediate@npm:1.0.4" - checksum: 1d3726183ade73fa1c83bd562b05ae34e97802229d5b9292cde7ed03846524f04eb0fdd2131cc159103e3a7afb7c4e958b35bf960e3c4846fa50d94a3278be6f - languageName: node - linkType: hard - "setimmediate@npm:^1.0.5": version: 1.0.5 resolution: "setimmediate@npm:1.0.5" @@ -13741,13 +13868,20 @@ __metadata: languageName: node linkType: hard -"signal-exit@npm:^3.0.0, signal-exit@npm:^3.0.2, signal-exit@npm:^3.0.7": +"signal-exit@npm:^3.0.0, signal-exit@npm:^3.0.2": version: 3.0.7 resolution: "signal-exit@npm:3.0.7" checksum: a2f098f247adc367dffc27845853e9959b9e88b01cb301658cfe4194352d8d2bb32e18467c786a7fe15f1d44b233ea35633d076d5e737870b7139949d1ab6318 languageName: node linkType: hard +"signal-exit@npm:^4.0.1": + version: 4.1.0 + resolution: "signal-exit@npm:4.1.0" + checksum: 64c757b498cb8629ffa5f75485340594d2f8189e9b08700e69199069c8e3070fb3e255f7ab873c05dc0b3cec412aea7402e10a5990cb6a050bd33ba062a6c549 + languageName: node + linkType: hard + "simple-concat@npm:^1.0.0": version: 1.0.1 resolution: "simple-concat@npm:1.0.1" @@ -13852,24 +13986,24 @@ __metadata: languageName: node linkType: hard -"socks-proxy-agent@npm:^7.0.0": - version: 7.0.0 - resolution: "socks-proxy-agent@npm:7.0.0" +"socks-proxy-agent@npm:^8.0.1": + version: 8.0.2 + resolution: "socks-proxy-agent@npm:8.0.2" dependencies: - agent-base: ^6.0.2 - debug: ^4.3.3 - socks: ^2.6.2 - checksum: 720554370154cbc979e2e9ce6a6ec6ced205d02757d8f5d93fe95adae454fc187a5cbfc6b022afab850a5ce9b4c7d73e0f98e381879cf45f66317a4895953846 + agent-base: ^7.0.2 + debug: ^4.3.4 + socks: ^2.7.1 + checksum: 4fb165df08f1f380881dcd887b3cdfdc1aba3797c76c1e9f51d29048be6e494c5b06d68e7aea2e23df4572428f27a3ec22b3d7c75c570c5346507433899a4b6d languageName: node linkType: hard -"socks@npm:^2.6.2": - version: 2.6.2 - resolution: "socks@npm:2.6.2" +"socks@npm:^2.7.1": + version: 2.7.1 + resolution: "socks@npm:2.7.1" dependencies: - ip: ^1.1.5 + ip: ^2.0.0 smart-buffer: ^4.2.0 - checksum: dd9194293059d737759d5c69273850ad4149f448426249325c4bea0e340d1cf3d266c3b022694b0dcf5d31f759de23657244c481fc1e8322add80b7985c36b5e + checksum: 259d9e3e8e1c9809a7f5c32238c3d4d2a36b39b83851d0f573bfde5f21c4b1288417ce1af06af1452569cd1eb0841169afd4998f0e04ba04656f6b7f0e46d748 languageName: node linkType: hard @@ -13926,53 +14060,58 @@ __metadata: linkType: hard "solhint@npm:^3.3.7": - version: 3.3.7 - resolution: "solhint@npm:3.3.7" - dependencies: - "@solidity-parser/parser": ^0.14.1 - ajv: ^6.6.1 - antlr4: 4.7.1 - ast-parents: 0.0.1 - chalk: ^2.4.2 - commander: 2.18.0 - cosmiconfig: ^5.0.7 - eslint: ^5.6.0 - fast-diff: ^1.1.2 - glob: ^7.1.3 - ignore: ^4.0.6 - js-yaml: ^3.12.0 - lodash: ^4.17.11 - prettier: ^1.14.3 - semver: ^6.3.0 + version: 3.6.2 + resolution: "solhint@npm:3.6.2" + dependencies: + "@solidity-parser/parser": ^0.16.0 + ajv: ^6.12.6 + antlr4: ^4.11.0 + ast-parents: ^0.0.1 + chalk: ^4.1.2 + commander: ^10.0.0 + cosmiconfig: ^8.0.0 + fast-diff: ^1.2.0 + glob: ^8.0.3 + ignore: ^5.2.4 + js-yaml: ^4.1.0 + lodash: ^4.17.21 + pluralize: ^8.0.0 + prettier: ^2.8.3 + semver: ^7.5.2 + strip-ansi: ^6.0.1 + table: ^6.8.1 + text-table: ^0.2.0 dependenciesMeta: prettier: optional: true bin: solhint: solhint.js - checksum: 140a4660b691ea78aa7de19aca2123991fb4f9bc7be574e1573ae428b356e12919805df56c2892ddbdd031a4a4db477a81425ad85aac6672f3fb73f4887c2abb + checksum: 96c2ab3c1444624facb45b929682c65d83019f392c7331463a45e8ed61f08122e24b6709a721b6086ddfb0d5e3c3d4281f175f74eb308415072917556bdeba22 languageName: node linkType: hard -"solidity-ast@npm:^0.4.15": - version: 0.4.35 - resolution: "solidity-ast@npm:0.4.35" - checksum: 6cde9e656dee814fa3d7ce9ef42f1cd0344162515d0d215dbd7d18bf931ed9cd6ce4093aed0a8abbcfb5a4a6faf6638f615aaad479e4657054c6a4ae2cb5092e +"solidity-ast@npm:^0.4.51": + version: 0.4.55 + resolution: "solidity-ast@npm:0.4.55" + dependencies: + array.prototype.findlast: ^1.2.2 + checksum: a33f50b48039ca6a980eeb5d2e55a32d93c48bacbe33494faad8d50262f734cdb5c10b6d01d8bda289e702e0f9d144dd120fca1aa954c5390be8300a74a48af6 languageName: node linkType: hard -"solidity-comments-extractor@npm:^0.0.7": - version: 0.0.7 - resolution: "solidity-comments-extractor@npm:0.0.7" - checksum: a5cedf2310709969bc1783a6c336171478536f2f0ea96ad88437e0ef1e8844c0b37dd75591b0a824ec9c30640ea7e31b5f03128e871e6235bef3426617ce96c4 +"solidity-comments-extractor@npm:^0.0.8": + version: 0.0.8 + resolution: "solidity-comments-extractor@npm:0.0.8" + checksum: ad025fc968e2d744b4270710c2f7f55b43d8046ab3f155fd880a7768d6fd163a93ea98f62be3b1115a29ba815bd8b5736bb5ffd1feff79083eca1bf273108d07 languageName: node linkType: hard -"solidity-coverage@npm:^0.8.2": - version: 0.8.2 - resolution: "solidity-coverage@npm:0.8.2" +"solidity-coverage@npm:^0.8.5": + version: 0.8.6 + resolution: "solidity-coverage@npm:0.8.6" dependencies: "@ethersproject/abi": ^5.0.9 - "@solidity-parser/parser": ^0.14.1 + "@solidity-parser/parser": ^0.18.0 chalk: ^2.4.2 death: ^1.1.0 detect-port: ^1.3.0 @@ -13983,7 +14122,7 @@ __metadata: globby: ^10.0.1 jsonschema: ^1.2.4 lodash: ^4.17.15 - mocha: 7.1.2 + mocha: ^10.2.0 node-emoji: ^1.10.0 pify: ^4.0.1 recursive-readdir: ^2.2.2 @@ -13995,7 +14134,7 @@ __metadata: hardhat: ^2.11.0 bin: solidity-coverage: plugins/bin.js - checksum: 489f73d56a1279f2394b7a14db315532884895baa00a4016e68a4e5be0eddca90a95cb3322e6a0b15e67f2d9003b9413ee24c1c61d78f558f5a2e1e233840825 + checksum: 178bab95b9b41683131bf05c23ec213aca38fb65973d93a19d56ec3be3675b79e63219e2e9b1ae05ad723eb301952cc2a3c53247c676b5236262e87249a5895f languageName: node linkType: hard @@ -14072,19 +14211,19 @@ __metadata: linkType: hard "spdx-correct@npm:^3.0.0": - version: 3.1.1 - resolution: "spdx-correct@npm:3.1.1" + version: 3.2.0 + resolution: "spdx-correct@npm:3.2.0" dependencies: spdx-expression-parse: ^3.0.0 spdx-license-ids: ^3.0.0 - checksum: 77ce438344a34f9930feffa61be0eddcda5b55fc592906ef75621d4b52c07400a97084d8701557b13f7d2aae0cb64f808431f469e566ef3fe0a3a131dcb775a6 + checksum: e9ae98d22f69c88e7aff5b8778dc01c361ef635580e82d29e5c60a6533cc8f4d820803e67d7432581af0cc4fb49973125076ee3b90df191d153e223c004193b2 languageName: node linkType: hard "spdx-exceptions@npm:^2.1.0": - version: 2.3.0 - resolution: "spdx-exceptions@npm:2.3.0" - checksum: cb69a26fa3b46305637123cd37c85f75610e8c477b6476fa7354eb67c08128d159f1d36715f19be6f9daf4b680337deb8c65acdcae7f2608ba51931540687ac0 + version: 2.4.0 + resolution: "spdx-exceptions@npm:2.4.0" + checksum: b1b650a8d94424473bf9629cf972c86a91c03cccc260f5c901bce0e4b92d831627fec28c9e0a1e9c34c5ebad0a12cf2eab887bec088e0a862abb9d720c2fd0a1 languageName: node linkType: hard @@ -14099,9 +14238,9 @@ __metadata: linkType: hard "spdx-license-ids@npm:^3.0.0": - version: 3.0.11 - resolution: "spdx-license-ids@npm:3.0.11" - checksum: 1da1acb090257773e60b022094050e810ae9fec874dc1461f65dc0400cd42dd830ab2df6e64fb49c2db3dce386dd0362110780e1b154db7c0bb413488836aaeb + version: 3.0.16 + resolution: "spdx-license-ids@npm:3.0.16" + checksum: 5cdaa85aaa24bd02f9353a2e357b4df0a4f205cb35655f3fd0a5674a4fb77081f28ffd425379214bc3be2c2b7593ce1215df6bcc75884aeee0a9811207feabe2 languageName: node linkType: hard @@ -14129,8 +14268,8 @@ __metadata: linkType: hard "sshpk@npm:^1.7.0": - version: 1.17.0 - resolution: "sshpk@npm:1.17.0" + version: 1.18.0 + resolution: "sshpk@npm:1.18.0" dependencies: asn1: ~0.2.3 assert-plus: ^1.0.0 @@ -14145,16 +14284,16 @@ __metadata: sshpk-conv: bin/sshpk-conv sshpk-sign: bin/sshpk-sign sshpk-verify: bin/sshpk-verify - checksum: ba109f65c8e6c35133b8e6ed5576abeff8aa8d614824b7275ec3ca308f081fef483607c28d97780c1e235818b0f93ed8c8b56d0a5968d5a23fd6af57718c7597 + checksum: 01d43374eee3a7e37b3b82fdbecd5518cbb2e47ccbed27d2ae30f9753f22bd6ffad31225cb8ef013bc3fb7785e686cea619203ee1439a228f965558c367c3cfa languageName: node linkType: hard -"ssri@npm:^9.0.0": - version: 9.0.1 - resolution: "ssri@npm:9.0.1" +"ssri@npm:^10.0.0": + version: 10.0.5 + resolution: "ssri@npm:10.0.5" dependencies: - minipass: ^3.1.1 - checksum: fb58f5e46b6923ae67b87ad5ef1c5ab6d427a17db0bead84570c2df3cd50b4ceb880ebdba2d60726588272890bae842a744e1ecce5bd2a2a582fccd5068309eb + minipass: ^7.0.3 + checksum: 0a31b65f21872dea1ed3f7c200d7bc1c1b91c15e419deca14f282508ba917cbb342c08a6814c7f68ca4ca4116dd1a85da2bbf39227480e50125a1ceffeecb750 languageName: node linkType: hard @@ -14184,13 +14323,6 @@ __metadata: languageName: node linkType: hard -"stealthy-require@npm:^1.1.1": - version: 1.1.1 - resolution: "stealthy-require@npm:1.1.1" - checksum: 6805b857a9f3a6a1079fc6652278038b81011f2a5b22cbd559f71a6c02087e6f1df941eb10163e3fdc5391ab5807aa46758d4258547c1f5ede31e6d9bfda8dd3 - languageName: node - linkType: hard - "stream-to-it@npm:^0.2.2": version: 0.2.4 resolution: "stream-to-it@npm:0.2.4" @@ -14210,13 +14342,6 @@ __metadata: languageName: node linkType: hard -"streamsearch@npm:^1.1.0": - version: 1.1.0 - resolution: "streamsearch@npm:1.1.0" - checksum: 1cce16cea8405d7a233d32ca5e00a00169cc0e19fbc02aa839959985f267335d435c07f96e5e0edd0eadc6d39c98d5435fb5bbbdefc62c41834eadc5622ad942 - languageName: node - linkType: hard - "strict-uri-encode@npm:^1.0.0": version: 1.1.0 resolution: "strict-uri-encode@npm:1.1.0" @@ -14231,6 +14356,17 @@ __metadata: languageName: node linkType: hard +"string-width-cjs@npm:string-width@^4.2.0, string-width@npm:^4.0.0, string-width@npm:^4.1.0, string-width@npm:^4.2.0, string-width@npm:^4.2.2, string-width@npm:^4.2.3": + version: 4.2.3 + resolution: "string-width@npm:4.2.3" + dependencies: + emoji-regex: ^8.0.0 + is-fullwidth-code-point: ^3.0.0 + strip-ansi: ^6.0.1 + checksum: e52c10dc3fbfcd6c3a15f159f54a90024241d0f149cf8aed2982a2d801d2e64df0bf1dc351cf8e95c3319323f9f220c16e740b06faecd53e2462df1d2b5443fb + languageName: node + linkType: hard + "string-width@npm:^1.0.1": version: 1.0.2 resolution: "string-width@npm:1.0.2" @@ -14242,18 +14378,7 @@ __metadata: languageName: node linkType: hard -"string-width@npm:^1.0.2 || 2 || 3 || 4, string-width@npm:^4.1.0, string-width@npm:^4.2.0, string-width@npm:^4.2.3": - version: 4.2.3 - resolution: "string-width@npm:4.2.3" - dependencies: - emoji-regex: ^8.0.0 - is-fullwidth-code-point: ^3.0.0 - strip-ansi: ^6.0.1 - checksum: e52c10dc3fbfcd6c3a15f159f54a90024241d0f149cf8aed2982a2d801d2e64df0bf1dc351cf8e95c3319323f9f220c16e740b06faecd53e2462df1d2b5443fb - languageName: node - linkType: hard - -"string-width@npm:^1.0.2 || 2, string-width@npm:^2.1.0, string-width@npm:^2.1.1": +"string-width@npm:^2.1.1": version: 2.1.1 resolution: "string-width@npm:2.1.1" dependencies: @@ -14274,36 +14399,47 @@ __metadata: languageName: node linkType: hard -"string.prototype.trim@npm:~1.2.5": - version: 1.2.6 - resolution: "string.prototype.trim@npm:1.2.6" +"string-width@npm:^5.0.1, string-width@npm:^5.1.2": + version: 5.1.2 + resolution: "string-width@npm:5.1.2" + dependencies: + eastasianwidth: ^0.2.0 + emoji-regex: ^9.2.2 + strip-ansi: ^7.0.1 + checksum: 7369deaa29f21dda9a438686154b62c2c5f661f8dda60449088f9f980196f7908fc39fdd1803e3e01541970287cf5deae336798337e9319a7055af89dafa7193 + languageName: node + linkType: hard + +"string.prototype.trim@npm:^1.2.8, string.prototype.trim@npm:~1.2.8": + version: 1.2.8 + resolution: "string.prototype.trim@npm:1.2.8" dependencies: call-bind: ^1.0.2 - define-properties: ^1.1.4 - es-abstract: ^1.19.5 - checksum: c5968e023afa9dec6a669c1f427f59aeb74f6f7ee5b0f4b9f0ffcef1d3846aa78b02227448cc874bbfa25dd1f8fd2324041c6cade38d4a986e4ade121ce1ea79 + define-properties: ^1.2.0 + es-abstract: ^1.22.1 + checksum: 49eb1a862a53aba73c3fb6c2a53f5463173cb1f4512374b623bcd6b43ad49dd559a06fb5789bdec771a40fc4d2a564411c0a75d35fb27e76bbe738c211ecff07 languageName: node linkType: hard -"string.prototype.trimend@npm:^1.0.5": - version: 1.0.5 - resolution: "string.prototype.trimend@npm:1.0.5" +"string.prototype.trimend@npm:^1.0.7": + version: 1.0.7 + resolution: "string.prototype.trimend@npm:1.0.7" dependencies: call-bind: ^1.0.2 - define-properties: ^1.1.4 - es-abstract: ^1.19.5 - checksum: d44f543833112f57224e79182debadc9f4f3bf9d48a0414d6f0cbd2a86f2b3e8c0ca1f95c3f8e5b32ae83e91554d79d932fc746b411895f03f93d89ed3dfb6bc + define-properties: ^1.2.0 + es-abstract: ^1.22.1 + checksum: 2375516272fd1ba75992f4c4aa88a7b5f3c7a9ca308d963bcd5645adf689eba6f8a04ebab80c33e30ec0aefc6554181a3a8416015c38da0aa118e60ec896310c languageName: node linkType: hard -"string.prototype.trimstart@npm:^1.0.5": - version: 1.0.5 - resolution: "string.prototype.trimstart@npm:1.0.5" +"string.prototype.trimstart@npm:^1.0.7": + version: 1.0.7 + resolution: "string.prototype.trimstart@npm:1.0.7" dependencies: call-bind: ^1.0.2 - define-properties: ^1.1.4 - es-abstract: ^1.19.5 - checksum: a4857c5399ad709d159a77371eeaa8f9cc284469a0b5e1bfe405de16f1fd4166a8ea6f4180e55032f348d1b679b1599fd4301fbc7a8b72bdb3e795e43f7b1048 + define-properties: ^1.2.0 + es-abstract: ^1.22.1 + checksum: 13d0c2cb0d5ff9e926fa0bec559158b062eed2b68cd5be777ffba782c96b2b492944e47057274e064549b94dd27cf81f48b27a31fee8af5b574cff253e7eb613 languageName: node linkType: hard @@ -14332,6 +14468,15 @@ __metadata: languageName: node linkType: hard +"strip-ansi-cjs@npm:strip-ansi@^6.0.1, strip-ansi@npm:^6.0.0, strip-ansi@npm:^6.0.1": + version: 6.0.1 + resolution: "strip-ansi@npm:6.0.1" + dependencies: + ansi-regex: ^5.0.1 + checksum: f3cd25890aef3ba6e1a74e20896c21a46f482e93df4a06567cebf2b57edabb15133f1f94e57434e0a958d61186087b1008e89c94875d019910a213181a14fc8c + languageName: node + linkType: hard + "strip-ansi@npm:^3.0.0, strip-ansi@npm:^3.0.1": version: 3.0.1 resolution: "strip-ansi@npm:3.0.1" @@ -14359,12 +14504,12 @@ __metadata: languageName: node linkType: hard -"strip-ansi@npm:^6.0.0, strip-ansi@npm:^6.0.1": - version: 6.0.1 - resolution: "strip-ansi@npm:6.0.1" +"strip-ansi@npm:^7.0.1": + version: 7.1.0 + resolution: "strip-ansi@npm:7.1.0" dependencies: - ansi-regex: ^5.0.1 - checksum: f3cd25890aef3ba6e1a74e20896c21a46f482e93df4a06567cebf2b57edabb15133f1f94e57434e0a958d61186087b1008e89c94875d019910a213181a14fc8c + ansi-regex: ^6.0.1 + checksum: 859c73fcf27869c22a4e4d8c6acfe690064659e84bef9458aa6d13719d09ca88dcfd40cbf31fd0be63518ea1a643fe070b4827d353e09533a5b0b9fd4553d64d languageName: node linkType: hard @@ -14407,29 +14552,13 @@ __metadata: languageName: node linkType: hard -"strip-json-comments@npm:2.0.1, strip-json-comments@npm:^2.0.1": - version: 2.0.1 - resolution: "strip-json-comments@npm:2.0.1" - checksum: 1074ccb63270d32ca28edfb0a281c96b94dc679077828135141f27d52a5a398ef5e78bcf22809d23cadc2b81dfbe345eb5fd8699b385c8b1128907dec4a7d1e1 - languageName: node - linkType: hard - -"strip-json-comments@npm:3.1.1, strip-json-comments@npm:^3.0.1, strip-json-comments@npm:^3.1.0, strip-json-comments@npm:^3.1.1": +"strip-json-comments@npm:3.1.1, strip-json-comments@npm:^3.0.1, strip-json-comments@npm:^3.1.1": version: 3.1.1 resolution: "strip-json-comments@npm:3.1.1" checksum: 492f73e27268f9b1c122733f28ecb0e7e8d8a531a6662efbd08e22cccb3f9475e90a1b82cab06a392f6afae6d2de636f977e231296400d0ec5304ba70f166443 languageName: node linkType: hard -"supports-color@npm:6.0.0": - version: 6.0.0 - resolution: "supports-color@npm:6.0.0" - dependencies: - has-flag: ^3.0.0 - checksum: 005b4a7e5d78a9a703454f5b7da34336b82825747724d1f3eefea6c3956afcb33b79b31854a93cef0fc1f2449919ae952f79abbfd09a5b5b43ecd26407d3a3a1 - languageName: node - linkType: hard - "supports-color@npm:8.1.1": version: 8.1.1 resolution: "supports-color@npm:8.1.1" @@ -14481,21 +14610,21 @@ __metadata: linkType: hard "swarm-js@npm:^0.1.40": - version: 0.1.40 - resolution: "swarm-js@npm:0.1.40" + version: 0.1.42 + resolution: "swarm-js@npm:0.1.42" dependencies: bluebird: ^3.5.0 buffer: ^5.0.5 eth-lib: ^0.1.26 fs-extra: ^4.0.2 - got: ^7.1.0 + got: ^11.8.5 mime-types: ^2.1.16 mkdirp-promise: ^5.0.1 mock-fs: ^4.1.0 setimmediate: ^1.0.5 tar: ^4.0.2 xhr-request: ^1.0.1 - checksum: 1de56e0cb02c1c80e041efb2bd2cc7250c5451c3016967266c16d5c1e8c74fe5c3aae45dc46065b1f4d77667a8453b967961ec58b3975469522f566aa840a914 + checksum: bbb54b84232ef113ee106cf8158d1c827fbf84b309799576f61603f63d7653fde7e71df981d07f9e4c41781bbbbd72be77e5a47e6b694d6a83b96a6a20641475 languageName: node linkType: hard @@ -14543,41 +14672,42 @@ __metadata: languageName: node linkType: hard -"table@npm:^6.8.0": - version: 6.8.0 - resolution: "table@npm:6.8.0" +"table@npm:^6.8.0, table@npm:^6.8.1": + version: 6.8.1 + resolution: "table@npm:6.8.1" dependencies: ajv: ^8.0.1 lodash.truncate: ^4.4.2 slice-ansi: ^4.0.0 string-width: ^4.2.3 strip-ansi: ^6.0.1 - checksum: 5b07fe462ee03d2e1fac02cbb578efd2e0b55ac07e3d3db2e950aa9570ade5a4a2b8d3c15e9f25c89e4e50b646bc4269934601ee1eef4ca7968ad31960977690 + checksum: 08249c7046125d9d0a944a6e96cfe9ec66908d6b8a9db125531be6eb05fa0de047fd5542e9d43b4f987057f00a093b276b8d3e19af162a9c40db2681058fd306 languageName: node linkType: hard "tape@npm:^4.6.3": - version: 4.15.1 - resolution: "tape@npm:4.15.1" + version: 4.17.0 + resolution: "tape@npm:4.17.0" dependencies: + "@ljharb/resumer": ~0.0.1 + "@ljharb/through": ~2.3.9 call-bind: ~1.0.2 deep-equal: ~1.1.1 - defined: ~1.0.0 + defined: ~1.0.1 dotignore: ~0.1.2 for-each: ~0.3.3 - glob: ~7.2.0 + glob: ~7.2.3 has: ~1.0.3 inherits: ~2.0.4 is-regex: ~1.1.4 - minimist: ~1.2.6 - object-inspect: ~1.12.0 - resolve: ~1.22.0 - resumer: ~0.0.0 - string.prototype.trim: ~1.2.5 - through: ~2.3.8 + minimist: ~1.2.8 + mock-property: ~1.0.0 + object-inspect: ~1.12.3 + resolve: ~1.22.6 + string.prototype.trim: ~1.2.8 bin: tape: bin/tape - checksum: 3726aa5979cbffe057455db9fd353c88ff26a1d8d3addc7d479fcbb8202caf12cb3e122492e203db00195b6fdfbc1f9c06762a4aeead7efc710ff46955be550f + checksum: b785f4997f4323d9a1b6f5bda97aaea65a4c68f81296ab46bd126776f3c6f4203073187d5a4bcaa98884bf28e3cfaa50c2d8d81cc0025e4777054455837390dc languageName: node linkType: hard @@ -14624,16 +14754,16 @@ __metadata: linkType: hard "tar@npm:^6.1.11, tar@npm:^6.1.2": - version: 6.1.11 - resolution: "tar@npm:6.1.11" + version: 6.2.0 + resolution: "tar@npm:6.2.0" dependencies: chownr: ^2.0.0 fs-minipass: ^2.0.0 - minipass: ^3.0.0 + minipass: ^5.0.0 minizlib: ^2.1.1 mkdirp: ^1.0.3 yallist: ^4.0.0 - checksum: a04c07bb9e2d8f46776517d4618f2406fb977a74d914ad98b264fc3db0fe8224da5bec11e5f8902c5b9bcb8ace22d95fbe3c7b36b8593b7dfc8391a25898f32f + checksum: db4d9fe74a2082c3a5016630092c54c8375ff3b280186938cfd104f2e089c4fd9bad58688ef6be9cf186a889671bf355c7cda38f09bbf60604b281715ca57f5c languageName: node linkType: hard @@ -14690,14 +14820,14 @@ __metadata: languageName: node linkType: hard -"through@npm:>=2.2.7 <3, through@npm:^2.3.6, through@npm:~2.3.4, through@npm:~2.3.8": +"through@npm:>=2.2.7 <3, through@npm:^2.3.6": version: 2.3.8 resolution: "through@npm:2.3.8" checksum: a38c3e059853c494af95d50c072b83f8b676a9ba2818dcc5b108ef252230735c54e0185437618596c790bbba8fcdaef5b290405981ffa09dce67b1f1bf190cbd languageName: node linkType: hard -"timed-out@npm:^4.0.0, timed-out@npm:^4.0.1": +"timed-out@npm:^4.0.1": version: 4.0.1 resolution: "timed-out@npm:4.0.1" checksum: 98efc5d6fc0d2a329277bd4d34f65c1bf44d9ca2b14fd267495df92898f522e6f563c5e9e467c418e0836f5ca1f47a84ca3ee1de79b1cc6fe433834b7f02ec54 @@ -14819,7 +14949,7 @@ __metadata: languageName: node linkType: hard -"tough-cookie@npm:^2.3.3, tough-cookie@npm:~2.5.0": +"tough-cookie@npm:~2.5.0": version: 2.5.0 resolution: "tough-cookie@npm:2.5.0" dependencies: @@ -14844,8 +14974,8 @@ __metadata: linkType: hard "ts-command-line-args@npm:^2.2.0": - version: 2.3.1 - resolution: "ts-command-line-args@npm:2.3.1" + version: 2.5.1 + resolution: "ts-command-line-args@npm:2.5.1" dependencies: chalk: ^4.1.0 command-line-args: ^5.1.1 @@ -14853,7 +14983,7 @@ __metadata: string-format: ^2.0.0 bin: write-markdown: dist/write-markdown.js - checksum: f74a3461891c2a52cc3fd850ad53e983972ba8208410c441a4491f66d15e5d5ea6bed696cbdb54427d6b798b902f841f0c9d3ed13468c2e179285b7507df7a0d + checksum: 7c0a7582e94f1d2160e3dd379851ec4f1758bc673ccd71bae07f839f83051b6b83e0ae14325c2d04ea728e5bde7b7eacfd2ab060b8fd4b8ab29e0bbf77f6c51e languageName: node linkType: hard @@ -14902,8 +15032,8 @@ __metadata: linkType: hard "ts-node@npm:^10.9.1": - version: 10.9.1 - resolution: "ts-node@npm:10.9.1" + version: 10.9.2 + resolution: "ts-node@npm:10.9.2" dependencies: "@cspotcode/source-map-support": ^0.8.0 "@tsconfig/node10": ^1.0.7 @@ -14935,33 +15065,33 @@ __metadata: ts-node-script: dist/bin-script.js ts-node-transpile-only: dist/bin-transpile.js ts-script: dist/bin-script-deprecated.js - checksum: 090adff1302ab20bd3486e6b4799e90f97726ed39e02b39e566f8ab674fd5bd5f727f43615debbfc580d33c6d9d1c6b1b3ce7d8e3cca3e20530a145ffa232c35 + checksum: fde256c9073969e234526e2cfead42591b9a2aec5222bac154b0de2fa9e4ceb30efcd717ee8bc785a56f3a119bdd5aa27b333d9dbec94ed254bd26f8944c67ac languageName: node linkType: hard -"tsconfig-paths@npm:^3.14.1": - version: 3.14.1 - resolution: "tsconfig-paths@npm:3.14.1" +"tsconfig-paths@npm:^3.15.0": + version: 3.15.0 + resolution: "tsconfig-paths@npm:3.15.0" dependencies: "@types/json5": ^0.0.29 - json5: ^1.0.1 + json5: ^1.0.2 minimist: ^1.2.6 strip-bom: ^3.0.0 - checksum: 8afa01c673ebb4782ba53d3a12df97fa837ce524f8ad38ee4e2b2fd57f5ac79abc21c574e9e9eb014d93efe7fe8214001b96233b5c6ea75bd1ea82afe17a4c6d + checksum: 59f35407a390d9482b320451f52a411a256a130ff0e7543d18c6f20afab29ac19fbe55c360a93d6476213cc335a4d76ce90f67df54c4e9037f7d240920832201 languageName: node linkType: hard -"tslib@npm:^1.8.1, tslib@npm:^1.9.0, tslib@npm:^1.9.3": +"tslib@npm:^1.11.1, tslib@npm:^1.8.1, tslib@npm:^1.9.0, tslib@npm:^1.9.3": version: 1.14.1 resolution: "tslib@npm:1.14.1" checksum: dbe628ef87f66691d5d2959b3e41b9ca0045c3ee3c7c7b906cc1e328b39f199bb1ad9e671c39025bd56122ac57dfbf7385a94843b1cc07c60a4db74795829acd languageName: node linkType: hard -"tslib@npm:^2.1.0": - version: 2.4.1 - resolution: "tslib@npm:2.4.1" - checksum: 19480d6e0313292bd6505d4efe096a6b31c70e21cf08b5febf4da62e95c265c8f571f7b36fcc3d1a17e068032f59c269fab3459d6cd3ed6949eafecf64315fca +"tslib@npm:^2.1.0, tslib@npm:^2.3.1, tslib@npm:^2.5.0": + version: 2.6.2 + resolution: "tslib@npm:2.6.2" + checksum: 329ea56123005922f39642318e3d1f0f8265d1e7fcb92c633e0809521da75eeaca28d2cf96d7248229deb40e5c19adf408259f4b9640afd20d13aecc1430f3ad languageName: node linkType: hard @@ -15031,7 +15161,7 @@ __metadata: languageName: node linkType: hard -"type-detect@npm:^4.0.0, type-detect@npm:^4.0.5": +"type-detect@npm:^4.0.0, type-detect@npm:^4.0.8": version: 4.0.8 resolution: "type-detect@npm:4.0.8" checksum: 62b5628bff67c0eb0b66afa371bd73e230399a8d2ad30d852716efcc4656a7516904570cd8631a49a3ce57c10225adf5d0cbdcb47f6b0255fe6557c453925a15 @@ -15083,10 +15213,10 @@ __metadata: languageName: node linkType: hard -"type@npm:^2.5.0": - version: 2.6.0 - resolution: "type@npm:2.6.0" - checksum: 80da01fcc0f6ed5a253dc326530e134000a8f66ea44b6d9687cde2f894f0d0b2486595b0cd040a64f7f79dc3120784236f8c9ef667a8aef03984e049b447cfb4 +"type@npm:^2.7.2": + version: 2.7.2 + resolution: "type@npm:2.7.2" + checksum: 0f42379a8adb67fe529add238a3e3d16699d95b42d01adfe7b9a7c5da297f5c1ba93de39265ba30ffeb37dfd0afb3fb66ae09f58d6515da442219c086219f6f4 languageName: node linkType: hard @@ -15108,8 +15238,8 @@ __metadata: linkType: hard "typechain@npm:^8.1.0": - version: 8.1.0 - resolution: "typechain@npm:8.1.0" + version: 8.3.2 + resolution: "typechain@npm:8.3.2" dependencies: "@types/prettier": ^2.1.1 debug: ^4.3.1 @@ -15125,7 +15255,54 @@ __metadata: typescript: ">=4.3.0" bin: typechain: dist/cli/cli.js - checksum: d8e05c4437d5ba54fd5dea28c20884c2aa259e911c6ffc9d1768a229633c661f378439e4b0a1a21f33372e8ed82adc324583381cde16f199af9440792a95da88 + checksum: 146a1896fa93403404be78757790b0f95b5457efebcca16b61622e09c374d555ef4f837c1c4eedf77e03abc50276d96a2f33064ec09bb802f62d8cc2b13fce70 + languageName: node + linkType: hard + +"typed-array-buffer@npm:^1.0.0": + version: 1.0.0 + resolution: "typed-array-buffer@npm:1.0.0" + dependencies: + call-bind: ^1.0.2 + get-intrinsic: ^1.2.1 + is-typed-array: ^1.1.10 + checksum: 3e0281c79b2a40cd97fe715db803884301993f4e8c18e8d79d75fd18f796e8cd203310fec8c7fdb5e6c09bedf0af4f6ab8b75eb3d3a85da69328f28a80456bd3 + languageName: node + linkType: hard + +"typed-array-byte-length@npm:^1.0.0": + version: 1.0.0 + resolution: "typed-array-byte-length@npm:1.0.0" + dependencies: + call-bind: ^1.0.2 + for-each: ^0.3.3 + has-proto: ^1.0.1 + is-typed-array: ^1.1.10 + checksum: b03db16458322b263d87a702ff25388293f1356326c8a678d7515767ef563ef80e1e67ce648b821ec13178dd628eb2afdc19f97001ceae7a31acf674c849af94 + languageName: node + linkType: hard + +"typed-array-byte-offset@npm:^1.0.0": + version: 1.0.0 + resolution: "typed-array-byte-offset@npm:1.0.0" + dependencies: + available-typed-arrays: ^1.0.5 + call-bind: ^1.0.2 + for-each: ^0.3.3 + has-proto: ^1.0.1 + is-typed-array: ^1.1.10 + checksum: 04f6f02d0e9a948a95fbfe0d5a70b002191fae0b8fe0fe3130a9b2336f043daf7a3dda56a31333c35a067a97e13f539949ab261ca0f3692c41603a46a94e960b + languageName: node + linkType: hard + +"typed-array-length@npm:^1.0.4": + version: 1.0.4 + resolution: "typed-array-length@npm:1.0.4" + dependencies: + call-bind: ^1.0.2 + for-each: ^0.3.3 + is-typed-array: ^1.1.9 + checksum: 2228febc93c7feff142b8c96a58d4a0d7623ecde6c7a24b2b98eb3170e99f7c7eff8c114f9b283085cd59dcd2bd43aadf20e25bba4b034a53c5bb292f71f8956 languageName: node linkType: hard @@ -15156,12 +15333,12 @@ __metadata: linkType: hard "typescript@npm:^4.7.4": - version: 4.7.4 - resolution: "typescript@npm:4.7.4" + version: 4.9.5 + resolution: "typescript@npm:4.9.5" bin: tsc: bin/tsc tsserver: bin/tsserver - checksum: 5750181b1cd7e6482c4195825547e70f944114fb47e58e4aa7553e62f11b3f3173766aef9c281783edfd881f7b8299cf35e3ca8caebe73d8464528c907a164df + checksum: ee000bc26848147ad423b581bd250075662a354d84f0e06eb76d3b892328d8d4440b7487b5a83e851b12b255f55d71835b008a66cbf8f255a11e4400159237db languageName: node linkType: hard @@ -15176,12 +15353,12 @@ __metadata: linkType: hard "typescript@patch:typescript@^4.7.4#~builtin": - version: 4.7.4 - resolution: "typescript@patch:typescript@npm%3A4.7.4#~builtin::version=4.7.4&hash=7ad353" + version: 4.9.5 + resolution: "typescript@patch:typescript@npm%3A4.9.5#~builtin::version=4.9.5&hash=7ad353" bin: tsc: bin/tsc tsserver: bin/tsserver - checksum: 9096d8f6c16cb80ef3bf96fcbbd055bf1c4a43bd14f3b7be45a9fbe7ada46ec977f604d5feed3263b4f2aa7d4c7477ce5f9cd87de0d6feedec69a983f3a4f93e + checksum: 2eee5c37cad4390385db5db5a8e81470e42e8f1401b0358d7390095d6f681b410f2c4a0c496c6ff9ebd775423c7785cdace7bcdad76c7bee283df3d9718c0f20 languageName: node linkType: hard @@ -15230,11 +15407,11 @@ __metadata: linkType: hard "uglify-js@npm:^3.1.4": - version: 3.16.1 - resolution: "uglify-js@npm:3.16.1" + version: 3.17.4 + resolution: "uglify-js@npm:3.17.4" bin: uglifyjs: bin/uglifyjs - checksum: e4108b35af7bcc9cf3be5366614bb1df2c78695aa14dee85b48cb9036a4478e60e91afe2375917e3284b61ef056fcab3a1d4bfc7c563e57bc77fd5ac89463a4c + checksum: 7b3897df38b6fc7d7d9f4dcd658599d81aa2b1fb0d074829dd4e5290f7318dbca1f4af2f45acb833b95b1fe0ed4698662ab61b87e94328eb4c0a0d3435baf924 languageName: node linkType: hard @@ -15273,21 +15450,26 @@ __metadata: languageName: node linkType: hard +"undici-types@npm:~5.26.4": + version: 5.26.5 + resolution: "undici-types@npm:5.26.5" + checksum: 3192ef6f3fd5df652f2dc1cd782b49d6ff14dc98e5dced492aa8a8c65425227da5da6aafe22523c67f035a272c599bb89cfe803c1db6311e44bed3042fc25487 + languageName: node + linkType: hard + "undici@npm:^5.14.0": - version: 5.18.0 - resolution: "undici@npm:5.18.0" + version: 5.28.3 + resolution: "undici@npm:5.28.3" dependencies: - busboy: ^1.6.0 - checksum: 74e0f357c376c745fcca612481a5742866ae36086ad387e626255f4c4a15fc5357d9d0fa4355271b6a633d50f5556c3e85720844680c44861c66e23afca7245f + "@fastify/busboy": ^2.0.0 + checksum: fa1e65aff896c5e2ee23637b632e306f9e3a2b32a3dc0b23ea71e5555ad350bcc25713aea894b3dccc0b7dc2c5e92a5a58435ebc2033b731a5524506f573dfd2 languageName: node linkType: hard -"undici@npm:^5.4.0": - version: 5.19.1 - resolution: "undici@npm:5.19.1" - dependencies: - busboy: ^1.6.0 - checksum: 57ee94ee74d944faa41dbcb2faf4e0c90069708d3aaae860185884e51376b5d457728352a8396d69a3c9cb752b62ff99a19a664c5aacb7ee61cc488af499a01c +"unfetch@npm:^4.2.0": + version: 4.2.0 + resolution: "unfetch@npm:4.2.0" + checksum: 6a4b2557e1d921eaa80c4425ce27a404945ec26491ed06e62598f333996a91a44c7908cb26dc7c2746d735762b13276cf4aa41829b4c8f438dde63add3045d7a languageName: node linkType: hard @@ -15303,21 +15485,21 @@ __metadata: languageName: node linkType: hard -"unique-filename@npm:^1.1.1": - version: 1.1.1 - resolution: "unique-filename@npm:1.1.1" +"unique-filename@npm:^3.0.0": + version: 3.0.0 + resolution: "unique-filename@npm:3.0.0" dependencies: - unique-slug: ^2.0.0 - checksum: cf4998c9228cc7647ba7814e255dec51be43673903897b1786eff2ac2d670f54d4d733357eb08dea969aa5e6875d0e1bd391d668fbdb5a179744e7c7551a6f80 + unique-slug: ^4.0.0 + checksum: 8e2f59b356cb2e54aab14ff98a51ac6c45781d15ceaab6d4f1c2228b780193dc70fae4463ce9e1df4479cb9d3304d7c2043a3fb905bdeca71cc7e8ce27e063df languageName: node linkType: hard -"unique-slug@npm:^2.0.0": - version: 2.0.2 - resolution: "unique-slug@npm:2.0.2" +"unique-slug@npm:^4.0.0": + version: 4.0.0 + resolution: "unique-slug@npm:4.0.0" dependencies: imurmurhash: ^0.1.4 - checksum: 5b6876a645da08d505dedb970d1571f6cebdf87044cb6b740c8dbb24f0d6e1dc8bdbf46825fd09f994d7cf50760e6f6e063cfa197d51c5902c00a861702eb75a + checksum: 0884b58365af59f89739e6f71e3feacb5b1b41f2df2d842d0757933620e6de08eff347d27e9d499b43c40476cbaf7988638d3acb2ffbcb9d35fd035591adfd15 languageName: node linkType: hard @@ -15329,9 +15511,9 @@ __metadata: linkType: hard "universalify@npm:^2.0.0": - version: 2.0.0 - resolution: "universalify@npm:2.0.0" - checksum: 2406a4edf4a8830aa6813278bab1f953a8e40f2f63a37873ffa9a3bc8f9745d06cc8e88f3572cb899b7e509013f7f6fcc3e37e8a6d914167a5381d8440518c44 + version: 2.0.1 + resolution: "universalify@npm:2.0.1" + checksum: ecd8469fe0db28e7de9e5289d32bd1b6ba8f7183db34f3bfc4ca53c49891c2d6aa05f3fb3936a81285a905cc509fb641a0c3fc131ec786167eff41236ae32e60 languageName: node linkType: hard @@ -15375,15 +15557,6 @@ __metadata: languageName: node linkType: hard -"url-parse-lax@npm:^1.0.0": - version: 1.0.0 - resolution: "url-parse-lax@npm:1.0.0" - dependencies: - prepend-http: ^1.0.1 - checksum: 03316acff753845329652258c16d1688765ee34f7d242a94dadf9ff6e43ea567ec062cec7aa27c37f76f2c57f95e0660695afff32fb97b527591c7340a3090fa - languageName: node - linkType: hard - "url-parse-lax@npm:^3.0.0": version: 3.0.0 resolution: "url-parse-lax@npm:3.0.0" @@ -15400,20 +15573,13 @@ __metadata: languageName: node linkType: hard -"url-to-options@npm:^1.0.1": - version: 1.0.1 - resolution: "url-to-options@npm:1.0.1" - checksum: 20e59f4578525fb0d30ffc22b13b5aa60bc9e57cefd4f5842720f5b57211b6dec54abeae2d675381ac4486fd1a2e987f1318725dea996e503ff89f8c8ce2c17e - languageName: node - linkType: hard - "url@npm:^0.11.0": - version: 0.11.0 - resolution: "url@npm:0.11.0" + version: 0.11.3 + resolution: "url@npm:0.11.3" dependencies: - punycode: 1.3.2 - querystring: 0.2.0 - checksum: 50d100d3dd2d98b9fe3ada48cadb0b08aa6be6d3ac64112b867b56b19be4bfcba03c2a9a0d7922bfd7ac17d4834e88537749fe182430dfd9b68e520175900d90 + punycode: ^1.4.1 + qs: ^6.11.2 + checksum: f9e7886f46a16f96d2e42fbcc5d682c231c55ef5442c1ff66150c0f6556f6e3a97d094a84f51be15ec2432711d212eb60426659ce418f5fcadeaa3f601532c4e languageName: node linkType: hard @@ -15425,12 +15591,12 @@ __metadata: linkType: hard "utf-8-validate@npm:^5.0.2": - version: 5.0.9 - resolution: "utf-8-validate@npm:5.0.9" + version: 5.0.10 + resolution: "utf-8-validate@npm:5.0.10" dependencies: node-gyp: latest node-gyp-build: ^4.3.0 - checksum: 90117f1b65e0a1256c83dfad529983617263b622f2379745311d0438c7ea31db0d134ebd0dca84c3f5847a3560a3d249644e478a9109c616f63c7ea19cac53dc + checksum: 5579350a023c66a2326752b6c8804cc7b39dcd251bb088241da38db994b8d78352e388dcc24ad398ab98385ba3c5ffcadb6b5b14b2637e43f767869055e46ba6 languageName: node linkType: hard @@ -15449,15 +15615,17 @@ __metadata: linkType: hard "util.promisify@npm:^1.0.0": - version: 1.1.1 - resolution: "util.promisify@npm:1.1.1" + version: 1.1.2 + resolution: "util.promisify@npm:1.1.2" dependencies: - call-bind: ^1.0.0 - define-properties: ^1.1.3 + call-bind: ^1.0.2 + define-properties: ^1.2.0 for-each: ^0.3.3 - has-symbols: ^1.0.1 - object.getownpropertydescriptors: ^2.1.1 - checksum: ea371c30b90576862487ae4efd7182aa5855019549a4019d82629acc2709e8ccb0f38944403eebec622fff8ebb44ac3f46a52d745d5f543d30606132a4905f96 + has-proto: ^1.0.1 + has-symbols: ^1.0.3 + object.getownpropertydescriptors: ^2.1.6 + safe-array-concat: ^1.0.0 + checksum: 9a5233e7fd067ca24abe2310f9c93e6df3adb644a662fcd826454d30539d3dd1d557b75bfed4cedd4993203012ea6add6d7dd268fed35bbdac4736dce9446373 languageName: node linkType: hard @@ -15468,13 +15636,6 @@ __metadata: languageName: node linkType: hard -"uuid@npm:2.0.1": - version: 2.0.1 - resolution: "uuid@npm:2.0.1" - checksum: e129e494e33cededdfc2cefbd63da966344b873bbfd3373a311b0acc2e7ab53d68b2515879444898867d84b863e44939e852484b9f3a54c4fd86d985a7dadb8d - languageName: node - linkType: hard - "uuid@npm:3.3.2": version: 3.3.2 resolution: "uuid@npm:3.3.2" @@ -15510,9 +15671,9 @@ __metadata: linkType: hard "v8-compile-cache@npm:^2.0.3": - version: 2.3.0 - resolution: "v8-compile-cache@npm:2.3.0" - checksum: adb0a271eaa2297f2f4c536acbfee872d0dd26ec2d76f66921aa7fc437319132773483344207bdbeee169225f4739016d8d2dbf0553913a52bb34da6d0334f8e + version: 2.4.0 + resolution: "v8-compile-cache@npm:2.4.0" + checksum: 8eb6ddb59d86f24566503f1e6ca98f3e6f43599f05359bd3ab737eaaf1585b338091478a4d3d5c2646632cf8030288d7888684ea62238cdce15a65ae2416718f languageName: node linkType: hard @@ -15861,33 +16022,19 @@ __metadata: languageName: node linkType: hard -"web3-utils@npm:^1.0.0-beta.31": - version: 1.7.4 - resolution: "web3-utils@npm:1.7.4" - dependencies: - 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: 5d9256366904e5c24c7198a8791aa76217100aa068650ccc18264ff670d1e8d42d40fcc5ddc66e3c05fac3b480753ccf7e519709e60aefd73d71dd4c4d2adcbb - languageName: node - linkType: hard - -"web3-utils@npm:^1.3.6": - version: 1.8.2 - resolution: "web3-utils@npm:1.8.2" +"web3-utils@npm:^1.0.0-beta.31, web3-utils@npm:^1.3.6": + version: 1.10.4 + resolution: "web3-utils@npm:1.10.4" dependencies: + "@ethereumjs/util": ^8.1.0 bn.js: ^5.2.1 ethereum-bloom-filters: ^1.0.6 - ethereumjs-util: ^7.1.0 + ethereum-cryptography: ^2.1.2 ethjs-unit: 0.1.6 number-to-bn: 1.7.0 randombytes: ^2.1.0 utf8: 3.0.0 - checksum: a6cda086d7bde4939fc55be8f1dc5040b4cacd9205ac2ac07f37d14305214679e030af7814a3e97f6fabf2901e3452cd0dc8ce7c1cdd8bce4d0d4bae72c50ad9 + checksum: a1535817a4653f1b5cc868aa19305158122379078a41e13642e1ba64803f6f8e5dd2fb8c45c033612b8f52dde42d8008afce85296c0608276fe1513dece66a49 languageName: node linkType: hard @@ -15979,13 +16126,26 @@ __metadata: linkType: hard "which-module@npm:^2.0.0": - version: 2.0.0 - resolution: "which-module@npm:2.0.0" - checksum: 809f7fd3dfcb2cdbe0180b60d68100c88785084f8f9492b0998c051d7a8efe56784492609d3f09ac161635b78ea29219eb1418a98c15ce87d085bce905705c9c + version: 2.0.1 + resolution: "which-module@npm:2.0.1" + checksum: 1967b7ce17a2485544a4fdd9063599f0f773959cca24176dbe8f405e55472d748b7c549cd7920ff6abb8f1ab7db0b0f1b36de1a21c57a8ff741f4f1e792c52be + languageName: node + linkType: hard + +"which-typed-array@npm:^1.1.13, which-typed-array@npm:^1.1.14": + version: 1.1.14 + resolution: "which-typed-array@npm:1.1.14" + dependencies: + available-typed-arrays: ^1.0.6 + call-bind: ^1.0.5 + for-each: ^0.3.3 + gopd: ^1.0.1 + has-tostringtag: ^1.0.1 + checksum: efe30c143c58630dde8ab96f9330e20165bacd77ca843c602b510120a415415573bcdef3ccbc30a0e5aaf20f257360cfe24712aea0008f149ce5bb99834c0c0b languageName: node linkType: hard -"which@npm:1.3.1, which@npm:^1.1.1, which@npm:^1.2.9, 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" dependencies: @@ -15996,7 +16156,7 @@ __metadata: languageName: node linkType: hard -"which@npm:^2.0.0, which@npm:^2.0.1, which@npm:^2.0.2": +"which@npm:^2.0.0, which@npm:^2.0.1": version: 2.0.2 resolution: "which@npm:2.0.2" dependencies: @@ -16007,21 +16167,23 @@ __metadata: languageName: node linkType: hard -"wide-align@npm:1.1.3": - version: 1.1.3 - resolution: "wide-align@npm:1.1.3" +"which@npm:^4.0.0": + version: 4.0.0 + resolution: "which@npm:4.0.0" dependencies: - string-width: ^1.0.2 || 2 - checksum: d09c8012652a9e6cab3e82338d1874a4d7db2ad1bd19ab43eb744acf0b9b5632ec406bdbbbb970a8f4771a7d5ef49824d038ba70aa884e7723f5b090ab87134d + isexe: ^3.1.1 + bin: + node-which: bin/which.js + checksum: f17e84c042592c21e23c8195108cff18c64050b9efb8459589116999ea9da6dd1509e6a1bac3aeebefd137be00fabbb61b5c2bc0aa0f8526f32b58ee2f545651 languageName: node linkType: hard -"wide-align@npm:^1.1.5": - version: 1.1.5 - resolution: "wide-align@npm:1.1.5" +"widest-line@npm:^3.1.0": + version: 3.1.0 + resolution: "widest-line@npm:3.1.0" dependencies: - string-width: ^1.0.2 || 2 || 3 || 4 - checksum: d5fc37cd561f9daee3c80e03b92ed3e84d80dde3365a8767263d03dacfc8fa06b065ffe1df00d8c2a09f731482fcacae745abfbb478d4af36d0a891fad4834d3 + string-width: ^4.0.0 + checksum: 03db6c9d0af9329c37d74378ff1d91972b12553c7d72a6f4e8525fe61563fa7adb0b9d6e8d546b7e059688712ea874edd5ded475999abdeedf708de9849310e0 languageName: node linkType: hard @@ -16034,10 +16196,10 @@ __metadata: languageName: node linkType: hard -"word-wrap@npm:^1.2.3, word-wrap@npm:~1.2.3": - version: 1.2.4 - resolution: "word-wrap@npm:1.2.4" - checksum: 8f1f2e0a397c0e074ca225ba9f67baa23f99293bc064e31355d426ae91b8b3f6b5f6c1fc9ae5e9141178bb362d563f55e62fd8d5c31f2a77e3ade56cb3e35bd1 +"word-wrap@npm:~1.2.3": + version: 1.2.5 + resolution: "word-wrap@npm:1.2.5" + checksum: f93ba3586fc181f94afdaff3a6fef27920b4b6d9eaefed0f428f8e07adea2a7f54a5f2830ce59406c8416f033f86902b91eb824072354645eea687dff3691ccb languageName: node linkType: hard @@ -16065,6 +16227,17 @@ __metadata: languageName: node linkType: hard +"wrap-ansi-cjs@npm:wrap-ansi@^7.0.0, wrap-ansi@npm:^7.0.0": + version: 7.0.0 + resolution: "wrap-ansi@npm:7.0.0" + dependencies: + ansi-styles: ^4.0.0 + string-width: ^4.1.0 + strip-ansi: ^6.0.0 + checksum: a790b846fd4505de962ba728a21aaeda189b8ee1c7568ca5e817d85930e06ef8d1689d49dbf0e881e8ef84436af3a88bc49115c2e2788d841ff1b8b5b51a608b + languageName: node + linkType: hard + "wrap-ansi@npm:^2.0.0": version: 2.1.0 resolution: "wrap-ansi@npm:2.1.0" @@ -16086,14 +16259,14 @@ __metadata: languageName: node linkType: hard -"wrap-ansi@npm:^7.0.0": - version: 7.0.0 - resolution: "wrap-ansi@npm:7.0.0" +"wrap-ansi@npm:^8.1.0": + version: 8.1.0 + resolution: "wrap-ansi@npm:8.1.0" dependencies: - ansi-styles: ^4.0.0 - string-width: ^4.1.0 - strip-ansi: ^6.0.0 - checksum: a790b846fd4505de962ba728a21aaeda189b8ee1c7568ca5e817d85930e06ef8d1689d49dbf0e881e8ef84436af3a88bc49115c2e2788d841ff1b8b5b51a608b + ansi-styles: ^6.1.0 + string-width: ^5.0.1 + strip-ansi: ^7.0.1 + checksum: 371733296dc2d616900ce15a0049dca0ef67597d6394c57347ba334393599e800bab03c41d4d45221b6bc967b8c453ec3ae4749eff3894202d16800fdfe0e238 languageName: node linkType: hard @@ -16148,7 +16321,7 @@ __metadata: languageName: node linkType: hard -"ws@npm:^7.4.5": +"ws@npm:^7.4.5, ws@npm:^7.4.6": version: 7.5.9 resolution: "ws@npm:7.5.9" peerDependencies: @@ -16163,21 +16336,6 @@ __metadata: languageName: node linkType: hard -"ws@npm:^7.4.6": - version: 7.5.8 - resolution: "ws@npm:7.5.8" - peerDependencies: - bufferutil: ^4.0.1 - utf-8-validate: ^5.0.2 - peerDependenciesMeta: - bufferutil: - optional: true - utf-8-validate: - optional: true - checksum: 49479ccf3ddab6500c5906fbcc316e9c8cd44b0ffb3903a6c1caf9b38cb9e06691685722a4c642cfa7d4c6eb390424fc3142cd4f8b940cfc7a9ce9761b1cd65b - languageName: node - linkType: hard - "xhr-request-promise@npm:^0.1.2": version: 0.1.3 resolution: "xhr-request-promise@npm:0.1.3" @@ -16223,13 +16381,6 @@ __metadata: languageName: node linkType: hard -"xmlhttprequest@npm:1.8.0": - version: 1.8.0 - resolution: "xmlhttprequest@npm:1.8.0" - checksum: c891cf0d7884b4f5cce835aa01f1965727cd352cbd2d7a2e0605bf11ec99ae2198364cca54656ec8b2581a5704dee6c2bf9911922a0ff2a71b613455d32e81b7 - languageName: node - linkType: hard - "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" @@ -16295,7 +16446,14 @@ __metadata: languageName: node linkType: hard -"yargs-parser@npm:13.1.2, yargs-parser@npm:^13.1.0, yargs-parser@npm:^13.1.2": +"yargs-parser@npm:20.2.4": + version: 20.2.4 + resolution: "yargs-parser@npm:20.2.4" + checksum: d251998a374b2743a20271c2fd752b9fbef24eb881d53a3b99a7caa5e8227fcafd9abf1f345ac5de46435821be25ec12189a11030c12ee6481fef6863ed8b924 + languageName: node + linkType: hard + +"yargs-parser@npm:^13.1.0": version: 13.1.2 resolution: "yargs-parser@npm:13.1.2" dependencies: @@ -16305,13 +16463,6 @@ __metadata: languageName: node linkType: hard -"yargs-parser@npm:20.2.4": - version: 20.2.4 - resolution: "yargs-parser@npm:20.2.4" - checksum: d251998a374b2743a20271c2fd752b9fbef24eb881d53a3b99a7caa5e8227fcafd9abf1f345ac5de46435821be25ec12189a11030c12ee6481fef6863ed8b924 - languageName: node - linkType: hard - "yargs-parser@npm:^16.1.0": version: 16.1.0 resolution: "yargs-parser@npm:16.1.0" @@ -16339,17 +16490,6 @@ __metadata: languageName: node linkType: hard -"yargs-unparser@npm:1.6.0": - version: 1.6.0 - resolution: "yargs-unparser@npm:1.6.0" - dependencies: - flat: ^4.1.0 - lodash: ^4.17.15 - yargs: ^13.3.0 - checksum: ca662bb94af53d816d47f2162f0a1d135783f09de9fd47645a5cb18dd25532b0b710432b680d2c065ff45de122ba4a96433c41595fa7bfcc08eb12e889db95c1 - languageName: node - linkType: hard - "yargs-unparser@npm:2.0.0": version: 2.0.0 resolution: "yargs-unparser@npm:2.0.0" @@ -16381,24 +16521,6 @@ __metadata: languageName: node linkType: hard -"yargs@npm:13.3.2, yargs@npm:^13.3.0": - version: 13.3.2 - resolution: "yargs@npm:13.3.2" - dependencies: - cliui: ^5.0.0 - find-up: ^3.0.0 - get-caller-file: ^2.0.1 - require-directory: ^2.1.1 - require-main-filename: ^2.0.0 - set-blocking: ^2.0.0 - string-width: ^3.0.0 - which-module: ^2.0.0 - y18n: ^4.0.0 - yargs-parser: ^13.1.2 - checksum: 75c13e837eb2bb25717957ba58d277e864efc0cca7f945c98bdf6477e6ec2f9be6afa9ed8a876b251a21423500c148d7b91e88dee7adea6029bdec97af1ef3e8 - languageName: node - linkType: hard - "yargs@npm:16.2.0": version: 16.2.0 resolution: "yargs@npm:16.2.0"