diff --git a/.env.example b/.env.example index 6bb663b..eed0a9a 100644 --- a/.env.example +++ b/.env.example @@ -1,22 +1,11 @@ # Network and account info RPC_URL_MAINNET=XXX RPC_URL_GOERLI=XXX +BSC_RPC_URL=XXX +OPTIMISM_RPC_URL=XXX PRIVATE_KEY=XXX ETHERSCAN_KEY=XXX -# Deploy configs -VERSION="1.0.0" -OWNER=XXX -TREASURY=XXX -BALANCER_POOL=XXX # 20-80 WETH-LIT pool -ORACLE_MULTIPLIER=XXX # 4 decimals -ORACLE_SECS=XXX -ORACLE_AGO=XXX -ORACLE_MIN_PRICE=XXX # 18 decimals - -OT_NAME="LIT Call Option Token" -OT_SYMBOL="oLIT" -OT_PAYMENT_TOKEN=0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2 # WETH \ No newline at end of file diff --git a/.gitignore b/.gitignore index 3269660..1cdc36d 100644 --- a/.gitignore +++ b/.gitignore @@ -9,3 +9,12 @@ out/ # Dotenv file .env + +# Hardhat +node_modules/ +artifacts/ +cache_hardhat/ +typechain-types/ + +*settings.json + diff --git a/.gitmodules b/.gitmodules index 5e0106b..75229a8 100644 --- a/.gitmodules +++ b/.gitmodules @@ -1,9 +1,23 @@ [submodule "lib/forge-std"] - path = lib/forge-std - url = https://github.com/foundry-rs/forge-std +path = lib/forge-std +url = https://github.com/foundry-rs/forge-std [submodule "lib/solmate"] - path = lib/solmate - url = https://github.com/rari-capital/solmate +path = lib/solmate +url = https://github.com/rari-capital/solmate [submodule "lib/create3-factory"] - path = lib/create3-factory - url = https://github.com/zeframlou/create3-factory +path = lib/create3-factory +url = https://github.com/zeframlou/create3-factory +[submodule "lib/v3-core"] +path = lib/v3-core +url = https://github.com/uniswap/v3-core +[submodule "lib/openzeppelin-contracts-upgradeable"] +path = lib/openzeppelin-contracts-upgradeable +url = https://github.com/OpenZeppelin/openzeppelin-contracts-upgradeable +branch = v4.9.6 +[submodule "lib/openzeppelin-contracts"] +path = lib/openzeppelin-contracts +url = https://github.com/OpenZeppelin/openzeppelin-contracts +branch = v4.9.6 +[submodule "lib/vault-v2"] +path = lib/vault-v2 +url = https://github.com/Byte-Masons/vault-v2 \ No newline at end of file diff --git a/.openzeppelin/bsc.json b/.openzeppelin/bsc.json new file mode 100644 index 0000000..7320b61 --- /dev/null +++ b/.openzeppelin/bsc.json @@ -0,0 +1,205 @@ +{ + "manifestVersion": "3.2", + "proxies": [ + { + "address": "0x45c19a3068642B98F5AEf1dEdE023443cd1FbFAd", + "txHash": "0xb0835bbacd616b635b6a1230e89a7d4f47fa5a4c55969a510d92b87d9b1367eb", + "kind": "uups" + } + ], + "impls": { + "d568e812a589f8a8e8864a13f1ba9c178a5bf812de48e4764db703f702d1ec44": { + "address": "0xc79593C528d673f5Db0B8707115193FE1596aB34", + "txHash": "0xb0107c79caa671945a41ee8e6e6300a08081285e10d0e56233c7db3023df32b3", + "layout": { + "solcVersion": "0.8.19", + "storage": [ + { + "label": "_initialized", + "offset": 0, + "slot": "0", + "type": "t_uint8", + "contract": "Initializable", + "src": "oz-upgradeable/proxy/utils/Initializable.sol:62", + "retypedFrom": "bool" + }, + { + "label": "_initializing", + "offset": 1, + "slot": "0", + "type": "t_bool", + "contract": "Initializable", + "src": "oz-upgradeable/proxy/utils/Initializable.sol:67" + }, + { + "label": "__gap", + "offset": 0, + "slot": "1", + "type": "t_array(t_uint256)50_storage", + "contract": "ContextUpgradeable", + "src": "oz-upgradeable/utils/ContextUpgradeable.sol:36" + }, + { + "label": "_balances", + "offset": 0, + "slot": "51", + "type": "t_mapping(t_address,t_uint256)", + "contract": "ERC20Upgradeable", + "src": "oz-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": "oz-upgradeable/token/ERC20/ERC20Upgradeable.sol:39" + }, + { + "label": "_totalSupply", + "offset": 0, + "slot": "53", + "type": "t_uint256", + "contract": "ERC20Upgradeable", + "src": "oz-upgradeable/token/ERC20/ERC20Upgradeable.sol:41" + }, + { + "label": "_name", + "offset": 0, + "slot": "54", + "type": "t_string_storage", + "contract": "ERC20Upgradeable", + "src": "oz-upgradeable/token/ERC20/ERC20Upgradeable.sol:43" + }, + { + "label": "_symbol", + "offset": 0, + "slot": "55", + "type": "t_string_storage", + "contract": "ERC20Upgradeable", + "src": "oz-upgradeable/token/ERC20/ERC20Upgradeable.sol:44" + }, + { + "label": "__gap", + "offset": 0, + "slot": "56", + "type": "t_array(t_uint256)45_storage", + "contract": "ERC20Upgradeable", + "src": "oz-upgradeable/token/ERC20/ERC20Upgradeable.sol:400" + }, + { + "label": "_owner", + "offset": 0, + "slot": "101", + "type": "t_address", + "contract": "OwnableUpgradeable", + "src": "oz-upgradeable/access/OwnableUpgradeable.sol:22" + }, + { + "label": "__gap", + "offset": 0, + "slot": "102", + "type": "t_array(t_uint256)49_storage", + "contract": "OwnableUpgradeable", + "src": "oz-upgradeable/access/OwnableUpgradeable.sol:94" + }, + { + "label": "__gap", + "offset": 0, + "slot": "151", + "type": "t_array(t_uint256)50_storage", + "contract": "ERC1967UpgradeUpgradeable", + "src": "oz-upgradeable/proxy/ERC1967/ERC1967UpgradeUpgradeable.sol:197" + }, + { + "label": "__gap", + "offset": 0, + "slot": "201", + "type": "t_array(t_uint256)50_storage", + "contract": "UUPSUpgradeable", + "src": "oz-upgradeable/proxy/utils/UUPSUpgradeable.sol:107" + }, + { + "label": "tokenAdmin", + "offset": 0, + "slot": "251", + "type": "t_address", + "contract": "OptionsToken", + "src": "src/OptionsToken.sol:45" + }, + { + "label": "isExerciseContract", + "offset": 0, + "slot": "252", + "type": "t_mapping(t_address,t_bool)", + "contract": "OptionsToken", + "src": "src/OptionsToken.sol:47" + }, + { + "label": "upgradeProposalTime", + "offset": 0, + "slot": "253", + "type": "t_uint256", + "contract": "OptionsToken", + "src": "src/OptionsToken.sol:48" + }, + { + "label": "nextImplementation", + "offset": 0, + "slot": "254", + "type": "t_address", + "contract": "OptionsToken", + "src": "src/OptionsToken.sol:49" + } + ], + "types": { + "t_address": { + "label": "address", + "numberOfBytes": "20" + }, + "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_mapping(t_address,t_bool)": { + "label": "mapping(address => bool)", + "numberOfBytes": "32" + }, + "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_string_storage": { + "label": "string", + "numberOfBytes": "32" + }, + "t_uint256": { + "label": "uint256", + "numberOfBytes": "32" + }, + "t_uint8": { + "label": "uint8", + "numberOfBytes": "1" + } + }, + "namespaces": {} + } + } + } +} diff --git a/README.md b/README.md index ba1fdbf..205bca8 100644 --- a/README.md +++ b/README.md @@ -1,9 +1,69 @@ -# OptionsToken +# **Table of content** +- [Description](#description) + - [OptionsToken](#optionstoken) + - [OptionsCompounder](#optionscompounder) +- [Installation](#installation) + - [Local Development](#local-development) +- [Testing](#testing) + - [Dynamic](#dynamic) + - [Static](#static) +- [Deployment](#deployment) +- [Checklist](#checklist) +- [Frontend integration](#frontend-integration) -An options token representing the right to purchase the underlying token at an oracle-specified rate. It's similar to a call option but with a variable strike price that's always at a certain discount to the market price. -It also has no expiry date. -## Installation + +# Description +## OptionsToken + +An options token representing the right to exercise any one of the whitelisted exercise contracts, allowing the user to receive different forms of discounted assets in return for the appropriate payment. The option does not expire. The options token receives user input and a specified exercise contract address, passing through to the exercise contract to execute the option. We fork https://github.com/timeless-fi/options-token, which is a simple implementation of an option for discounted tokens at an adjusted oracle rate. Here, we divorce the exercise functionality from the token contract, and allow an admin to whitelist and fund exercise contracts as the desired. We also implement more potential oracle types, and make several other minor changes. + +We want to ensure there are no attacks on pricing in DiscountExercise, atomically or otherwise, in each oracle implementation. We want to ensure users will never pay more than maxPaymentAmount. When properly specified, this should ensure users experience no more deviation in price than they specify. + +Given the nature of this token, it is fine for the admin to have some centralized permissions (admin can mint tokens, admin is the one who funds exercise contracts, etc). The team is responsible for refilling the exercise contracts. We limit the amount of funds we leave in an exercise contract at any given time to limit risk. + +### Flow of an Option Token Exercise (Ex. Discount Exercise) + +The user will always interact with the OptionsToken itself, and never with any exercise contract directly. + +#### ZAP +1. User calls exercise on the OptionsToken, specifying their desired exercise contract and encoding exercise parameters +2. OptionsToken validates the exercise contract. +3. DiscountExercise decodes the parameters for the exercise function on the chosen exercise contract, and calls the specified function. In the case of zapping in DiscountExercise, the parameters are maxPaymentAmount, deadline, and isInstantExit set to true. +4. OptionsTokens are burnt. +5. A penalty fee in the form of underlying tokens (available in the contract) is calculated, then conditionally swapped to the desired token and distributed to the fee recipients. + - Swapping and distribution occur only when the fee amount exceeds a specified trigger to avoid swapping small amounts. + - The transaction reverts if the minimal desired amount of desired tokens is not obtained. +6. The underlying tokens available in the DiscountExercise contract are sent to the user. The amount of underlying tokens is discounted by the multiplier and reduced by the penalty fee. + +#### REDEEM +1. The user approves OptionsToken the amount of Payment Token they wish to spend +2. User calls exercise on the OptionsToken, specifying their desired exercise contract and encoding exercise parameters +3. OptionsToken validates the exercise contract, decodes the parameters for the exercise function on the exercise contract of choice, and calls said function. In the case of DiscountExercise, the params are maxPaymentAmount, deadline and isInstantExit set to false. +4. oTokens are burnt, WETH is sent to the treasury, and underlyingTokens, discounted by the multiplier, are sent to the user exercising + - Can be priced using balancer, thena, univ3 twap oracles + - Reverts above maxPaymentAmount or past deadline + +## OptionsCompounder + +The Compounder platform facilitates the utilization of flash loans to exercise the option, enabling the acquisition of the underlying token at a discounted rate via payment token. + +### Flow of an Options Compounder (Ex. Discount Exercise) - strategy usage + +1. Admin configures swap paths, oracles, initializer args, etc +2. Strategy has an oToken balance +3. Keeper calls harvestOTokens +4. Calculate Payment Amount from Discount Exercise given oToken balance +5. Flashloan the necessary amount of funds to exercise in paymentToken +6. Callback from flashloan is called + - oTokens are exercised using paymentToken that was flash loaned + - Underlying token is received by the strategy + - Calculate minAmountOut by directly querying the same oracle consumed by the DiscountExercise we interact with + - Swap entire amount into payment token to repay flashloan + - Assess profitability in units of paymentToken, swap profits to want of the strategy if not same token as paymentToken + - Emit event that reflects the oTokens compounded + +# Installation To install with [DappTools](https://github.com/dapphub/dapptools): @@ -33,8 +93,1716 @@ forge install forge build ``` -### Testing +# Testing + +## Dynamic ``` forge test -``` \ No newline at end of file +``` + +`--report lcov` - coverage which can be turned on in code using "Coverage Gutters" + +## Static + +`slither . --include-path src/` + +`--checklist` - report in md + +`--print inheritance-graph` - generate inheritance graph in xdot + +`xdot inheritance-graph.dot` - open inheritance graph + +# Deployment + +Inside `./scripts` there is "config.json" where can be defined deployment configurations. +You can choose which contract to deploy by adding/removing string in CONTRACTS_TO_DEPLOY. If some contracts are removed from CONTRACTS_TO_DEPLOY, there must be defined the address for already existing contract on the chain (example: SWAPPER, OPTIONS_COMPOUNDER). +There are 2 deployment scripts. One is for swapper and paths updates and second for all optionsToken infra (swapper address must be passed here). + +## Examples of config.json +### All contracts to deploy +``` +{ + "VERSION": "1.1.0", + "OWNER": , + "ORACLE_SOURCE": , + "ORACLE_SECS": 1800, + "ORACLE_MIN_PRICE": 10000000, + "OT_NAME": "ICL Call Option Token", + "OT_SYMBOL": "oICL", + "OT_PAYMENT_TOKEN": "0xDfc7C877a950e49D2610114102175A06C2e3167a", + "OT_UNDERLYING_TOKEN": "0x95177295a394f2b9b04545fff58f4af0673e839d", + "OT_TOKEN_ADMIN": "0xF29dA3595351dBFd0D647857C46F8D63Fc2e68C5", + "VELO_ROUTER": "0x3a63171DD9BebF4D07BC782FECC7eb0b890C2A45", + "ADDRESS_PROVIDER": "0xEDc83309549e36f3c7FD8c2C5C54B4c8e5FA00FC", + "MULTIPLIER": 5000, + "FEE_RECIPIENTS": [ + + ], + "FEE_BPS": [ + 10000 + ], + "STRATS": [ + + ], + "INSTANT_EXIT_FEE": 1000, + "MIN_AMOUNT_TO_TRIGGER_SWAP": 1e15, + "CONTRACTS_TO_DEPLOY": [], + "SWAPPER": "0x63D170618A8Ed1987F3CA6391b5e2F6a4554Cf53", + "DISCOUNT_EXERCISE": "0xcb727532e24dFe22E74D3892b998f5e915676Da8", + "OPTIONS_TOKEN": "0x3B6eA0fA8A487c90007ce120a83920fd52b06f6D", + "OPTIONS_COMPOUNDER": "0xf6cf2065C35595c12B532e54ACDe5A4597e32e6e", + "ORACLE": "0xDaA2c821428f62e1B08009a69CE824253CCEE5f9" +} +``` +### Only configure options compounder (no deployments) +``` +{ + "VERSION": "1.1.0", + "OWNER": , + "ORACLE_SOURCE": , + "ORACLE_SECS": 1800, + "ORACLE_MIN_PRICE": 10000000, + "OT_NAME": "ICL Call Option Token", + "OT_SYMBOL": "oICL", + "OT_PAYMENT_TOKEN": "0xDfc7C877a950e49D2610114102175A06C2e3167a", + "OT_UNDERLYING_TOKEN": "0x95177295a394f2b9b04545fff58f4af0673e839d", + "OT_TOKEN_ADMIN": "0xF29dA3595351dBFd0D647857C46F8D63Fc2e68C5", + "VELO_ROUTER": "0x3a63171DD9BebF4D07BC782FECC7eb0b890C2A45", + "ADDRESS_PROVIDER": "0xEDc83309549e36f3c7FD8c2C5C54B4c8e5FA00FC", + "MULTIPLIER": 5000, + "FEE_RECIPIENTS": [ + + ], + "FEE_BPS": [ + 10000 + ], + "STRATS": [ + + ], + "INSTANT_EXIT_FEE": 1000, + "MIN_AMOUNT_TO_TRIGGER_SWAP": 1e15, + "CONTRACTS_TO_DEPLOY": [ + "OptionsToken", + "DiscountExercise", + "ThenaOracle" + ], + "SWAPPER": "0x63D170618A8Ed1987F3CA6391b5e2F6a4554Cf53", + "DISCOUNT_EXERCISE": "undefined", + "OPTIONS_TOKEN": "undefined", + "OPTIONS_COMPOUNDER": "undefined", + "ORACLE": "undefined" +} +``` + +# Checklist + +## Internal Audit Checklist + +- [x] All functionality that touches funds can be paused +- [ ] Pause function called by 2/7 Guardian +- [ ] Guardian has 7 members globally dispersed +- [x] Arithmetic errors +- [x] Re-entrancy +- [x] Flashloans +- [x] Access Control + +- [x] (N/A) Unchecked External Calls +- [x] (N/A) Account abstraction/multicall issues +- [x] Static analysis -> Slither + - [x] [Discount-Exercise](#discount-exercise-slither) + - [x] [Options-Compounder](#options-compounder-slither) + - [x] [Options-Token](#options-token-slither) + - [x] [Thena-Oracle](#thena-oracle-slither) + - [x] [Base-Exercise](#base-exercise-slither) + +## Pre-deployment Checklist + +- [x] Contracts pass all tests +- [x] Contracts deployed to testnet + - [x] [DiscountExercise](https://explorer.mode.network/address/0xcb727532e24dFe22E74D3892b998f5e915676Da8?tab=contract) + - [x] [ReaperSwapper](https://explorer.mode.network/address/0x63D170618A8Ed1987F3CA6391b5e2F6a4554Cf53?tab=contract) + - [x] [VeloOracle](https://explorer.mode.network/address/0xDaA2c821428f62e1B08009a69CE824253CCEE5f9?tab=contract) + - [x] [OptionsToken](https://explorer.mode.network/address/0x3B6eA0fA8A487c90007ce120a83920fd52b06f6D?tab=contract) + - [ ] [OptionsCompounder](https://explorer.mode.network/address/0xf6cf2065C35595c12B532e54ACDe5A4597e32e6e?tab=contract) +- [x] Unchecked External Calls +- [ ] Account abstraction/multicall issues +- [x] USE SLITHER + +- [x] Does this deployment have access to funds, either directly or indirectly (zappers, leveragers, etc.)? + +Minimum security if Yes: + +- [x] Internal Audit (not the author, minimum 1x Junior review + minimum 1x Senior review) +- [x] External Audit (impact scope) + - [x] [OptionsToken zapping feature scope](https://docs.google.com/document/d/1HrmXSEKuBK5U9Ix8ZSkAYf2VEYwxZ17piOjhjY4XPzs/edit?usp=drive_link) + - [x] [OptionsToken zapping feature audit](https://drive.google.com/file/d/1kbYnVN1HJrpllkMXXb4mmwLeU361YASG/view?usp=drive_link) + - [x] [OptionsCompounder integration scope](https://docs.google.com/document/d/1eKcyiVvmux2wv2P92qLQLSYIgbD-qETurHObyhOLh8Y/edit?usp=drive_link) + - [x] [OptionsCompounder integration audit](https://drive.google.com/file/d/1GR0Jnxo9Txa6sJ8aM_mP4xBfaBJ9UNG2/view) + +Action items in support of deployment: + +- [ ] Minimum two people present for deployment +- [x] All developers who worked on and reviewed the contract should be included in the readme + - Developers involved: xRave110 (change owner), Eidolon (reviewer), Zokunei (reviewer), Goober (reviewer), Beirao (reviewer) +- [ ] Documentation of deployment procedure if non-standard (i.e. if multiple scripts are necessary) + +## Discount Exercise Slither + +Summary + - [reentrancy-no-eth](#reentrancy-no-eth) (1 results) (Medium) + - [unused-return](#unused-return) (2 results) (Medium) + - [reentrancy-benign](#reentrancy-benign) (1 results) (Low) + - [reentrancy-events](#reentrancy-events) (4 results) (Low) + - [timestamp](#timestamp) (2 results) (Low) + - [pragma](#pragma) (1 results) (Informational) + - [solc-version](#solc-version) (1 results) (Informational) + - [naming-convention](#naming-convention) (3 results) (Informational) + - [unused-import](#unused-import) (2 results) (Informational) +## reentrancy-no-eth +Impact: Medium +Confidence: Medium + - [ ] ID-0 +Reentrancy in [DiscountExercise._zap(address,uint256,address,DiscountExerciseParams)](src/exercise/DiscountExercise.sol#L211-L249): + External calls: + - [underlyingToken.approve(swapProps.swapper,feeAmount)](src/exercise/DiscountExercise.sol#L228) + - [amountOut = _generalSwap(swapProps.exchangeTypes,address(underlyingToken),address(paymentToken),feeAmount,minAmountOut,swapProps.exchangeAddress)](src/exercise/DiscountExercise.sol#L230-L232) + - [_swapper.swapUniV2(tokenIn,tokenOut,amount,minAmountOutData,exchangeAddress)](src/helpers/SwapHelper.sol#L72) + - [_swapper.swapBal(tokenIn,tokenOut,amount,minAmountOutData,exchangeAddress)](src/helpers/SwapHelper.sol#L74) + - [_swapper.swapVelo(tokenIn,tokenOut,amount,minAmountOutData,exchangeAddress)](src/helpers/SwapHelper.sol#L76) + - [_swapper.swapUniV3(tokenIn,tokenOut,amount,minAmountOutData,exchangeAddress)](src/helpers/SwapHelper.sol#L78) + State variables written after the call(s): + - [feeAmount = 0](src/exercise/DiscountExercise.sol#L238) + [DiscountExercise.feeAmount](src/exercise/DiscountExercise.sol#L74) can be used in cross function reentrancies: + - [DiscountExercise._zap(address,uint256,address,DiscountExerciseParams)](src/exercise/DiscountExercise.sol#L211-L249) + +src/exercise/DiscountExercise.sol#L211-L249 + +Justification: External call is happening to well known dexes which are verified against any reentrancy attacks but fix may be implemented using additional temporary variable. + +## unused-return +Impact: Medium +Confidence: Medium + - [ ] ID-1 +[DiscountExercise._zap(address,uint256,address,DiscountExerciseParams)](src/exercise/DiscountExercise.sol#L211-L249) ignores return value by [underlyingToken.approve(swapProps.swapper,feeAmount)](src/exercise/DiscountExercise.sol#L228) + +src/exercise/DiscountExercise.sol#L211-L249 + + + - [ ] ID-2 +[DiscountExercise._zap(address,uint256,address,DiscountExerciseParams)](src/exercise/DiscountExercise.sol#L211-L249) ignores return value by [underlyingToken.approve(swapProps.swapper,0)](src/exercise/DiscountExercise.sol#L239) + +src/exercise/DiscountExercise.sol#L211-L249 + +Justification: It is just potential DOS which is very unlikely. + +## reentrancy-benign +Impact: Low +Confidence: Medium + - [ ] ID-3 +Reentrancy in [DiscountExercise._pay(address,uint256)](src/exercise/DiscountExercise.sol#L269-L278): + External calls: + - [underlyingToken.safeTransfer(to,balance)](src/exercise/DiscountExercise.sol#L272) + - [underlyingToken.safeTransfer(to,amount)](src/exercise/DiscountExercise.sol#L275) + State variables written after the call(s): + - [credit[to] += remainingAmount](src/exercise/DiscountExercise.sol#L277) + +src/exercise/DiscountExercise.sol#L269-L278 + +Justification: Tokens are set by the addresses with special access role who knows that onReceiveErc20 hook might lead to the potential reentrancy attack. + +## reentrancy-events +Impact: Low +Confidence: Medium + - [ ] ID-4 +Reentrancy in [DiscountExercise._zap(address,uint256,address,DiscountExerciseParams)](src/exercise/DiscountExercise.sol#L211-L249): + External calls: + - [underlyingToken.approve(swapProps.swapper,feeAmount)](src/exercise/DiscountExercise.sol#L228) + - [amountOut = _generalSwap(swapProps.exchangeTypes,address(underlyingToken),address(paymentToken),feeAmount,minAmountOut,swapProps.exchangeAddress)](src/exercise/DiscountExercise.sol#L230-L232) + - [_swapper.swapUniV2(tokenIn,tokenOut,amount,minAmountOutData,exchangeAddress)](src/helpers/SwapHelper.sol#L72) + - [_swapper.swapBal(tokenIn,tokenOut,amount,minAmountOutData,exchangeAddress)](src/helpers/SwapHelper.sol#L74) + - [_swapper.swapVelo(tokenIn,tokenOut,amount,minAmountOutData,exchangeAddress)](src/helpers/SwapHelper.sol#L76) + - [_swapper.swapUniV3(tokenIn,tokenOut,amount,minAmountOutData,exchangeAddress)](src/helpers/SwapHelper.sol#L78) + - [underlyingToken.approve(swapProps.swapper,0)](src/exercise/DiscountExercise.sol#L239) + - [distributeFees(paymentToken.balanceOf(address(this)),paymentToken)](src/exercise/DiscountExercise.sol#L242) + - [returndata = address(token).functionCall(data,SafeERC20: low-level call failed)](lib/openzeppelin-contracts/contracts/token/ERC20/utils/SafeERC20.sol#L110) + - [(success,returndata) = target.call{value: value}(data)](lib/openzeppelin-contracts/contracts/utils/Address.sol#L135) + - [token.safeTransfer(feeRecipients[i],feeAmount)](src/exercise/BaseExercise.sol#L90) + - [token.safeTransfer(feeRecipients[feeRecipients.length - 1],remaining)](src/exercise/BaseExercise.sol#L93) + - [_pay(recipient,underlyingAmount)](src/exercise/DiscountExercise.sol#L246) + - [returndata = address(token).functionCall(data,SafeERC20: low-level call failed)](lib/openzeppelin-contracts/contracts/token/ERC20/utils/SafeERC20.sol#L110) + - [underlyingToken.safeTransfer(to,balance)](src/exercise/DiscountExercise.sol#L272) + - [(success,returndata) = target.call{value: value}(data)](lib/openzeppelin-contracts/contracts/utils/Address.sol#L135) + - [underlyingToken.safeTransfer(to,amount)](src/exercise/DiscountExercise.sol#L275) + External calls sending eth: + - [distributeFees(paymentToken.balanceOf(address(this)),paymentToken)](src/exercise/DiscountExercise.sol#L242) + - [(success,returndata) = target.call{value: value}(data)](lib/openzeppelin-contracts/contracts/utils/Address.sol#L135) + - [_pay(recipient,underlyingAmount)](src/exercise/DiscountExercise.sol#L246) + - [(success,returndata) = target.call{value: value}(data)](lib/openzeppelin-contracts/contracts/utils/Address.sol#L135) + Event emitted after the call(s): + - [Exercised(from,recipient,underlyingAmount,paymentAmount)](src/exercise/DiscountExercise.sol#L248) + +src/exercise/DiscountExercise.sol#L211-L249 + + + - [ ] ID-5 +Reentrancy in [DiscountExercise.claim(address)](src/exercise/DiscountExercise.sol#L134-L140): + External calls: + - [underlyingToken.safeTransfer(to,amount)](src/exercise/DiscountExercise.sol#L138) + Event emitted after the call(s): + - [Claimed(amount)](src/exercise/DiscountExercise.sol#L139) + +src/exercise/DiscountExercise.sol#L134-L140 + + + - [ ] ID-6 +Reentrancy in [DiscountExercise._zap(address,uint256,address,DiscountExerciseParams)](src/exercise/DiscountExercise.sol#L211-L249): + External calls: + - [underlyingToken.approve(swapProps.swapper,feeAmount)](src/exercise/DiscountExercise.sol#L228) + - [amountOut = _generalSwap(swapProps.exchangeTypes,address(underlyingToken),address(paymentToken),feeAmount,minAmountOut,swapProps.exchangeAddress)](src/exercise/DiscountExercise.sol#L230-L232) + - [_swapper.swapUniV2(tokenIn,tokenOut,amount,minAmountOutData,exchangeAddress)](src/helpers/SwapHelper.sol#L72) + - [_swapper.swapBal(tokenIn,tokenOut,amount,minAmountOutData,exchangeAddress)](src/helpers/SwapHelper.sol#L74) + - [_swapper.swapVelo(tokenIn,tokenOut,amount,minAmountOutData,exchangeAddress)](src/helpers/SwapHelper.sol#L76) + - [_swapper.swapUniV3(tokenIn,tokenOut,amount,minAmountOutData,exchangeAddress)](src/helpers/SwapHelper.sol#L78) + - [underlyingToken.approve(swapProps.swapper,0)](src/exercise/DiscountExercise.sol#L239) + - [distributeFees(paymentToken.balanceOf(address(this)),paymentToken)](src/exercise/DiscountExercise.sol#L242) + - [returndata = address(token).functionCall(data,SafeERC20: low-level call failed)](lib/openzeppelin-contracts/contracts/token/ERC20/utils/SafeERC20.sol#L110) + - [(success,returndata) = target.call{value: value}(data)](lib/openzeppelin-contracts/contracts/utils/Address.sol#L135) + - [token.safeTransfer(feeRecipients[i],feeAmount)](src/exercise/BaseExercise.sol#L90) + - [token.safeTransfer(feeRecipients[feeRecipients.length - 1],remaining)](src/exercise/BaseExercise.sol#L93) + External calls sending eth: + - [distributeFees(paymentToken.balanceOf(address(this)),paymentToken)](src/exercise/DiscountExercise.sol#L242) + - [(success,returndata) = target.call{value: value}(data)](lib/openzeppelin-contracts/contracts/utils/Address.sol#L135) + Event emitted after the call(s): + - [DistributeFees(feeRecipients,feeBPS,totalAmount)](src/exercise/BaseExercise.sol#L94) + - [distributeFees(paymentToken.balanceOf(address(this)),paymentToken)](src/exercise/DiscountExercise.sol#L242) + +src/exercise/DiscountExercise.sol#L211-L249 + + + - [ ] ID-7 +Reentrancy in [DiscountExercise._redeem(address,uint256,address,DiscountExerciseParams)](src/exercise/DiscountExercise.sol#L252-L267): + External calls: + - [distributeFeesFrom(paymentAmount,paymentToken,from)](src/exercise/DiscountExercise.sol#L262) + - [returndata = address(token).functionCall(data,SafeERC20: low-level call failed)](lib/openzeppelin-contracts/contracts/token/ERC20/utils/SafeERC20.sol#L110) + - [(success,returndata) = target.call{value: value}(data)](lib/openzeppelin-contracts/contracts/utils/Address.sol#L135) + - [token.safeTransferFrom(from,feeRecipients[i],feeAmount)](src/exercise/BaseExercise.sol#L77) + - [token.safeTransferFrom(from,feeRecipients[feeRecipients.length - 1],remaining)](src/exercise/BaseExercise.sol#L80) + - [_pay(recipient,amount)](src/exercise/DiscountExercise.sol#L264) + - [returndata = address(token).functionCall(data,SafeERC20: low-level call failed)](lib/openzeppelin-contracts/contracts/token/ERC20/utils/SafeERC20.sol#L110) + - [underlyingToken.safeTransfer(to,balance)](src/exercise/DiscountExercise.sol#L272) + - [(success,returndata) = target.call{value: value}(data)](lib/openzeppelin-contracts/contracts/utils/Address.sol#L135) + - [underlyingToken.safeTransfer(to,amount)](src/exercise/DiscountExercise.sol#L275) + External calls sending eth: + - [distributeFeesFrom(paymentAmount,paymentToken,from)](src/exercise/DiscountExercise.sol#L262) + - [(success,returndata) = target.call{value: value}(data)](lib/openzeppelin-contracts/contracts/utils/Address.sol#L135) + - [_pay(recipient,amount)](src/exercise/DiscountExercise.sol#L264) + - [(success,returndata) = target.call{value: value}(data)](lib/openzeppelin-contracts/contracts/utils/Address.sol#L135) + Event emitted after the call(s): + - [Exercised(from,recipient,amount,paymentAmount)](src/exercise/DiscountExercise.sol#L266) + +src/exercise/DiscountExercise.sol#L252-L267 + + +## timestamp +Impact: Low +Confidence: Medium + - [ ] ID-8 +[DiscountExercise._redeem(address,uint256,address,DiscountExerciseParams)](src/exercise/DiscountExercise.sol#L252-L267) uses timestamp for comparisons + Dangerous comparisons: + - [block.timestamp > params.deadline](src/exercise/DiscountExercise.sol#L257) + +src/exercise/DiscountExercise.sol#L252-L267 + + + - [ ] ID-9 +[DiscountExercise._zap(address,uint256,address,DiscountExerciseParams)](src/exercise/DiscountExercise.sol#L211-L249) uses timestamp for comparisons + Dangerous comparisons: + - [block.timestamp > params.deadline](src/exercise/DiscountExercise.sol#L215) + +src/exercise/DiscountExercise.sol#L211-L249 + + +## pragma +Impact: Informational +Confidence: High + - [ ] ID-10 +11 different versions of Solidity are used: + - Version constraint ^0.8.0 is used by: + -[^0.8.0](lib/openzeppelin-contracts-upgradeable/contracts/access/AccessControlEnumerableUpgradeable.sol#L4) + -[^0.8.0](lib/openzeppelin-contracts-upgradeable/contracts/access/AccessControlUpgradeable.sol#L4) + -[^0.8.0](lib/openzeppelin-contracts-upgradeable/contracts/access/IAccessControlEnumerableUpgradeable.sol#L4) + -[^0.8.0](lib/openzeppelin-contracts-upgradeable/contracts/access/IAccessControlUpgradeable.sol#L4) + -[^0.8.0](lib/openzeppelin-contracts-upgradeable/contracts/access/OwnableUpgradeable.sol#L4) + -[^0.8.0](lib/openzeppelin-contracts-upgradeable/contracts/interfaces/IERC1967Upgradeable.sol#L4) + -[^0.8.0](lib/openzeppelin-contracts-upgradeable/contracts/interfaces/draft-IERC1822Upgradeable.sol#L4) + -[^0.8.0](lib/openzeppelin-contracts-upgradeable/contracts/proxy/beacon/IBeaconUpgradeable.sol#L4) + -[^0.8.0](lib/openzeppelin-contracts-upgradeable/contracts/proxy/utils/UUPSUpgradeable.sol#L4) + -[^0.8.0](lib/openzeppelin-contracts-upgradeable/contracts/security/PausableUpgradeable.sol#L4) + -[^0.8.0](lib/openzeppelin-contracts-upgradeable/contracts/token/ERC20/ERC20Upgradeable.sol#L4) + -[^0.8.0](lib/openzeppelin-contracts-upgradeable/contracts/token/ERC20/IERC20Upgradeable.sol#L4) + -[^0.8.0](lib/openzeppelin-contracts-upgradeable/contracts/token/ERC20/extensions/IERC20MetadataUpgradeable.sol#L4) + -[^0.8.0](lib/openzeppelin-contracts-upgradeable/contracts/token/ERC20/extensions/draft-IERC20PermitUpgradeable.sol#L4) + -[^0.8.0](lib/openzeppelin-contracts-upgradeable/contracts/token/ERC20/utils/SafeERC20Upgradeable.sol#L4) + -[^0.8.0](lib/openzeppelin-contracts-upgradeable/contracts/utils/ContextUpgradeable.sol#L4) + -[^0.8.0](lib/openzeppelin-contracts-upgradeable/contracts/utils/StorageSlotUpgradeable.sol#L4) + -[^0.8.0](lib/openzeppelin-contracts-upgradeable/contracts/utils/StringsUpgradeable.sol#L4) + -[^0.8.0](lib/openzeppelin-contracts-upgradeable/contracts/utils/introspection/ERC165Upgradeable.sol#L4) + -[^0.8.0](lib/openzeppelin-contracts-upgradeable/contracts/utils/introspection/IERC165Upgradeable.sol#L4) + -[^0.8.0](lib/openzeppelin-contracts-upgradeable/contracts/utils/math/MathUpgradeable.sol#L4) + -[^0.8.0](lib/openzeppelin-contracts-upgradeable/contracts/utils/structs/EnumerableSetUpgradeable.sol#L5) + -[^0.8.0](lib/openzeppelin-contracts/contracts/security/Pausable.sol#L4) + -[^0.8.0](lib/openzeppelin-contracts/contracts/token/ERC20/IERC20.sol#L4) + -[^0.8.0](lib/openzeppelin-contracts/contracts/token/ERC20/extensions/draft-IERC20Permit.sol#L4) + -[^0.8.0](lib/openzeppelin-contracts/contracts/token/ERC20/utils/SafeERC20.sol#L4) + -[^0.8.0](lib/openzeppelin-contracts/contracts/utils/Context.sol#L4) + -[^0.8.0](lib/v3-core/contracts/libraries/FullMath.sol#L2) + -[^0.8.0](lib/v3-core/contracts/libraries/TickMath.sol#L2) + -[^0.8.0](lib/vault-v2/src/ReaperSwapper.sol#L3) + -[^0.8.0](lib/vault-v2/src/interfaces/AggregatorV3Interface.sol#L4) + -[^0.8.0](lib/vault-v2/src/interfaces/IAsset.sol#L3) + -[^0.8.0](lib/vault-v2/src/interfaces/IAuthorizer.sol#L3) + -[^0.8.0](lib/vault-v2/src/interfaces/IBasePool.sol#L3) + -[^0.8.0](lib/vault-v2/src/interfaces/IBaseWeightedPool.sol#L3) + -[^0.8.0](lib/vault-v2/src/interfaces/IBeetVault.sol#L3) + -[^0.8.0](lib/vault-v2/src/interfaces/IPoolSwapStructs.sol#L3) + -[^0.8.0](lib/vault-v2/src/interfaces/ISignaturesValidator.sol#L3) + -[^0.8.0](lib/vault-v2/src/interfaces/ISwapErrors.sol#L3) + -[^0.8.0](lib/vault-v2/src/interfaces/ISwapper.sol#L3) + -[^0.8.0](lib/vault-v2/src/interfaces/ISwapperSwaps.sol#L3) + -[^0.8.0](lib/vault-v2/src/interfaces/ITemporarilyPausable.sol#L3) + -[^0.8.0](lib/vault-v2/src/interfaces/IUniswapV2Router01.sol#L2) + -[^0.8.0](lib/vault-v2/src/interfaces/IVeloPair.sol#L2) + -[^0.8.0](lib/vault-v2/src/interfaces/IVeloRouter.sol#L2) + -[^0.8.0](lib/vault-v2/src/interfaces/IVeloV1AndV2Factory.sol#L2) + -[^0.8.0](lib/vault-v2/src/libraries/Babylonian.sol#L3) + -[^0.8.0](lib/vault-v2/src/libraries/ReaperMathUtils.sol#L3) + -[^0.8.0](lib/vault-v2/src/mixins/BalMixin.sol#L3) + -[^0.8.0](lib/vault-v2/src/mixins/ReaperAccessControl.sol#L5) + -[^0.8.0](lib/vault-v2/src/mixins/UniV2Mixin.sol#L3) + -[^0.8.0](lib/vault-v2/src/mixins/UniV3Mixin.sol#L3) + -[^0.8.0](lib/vault-v2/src/mixins/VeloSolidMixin.sol#L3) + -[^0.8.0](src/OptionsCompounder.sol#L3) + -[^0.8.0](src/interfaces/IBalancerTwapOracle.sol#L15) + -[^0.8.0](src/interfaces/IFlashLoanReceiver.sol#L2) + -[^0.8.0](src/interfaces/ILendingPool.sol#L2) + -[^0.8.0](src/interfaces/ILendingPoolAddressesProvider.sol#L2) + -[^0.8.0](src/interfaces/IOptionsCompounder.sol#L3) + -[^0.8.0](src/interfaces/ISwapperSwaps.sol#L3) + -[^0.8.0](src/libraries/DataTypes.sol#L2) + - Version constraint ^0.8.2 is used by: + -[^0.8.2](lib/openzeppelin-contracts-upgradeable/contracts/proxy/ERC1967/ERC1967UpgradeUpgradeable.sol#L4) + -[^0.8.2](lib/openzeppelin-contracts-upgradeable/contracts/proxy/utils/Initializable.sol#L4) + - Version constraint ^0.8.1 is used by: + -[^0.8.1](lib/openzeppelin-contracts-upgradeable/contracts/utils/AddressUpgradeable.sol#L4) + -[^0.8.1](lib/openzeppelin-contracts/contracts/utils/Address.sol#L4) + - Version constraint >=0.8.0 is used by: + -[>=0.8.0](lib/solmate/src/auth/Owned.sol#L2) + -[>=0.8.0](lib/solmate/src/tokens/ERC20.sol#L2) + -[>=0.8.0](lib/solmate/src/utils/FixedPointMathLib.sol#L2) + - Version constraint >=0.5.0 is used by: + -[>=0.5.0](lib/v3-core/contracts/interfaces/IUniswapV3Pool.sol#L2) + -[>=0.5.0](lib/v3-core/contracts/interfaces/pool/IUniswapV3PoolActions.sol#L2) + -[>=0.5.0](lib/v3-core/contracts/interfaces/pool/IUniswapV3PoolDerivedState.sol#L2) + -[>=0.5.0](lib/v3-core/contracts/interfaces/pool/IUniswapV3PoolErrors.sol#L2) + -[>=0.5.0](lib/v3-core/contracts/interfaces/pool/IUniswapV3PoolEvents.sol#L2) + -[>=0.5.0](lib/v3-core/contracts/interfaces/pool/IUniswapV3PoolImmutables.sol#L2) + -[>=0.5.0](lib/v3-core/contracts/interfaces/pool/IUniswapV3PoolOwnerActions.sol#L2) + -[>=0.5.0](lib/v3-core/contracts/interfaces/pool/IUniswapV3PoolState.sol#L2) + -[>=0.5.0](lib/vault-v2/src/interfaces/IPeripheryImmutableState.sol#L2) + -[>=0.5.0](lib/vault-v2/src/interfaces/IUniswapV3Factory.sol#L2) + -[>=0.5.0](lib/vault-v2/src/interfaces/IUniswapV3SwapCallback.sol#L2) + -[>=0.5.0](src/interfaces/IAlgebraPool.sol#L2) + - Version constraint >=0.7.5 is used by: + -[>=0.7.5](lib/vault-v2/src/interfaces/ISwapRouter.sol#L2) + - Version constraint >=0.6.2 is used by: + -[>=0.6.2](lib/vault-v2/src/interfaces/IUniswapV2Router02.sol#L2) + - Version constraint >=0.6.0 is used by: + -[>=0.6.0](lib/vault-v2/src/libraries/TransferHelper.sol#L2) + - Version constraint ^0.8.13 is used by: + -[^0.8.13](src/OptionsToken.sol#L2) + -[^0.8.13](src/exercise/BaseExercise.sol#L2) + -[^0.8.13](src/exercise/DiscountExercise.sol#L2) + -[^0.8.13](src/helpers/SwapHelper.sol#L3) + -[^0.8.13](src/interfaces/IBalancer2TokensPool.sol#L2) + -[^0.8.13](src/interfaces/IExercise.sol#L2) + -[^0.8.13](src/interfaces/IOptionsToken.sol#L2) + -[^0.8.13](src/oracles/AlgebraOracle.sol#L2) + -[^0.8.13](src/oracles/BalancerOracle.sol#L2) + -[^0.8.13](src/oracles/ThenaOracle.sol#L2) + -[^0.8.13](src/oracles/UniswapV3Oracle.sol#L2) + - Version constraint >=0.7.0<0.9.0 is used by: + -[>=0.7.0<0.9.0](src/interfaces/IBalancerVault.sol#L17) + -[>=0.7.0<0.9.0](src/interfaces/IERC20Mintable.sol#L3) + -[>=0.7.0<0.9.0](src/interfaces/IOracle.sol#L3) + - Version constraint >=0.5 is used by: + -[>=0.5](src/interfaces/IThenaPair.sol#L1) + +lib/openzeppelin-contracts-upgradeable/contracts/access/AccessControlEnumerableUpgradeable.sol#L4 + + +## solc-version +Impact: Informational +Confidence: High + - [ ] ID-11 +Version constraint ^0.8.13 contains known severe issues (https://solidity.readthedocs.io/en/latest/bugs.html) + - VerbatimInvalidDeduplication + - FullInlinerNonExpressionSplitArgumentEvaluationOrder + - MissingSideEffectsOnSelectorAccess + - StorageWriteRemovalBeforeConditionalTermination + - AbiReencodingHeadOverflowWithStaticArrayCleanup + - DirtyBytesArrayToStorage + - InlineAssemblyMemorySideEffects + - DataLocationChangeInInternalOverride + - NestedCalldataArrayAbiReencodingSizeValidation. +It is used by: + - [^0.8.13](src/OptionsToken.sol#L2) + - [^0.8.13](src/exercise/BaseExercise.sol#L2) + - [^0.8.13](src/exercise/DiscountExercise.sol#L2) + - [^0.8.13](src/helpers/SwapHelper.sol#L3) + - [^0.8.13](src/interfaces/IBalancer2TokensPool.sol#L2) + - [^0.8.13](src/interfaces/IExercise.sol#L2) + - [^0.8.13](src/interfaces/IOptionsToken.sol#L2) + - [^0.8.13](src/oracles/AlgebraOracle.sol#L2) + - [^0.8.13](src/oracles/BalancerOracle.sol#L2) + - [^0.8.13](src/oracles/ThenaOracle.sol#L2) + - [^0.8.13](src/oracles/UniswapV3Oracle.sol#L2) + +src/OptionsToken.sol#L2 + + +## naming-convention +Impact: Informational +Confidence: High + - [ ] ID-12 +Parameter [DiscountExercise.setSwapProps(SwapProps)._swapProps](src/exercise/DiscountExercise.sol#L143) is not in mixedCase + +src/exercise/DiscountExercise.sol#L143 + + + - [ ] ID-13 +Parameter [DiscountExercise.setMinAmountToTriggerSwap(uint256)._minAmountToTriggerSwap](src/exercise/DiscountExercise.sol#L193) is not in mixedCase + +src/exercise/DiscountExercise.sol#L193 + + + - [ ] ID-14 +Parameter [DiscountExercise.setInstantExitFee(uint256)._instantExitFee](src/exercise/DiscountExercise.sol#L179) is not in mixedCase + +src/exercise/DiscountExercise.sol#L179 + + +## unused-import +Impact: Informational +Confidence: High + - [ ] ID-15 +The following unused import(s) in src/interfaces/IOptionsCompounder.sol should be removed: + -import {IOptionsToken} from "./IOptionsToken.sol"; (src/interfaces/IOptionsCompounder.sol#5) + + - [ ] ID-16 +The following unused import(s) in src/OptionsCompounder.sol should be removed: + -import {ReaperAccessControl} from "vault-v2/mixins/ReaperAccessControl.sol"; (src/OptionsCompounder.sol#11) + +INFO:Slither:. analyzed (100 contracts with 94 detectors), 17 result(s) found + +## Options Compounder Slither + +Summary + - [arbitrary-send-erc20](#arbitrary-send-erc20) (1 results) (High) + - [reentrancy-eth](#reentrancy-eth) (1 results) (High) + - [incorrect-equality](#incorrect-equality) (1 results) (Medium) + - [unused-return](#unused-return) (6 results) (Medium) + - [missing-zero-check](#missing-zero-check) (1 results) (Low) + - [reentrancy-benign](#reentrancy-benign) (1 results) (Low) + - [reentrancy-events](#reentrancy-events) (1 results) (Low) + - [timestamp](#timestamp) (1 results) (Low) + - [boolean-equal](#boolean-equal) (3 results) (Informational) + - [pragma](#pragma) (1 results) (Informational) + - [solc-version](#solc-version) (1 results) (Informational) + - [missing-inheritance](#missing-inheritance) (1 results) (Informational) + - [naming-convention](#naming-convention) (13 results) (Informational) +## arbitrary-send-erc20 +Impact: High +Confidence: High + - [ ] ID-0 +[OptionsCompounder._exerciseOptionAndReturnDebt(address,uint256,uint256,bytes)](src/OptionsCompounder.sol#L241-L320) uses arbitrary from in transferFrom: [IERC20(address(optionsToken)).safeTransferFrom(flashloanParams.sender,address(this),flashloanParams.optionsAmount)](src/OptionsCompounder.sol#L258) + +src/OptionsCompounder.sol#L241-L320 + + +## reentrancy-eth +Impact: High +Confidence: Medium + - [ ] ID-1 +Reentrancy in [OptionsCompounder.executeOperation(address[],uint256[],uint256[],address,bytes)](src/OptionsCompounder.sol#L214-L230): + External calls: + - [_exerciseOptionAndReturnDebt(assets[0],amounts[0],premiums[0],params)](src/OptionsCompounder.sol#L227) + - [returndata = address(token).functionCall(data,SafeERC20: low-level call failed)](lib/openzeppelin-contracts/contracts/token/ERC20/utils/SafeERC20.sol#L110) + - [(success,returndata) = target.call{value: value}(data)](lib/openzeppelin-contracts/contracts/utils/Address.sol#L135) + - [_swapper.swapUniV2(tokenIn,tokenOut,amount,minAmountOutData,exchangeAddress)](src/helpers/SwapHelper.sol#L72) + - [_swapper.swapBal(tokenIn,tokenOut,amount,minAmountOutData,exchangeAddress)](src/helpers/SwapHelper.sol#L74) + - [_swapper.swapVelo(tokenIn,tokenOut,amount,minAmountOutData,exchangeAddress)](src/helpers/SwapHelper.sol#L76) + - [IERC20(address(optionsToken)).safeTransferFrom(flashloanParams.sender,address(this),flashloanParams.optionsAmount)](src/OptionsCompounder.sol#L258) + - [_swapper.swapUniV3(tokenIn,tokenOut,amount,minAmountOutData,exchangeAddress)](src/helpers/SwapHelper.sol#L78) + - [IERC20(asset).approve(flashloanParams.exerciserContract,amount)](src/OptionsCompounder.sol#L265) + - [optionsToken.exercise(flashloanParams.optionsAmount,address(this),flashloanParams.exerciserContract,exerciseParams)](src/OptionsCompounder.sol#L267) + - [IERC20(asset).approve(flashloanParams.exerciserContract,0)](src/OptionsCompounder.sol#L270) + - [underlyingToken.approve(swapper,balanceOfUnderlyingToken)](src/OptionsCompounder.sol#L280) + - [underlyingToken.approve(swapper,0)](src/OptionsCompounder.sol#L292) + - [IERC20(asset).approve(address(lendingPool),totalAmountToPay)](src/OptionsCompounder.sol#L315) + - [IERC20(asset).safeTransfer(flashloanParams.sender,gainInPaymentToken)](src/OptionsCompounder.sol#L316) + External calls sending eth: + - [_exerciseOptionAndReturnDebt(assets[0],amounts[0],premiums[0],params)](src/OptionsCompounder.sol#L227) + - [(success,returndata) = target.call{value: value}(data)](lib/openzeppelin-contracts/contracts/utils/Address.sol#L135) + State variables written after the call(s): + - [flashloanFinished = true](src/OptionsCompounder.sol#L228) + [OptionsCompounder.flashloanFinished](src/OptionsCompounder.sol#L48) can be used in cross function reentrancies: + - [OptionsCompounder._harvestOTokens(uint256,address,uint256)](src/OptionsCompounder.sol#L164-L202) + - [OptionsCompounder.executeOperation(address[],uint256[],uint256[],address,bytes)](src/OptionsCompounder.sol#L214-L230) + - [OptionsCompounder.initialize(address,address,address,SwapProps,IOracle)](src/OptionsCompounder.sol#L74-L87) + +src/OptionsCompounder.sol#L214-L230 + + +## incorrect-equality +Impact: Medium +Confidence: High + - [ ] ID-2 +[OptionsCompounder._exerciseOptionAndReturnDebt(address,uint256,uint256,bytes)](src/OptionsCompounder.sol#L241-L320) uses a dangerous strict equality: + - [swapAmountOut == 0](src/OptionsCompounder.sol#L287) + +src/OptionsCompounder.sol#L241-L320 + + +## unused-return +Impact: Medium +Confidence: Medium + - [ ] ID-3 +[OptionsCompounder._exerciseOptionAndReturnDebt(address,uint256,uint256,bytes)](src/OptionsCompounder.sol#L241-L320) ignores return value by [IERC20(asset).approve(flashloanParams.exerciserContract,0)](src/OptionsCompounder.sol#L270) + +src/OptionsCompounder.sol#L241-L320 + + + - [ ] ID-4 +[OptionsCompounder._exerciseOptionAndReturnDebt(address,uint256,uint256,bytes)](src/OptionsCompounder.sol#L241-L320) ignores return value by [IERC20(asset).approve(address(lendingPool),totalAmountToPay)](src/OptionsCompounder.sol#L315) + +src/OptionsCompounder.sol#L241-L320 + + + - [ ] ID-5 +[OptionsCompounder._exerciseOptionAndReturnDebt(address,uint256,uint256,bytes)](src/OptionsCompounder.sol#L241-L320) ignores return value by [underlyingToken.approve(swapper,balanceOfUnderlyingToken)](src/OptionsCompounder.sol#L280) + +src/OptionsCompounder.sol#L241-L320 + + + - [ ] ID-6 +[OptionsCompounder._exerciseOptionAndReturnDebt(address,uint256,uint256,bytes)](src/OptionsCompounder.sol#L241-L320) ignores return value by [underlyingToken.approve(swapper,0)](src/OptionsCompounder.sol#L292) + +src/OptionsCompounder.sol#L241-L320 + + + - [ ] ID-7 +[OptionsCompounder._exerciseOptionAndReturnDebt(address,uint256,uint256,bytes)](src/OptionsCompounder.sol#L241-L320) ignores return value by [IERC20(asset).approve(flashloanParams.exerciserContract,amount)](src/OptionsCompounder.sol#L265) + +src/OptionsCompounder.sol#L241-L320 + + + - [ ] ID-8 +[OptionsCompounder._exerciseOptionAndReturnDebt(address,uint256,uint256,bytes)](src/OptionsCompounder.sol#L241-L320) ignores return value by [optionsToken.exercise(flashloanParams.optionsAmount,address(this),flashloanParams.exerciserContract,exerciseParams)](src/OptionsCompounder.sol#L267) + +src/OptionsCompounder.sol#L241-L320 + + +## missing-zero-check +Impact: Low +Confidence: Medium + - [ ] ID-9 +[OptionsCompounder.initiateUpgradeCooldown(address)._nextImplementation](src/OptionsCompounder.sol#L326) lacks a zero-check on : + - [nextImplementation = _nextImplementation](src/OptionsCompounder.sol#L328) + +src/OptionsCompounder.sol#L326 + + +## reentrancy-benign +Impact: Low +Confidence: Medium + - [ ] ID-10 +Reentrancy in [OptionsCompounder._harvestOTokens(uint256,address,uint256)](src/OptionsCompounder.sol#L164-L202): + External calls: + - [optionsToken.isExerciseContract(exerciseContract) == false](src/OptionsCompounder.sol#L166) + State variables written after the call(s): + - [flashloanFinished = false](src/OptionsCompounder.sol#L192) + +src/OptionsCompounder.sol#L164-L202 + + +## reentrancy-events +Impact: Low +Confidence: Medium + - [ ] ID-11 +Reentrancy in [OptionsCompounder._exerciseOptionAndReturnDebt(address,uint256,uint256,bytes)](src/OptionsCompounder.sol#L241-L320): + External calls: + - [IERC20(address(optionsToken)).safeTransferFrom(flashloanParams.sender,address(this),flashloanParams.optionsAmount)](src/OptionsCompounder.sol#L258) + - [IERC20(asset).approve(flashloanParams.exerciserContract,amount)](src/OptionsCompounder.sol#L265) + - [optionsToken.exercise(flashloanParams.optionsAmount,address(this),flashloanParams.exerciserContract,exerciseParams)](src/OptionsCompounder.sol#L267) + - [IERC20(asset).approve(flashloanParams.exerciserContract,0)](src/OptionsCompounder.sol#L270) + - [underlyingToken.approve(swapper,balanceOfUnderlyingToken)](src/OptionsCompounder.sol#L280) + - [swapAmountOut = _generalSwap(swapProps.exchangeTypes,address(underlyingToken),asset,balanceOfUnderlyingToken,minAmountOut,swapProps.exchangeAddress)](src/OptionsCompounder.sol#L283-L285) + - [_swapper.swapUniV2(tokenIn,tokenOut,amount,minAmountOutData,exchangeAddress)](src/helpers/SwapHelper.sol#L72) + - [_swapper.swapBal(tokenIn,tokenOut,amount,minAmountOutData,exchangeAddress)](src/helpers/SwapHelper.sol#L74) + - [_swapper.swapVelo(tokenIn,tokenOut,amount,minAmountOutData,exchangeAddress)](src/helpers/SwapHelper.sol#L76) + - [_swapper.swapUniV3(tokenIn,tokenOut,amount,minAmountOutData,exchangeAddress)](src/helpers/SwapHelper.sol#L78) + - [underlyingToken.approve(swapper,0)](src/OptionsCompounder.sol#L292) + - [IERC20(asset).approve(address(lendingPool),totalAmountToPay)](src/OptionsCompounder.sol#L315) + - [IERC20(asset).safeTransfer(flashloanParams.sender,gainInPaymentToken)](src/OptionsCompounder.sol#L316) + Event emitted after the call(s): + - [OTokenCompounded(gainInPaymentToken,totalAmountToPay)](src/OptionsCompounder.sol#L318) + +src/OptionsCompounder.sol#L241-L320 + + +## timestamp +Impact: Low +Confidence: Medium + - [ ] ID-12 +[OptionsCompounder._authorizeUpgrade(address)](src/OptionsCompounder.sol#L350-L354) uses timestamp for comparisons + Dangerous comparisons: + - [require(bool,string)(upgradeProposalTime + UPGRADE_TIMELOCK < block.timestamp,Upgrade cooldown not initiated or still ongoing)](src/OptionsCompounder.sol#L351) + +src/OptionsCompounder.sol#L350-L354 + + +## boolean-equal +Impact: Informational +Confidence: High + - [ ] ID-13 +[OptionsCompounder._harvestOTokens(uint256,address,uint256)](src/OptionsCompounder.sol#L164-L202) compares to a boolean constant: + -[optionsToken.isExerciseContract(exerciseContract) == false](src/OptionsCompounder.sol#L166) + +src/OptionsCompounder.sol#L164-L202 + + + - [ ] ID-14 +[OptionsCompounder._harvestOTokens(uint256,address,uint256)](src/OptionsCompounder.sol#L164-L202) compares to a boolean constant: + -[flashloanFinished == false](src/OptionsCompounder.sol#L170) + +src/OptionsCompounder.sol#L164-L202 + + + - [ ] ID-15 +[OptionsCompounder.executeOperation(address[],uint256[],uint256[],address,bytes)](src/OptionsCompounder.sol#L214-L230) compares to a boolean constant: + -[flashloanFinished != false || msg.sender != address(lendingPool)](src/OptionsCompounder.sol#L219) + +src/OptionsCompounder.sol#L214-L230 + + +## pragma +Impact: Informational +Confidence: High + - [ ] ID-16 +11 different versions of Solidity are used: + - Version constraint ^0.8.0 is used by: + -[^0.8.0](lib/openzeppelin-contracts-upgradeable/contracts/access/AccessControlEnumerableUpgradeable.sol#L4) + -[^0.8.0](lib/openzeppelin-contracts-upgradeable/contracts/access/AccessControlUpgradeable.sol#L4) + -[^0.8.0](lib/openzeppelin-contracts-upgradeable/contracts/access/IAccessControlEnumerableUpgradeable.sol#L4) + -[^0.8.0](lib/openzeppelin-contracts-upgradeable/contracts/access/IAccessControlUpgradeable.sol#L4) + -[^0.8.0](lib/openzeppelin-contracts-upgradeable/contracts/access/OwnableUpgradeable.sol#L4) + -[^0.8.0](lib/openzeppelin-contracts-upgradeable/contracts/interfaces/IERC1967Upgradeable.sol#L4) + -[^0.8.0](lib/openzeppelin-contracts-upgradeable/contracts/interfaces/draft-IERC1822Upgradeable.sol#L4) + -[^0.8.0](lib/openzeppelin-contracts-upgradeable/contracts/proxy/beacon/IBeaconUpgradeable.sol#L4) + -[^0.8.0](lib/openzeppelin-contracts-upgradeable/contracts/proxy/utils/UUPSUpgradeable.sol#L4) + -[^0.8.0](lib/openzeppelin-contracts-upgradeable/contracts/security/PausableUpgradeable.sol#L4) + -[^0.8.0](lib/openzeppelin-contracts-upgradeable/contracts/token/ERC20/ERC20Upgradeable.sol#L4) + -[^0.8.0](lib/openzeppelin-contracts-upgradeable/contracts/token/ERC20/IERC20Upgradeable.sol#L4) + -[^0.8.0](lib/openzeppelin-contracts-upgradeable/contracts/token/ERC20/extensions/IERC20MetadataUpgradeable.sol#L4) + -[^0.8.0](lib/openzeppelin-contracts-upgradeable/contracts/token/ERC20/extensions/draft-IERC20PermitUpgradeable.sol#L4) + -[^0.8.0](lib/openzeppelin-contracts-upgradeable/contracts/token/ERC20/utils/SafeERC20Upgradeable.sol#L4) + -[^0.8.0](lib/openzeppelin-contracts-upgradeable/contracts/utils/ContextUpgradeable.sol#L4) + -[^0.8.0](lib/openzeppelin-contracts-upgradeable/contracts/utils/StorageSlotUpgradeable.sol#L4) + -[^0.8.0](lib/openzeppelin-contracts-upgradeable/contracts/utils/StringsUpgradeable.sol#L4) + -[^0.8.0](lib/openzeppelin-contracts-upgradeable/contracts/utils/introspection/ERC165Upgradeable.sol#L4) + -[^0.8.0](lib/openzeppelin-contracts-upgradeable/contracts/utils/introspection/IERC165Upgradeable.sol#L4) + -[^0.8.0](lib/openzeppelin-contracts-upgradeable/contracts/utils/math/MathUpgradeable.sol#L4) + -[^0.8.0](lib/openzeppelin-contracts-upgradeable/contracts/utils/structs/EnumerableSetUpgradeable.sol#L5) + -[^0.8.0](lib/openzeppelin-contracts/contracts/security/Pausable.sol#L4) + -[^0.8.0](lib/openzeppelin-contracts/contracts/token/ERC20/IERC20.sol#L4) + -[^0.8.0](lib/openzeppelin-contracts/contracts/token/ERC20/extensions/draft-IERC20Permit.sol#L4) + -[^0.8.0](lib/openzeppelin-contracts/contracts/token/ERC20/utils/SafeERC20.sol#L4) + -[^0.8.0](lib/openzeppelin-contracts/contracts/utils/Context.sol#L4) + -[^0.8.0](lib/v3-core/contracts/libraries/FullMath.sol#L2) + -[^0.8.0](lib/v3-core/contracts/libraries/TickMath.sol#L2) + -[^0.8.0](lib/vault-v2/src/ReaperSwapper.sol#L3) + -[^0.8.0](lib/vault-v2/src/interfaces/AggregatorV3Interface.sol#L4) + -[^0.8.0](lib/vault-v2/src/interfaces/IAsset.sol#L3) + -[^0.8.0](lib/vault-v2/src/interfaces/IAuthorizer.sol#L3) + -[^0.8.0](lib/vault-v2/src/interfaces/IBasePool.sol#L3) + -[^0.8.0](lib/vault-v2/src/interfaces/IBaseWeightedPool.sol#L3) + -[^0.8.0](lib/vault-v2/src/interfaces/IBeetVault.sol#L3) + -[^0.8.0](lib/vault-v2/src/interfaces/IPoolSwapStructs.sol#L3) + -[^0.8.0](lib/vault-v2/src/interfaces/ISignaturesValidator.sol#L3) + -[^0.8.0](lib/vault-v2/src/interfaces/ISwapErrors.sol#L3) + -[^0.8.0](lib/vault-v2/src/interfaces/ISwapper.sol#L3) + -[^0.8.0](lib/vault-v2/src/interfaces/ISwapperSwaps.sol#L3) + -[^0.8.0](lib/vault-v2/src/interfaces/ITemporarilyPausable.sol#L3) + -[^0.8.0](lib/vault-v2/src/interfaces/IUniswapV2Router01.sol#L2) + -[^0.8.0](lib/vault-v2/src/interfaces/IVeloPair.sol#L2) + -[^0.8.0](lib/vault-v2/src/interfaces/IVeloRouter.sol#L2) + -[^0.8.0](lib/vault-v2/src/interfaces/IVeloV1AndV2Factory.sol#L2) + -[^0.8.0](lib/vault-v2/src/libraries/Babylonian.sol#L3) + -[^0.8.0](lib/vault-v2/src/libraries/ReaperMathUtils.sol#L3) + -[^0.8.0](lib/vault-v2/src/mixins/BalMixin.sol#L3) + -[^0.8.0](lib/vault-v2/src/mixins/ReaperAccessControl.sol#L5) + -[^0.8.0](lib/vault-v2/src/mixins/UniV2Mixin.sol#L3) + -[^0.8.0](lib/vault-v2/src/mixins/UniV3Mixin.sol#L3) + -[^0.8.0](lib/vault-v2/src/mixins/VeloSolidMixin.sol#L3) + -[^0.8.0](src/OptionsCompounder.sol#L3) + -[^0.8.0](src/interfaces/IBalancerTwapOracle.sol#L15) + -[^0.8.0](src/interfaces/IFlashLoanReceiver.sol#L2) + -[^0.8.0](src/interfaces/ILendingPool.sol#L2) + -[^0.8.0](src/interfaces/ILendingPoolAddressesProvider.sol#L2) + -[^0.8.0](src/interfaces/IOptionsCompounder.sol#L3) + -[^0.8.0](src/interfaces/ISwapperSwaps.sol#L3) + -[^0.8.0](src/libraries/DataTypes.sol#L2) + - Version constraint ^0.8.2 is used by: + -[^0.8.2](lib/openzeppelin-contracts-upgradeable/contracts/proxy/ERC1967/ERC1967UpgradeUpgradeable.sol#L4) + -[^0.8.2](lib/openzeppelin-contracts-upgradeable/contracts/proxy/utils/Initializable.sol#L4) + - Version constraint ^0.8.1 is used by: + -[^0.8.1](lib/openzeppelin-contracts-upgradeable/contracts/utils/AddressUpgradeable.sol#L4) + -[^0.8.1](lib/openzeppelin-contracts/contracts/utils/Address.sol#L4) + - Version constraint >=0.8.0 is used by: + -[>=0.8.0](lib/solmate/src/auth/Owned.sol#L2) + -[>=0.8.0](lib/solmate/src/tokens/ERC20.sol#L2) + -[>=0.8.0](lib/solmate/src/utils/FixedPointMathLib.sol#L2) + - Version constraint >=0.5.0 is used by: + -[>=0.5.0](lib/v3-core/contracts/interfaces/IUniswapV3Pool.sol#L2) + -[>=0.5.0](lib/v3-core/contracts/interfaces/pool/IUniswapV3PoolActions.sol#L2) + -[>=0.5.0](lib/v3-core/contracts/interfaces/pool/IUniswapV3PoolDerivedState.sol#L2) + -[>=0.5.0](lib/v3-core/contracts/interfaces/pool/IUniswapV3PoolErrors.sol#L2) + -[>=0.5.0](lib/v3-core/contracts/interfaces/pool/IUniswapV3PoolEvents.sol#L2) + -[>=0.5.0](lib/v3-core/contracts/interfaces/pool/IUniswapV3PoolImmutables.sol#L2) + -[>=0.5.0](lib/v3-core/contracts/interfaces/pool/IUniswapV3PoolOwnerActions.sol#L2) + -[>=0.5.0](lib/v3-core/contracts/interfaces/pool/IUniswapV3PoolState.sol#L2) + -[>=0.5.0](lib/vault-v2/src/interfaces/IPeripheryImmutableState.sol#L2) + -[>=0.5.0](lib/vault-v2/src/interfaces/IUniswapV3Factory.sol#L2) + -[>=0.5.0](lib/vault-v2/src/interfaces/IUniswapV3SwapCallback.sol#L2) + -[>=0.5.0](src/interfaces/IAlgebraPool.sol#L2) + - Version constraint >=0.7.5 is used by: + -[>=0.7.5](lib/vault-v2/src/interfaces/ISwapRouter.sol#L2) + - Version constraint >=0.6.2 is used by: + -[>=0.6.2](lib/vault-v2/src/interfaces/IUniswapV2Router02.sol#L2) + - Version constraint >=0.6.0 is used by: + -[>=0.6.0](lib/vault-v2/src/libraries/TransferHelper.sol#L2) + - Version constraint ^0.8.13 is used by: + -[^0.8.13](src/OptionsToken.sol#L2) + -[^0.8.13](src/exercise/BaseExercise.sol#L2) + -[^0.8.13](src/exercise/DiscountExercise.sol#L2) + -[^0.8.13](src/helpers/SwapHelper.sol#L3) + -[^0.8.13](src/interfaces/IBalancer2TokensPool.sol#L2) + -[^0.8.13](src/interfaces/IExercise.sol#L2) + -[^0.8.13](src/interfaces/IOptionsToken.sol#L2) + -[^0.8.13](src/oracles/AlgebraOracle.sol#L2) + -[^0.8.13](src/oracles/BalancerOracle.sol#L2) + -[^0.8.13](src/oracles/ThenaOracle.sol#L2) + -[^0.8.13](src/oracles/UniswapV3Oracle.sol#L2) + - Version constraint >=0.7.0<0.9.0 is used by: + -[>=0.7.0<0.9.0](src/interfaces/IBalancerVault.sol#L17) + -[>=0.7.0<0.9.0](src/interfaces/IERC20Mintable.sol#L3) + -[>=0.7.0<0.9.0](src/interfaces/IOracle.sol#L3) + - Version constraint >=0.5 is used by: + -[>=0.5](src/interfaces/IThenaPair.sol#L1) + +lib/openzeppelin-contracts-upgradeable/contracts/access/AccessControlEnumerableUpgradeable.sol#L4 + + +## solc-version +Impact: Informational +Confidence: High + - [ ] ID-17 +Version constraint ^0.8.0 contains known severe issues (https://solidity.readthedocs.io/en/latest/bugs.html) + - FullInlinerNonExpressionSplitArgumentEvaluationOrder + - MissingSideEffectsOnSelectorAccess + - AbiReencodingHeadOverflowWithStaticArrayCleanup + - DirtyBytesArrayToStorage + - DataLocationChangeInInternalOverride + - NestedCalldataArrayAbiReencodingSizeValidation + - SignedImmutables + - ABIDecodeTwoDimensionalArrayMemory + - KeccakCaching. +It is used by: + - [^0.8.0](lib/openzeppelin-contracts-upgradeable/contracts/access/AccessControlEnumerableUpgradeable.sol#L4) + - [^0.8.0](lib/openzeppelin-contracts-upgradeable/contracts/access/AccessControlUpgradeable.sol#L4) + - [^0.8.0](lib/openzeppelin-contracts-upgradeable/contracts/access/IAccessControlEnumerableUpgradeable.sol#L4) + - [^0.8.0](lib/openzeppelin-contracts-upgradeable/contracts/access/IAccessControlUpgradeable.sol#L4) + - [^0.8.0](lib/openzeppelin-contracts-upgradeable/contracts/access/OwnableUpgradeable.sol#L4) + - [^0.8.0](lib/openzeppelin-contracts-upgradeable/contracts/interfaces/IERC1967Upgradeable.sol#L4) + - [^0.8.0](lib/openzeppelin-contracts-upgradeable/contracts/interfaces/draft-IERC1822Upgradeable.sol#L4) + - [^0.8.0](lib/openzeppelin-contracts-upgradeable/contracts/proxy/beacon/IBeaconUpgradeable.sol#L4) + - [^0.8.0](lib/openzeppelin-contracts-upgradeable/contracts/proxy/utils/UUPSUpgradeable.sol#L4) + - [^0.8.0](lib/openzeppelin-contracts-upgradeable/contracts/security/PausableUpgradeable.sol#L4) + - [^0.8.0](lib/openzeppelin-contracts-upgradeable/contracts/token/ERC20/ERC20Upgradeable.sol#L4) + - [^0.8.0](lib/openzeppelin-contracts-upgradeable/contracts/token/ERC20/IERC20Upgradeable.sol#L4) + - [^0.8.0](lib/openzeppelin-contracts-upgradeable/contracts/token/ERC20/extensions/IERC20MetadataUpgradeable.sol#L4) + - [^0.8.0](lib/openzeppelin-contracts-upgradeable/contracts/token/ERC20/extensions/draft-IERC20PermitUpgradeable.sol#L4) + - [^0.8.0](lib/openzeppelin-contracts-upgradeable/contracts/token/ERC20/utils/SafeERC20Upgradeable.sol#L4) + - [^0.8.0](lib/openzeppelin-contracts-upgradeable/contracts/utils/ContextUpgradeable.sol#L4) + - [^0.8.0](lib/openzeppelin-contracts-upgradeable/contracts/utils/StorageSlotUpgradeable.sol#L4) + - [^0.8.0](lib/openzeppelin-contracts-upgradeable/contracts/utils/StringsUpgradeable.sol#L4) + - [^0.8.0](lib/openzeppelin-contracts-upgradeable/contracts/utils/introspection/ERC165Upgradeable.sol#L4) + - [^0.8.0](lib/openzeppelin-contracts-upgradeable/contracts/utils/introspection/IERC165Upgradeable.sol#L4) + - [^0.8.0](lib/openzeppelin-contracts-upgradeable/contracts/utils/math/MathUpgradeable.sol#L4) + - [^0.8.0](lib/openzeppelin-contracts-upgradeable/contracts/utils/structs/EnumerableSetUpgradeable.sol#L5) + - [^0.8.0](lib/openzeppelin-contracts/contracts/security/Pausable.sol#L4) + - [^0.8.0](lib/openzeppelin-contracts/contracts/token/ERC20/IERC20.sol#L4) + - [^0.8.0](lib/openzeppelin-contracts/contracts/token/ERC20/extensions/draft-IERC20Permit.sol#L4) + - [^0.8.0](lib/openzeppelin-contracts/contracts/token/ERC20/utils/SafeERC20.sol#L4) + - [^0.8.0](lib/openzeppelin-contracts/contracts/utils/Context.sol#L4) + - [^0.8.0](lib/v3-core/contracts/libraries/FullMath.sol#L2) + - [^0.8.0](lib/v3-core/contracts/libraries/TickMath.sol#L2) + - [^0.8.0](lib/vault-v2/src/ReaperSwapper.sol#L3) + - [^0.8.0](lib/vault-v2/src/interfaces/AggregatorV3Interface.sol#L4) + - [^0.8.0](lib/vault-v2/src/interfaces/IAsset.sol#L3) + - [^0.8.0](lib/vault-v2/src/interfaces/IAuthorizer.sol#L3) + - [^0.8.0](lib/vault-v2/src/interfaces/IBasePool.sol#L3) + - [^0.8.0](lib/vault-v2/src/interfaces/IBaseWeightedPool.sol#L3) + - [^0.8.0](lib/vault-v2/src/interfaces/IBeetVault.sol#L3) + - [^0.8.0](lib/vault-v2/src/interfaces/IPoolSwapStructs.sol#L3) + - [^0.8.0](lib/vault-v2/src/interfaces/ISignaturesValidator.sol#L3) + - [^0.8.0](lib/vault-v2/src/interfaces/ISwapErrors.sol#L3) + - [^0.8.0](lib/vault-v2/src/interfaces/ISwapper.sol#L3) + - [^0.8.0](lib/vault-v2/src/interfaces/ISwapperSwaps.sol#L3) + - [^0.8.0](lib/vault-v2/src/interfaces/ITemporarilyPausable.sol#L3) + - [^0.8.0](lib/vault-v2/src/interfaces/IUniswapV2Router01.sol#L2) + - [^0.8.0](lib/vault-v2/src/interfaces/IVeloPair.sol#L2) + - [^0.8.0](lib/vault-v2/src/interfaces/IVeloRouter.sol#L2) + - [^0.8.0](lib/vault-v2/src/interfaces/IVeloV1AndV2Factory.sol#L2) + - [^0.8.0](lib/vault-v2/src/libraries/Babylonian.sol#L3) + - [^0.8.0](lib/vault-v2/src/libraries/ReaperMathUtils.sol#L3) + - [^0.8.0](lib/vault-v2/src/mixins/BalMixin.sol#L3) + - [^0.8.0](lib/vault-v2/src/mixins/ReaperAccessControl.sol#L5) + - [^0.8.0](lib/vault-v2/src/mixins/UniV2Mixin.sol#L3) + - [^0.8.0](lib/vault-v2/src/mixins/UniV3Mixin.sol#L3) + - [^0.8.0](lib/vault-v2/src/mixins/VeloSolidMixin.sol#L3) + - [^0.8.0](src/OptionsCompounder.sol#L3) + - [^0.8.0](src/interfaces/IBalancerTwapOracle.sol#L15) + - [^0.8.0](src/interfaces/IFlashLoanReceiver.sol#L2) + - [^0.8.0](src/interfaces/ILendingPool.sol#L2) + - [^0.8.0](src/interfaces/ILendingPoolAddressesProvider.sol#L2) + - [^0.8.0](src/interfaces/IOptionsCompounder.sol#L3) + - [^0.8.0](src/interfaces/ISwapperSwaps.sol#L3) + - [^0.8.0](src/libraries/DataTypes.sol#L2) + +lib/openzeppelin-contracts-upgradeable/contracts/access/AccessControlEnumerableUpgradeable.sol#L4 + + +## missing-inheritance +Impact: Informational +Confidence: High + - [ ] ID-18 +[OptionsCompounder](src/OptionsCompounder.sol#L25-L370) should inherit from [IOptionsCompounder](src/interfaces/IOptionsCompounder.sol#L20-L24) + +src/OptionsCompounder.sol#L25-L370 + + +## naming-convention +Impact: Informational +Confidence: High + - [ ] ID-19 +Parameter [OptionsCompounder.initialize(address,address,address,SwapProps,IOracle)._optionsToken](src/OptionsCompounder.sol#L74) is not in mixedCase + +src/OptionsCompounder.sol#L74 + + + - [ ] ID-20 +Parameter [OptionsCompounder.initialize(address,address,address,SwapProps,IOracle)._swapper](src/OptionsCompounder.sol#L74) is not in mixedCase + +src/OptionsCompounder.sol#L74 + + + - [ ] ID-21 +Parameter [OptionsCompounder.initialize(address,address,address,SwapProps,IOracle)._oracle](src/OptionsCompounder.sol#L74) is not in mixedCase + +src/OptionsCompounder.sol#L74 + + + - [ ] ID-22 +Parameter [OptionsCompounder.initialize(address,address,address,SwapProps,IOracle)._swapProps](src/OptionsCompounder.sol#L74) is not in mixedCase + +src/OptionsCompounder.sol#L74 + + + - [ ] ID-23 +Parameter [OptionsCompounder.setSwapProps(SwapProps)._swapProps](src/OptionsCompounder.sol#L108) is not in mixedCase + +src/OptionsCompounder.sol#L108 + + + - [ ] ID-24 +Parameter [OptionsCompounder.setOracle(IOracle)._oracle](src/OptionsCompounder.sol#L112) is not in mixedCase + +src/OptionsCompounder.sol#L112 + + + - [ ] ID-25 +Function [OptionsCompounder.ADDRESSES_PROVIDER()](src/OptionsCompounder.sol#L363-L365) is not in mixedCase + +src/OptionsCompounder.sol#L363-L365 + + + - [ ] ID-26 +Parameter [OptionsCompounder.initialize(address,address,address,SwapProps,IOracle)._addressProvider](src/OptionsCompounder.sol#L74) is not in mixedCase + +src/OptionsCompounder.sol#L74 + + + - [ ] ID-27 +Parameter [OptionsCompounder.initiateUpgradeCooldown(address)._nextImplementation](src/OptionsCompounder.sol#L326) is not in mixedCase + +src/OptionsCompounder.sol#L326 + + + - [ ] ID-28 +Parameter [OptionsCompounder.setOptionsToken(address)._optionsToken](src/OptionsCompounder.sol#L97) is not in mixedCase + +src/OptionsCompounder.sol#L97 + + + - [ ] ID-29 +Parameter [OptionsCompounder.setSwapper(address)._swapper](src/OptionsCompounder.sol#L123) is not in mixedCase + +src/OptionsCompounder.sol#L123 + + + - [ ] ID-30 +Function [OptionsCompounder.LENDING_POOL()](src/OptionsCompounder.sol#L367-L369) is not in mixedCase + +src/OptionsCompounder.sol#L367-L369 + + + - [ ] ID-31 +Parameter [OptionsCompounder.setAddressProvider(address)._addressProvider](src/OptionsCompounder.sol#L134) is not in mixedCase + +src/OptionsCompounder.sol#L134 + +## Options Token Slither + +Summary + - [missing-zero-check](#missing-zero-check) (2 results) (Low) + - [reentrancy-events](#reentrancy-events) (1 results) (Low) + - [timestamp](#timestamp) (1 results) (Low) + - [pragma](#pragma) (1 results) (Informational) + - [solc-version](#solc-version) (1 results) (Informational) + - [naming-convention](#naming-convention) (3 results) (Informational) +## missing-zero-check +Impact: Low +Confidence: Medium + - [ ] ID-0 +[OptionsToken.initialize(string,string,address).tokenAdmin_](src/OptionsToken.sol#L60) lacks a zero-check on : + - [tokenAdmin = tokenAdmin_](src/OptionsToken.sol#L65) + +src/OptionsToken.sol#L60 + + + - [ ] ID-1 +[OptionsToken.initiateUpgradeCooldown(address)._nextImplementation](src/OptionsToken.sol#L178) lacks a zero-check on : + - [nextImplementation = _nextImplementation](src/OptionsToken.sol#L180) + +src/OptionsToken.sol#L178 + + +## reentrancy-events +Impact: Low +Confidence: Medium + - [ ] ID-2 +Reentrancy in [OptionsToken._exercise(uint256,address,address,bytes)](src/OptionsToken.sol#L144-L168): + External calls: + - [(paymentAmount,data0,data1,data2) = IExercise(option).exercise(msg.sender,amount,recipient,params)](src/OptionsToken.sol#L164) + Event emitted after the call(s): + - [Exercise(msg.sender,recipient,amount,data0,data1,data2)](src/OptionsToken.sol#L167) + +src/OptionsToken.sol#L144-L168 + + +## timestamp +Impact: Low +Confidence: Medium + - [ ] ID-3 +[OptionsToken._authorizeUpgrade(address)](src/OptionsToken.sol#L202-L206) uses timestamp for comparisons + Dangerous comparisons: + - [require(bool,string)(upgradeProposalTime + UPGRADE_TIMELOCK < block.timestamp,Upgrade cooldown not initiated or still ongoing)](src/OptionsToken.sol#L203) + +src/OptionsToken.sol#L202-L206 + + +## pragma +Impact: Informational +Confidence: High + - [ ] ID-4 +11 different versions of Solidity are used: + - Version constraint ^0.8.0 is used by: + -[^0.8.0](lib/openzeppelin-contracts-upgradeable/contracts/access/AccessControlEnumerableUpgradeable.sol#L4) + -[^0.8.0](lib/openzeppelin-contracts-upgradeable/contracts/access/AccessControlUpgradeable.sol#L4) + -[^0.8.0](lib/openzeppelin-contracts-upgradeable/contracts/access/IAccessControlEnumerableUpgradeable.sol#L4) + -[^0.8.0](lib/openzeppelin-contracts-upgradeable/contracts/access/IAccessControlUpgradeable.sol#L4) + -[^0.8.0](lib/openzeppelin-contracts-upgradeable/contracts/access/OwnableUpgradeable.sol#L4) + -[^0.8.0](lib/openzeppelin-contracts-upgradeable/contracts/interfaces/IERC1967Upgradeable.sol#L4) + -[^0.8.0](lib/openzeppelin-contracts-upgradeable/contracts/interfaces/draft-IERC1822Upgradeable.sol#L4) + -[^0.8.0](lib/openzeppelin-contracts-upgradeable/contracts/proxy/beacon/IBeaconUpgradeable.sol#L4) + -[^0.8.0](lib/openzeppelin-contracts-upgradeable/contracts/proxy/utils/UUPSUpgradeable.sol#L4) + -[^0.8.0](lib/openzeppelin-contracts-upgradeable/contracts/security/PausableUpgradeable.sol#L4) + -[^0.8.0](lib/openzeppelin-contracts-upgradeable/contracts/token/ERC20/ERC20Upgradeable.sol#L4) + -[^0.8.0](lib/openzeppelin-contracts-upgradeable/contracts/token/ERC20/IERC20Upgradeable.sol#L4) + -[^0.8.0](lib/openzeppelin-contracts-upgradeable/contracts/token/ERC20/extensions/IERC20MetadataUpgradeable.sol#L4) + -[^0.8.0](lib/openzeppelin-contracts-upgradeable/contracts/token/ERC20/extensions/draft-IERC20PermitUpgradeable.sol#L4) + -[^0.8.0](lib/openzeppelin-contracts-upgradeable/contracts/token/ERC20/utils/SafeERC20Upgradeable.sol#L4) + -[^0.8.0](lib/openzeppelin-contracts-upgradeable/contracts/utils/ContextUpgradeable.sol#L4) + -[^0.8.0](lib/openzeppelin-contracts-upgradeable/contracts/utils/StorageSlotUpgradeable.sol#L4) + -[^0.8.0](lib/openzeppelin-contracts-upgradeable/contracts/utils/StringsUpgradeable.sol#L4) + -[^0.8.0](lib/openzeppelin-contracts-upgradeable/contracts/utils/introspection/ERC165Upgradeable.sol#L4) + -[^0.8.0](lib/openzeppelin-contracts-upgradeable/contracts/utils/introspection/IERC165Upgradeable.sol#L4) + -[^0.8.0](lib/openzeppelin-contracts-upgradeable/contracts/utils/math/MathUpgradeable.sol#L4) + -[^0.8.0](lib/openzeppelin-contracts-upgradeable/contracts/utils/structs/EnumerableSetUpgradeable.sol#L5) + -[^0.8.0](lib/openzeppelin-contracts/contracts/security/Pausable.sol#L4) + -[^0.8.0](lib/openzeppelin-contracts/contracts/token/ERC20/IERC20.sol#L4) + -[^0.8.0](lib/openzeppelin-contracts/contracts/token/ERC20/extensions/draft-IERC20Permit.sol#L4) + -[^0.8.0](lib/openzeppelin-contracts/contracts/token/ERC20/utils/SafeERC20.sol#L4) + -[^0.8.0](lib/openzeppelin-contracts/contracts/utils/Context.sol#L4) + -[^0.8.0](lib/v3-core/contracts/libraries/FullMath.sol#L2) + -[^0.8.0](lib/v3-core/contracts/libraries/TickMath.sol#L2) + -[^0.8.0](lib/vault-v2/src/ReaperSwapper.sol#L3) + -[^0.8.0](lib/vault-v2/src/interfaces/AggregatorV3Interface.sol#L4) + -[^0.8.0](lib/vault-v2/src/interfaces/IAsset.sol#L3) + -[^0.8.0](lib/vault-v2/src/interfaces/IAuthorizer.sol#L3) + -[^0.8.0](lib/vault-v2/src/interfaces/IBasePool.sol#L3) + -[^0.8.0](lib/vault-v2/src/interfaces/IBaseWeightedPool.sol#L3) + -[^0.8.0](lib/vault-v2/src/interfaces/IBeetVault.sol#L3) + -[^0.8.0](lib/vault-v2/src/interfaces/IPoolSwapStructs.sol#L3) + -[^0.8.0](lib/vault-v2/src/interfaces/ISignaturesValidator.sol#L3) + -[^0.8.0](lib/vault-v2/src/interfaces/ISwapErrors.sol#L3) + -[^0.8.0](lib/vault-v2/src/interfaces/ISwapper.sol#L3) + -[^0.8.0](lib/vault-v2/src/interfaces/ISwapperSwaps.sol#L3) + -[^0.8.0](lib/vault-v2/src/interfaces/ITemporarilyPausable.sol#L3) + -[^0.8.0](lib/vault-v2/src/interfaces/IUniswapV2Router01.sol#L2) + -[^0.8.0](lib/vault-v2/src/interfaces/IVeloPair.sol#L2) + -[^0.8.0](lib/vault-v2/src/interfaces/IVeloRouter.sol#L2) + -[^0.8.0](lib/vault-v2/src/interfaces/IVeloV1AndV2Factory.sol#L2) + -[^0.8.0](lib/vault-v2/src/libraries/Babylonian.sol#L3) + -[^0.8.0](lib/vault-v2/src/libraries/ReaperMathUtils.sol#L3) + -[^0.8.0](lib/vault-v2/src/mixins/BalMixin.sol#L3) + -[^0.8.0](lib/vault-v2/src/mixins/ReaperAccessControl.sol#L5) + -[^0.8.0](lib/vault-v2/src/mixins/UniV2Mixin.sol#L3) + -[^0.8.0](lib/vault-v2/src/mixins/UniV3Mixin.sol#L3) + -[^0.8.0](lib/vault-v2/src/mixins/VeloSolidMixin.sol#L3) + -[^0.8.0](src/OptionsCompounder.sol#L3) + -[^0.8.0](src/interfaces/IBalancerTwapOracle.sol#L15) + -[^0.8.0](src/interfaces/IFlashLoanReceiver.sol#L2) + -[^0.8.0](src/interfaces/ILendingPool.sol#L2) + -[^0.8.0](src/interfaces/ILendingPoolAddressesProvider.sol#L2) + -[^0.8.0](src/interfaces/IOptionsCompounder.sol#L3) + -[^0.8.0](src/interfaces/ISwapperSwaps.sol#L3) + -[^0.8.0](src/libraries/DataTypes.sol#L2) + - Version constraint ^0.8.2 is used by: + -[^0.8.2](lib/openzeppelin-contracts-upgradeable/contracts/proxy/ERC1967/ERC1967UpgradeUpgradeable.sol#L4) + -[^0.8.2](lib/openzeppelin-contracts-upgradeable/contracts/proxy/utils/Initializable.sol#L4) + - Version constraint ^0.8.1 is used by: + -[^0.8.1](lib/openzeppelin-contracts-upgradeable/contracts/utils/AddressUpgradeable.sol#L4) + -[^0.8.1](lib/openzeppelin-contracts/contracts/utils/Address.sol#L4) + - Version constraint >=0.8.0 is used by: + -[>=0.8.0](lib/solmate/src/auth/Owned.sol#L2) + -[>=0.8.0](lib/solmate/src/tokens/ERC20.sol#L2) + -[>=0.8.0](lib/solmate/src/utils/FixedPointMathLib.sol#L2) + - Version constraint >=0.5.0 is used by: + -[>=0.5.0](lib/v3-core/contracts/interfaces/IUniswapV3Pool.sol#L2) + -[>=0.5.0](lib/v3-core/contracts/interfaces/pool/IUniswapV3PoolActions.sol#L2) + -[>=0.5.0](lib/v3-core/contracts/interfaces/pool/IUniswapV3PoolDerivedState.sol#L2) + -[>=0.5.0](lib/v3-core/contracts/interfaces/pool/IUniswapV3PoolErrors.sol#L2) + -[>=0.5.0](lib/v3-core/contracts/interfaces/pool/IUniswapV3PoolEvents.sol#L2) + -[>=0.5.0](lib/v3-core/contracts/interfaces/pool/IUniswapV3PoolImmutables.sol#L2) + -[>=0.5.0](lib/v3-core/contracts/interfaces/pool/IUniswapV3PoolOwnerActions.sol#L2) + -[>=0.5.0](lib/v3-core/contracts/interfaces/pool/IUniswapV3PoolState.sol#L2) + -[>=0.5.0](lib/vault-v2/src/interfaces/IPeripheryImmutableState.sol#L2) + -[>=0.5.0](lib/vault-v2/src/interfaces/IUniswapV3Factory.sol#L2) + -[>=0.5.0](lib/vault-v2/src/interfaces/IUniswapV3SwapCallback.sol#L2) + -[>=0.5.0](src/interfaces/IAlgebraPool.sol#L2) + - Version constraint >=0.7.5 is used by: + -[>=0.7.5](lib/vault-v2/src/interfaces/ISwapRouter.sol#L2) + - Version constraint >=0.6.2 is used by: + -[>=0.6.2](lib/vault-v2/src/interfaces/IUniswapV2Router02.sol#L2) + - Version constraint >=0.6.0 is used by: + -[>=0.6.0](lib/vault-v2/src/libraries/TransferHelper.sol#L2) + - Version constraint ^0.8.13 is used by: + -[^0.8.13](src/OptionsToken.sol#L2) + -[^0.8.13](src/exercise/BaseExercise.sol#L2) + -[^0.8.13](src/exercise/DiscountExercise.sol#L2) + -[^0.8.13](src/helpers/SwapHelper.sol#L3) + -[^0.8.13](src/interfaces/IBalancer2TokensPool.sol#L2) + -[^0.8.13](src/interfaces/IExercise.sol#L2) + -[^0.8.13](src/interfaces/IOptionsToken.sol#L2) + -[^0.8.13](src/oracles/AlgebraOracle.sol#L2) + -[^0.8.13](src/oracles/BalancerOracle.sol#L2) + -[^0.8.13](src/oracles/ThenaOracle.sol#L2) + -[^0.8.13](src/oracles/UniswapV3Oracle.sol#L2) + - Version constraint >=0.7.0<0.9.0 is used by: + -[>=0.7.0<0.9.0](src/interfaces/IBalancerVault.sol#L17) + -[>=0.7.0<0.9.0](src/interfaces/IERC20Mintable.sol#L3) + -[>=0.7.0<0.9.0](src/interfaces/IOracle.sol#L3) + - Version constraint >=0.5 is used by: + -[>=0.5](src/interfaces/IThenaPair.sol#L1) + +lib/openzeppelin-contracts-upgradeable/contracts/access/AccessControlEnumerableUpgradeable.sol#L4 + + +## solc-version +Impact: Informational +Confidence: High + - [ ] ID-5 +Version constraint ^0.8.13 contains known severe issues (https://solidity.readthedocs.io/en/latest/bugs.html) + - VerbatimInvalidDeduplication + - FullInlinerNonExpressionSplitArgumentEvaluationOrder + - MissingSideEffectsOnSelectorAccess + - StorageWriteRemovalBeforeConditionalTermination + - AbiReencodingHeadOverflowWithStaticArrayCleanup + - DirtyBytesArrayToStorage + - InlineAssemblyMemorySideEffects + - DataLocationChangeInInternalOverride + - NestedCalldataArrayAbiReencodingSizeValidation. +It is used by: + - [^0.8.13](src/OptionsToken.sol#L2) + - [^0.8.13](src/exercise/BaseExercise.sol#L2) + - [^0.8.13](src/exercise/DiscountExercise.sol#L2) + - [^0.8.13](src/helpers/SwapHelper.sol#L3) + - [^0.8.13](src/interfaces/IBalancer2TokensPool.sol#L2) + - [^0.8.13](src/interfaces/IExercise.sol#L2) + - [^0.8.13](src/interfaces/IOptionsToken.sol#L2) + - [^0.8.13](src/oracles/AlgebraOracle.sol#L2) + - [^0.8.13](src/oracles/BalancerOracle.sol#L2) + - [^0.8.13](src/oracles/ThenaOracle.sol#L2) + - [^0.8.13](src/oracles/UniswapV3Oracle.sol#L2) + +src/OptionsToken.sol#L2 + + +## naming-convention +Impact: Informational +Confidence: High + - [ ] ID-6 +Parameter [OptionsToken.initiateUpgradeCooldown(address)._nextImplementation](src/OptionsToken.sol#L178) is not in mixedCase + +src/OptionsToken.sol#L178 + + + - [ ] ID-7 +Parameter [OptionsToken.setExerciseContract(address,bool)._isExercise](src/OptionsToken.sol#L125) is not in mixedCase + +src/OptionsToken.sol#L125 + + + - [ ] ID-8 +Parameter [OptionsToken.setExerciseContract(address,bool)._address](src/OptionsToken.sol#L125) is not in mixedCase + +src/OptionsToken.sol#L125 + +## Thena Oracle Slither + +Summary + - [pragma](#pragma) (1 results) (Informational) + - [solc-version](#solc-version) (1 results) (Informational) + - [immutable-states](#immutable-states) (1 results) (Optimization) +## pragma +Impact: Informational +Confidence: High + - [ ] ID-0 +11 different versions of Solidity are used: + - Version constraint ^0.8.0 is used by: + -[^0.8.0](lib/openzeppelin-contracts-upgradeable/contracts/access/AccessControlEnumerableUpgradeable.sol#L4) + -[^0.8.0](lib/openzeppelin-contracts-upgradeable/contracts/access/AccessControlUpgradeable.sol#L4) + -[^0.8.0](lib/openzeppelin-contracts-upgradeable/contracts/access/IAccessControlEnumerableUpgradeable.sol#L4) + -[^0.8.0](lib/openzeppelin-contracts-upgradeable/contracts/access/IAccessControlUpgradeable.sol#L4) + -[^0.8.0](lib/openzeppelin-contracts-upgradeable/contracts/access/OwnableUpgradeable.sol#L4) + -[^0.8.0](lib/openzeppelin-contracts-upgradeable/contracts/interfaces/IERC1967Upgradeable.sol#L4) + -[^0.8.0](lib/openzeppelin-contracts-upgradeable/contracts/interfaces/draft-IERC1822Upgradeable.sol#L4) + -[^0.8.0](lib/openzeppelin-contracts-upgradeable/contracts/proxy/beacon/IBeaconUpgradeable.sol#L4) + -[^0.8.0](lib/openzeppelin-contracts-upgradeable/contracts/proxy/utils/UUPSUpgradeable.sol#L4) + -[^0.8.0](lib/openzeppelin-contracts-upgradeable/contracts/security/PausableUpgradeable.sol#L4) + -[^0.8.0](lib/openzeppelin-contracts-upgradeable/contracts/token/ERC20/ERC20Upgradeable.sol#L4) + -[^0.8.0](lib/openzeppelin-contracts-upgradeable/contracts/token/ERC20/IERC20Upgradeable.sol#L4) + -[^0.8.0](lib/openzeppelin-contracts-upgradeable/contracts/token/ERC20/extensions/IERC20MetadataUpgradeable.sol#L4) + -[^0.8.0](lib/openzeppelin-contracts-upgradeable/contracts/token/ERC20/extensions/draft-IERC20PermitUpgradeable.sol#L4) + -[^0.8.0](lib/openzeppelin-contracts-upgradeable/contracts/token/ERC20/utils/SafeERC20Upgradeable.sol#L4) + -[^0.8.0](lib/openzeppelin-contracts-upgradeable/contracts/utils/ContextUpgradeable.sol#L4) + -[^0.8.0](lib/openzeppelin-contracts-upgradeable/contracts/utils/StorageSlotUpgradeable.sol#L4) + -[^0.8.0](lib/openzeppelin-contracts-upgradeable/contracts/utils/StringsUpgradeable.sol#L4) + -[^0.8.0](lib/openzeppelin-contracts-upgradeable/contracts/utils/introspection/ERC165Upgradeable.sol#L4) + -[^0.8.0](lib/openzeppelin-contracts-upgradeable/contracts/utils/introspection/IERC165Upgradeable.sol#L4) + -[^0.8.0](lib/openzeppelin-contracts-upgradeable/contracts/utils/math/MathUpgradeable.sol#L4) + -[^0.8.0](lib/openzeppelin-contracts-upgradeable/contracts/utils/structs/EnumerableSetUpgradeable.sol#L5) + -[^0.8.0](lib/openzeppelin-contracts/contracts/security/Pausable.sol#L4) + -[^0.8.0](lib/openzeppelin-contracts/contracts/token/ERC20/IERC20.sol#L4) + -[^0.8.0](lib/openzeppelin-contracts/contracts/token/ERC20/extensions/draft-IERC20Permit.sol#L4) + -[^0.8.0](lib/openzeppelin-contracts/contracts/token/ERC20/utils/SafeERC20.sol#L4) + -[^0.8.0](lib/openzeppelin-contracts/contracts/utils/Context.sol#L4) + -[^0.8.0](lib/v3-core/contracts/libraries/FullMath.sol#L2) + -[^0.8.0](lib/v3-core/contracts/libraries/TickMath.sol#L2) + -[^0.8.0](lib/vault-v2/src/ReaperSwapper.sol#L3) + -[^0.8.0](lib/vault-v2/src/interfaces/AggregatorV3Interface.sol#L4) + -[^0.8.0](lib/vault-v2/src/interfaces/IAsset.sol#L3) + -[^0.8.0](lib/vault-v2/src/interfaces/IAuthorizer.sol#L3) + -[^0.8.0](lib/vault-v2/src/interfaces/IBasePool.sol#L3) + -[^0.8.0](lib/vault-v2/src/interfaces/IBaseWeightedPool.sol#L3) + -[^0.8.0](lib/vault-v2/src/interfaces/IBeetVault.sol#L3) + -[^0.8.0](lib/vault-v2/src/interfaces/IPoolSwapStructs.sol#L3) + -[^0.8.0](lib/vault-v2/src/interfaces/ISignaturesValidator.sol#L3) + -[^0.8.0](lib/vault-v2/src/interfaces/ISwapErrors.sol#L3) + -[^0.8.0](lib/vault-v2/src/interfaces/ISwapper.sol#L3) + -[^0.8.0](lib/vault-v2/src/interfaces/ISwapperSwaps.sol#L3) + -[^0.8.0](lib/vault-v2/src/interfaces/ITemporarilyPausable.sol#L3) + -[^0.8.0](lib/vault-v2/src/interfaces/IUniswapV2Router01.sol#L2) + -[^0.8.0](lib/vault-v2/src/interfaces/IVeloPair.sol#L2) + -[^0.8.0](lib/vault-v2/src/interfaces/IVeloRouter.sol#L2) + -[^0.8.0](lib/vault-v2/src/interfaces/IVeloV1AndV2Factory.sol#L2) + -[^0.8.0](lib/vault-v2/src/libraries/Babylonian.sol#L3) + -[^0.8.0](lib/vault-v2/src/libraries/ReaperMathUtils.sol#L3) + -[^0.8.0](lib/vault-v2/src/mixins/BalMixin.sol#L3) + -[^0.8.0](lib/vault-v2/src/mixins/ReaperAccessControl.sol#L5) + -[^0.8.0](lib/vault-v2/src/mixins/UniV2Mixin.sol#L3) + -[^0.8.0](lib/vault-v2/src/mixins/UniV3Mixin.sol#L3) + -[^0.8.0](lib/vault-v2/src/mixins/VeloSolidMixin.sol#L3) + -[^0.8.0](src/OptionsCompounder.sol#L3) + -[^0.8.0](src/interfaces/IBalancerTwapOracle.sol#L15) + -[^0.8.0](src/interfaces/IFlashLoanReceiver.sol#L2) + -[^0.8.0](src/interfaces/ILendingPool.sol#L2) + -[^0.8.0](src/interfaces/ILendingPoolAddressesProvider.sol#L2) + -[^0.8.0](src/interfaces/IOptionsCompounder.sol#L3) + -[^0.8.0](src/interfaces/ISwapperSwaps.sol#L3) + -[^0.8.0](src/libraries/DataTypes.sol#L2) + - Version constraint ^0.8.2 is used by: + -[^0.8.2](lib/openzeppelin-contracts-upgradeable/contracts/proxy/ERC1967/ERC1967UpgradeUpgradeable.sol#L4) + -[^0.8.2](lib/openzeppelin-contracts-upgradeable/contracts/proxy/utils/Initializable.sol#L4) + - Version constraint ^0.8.1 is used by: + -[^0.8.1](lib/openzeppelin-contracts-upgradeable/contracts/utils/AddressUpgradeable.sol#L4) + -[^0.8.1](lib/openzeppelin-contracts/contracts/utils/Address.sol#L4) + - Version constraint >=0.8.0 is used by: + -[>=0.8.0](lib/solmate/src/auth/Owned.sol#L2) + -[>=0.8.0](lib/solmate/src/tokens/ERC20.sol#L2) + -[>=0.8.0](lib/solmate/src/utils/FixedPointMathLib.sol#L2) + - Version constraint >=0.5.0 is used by: + -[>=0.5.0](lib/v3-core/contracts/interfaces/IUniswapV3Pool.sol#L2) + -[>=0.5.0](lib/v3-core/contracts/interfaces/pool/IUniswapV3PoolActions.sol#L2) + -[>=0.5.0](lib/v3-core/contracts/interfaces/pool/IUniswapV3PoolDerivedState.sol#L2) + -[>=0.5.0](lib/v3-core/contracts/interfaces/pool/IUniswapV3PoolErrors.sol#L2) + -[>=0.5.0](lib/v3-core/contracts/interfaces/pool/IUniswapV3PoolEvents.sol#L2) + -[>=0.5.0](lib/v3-core/contracts/interfaces/pool/IUniswapV3PoolImmutables.sol#L2) + -[>=0.5.0](lib/v3-core/contracts/interfaces/pool/IUniswapV3PoolOwnerActions.sol#L2) + -[>=0.5.0](lib/v3-core/contracts/interfaces/pool/IUniswapV3PoolState.sol#L2) + -[>=0.5.0](lib/vault-v2/src/interfaces/IPeripheryImmutableState.sol#L2) + -[>=0.5.0](lib/vault-v2/src/interfaces/IUniswapV3Factory.sol#L2) + -[>=0.5.0](lib/vault-v2/src/interfaces/IUniswapV3SwapCallback.sol#L2) + -[>=0.5.0](src/interfaces/IAlgebraPool.sol#L2) + - Version constraint >=0.7.5 is used by: + -[>=0.7.5](lib/vault-v2/src/interfaces/ISwapRouter.sol#L2) + - Version constraint >=0.6.2 is used by: + -[>=0.6.2](lib/vault-v2/src/interfaces/IUniswapV2Router02.sol#L2) + - Version constraint >=0.6.0 is used by: + -[>=0.6.0](lib/vault-v2/src/libraries/TransferHelper.sol#L2) + - Version constraint ^0.8.13 is used by: + -[^0.8.13](src/OptionsToken.sol#L2) + -[^0.8.13](src/exercise/BaseExercise.sol#L2) + -[^0.8.13](src/exercise/DiscountExercise.sol#L2) + -[^0.8.13](src/helpers/SwapHelper.sol#L3) + -[^0.8.13](src/interfaces/IBalancer2TokensPool.sol#L2) + -[^0.8.13](src/interfaces/IExercise.sol#L2) + -[^0.8.13](src/interfaces/IOptionsToken.sol#L2) + -[^0.8.13](src/oracles/AlgebraOracle.sol#L2) + -[^0.8.13](src/oracles/BalancerOracle.sol#L2) + -[^0.8.13](src/oracles/ThenaOracle.sol#L2) + -[^0.8.13](src/oracles/UniswapV3Oracle.sol#L2) + - Version constraint >=0.7.0<0.9.0 is used by: + -[>=0.7.0<0.9.0](src/interfaces/IBalancerVault.sol#L17) + -[>=0.7.0<0.9.0](src/interfaces/IERC20Mintable.sol#L3) + -[>=0.7.0<0.9.0](src/interfaces/IOracle.sol#L3) + - Version constraint >=0.5 is used by: + -[>=0.5](src/interfaces/IThenaPair.sol#L1) + +lib/openzeppelin-contracts-upgradeable/contracts/access/AccessControlEnumerableUpgradeable.sol#L4 + + +## solc-version +Impact: Informational +Confidence: High + - [ ] ID-1 +Version constraint ^0.8.13 contains known severe issues (https://solidity.readthedocs.io/en/latest/bugs.html) + - VerbatimInvalidDeduplication + - FullInlinerNonExpressionSplitArgumentEvaluationOrder + - MissingSideEffectsOnSelectorAccess + - StorageWriteRemovalBeforeConditionalTermination + - AbiReencodingHeadOverflowWithStaticArrayCleanup + - DirtyBytesArrayToStorage + - InlineAssemblyMemorySideEffects + - DataLocationChangeInInternalOverride + - NestedCalldataArrayAbiReencodingSizeValidation. +It is used by: + - [^0.8.13](src/OptionsToken.sol#L2) + - [^0.8.13](src/exercise/BaseExercise.sol#L2) + - [^0.8.13](src/exercise/DiscountExercise.sol#L2) + - [^0.8.13](src/helpers/SwapHelper.sol#L3) + - [^0.8.13](src/interfaces/IBalancer2TokensPool.sol#L2) + - [^0.8.13](src/interfaces/IExercise.sol#L2) + - [^0.8.13](src/interfaces/IOptionsToken.sol#L2) + - [^0.8.13](src/oracles/AlgebraOracle.sol#L2) + - [^0.8.13](src/oracles/BalancerOracle.sol#L2) + - [^0.8.13](src/oracles/ThenaOracle.sol#L2) + - [^0.8.13](src/oracles/UniswapV3Oracle.sol#L2) + +src/OptionsToken.sol#L2 + + +## immutable-states +Impact: Optimization +Confidence: High + - [ ] ID-2 +[ThenaOracle.isToken0](src/oracles/ThenaOracle.sol#L60) should be immutable + +src/oracles/ThenaOracle.sol#L60 + +## Base Exercise Slither + +Summary + - [arbitrary-send-erc20](#arbitrary-send-erc20) (2 results) (High) + - [reentrancy-events](#reentrancy-events) (5 results) (Low) + - [pragma](#pragma) (1 results) (Informational) + - [solc-version](#solc-version) (1 results) (Informational) + - [naming-convention](#naming-convention) (2 results) (Informational) +## arbitrary-send-erc20 +Impact: High +Confidence: High + - [ ] ID-0 +[BaseExercise.distributeFeesFrom(uint256,IERC20,address)](src/exercise/BaseExercise.sol#L73-L82) uses arbitrary from in transferFrom: [token.safeTransferFrom(from,feeRecipients[i],feeAmount)](src/exercise/BaseExercise.sol#L77) + +src/exercise/BaseExercise.sol#L73-L82 + + + - [ ] ID-1 +[BaseExercise.distributeFeesFrom(uint256,IERC20,address)](src/exercise/BaseExercise.sol#L73-L82) uses arbitrary from in transferFrom: [token.safeTransferFrom(from,feeRecipients[feeRecipients.length - 1],remaining)](src/exercise/BaseExercise.sol#L80) + +src/exercise/BaseExercise.sol#L73-L82 + + +## reentrancy-events +Impact: Low +Confidence: Medium + - [ ] ID-2 +Reentrancy in [DiscountExercise._zap(address,uint256,address,DiscountExerciseParams)](src/exercise/DiscountExercise.sol#L211-L249): + External calls: + - [underlyingToken.approve(swapProps.swapper,feeAmount)](src/exercise/DiscountExercise.sol#L228) + - [amountOut = _generalSwap(swapProps.exchangeTypes,address(underlyingToken),address(paymentToken),feeAmount,minAmountOut,swapProps.exchangeAddress)](src/exercise/DiscountExercise.sol#L230-L232) + - [_swapper.swapUniV2(tokenIn,tokenOut,amount,minAmountOutData,exchangeAddress)](src/helpers/SwapHelper.sol#L72) + - [_swapper.swapBal(tokenIn,tokenOut,amount,minAmountOutData,exchangeAddress)](src/helpers/SwapHelper.sol#L74) + - [_swapper.swapVelo(tokenIn,tokenOut,amount,minAmountOutData,exchangeAddress)](src/helpers/SwapHelper.sol#L76) + - [_swapper.swapUniV3(tokenIn,tokenOut,amount,minAmountOutData,exchangeAddress)](src/helpers/SwapHelper.sol#L78) + - [underlyingToken.approve(swapProps.swapper,0)](src/exercise/DiscountExercise.sol#L239) + - [distributeFees(paymentToken.balanceOf(address(this)),paymentToken)](src/exercise/DiscountExercise.sol#L242) + - [returndata = address(token).functionCall(data,SafeERC20: low-level call failed)](lib/openzeppelin-contracts/contracts/token/ERC20/utils/SafeERC20.sol#L110) + - [(success,returndata) = target.call{value: value}(data)](lib/openzeppelin-contracts/contracts/utils/Address.sol#L135) + - [token.safeTransfer(feeRecipients[i],feeAmount)](src/exercise/BaseExercise.sol#L90) + - [token.safeTransfer(feeRecipients[feeRecipients.length - 1],remaining)](src/exercise/BaseExercise.sol#L93) + - [_pay(recipient,underlyingAmount)](src/exercise/DiscountExercise.sol#L246) + - [returndata = address(token).functionCall(data,SafeERC20: low-level call failed)](lib/openzeppelin-contracts/contracts/token/ERC20/utils/SafeERC20.sol#L110) + - [underlyingToken.safeTransfer(to,balance)](src/exercise/DiscountExercise.sol#L272) + - [(success,returndata) = target.call{value: value}(data)](lib/openzeppelin-contracts/contracts/utils/Address.sol#L135) + - [underlyingToken.safeTransfer(to,amount)](src/exercise/DiscountExercise.sol#L275) + External calls sending eth: + - [distributeFees(paymentToken.balanceOf(address(this)),paymentToken)](src/exercise/DiscountExercise.sol#L242) + - [(success,returndata) = target.call{value: value}(data)](lib/openzeppelin-contracts/contracts/utils/Address.sol#L135) + - [_pay(recipient,underlyingAmount)](src/exercise/DiscountExercise.sol#L246) + - [(success,returndata) = target.call{value: value}(data)](lib/openzeppelin-contracts/contracts/utils/Address.sol#L135) + Event emitted after the call(s): + - [Exercised(from,recipient,underlyingAmount,paymentAmount)](src/exercise/DiscountExercise.sol#L248) + +src/exercise/DiscountExercise.sol#L211-L249 + + + - [ ] ID-3 +Reentrancy in [BaseExercise.distributeFees(uint256,IERC20)](src/exercise/BaseExercise.sol#L86-L95): + External calls: + - [token.safeTransfer(feeRecipients[i],feeAmount)](src/exercise/BaseExercise.sol#L90) + - [token.safeTransfer(feeRecipients[feeRecipients.length - 1],remaining)](src/exercise/BaseExercise.sol#L93) + Event emitted after the call(s): + - [DistributeFees(feeRecipients,feeBPS,totalAmount)](src/exercise/BaseExercise.sol#L94) + +src/exercise/BaseExercise.sol#L86-L95 + + + - [ ] ID-4 +Reentrancy in [DiscountExercise._zap(address,uint256,address,DiscountExerciseParams)](src/exercise/DiscountExercise.sol#L211-L249): + External calls: + - [underlyingToken.approve(swapProps.swapper,feeAmount)](src/exercise/DiscountExercise.sol#L228) + - [amountOut = _generalSwap(swapProps.exchangeTypes,address(underlyingToken),address(paymentToken),feeAmount,minAmountOut,swapProps.exchangeAddress)](src/exercise/DiscountExercise.sol#L230-L232) + - [_swapper.swapUniV2(tokenIn,tokenOut,amount,minAmountOutData,exchangeAddress)](src/helpers/SwapHelper.sol#L72) + - [_swapper.swapBal(tokenIn,tokenOut,amount,minAmountOutData,exchangeAddress)](src/helpers/SwapHelper.sol#L74) + - [_swapper.swapVelo(tokenIn,tokenOut,amount,minAmountOutData,exchangeAddress)](src/helpers/SwapHelper.sol#L76) + - [_swapper.swapUniV3(tokenIn,tokenOut,amount,minAmountOutData,exchangeAddress)](src/helpers/SwapHelper.sol#L78) + - [underlyingToken.approve(swapProps.swapper,0)](src/exercise/DiscountExercise.sol#L239) + - [distributeFees(paymentToken.balanceOf(address(this)),paymentToken)](src/exercise/DiscountExercise.sol#L242) + - [returndata = address(token).functionCall(data,SafeERC20: low-level call failed)](lib/openzeppelin-contracts/contracts/token/ERC20/utils/SafeERC20.sol#L110) + - [(success,returndata) = target.call{value: value}(data)](lib/openzeppelin-contracts/contracts/utils/Address.sol#L135) + - [token.safeTransfer(feeRecipients[i],feeAmount)](src/exercise/BaseExercise.sol#L90) + - [token.safeTransfer(feeRecipients[feeRecipients.length - 1],remaining)](src/exercise/BaseExercise.sol#L93) + External calls sending eth: + - [distributeFees(paymentToken.balanceOf(address(this)),paymentToken)](src/exercise/DiscountExercise.sol#L242) + - [(success,returndata) = target.call{value: value}(data)](lib/openzeppelin-contracts/contracts/utils/Address.sol#L135) + Event emitted after the call(s): + - [DistributeFees(feeRecipients,feeBPS,totalAmount)](src/exercise/BaseExercise.sol#L94) + - [distributeFees(paymentToken.balanceOf(address(this)),paymentToken)](src/exercise/DiscountExercise.sol#L242) + +src/exercise/DiscountExercise.sol#L211-L249 + + + - [ ] ID-5 +Reentrancy in [DiscountExercise._redeem(address,uint256,address,DiscountExerciseParams)](src/exercise/DiscountExercise.sol#L252-L267): + External calls: + - [distributeFeesFrom(paymentAmount,paymentToken,from)](src/exercise/DiscountExercise.sol#L262) + - [returndata = address(token).functionCall(data,SafeERC20: low-level call failed)](lib/openzeppelin-contracts/contracts/token/ERC20/utils/SafeERC20.sol#L110) + - [(success,returndata) = target.call{value: value}(data)](lib/openzeppelin-contracts/contracts/utils/Address.sol#L135) + - [token.safeTransferFrom(from,feeRecipients[i],feeAmount)](src/exercise/BaseExercise.sol#L77) + - [token.safeTransferFrom(from,feeRecipients[feeRecipients.length - 1],remaining)](src/exercise/BaseExercise.sol#L80) + - [_pay(recipient,amount)](src/exercise/DiscountExercise.sol#L264) + - [returndata = address(token).functionCall(data,SafeERC20: low-level call failed)](lib/openzeppelin-contracts/contracts/token/ERC20/utils/SafeERC20.sol#L110) + - [underlyingToken.safeTransfer(to,balance)](src/exercise/DiscountExercise.sol#L272) + - [(success,returndata) = target.call{value: value}(data)](lib/openzeppelin-contracts/contracts/utils/Address.sol#L135) + - [underlyingToken.safeTransfer(to,amount)](src/exercise/DiscountExercise.sol#L275) + External calls sending eth: + - [distributeFeesFrom(paymentAmount,paymentToken,from)](src/exercise/DiscountExercise.sol#L262) + - [(success,returndata) = target.call{value: value}(data)](lib/openzeppelin-contracts/contracts/utils/Address.sol#L135) + - [_pay(recipient,amount)](src/exercise/DiscountExercise.sol#L264) + - [(success,returndata) = target.call{value: value}(data)](lib/openzeppelin-contracts/contracts/utils/Address.sol#L135) + Event emitted after the call(s): + - [Exercised(from,recipient,amount,paymentAmount)](src/exercise/DiscountExercise.sol#L266) + +src/exercise/DiscountExercise.sol#L252-L267 + + + - [ ] ID-6 +Reentrancy in [BaseExercise.distributeFeesFrom(uint256,IERC20,address)](src/exercise/BaseExercise.sol#L73-L82): + External calls: + - [token.safeTransferFrom(from,feeRecipients[i],feeAmount)](src/exercise/BaseExercise.sol#L77) + - [token.safeTransferFrom(from,feeRecipients[feeRecipients.length - 1],remaining)](src/exercise/BaseExercise.sol#L80) + Event emitted after the call(s): + - [DistributeFees(feeRecipients,feeBPS,totalAmount)](src/exercise/BaseExercise.sol#L81) + +src/exercise/BaseExercise.sol#L73-L82 + + +## pragma +Impact: Informational +Confidence: High + - [ ] ID-7 +11 different versions of Solidity are used: + - Version constraint ^0.8.0 is used by: + -[^0.8.0](lib/openzeppelin-contracts-upgradeable/contracts/access/AccessControlEnumerableUpgradeable.sol#L4) + -[^0.8.0](lib/openzeppelin-contracts-upgradeable/contracts/access/AccessControlUpgradeable.sol#L4) + -[^0.8.0](lib/openzeppelin-contracts-upgradeable/contracts/access/IAccessControlEnumerableUpgradeable.sol#L4) + -[^0.8.0](lib/openzeppelin-contracts-upgradeable/contracts/access/IAccessControlUpgradeable.sol#L4) + -[^0.8.0](lib/openzeppelin-contracts-upgradeable/contracts/access/OwnableUpgradeable.sol#L4) + -[^0.8.0](lib/openzeppelin-contracts-upgradeable/contracts/interfaces/IERC1967Upgradeable.sol#L4) + -[^0.8.0](lib/openzeppelin-contracts-upgradeable/contracts/interfaces/draft-IERC1822Upgradeable.sol#L4) + -[^0.8.0](lib/openzeppelin-contracts-upgradeable/contracts/proxy/beacon/IBeaconUpgradeable.sol#L4) + -[^0.8.0](lib/openzeppelin-contracts-upgradeable/contracts/proxy/utils/UUPSUpgradeable.sol#L4) + -[^0.8.0](lib/openzeppelin-contracts-upgradeable/contracts/security/PausableUpgradeable.sol#L4) + -[^0.8.0](lib/openzeppelin-contracts-upgradeable/contracts/token/ERC20/ERC20Upgradeable.sol#L4) + -[^0.8.0](lib/openzeppelin-contracts-upgradeable/contracts/token/ERC20/IERC20Upgradeable.sol#L4) + -[^0.8.0](lib/openzeppelin-contracts-upgradeable/contracts/token/ERC20/extensions/IERC20MetadataUpgradeable.sol#L4) + -[^0.8.0](lib/openzeppelin-contracts-upgradeable/contracts/token/ERC20/extensions/draft-IERC20PermitUpgradeable.sol#L4) + -[^0.8.0](lib/openzeppelin-contracts-upgradeable/contracts/token/ERC20/utils/SafeERC20Upgradeable.sol#L4) + -[^0.8.0](lib/openzeppelin-contracts-upgradeable/contracts/utils/ContextUpgradeable.sol#L4) + -[^0.8.0](lib/openzeppelin-contracts-upgradeable/contracts/utils/StorageSlotUpgradeable.sol#L4) + -[^0.8.0](lib/openzeppelin-contracts-upgradeable/contracts/utils/StringsUpgradeable.sol#L4) + -[^0.8.0](lib/openzeppelin-contracts-upgradeable/contracts/utils/introspection/ERC165Upgradeable.sol#L4) + -[^0.8.0](lib/openzeppelin-contracts-upgradeable/contracts/utils/introspection/IERC165Upgradeable.sol#L4) + -[^0.8.0](lib/openzeppelin-contracts-upgradeable/contracts/utils/math/MathUpgradeable.sol#L4) + -[^0.8.0](lib/openzeppelin-contracts-upgradeable/contracts/utils/structs/EnumerableSetUpgradeable.sol#L5) + -[^0.8.0](lib/openzeppelin-contracts/contracts/security/Pausable.sol#L4) + -[^0.8.0](lib/openzeppelin-contracts/contracts/token/ERC20/IERC20.sol#L4) + -[^0.8.0](lib/openzeppelin-contracts/contracts/token/ERC20/extensions/draft-IERC20Permit.sol#L4) + -[^0.8.0](lib/openzeppelin-contracts/contracts/token/ERC20/utils/SafeERC20.sol#L4) + -[^0.8.0](lib/openzeppelin-contracts/contracts/utils/Context.sol#L4) + -[^0.8.0](lib/v3-core/contracts/libraries/FullMath.sol#L2) + -[^0.8.0](lib/v3-core/contracts/libraries/TickMath.sol#L2) + -[^0.8.0](lib/vault-v2/src/ReaperSwapper.sol#L3) + -[^0.8.0](lib/vault-v2/src/interfaces/AggregatorV3Interface.sol#L4) + -[^0.8.0](lib/vault-v2/src/interfaces/IAsset.sol#L3) + -[^0.8.0](lib/vault-v2/src/interfaces/IAuthorizer.sol#L3) + -[^0.8.0](lib/vault-v2/src/interfaces/IBasePool.sol#L3) + -[^0.8.0](lib/vault-v2/src/interfaces/IBaseWeightedPool.sol#L3) + -[^0.8.0](lib/vault-v2/src/interfaces/IBeetVault.sol#L3) + -[^0.8.0](lib/vault-v2/src/interfaces/IPoolSwapStructs.sol#L3) + -[^0.8.0](lib/vault-v2/src/interfaces/ISignaturesValidator.sol#L3) + -[^0.8.0](lib/vault-v2/src/interfaces/ISwapErrors.sol#L3) + -[^0.8.0](lib/vault-v2/src/interfaces/ISwapper.sol#L3) + -[^0.8.0](lib/vault-v2/src/interfaces/ISwapperSwaps.sol#L3) + -[^0.8.0](lib/vault-v2/src/interfaces/ITemporarilyPausable.sol#L3) + -[^0.8.0](lib/vault-v2/src/interfaces/IUniswapV2Router01.sol#L2) + -[^0.8.0](lib/vault-v2/src/interfaces/IVeloPair.sol#L2) + -[^0.8.0](lib/vault-v2/src/interfaces/IVeloRouter.sol#L2) + -[^0.8.0](lib/vault-v2/src/interfaces/IVeloV1AndV2Factory.sol#L2) + -[^0.8.0](lib/vault-v2/src/libraries/Babylonian.sol#L3) + -[^0.8.0](lib/vault-v2/src/libraries/ReaperMathUtils.sol#L3) + -[^0.8.0](lib/vault-v2/src/mixins/BalMixin.sol#L3) + -[^0.8.0](lib/vault-v2/src/mixins/ReaperAccessControl.sol#L5) + -[^0.8.0](lib/vault-v2/src/mixins/UniV2Mixin.sol#L3) + -[^0.8.0](lib/vault-v2/src/mixins/UniV3Mixin.sol#L3) + -[^0.8.0](lib/vault-v2/src/mixins/VeloSolidMixin.sol#L3) + -[^0.8.0](src/OptionsCompounder.sol#L3) + -[^0.8.0](src/interfaces/IBalancerTwapOracle.sol#L15) + -[^0.8.0](src/interfaces/IFlashLoanReceiver.sol#L2) + -[^0.8.0](src/interfaces/ILendingPool.sol#L2) + -[^0.8.0](src/interfaces/ILendingPoolAddressesProvider.sol#L2) + -[^0.8.0](src/interfaces/IOptionsCompounder.sol#L3) + -[^0.8.0](src/interfaces/ISwapperSwaps.sol#L3) + -[^0.8.0](src/libraries/DataTypes.sol#L2) + - Version constraint ^0.8.2 is used by: + -[^0.8.2](lib/openzeppelin-contracts-upgradeable/contracts/proxy/ERC1967/ERC1967UpgradeUpgradeable.sol#L4) + -[^0.8.2](lib/openzeppelin-contracts-upgradeable/contracts/proxy/utils/Initializable.sol#L4) + - Version constraint ^0.8.1 is used by: + -[^0.8.1](lib/openzeppelin-contracts-upgradeable/contracts/utils/AddressUpgradeable.sol#L4) + -[^0.8.1](lib/openzeppelin-contracts/contracts/utils/Address.sol#L4) + - Version constraint >=0.8.0 is used by: + -[>=0.8.0](lib/solmate/src/auth/Owned.sol#L2) + -[>=0.8.0](lib/solmate/src/tokens/ERC20.sol#L2) + -[>=0.8.0](lib/solmate/src/utils/FixedPointMathLib.sol#L2) + - Version constraint >=0.5.0 is used by: + -[>=0.5.0](lib/v3-core/contracts/interfaces/IUniswapV3Pool.sol#L2) + -[>=0.5.0](lib/v3-core/contracts/interfaces/pool/IUniswapV3PoolActions.sol#L2) + -[>=0.5.0](lib/v3-core/contracts/interfaces/pool/IUniswapV3PoolDerivedState.sol#L2) + -[>=0.5.0](lib/v3-core/contracts/interfaces/pool/IUniswapV3PoolErrors.sol#L2) + -[>=0.5.0](lib/v3-core/contracts/interfaces/pool/IUniswapV3PoolEvents.sol#L2) + -[>=0.5.0](lib/v3-core/contracts/interfaces/pool/IUniswapV3PoolImmutables.sol#L2) + -[>=0.5.0](lib/v3-core/contracts/interfaces/pool/IUniswapV3PoolOwnerActions.sol#L2) + -[>=0.5.0](lib/v3-core/contracts/interfaces/pool/IUniswapV3PoolState.sol#L2) + -[>=0.5.0](lib/vault-v2/src/interfaces/IPeripheryImmutableState.sol#L2) + -[>=0.5.0](lib/vault-v2/src/interfaces/IUniswapV3Factory.sol#L2) + -[>=0.5.0](lib/vault-v2/src/interfaces/IUniswapV3SwapCallback.sol#L2) + -[>=0.5.0](src/interfaces/IAlgebraPool.sol#L2) + - Version constraint >=0.7.5 is used by: + -[>=0.7.5](lib/vault-v2/src/interfaces/ISwapRouter.sol#L2) + - Version constraint >=0.6.2 is used by: + -[>=0.6.2](lib/vault-v2/src/interfaces/IUniswapV2Router02.sol#L2) + - Version constraint >=0.6.0 is used by: + -[>=0.6.0](lib/vault-v2/src/libraries/TransferHelper.sol#L2) + - Version constraint ^0.8.13 is used by: + -[^0.8.13](src/OptionsToken.sol#L2) + -[^0.8.13](src/exercise/BaseExercise.sol#L2) + -[^0.8.13](src/exercise/DiscountExercise.sol#L2) + -[^0.8.13](src/helpers/SwapHelper.sol#L3) + -[^0.8.13](src/interfaces/IBalancer2TokensPool.sol#L2) + -[^0.8.13](src/interfaces/IExercise.sol#L2) + -[^0.8.13](src/interfaces/IOptionsToken.sol#L2) + -[^0.8.13](src/oracles/AlgebraOracle.sol#L2) + -[^0.8.13](src/oracles/BalancerOracle.sol#L2) + -[^0.8.13](src/oracles/ThenaOracle.sol#L2) + -[^0.8.13](src/oracles/UniswapV3Oracle.sol#L2) + - Version constraint >=0.7.0<0.9.0 is used by: + -[>=0.7.0<0.9.0](src/interfaces/IBalancerVault.sol#L17) + -[>=0.7.0<0.9.0](src/interfaces/IERC20Mintable.sol#L3) + -[>=0.7.0<0.9.0](src/interfaces/IOracle.sol#L3) + - Version constraint >=0.5 is used by: + -[>=0.5](src/interfaces/IThenaPair.sol#L1) + +lib/openzeppelin-contracts-upgradeable/contracts/access/AccessControlEnumerableUpgradeable.sol#L4 + + +## solc-version +Impact: Informational +Confidence: High + - [ ] ID-8 +Version constraint ^0.8.13 contains known severe issues (https://solidity.readthedocs.io/en/latest/bugs.html) + - VerbatimInvalidDeduplication + - FullInlinerNonExpressionSplitArgumentEvaluationOrder + - MissingSideEffectsOnSelectorAccess + - StorageWriteRemovalBeforeConditionalTermination + - AbiReencodingHeadOverflowWithStaticArrayCleanup + - DirtyBytesArrayToStorage + - InlineAssemblyMemorySideEffects + - DataLocationChangeInInternalOverride + - NestedCalldataArrayAbiReencodingSizeValidation. +It is used by: + - [^0.8.13](src/OptionsToken.sol#L2) + - [^0.8.13](src/exercise/BaseExercise.sol#L2) + - [^0.8.13](src/exercise/DiscountExercise.sol#L2) + - [^0.8.13](src/helpers/SwapHelper.sol#L3) + - [^0.8.13](src/interfaces/IBalancer2TokensPool.sol#L2) + - [^0.8.13](src/interfaces/IExercise.sol#L2) + - [^0.8.13](src/interfaces/IOptionsToken.sol#L2) + - [^0.8.13](src/oracles/AlgebraOracle.sol#L2) + - [^0.8.13](src/oracles/BalancerOracle.sol#L2) + - [^0.8.13](src/oracles/ThenaOracle.sol#L2) + - [^0.8.13](src/oracles/UniswapV3Oracle.sol#L2) + +src/OptionsToken.sol#L2 + + +## naming-convention +Impact: Informational +Confidence: High + - [ ] ID-9 +Parameter [BaseExercise.setFees(address[],uint256[])._feeRecipients](src/exercise/BaseExercise.sol#L55) is not in mixedCase + +src/exercise/BaseExercise.sol#L55 + + + - [ ] ID-10 +Parameter [BaseExercise.setFees(address[],uint256[])._feeBPS](src/exercise/BaseExercise.sol#L55) is not in mixedCase + +src/exercise/BaseExercise.sol#L55 + +# Frontend Integration + +Frontend shall allow to go through 3 different scenarios: +- Pay [PaymentTokens](#paymenttoken) to [**redeem**](#redeem-flow) [UnderlyingTokens](#underlyingtoken) from OptionsTokens +- [**Zap**](#zap-flow) OptionsTokens into the [UnderlyingTokens](#underlyingtoken) +- [**Claim**](#claim-flow---optional) not exercised [UnderlyingTokens](#underlyingtoken) (due to lack of funds in exercise contract) -> this is probably optional frontend feature + +## Redeem flow + - Standard ERC20 approve action on [PaymentToken](#paymenttoken) + - Note: `getPaymentAmount(uint256 amount)` interface may be usefull to get correct amount of PaymentToken needed. + - Exercise optionsToken with following parameters: + - amount of options tokens to spend (defined by user) + - recipient of the [UnderlyingTokens](#underlyingtoken) transferred during exercise (user address) + - option of the exercise -> it is DiscountExercise contract address + - encoded params: + - maxPaymentAmount - calculated maximal payment amount (amount * price * multiplier). Price can be get from oracle contract using interface `getPrice()`. Multiplier can be get from DiscountExercise contract using interface `multiplier()` + - deadline - current block timestamp + - isInstantExit - determines whether it is redeem (false) or zap (true) action. **Shall be hardcoded to false.** + - Events emitted: + - `Exercise(address indexed sender, address indexed recipient, uint256 amount, address data0, uint256 data1, uint256 data2)` - from OptionsToken contract. data0, data1, data2 - are not used in this case. + - `Exercised(from, recipient, underlyingAmount, paymentAmount)` from DiscountExercise contract + - Note: Amount of [UnderlyingTokens](#underlyingtoken) to receive from redeeming is the same amount that is specified for optionsTokens. + +## Zap flow + - Call `exercise(uint256 amount, address recipient, address option, bytes calldata params)` from optionsToken contract with following parameters: + - amount of options tokens to spend (defined by user) + - recipient of the [UnderlyingTokens](#underlyingtoken) transferred during exercise (user address) + - option of the exercise -> it is DiscountExercise contract address + - encoded params: + - maxPaymentAmount - calculated maximal payment amount (amount * price * multiplier). Price can be get from oracle contract using interface `getPrice()`. Multiplier can be get from DiscountExercise contract using interface `multiplier()` + - deadline - current block timestamp + - isInstantExit - determines whether it is redeem (false) or zap (true) action. **Shall be hardcoded to true.** + - Events emitted: + - `Exercise(address indexed sender, address indexed recipient, uint256 amount, address data0, uint256 data1, uint256 data2)` - from OptionsToken contract. data0, data1, data2 - are not used in this case. + - `Exercised(from, recipient, underlyingAmount, paymentAmount)` from DiscountExercise contract + - Note: Amount of [UnderlyingTokens](#underlyingtoken) to receive from zapping is: amountOfOTokens * (1 - multiplier) * (1 - instantExitFee). Everything is denominated in BPS (10 000). InstantExitFee can be get by calling `instantExitFee()`. + - Note: `getPaymentAmount(uint256 amount)` interface may be usefull to get correct amount of PaymentToken needed. + - Note: Usually swap action happens here so standard events for swapping are here, but contract handles all actions like approvals etc + +## Claim flow - optional +- Call `claim(address to)` with address of recipient of the [UnderlyingTokens](#underlyingtoken) transferred during claiming process +- `Claimed(amount)` event is emitted + +## Changelog +Main change between version 1.0.0 deployed on Harbor is the zap feature which requires additional variable in `params` argument (`isInstantExit`) of the `exercise(uint256 amount, address recipient, address option, bytes calldata params)` interface. +Now frontend shall allow to obtain [UnderlyingTokens](#underlyingtoken) in two ways (redeem and zap). + +## Legend +### PaymentToken +Token used to pay for exercising options token. Ironclad -> MODE, Harbor -> WBNB +### UnderlyingToken +Token which can be obtained from exercising. Ironclad -> ICL, Harbor -> HBR + diff --git a/foundry.toml b/foundry.toml index 89e3e2d..8129a9c 100644 --- a/foundry.toml +++ b/foundry.toml @@ -1,7 +1,10 @@ [profile.default] optimizer_runs = 1000000 verbosity = 1 -via_ir = true +via_ir = false + +[fmt] +line_length = 150 # Extreme Fuzzing CI Profile :P [profile.ci] diff --git a/hardhat.config.ts b/hardhat.config.ts new file mode 100644 index 0000000..5a48c4b --- /dev/null +++ b/hardhat.config.ts @@ -0,0 +1,78 @@ +import { HardhatUserConfig } from "hardhat/config"; +import "@nomicfoundation/hardhat-toolbox"; +import '@openzeppelin/hardhat-upgrades'; +import "@nomicfoundation/hardhat-foundry"; +import glob from "glob"; +import {TASK_COMPILE_SOLIDITY_GET_SOURCE_PATHS} from "hardhat/builtin-tasks/task-names"; +import path from "path"; +import { subtask } from "hardhat/config"; + +import { config as dotenvConfig } from "dotenv"; + +// import "@nomiclabs/hardhat-ethers"; +import "@nomicfoundation/hardhat-verify"; +import "hardhat-contract-sizer"; + +dotenvConfig(); + +const PRIVATE_KEY = process.env.PRIVATE_KEY || ""; + +const config: HardhatUserConfig = { + solidity: { + version: "0.8.19", + settings: { + optimizer: { enabled: true, runs: 200 } + } + }, + sourcify: { + enabled: true + }, + paths: { + sources: "./src", + tests: "./test_hardhat", + }, + networks: { + op: { + url: "https://mainnet.optimism.io", + chainId: 10, + accounts: [`0x${PRIVATE_KEY}`], + }, + bsc: { + url: "https://bsc-dataseed.binance.org/", + chainId: 56, + accounts: [`0x${PRIVATE_KEY}`], + }, + mode: { + url: "https://mainnet.mode.network/", + chainId: 34443, + accounts: [`0x${PRIVATE_KEY}`], + }, + }, + etherscan: { + apiKey: { + bsc: process.env.ETHERSCAN_KEY || "", + }, + // customChains: [ + // { + // network: "mode", + // chainId: 34443, + // urls: { + // apiURL: "https://explorer.mode.network", + // browserURL: "https://explorer.mode.network" + // } + // } + // ] + }, + +}; + +subtask(TASK_COMPILE_SOLIDITY_GET_SOURCE_PATHS).setAction(async (_, hre, runSuper) => { + const paths = await runSuper(); + + const otherDirectoryGlob = path.join(hre.config.paths.root, "test", "**", "*.sol"); + const otherPaths = glob.sync(otherDirectoryGlob); + + return [...paths, ...otherPaths]; +}); + +export default config; diff --git a/lib/openzeppelin-contracts b/lib/openzeppelin-contracts new file mode 160000 index 0000000..0a25c19 --- /dev/null +++ b/lib/openzeppelin-contracts @@ -0,0 +1 @@ +Subproject commit 0a25c1940ca220686588c4af3ec526f725fe2582 diff --git a/lib/openzeppelin-contracts-upgradeable b/lib/openzeppelin-contracts-upgradeable new file mode 160000 index 0000000..58fa0f8 --- /dev/null +++ b/lib/openzeppelin-contracts-upgradeable @@ -0,0 +1 @@ +Subproject commit 58fa0f81c4036f1a3b616fdffad2fd27e5d5ce21 diff --git a/lib/v3-core b/lib/v3-core new file mode 160000 index 0000000..6562c52 --- /dev/null +++ b/lib/v3-core @@ -0,0 +1 @@ +Subproject commit 6562c52e8f75f0c10f9deaf44861847585fc8129 diff --git a/lib/vault-v2 b/lib/vault-v2 new file mode 160000 index 0000000..6cbaea2 --- /dev/null +++ b/lib/vault-v2 @@ -0,0 +1 @@ +Subproject commit 6cbaea2c6d3f6fbfa3aebab182f9a75d056f7a43 diff --git a/package-lock.json b/package-lock.json new file mode 100644 index 0000000..7265a9d --- /dev/null +++ b/package-lock.json @@ -0,0 +1,8519 @@ +{ + "name": "options-token", + "version": "1.0.0", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "options-token", + "version": "1.0.0", + "license": "AGPL-3.0", + "dependencies": { + "@nomicfoundation/hardhat-network-helpers": "^1.0.10" + }, + "devDependencies": { + "@nomicfoundation/hardhat-ethers": "^3.0.5", + "@nomicfoundation/hardhat-foundry": "^1.1.1", + "@nomicfoundation/hardhat-toolbox": "^4.0.0", + "@openzeppelin/hardhat-upgrades": "^3.0.1", + "dotenv": "^16.3.1", + "ethers": "^6.9.2", + "hardhat": "^2.19.4", + "hardhat-contract-sizer": "^2.10.0" + } + }, + "node_modules/@adraffy/ens-normalize": { + "version": "1.10.0", + "resolved": "https://registry.npmjs.org/@adraffy/ens-normalize/-/ens-normalize-1.10.0.tgz", + "integrity": "sha512-nA9XHtlAkYfJxY7bce8DcN7eKxWWCWkU+1GR9d+U6MbNpfwQp8TI7vqOsBsMcHoT4mBu2kypKoSKnghEzOOq5Q==", + "dev": true + }, + "node_modules/@aws-crypto/sha256-js": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/@aws-crypto/sha256-js/-/sha256-js-1.2.2.tgz", + "integrity": "sha512-Nr1QJIbW/afYYGzYvrF70LtaHrIRtd4TNAglX8BvlfxJLZ45SAmueIKYl5tWoNBPzp65ymXGFK0Bb1vZUpuc9g==", + "dev": true, + "dependencies": { + "@aws-crypto/util": "^1.2.2", + "@aws-sdk/types": "^3.1.0", + "tslib": "^1.11.1" + } + }, + "node_modules/@aws-crypto/sha256-js/node_modules/tslib": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", + "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==", + "dev": true + }, + "node_modules/@aws-crypto/util": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/@aws-crypto/util/-/util-1.2.2.tgz", + "integrity": "sha512-H8PjG5WJ4wz0UXAFXeJjWCW1vkvIJ3qUUD+rGRwJ2/hj+xT58Qle2MTql/2MGzkU+1JLAFuR6aJpLAjHwhmwwg==", + "dev": true, + "dependencies": { + "@aws-sdk/types": "^3.1.0", + "@aws-sdk/util-utf8-browser": "^3.0.0", + "tslib": "^1.11.1" + } + }, + "node_modules/@aws-crypto/util/node_modules/tslib": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", + "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==", + "dev": true + }, + "node_modules/@aws-sdk/types": { + "version": "3.485.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/types/-/types-3.485.0.tgz", + "integrity": "sha512-+QW32YQdvZRDOwrAQPo/qCyXoSjgXB6RwJwCwkd8ebJXRXw6tmGKIHaZqYHt/LtBymvnaBgBBADNa4+qFvlOFw==", + "dev": true, + "dependencies": { + "@smithy/types": "^2.8.0", + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@aws-sdk/types/node_modules/tslib": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz", + "integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==", + "dev": true + }, + "node_modules/@aws-sdk/util-utf8-browser": { + "version": "3.259.0", + "resolved": "https://registry.npmjs.org/@aws-sdk/util-utf8-browser/-/util-utf8-browser-3.259.0.tgz", + "integrity": "sha512-UvFa/vR+e19XookZF8RzFZBrw2EUkQWxiBW0yYQAhvk3C+QVGl0H3ouca8LDBlBfQKXwmW3huo/59H8rwb1wJw==", + "dev": true, + "dependencies": { + "tslib": "^2.3.1" + } + }, + "node_modules/@chainsafe/as-sha256": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/@chainsafe/as-sha256/-/as-sha256-0.3.1.tgz", + "integrity": "sha512-hldFFYuf49ed7DAakWVXSJODuq3pzJEguD8tQ7h+sGkM18vja+OFoJI9krnGmgzyuZC2ETX0NOIcCTy31v2Mtg==" + }, + "node_modules/@chainsafe/persistent-merkle-tree": { + "version": "0.4.2", + "resolved": "https://registry.npmjs.org/@chainsafe/persistent-merkle-tree/-/persistent-merkle-tree-0.4.2.tgz", + "integrity": "sha512-lLO3ihKPngXLTus/L7WHKaw9PnNJWizlOF1H9NNzHP6Xvh82vzg9F2bzkXhYIFshMZ2gTCEz8tq6STe7r5NDfQ==", + "dependencies": { + "@chainsafe/as-sha256": "^0.3.1" + } + }, + "node_modules/@chainsafe/ssz": { + "version": "0.9.4", + "resolved": "https://registry.npmjs.org/@chainsafe/ssz/-/ssz-0.9.4.tgz", + "integrity": "sha512-77Qtg2N1ayqs4Bg/wvnWfg5Bta7iy7IRh8XqXh7oNMeP2HBbBwx8m6yTpA8p0EHItWPEBkgZd5S5/LSlp3GXuQ==", + "dependencies": { + "@chainsafe/as-sha256": "^0.3.1", + "@chainsafe/persistent-merkle-tree": "^0.4.2", + "case": "^1.6.3" + } + }, + "node_modules/@colors/colors": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/@colors/colors/-/colors-1.5.0.tgz", + "integrity": "sha512-ooWCrlZP11i8GImSjTHYHLkvFDP48nS4+204nGb1RiX/WXYHmJA2III9/e2DWVabCESdW7hBAEzHRqUn9OUVvQ==", + "dev": true, + "optional": true, + "engines": { + "node": ">=0.1.90" + } + }, + "node_modules/@cspotcode/source-map-support": { + "version": "0.8.1", + "resolved": "https://registry.npmjs.org/@cspotcode/source-map-support/-/source-map-support-0.8.1.tgz", + "integrity": "sha512-IchNf6dN4tHoMFIn/7OE8LWZ19Y6q/67Bmf6vnGREv8RSbBVb9LPJxEcnwrcwX6ixSvaiGoomAUvu4YSxXrVgw==", + "devOptional": true, + "peer": true, + "dependencies": { + "@jridgewell/trace-mapping": "0.3.9" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/@ethereumjs/rlp": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/@ethereumjs/rlp/-/rlp-4.0.1.tgz", + "integrity": "sha512-tqsQiBQDQdmPWE1xkkBq4rlSW5QZpLOUJ5RJh2/9fug+q9tnUhuZoVLk7s0scUIKTOzEtR72DFBXI4WiZcMpvw==", + "dev": true, + "peer": true, + "bin": { + "rlp": "bin/rlp" + }, + "engines": { + "node": ">=14" + } + }, + "node_modules/@ethereumjs/util": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/@ethereumjs/util/-/util-8.1.0.tgz", + "integrity": "sha512-zQ0IqbdX8FZ9aw11vP+dZkKDkS+kgIvQPHnSAXzP9pLu+Rfu3D3XEeLbicvoXJTYnhZiPmsZUxgdzXwNKxRPbA==", + "dev": true, + "peer": true, + "dependencies": { + "@ethereumjs/rlp": "^4.0.1", + "ethereum-cryptography": "^2.0.0", + "micro-ftch": "^0.3.1" + }, + "engines": { + "node": ">=14" + } + }, + "node_modules/@ethereumjs/util/node_modules/@noble/curves": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@noble/curves/-/curves-1.1.0.tgz", + "integrity": "sha512-091oBExgENk/kGj3AZmtBDMpxQPDtxQABR2B9lb1JbVTs6ytdzZNwvhxQ4MWasRNEzlbEH8jCWFCwhF/Obj5AA==", + "dev": true, + "peer": true, + "dependencies": { + "@noble/hashes": "1.3.1" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/@ethereumjs/util/node_modules/@noble/hashes": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/@noble/hashes/-/hashes-1.3.1.tgz", + "integrity": "sha512-EbqwksQwz9xDRGfDST86whPBgM65E0OH/pCgqW0GBVzO22bNE+NuIbeTb714+IfSjU3aRk47EUvXIb5bTsenKA==", + "dev": true, + "peer": true, + "engines": { + "node": ">= 16" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/@ethereumjs/util/node_modules/ethereum-cryptography": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ethereum-cryptography/-/ethereum-cryptography-2.1.2.tgz", + "integrity": "sha512-Z5Ba0T0ImZ8fqXrJbpHcbpAvIswRte2wGNR/KePnu8GbbvgJ47lMxT/ZZPG6i9Jaht4azPDop4HaM00J0J59ug==", + "dev": true, + "peer": true, + "dependencies": { + "@noble/curves": "1.1.0", + "@noble/hashes": "1.3.1", + "@scure/bip32": "1.3.1", + "@scure/bip39": "1.2.1" + } + }, + "node_modules/@ethersproject/abi": { + "version": "5.7.0", + "resolved": "https://registry.npmjs.org/@ethersproject/abi/-/abi-5.7.0.tgz", + "integrity": "sha512-351ktp42TiRcYB3H1OP8yajPeAQstMW/yCFokj/AthP9bLHzQFPlOrxOcwYEDkUAICmOHljvN4K39OMTMUa9RA==", + "funding": [ + { + "type": "individual", + "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2" + }, + { + "type": "individual", + "url": "https://www.buymeacoffee.com/ricmoo" + } + ], + "dependencies": { + "@ethersproject/address": "^5.7.0", + "@ethersproject/bignumber": "^5.7.0", + "@ethersproject/bytes": "^5.7.0", + "@ethersproject/constants": "^5.7.0", + "@ethersproject/hash": "^5.7.0", + "@ethersproject/keccak256": "^5.7.0", + "@ethersproject/logger": "^5.7.0", + "@ethersproject/properties": "^5.7.0", + "@ethersproject/strings": "^5.7.0" + } + }, + "node_modules/@ethersproject/abstract-provider": { + "version": "5.7.0", + "resolved": "https://registry.npmjs.org/@ethersproject/abstract-provider/-/abstract-provider-5.7.0.tgz", + "integrity": "sha512-R41c9UkchKCpAqStMYUpdunjo3pkEvZC3FAwZn5S5MGbXoMQOHIdHItezTETxAO5bevtMApSyEhn9+CHcDsWBw==", + "funding": [ + { + "type": "individual", + "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2" + }, + { + "type": "individual", + "url": "https://www.buymeacoffee.com/ricmoo" + } + ], + "dependencies": { + "@ethersproject/bignumber": "^5.7.0", + "@ethersproject/bytes": "^5.7.0", + "@ethersproject/logger": "^5.7.0", + "@ethersproject/networks": "^5.7.0", + "@ethersproject/properties": "^5.7.0", + "@ethersproject/transactions": "^5.7.0", + "@ethersproject/web": "^5.7.0" + } + }, + "node_modules/@ethersproject/abstract-signer": { + "version": "5.7.0", + "resolved": "https://registry.npmjs.org/@ethersproject/abstract-signer/-/abstract-signer-5.7.0.tgz", + "integrity": "sha512-a16V8bq1/Cz+TGCkE2OPMTOUDLS3grCpdjoJCYNnVBbdYEMSgKrU0+B90s8b6H+ByYTBZN7a3g76jdIJi7UfKQ==", + "funding": [ + { + "type": "individual", + "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2" + }, + { + "type": "individual", + "url": "https://www.buymeacoffee.com/ricmoo" + } + ], + "dependencies": { + "@ethersproject/abstract-provider": "^5.7.0", + "@ethersproject/bignumber": "^5.7.0", + "@ethersproject/bytes": "^5.7.0", + "@ethersproject/logger": "^5.7.0", + "@ethersproject/properties": "^5.7.0" + } + }, + "node_modules/@ethersproject/address": { + "version": "5.7.0", + "resolved": "https://registry.npmjs.org/@ethersproject/address/-/address-5.7.0.tgz", + "integrity": "sha512-9wYhYt7aghVGo758POM5nqcOMaE168Q6aRLJZwUmiqSrAungkG74gSSeKEIR7ukixesdRZGPgVqme6vmxs1fkA==", + "funding": [ + { + "type": "individual", + "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2" + }, + { + "type": "individual", + "url": "https://www.buymeacoffee.com/ricmoo" + } + ], + "dependencies": { + "@ethersproject/bignumber": "^5.7.0", + "@ethersproject/bytes": "^5.7.0", + "@ethersproject/keccak256": "^5.7.0", + "@ethersproject/logger": "^5.7.0", + "@ethersproject/rlp": "^5.7.0" + } + }, + "node_modules/@ethersproject/base64": { + "version": "5.7.0", + "resolved": "https://registry.npmjs.org/@ethersproject/base64/-/base64-5.7.0.tgz", + "integrity": "sha512-Dr8tcHt2mEbsZr/mwTPIQAf3Ai0Bks/7gTw9dSqk1mQvhW3XvRlmDJr/4n+wg1JmCl16NZue17CDh8xb/vZ0sQ==", + "funding": [ + { + "type": "individual", + "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2" + }, + { + "type": "individual", + "url": "https://www.buymeacoffee.com/ricmoo" + } + ], + "dependencies": { + "@ethersproject/bytes": "^5.7.0" + } + }, + "node_modules/@ethersproject/basex": { + "version": "5.7.0", + "resolved": "https://registry.npmjs.org/@ethersproject/basex/-/basex-5.7.0.tgz", + "integrity": "sha512-ywlh43GwZLv2Voc2gQVTKBoVQ1mti3d8HK5aMxsfu/nRDnMmNqaSJ3r3n85HBByT8OpoY96SXM1FogC533T4zw==", + "funding": [ + { + "type": "individual", + "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2" + }, + { + "type": "individual", + "url": "https://www.buymeacoffee.com/ricmoo" + } + ], + "dependencies": { + "@ethersproject/bytes": "^5.7.0", + "@ethersproject/properties": "^5.7.0" + } + }, + "node_modules/@ethersproject/bignumber": { + "version": "5.7.0", + "resolved": "https://registry.npmjs.org/@ethersproject/bignumber/-/bignumber-5.7.0.tgz", + "integrity": "sha512-n1CAdIHRWjSucQO3MC1zPSVgV/6dy/fjL9pMrPP9peL+QxEg9wOsVqwD4+818B6LUEtaXzVHQiuivzRoxPxUGw==", + "funding": [ + { + "type": "individual", + "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2" + }, + { + "type": "individual", + "url": "https://www.buymeacoffee.com/ricmoo" + } + ], + "dependencies": { + "@ethersproject/bytes": "^5.7.0", + "@ethersproject/logger": "^5.7.0", + "bn.js": "^5.2.1" + } + }, + "node_modules/@ethersproject/bytes": { + "version": "5.7.0", + "resolved": "https://registry.npmjs.org/@ethersproject/bytes/-/bytes-5.7.0.tgz", + "integrity": "sha512-nsbxwgFXWh9NyYWo+U8atvmMsSdKJprTcICAkvbBffT75qDocbuggBU0SJiVK2MuTrp0q+xvLkTnGMPK1+uA9A==", + "funding": [ + { + "type": "individual", + "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2" + }, + { + "type": "individual", + "url": "https://www.buymeacoffee.com/ricmoo" + } + ], + "dependencies": { + "@ethersproject/logger": "^5.7.0" + } + }, + "node_modules/@ethersproject/constants": { + "version": "5.7.0", + "resolved": "https://registry.npmjs.org/@ethersproject/constants/-/constants-5.7.0.tgz", + "integrity": "sha512-DHI+y5dBNvkpYUMiRQyxRBYBefZkJfo70VUkUAsRjcPs47muV9evftfZ0PJVCXYbAiCgght0DtcF9srFQmIgWA==", + "funding": [ + { + "type": "individual", + "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2" + }, + { + "type": "individual", + "url": "https://www.buymeacoffee.com/ricmoo" + } + ], + "dependencies": { + "@ethersproject/bignumber": "^5.7.0" + } + }, + "node_modules/@ethersproject/contracts": { + "version": "5.7.0", + "resolved": "https://registry.npmjs.org/@ethersproject/contracts/-/contracts-5.7.0.tgz", + "integrity": "sha512-5GJbzEU3X+d33CdfPhcyS+z8MzsTrBGk/sc+G+59+tPa9yFkl6HQ9D6L0QMgNTA9q8dT0XKxxkyp883XsQvbbg==", + "funding": [ + { + "type": "individual", + "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2" + }, + { + "type": "individual", + "url": "https://www.buymeacoffee.com/ricmoo" + } + ], + "dependencies": { + "@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" + } + }, + "node_modules/@ethersproject/hash": { + "version": "5.7.0", + "resolved": "https://registry.npmjs.org/@ethersproject/hash/-/hash-5.7.0.tgz", + "integrity": "sha512-qX5WrQfnah1EFnO5zJv1v46a8HW0+E5xuBBDTwMFZLuVTx0tbU2kkx15NqdjxecrLGatQN9FGQKpb1FKdHCt+g==", + "funding": [ + { + "type": "individual", + "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2" + }, + { + "type": "individual", + "url": "https://www.buymeacoffee.com/ricmoo" + } + ], + "dependencies": { + "@ethersproject/abstract-signer": "^5.7.0", + "@ethersproject/address": "^5.7.0", + "@ethersproject/base64": "^5.7.0", + "@ethersproject/bignumber": "^5.7.0", + "@ethersproject/bytes": "^5.7.0", + "@ethersproject/keccak256": "^5.7.0", + "@ethersproject/logger": "^5.7.0", + "@ethersproject/properties": "^5.7.0", + "@ethersproject/strings": "^5.7.0" + } + }, + "node_modules/@ethersproject/hdnode": { + "version": "5.7.0", + "resolved": "https://registry.npmjs.org/@ethersproject/hdnode/-/hdnode-5.7.0.tgz", + "integrity": "sha512-OmyYo9EENBPPf4ERhR7oj6uAtUAhYGqOnIS+jE5pTXvdKBS99ikzq1E7Iv0ZQZ5V36Lqx1qZLeak0Ra16qpeOg==", + "funding": [ + { + "type": "individual", + "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2" + }, + { + "type": "individual", + "url": "https://www.buymeacoffee.com/ricmoo" + } + ], + "dependencies": { + "@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" + } + }, + "node_modules/@ethersproject/json-wallets": { + "version": "5.7.0", + "resolved": "https://registry.npmjs.org/@ethersproject/json-wallets/-/json-wallets-5.7.0.tgz", + "integrity": "sha512-8oee5Xgu6+RKgJTkvEMl2wDgSPSAQ9MB/3JYjFV9jlKvcYHUXZC+cQp0njgmxdHkYWn8s6/IqIZYm0YWCjO/0g==", + "funding": [ + { + "type": "individual", + "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2" + }, + { + "type": "individual", + "url": "https://www.buymeacoffee.com/ricmoo" + } + ], + "dependencies": { + "@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" + } + }, + "node_modules/@ethersproject/json-wallets/node_modules/aes-js": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/aes-js/-/aes-js-3.0.0.tgz", + "integrity": "sha512-H7wUZRn8WpTq9jocdxQ2c8x2sKo9ZVmzfRE13GiNJXfp7NcKYEdvl3vspKjXox6RIG2VtaRe4JFvxG4rqp2Zuw==" + }, + "node_modules/@ethersproject/keccak256": { + "version": "5.7.0", + "resolved": "https://registry.npmjs.org/@ethersproject/keccak256/-/keccak256-5.7.0.tgz", + "integrity": "sha512-2UcPboeL/iW+pSg6vZ6ydF8tCnv3Iu/8tUmLLzWWGzxWKFFqOBQFLo6uLUv6BDrLgCDfN28RJ/wtByx+jZ4KBg==", + "funding": [ + { + "type": "individual", + "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2" + }, + { + "type": "individual", + "url": "https://www.buymeacoffee.com/ricmoo" + } + ], + "dependencies": { + "@ethersproject/bytes": "^5.7.0", + "js-sha3": "0.8.0" + } + }, + "node_modules/@ethersproject/logger": { + "version": "5.7.0", + "resolved": "https://registry.npmjs.org/@ethersproject/logger/-/logger-5.7.0.tgz", + "integrity": "sha512-0odtFdXu/XHtjQXJYA3u9G0G8btm0ND5Cu8M7i5vhEcE8/HmF4Lbdqanwyv4uQTr2tx6b7fQRmgLrsnpQlmnig==", + "funding": [ + { + "type": "individual", + "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2" + }, + { + "type": "individual", + "url": "https://www.buymeacoffee.com/ricmoo" + } + ] + }, + "node_modules/@ethersproject/networks": { + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/@ethersproject/networks/-/networks-5.7.1.tgz", + "integrity": "sha512-n/MufjFYv3yFcUyfhnXotyDlNdFb7onmkSy8aQERi2PjNcnWQ66xXxa3XlS8nCcA8aJKJjIIMNJTC7tu80GwpQ==", + "funding": [ + { + "type": "individual", + "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2" + }, + { + "type": "individual", + "url": "https://www.buymeacoffee.com/ricmoo" + } + ], + "dependencies": { + "@ethersproject/logger": "^5.7.0" + } + }, + "node_modules/@ethersproject/pbkdf2": { + "version": "5.7.0", + "resolved": "https://registry.npmjs.org/@ethersproject/pbkdf2/-/pbkdf2-5.7.0.tgz", + "integrity": "sha512-oR/dBRZR6GTyaofd86DehG72hY6NpAjhabkhxgr3X2FpJtJuodEl2auADWBZfhDHgVCbu3/H/Ocq2uC6dpNjjw==", + "funding": [ + { + "type": "individual", + "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2" + }, + { + "type": "individual", + "url": "https://www.buymeacoffee.com/ricmoo" + } + ], + "dependencies": { + "@ethersproject/bytes": "^5.7.0", + "@ethersproject/sha2": "^5.7.0" + } + }, + "node_modules/@ethersproject/properties": { + "version": "5.7.0", + "resolved": "https://registry.npmjs.org/@ethersproject/properties/-/properties-5.7.0.tgz", + "integrity": "sha512-J87jy8suntrAkIZtecpxEPxY//szqr1mlBaYlQ0r4RCaiD2hjheqF9s1LVE8vVuJCXisjIP+JgtK/Do54ej4Sw==", + "funding": [ + { + "type": "individual", + "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2" + }, + { + "type": "individual", + "url": "https://www.buymeacoffee.com/ricmoo" + } + ], + "dependencies": { + "@ethersproject/logger": "^5.7.0" + } + }, + "node_modules/@ethersproject/providers": { + "version": "5.7.2", + "resolved": "https://registry.npmjs.org/@ethersproject/providers/-/providers-5.7.2.tgz", + "integrity": "sha512-g34EWZ1WWAVgr4aptGlVBF8mhl3VWjv+8hoAnzStu8Ah22VHBsuGzP17eb6xDVRzw895G4W7vvx60lFFur/1Rg==", + "funding": [ + { + "type": "individual", + "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2" + }, + { + "type": "individual", + "url": "https://www.buymeacoffee.com/ricmoo" + } + ], + "dependencies": { + "@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" + } + }, + "node_modules/@ethersproject/providers/node_modules/ws": { + "version": "7.4.6", + "resolved": "https://registry.npmjs.org/ws/-/ws-7.4.6.tgz", + "integrity": "sha512-YmhHDO4MzaDLB+M9ym/mDA5z0naX8j7SIlT8f8z+I0VtzsRbekxEutHSme7NPS2qE8StCYQNUnfWdXta/Yu85A==", + "engines": { + "node": ">=8.3.0" + }, + "peerDependencies": { + "bufferutil": "^4.0.1", + "utf-8-validate": "^5.0.2" + }, + "peerDependenciesMeta": { + "bufferutil": { + "optional": true + }, + "utf-8-validate": { + "optional": true + } + } + }, + "node_modules/@ethersproject/random": { + "version": "5.7.0", + "resolved": "https://registry.npmjs.org/@ethersproject/random/-/random-5.7.0.tgz", + "integrity": "sha512-19WjScqRA8IIeWclFme75VMXSBvi4e6InrUNuaR4s5pTF2qNhcGdCUwdxUVGtDDqC00sDLCO93jPQoDUH4HVmQ==", + "funding": [ + { + "type": "individual", + "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2" + }, + { + "type": "individual", + "url": "https://www.buymeacoffee.com/ricmoo" + } + ], + "dependencies": { + "@ethersproject/bytes": "^5.7.0", + "@ethersproject/logger": "^5.7.0" + } + }, + "node_modules/@ethersproject/rlp": { + "version": "5.7.0", + "resolved": "https://registry.npmjs.org/@ethersproject/rlp/-/rlp-5.7.0.tgz", + "integrity": "sha512-rBxzX2vK8mVF7b0Tol44t5Tb8gomOHkj5guL+HhzQ1yBh/ydjGnpw6at+X6Iw0Kp3OzzzkcKp8N9r0W4kYSs9w==", + "funding": [ + { + "type": "individual", + "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2" + }, + { + "type": "individual", + "url": "https://www.buymeacoffee.com/ricmoo" + } + ], + "dependencies": { + "@ethersproject/bytes": "^5.7.0", + "@ethersproject/logger": "^5.7.0" + } + }, + "node_modules/@ethersproject/sha2": { + "version": "5.7.0", + "resolved": "https://registry.npmjs.org/@ethersproject/sha2/-/sha2-5.7.0.tgz", + "integrity": "sha512-gKlH42riwb3KYp0reLsFTokByAKoJdgFCwI+CCiX/k+Jm2mbNs6oOaCjYQSlI1+XBVejwH2KrmCbMAT/GnRDQw==", + "funding": [ + { + "type": "individual", + "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2" + }, + { + "type": "individual", + "url": "https://www.buymeacoffee.com/ricmoo" + } + ], + "dependencies": { + "@ethersproject/bytes": "^5.7.0", + "@ethersproject/logger": "^5.7.0", + "hash.js": "1.1.7" + } + }, + "node_modules/@ethersproject/signing-key": { + "version": "5.7.0", + "resolved": "https://registry.npmjs.org/@ethersproject/signing-key/-/signing-key-5.7.0.tgz", + "integrity": "sha512-MZdy2nL3wO0u7gkB4nA/pEf8lu1TlFswPNmy8AiYkfKTdO6eXBJyUdmHO/ehm/htHw9K/qF8ujnTyUAD+Ry54Q==", + "funding": [ + { + "type": "individual", + "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2" + }, + { + "type": "individual", + "url": "https://www.buymeacoffee.com/ricmoo" + } + ], + "dependencies": { + "@ethersproject/bytes": "^5.7.0", + "@ethersproject/logger": "^5.7.0", + "@ethersproject/properties": "^5.7.0", + "bn.js": "^5.2.1", + "elliptic": "6.5.4", + "hash.js": "1.1.7" + } + }, + "node_modules/@ethersproject/solidity": { + "version": "5.7.0", + "resolved": "https://registry.npmjs.org/@ethersproject/solidity/-/solidity-5.7.0.tgz", + "integrity": "sha512-HmabMd2Dt/raavyaGukF4XxizWKhKQ24DoLtdNbBmNKUOPqwjsKQSdV9GQtj9CBEea9DlzETlVER1gYeXXBGaA==", + "funding": [ + { + "type": "individual", + "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2" + }, + { + "type": "individual", + "url": "https://www.buymeacoffee.com/ricmoo" + } + ], + "dependencies": { + "@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" + } + }, + "node_modules/@ethersproject/strings": { + "version": "5.7.0", + "resolved": "https://registry.npmjs.org/@ethersproject/strings/-/strings-5.7.0.tgz", + "integrity": "sha512-/9nu+lj0YswRNSH0NXYqrh8775XNyEdUQAuf3f+SmOrnVewcJ5SBNAjF7lpgehKi4abvNNXyf+HX86czCdJ8Mg==", + "funding": [ + { + "type": "individual", + "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2" + }, + { + "type": "individual", + "url": "https://www.buymeacoffee.com/ricmoo" + } + ], + "dependencies": { + "@ethersproject/bytes": "^5.7.0", + "@ethersproject/constants": "^5.7.0", + "@ethersproject/logger": "^5.7.0" + } + }, + "node_modules/@ethersproject/transactions": { + "version": "5.7.0", + "resolved": "https://registry.npmjs.org/@ethersproject/transactions/-/transactions-5.7.0.tgz", + "integrity": "sha512-kmcNicCp1lp8qanMTC3RIikGgoJ80ztTyvtsFvCYpSCfkjhD0jZ2LOrnbcuxuToLIUYYf+4XwD1rP+B/erDIhQ==", + "funding": [ + { + "type": "individual", + "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2" + }, + { + "type": "individual", + "url": "https://www.buymeacoffee.com/ricmoo" + } + ], + "dependencies": { + "@ethersproject/address": "^5.7.0", + "@ethersproject/bignumber": "^5.7.0", + "@ethersproject/bytes": "^5.7.0", + "@ethersproject/constants": "^5.7.0", + "@ethersproject/keccak256": "^5.7.0", + "@ethersproject/logger": "^5.7.0", + "@ethersproject/properties": "^5.7.0", + "@ethersproject/rlp": "^5.7.0", + "@ethersproject/signing-key": "^5.7.0" + } + }, + "node_modules/@ethersproject/units": { + "version": "5.7.0", + "resolved": "https://registry.npmjs.org/@ethersproject/units/-/units-5.7.0.tgz", + "integrity": "sha512-pD3xLMy3SJu9kG5xDGI7+xhTEmGXlEqXU4OfNapmfnxLVY4EMSSRp7j1k7eezutBPH7RBN/7QPnwR7hzNlEFeg==", + "funding": [ + { + "type": "individual", + "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2" + }, + { + "type": "individual", + "url": "https://www.buymeacoffee.com/ricmoo" + } + ], + "dependencies": { + "@ethersproject/bignumber": "^5.7.0", + "@ethersproject/constants": "^5.7.0", + "@ethersproject/logger": "^5.7.0" + } + }, + "node_modules/@ethersproject/wallet": { + "version": "5.7.0", + "resolved": "https://registry.npmjs.org/@ethersproject/wallet/-/wallet-5.7.0.tgz", + "integrity": "sha512-MhmXlJXEJFBFVKrDLB4ZdDzxcBxQ3rLyCkhNqVu3CDYvR97E+8r01UgrI+TI99Le+aYm/in/0vp86guJuM7FCA==", + "funding": [ + { + "type": "individual", + "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2" + }, + { + "type": "individual", + "url": "https://www.buymeacoffee.com/ricmoo" + } + ], + "dependencies": { + "@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" + } + }, + "node_modules/@ethersproject/web": { + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/@ethersproject/web/-/web-5.7.1.tgz", + "integrity": "sha512-Gueu8lSvyjBWL4cYsWsjh6MtMwM0+H4HvqFPZfB6dV8ctbP9zFAO73VG1cMWae0FLPCtz0peKPpZY8/ugJJX2w==", + "funding": [ + { + "type": "individual", + "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2" + }, + { + "type": "individual", + "url": "https://www.buymeacoffee.com/ricmoo" + } + ], + "dependencies": { + "@ethersproject/base64": "^5.7.0", + "@ethersproject/bytes": "^5.7.0", + "@ethersproject/logger": "^5.7.0", + "@ethersproject/properties": "^5.7.0", + "@ethersproject/strings": "^5.7.0" + } + }, + "node_modules/@ethersproject/wordlists": { + "version": "5.7.0", + "resolved": "https://registry.npmjs.org/@ethersproject/wordlists/-/wordlists-5.7.0.tgz", + "integrity": "sha512-S2TFNJNfHWVHNE6cNDjbVlZ6MgE17MIxMbMg2zv3wn+3XSJGosL1m9ZVv3GXCf/2ymSsQ+hRI5IzoMJTG6aoVA==", + "funding": [ + { + "type": "individual", + "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2" + }, + { + "type": "individual", + "url": "https://www.buymeacoffee.com/ricmoo" + } + ], + "dependencies": { + "@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" + } + }, + "node_modules/@fastify/busboy": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/@fastify/busboy/-/busboy-2.1.0.tgz", + "integrity": "sha512-+KpH+QxZU7O4675t3mnkQKcZZg56u+K/Ct2K+N2AZYNVK8kyeo/bI18tI8aPm3tvNNRyTWfj6s5tnGNlcbQRsA==", + "engines": { + "node": ">=14" + } + }, + "node_modules/@jridgewell/resolve-uri": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.1.tgz", + "integrity": "sha512-dSYZh7HhCDtCKm4QakX0xFpsRDqjjtZf/kjI/v3T3Nwt5r8/qz/M19F9ySyOqU94SXBmeG9ttTul+YnR4LOxFA==", + "devOptional": true, + "peer": true, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@jridgewell/sourcemap-codec": { + "version": "1.4.15", + "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.15.tgz", + "integrity": "sha512-eF2rxCRulEKXHTRiDrDy6erMYWqNw4LPdQ8UQA4huuxaQsVeRPFl2oM8oDGxMFhJUWZf9McpLtJasDDZb/Bpeg==", + "devOptional": true, + "peer": true + }, + "node_modules/@jridgewell/trace-mapping": { + "version": "0.3.9", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.9.tgz", + "integrity": "sha512-3Belt6tdc8bPgAtbcmdtNJlirVoTmEb5e2gC94PnkwEW9jI6CAHUeoG85tjWP5WquqfavoMtMwiG4P926ZKKuQ==", + "devOptional": true, + "peer": true, + "dependencies": { + "@jridgewell/resolve-uri": "^3.0.3", + "@jridgewell/sourcemap-codec": "^1.4.10" + } + }, + "node_modules/@metamask/eth-sig-util": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/@metamask/eth-sig-util/-/eth-sig-util-4.0.1.tgz", + "integrity": "sha512-tghyZKLHZjcdlDqCA3gNZmLeR0XvOE9U1qoQO9ohyAZT6Pya+H9vkBPcsyXytmYLNgVoin7CKCmweo/R43V+tQ==", + "dependencies": { + "ethereumjs-abi": "^0.6.8", + "ethereumjs-util": "^6.2.1", + "ethjs-util": "^0.1.6", + "tweetnacl": "^1.0.3", + "tweetnacl-util": "^0.15.1" + }, + "engines": { + "node": ">=12.0.0" + } + }, + "node_modules/@metamask/eth-sig-util/node_modules/@types/bn.js": { + "version": "4.11.6", + "resolved": "https://registry.npmjs.org/@types/bn.js/-/bn.js-4.11.6.tgz", + "integrity": "sha512-pqr857jrp2kPuO9uRjZ3PwnJTjoQy+fcdxvBTvHm6dkmEL9q+hDD/2j/0ELOBPtPnS8LjCX0gI9nbl8lVkadpg==", + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@metamask/eth-sig-util/node_modules/bn.js": { + "version": "4.12.0", + "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.12.0.tgz", + "integrity": "sha512-c98Bf3tPniI+scsdk237ku1Dc3ujXQTSgyiPUDEOe7tRkhrqridvh8klBv0HCEso1OLOYcHuCv/cS6DNxKH+ZA==" + }, + "node_modules/@metamask/eth-sig-util/node_modules/ethereumjs-util": { + "version": "6.2.1", + "resolved": "https://registry.npmjs.org/ethereumjs-util/-/ethereumjs-util-6.2.1.tgz", + "integrity": "sha512-W2Ktez4L01Vexijrm5EB6w7dg4n/TgpoYU4avuT5T3Vmnw/eCRtiBrJfQYS/DCSvDIOLn2k57GcHdeBcgVxAqw==", + "dependencies": { + "@types/bn.js": "^4.11.3", + "bn.js": "^4.11.0", + "create-hash": "^1.1.2", + "elliptic": "^6.5.2", + "ethereum-cryptography": "^0.1.3", + "ethjs-util": "0.1.6", + "rlp": "^2.2.3" + } + }, + "node_modules/@noble/curves": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@noble/curves/-/curves-1.2.0.tgz", + "integrity": "sha512-oYclrNgRaM9SsBUBVbb8M6DTV7ZHRTKugureoYEncY5c65HOmRzvSiTE3y5CYaPYJA/GVkrhXEoF0M3Ya9PMnw==", + "dev": true, + "dependencies": { + "@noble/hashes": "1.3.2" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/@noble/hashes": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/@noble/hashes/-/hashes-1.3.2.tgz", + "integrity": "sha512-MVC8EAQp7MvEcm30KWENFjgR+Mkmf+D189XJTkFIlwohU5hcBbn1ZkKq7KVTi2Hme3PMGF390DaL52beVrIihQ==", + "dev": true, + "engines": { + "node": ">= 16" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/@noble/secp256k1": { + "version": "1.7.1", + "resolved": "https://registry.npmjs.org/@noble/secp256k1/-/secp256k1-1.7.1.tgz", + "integrity": "sha512-hOUk6AyBFmqVrv7k5WAw/LpszxVbj9gGN4JRkIX52fdFAj1UA61KXmZDvqVEm+pOyec3+fIeZB02LYa/pWOArw==", + "funding": [ + { + "type": "individual", + "url": "https://paulmillr.com/funding/" + } + ] + }, + "node_modules/@nodelib/fs.scandir": { + "version": "2.1.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", + "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==", + "dev": true, + "peer": true, + "dependencies": { + "@nodelib/fs.stat": "2.0.5", + "run-parallel": "^1.1.9" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@nodelib/fs.stat": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz", + "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==", + "dev": true, + "peer": true, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@nodelib/fs.walk": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz", + "integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==", + "dev": true, + "peer": true, + "dependencies": { + "@nodelib/fs.scandir": "2.1.5", + "fastq": "^1.6.0" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@nomicfoundation/ethereumjs-block": { + "version": "5.0.2", + "resolved": "https://registry.npmjs.org/@nomicfoundation/ethereumjs-block/-/ethereumjs-block-5.0.2.tgz", + "integrity": "sha512-hSe6CuHI4SsSiWWjHDIzWhSiAVpzMUcDRpWYzN0T9l8/Rz7xNn3elwVOJ/tAyS0LqL6vitUD78Uk7lQDXZun7Q==", + "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" + }, + "engines": { + "node": ">=14" + } + }, + "node_modules/@nomicfoundation/ethereumjs-block/node_modules/ethers": { + "version": "5.7.2", + "resolved": "https://registry.npmjs.org/ethers/-/ethers-5.7.2.tgz", + "integrity": "sha512-wswUsmWo1aOK8rR7DIKiWSw9DbLWe6x98Jrn8wcTflTVvaXhAMaB5zGAXy0GYQEQp9iO1iSHWVyARQm11zUtyg==", + "funding": [ + { + "type": "individual", + "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2" + }, + { + "type": "individual", + "url": "https://www.buymeacoffee.com/ricmoo" + } + ], + "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" + } + }, + "node_modules/@nomicfoundation/ethereumjs-blockchain": { + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/@nomicfoundation/ethereumjs-blockchain/-/ethereumjs-blockchain-7.0.2.tgz", + "integrity": "sha512-8UUsSXJs+MFfIIAKdh3cG16iNmWzWC/91P40sazNvrqhhdR/RtGDlFk2iFTGbBAZPs2+klZVzhRX8m2wvuvz3w==", + "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" + }, + "engines": { + "node": ">=14" + } + }, + "node_modules/@nomicfoundation/ethereumjs-common": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/@nomicfoundation/ethereumjs-common/-/ethereumjs-common-4.0.2.tgz", + "integrity": "sha512-I2WGP3HMGsOoycSdOTSqIaES0ughQTueOsddJ36aYVpI3SN8YSusgRFLwzDJwRFVIYDKx/iJz0sQ5kBHVgdDwg==", + "dependencies": { + "@nomicfoundation/ethereumjs-util": "9.0.2", + "crc-32": "^1.2.0" + } + }, + "node_modules/@nomicfoundation/ethereumjs-ethash": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/@nomicfoundation/ethereumjs-ethash/-/ethereumjs-ethash-3.0.2.tgz", + "integrity": "sha512-8PfoOQCcIcO9Pylq0Buijuq/O73tmMVURK0OqdjhwqcGHYC2PwhbajDh7GZ55ekB0Px197ajK3PQhpKoiI/UPg==", + "dependencies": { + "@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" + }, + "engines": { + "node": ">=14" + } + }, + "node_modules/@nomicfoundation/ethereumjs-evm": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/@nomicfoundation/ethereumjs-evm/-/ethereumjs-evm-2.0.2.tgz", + "integrity": "sha512-rBLcUaUfANJxyOx9HIdMX6uXGin6lANCulIm/pjMgRqfiCRMZie3WKYxTSd8ZE/d+qT+zTedBF4+VHTdTSePmQ==", + "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" + }, + "engines": { + "node": ">=14" + } + }, + "node_modules/@nomicfoundation/ethereumjs-rlp": { + "version": "5.0.2", + "resolved": "https://registry.npmjs.org/@nomicfoundation/ethereumjs-rlp/-/ethereumjs-rlp-5.0.2.tgz", + "integrity": "sha512-QwmemBc+MMsHJ1P1QvPl8R8p2aPvvVcKBbvHnQOKBpBztEo0omN0eaob6FeZS/e3y9NSe+mfu3nNFBHszqkjTA==", + "bin": { + "rlp": "bin/rlp" + }, + "engines": { + "node": ">=14" + } + }, + "node_modules/@nomicfoundation/ethereumjs-statemanager": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/@nomicfoundation/ethereumjs-statemanager/-/ethereumjs-statemanager-2.0.2.tgz", + "integrity": "sha512-dlKy5dIXLuDubx8Z74sipciZnJTRSV/uHG48RSijhgm1V7eXYFC567xgKtsKiVZB1ViTP9iFL4B6Je0xD6X2OA==", + "dependencies": { + "@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" + } + }, + "node_modules/@nomicfoundation/ethereumjs-statemanager/node_modules/ethers": { + "version": "5.7.2", + "resolved": "https://registry.npmjs.org/ethers/-/ethers-5.7.2.tgz", + "integrity": "sha512-wswUsmWo1aOK8rR7DIKiWSw9DbLWe6x98Jrn8wcTflTVvaXhAMaB5zGAXy0GYQEQp9iO1iSHWVyARQm11zUtyg==", + "funding": [ + { + "type": "individual", + "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2" + }, + { + "type": "individual", + "url": "https://www.buymeacoffee.com/ricmoo" + } + ], + "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" + } + }, + "node_modules/@nomicfoundation/ethereumjs-trie": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/@nomicfoundation/ethereumjs-trie/-/ethereumjs-trie-6.0.2.tgz", + "integrity": "sha512-yw8vg9hBeLYk4YNg5MrSJ5H55TLOv2FSWUTROtDtTMMmDGROsAu+0tBjiNGTnKRi400M6cEzoFfa89Fc5k8NTQ==", + "dependencies": { + "@nomicfoundation/ethereumjs-rlp": "5.0.2", + "@nomicfoundation/ethereumjs-util": "9.0.2", + "@types/readable-stream": "^2.3.13", + "ethereum-cryptography": "0.1.3", + "readable-stream": "^3.6.0" + }, + "engines": { + "node": ">=14" + } + }, + "node_modules/@nomicfoundation/ethereumjs-tx": { + "version": "5.0.2", + "resolved": "https://registry.npmjs.org/@nomicfoundation/ethereumjs-tx/-/ethereumjs-tx-5.0.2.tgz", + "integrity": "sha512-T+l4/MmTp7VhJeNloMkM+lPU3YMUaXdcXgTGCf8+ZFvV9NYZTRLFekRwlG6/JMmVfIfbrW+dRRJ9A6H5Q/Z64g==", + "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" + }, + "engines": { + "node": ">=14" + } + }, + "node_modules/@nomicfoundation/ethereumjs-util": { + "version": "9.0.2", + "resolved": "https://registry.npmjs.org/@nomicfoundation/ethereumjs-util/-/ethereumjs-util-9.0.2.tgz", + "integrity": "sha512-4Wu9D3LykbSBWZo8nJCnzVIYGvGCuyiYLIJa9XXNVt1q1jUzHdB+sJvx95VGCpPkCT+IbLecW6yfzy3E1bQrwQ==", + "dependencies": { + "@chainsafe/ssz": "^0.10.0", + "@nomicfoundation/ethereumjs-rlp": "5.0.2", + "ethereum-cryptography": "0.1.3" + }, + "engines": { + "node": ">=14" + } + }, + "node_modules/@nomicfoundation/ethereumjs-util/node_modules/@chainsafe/persistent-merkle-tree": { + "version": "0.5.0", + "resolved": "https://registry.npmjs.org/@chainsafe/persistent-merkle-tree/-/persistent-merkle-tree-0.5.0.tgz", + "integrity": "sha512-l0V1b5clxA3iwQLXP40zYjyZYospQLZXzBVIhhr9kDg/1qHZfzzHw0jj4VPBijfYCArZDlPkRi1wZaV2POKeuw==", + "dependencies": { + "@chainsafe/as-sha256": "^0.3.1" + } + }, + "node_modules/@nomicfoundation/ethereumjs-util/node_modules/@chainsafe/ssz": { + "version": "0.10.2", + "resolved": "https://registry.npmjs.org/@chainsafe/ssz/-/ssz-0.10.2.tgz", + "integrity": "sha512-/NL3Lh8K+0q7A3LsiFq09YXS9fPE+ead2rr7vM2QK8PLzrNsw3uqrif9bpRX5UxgeRjM+vYi+boCM3+GM4ovXg==", + "dependencies": { + "@chainsafe/as-sha256": "^0.3.1", + "@chainsafe/persistent-merkle-tree": "^0.5.0" + } + }, + "node_modules/@nomicfoundation/ethereumjs-vm": { + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/@nomicfoundation/ethereumjs-vm/-/ethereumjs-vm-7.0.2.tgz", + "integrity": "sha512-Bj3KZT64j54Tcwr7Qm/0jkeZXJMfdcAtRBedou+Hx0dPOSIgqaIr0vvLwP65TpHbak2DmAq+KJbW2KNtIoFwvA==", + "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" + }, + "engines": { + "node": ">=14" + } + }, + "node_modules/@nomicfoundation/hardhat-chai-matchers": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/@nomicfoundation/hardhat-chai-matchers/-/hardhat-chai-matchers-2.0.3.tgz", + "integrity": "sha512-A40s7EAK4Acr8UP1Yudgi9GGD9Cca/K3LHt3DzmRIje14lBfHtg9atGQ7qK56vdPcTwKmeaGn30FzxMUfPGEMw==", + "dev": true, + "peer": true, + "dependencies": { + "@types/chai-as-promised": "^7.1.3", + "chai-as-promised": "^7.1.1", + "deep-eql": "^4.0.1", + "ordinal": "^1.0.3" + }, + "peerDependencies": { + "@nomicfoundation/hardhat-ethers": "^3.0.0", + "chai": "^4.2.0", + "ethers": "^6.1.0", + "hardhat": "^2.9.4" + } + }, + "node_modules/@nomicfoundation/hardhat-ethers": { + "version": "3.0.5", + "resolved": "https://registry.npmjs.org/@nomicfoundation/hardhat-ethers/-/hardhat-ethers-3.0.5.tgz", + "integrity": "sha512-RNFe8OtbZK6Ila9kIlHp0+S80/0Bu/3p41HUpaRIoHLm6X3WekTd83vob3rE54Duufu1edCiBDxspBzi2rxHHw==", + "dev": true, + "dependencies": { + "debug": "^4.1.1", + "lodash.isequal": "^4.5.0" + }, + "peerDependencies": { + "ethers": "^6.1.0", + "hardhat": "^2.0.0" + } + }, + "node_modules/@nomicfoundation/hardhat-foundry": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@nomicfoundation/hardhat-foundry/-/hardhat-foundry-1.1.1.tgz", + "integrity": "sha512-cXGCBHAiXas9Pg9MhMOpBVQCkWRYoRFG7GJJAph+sdQsfd22iRs5U5Vs9XmpGEQd1yEvYISQZMeE68Nxj65iUQ==", + "dev": true, + "dependencies": { + "chalk": "^2.4.2" + }, + "peerDependencies": { + "hardhat": "^2.17.2" + } + }, + "node_modules/@nomicfoundation/hardhat-network-helpers": { + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/@nomicfoundation/hardhat-network-helpers/-/hardhat-network-helpers-1.0.10.tgz", + "integrity": "sha512-R35/BMBlx7tWN5V6d/8/19QCwEmIdbnA4ZrsuXgvs8i2qFx5i7h6mH5pBS4Pwi4WigLH+upl6faYusrNPuzMrQ==", + "dependencies": { + "ethereumjs-util": "^7.1.4" + }, + "peerDependencies": { + "hardhat": "^2.9.5" + } + }, + "node_modules/@nomicfoundation/hardhat-toolbox": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/@nomicfoundation/hardhat-toolbox/-/hardhat-toolbox-4.0.0.tgz", + "integrity": "sha512-jhcWHp0aHaL0aDYj8IJl80v4SZXWMS1A2XxXa1CA6pBiFfJKuZinCkO6wb+POAt0LIfXB3gA3AgdcOccrcwBwA==", + "dev": true, + "peerDependencies": { + "@nomicfoundation/hardhat-chai-matchers": "^2.0.0", + "@nomicfoundation/hardhat-ethers": "^3.0.0", + "@nomicfoundation/hardhat-network-helpers": "^1.0.0", + "@nomicfoundation/hardhat-verify": "^2.0.0", + "@typechain/ethers-v6": "^0.5.0", + "@typechain/hardhat": "^9.0.0", + "@types/chai": "^4.2.0", + "@types/mocha": ">=9.1.0", + "@types/node": ">=16.0.0", + "chai": "^4.2.0", + "ethers": "^6.4.0", + "hardhat": "^2.11.0", + "hardhat-gas-reporter": "^1.0.8", + "solidity-coverage": "^0.8.1", + "ts-node": ">=8.0.0", + "typechain": "^8.3.0", + "typescript": ">=4.5.0" + } + }, + "node_modules/@nomicfoundation/hardhat-verify": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/@nomicfoundation/hardhat-verify/-/hardhat-verify-2.0.3.tgz", + "integrity": "sha512-ESbRu9by53wu6VvgwtMtm108RSmuNsVqXtzg061D+/4R7jaWh/Wl/8ve+p6SdDX7vA1Z3L02hDO1Q3BY4luLXQ==", + "dev": true, + "peer": true, + "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" + } + }, + "node_modules/@nomicfoundation/solidity-analyzer": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/@nomicfoundation/solidity-analyzer/-/solidity-analyzer-0.1.1.tgz", + "integrity": "sha512-1LMtXj1puAxyFusBgUIy5pZk3073cNXYnXUpuNKFghHbIit/xZgbk0AokpUADbNm3gyD6bFWl3LRFh3dhVdREg==", + "engines": { + "node": ">= 12" + }, + "optionalDependencies": { + "@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" + } + }, + "node_modules/@nomicfoundation/solidity-analyzer-darwin-arm64": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/@nomicfoundation/solidity-analyzer-darwin-arm64/-/solidity-analyzer-darwin-arm64-0.1.1.tgz", + "integrity": "sha512-KcTodaQw8ivDZyF+D76FokN/HdpgGpfjc/gFCImdLUyqB6eSWVaZPazMbeAjmfhx3R0zm/NYVzxwAokFKgrc0w==", + "cpu": [ + "arm64" + ], + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@nomicfoundation/solidity-analyzer-darwin-x64": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/@nomicfoundation/solidity-analyzer-darwin-x64/-/solidity-analyzer-darwin-x64-0.1.1.tgz", + "integrity": "sha512-XhQG4BaJE6cIbjAVtzGOGbK3sn1BO9W29uhk9J8y8fZF1DYz0Doj8QDMfpMu+A6TjPDs61lbsmeYodIDnfveSA==", + "cpu": [ + "x64" + ], + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@nomicfoundation/solidity-analyzer-freebsd-x64": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/@nomicfoundation/solidity-analyzer-freebsd-x64/-/solidity-analyzer-freebsd-x64-0.1.1.tgz", + "integrity": "sha512-GHF1VKRdHW3G8CndkwdaeLkVBi5A9u2jwtlS7SLhBc8b5U/GcoL39Q+1CSO3hYqePNP+eV5YI7Zgm0ea6kMHoA==", + "cpu": [ + "x64" + ], + "optional": true, + "os": [ + "freebsd" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@nomicfoundation/solidity-analyzer-linux-arm64-gnu": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/@nomicfoundation/solidity-analyzer-linux-arm64-gnu/-/solidity-analyzer-linux-arm64-gnu-0.1.1.tgz", + "integrity": "sha512-g4Cv2fO37ZsUENQ2vwPnZc2zRenHyAxHcyBjKcjaSmmkKrFr64yvzeNO8S3GBFCo90rfochLs99wFVGT/0owpg==", + "cpu": [ + "arm64" + ], + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@nomicfoundation/solidity-analyzer-linux-arm64-musl": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/@nomicfoundation/solidity-analyzer-linux-arm64-musl/-/solidity-analyzer-linux-arm64-musl-0.1.1.tgz", + "integrity": "sha512-WJ3CE5Oek25OGE3WwzK7oaopY8xMw9Lhb0mlYuJl/maZVo+WtP36XoQTb7bW/i8aAdHW5Z+BqrHMux23pvxG3w==", + "cpu": [ + "arm64" + ], + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@nomicfoundation/solidity-analyzer-linux-x64-gnu": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/@nomicfoundation/solidity-analyzer-linux-x64-gnu/-/solidity-analyzer-linux-x64-gnu-0.1.1.tgz", + "integrity": "sha512-5WN7leSr5fkUBBjE4f3wKENUy9HQStu7HmWqbtknfXkkil+eNWiBV275IOlpXku7v3uLsXTOKpnnGHJYI2qsdA==", + "cpu": [ + "x64" + ], + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@nomicfoundation/solidity-analyzer-linux-x64-musl": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/@nomicfoundation/solidity-analyzer-linux-x64-musl/-/solidity-analyzer-linux-x64-musl-0.1.1.tgz", + "integrity": "sha512-KdYMkJOq0SYPQMmErv/63CwGwMm5XHenEna9X9aB8mQmhDBrYrlAOSsIPgFCUSL0hjxE3xHP65/EPXR/InD2+w==", + "cpu": [ + "x64" + ], + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@nomicfoundation/solidity-analyzer-win32-arm64-msvc": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/@nomicfoundation/solidity-analyzer-win32-arm64-msvc/-/solidity-analyzer-win32-arm64-msvc-0.1.1.tgz", + "integrity": "sha512-VFZASBfl4qiBYwW5xeY20exWhmv6ww9sWu/krWSesv3q5hA0o1JuzmPHR4LPN6SUZj5vcqci0O6JOL8BPw+APg==", + "cpu": [ + "arm64" + ], + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@nomicfoundation/solidity-analyzer-win32-ia32-msvc": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/@nomicfoundation/solidity-analyzer-win32-ia32-msvc/-/solidity-analyzer-win32-ia32-msvc-0.1.1.tgz", + "integrity": "sha512-JnFkYuyCSA70j6Si6cS1A9Gh1aHTEb8kOTBApp/c7NRTFGNMH8eaInKlyuuiIbvYFhlXW4LicqyYuWNNq9hkpQ==", + "cpu": [ + "ia32" + ], + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@nomicfoundation/solidity-analyzer-win32-x64-msvc": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/@nomicfoundation/solidity-analyzer-win32-x64-msvc/-/solidity-analyzer-win32-x64-msvc-0.1.1.tgz", + "integrity": "sha512-HrVJr6+WjIXGnw3Q9u6KQcbZCtk0caVWhCdFADySvRyUxJ8PnzlaP+MhwNE8oyT8OZ6ejHBRrrgjSqDCFXGirw==", + "cpu": [ + "x64" + ], + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@openzeppelin/defender-admin-client": { + "version": "1.54.1", + "resolved": "https://registry.npmjs.org/@openzeppelin/defender-admin-client/-/defender-admin-client-1.54.1.tgz", + "integrity": "sha512-kRpSUdTsnSqntp4FOXIm95t+6VKHc8CUY2Si71VDuxs0q7HSPZkdpRPSntcolwEzWy9L4a8NS/QMwDF5NJ4X1g==", + "dev": true, + "dependencies": { + "@openzeppelin/defender-base-client": "1.54.1", + "axios": "^1.4.0", + "ethers": "^5.7.2", + "lodash": "^4.17.19", + "node-fetch": "^2.6.0" + } + }, + "node_modules/@openzeppelin/defender-admin-client/node_modules/ethers": { + "version": "5.7.2", + "resolved": "https://registry.npmjs.org/ethers/-/ethers-5.7.2.tgz", + "integrity": "sha512-wswUsmWo1aOK8rR7DIKiWSw9DbLWe6x98Jrn8wcTflTVvaXhAMaB5zGAXy0GYQEQp9iO1iSHWVyARQm11zUtyg==", + "dev": true, + "funding": [ + { + "type": "individual", + "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2" + }, + { + "type": "individual", + "url": "https://www.buymeacoffee.com/ricmoo" + } + ], + "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" + } + }, + "node_modules/@openzeppelin/defender-base-client": { + "version": "1.54.1", + "resolved": "https://registry.npmjs.org/@openzeppelin/defender-base-client/-/defender-base-client-1.54.1.tgz", + "integrity": "sha512-DRGz/7KN3ZQwu28YWMOaojrC7jjPkz/uCwkC8/C8B11qwZhA5qIVvyhYHhhFOCl0J84+E3TNdvkPD2q3p2WaJw==", + "dev": true, + "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" + } + }, + "node_modules/@openzeppelin/defender-sdk-base-client": { + "version": "1.8.0", + "resolved": "https://registry.npmjs.org/@openzeppelin/defender-sdk-base-client/-/defender-sdk-base-client-1.8.0.tgz", + "integrity": "sha512-XIJat6BW2CTM74AwG5IL0Q/aE6RXj8x7smnVKmBql4wMvmirVW+njfwzZCLhUTiBXg9AlHxIInEF14SabfIisg==", + "dev": true, + "dependencies": { + "amazon-cognito-identity-js": "^6.3.6", + "async-retry": "^1.3.3" + } + }, + "node_modules/@openzeppelin/defender-sdk-deploy-client": { + "version": "1.8.0", + "resolved": "https://registry.npmjs.org/@openzeppelin/defender-sdk-deploy-client/-/defender-sdk-deploy-client-1.8.0.tgz", + "integrity": "sha512-/tNS2EnHuA5l095wzMbIkGMDNHZLcZQ2eLUP8z+AeKaAUeR2z4qzZ1ul21kR3EJURAyoy8aULFZanLggoBWHrA==", + "dev": true, + "dependencies": { + "@ethersproject/abi": "^5.7.0", + "@openzeppelin/defender-sdk-base-client": "^1.8.0", + "axios": "^1.4.0", + "lodash": "^4.17.21" + } + }, + "node_modules/@openzeppelin/hardhat-upgrades": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/@openzeppelin/hardhat-upgrades/-/hardhat-upgrades-3.0.1.tgz", + "integrity": "sha512-NtD2/n2PKNqHBafQy3AM6KCvsDZD0w97po7fFa4wctl0fg/8QwGAg3fB8InkBFEhGn17+IgshRI8G94hUrFPcQ==", + "dev": true, + "dependencies": { + "@openzeppelin/defender-admin-client": "^1.52.0", + "@openzeppelin/defender-base-client": "^1.52.0", + "@openzeppelin/defender-sdk-base-client": "^1.8.0", + "@openzeppelin/defender-sdk-deploy-client": "^1.8.0", + "@openzeppelin/upgrades-core": "^1.32.0", + "chalk": "^4.1.0", + "debug": "^4.1.1", + "ethereumjs-util": "^7.1.5", + "proper-lockfile": "^4.1.1", + "undici": "^5.28.2" + }, + "bin": { + "migrate-oz-cli-project": "dist/scripts/migrate-oz-cli-project.js" + }, + "peerDependencies": { + "@nomicfoundation/hardhat-ethers": "^3.0.0", + "@nomicfoundation/hardhat-verify": "^2.0.0", + "ethers": "^6.6.0", + "hardhat": "^2.0.2" + }, + "peerDependenciesMeta": { + "@nomicfoundation/hardhat-verify": { + "optional": true + } + } + }, + "node_modules/@openzeppelin/hardhat-upgrades/node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/@openzeppelin/hardhat-upgrades/node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/@openzeppelin/hardhat-upgrades/node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/@openzeppelin/hardhat-upgrades/node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true + }, + "node_modules/@openzeppelin/hardhat-upgrades/node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/@openzeppelin/hardhat-upgrades/node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/@openzeppelin/upgrades-core": { + "version": "1.32.2", + "resolved": "https://registry.npmjs.org/@openzeppelin/upgrades-core/-/upgrades-core-1.32.2.tgz", + "integrity": "sha512-EkXriOHZfn6u00Tbq0zUuhHDeTQB9WyAZKZo3UeYdqULb7E3vqxZgxgXmWJwEzAb6E77DvprzQ4gwCAjMV/S4Q==", + "dev": true, + "dependencies": { + "cbor": "^9.0.0", + "chalk": "^4.1.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.51" + }, + "bin": { + "openzeppelin-upgrades-core": "dist/cli/cli.js" + } + }, + "node_modules/@openzeppelin/upgrades-core/node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/@openzeppelin/upgrades-core/node_modules/cbor": { + "version": "9.0.1", + "resolved": "https://registry.npmjs.org/cbor/-/cbor-9.0.1.tgz", + "integrity": "sha512-/TQOWyamDxvVIv+DY9cOLNuABkoyz8K/F3QE56539pGVYohx0+MEA1f4lChFTX79dBTBS7R1PF6ovH7G+VtBfQ==", + "dev": true, + "dependencies": { + "nofilter": "^3.1.0" + }, + "engines": { + "node": ">=16" + } + }, + "node_modules/@openzeppelin/upgrades-core/node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/@openzeppelin/upgrades-core/node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/@openzeppelin/upgrades-core/node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true + }, + "node_modules/@openzeppelin/upgrades-core/node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/@openzeppelin/upgrades-core/node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/@scure/base": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/@scure/base/-/base-1.1.5.tgz", + "integrity": "sha512-Brj9FiG2W1MRQSTB212YVPRrcbjkv48FoZi/u4l/zds/ieRrqsh7aUf6CLwkAq61oKXr/ZlTzlY66gLIj3TFTQ==", + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/@scure/bip32": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/@scure/bip32/-/bip32-1.3.1.tgz", + "integrity": "sha512-osvveYtyzdEVbt3OfwwXFr4P2iVBL5u1Q3q4ONBfDY/UpOuXmOlbgwc1xECEboY8wIays8Yt6onaWMUdUbfl0A==", + "dev": true, + "peer": true, + "dependencies": { + "@noble/curves": "~1.1.0", + "@noble/hashes": "~1.3.1", + "@scure/base": "~1.1.0" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/@scure/bip32/node_modules/@noble/curves": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@noble/curves/-/curves-1.1.0.tgz", + "integrity": "sha512-091oBExgENk/kGj3AZmtBDMpxQPDtxQABR2B9lb1JbVTs6ytdzZNwvhxQ4MWasRNEzlbEH8jCWFCwhF/Obj5AA==", + "dev": true, + "peer": true, + "dependencies": { + "@noble/hashes": "1.3.1" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/@scure/bip32/node_modules/@noble/hashes": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/@noble/hashes/-/hashes-1.3.1.tgz", + "integrity": "sha512-EbqwksQwz9xDRGfDST86whPBgM65E0OH/pCgqW0GBVzO22bNE+NuIbeTb714+IfSjU3aRk47EUvXIb5bTsenKA==", + "dev": true, + "peer": true, + "engines": { + "node": ">= 16" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/@scure/bip39": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/@scure/bip39/-/bip39-1.2.1.tgz", + "integrity": "sha512-Z3/Fsz1yr904dduJD0NpiyRHhRYHdcnyh73FZWiV+/qhWi83wNJ3NWolYqCEN+ZWsUz2TWwajJggcRE9r1zUYg==", + "dev": true, + "peer": true, + "dependencies": { + "@noble/hashes": "~1.3.0", + "@scure/base": "~1.1.0" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/@sentry/core": { + "version": "5.30.0", + "resolved": "https://registry.npmjs.org/@sentry/core/-/core-5.30.0.tgz", + "integrity": "sha512-TmfrII8w1PQZSZgPpUESqjB+jC6MvZJZdLtE/0hZ+SrnKhW3x5WlYLvTXZpcWePYBku7rl2wn1RZu6uT0qCTeg==", + "dependencies": { + "@sentry/hub": "5.30.0", + "@sentry/minimal": "5.30.0", + "@sentry/types": "5.30.0", + "@sentry/utils": "5.30.0", + "tslib": "^1.9.3" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/@sentry/core/node_modules/tslib": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", + "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==" + }, + "node_modules/@sentry/hub": { + "version": "5.30.0", + "resolved": "https://registry.npmjs.org/@sentry/hub/-/hub-5.30.0.tgz", + "integrity": "sha512-2tYrGnzb1gKz2EkMDQcfLrDTvmGcQPuWxLnJKXJvYTQDGLlEvi2tWz1VIHjunmOvJrB5aIQLhm+dcMRwFZDCqQ==", + "dependencies": { + "@sentry/types": "5.30.0", + "@sentry/utils": "5.30.0", + "tslib": "^1.9.3" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/@sentry/hub/node_modules/tslib": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", + "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==" + }, + "node_modules/@sentry/minimal": { + "version": "5.30.0", + "resolved": "https://registry.npmjs.org/@sentry/minimal/-/minimal-5.30.0.tgz", + "integrity": "sha512-BwWb/owZKtkDX+Sc4zCSTNcvZUq7YcH3uAVlmh/gtR9rmUvbzAA3ewLuB3myi4wWRAMEtny6+J/FN/x+2wn9Xw==", + "dependencies": { + "@sentry/hub": "5.30.0", + "@sentry/types": "5.30.0", + "tslib": "^1.9.3" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/@sentry/minimal/node_modules/tslib": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", + "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==" + }, + "node_modules/@sentry/node": { + "version": "5.30.0", + "resolved": "https://registry.npmjs.org/@sentry/node/-/node-5.30.0.tgz", + "integrity": "sha512-Br5oyVBF0fZo6ZS9bxbJZG4ApAjRqAnqFFurMVJJdunNb80brh7a5Qva2kjhm+U6r9NJAB5OmDyPkA1Qnt+QVg==", + "dependencies": { + "@sentry/core": "5.30.0", + "@sentry/hub": "5.30.0", + "@sentry/tracing": "5.30.0", + "@sentry/types": "5.30.0", + "@sentry/utils": "5.30.0", + "cookie": "^0.4.1", + "https-proxy-agent": "^5.0.0", + "lru_map": "^0.3.3", + "tslib": "^1.9.3" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/@sentry/node/node_modules/tslib": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", + "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==" + }, + "node_modules/@sentry/tracing": { + "version": "5.30.0", + "resolved": "https://registry.npmjs.org/@sentry/tracing/-/tracing-5.30.0.tgz", + "integrity": "sha512-dUFowCr0AIMwiLD7Fs314Mdzcug+gBVo/+NCMyDw8tFxJkwWAKl7Qa2OZxLQ0ZHjakcj1hNKfCQJ9rhyfOl4Aw==", + "dependencies": { + "@sentry/hub": "5.30.0", + "@sentry/minimal": "5.30.0", + "@sentry/types": "5.30.0", + "@sentry/utils": "5.30.0", + "tslib": "^1.9.3" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/@sentry/tracing/node_modules/tslib": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", + "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==" + }, + "node_modules/@sentry/types": { + "version": "5.30.0", + "resolved": "https://registry.npmjs.org/@sentry/types/-/types-5.30.0.tgz", + "integrity": "sha512-R8xOqlSTZ+htqrfteCWU5Nk0CDN5ApUTvrlvBuiH1DyP6czDZ4ktbZB0hAgBlVcK0U+qpD3ag3Tqqpa5Q67rPw==", + "engines": { + "node": ">=6" + } + }, + "node_modules/@sentry/utils": { + "version": "5.30.0", + "resolved": "https://registry.npmjs.org/@sentry/utils/-/utils-5.30.0.tgz", + "integrity": "sha512-zaYmoH0NWWtvnJjC9/CBseXMtKHm/tm40sz3YfJRxeQjyzRqNQPgivpd9R/oDJCYj999mzdW382p/qi2ypjLww==", + "dependencies": { + "@sentry/types": "5.30.0", + "tslib": "^1.9.3" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/@sentry/utils/node_modules/tslib": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", + "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==" + }, + "node_modules/@smithy/types": { + "version": "2.8.0", + "resolved": "https://registry.npmjs.org/@smithy/types/-/types-2.8.0.tgz", + "integrity": "sha512-h9sz24cFgt/W1Re22OlhQKmUZkNh244ApgRsUDYinqF8R+QgcsBIX344u2j61TPshsTz3CvL6HYU1DnQdsSrHA==", + "dev": true, + "dependencies": { + "tslib": "^2.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/@smithy/types/node_modules/tslib": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz", + "integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==", + "dev": true + }, + "node_modules/@solidity-parser/parser": { + "version": "0.14.5", + "resolved": "https://registry.npmjs.org/@solidity-parser/parser/-/parser-0.14.5.tgz", + "integrity": "sha512-6dKnHZn7fg/iQATVEzqyUOyEidbn05q7YA2mQ9hC0MMXhhV3/JrsxmFSYZAcr7j1yUP700LLhTruvJ3MiQmjJg==", + "dev": true, + "peer": true, + "dependencies": { + "antlr4ts": "^0.5.0-alpha.4" + } + }, + "node_modules/@tsconfig/node10": { + "version": "1.0.9", + "resolved": "https://registry.npmjs.org/@tsconfig/node10/-/node10-1.0.9.tgz", + "integrity": "sha512-jNsYVVxU8v5g43Erja32laIDHXeoNvFEpX33OK4d6hljo3jDhCBDhx5dhCCTMWUojscpAagGiRkBKxpdl9fxqA==", + "devOptional": true, + "peer": true + }, + "node_modules/@tsconfig/node12": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/@tsconfig/node12/-/node12-1.0.11.tgz", + "integrity": "sha512-cqefuRsh12pWyGsIoBKJA9luFu3mRxCA+ORZvA4ktLSzIuCUtWVxGIuXigEwO5/ywWFMZ2QEGKWvkZG1zDMTag==", + "devOptional": true, + "peer": true + }, + "node_modules/@tsconfig/node14": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/@tsconfig/node14/-/node14-1.0.3.tgz", + "integrity": "sha512-ysT8mhdixWK6Hw3i1V2AeRqZ5WfXg1G43mqoYlM2nc6388Fq5jcXyr5mRsqViLx/GJYdoL0bfXD8nmF+Zn/Iow==", + "devOptional": true, + "peer": true + }, + "node_modules/@tsconfig/node16": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/@tsconfig/node16/-/node16-1.0.4.tgz", + "integrity": "sha512-vxhUy4J8lyeyinH7Azl1pdd43GJhZH/tP2weN8TntQblOY+A0XbT8DJk1/oCPuOOyg/Ja757rG0CgHcWC8OfMA==", + "devOptional": true, + "peer": true + }, + "node_modules/@typechain/ethers-v6": { + "version": "0.5.1", + "resolved": "https://registry.npmjs.org/@typechain/ethers-v6/-/ethers-v6-0.5.1.tgz", + "integrity": "sha512-F+GklO8jBWlsaVV+9oHaPh5NJdd6rAKN4tklGfInX1Q7h0xPgVLP39Jl3eCulPB5qexI71ZFHwbljx4ZXNfouA==", + "dev": true, + "peer": true, + "dependencies": { + "lodash": "^4.17.15", + "ts-essentials": "^7.0.1" + }, + "peerDependencies": { + "ethers": "6.x", + "typechain": "^8.3.2", + "typescript": ">=4.7.0" + } + }, + "node_modules/@typechain/hardhat": { + "version": "9.1.0", + "resolved": "https://registry.npmjs.org/@typechain/hardhat/-/hardhat-9.1.0.tgz", + "integrity": "sha512-mtaUlzLlkqTlfPwB3FORdejqBskSnh+Jl8AIJGjXNAQfRQ4ofHADPl1+oU7Z3pAJzmZbUXII8MhOLQltcHgKnA==", + "dev": true, + "peer": true, + "dependencies": { + "fs-extra": "^9.1.0" + }, + "peerDependencies": { + "@typechain/ethers-v6": "^0.5.1", + "ethers": "^6.1.0", + "hardhat": "^2.9.9", + "typechain": "^8.3.2" + } + }, + "node_modules/@types/bn.js": { + "version": "5.1.5", + "resolved": "https://registry.npmjs.org/@types/bn.js/-/bn.js-5.1.5.tgz", + "integrity": "sha512-V46N0zwKRF5Q00AZ6hWtN0T8gGmDUaUzLWQvHFo5yThtVwK/VCenFY3wXVbOvNfajEpsTfQM4IN9k/d6gUVX3A==", + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@types/chai": { + "version": "4.3.11", + "resolved": "https://registry.npmjs.org/@types/chai/-/chai-4.3.11.tgz", + "integrity": "sha512-qQR1dr2rGIHYlJulmr8Ioq3De0Le9E4MJ5AiaeAETJJpndT1uUNHsGFK3L/UIu+rbkQSdj8J/w2bCsBZc/Y5fQ==", + "dev": true, + "peer": true + }, + "node_modules/@types/chai-as-promised": { + "version": "7.1.8", + "resolved": "https://registry.npmjs.org/@types/chai-as-promised/-/chai-as-promised-7.1.8.tgz", + "integrity": "sha512-ThlRVIJhr69FLlh6IctTXFkmhtP3NpMZ2QGq69StYLyKZFp/HOp1VdKZj7RvfNWYYcJ1xlbLGLLWj1UvP5u/Gw==", + "dev": true, + "peer": true, + "dependencies": { + "@types/chai": "*" + } + }, + "node_modules/@types/concat-stream": { + "version": "1.6.1", + "resolved": "https://registry.npmjs.org/@types/concat-stream/-/concat-stream-1.6.1.tgz", + "integrity": "sha512-eHE4cQPoj6ngxBZMvVf6Hw7Mh4jMW4U9lpGmS5GBPB9RYxlFg+CHaVN7ErNY4W9XfLIEn20b4VDYaIrbq0q4uA==", + "dev": true, + "peer": true, + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@types/form-data": { + "version": "0.0.33", + "resolved": "https://registry.npmjs.org/@types/form-data/-/form-data-0.0.33.tgz", + "integrity": "sha512-8BSvG1kGm83cyJITQMZSulnl6QV8jqAGreJsc5tPu1Jq0vTSOiY/k24Wx82JRpWwZSqrala6sd5rWi6aNXvqcw==", + "dev": true, + "peer": true, + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@types/glob": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/@types/glob/-/glob-7.2.0.tgz", + "integrity": "sha512-ZUxbzKl0IfJILTS6t7ip5fQQM/J3TJYubDm3nMbgubNNYS62eXeUpoLUC8/7fJNiFYHTrGPQn7hspDUzIHX3UA==", + "dev": true, + "peer": true, + "dependencies": { + "@types/minimatch": "*", + "@types/node": "*" + } + }, + "node_modules/@types/lru-cache": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/@types/lru-cache/-/lru-cache-5.1.1.tgz", + "integrity": "sha512-ssE3Vlrys7sdIzs5LOxCzTVMsU7i9oa/IaW92wF32JFb3CVczqOkru2xspuKczHEbG3nvmPY7IFqVmGGHdNbYw==" + }, + "node_modules/@types/minimatch": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/@types/minimatch/-/minimatch-5.1.2.tgz", + "integrity": "sha512-K0VQKziLUWkVKiRVrx4a40iPaxTUefQmjtkQofBkYRcoaaL/8rhwDWww9qWbrgicNOgnpIsMxyNIUM4+n6dUIA==", + "dev": true, + "peer": true + }, + "node_modules/@types/mocha": { + "version": "10.0.6", + "resolved": "https://registry.npmjs.org/@types/mocha/-/mocha-10.0.6.tgz", + "integrity": "sha512-dJvrYWxP/UcXm36Qn36fxhUKu8A/xMRXVT2cliFF1Z7UA9liG5Psj3ezNSZw+5puH2czDXRLcXQxf8JbJt0ejg==", + "dev": true, + "peer": true + }, + "node_modules/@types/node": { + "version": "20.10.6", + "resolved": "https://registry.npmjs.org/@types/node/-/node-20.10.6.tgz", + "integrity": "sha512-Vac8H+NlRNNlAmDfGUP7b5h/KA+AtWIzuXy0E6OyP8f1tCLYAtPvKRRDJjAPqhpCb0t6U2j7/xqAuLEebW2kiw==", + "dependencies": { + "undici-types": "~5.26.4" + } + }, + "node_modules/@types/pbkdf2": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/@types/pbkdf2/-/pbkdf2-3.1.2.tgz", + "integrity": "sha512-uRwJqmiXmh9++aSu1VNEn3iIxWOhd8AHXNSdlaLfdAAdSTY9jYVeGWnzejM3dvrkbqE3/hyQkQQ29IFATEGlew==", + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@types/prettier": { + "version": "2.7.3", + "resolved": "https://registry.npmjs.org/@types/prettier/-/prettier-2.7.3.tgz", + "integrity": "sha512-+68kP9yzs4LMp7VNh8gdzMSPZFL44MLGqiHWvttYJe+6qnuVr4Ek9wSBQoveqY/r+LwjCcU29kNVkidwim+kYA==", + "dev": true, + "peer": true + }, + "node_modules/@types/qs": { + "version": "6.9.11", + "resolved": "https://registry.npmjs.org/@types/qs/-/qs-6.9.11.tgz", + "integrity": "sha512-oGk0gmhnEJK4Yyk+oI7EfXsLayXatCWPHary1MtcmbAifkobT9cM9yutG/hZKIseOU0MqbIwQ/u2nn/Gb+ltuQ==", + "dev": true, + "peer": true + }, + "node_modules/@types/readable-stream": { + "version": "2.3.15", + "resolved": "https://registry.npmjs.org/@types/readable-stream/-/readable-stream-2.3.15.tgz", + "integrity": "sha512-oM5JSKQCcICF1wvGgmecmHldZ48OZamtMxcGGVICOJA8o8cahXC1zEVAif8iwoc5j8etxFaRFnf095+CDsuoFQ==", + "dependencies": { + "@types/node": "*", + "safe-buffer": "~5.1.1" + } + }, + "node_modules/@types/readable-stream/node_modules/safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" + }, + "node_modules/@types/secp256k1": { + "version": "4.0.6", + "resolved": "https://registry.npmjs.org/@types/secp256k1/-/secp256k1-4.0.6.tgz", + "integrity": "sha512-hHxJU6PAEUn0TP4S/ZOzuTUvJWuZ6eIKeNKb5RBpODvSl6hp1Wrw4s7ATY50rklRCScUDpHzVA/DQdSjJ3UoYQ==", + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/abbrev": { + "version": "1.0.9", + "resolved": "https://registry.npmjs.org/abbrev/-/abbrev-1.0.9.tgz", + "integrity": "sha512-LEyx4aLEC3x6T0UguF6YILf+ntvmOaWsVfENmIW0E9H09vKlLDGelMjjSm0jkDHALj8A8quZ/HapKNigzwge+Q==", + "dev": true, + "peer": true + }, + "node_modules/abstract-level": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/abstract-level/-/abstract-level-1.0.3.tgz", + "integrity": "sha512-t6jv+xHy+VYwc4xqZMn2Pa9DjcdzvzZmQGRjTFc8spIbRGHgBrEKbPq+rYXc7CCo0lxgYvSgKVg9qZAhpVQSjA==", + "dependencies": { + "buffer": "^6.0.3", + "catering": "^2.1.0", + "is-buffer": "^2.0.5", + "level-supports": "^4.0.0", + "level-transcoder": "^1.0.1", + "module-error": "^1.0.1", + "queue-microtask": "^1.2.3" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/acorn": { + "version": "8.11.3", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.11.3.tgz", + "integrity": "sha512-Y9rRfJG5jcKOE0CLisYbojUjIrIEE7AGMzA/Sm4BslANhbS+cDMpgBdcPT91oJ7OuJ9hYJBx59RjbhxVnrF8Xg==", + "devOptional": true, + "peer": true, + "bin": { + "acorn": "bin/acorn" + }, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/acorn-walk": { + "version": "8.3.1", + "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-8.3.1.tgz", + "integrity": "sha512-TgUZgYvqZprrl7YldZNoa9OciCAyZR+Ejm9eXzKCmjsF5IKp/wgQ7Z/ZpjpGTIUPwrHQIcYeI8qDh4PsEwxMbw==", + "devOptional": true, + "peer": true, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/address": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/address/-/address-1.2.2.tgz", + "integrity": "sha512-4B/qKCfeE/ODUaAUpSwfzazo5x29WD4r3vXiWsB7I2mSDAihwEqKO+g8GELZUQSSAo5e1XTYh3ZVfLyxBc12nA==", + "dev": true, + "peer": true, + "engines": { + "node": ">= 10.0.0" + } + }, + "node_modules/adm-zip": { + "version": "0.4.16", + "resolved": "https://registry.npmjs.org/adm-zip/-/adm-zip-0.4.16.tgz", + "integrity": "sha512-TFi4HBKSGfIKsK5YCkKaaFG2m4PEDyViZmEwof3MTIgzimHLto6muaHVpbrljdIvIrFZzEq/p4nafOeLcYegrg==", + "engines": { + "node": ">=0.3.0" + } + }, + "node_modules/aes-js": { + "version": "4.0.0-beta.5", + "resolved": "https://registry.npmjs.org/aes-js/-/aes-js-4.0.0-beta.5.tgz", + "integrity": "sha512-G965FqalsNyrPqgEGON7nIx1e/OVENSgiEIzyC63haUMuvNnwIgIjMs52hlTCKhkBny7A2ORNlfY9Zu+jmGk1Q==", + "dev": true + }, + "node_modules/agent-base": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-6.0.2.tgz", + "integrity": "sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ==", + "dependencies": { + "debug": "4" + }, + "engines": { + "node": ">= 6.0.0" + } + }, + "node_modules/aggregate-error": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/aggregate-error/-/aggregate-error-3.1.0.tgz", + "integrity": "sha512-4I7Td01quW/RpocfNayFdFVk1qSuoh0E7JrbRJ16nH01HhKFQ88INq9Sd+nd72zqRySlr9BmDA8xlEJ6vJMrYA==", + "dependencies": { + "clean-stack": "^2.0.0", + "indent-string": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/ajv": { + "version": "8.12.0", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.12.0.tgz", + "integrity": "sha512-sRu1kpcO9yLtYxBKvqfTeh9KzZEwO3STyX1HT+4CaDzC6HpTGYhIhPIzj9XuKU7KYDwnaeh5hcOwjy1QuJzBPA==", + "dev": true, + "peer": true, + "dependencies": { + "fast-deep-equal": "^3.1.1", + "json-schema-traverse": "^1.0.0", + "require-from-string": "^2.0.2", + "uri-js": "^4.2.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/amazon-cognito-identity-js": { + "version": "6.3.7", + "resolved": "https://registry.npmjs.org/amazon-cognito-identity-js/-/amazon-cognito-identity-js-6.3.7.tgz", + "integrity": "sha512-tSjnM7KyAeOZ7UMah+oOZ6cW4Gf64FFcc7BE2l7MTcp7ekAPrXaCbpcW2xEpH1EiDS4cPcAouHzmCuc2tr72vQ==", + "dev": true, + "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" + } + }, + "node_modules/amazon-cognito-identity-js/node_modules/buffer": { + "version": "4.9.2", + "resolved": "https://registry.npmjs.org/buffer/-/buffer-4.9.2.tgz", + "integrity": "sha512-xq+q3SRMOxGivLhBNaUdC64hDTQwejJ+H0T/NB1XMtTVEwNTrfFF3gAxiyW0Bu/xWEGhjVKgUcMhCrUy2+uCWg==", + "dev": true, + "dependencies": { + "base64-js": "^1.0.2", + "ieee754": "^1.1.4", + "isarray": "^1.0.0" + } + }, + "node_modules/amdefine": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/amdefine/-/amdefine-1.0.1.tgz", + "integrity": "sha512-S2Hw0TtNkMJhIabBwIojKL9YHO5T0n5eNqWJ7Lrlel/zDbftQpxpapi8tZs3X1HWa+u+QeydGmzzNU0m09+Rcg==", + "dev": true, + "optional": true, + "peer": true, + "engines": { + "node": ">=0.4.2" + } + }, + "node_modules/ansi-colors": { + "version": "4.1.3", + "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-4.1.3.tgz", + "integrity": "sha512-/6w/C21Pm1A7aZitlI5Ni/2J6FFQN8i1Cvz3kHABAAbw93v/NlvKdVOqz7CCWz/3iv/JplRSEEZ83XION15ovw==", + "engines": { + "node": ">=6" + } + }, + "node_modules/ansi-escapes": { + "version": "4.3.2", + "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-4.3.2.tgz", + "integrity": "sha512-gKXj5ALrKWQLsYG9jlTRmR/xKluxHV+Z9QEwNIgCfM1/uwPMCuzVVnh5mwTd+OuBZcwSIMbqssNWRm1lE51QaQ==", + "dependencies": { + "type-fest": "^0.21.3" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "engines": { + "node": ">=8" + } + }, + "node_modules/ansi-styles": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", + "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", + "dependencies": { + "color-convert": "^1.9.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/antlr4ts": { + "version": "0.5.0-alpha.4", + "resolved": "https://registry.npmjs.org/antlr4ts/-/antlr4ts-0.5.0-alpha.4.tgz", + "integrity": "sha512-WPQDt1B74OfPv/IMS2ekXAKkTZIHl88uMetg6q3OTqgFxZ/dxDXI0EWLyZid/1Pe6hTftyg5N7gel5wNAGxXyQ==", + "dev": true, + "peer": true + }, + "node_modules/anymatch": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz", + "integrity": "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==", + "dependencies": { + "normalize-path": "^3.0.0", + "picomatch": "^2.0.4" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/arg": { + "version": "4.1.3", + "resolved": "https://registry.npmjs.org/arg/-/arg-4.1.3.tgz", + "integrity": "sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA==", + "devOptional": true, + "peer": true + }, + "node_modules/argparse": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", + "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==" + }, + "node_modules/array-back": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/array-back/-/array-back-3.1.0.tgz", + "integrity": "sha512-TkuxA4UCOvxuDK6NZYXCalszEzj+TLszyASooky+i742l9TqsOdYCMJJupxRic61hwquNtppB3hgcuq9SVSH1Q==", + "dev": true, + "peer": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/array-buffer-byte-length": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/array-buffer-byte-length/-/array-buffer-byte-length-1.0.0.tgz", + "integrity": "sha512-LPuwb2P+NrQw3XhxGc36+XSvuBPopovXYTR9Ew++Du9Yb/bx5AzBfrIsBoj0EZUifjQU+sHL21sseZ3jerWO/A==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.2", + "is-array-buffer": "^3.0.1" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/array-union": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/array-union/-/array-union-2.1.0.tgz", + "integrity": "sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==", + "dev": true, + "peer": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/array-uniq": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/array-uniq/-/array-uniq-1.0.3.tgz", + "integrity": "sha512-MNha4BWQ6JbwhFhj03YK552f7cb3AzoE8SzeljgChvL1dl3IcvggXVz1DilzySZkCja+CXuZbdW7yATchWn8/Q==", + "dev": true, + "peer": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/array.prototype.findlast": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/array.prototype.findlast/-/array.prototype.findlast-1.2.3.tgz", + "integrity": "sha512-kcBubumjciBg4JKp5KTKtI7ec7tRefPk88yjkWJwaVKYd9QfTaxcsOxoMNKd7iBr447zCfDV0z1kOF47umv42g==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.2", + "define-properties": "^1.2.0", + "es-abstract": "^1.22.1", + "es-shim-unscopables": "^1.0.0", + "get-intrinsic": "^1.2.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/arraybuffer.prototype.slice": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/arraybuffer.prototype.slice/-/arraybuffer.prototype.slice-1.0.2.tgz", + "integrity": "sha512-yMBKppFur/fbHu9/6USUe03bZ4knMYiwFBcyiaXB8Go0qNehwX6inYPzK9U0NeQvGxKthcmHcaR8P5MStSRBAw==", + "dev": true, + "dependencies": { + "array-buffer-byte-length": "^1.0.0", + "call-bind": "^1.0.2", + "define-properties": "^1.2.0", + "es-abstract": "^1.22.1", + "get-intrinsic": "^1.2.1", + "is-array-buffer": "^3.0.2", + "is-shared-array-buffer": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/asap": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/asap/-/asap-2.0.6.tgz", + "integrity": "sha512-BSHWgDSAiKs50o2Re8ppvp3seVHXSRM44cdSsT9FfNEUUZLOGWVCsiWaRPWM1Znn+mqZ1OfVZ3z3DWEzSp7hRA==", + "dev": true, + "peer": true + }, + "node_modules/assertion-error": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/assertion-error/-/assertion-error-1.1.0.tgz", + "integrity": "sha512-jgsaNduz+ndvGyFt3uSuWqvy4lCnIJiovtouQN5JZHOKCS2QuhEdbcQHFhVksz2N2U9hXJo8odG7ETyWlEeuDw==", + "dev": true, + "peer": true, + "engines": { + "node": "*" + } + }, + "node_modules/astral-regex": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/astral-regex/-/astral-regex-2.0.0.tgz", + "integrity": "sha512-Z7tMw1ytTXt5jqMcOP+OQteU1VuNK9Y02uuJtKQ1Sv69jXQKKg5cibLwGJow8yzZP+eAc18EmLGPal0bp36rvQ==", + "dev": true, + "peer": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/async": { + "version": "1.5.2", + "resolved": "https://registry.npmjs.org/async/-/async-1.5.2.tgz", + "integrity": "sha512-nSVgobk4rv61R9PUSDtYt7mPVB2olxNR5RWJcAsH676/ef11bUZwvu7+RGYrYauVdDPcO519v68wRhXQtxsV9w==", + "dev": true, + "peer": true + }, + "node_modules/async-retry": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/async-retry/-/async-retry-1.3.3.tgz", + "integrity": "sha512-wfr/jstw9xNi/0teMHrRW7dsz3Lt5ARhYNZ2ewpadnhaIp5mbALhOAP+EAdsC7t4Z6wqsDVv9+W6gm1Dk9mEyw==", + "dev": true, + "dependencies": { + "retry": "0.13.1" + } + }, + "node_modules/asynckit": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", + "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==", + "dev": true + }, + "node_modules/at-least-node": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/at-least-node/-/at-least-node-1.0.0.tgz", + "integrity": "sha512-+q/t7Ekv1EDY2l6Gda6LLiX14rU9TV20Wa3ofeQmwPFZbOMo9DXrLbOjFaaclkXKWidIaopwAObQDqwWtGUjqg==", + "dev": true, + "peer": true, + "engines": { + "node": ">= 4.0.0" + } + }, + "node_modules/available-typed-arrays": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/available-typed-arrays/-/available-typed-arrays-1.0.5.tgz", + "integrity": "sha512-DMD0KiN46eipeziST1LPP/STfDU0sufISXmjSgvVsoU2tqxctQeASejWcfNtxYKqETM1UxQ8sp2OrSBWpHY6sw==", + "dev": true, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/axios": { + "version": "1.6.4", + "resolved": "https://registry.npmjs.org/axios/-/axios-1.6.4.tgz", + "integrity": "sha512-heJnIs6N4aa1eSthhN9M5ioILu8Wi8vmQW9iHQ9NUvfkJb0lEEDUiIdQNAuBtfUt3FxReaKdpQA5DbmMOqzF/A==", + "dev": true, + "dependencies": { + "follow-redirects": "^1.15.4", + "form-data": "^4.0.0", + "proxy-from-env": "^1.1.0" + } + }, + "node_modules/balanced-match": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==" + }, + "node_modules/base-x": { + "version": "3.0.9", + "resolved": "https://registry.npmjs.org/base-x/-/base-x-3.0.9.tgz", + "integrity": "sha512-H7JU6iBHTal1gp56aKoaa//YUxEaAOUiydvrV/pILqIHXTtqxSkATOnDA2u+jZ/61sD+L/412+7kzXRtWukhpQ==", + "dependencies": { + "safe-buffer": "^5.0.1" + } + }, + "node_modules/base64-js": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz", + "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ] + }, + "node_modules/bech32": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/bech32/-/bech32-1.1.4.tgz", + "integrity": "sha512-s0IrSOzLlbvX7yp4WBfPITzpAU8sqQcpsmwXDiKwrG4r491vwCO/XpejasRNl0piBMe/DvP4Tz0mIS/X1DPJBQ==" + }, + "node_modules/bigint-crypto-utils": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/bigint-crypto-utils/-/bigint-crypto-utils-3.3.0.tgz", + "integrity": "sha512-jOTSb+drvEDxEq6OuUybOAv/xxoh3cuYRUIPyu8sSHQNKM303UQ2R1DAo45o1AkcIXw6fzbaFI1+xGGdaXs2lg==", + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/binary-extensions": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.2.0.tgz", + "integrity": "sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA==", + "engines": { + "node": ">=8" + } + }, + "node_modules/blakejs": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/blakejs/-/blakejs-1.2.1.tgz", + "integrity": "sha512-QXUSXI3QVc/gJME0dBpXrag1kbzOqCjCX8/b54ntNyW6sjtoqxqRk3LTmXzaJoh71zMsDCjM+47jS7XiwN/+fQ==" + }, + "node_modules/bn.js": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-5.2.1.tgz", + "integrity": "sha512-eXRvHzWyYPBuB4NBy0cmYQjGitUrtqwbvlzP3G6VFnNRbsZQIxQ10PbKKHt8gZ/HW/D/747aDl+QkDqg3KQLMQ==" + }, + "node_modules/brace-expansion": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/braces": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz", + "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==", + "dependencies": { + "fill-range": "^7.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/brorand": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/brorand/-/brorand-1.1.0.tgz", + "integrity": "sha512-cKV8tMCEpQs4hK/ik71d6LrPOnpkpGBR0wzxqr68g2m/LB2GxVYQroAjMJZRVM1Y4BCjCKc3vAamxSzOY2RP+w==" + }, + "node_modules/browser-level": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/browser-level/-/browser-level-1.0.1.tgz", + "integrity": "sha512-XECYKJ+Dbzw0lbydyQuJzwNXtOpbMSq737qxJN11sIRTErOMShvDpbzTlgju7orJKvx4epULolZAuJGLzCmWRQ==", + "dependencies": { + "abstract-level": "^1.0.2", + "catering": "^2.1.1", + "module-error": "^1.0.2", + "run-parallel-limit": "^1.1.0" + } + }, + "node_modules/browser-stdout": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/browser-stdout/-/browser-stdout-1.3.1.tgz", + "integrity": "sha512-qhAVI1+Av2X7qelOfAIYwXONood6XlZE/fXaBSmW/T5SzLAmCgzi+eiWE7fUvbHaeNBQH13UftjpXxsfLkMpgw==" + }, + "node_modules/browserify-aes": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/browserify-aes/-/browserify-aes-1.2.0.tgz", + "integrity": "sha512-+7CHXqGuspUn/Sl5aO7Ea0xWGAtETPXNSAjHo48JfLdPWcMng33Xe4znFvQweqc/uzk5zSOI3H52CYnjCfb5hA==", + "dependencies": { + "buffer-xor": "^1.0.3", + "cipher-base": "^1.0.0", + "create-hash": "^1.1.0", + "evp_bytestokey": "^1.0.3", + "inherits": "^2.0.1", + "safe-buffer": "^5.0.1" + } + }, + "node_modules/bs58": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/bs58/-/bs58-4.0.1.tgz", + "integrity": "sha512-Ok3Wdf5vOIlBrgCvTq96gBkJw+JUEzdBgyaza5HLtPm7yTHkjRy8+JzNyHF7BHa0bNWOQIp3m5YF0nnFcOIKLw==", + "dependencies": { + "base-x": "^3.0.2" + } + }, + "node_modules/bs58check": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/bs58check/-/bs58check-2.1.2.tgz", + "integrity": "sha512-0TS1jicxdU09dwJMNZtVAfzPi6Q6QeN0pM1Fkzrjn+XYHvzMKPU3pHVpva+769iNVSfIYWf7LJ6WR+BuuMf8cA==", + "dependencies": { + "bs58": "^4.0.0", + "create-hash": "^1.1.0", + "safe-buffer": "^5.1.2" + } + }, + "node_modules/buffer": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/buffer/-/buffer-6.0.3.tgz", + "integrity": "sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "dependencies": { + "base64-js": "^1.3.1", + "ieee754": "^1.2.1" + } + }, + "node_modules/buffer-from": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz", + "integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==" + }, + "node_modules/buffer-xor": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/buffer-xor/-/buffer-xor-1.0.3.tgz", + "integrity": "sha512-571s0T7nZWK6vB67HI5dyUF7wXiNcfaPPPTl6zYCNApANjIvYJTg7hlud/+cJpdAhS7dVzqMLmfhfHR3rAcOjQ==" + }, + "node_modules/bytes": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz", + "integrity": "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/call-bind": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.5.tgz", + "integrity": "sha512-C3nQxfFZxFRVoJoGKKI8y3MOEo129NQ+FgQ08iye+Mk4zNZZGdjfs06bVTr+DBSlA66Q2VEcMki/cUCP4SercQ==", + "dev": true, + "dependencies": { + "function-bind": "^1.1.2", + "get-intrinsic": "^1.2.1", + "set-function-length": "^1.1.1" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/camelcase": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-6.3.0.tgz", + "integrity": "sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/case": { + "version": "1.6.3", + "resolved": "https://registry.npmjs.org/case/-/case-1.6.3.tgz", + "integrity": "sha512-mzDSXIPaFwVDvZAHqZ9VlbyF4yyXRuX6IvB06WvPYkqJVO24kX1PPhv9bfpKNFZyxYFmmgo03HUiD8iklmJYRQ==", + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/caseless": { + "version": "0.12.0", + "resolved": "https://registry.npmjs.org/caseless/-/caseless-0.12.0.tgz", + "integrity": "sha512-4tYFyifaFfGacoiObjJegolkwSU4xQNGbVgUiNYVUxbQ2x2lUsFvY4hVgVzGiIe6WLOPqycWXA40l+PWsxthUw==", + "dev": true, + "peer": true + }, + "node_modules/catering": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/catering/-/catering-2.1.1.tgz", + "integrity": "sha512-K7Qy8O9p76sL3/3m7/zLKbRkyOlSZAgzEaLhyj2mXS8PsCud2Eo4hAb8aLtZqHh0QGqLcb9dlJSu6lHRVENm1w==", + "engines": { + "node": ">=6" + } + }, + "node_modules/cbor": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/cbor/-/cbor-8.1.0.tgz", + "integrity": "sha512-DwGjNW9omn6EwP70aXsn7FQJx5kO12tX0bZkaTjzdVFM6/7nhA4t0EENocKGx6D2Bch9PE2KzCUf5SceBdeijg==", + "dev": true, + "peer": true, + "dependencies": { + "nofilter": "^3.1.0" + }, + "engines": { + "node": ">=12.19" + } + }, + "node_modules/chai": { + "version": "4.3.10", + "resolved": "https://registry.npmjs.org/chai/-/chai-4.3.10.tgz", + "integrity": "sha512-0UXG04VuVbruMUYbJ6JctvH0YnC/4q3/AkT18q4NaITo91CUm0liMS9VqzT9vZhVQ/1eqPanMWjBM+Juhfb/9g==", + "dev": true, + "peer": true, + "dependencies": { + "assertion-error": "^1.1.0", + "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.8" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/chai-as-promised": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/chai-as-promised/-/chai-as-promised-7.1.1.tgz", + "integrity": "sha512-azL6xMoi+uxu6z4rhWQ1jbdUhOMhis2PvscD/xjLqNMkv3BPPp2JyyuTHOrf9BOosGpNQ11v6BKv/g57RXbiaA==", + "dev": true, + "peer": true, + "dependencies": { + "check-error": "^1.0.2" + }, + "peerDependencies": { + "chai": ">= 2.1.2 < 5" + } + }, + "node_modules/chalk": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", + "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", + "dependencies": { + "ansi-styles": "^3.2.1", + "escape-string-regexp": "^1.0.5", + "supports-color": "^5.3.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/charenc": { + "version": "0.0.2", + "resolved": "https://registry.npmjs.org/charenc/-/charenc-0.0.2.tgz", + "integrity": "sha512-yrLQ/yVUFXkzg7EDQsPieE/53+0RlaWTs+wBrvW36cyilJ2SaDWfl4Yj7MtLTXleV9uEKefbAGUPv2/iWSooRA==", + "dev": true, + "peer": true, + "engines": { + "node": "*" + } + }, + "node_modules/check-error": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/check-error/-/check-error-1.0.3.tgz", + "integrity": "sha512-iKEoDYaRmd1mxM90a2OEfWhjsjPpYPuQ+lMYsoxB126+t8fw7ySEO48nmDg5COTjxDI65/Y2OWpeEHk3ZOe8zg==", + "dev": true, + "peer": true, + "dependencies": { + "get-func-name": "^2.0.2" + }, + "engines": { + "node": "*" + } + }, + "node_modules/chokidar": { + "version": "3.5.3", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.5.3.tgz", + "integrity": "sha512-Dr3sfKRP6oTcjf2JmUmFJfeVMvXBdegxB0iVQ5eb2V10uFJUCAS8OByZdVAyVb8xXNz3GjjTgj9kLWsZTqE6kw==", + "funding": [ + { + "type": "individual", + "url": "https://paulmillr.com/funding/" + } + ], + "dependencies": { + "anymatch": "~3.1.2", + "braces": "~3.0.2", + "glob-parent": "~5.1.2", + "is-binary-path": "~2.1.0", + "is-glob": "~4.0.1", + "normalize-path": "~3.0.0", + "readdirp": "~3.6.0" + }, + "engines": { + "node": ">= 8.10.0" + }, + "optionalDependencies": { + "fsevents": "~2.3.2" + } + }, + "node_modules/ci-info": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-2.0.0.tgz", + "integrity": "sha512-5tK7EtrZ0N+OLFMthtqOj4fI2Jeb88C4CAZPu25LDVUgXJ0A3Js4PMGqrn0JU1W0Mh1/Z8wZzYPxqUrXeBboCQ==" + }, + "node_modules/cipher-base": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/cipher-base/-/cipher-base-1.0.4.tgz", + "integrity": "sha512-Kkht5ye6ZGmwv40uUDZztayT2ThLQGfnj/T71N/XzeZeo3nf8foyW7zGTsPYkEya3m5f3cAypH+qe7YOrM1U2Q==", + "dependencies": { + "inherits": "^2.0.1", + "safe-buffer": "^5.0.1" + } + }, + "node_modules/classic-level": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/classic-level/-/classic-level-1.3.0.tgz", + "integrity": "sha512-iwFAJQYtqRTRM0F6L8h4JCt00ZSGdOyqh7yVrhhjrOpFhmBjNlRUey64MCiyo6UmQHMJ+No3c81nujPv+n9yrg==", + "hasInstallScript": true, + "dependencies": { + "abstract-level": "^1.0.2", + "catering": "^2.1.0", + "module-error": "^1.0.1", + "napi-macros": "^2.2.2", + "node-gyp-build": "^4.3.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/clean-stack": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/clean-stack/-/clean-stack-2.2.0.tgz", + "integrity": "sha512-4diC9HaTE+KRAMWhDhrGOECgWZxoevMc5TlkObMqNSsVU62PYzXZ/SMTjzyGAFF1YusgxGcSWTEXBhp0CPwQ1A==", + "engines": { + "node": ">=6" + } + }, + "node_modules/cli-table3": { + "version": "0.5.1", + "resolved": "https://registry.npmjs.org/cli-table3/-/cli-table3-0.5.1.tgz", + "integrity": "sha512-7Qg2Jrep1S/+Q3EceiZtQcDPWxhAvBw+ERf1162v4sikJrvojMHFqXt8QIVha8UlH9rgU0BeWPytZ9/TzYqlUw==", + "dev": true, + "peer": true, + "dependencies": { + "object-assign": "^4.1.0", + "string-width": "^2.1.1" + }, + "engines": { + "node": ">=6" + }, + "optionalDependencies": { + "colors": "^1.1.2" + } + }, + "node_modules/cliui": { + "version": "7.0.4", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-7.0.4.tgz", + "integrity": "sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ==", + "dependencies": { + "string-width": "^4.2.0", + "strip-ansi": "^6.0.0", + "wrap-ansi": "^7.0.0" + } + }, + "node_modules/cliui/node_modules/is-fullwidth-code-point": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "engines": { + "node": ">=8" + } + }, + "node_modules/cliui/node_modules/string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/color-convert": { + "version": "1.9.3", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", + "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", + "dependencies": { + "color-name": "1.1.3" + } + }, + "node_modules/color-name": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", + "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==" + }, + "node_modules/colors": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/colors/-/colors-1.4.0.tgz", + "integrity": "sha512-a+UqTh4kgZg/SlGvfbzDHpgRu7AAQOmmqRHJnxhRZICKFUT91brVhNNt58CMWU9PsBbv3PDCZUHbVxuDiH2mtA==", + "dev": true, + "peer": true, + "engines": { + "node": ">=0.1.90" + } + }, + "node_modules/combined-stream": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", + "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", + "dev": true, + "dependencies": { + "delayed-stream": "~1.0.0" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/command-exists": { + "version": "1.2.9", + "resolved": "https://registry.npmjs.org/command-exists/-/command-exists-1.2.9.tgz", + "integrity": "sha512-LTQ/SGc+s0Xc0Fu5WaKnR0YiygZkm9eKFvyS+fRsU7/ZWFF8ykFM6Pc9aCVf1+xasOOZpO3BAVgVrKvsqKHV7w==" + }, + "node_modules/command-line-args": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/command-line-args/-/command-line-args-5.2.1.tgz", + "integrity": "sha512-H4UfQhZyakIjC74I9d34fGYDwk3XpSr17QhEd0Q3I9Xq1CETHo4Hcuo87WyWHpAF1aSLjLRf5lD9ZGX2qStUvg==", + "dev": true, + "peer": true, + "dependencies": { + "array-back": "^3.1.0", + "find-replace": "^3.0.0", + "lodash.camelcase": "^4.3.0", + "typical": "^4.0.0" + }, + "engines": { + "node": ">=4.0.0" + } + }, + "node_modules/command-line-usage": { + "version": "6.1.3", + "resolved": "https://registry.npmjs.org/command-line-usage/-/command-line-usage-6.1.3.tgz", + "integrity": "sha512-sH5ZSPr+7UStsloltmDh7Ce5fb8XPlHyoPzTpyyMuYCtervL65+ubVZ6Q61cFtFl62UyJlc8/JwERRbAFPUqgw==", + "dev": true, + "peer": true, + "dependencies": { + "array-back": "^4.0.2", + "chalk": "^2.4.2", + "table-layout": "^1.0.2", + "typical": "^5.2.0" + }, + "engines": { + "node": ">=8.0.0" + } + }, + "node_modules/command-line-usage/node_modules/array-back": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/array-back/-/array-back-4.0.2.tgz", + "integrity": "sha512-NbdMezxqf94cnNfWLL7V/im0Ub+Anbb0IoZhvzie8+4HJ4nMQuzHuy49FkGYCJK2yAloZ3meiB6AVMClbrI1vg==", + "dev": true, + "peer": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/command-line-usage/node_modules/typical": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/typical/-/typical-5.2.0.tgz", + "integrity": "sha512-dvdQgNDNJo+8B2uBQoqdb11eUCE1JQXhvjC/CZtgvZseVd5TYMXnq0+vuUemXbd/Se29cTaUuPX3YIc2xgbvIg==", + "dev": true, + "peer": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/commander": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/commander/-/commander-3.0.2.tgz", + "integrity": "sha512-Gar0ASD4BDyKC4hl4DwHqDrmvjoxWKZigVnAbn5H1owvm4CxCPdb0HQDehwNYMJpla5+M2tPmPARzhtYuwpHow==" + }, + "node_modules/compare-versions": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/compare-versions/-/compare-versions-6.1.0.tgz", + "integrity": "sha512-LNZQXhqUvqUTotpZ00qLSaify3b4VFD588aRr8MKFw4CMUr98ytzCW5wDH5qx/DEY5kCDXcbcRuCqL0szEf2tg==", + "dev": true + }, + "node_modules/concat-map": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", + "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==" + }, + "node_modules/concat-stream": { + "version": "1.6.2", + "resolved": "https://registry.npmjs.org/concat-stream/-/concat-stream-1.6.2.tgz", + "integrity": "sha512-27HBghJxjiZtIk3Ycvn/4kbJk/1uZuJFfuPEns6LaEvpvG1f0hTea8lilrouyo9mVc2GWdcEZ8OLoGmSADlrCw==", + "dev": true, + "engines": [ + "node >= 0.8" + ], + "peer": true, + "dependencies": { + "buffer-from": "^1.0.0", + "inherits": "^2.0.3", + "readable-stream": "^2.2.2", + "typedarray": "^0.0.6" + } + }, + "node_modules/concat-stream/node_modules/readable-stream": { + "version": "2.3.8", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.8.tgz", + "integrity": "sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==", + "dev": true, + "peer": true, + "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" + } + }, + "node_modules/concat-stream/node_modules/safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", + "dev": true, + "peer": true + }, + "node_modules/concat-stream/node_modules/string_decoder": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "dev": true, + "peer": true, + "dependencies": { + "safe-buffer": "~5.1.0" + } + }, + "node_modules/cookie": { + "version": "0.4.2", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.4.2.tgz", + "integrity": "sha512-aSWTXFzaKWkvHO1Ny/s+ePFpvKsPnjc551iI41v3ny/ow6tBG5Vd+FuqGNhh1LxOmVzOlGUriIlOaokOvhaStA==", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/core-util-is": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.3.tgz", + "integrity": "sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==", + "dev": true, + "peer": true + }, + "node_modules/crc-32": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/crc-32/-/crc-32-1.2.2.tgz", + "integrity": "sha512-ROmzCKrTnOwybPcJApAA6WBWij23HVfGVNKqqrZpuyZOHqK2CwHSvpGuyt/UNNvaIjEd8X5IFGp4Mh+Ie1IHJQ==", + "bin": { + "crc32": "bin/crc32.njs" + }, + "engines": { + "node": ">=0.8" + } + }, + "node_modules/create-hash": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/create-hash/-/create-hash-1.2.0.tgz", + "integrity": "sha512-z00bCGNHDG8mHAkP7CtT1qVu+bFQUPjYq/4Iv3C3kWjTFV10zIjfSoeqXo9Asws8gwSHDGj/hl2u4OGIjapeCg==", + "dependencies": { + "cipher-base": "^1.0.1", + "inherits": "^2.0.1", + "md5.js": "^1.3.4", + "ripemd160": "^2.0.1", + "sha.js": "^2.4.0" + } + }, + "node_modules/create-hmac": { + "version": "1.1.7", + "resolved": "https://registry.npmjs.org/create-hmac/-/create-hmac-1.1.7.tgz", + "integrity": "sha512-MJG9liiZ+ogc4TzUwuvbER1JRdgvUFSB5+VR/g5h82fGaIRWMWddtKBHi7/sVhfjQZ6SehlyhvQYrcYkaUIpLg==", + "dependencies": { + "cipher-base": "^1.0.3", + "create-hash": "^1.1.0", + "inherits": "^2.0.1", + "ripemd160": "^2.0.0", + "safe-buffer": "^5.0.1", + "sha.js": "^2.4.8" + } + }, + "node_modules/create-require": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/create-require/-/create-require-1.1.1.tgz", + "integrity": "sha512-dcKFX3jn0MpIaXjisoRvexIJVEKzaq7z2rZKxf+MSr9TkdmHmsU4m2lcLojrj/FHl8mk5VxMmYA+ftRkP/3oKQ==", + "devOptional": true, + "peer": true + }, + "node_modules/crypt": { + "version": "0.0.2", + "resolved": "https://registry.npmjs.org/crypt/-/crypt-0.0.2.tgz", + "integrity": "sha512-mCxBlsHFYh9C+HVpiEacem8FEBnMXgU9gy4zmNC+SXAZNB/1idgp/aulFJ4FgCi7GPEVbfyng092GqL2k2rmow==", + "dev": true, + "peer": true, + "engines": { + "node": "*" + } + }, + "node_modules/death": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/death/-/death-1.1.0.tgz", + "integrity": "sha512-vsV6S4KVHvTGxbEcij7hkWRv0It+sGGWVOM67dQde/o5Xjnr+KmLjxWJii2uEObIrt1CcM9w0Yaovx+iOlIL+w==", + "dev": true, + "peer": true + }, + "node_modules/debug": { + "version": "4.3.4", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", + "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", + "dependencies": { + "ms": "2.1.2" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/decamelize": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-4.0.0.tgz", + "integrity": "sha512-9iE1PgSik9HeIIw2JO94IidnE3eBoQrFJ3w7sFuzSX4DpmZ3v5sZpUiV5Swcf6mQEF+Y0ru8Neo+p+nyh2J+hQ==", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/deep-eql": { + "version": "4.1.3", + "resolved": "https://registry.npmjs.org/deep-eql/-/deep-eql-4.1.3.tgz", + "integrity": "sha512-WaEtAOpRA1MQ0eohqZjpGD8zdI0Ovsm8mmFhaDN8dvDZzyoUMcYDnf5Y6iu7HTXxf8JDS23qWa4a+hKCDyOPzw==", + "dev": true, + "peer": true, + "dependencies": { + "type-detect": "^4.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/deep-extend": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/deep-extend/-/deep-extend-0.6.0.tgz", + "integrity": "sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA==", + "dev": true, + "peer": true, + "engines": { + "node": ">=4.0.0" + } + }, + "node_modules/deep-is": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz", + "integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==", + "dev": true, + "peer": true + }, + "node_modules/define-data-property": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/define-data-property/-/define-data-property-1.1.1.tgz", + "integrity": "sha512-E7uGkTzkk1d0ByLeSc6ZsFS79Axg+m1P/VsgYsxHgiuc3tFSj+MjMIwe90FC4lOAZzNBdY7kkO2P2wKdsQ1vgQ==", + "dev": true, + "dependencies": { + "get-intrinsic": "^1.2.1", + "gopd": "^1.0.1", + "has-property-descriptors": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/define-properties": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.2.1.tgz", + "integrity": "sha512-8QmQKqEASLd5nx0U1B1okLElbUuuttJ/AnYmRXbbbGDWh6uS208EjD4Xqq/I9wK7u0v6O08XhTWnt5XtEbR6Dg==", + "dev": true, + "dependencies": { + "define-data-property": "^1.0.1", + "has-property-descriptors": "^1.0.0", + "object-keys": "^1.1.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/delayed-stream": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", + "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==", + "dev": true, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/depd": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz", + "integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/detect-port": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/detect-port/-/detect-port-1.5.1.tgz", + "integrity": "sha512-aBzdj76lueB6uUst5iAs7+0H/oOjqI5D16XUWxlWMIMROhcM0rfsNVk93zTngq1dDNpoXRr++Sus7ETAExppAQ==", + "dev": true, + "peer": true, + "dependencies": { + "address": "^1.0.1", + "debug": "4" + }, + "bin": { + "detect": "bin/detect-port.js", + "detect-port": "bin/detect-port.js" + } + }, + "node_modules/diff": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/diff/-/diff-5.0.0.tgz", + "integrity": "sha512-/VTCrvm5Z0JGty/BWHljh+BAiw3IK+2j87NGMu8Nwc/f48WoDAC395uomO9ZD117ZOBaHmkX1oyLvkVM/aIT3w==", + "engines": { + "node": ">=0.3.1" + } + }, + "node_modules/difflib": { + "version": "0.2.4", + "resolved": "https://registry.npmjs.org/difflib/-/difflib-0.2.4.tgz", + "integrity": "sha512-9YVwmMb0wQHQNr5J9m6BSj6fk4pfGITGQOOs+D9Fl+INODWFOfvhIU1hNv6GgR1RBoC/9NJcwu77zShxV0kT7w==", + "dev": true, + "peer": true, + "dependencies": { + "heap": ">= 0.2.0" + }, + "engines": { + "node": "*" + } + }, + "node_modules/dir-glob": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/dir-glob/-/dir-glob-3.0.1.tgz", + "integrity": "sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA==", + "dev": true, + "peer": true, + "dependencies": { + "path-type": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/dotenv": { + "version": "16.3.1", + "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.3.1.tgz", + "integrity": "sha512-IPzF4w4/Rd94bA9imS68tZBaYyBWSCE47V1RGuMrB94iyTOIEwRmVL2x/4An+6mETpLrKJ5hQkB8W4kFAadeIQ==", + "dev": true, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/motdotla/dotenv?sponsor=1" + } + }, + "node_modules/elliptic": { + "version": "6.5.4", + "resolved": "https://registry.npmjs.org/elliptic/-/elliptic-6.5.4.tgz", + "integrity": "sha512-iLhC6ULemrljPZb+QutR5TQGB+pdW6KGD5RSegS+8sorOZT+rdQFbsQFJgvN3eRqNALqJer4oQ16YvJHlU8hzQ==", + "dependencies": { + "bn.js": "^4.11.9", + "brorand": "^1.1.0", + "hash.js": "^1.0.0", + "hmac-drbg": "^1.0.1", + "inherits": "^2.0.4", + "minimalistic-assert": "^1.0.1", + "minimalistic-crypto-utils": "^1.0.1" + } + }, + "node_modules/elliptic/node_modules/bn.js": { + "version": "4.12.0", + "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.12.0.tgz", + "integrity": "sha512-c98Bf3tPniI+scsdk237ku1Dc3ujXQTSgyiPUDEOe7tRkhrqridvh8klBv0HCEso1OLOYcHuCv/cS6DNxKH+ZA==" + }, + "node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==" + }, + "node_modules/enquirer": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/enquirer/-/enquirer-2.4.1.tgz", + "integrity": "sha512-rRqJg/6gd538VHvR3PSrdRBb/1Vy2YfzHqzvbhGIQpDRKIa4FgV/54b5Q1xYSxOOwKvjXweS26E0Q+nAMwp2pQ==", + "dependencies": { + "ansi-colors": "^4.1.1", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8.6" + } + }, + "node_modules/env-paths": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/env-paths/-/env-paths-2.2.1.tgz", + "integrity": "sha512-+h1lkLKhZMTYjog1VEpJNG7NZJWcuc2DDk/qsqSTRRCOXiLjeQ1d1/udrUGhqMxUgAlwKNZ0cf2uqan5GLuS2A==", + "engines": { + "node": ">=6" + } + }, + "node_modules/es-abstract": { + "version": "1.22.3", + "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.22.3.tgz", + "integrity": "sha512-eiiY8HQeYfYH2Con2berK+To6GrK2RxbPawDkGq4UiCQQfZHb6wX9qQqkbpPqaxQFcl8d9QzZqo0tGE0VcrdwA==", + "dev": true, + "dependencies": { + "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.prototype.name": "^1.1.6", + "get-intrinsic": "^1.2.2", + "get-symbol-description": "^1.0.0", + "globalthis": "^1.0.3", + "gopd": "^1.0.1", + "has-property-descriptors": "^1.0.0", + "has-proto": "^1.0.1", + "has-symbols": "^1.0.3", + "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.13.1", + "object-keys": "^1.1.1", + "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", + "which-typed-array": "^1.1.13" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/es-set-tostringtag": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/es-set-tostringtag/-/es-set-tostringtag-2.0.2.tgz", + "integrity": "sha512-BuDyupZt65P9D2D2vA/zqcI3G5xRsklm5N3xCwuiy+/vKy8i0ifdsQP1sLgO4tZDSCaQUSnmC48khknGMV3D2Q==", + "dev": true, + "dependencies": { + "get-intrinsic": "^1.2.2", + "has-tostringtag": "^1.0.0", + "hasown": "^2.0.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-shim-unscopables": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/es-shim-unscopables/-/es-shim-unscopables-1.0.2.tgz", + "integrity": "sha512-J3yBRXCzDu4ULnQwxyToo/OjdMx6akgVC7K6few0a7F/0wLtmKKN7I73AH5T2836UuXRqN7Qg+IIUw/+YJksRw==", + "dev": true, + "dependencies": { + "hasown": "^2.0.0" + } + }, + "node_modules/es-to-primitive": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/es-to-primitive/-/es-to-primitive-1.2.1.tgz", + "integrity": "sha512-QCOllgZJtaUo9miYBcLChTUaHNjJF3PYs1VidD7AwiEj1kYxKeQTctLAezAOH5ZKRH0g2IgPn6KwB4IT8iRpvA==", + "dev": true, + "dependencies": { + "is-callable": "^1.1.4", + "is-date-object": "^1.0.1", + "is-symbol": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/escalade": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.1.tgz", + "integrity": "sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw==", + "engines": { + "node": ">=6" + } + }, + "node_modules/escape-string-regexp": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", + "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==", + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/escodegen": { + "version": "1.8.1", + "resolved": "https://registry.npmjs.org/escodegen/-/escodegen-1.8.1.tgz", + "integrity": "sha512-yhi5S+mNTOuRvyW4gWlg5W1byMaQGWWSYHXsuFZ7GBo7tpyOwi2EdzMP/QWxh9hwkD2m+wDVHJsxhRIj+v/b/A==", + "dev": true, + "peer": true, + "dependencies": { + "esprima": "^2.7.1", + "estraverse": "^1.9.1", + "esutils": "^2.0.2", + "optionator": "^0.8.1" + }, + "bin": { + "escodegen": "bin/escodegen.js", + "esgenerate": "bin/esgenerate.js" + }, + "engines": { + "node": ">=0.12.0" + }, + "optionalDependencies": { + "source-map": "~0.2.0" + } + }, + "node_modules/esprima": { + "version": "2.7.3", + "resolved": "https://registry.npmjs.org/esprima/-/esprima-2.7.3.tgz", + "integrity": "sha512-OarPfz0lFCiW4/AV2Oy1Rp9qu0iusTKqykwTspGCZtPxmF81JR4MmIebvF1F9+UOKth2ZubLQ4XGGaU+hSn99A==", + "dev": true, + "peer": true, + "bin": { + "esparse": "bin/esparse.js", + "esvalidate": "bin/esvalidate.js" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/estraverse": { + "version": "1.9.3", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-1.9.3.tgz", + "integrity": "sha512-25w1fMXQrGdoquWnScXZGckOv+Wes+JDnuN/+7ex3SauFRS72r2lFDec0EKPt2YD1wUJ/IrfEex+9yp4hfSOJA==", + "dev": true, + "peer": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/esutils": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", + "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", + "dev": true, + "peer": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/eth-gas-reporter": { + "version": "0.2.27", + "resolved": "https://registry.npmjs.org/eth-gas-reporter/-/eth-gas-reporter-0.2.27.tgz", + "integrity": "sha512-femhvoAM7wL0GcI8ozTdxfuBtBFJ9qsyIAsmKVjlWAHUbdnnXHt+lKzz/kmldM5lA9jLuNHGwuIxorNpLbR1Zw==", + "dev": true, + "peer": true, + "dependencies": { + "@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": "^5.7.2", + "fs-readdir-recursive": "^1.1.0", + "lodash": "^4.17.14", + "markdown-table": "^1.1.3", + "mocha": "^10.2.0", + "req-cwd": "^2.0.0", + "sha1": "^1.1.1", + "sync-request": "^6.0.0" + }, + "peerDependencies": { + "@codechecks/client": "^0.1.0" + }, + "peerDependenciesMeta": { + "@codechecks/client": { + "optional": true + } + } + }, + "node_modules/eth-gas-reporter/node_modules/@noble/hashes": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@noble/hashes/-/hashes-1.2.0.tgz", + "integrity": "sha512-FZfhjEDbT5GRswV3C6uvLPHMiVD6lQBmpoX5+eSiPaMTXte/IKqI5dykDxzZB/WBeK/CDuQRBWarPdi3FNY2zQ==", + "dev": true, + "funding": [ + { + "type": "individual", + "url": "https://paulmillr.com/funding/" + } + ], + "peer": true + }, + "node_modules/eth-gas-reporter/node_modules/@scure/bip32": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/@scure/bip32/-/bip32-1.1.5.tgz", + "integrity": "sha512-XyNh1rB0SkEqd3tXcXMi+Xe1fvg+kUIcoRIEujP1Jgv7DqW2r9lg3Ah0NkFaCs9sTkQAQA8kw7xiRXzENi9Rtw==", + "dev": true, + "funding": [ + { + "type": "individual", + "url": "https://paulmillr.com/funding/" + } + ], + "peer": true, + "dependencies": { + "@noble/hashes": "~1.2.0", + "@noble/secp256k1": "~1.7.0", + "@scure/base": "~1.1.0" + } + }, + "node_modules/eth-gas-reporter/node_modules/@scure/bip39": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@scure/bip39/-/bip39-1.1.1.tgz", + "integrity": "sha512-t+wDck2rVkh65Hmv280fYdVdY25J9YeEUIgn2LG1WM6gxFkGzcksoDiUkWVpVp3Oex9xGC68JU2dSbUfwZ2jPg==", + "dev": true, + "funding": [ + { + "type": "individual", + "url": "https://paulmillr.com/funding/" + } + ], + "peer": true, + "dependencies": { + "@noble/hashes": "~1.2.0", + "@scure/base": "~1.1.0" + } + }, + "node_modules/eth-gas-reporter/node_modules/ethereum-cryptography": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/ethereum-cryptography/-/ethereum-cryptography-1.2.0.tgz", + "integrity": "sha512-6yFQC9b5ug6/17CQpCyE3k9eKBMdhyVjzUy1WkiuY/E4vj/SXDBbCw8QEIaXqf0Mf2SnY6RmpDcwlUmBSS0EJw==", + "dev": true, + "peer": true, + "dependencies": { + "@noble/hashes": "1.2.0", + "@noble/secp256k1": "1.7.1", + "@scure/bip32": "1.1.5", + "@scure/bip39": "1.1.1" + } + }, + "node_modules/eth-gas-reporter/node_modules/ethers": { + "version": "5.7.2", + "resolved": "https://registry.npmjs.org/ethers/-/ethers-5.7.2.tgz", + "integrity": "sha512-wswUsmWo1aOK8rR7DIKiWSw9DbLWe6x98Jrn8wcTflTVvaXhAMaB5zGAXy0GYQEQp9iO1iSHWVyARQm11zUtyg==", + "dev": true, + "funding": [ + { + "type": "individual", + "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2" + }, + { + "type": "individual", + "url": "https://www.buymeacoffee.com/ricmoo" + } + ], + "peer": true, + "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" + } + }, + "node_modules/ethereum-bloom-filters": { + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/ethereum-bloom-filters/-/ethereum-bloom-filters-1.0.10.tgz", + "integrity": "sha512-rxJ5OFN3RwjQxDcFP2Z5+Q9ho4eIdEmSc2ht0fCu8Se9nbXjZ7/031uXoUYJ87KHCOdVeiUuwSnoS7hmYAGVHA==", + "dev": true, + "peer": true, + "dependencies": { + "js-sha3": "^0.8.0" + } + }, + "node_modules/ethereum-cryptography": { + "version": "0.1.3", + "resolved": "https://registry.npmjs.org/ethereum-cryptography/-/ethereum-cryptography-0.1.3.tgz", + "integrity": "sha512-w8/4x1SGGzc+tO97TASLja6SLd3fRIK2tLVcV2Gx4IB21hE19atll5Cq9o3d0ZmAYC/8aw0ipieTSiekAea4SQ==", + "dependencies": { + "@types/pbkdf2": "^3.0.0", + "@types/secp256k1": "^4.0.1", + "blakejs": "^1.1.0", + "browserify-aes": "^1.2.0", + "bs58check": "^2.1.2", + "create-hash": "^1.2.0", + "create-hmac": "^1.1.7", + "hash.js": "^1.1.7", + "keccak": "^3.0.0", + "pbkdf2": "^3.0.17", + "randombytes": "^2.1.0", + "safe-buffer": "^5.1.2", + "scrypt-js": "^3.0.0", + "secp256k1": "^4.0.1", + "setimmediate": "^1.0.5" + } + }, + "node_modules/ethereumjs-abi": { + "version": "0.6.8", + "resolved": "https://registry.npmjs.org/ethereumjs-abi/-/ethereumjs-abi-0.6.8.tgz", + "integrity": "sha512-Tx0r/iXI6r+lRsdvkFDlut0N08jWMnKRZ6Gkq+Nmw75lZe4e6o3EkSnkaBP5NF6+m5PTGAr9JP43N3LyeoglsA==", + "dependencies": { + "bn.js": "^4.11.8", + "ethereumjs-util": "^6.0.0" + } + }, + "node_modules/ethereumjs-abi/node_modules/@types/bn.js": { + "version": "4.11.6", + "resolved": "https://registry.npmjs.org/@types/bn.js/-/bn.js-4.11.6.tgz", + "integrity": "sha512-pqr857jrp2kPuO9uRjZ3PwnJTjoQy+fcdxvBTvHm6dkmEL9q+hDD/2j/0ELOBPtPnS8LjCX0gI9nbl8lVkadpg==", + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/ethereumjs-abi/node_modules/bn.js": { + "version": "4.12.0", + "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.12.0.tgz", + "integrity": "sha512-c98Bf3tPniI+scsdk237ku1Dc3ujXQTSgyiPUDEOe7tRkhrqridvh8klBv0HCEso1OLOYcHuCv/cS6DNxKH+ZA==" + }, + "node_modules/ethereumjs-abi/node_modules/ethereumjs-util": { + "version": "6.2.1", + "resolved": "https://registry.npmjs.org/ethereumjs-util/-/ethereumjs-util-6.2.1.tgz", + "integrity": "sha512-W2Ktez4L01Vexijrm5EB6w7dg4n/TgpoYU4avuT5T3Vmnw/eCRtiBrJfQYS/DCSvDIOLn2k57GcHdeBcgVxAqw==", + "dependencies": { + "@types/bn.js": "^4.11.3", + "bn.js": "^4.11.0", + "create-hash": "^1.1.2", + "elliptic": "^6.5.2", + "ethereum-cryptography": "^0.1.3", + "ethjs-util": "0.1.6", + "rlp": "^2.2.3" + } + }, + "node_modules/ethereumjs-util": { + "version": "7.1.5", + "resolved": "https://registry.npmjs.org/ethereumjs-util/-/ethereumjs-util-7.1.5.tgz", + "integrity": "sha512-SDl5kKrQAudFBUe5OJM9Ac6WmMyYmXX/6sTmLZ3ffG2eY6ZIGBes3pEDxNN6V72WyOw4CPD5RomKdsa8DAAwLg==", + "dependencies": { + "@types/bn.js": "^5.1.0", + "bn.js": "^5.1.2", + "create-hash": "^1.1.2", + "ethereum-cryptography": "^0.1.3", + "rlp": "^2.2.4" + }, + "engines": { + "node": ">=10.0.0" + } + }, + "node_modules/ethers": { + "version": "6.9.2", + "resolved": "https://registry.npmjs.org/ethers/-/ethers-6.9.2.tgz", + "integrity": "sha512-YpkrtILnMQz5jSEsJQRTpduaGT/CXuLnUIuOYzHA0v/7c8IX91m2J48wSKjzGL5L9J/Us3tLoUdb+OwE3U+FFQ==", + "dev": true, + "funding": [ + { + "type": "individual", + "url": "https://github.com/sponsors/ethers-io/" + }, + { + "type": "individual", + "url": "https://www.buymeacoffee.com/ricmoo" + } + ], + "dependencies": { + "@adraffy/ens-normalize": "1.10.0", + "@noble/curves": "1.2.0", + "@noble/hashes": "1.3.2", + "@types/node": "18.15.13", + "aes-js": "4.0.0-beta.5", + "tslib": "2.4.0", + "ws": "8.5.0" + }, + "engines": { + "node": ">=14.0.0" + } + }, + "node_modules/ethers/node_modules/@types/node": { + "version": "18.15.13", + "resolved": "https://registry.npmjs.org/@types/node/-/node-18.15.13.tgz", + "integrity": "sha512-N+0kuo9KgrUQ1Sn/ifDXsvg0TTleP7rIy4zOBGECxAljqvqfqpTfzx0Q1NUedOixRMBfe2Whhb056a42cWs26Q==", + "dev": true + }, + "node_modules/ethjs-unit": { + "version": "0.1.6", + "resolved": "https://registry.npmjs.org/ethjs-unit/-/ethjs-unit-0.1.6.tgz", + "integrity": "sha512-/Sn9Y0oKl0uqQuvgFk/zQgR7aw1g36qX/jzSQ5lSwlO0GigPymk4eGQfeNTD03w1dPOqfz8V77Cy43jH56pagw==", + "dev": true, + "peer": true, + "dependencies": { + "bn.js": "4.11.6", + "number-to-bn": "1.7.0" + }, + "engines": { + "node": ">=6.5.0", + "npm": ">=3" + } + }, + "node_modules/ethjs-unit/node_modules/bn.js": { + "version": "4.11.6", + "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.11.6.tgz", + "integrity": "sha512-XWwnNNFCuuSQ0m3r3C4LE3EiORltHd9M05pq6FOlVeiophzRbMo50Sbz1ehl8K3Z+jw9+vmgnXefY1hz8X+2wA==", + "dev": true, + "peer": true + }, + "node_modules/ethjs-util": { + "version": "0.1.6", + "resolved": "https://registry.npmjs.org/ethjs-util/-/ethjs-util-0.1.6.tgz", + "integrity": "sha512-CUnVOQq7gSpDHZVVrQW8ExxUETWrnrvXYvYz55wOU8Uj4VCgw56XC2B/fVqQN+f7gmrnRHSLVnFAwsCuNwji8w==", + "dependencies": { + "is-hex-prefixed": "1.0.0", + "strip-hex-prefix": "1.0.0" + }, + "engines": { + "node": ">=6.5.0", + "npm": ">=3" + } + }, + "node_modules/evp_bytestokey": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/evp_bytestokey/-/evp_bytestokey-1.0.3.tgz", + "integrity": "sha512-/f2Go4TognH/KvCISP7OUsHn85hT9nUkxxA9BEWxFn+Oj9o8ZNLm/40hdlgSLyuOimsrTKLUMEorQexp/aPQeA==", + "dependencies": { + "md5.js": "^1.3.4", + "safe-buffer": "^5.1.1" + } + }, + "node_modules/fast-base64-decode": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fast-base64-decode/-/fast-base64-decode-1.0.0.tgz", + "integrity": "sha512-qwaScUgUGBYeDNRnbc/KyllVU88Jk1pRHPStuF/lO7B0/RTRLj7U0lkdTAutlBblY08rwZDff6tNU9cjv6j//Q==", + "dev": true + }, + "node_modules/fast-deep-equal": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", + "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", + "dev": true, + "peer": true + }, + "node_modules/fast-glob": { + "version": "3.3.2", + "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.2.tgz", + "integrity": "sha512-oX2ruAFQwf/Orj8m737Y5adxDQO0LAB7/S5MnxCdTNDd4p6BsyIVsv9JQsATbTSq8KHRpLwIHbVlUNatxd+1Ow==", + "dev": true, + "peer": true, + "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" + }, + "engines": { + "node": ">=8.6.0" + } + }, + "node_modules/fast-levenshtein": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", + "integrity": "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==", + "dev": true, + "peer": true + }, + "node_modules/fastq": { + "version": "1.16.0", + "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.16.0.tgz", + "integrity": "sha512-ifCoaXsDrsdkWTtiNJX5uzHDsrck5TzfKKDcuFFTIrrc/BS076qgEIfoIy1VeZqViznfKiysPYTh/QeHtnIsYA==", + "dev": true, + "peer": true, + "dependencies": { + "reusify": "^1.0.4" + } + }, + "node_modules/fill-range": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", + "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==", + "dependencies": { + "to-regex-range": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/find-replace": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/find-replace/-/find-replace-3.0.0.tgz", + "integrity": "sha512-6Tb2myMioCAgv5kfvP5/PkZZ/ntTpVK39fHY7WkWBgvbeE+VHd/tZuZ4mrC+bxh4cfOZeYKVPaJIZtZXV7GNCQ==", + "dev": true, + "peer": true, + "dependencies": { + "array-back": "^3.0.1" + }, + "engines": { + "node": ">=4.0.0" + } + }, + "node_modules/find-up": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-2.1.0.tgz", + "integrity": "sha512-NWzkk0jSJtTt08+FBFMvXoeZnOJD+jTtsRmBYbAIzJdX6l7dLgR7CTubCM5/eDdPUBvLCeVasP1brfVR/9/EZQ==", + "dependencies": { + "locate-path": "^2.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/flat": { + "version": "5.0.2", + "resolved": "https://registry.npmjs.org/flat/-/flat-5.0.2.tgz", + "integrity": "sha512-b6suED+5/3rTpUBdG1gupIl8MPFCAMA0QXwmljLhvCUKcUvdE4gWky9zpuGCcXHOsz4J9wPGNWq6OKpmIzz3hQ==", + "bin": { + "flat": "cli.js" + } + }, + "node_modules/follow-redirects": { + "version": "1.15.4", + "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.4.tgz", + "integrity": "sha512-Cr4D/5wlrb0z9dgERpUL3LrmPKVDsETIJhaCMeDfuFYcqa5bldGV6wBsAN6X/vxlXQtFBMrXdXxdL8CbDTGniw==", + "funding": [ + { + "type": "individual", + "url": "https://github.com/sponsors/RubenVerborgh" + } + ], + "engines": { + "node": ">=4.0" + }, + "peerDependenciesMeta": { + "debug": { + "optional": true + } + } + }, + "node_modules/for-each": { + "version": "0.3.3", + "resolved": "https://registry.npmjs.org/for-each/-/for-each-0.3.3.tgz", + "integrity": "sha512-jqYfLp7mo9vIyQf8ykW2v7A+2N4QjeCeI5+Dz9XraiO1ign81wjiH7Fb9vSOWvQfNtmSa4H2RoQTrrXivdUZmw==", + "dev": true, + "dependencies": { + "is-callable": "^1.1.3" + } + }, + "node_modules/form-data": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.0.tgz", + "integrity": "sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww==", + "dev": true, + "dependencies": { + "asynckit": "^0.4.0", + "combined-stream": "^1.0.8", + "mime-types": "^2.1.12" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/fp-ts": { + "version": "1.19.3", + "resolved": "https://registry.npmjs.org/fp-ts/-/fp-ts-1.19.3.tgz", + "integrity": "sha512-H5KQDspykdHuztLTg+ajGN0Z2qUjcEf3Ybxc6hLt0k7/zPkn29XnKnxlBPyW2XIddWrGaJBzBl4VLYOtk39yZg==" + }, + "node_modules/fs-extra": { + "version": "9.1.0", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-9.1.0.tgz", + "integrity": "sha512-hcg3ZmepS30/7BSFqRvoo3DOMQu7IjqxO5nCDt+zM9XWjb33Wg7ziNT+Qvqbuc3+gWpzO02JubVyk2G4Zvo1OQ==", + "dev": true, + "peer": true, + "dependencies": { + "at-least-node": "^1.0.0", + "graceful-fs": "^4.2.0", + "jsonfile": "^6.0.1", + "universalify": "^2.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/fs-readdir-recursive": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/fs-readdir-recursive/-/fs-readdir-recursive-1.1.0.tgz", + "integrity": "sha512-GNanXlVr2pf02+sPN40XN8HG+ePaNcvM0q5mZBd668Obwb0yD5GiUbZOFgwn8kGMY6I3mdyDJzieUy3PTYyTRA==", + "dev": true, + "peer": true + }, + "node_modules/fs.realpath": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", + "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==" + }, + "node_modules/fsevents": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", + "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", + "hasInstallScript": true, + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + } + }, + "node_modules/function-bind": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", + "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==", + "dev": true, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/function.prototype.name": { + "version": "1.1.6", + "resolved": "https://registry.npmjs.org/function.prototype.name/-/function.prototype.name-1.1.6.tgz", + "integrity": "sha512-Z5kx79swU5P27WEayXM1tBi5Ze/lbIyiNgU3qyXUOf9b2rgXYyF9Dy9Cx+IQv/Lc8WCG6L82zwUPpSS9hGehIg==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.2", + "define-properties": "^1.2.0", + "es-abstract": "^1.22.1", + "functions-have-names": "^1.2.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/functional-red-black-tree": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/functional-red-black-tree/-/functional-red-black-tree-1.0.1.tgz", + "integrity": "sha512-dsKNQNdj6xA3T+QlADDA7mOSlX0qiMINjn0cgr+eGHGsbSHzTabcIogz2+p/iqP1Xs6EP/sS2SbqH+brGTbq0g==" + }, + "node_modules/functions-have-names": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/functions-have-names/-/functions-have-names-1.2.3.tgz", + "integrity": "sha512-xckBUXyTIqT97tq2x2AMb+g163b5JFysYk0x4qxNFwbfQkmNZoiRHb6sPzI9/QV33WeuvVYBUIiD4NzNIyqaRQ==", + "dev": true, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/get-caller-file": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", + "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", + "engines": { + "node": "6.* || 8.* || >= 10.*" + } + }, + "node_modules/get-func-name": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/get-func-name/-/get-func-name-2.0.2.tgz", + "integrity": "sha512-8vXOvuE167CtIc3OyItco7N/dpRtBbYOsPsXCz7X/PMnlGjYjSGuZJgM1Y7mmew7BKf9BqvLX2tnOVy1BBUsxQ==", + "dev": true, + "peer": true, + "engines": { + "node": "*" + } + }, + "node_modules/get-intrinsic": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.2.2.tgz", + "integrity": "sha512-0gSo4ml/0j98Y3lngkFEot/zhiCeWsbYIlZ+uZOVgzLyLaUw7wxUL+nCTP0XJvJg1AXulJRI3UJi8GsbDuxdGA==", + "dev": true, + "dependencies": { + "function-bind": "^1.1.2", + "has-proto": "^1.0.1", + "has-symbols": "^1.0.3", + "hasown": "^2.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/get-port": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/get-port/-/get-port-3.2.0.tgz", + "integrity": "sha512-x5UJKlgeUiNT8nyo/AcnwLnZuZNcSjSw0kogRB+Whd1fjjFq4B1hySFxSFWWSn4mIBzg3sRNUDFYc4g5gjPoLg==", + "dev": true, + "peer": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/get-symbol-description": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/get-symbol-description/-/get-symbol-description-1.0.0.tgz", + "integrity": "sha512-2EmdH1YvIQiZpltCNgkuiUnyukzxM/R6NDJX31Ke3BG1Nq5b0S2PhX59UKi9vZpPDQVdqn+1IcaAwnzTT5vCjw==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.2", + "get-intrinsic": "^1.1.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/ghost-testrpc": { + "version": "0.0.2", + "resolved": "https://registry.npmjs.org/ghost-testrpc/-/ghost-testrpc-0.0.2.tgz", + "integrity": "sha512-i08dAEgJ2g8z5buJIrCTduwPIhih3DP+hOCTyyryikfV8T0bNvHnGXO67i0DD1H4GBDETTclPy9njZbfluQYrQ==", + "dev": true, + "peer": true, + "dependencies": { + "chalk": "^2.4.2", + "node-emoji": "^1.10.0" + }, + "bin": { + "testrpc-sc": "index.js" + } + }, + "node_modules/glob": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.0.tgz", + "integrity": "sha512-lmLf6gtyrPq8tTjSmrO94wBeQbFR3HbLHbuyD69wuyQkImp2hWqMGB47OX65FBkPffO641IP9jWa1z4ivqG26Q==", + "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" + }, + "engines": { + "node": "*" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/glob-parent": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", + "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", + "dependencies": { + "is-glob": "^4.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/global-modules": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/global-modules/-/global-modules-2.0.0.tgz", + "integrity": "sha512-NGbfmJBp9x8IxyJSd1P+otYK8vonoJactOogrVfFRIAEY1ukil8RSKDz2Yo7wh1oihl51l/r6W4epkeKJHqL8A==", + "dev": true, + "peer": true, + "dependencies": { + "global-prefix": "^3.0.0" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/global-prefix": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/global-prefix/-/global-prefix-3.0.0.tgz", + "integrity": "sha512-awConJSVCHVGND6x3tmMaKcQvwXLhjdkmomy2W+Goaui8YPgYgXJZewhg3fWC+DlfqqQuWg8AwqjGTD2nAPVWg==", + "dev": true, + "peer": true, + "dependencies": { + "ini": "^1.3.5", + "kind-of": "^6.0.2", + "which": "^1.3.1" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/globalthis": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/globalthis/-/globalthis-1.0.3.tgz", + "integrity": "sha512-sFdI5LyBiNTHjRd7cGPWapiHWMOXKyuBNX/cWJ3NfzrZQVa8GI/8cofCl74AOVqq9W5kNmguTIzJ/1s2gyI9wA==", + "dev": true, + "dependencies": { + "define-properties": "^1.1.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/globby": { + "version": "10.0.2", + "resolved": "https://registry.npmjs.org/globby/-/globby-10.0.2.tgz", + "integrity": "sha512-7dUi7RvCoT/xast/o/dLN53oqND4yk0nsHkhRgn9w65C4PofCLOoJ39iSOg+qVDdWQPIEj+eszMHQ+aLVwwQSg==", + "dev": true, + "peer": true, + "dependencies": { + "@types/glob": "^7.1.1", + "array-union": "^2.1.0", + "dir-glob": "^3.0.1", + "fast-glob": "^3.0.3", + "glob": "^7.1.3", + "ignore": "^5.1.1", + "merge2": "^1.2.3", + "slash": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/gopd": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.0.1.tgz", + "integrity": "sha512-d65bNlIadxvpb/A2abVdlqKqV563juRnZ1Wtk6s1sIR8uNsXR70xqIzVqxVf1eTqDunwT2MkczEeaezCKTZhwA==", + "dev": true, + "dependencies": { + "get-intrinsic": "^1.1.3" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/graceful-fs": { + "version": "4.2.11", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz", + "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==" + }, + "node_modules/handlebars": { + "version": "4.7.8", + "resolved": "https://registry.npmjs.org/handlebars/-/handlebars-4.7.8.tgz", + "integrity": "sha512-vafaFqs8MZkRrSX7sFVUdo3ap/eNiLnb4IakshzvP56X5Nr1iGKAIqdX6tMlm6HcNRIkr6AxO5jFEoJzzpT8aQ==", + "dev": true, + "peer": true, + "dependencies": { + "minimist": "^1.2.5", + "neo-async": "^2.6.2", + "source-map": "^0.6.1", + "wordwrap": "^1.0.0" + }, + "bin": { + "handlebars": "bin/handlebars" + }, + "engines": { + "node": ">=0.4.7" + }, + "optionalDependencies": { + "uglify-js": "^3.1.4" + } + }, + "node_modules/handlebars/node_modules/source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true, + "peer": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/hardhat": { + "version": "2.19.4", + "resolved": "https://registry.npmjs.org/hardhat/-/hardhat-2.19.4.tgz", + "integrity": "sha512-fTQJpqSt3Xo9Mn/WrdblNGAfcANM6XC3tAEi6YogB4s02DmTf93A8QsGb8uR0KR8TFcpcS8lgiW4ugAIYpnbrQ==", + "dependencies": { + "@ethersproject/abi": "^5.1.2", + "@metamask/eth-sig-util": "^4.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", + "adm-zip": "^0.4.16", + "aggregate-error": "^3.0.0", + "ansi-escapes": "^4.3.0", + "chalk": "^2.4.2", + "chokidar": "^3.4.0", + "ci-info": "^2.0.0", + "debug": "^4.1.1", + "enquirer": "^2.3.0", + "env-paths": "^2.2.0", + "ethereum-cryptography": "^1.0.3", + "ethereumjs-abi": "^0.6.8", + "find-up": "^2.1.0", + "fp-ts": "1.19.3", + "fs-extra": "^7.0.1", + "glob": "7.2.0", + "immutable": "^4.0.0-rc.12", + "io-ts": "1.10.4", + "keccak": "^3.0.2", + "lodash": "^4.17.11", + "mnemonist": "^0.38.0", + "mocha": "^10.0.0", + "p-map": "^4.0.0", + "raw-body": "^2.4.1", + "resolve": "1.17.0", + "semver": "^6.3.0", + "solc": "0.7.3", + "source-map-support": "^0.5.13", + "stacktrace-parser": "^0.1.10", + "tsort": "0.0.1", + "undici": "^5.14.0", + "uuid": "^8.3.2", + "ws": "^7.4.6" + }, + "bin": { + "hardhat": "internal/cli/bootstrap.js" + }, + "peerDependencies": { + "ts-node": "*", + "typescript": "*" + }, + "peerDependenciesMeta": { + "ts-node": { + "optional": true + }, + "typescript": { + "optional": true + } + } + }, + "node_modules/hardhat-contract-sizer": { + "version": "2.10.0", + "resolved": "https://registry.npmjs.org/hardhat-contract-sizer/-/hardhat-contract-sizer-2.10.0.tgz", + "integrity": "sha512-QiinUgBD5MqJZJh1hl1jc9dNnpJg7eE/w4/4GEnrcmZJJTDbVFNe3+/3Ep24XqISSkYxRz36czcPHKHd/a0dwA==", + "dev": true, + "dependencies": { + "chalk": "^4.0.0", + "cli-table3": "^0.6.0", + "strip-ansi": "^6.0.0" + }, + "peerDependencies": { + "hardhat": "^2.0.0" + } + }, + "node_modules/hardhat-contract-sizer/node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/hardhat-contract-sizer/node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/hardhat-contract-sizer/node_modules/cli-table3": { + "version": "0.6.5", + "resolved": "https://registry.npmjs.org/cli-table3/-/cli-table3-0.6.5.tgz", + "integrity": "sha512-+W/5efTR7y5HRD7gACw9yQjqMVvEMLBHmboM/kPWam+H+Hmyrgjh6YncVKK122YZkXrLudzTuAukUw9FnMf7IQ==", + "dev": true, + "dependencies": { + "string-width": "^4.2.0" + }, + "engines": { + "node": "10.* || >= 12.*" + }, + "optionalDependencies": { + "@colors/colors": "1.5.0" + } + }, + "node_modules/hardhat-contract-sizer/node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/hardhat-contract-sizer/node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true + }, + "node_modules/hardhat-contract-sizer/node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/hardhat-contract-sizer/node_modules/is-fullwidth-code-point": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/hardhat-contract-sizer/node_modules/string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dev": true, + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/hardhat-contract-sizer/node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/hardhat-gas-reporter": { + "version": "1.0.9", + "resolved": "https://registry.npmjs.org/hardhat-gas-reporter/-/hardhat-gas-reporter-1.0.9.tgz", + "integrity": "sha512-INN26G3EW43adGKBNzYWOlI3+rlLnasXTwW79YNnUhXPDa+yHESgt639dJEs37gCjhkbNKcRRJnomXEuMFBXJg==", + "dev": true, + "peer": true, + "dependencies": { + "array-uniq": "1.0.3", + "eth-gas-reporter": "^0.2.25", + "sha1": "^1.1.1" + }, + "peerDependencies": { + "hardhat": "^2.0.2" + } + }, + "node_modules/hardhat/node_modules/@noble/hashes": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@noble/hashes/-/hashes-1.2.0.tgz", + "integrity": "sha512-FZfhjEDbT5GRswV3C6uvLPHMiVD6lQBmpoX5+eSiPaMTXte/IKqI5dykDxzZB/WBeK/CDuQRBWarPdi3FNY2zQ==", + "funding": [ + { + "type": "individual", + "url": "https://paulmillr.com/funding/" + } + ] + }, + "node_modules/hardhat/node_modules/@scure/bip32": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/@scure/bip32/-/bip32-1.1.5.tgz", + "integrity": "sha512-XyNh1rB0SkEqd3tXcXMi+Xe1fvg+kUIcoRIEujP1Jgv7DqW2r9lg3Ah0NkFaCs9sTkQAQA8kw7xiRXzENi9Rtw==", + "funding": [ + { + "type": "individual", + "url": "https://paulmillr.com/funding/" + } + ], + "dependencies": { + "@noble/hashes": "~1.2.0", + "@noble/secp256k1": "~1.7.0", + "@scure/base": "~1.1.0" + } + }, + "node_modules/hardhat/node_modules/@scure/bip39": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@scure/bip39/-/bip39-1.1.1.tgz", + "integrity": "sha512-t+wDck2rVkh65Hmv280fYdVdY25J9YeEUIgn2LG1WM6gxFkGzcksoDiUkWVpVp3Oex9xGC68JU2dSbUfwZ2jPg==", + "funding": [ + { + "type": "individual", + "url": "https://paulmillr.com/funding/" + } + ], + "dependencies": { + "@noble/hashes": "~1.2.0", + "@scure/base": "~1.1.0" + } + }, + "node_modules/hardhat/node_modules/ethereum-cryptography": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/ethereum-cryptography/-/ethereum-cryptography-1.2.0.tgz", + "integrity": "sha512-6yFQC9b5ug6/17CQpCyE3k9eKBMdhyVjzUy1WkiuY/E4vj/SXDBbCw8QEIaXqf0Mf2SnY6RmpDcwlUmBSS0EJw==", + "dependencies": { + "@noble/hashes": "1.2.0", + "@noble/secp256k1": "1.7.1", + "@scure/bip32": "1.1.5", + "@scure/bip39": "1.1.1" + } + }, + "node_modules/hardhat/node_modules/fs-extra": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-7.0.1.tgz", + "integrity": "sha512-YJDaCJZEnBmcbw13fvdAM9AwNOJwOzrE4pqMqBq5nFiEqXUqHwlK4B+3pUw6JNvfSPtX05xFHtYy/1ni01eGCw==", + "dependencies": { + "graceful-fs": "^4.1.2", + "jsonfile": "^4.0.0", + "universalify": "^0.1.0" + }, + "engines": { + "node": ">=6 <7 || >=8" + } + }, + "node_modules/hardhat/node_modules/jsonfile": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-4.0.0.tgz", + "integrity": "sha512-m6F1R3z8jjlf2imQHS2Qez5sjKWQzbuuhuJ/FKYFRZvPE3PuHcSMVZzfsLhGVOkfd20obL5SWEBew5ShlquNxg==", + "optionalDependencies": { + "graceful-fs": "^4.1.6" + } + }, + "node_modules/hardhat/node_modules/universalify": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/universalify/-/universalify-0.1.2.tgz", + "integrity": "sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg==", + "engines": { + "node": ">= 4.0.0" + } + }, + "node_modules/hardhat/node_modules/ws": { + "version": "7.5.9", + "resolved": "https://registry.npmjs.org/ws/-/ws-7.5.9.tgz", + "integrity": "sha512-F+P9Jil7UiSKSkppIiD94dN07AwvFixvLIj1Og1Rl9GGMuNipJnV9JzjD6XuqmAeiswGvUmNLjr5cFuXwNS77Q==", + "engines": { + "node": ">=8.3.0" + }, + "peerDependencies": { + "bufferutil": "^4.0.1", + "utf-8-validate": "^5.0.2" + }, + "peerDependenciesMeta": { + "bufferutil": { + "optional": true + }, + "utf-8-validate": { + "optional": true + } + } + }, + "node_modules/has-bigints": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/has-bigints/-/has-bigints-1.0.2.tgz", + "integrity": "sha512-tSvCKtBr9lkF0Ex0aQiP9N+OpV4zi2r/Nee5VkRDbaqv35RLYMzbwQfFSZZH0kR+Rd6302UJZ2p/bJCEoR3VoQ==", + "dev": true, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-flag": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", + "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==", + "engines": { + "node": ">=4" + } + }, + "node_modules/has-property-descriptors": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/has-property-descriptors/-/has-property-descriptors-1.0.1.tgz", + "integrity": "sha512-VsX8eaIewvas0xnvinAe9bw4WfIeODpGYikiWYLH+dma0Jw6KHYqWiWfhQlgOVK8D6PvjubK5Uc4P0iIhIcNVg==", + "dev": true, + "dependencies": { + "get-intrinsic": "^1.2.2" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-proto": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/has-proto/-/has-proto-1.0.1.tgz", + "integrity": "sha512-7qE+iP+O+bgF9clE5+UoBFzE65mlBiVj3tKCrlNQ0Ogwm0BjpT/gK4SlLYDMybDh5I3TCTKnPPa0oMG7JDYrhg==", + "dev": true, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-symbols": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.3.tgz", + "integrity": "sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A==", + "dev": true, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-tostringtag": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.0.tgz", + "integrity": "sha512-kFjcSNhnlGV1kyoGk7OXKSawH5JOb/LzUc5w9B02hOTO0dfFRjbHQKvg1d6cf3HbeUmtU9VbbV3qzZ2Teh97WQ==", + "dev": true, + "dependencies": { + "has-symbols": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/hash-base": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/hash-base/-/hash-base-3.1.0.tgz", + "integrity": "sha512-1nmYp/rhMDiE7AYkDw+lLwlAzz0AntGIe51F3RfFfEqyQ3feY2eI/NcwC6umIQVOASPMsWJLJScWKSSvzL9IVA==", + "dependencies": { + "inherits": "^2.0.4", + "readable-stream": "^3.6.0", + "safe-buffer": "^5.2.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/hash.js": { + "version": "1.1.7", + "resolved": "https://registry.npmjs.org/hash.js/-/hash.js-1.1.7.tgz", + "integrity": "sha512-taOaskGt4z4SOANNseOviYDvjEJinIkRgmp7LbKP2YTTmVxWBl87s/uzK9r+44BclBSp2X7K1hqeNfz9JbBeXA==", + "dependencies": { + "inherits": "^2.0.3", + "minimalistic-assert": "^1.0.1" + } + }, + "node_modules/hasown": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.0.tgz", + "integrity": "sha512-vUptKVTpIJhcczKBbgnS+RtcuYMB8+oNzPK2/Hp3hanz8JmpATdmmgLgSaadVREkDm+e2giHwY3ZRkyjSIDDFA==", + "dev": true, + "dependencies": { + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/he": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/he/-/he-1.2.0.tgz", + "integrity": "sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==", + "bin": { + "he": "bin/he" + } + }, + "node_modules/heap": { + "version": "0.2.7", + "resolved": "https://registry.npmjs.org/heap/-/heap-0.2.7.tgz", + "integrity": "sha512-2bsegYkkHO+h/9MGbn6KWcE45cHZgPANo5LXF7EvWdT0yT2EguSVO1nDgU5c8+ZOPwp2vMNa7YFsJhVcDR9Sdg==", + "dev": true, + "peer": true + }, + "node_modules/hmac-drbg": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/hmac-drbg/-/hmac-drbg-1.0.1.tgz", + "integrity": "sha512-Tti3gMqLdZfhOQY1Mzf/AanLiqh1WTiJgEj26ZuYQ9fbkLomzGchCws4FyrSd4VkpBfiNhaE1On+lOz894jvXg==", + "dependencies": { + "hash.js": "^1.0.3", + "minimalistic-assert": "^1.0.0", + "minimalistic-crypto-utils": "^1.0.1" + } + }, + "node_modules/http-basic": { + "version": "8.1.3", + "resolved": "https://registry.npmjs.org/http-basic/-/http-basic-8.1.3.tgz", + "integrity": "sha512-/EcDMwJZh3mABI2NhGfHOGOeOZITqfkEO4p/xK+l3NpyncIHUQBoMvCSF/b5GqvKtySC2srL/GGG3+EtlqlmCw==", + "dev": true, + "peer": true, + "dependencies": { + "caseless": "^0.12.0", + "concat-stream": "^1.6.2", + "http-response-object": "^3.0.1", + "parse-cache-control": "^1.0.1" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/http-errors": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.0.tgz", + "integrity": "sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ==", + "dependencies": { + "depd": "2.0.0", + "inherits": "2.0.4", + "setprototypeof": "1.2.0", + "statuses": "2.0.1", + "toidentifier": "1.0.1" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/http-response-object": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/http-response-object/-/http-response-object-3.0.2.tgz", + "integrity": "sha512-bqX0XTF6fnXSQcEJ2Iuyr75yVakyjIDCqroJQ/aHfSdlM743Cwqoi2nDYMzLGWUcuTWGWy8AAvOKXTfiv6q9RA==", + "dev": true, + "peer": true, + "dependencies": { + "@types/node": "^10.0.3" + } + }, + "node_modules/http-response-object/node_modules/@types/node": { + "version": "10.17.60", + "resolved": "https://registry.npmjs.org/@types/node/-/node-10.17.60.tgz", + "integrity": "sha512-F0KIgDJfy2nA3zMLmWGKxcH2ZVEtCZXHHdOQs2gSaQ27+lNeEfGxzkIw90aXswATX7AZ33tahPbzy6KAfUreVw==", + "dev": true, + "peer": true + }, + "node_modules/https-proxy-agent": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-5.0.1.tgz", + "integrity": "sha512-dFcAjpTQFgoLMzC2VwU+C/CbS7uRL0lWmxDITmqm7C+7F0Odmj6s9l6alZc6AELXhrnggM2CeWSXHGOdX2YtwA==", + "dependencies": { + "agent-base": "6", + "debug": "4" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/iconv-lite": { + "version": "0.4.24", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", + "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", + "dependencies": { + "safer-buffer": ">= 2.1.2 < 3" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/ieee754": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz", + "integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ] + }, + "node_modules/ignore": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.0.tgz", + "integrity": "sha512-g7dmpshy+gD7mh88OC9NwSGTKoc3kyLAZQRU1mt53Aw/vnvfXnbC+F/7F7QoYVKbV+KNvJx8wArewKy1vXMtlg==", + "dev": true, + "peer": true, + "engines": { + "node": ">= 4" + } + }, + "node_modules/immutable": { + "version": "4.3.4", + "resolved": "https://registry.npmjs.org/immutable/-/immutable-4.3.4.tgz", + "integrity": "sha512-fsXeu4J4i6WNWSikpI88v/PcVflZz+6kMhUfIwc5SY+poQRPnaf5V7qds6SUyUN3cVxEzuCab7QIoLOQ+DQ1wA==" + }, + "node_modules/indent-string": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/indent-string/-/indent-string-4.0.0.tgz", + "integrity": "sha512-EdDDZu4A2OyIK7Lr/2zG+w5jmbuk1DVBnEwREQvBzspBJkCEbRa8GxU1lghYcaGJCnRWibjDXlq779X1/y5xwg==", + "engines": { + "node": ">=8" + } + }, + "node_modules/inflight": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", + "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==", + "dependencies": { + "once": "^1.3.0", + "wrappy": "1" + } + }, + "node_modules/inherits": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==" + }, + "node_modules/ini": { + "version": "1.3.8", + "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.8.tgz", + "integrity": "sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==", + "dev": true, + "peer": true + }, + "node_modules/internal-slot": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/internal-slot/-/internal-slot-1.0.6.tgz", + "integrity": "sha512-Xj6dv+PsbtwyPpEflsejS+oIZxmMlV44zAhG479uYu89MsjcYOhCFnNyKrkJrihbsiasQyY0afoCl/9BLR65bg==", + "dev": true, + "dependencies": { + "get-intrinsic": "^1.2.2", + "hasown": "^2.0.0", + "side-channel": "^1.0.4" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/interpret": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/interpret/-/interpret-1.4.0.tgz", + "integrity": "sha512-agE4QfB2Lkp9uICn7BAqoscw4SZP9kTE2hxiFI3jBPmXJfdqiahTbUuKGsMoN2GtqL9AxhYioAcVvgsb1HvRbA==", + "dev": true, + "peer": true, + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/io-ts": { + "version": "1.10.4", + "resolved": "https://registry.npmjs.org/io-ts/-/io-ts-1.10.4.tgz", + "integrity": "sha512-b23PteSnYXSONJ6JQXRAlvJhuw8KOtkqa87W4wDtvMrud/DTJd5X+NpOOI+O/zZwVq6v0VLAaJ+1EDViKEuN9g==", + "dependencies": { + "fp-ts": "^1.0.0" + } + }, + "node_modules/is-array-buffer": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/is-array-buffer/-/is-array-buffer-3.0.2.tgz", + "integrity": "sha512-y+FyyR/w8vfIRq4eQcM1EYgSTnmHXPqaF+IgzgraytCFq5Xh8lllDVmAZolPJiZttZLeFSINPYMaEJ7/vWUa1w==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.2", + "get-intrinsic": "^1.2.0", + "is-typed-array": "^1.1.10" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-bigint": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/is-bigint/-/is-bigint-1.0.4.tgz", + "integrity": "sha512-zB9CruMamjym81i2JZ3UMn54PKGsQzsJeo6xvN3HJJ4CAsQNB6iRutp2To77OfCNuoxspsIhzaPoO1zyCEhFOg==", + "dev": true, + "dependencies": { + "has-bigints": "^1.0.1" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-binary-path": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", + "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==", + "dependencies": { + "binary-extensions": "^2.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/is-boolean-object": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/is-boolean-object/-/is-boolean-object-1.1.2.tgz", + "integrity": "sha512-gDYaKHJmnj4aWxyj6YHyXVpdQawtVLHU5cb+eztPGczf6cjuTdwve5ZIEfgXqH4e57An1D1AKf8CZ3kYrQRqYA==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.2", + "has-tostringtag": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-buffer": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-2.0.5.tgz", + "integrity": "sha512-i2R6zNFDwgEHJyQUtJEk0XFi1i0dPFn/oqjK3/vPCcDeJvW5NQ83V8QbicfF1SupOaB0h8ntgBC2YiE7dfyctQ==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "engines": { + "node": ">=4" + } + }, + "node_modules/is-callable": { + "version": "1.2.7", + "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.2.7.tgz", + "integrity": "sha512-1BC0BVFhS/p0qtw6enp8e+8OD0UrK0oFLztSjNzhcKA3WDuJxxAPXzPuPtKkjEY9UUoEWlX/8fgKeu2S8i9JTA==", + "dev": true, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-date-object": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/is-date-object/-/is-date-object-1.0.5.tgz", + "integrity": "sha512-9YQaSxsAiSwcvS33MBk3wTCVnWK+HhF8VZR2jRxehM16QcVOdHqPn4VPHmRK4lSr38n9JriurInLcP90xsYNfQ==", + "dev": true, + "dependencies": { + "has-tostringtag": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-extglob": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", + "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-fullwidth-code-point": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz", + "integrity": "sha512-VHskAKYM8RfSFXwee5t5cbN5PZeq1Wrh6qd5bkyiXIf6UQcN6w/A0eXM9r6t8d+GYOh+o6ZhiEnb88LN/Y8m2w==", + "dev": true, + "peer": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/is-glob": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", + "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", + "dependencies": { + "is-extglob": "^2.1.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-hex-prefixed": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-hex-prefixed/-/is-hex-prefixed-1.0.0.tgz", + "integrity": "sha512-WvtOiug1VFrE9v1Cydwm+FnXd3+w9GaeVUss5W4v/SLy3UW00vP+6iNF2SdnfiBoLy4bTqVdkftNGTUeOFVsbA==", + "engines": { + "node": ">=6.5.0", + "npm": ">=3" + } + }, + "node_modules/is-negative-zero": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/is-negative-zero/-/is-negative-zero-2.0.2.tgz", + "integrity": "sha512-dqJvarLawXsFbNDeJW7zAz8ItJ9cd28YufuuFzh0G8pNHjJMnY08Dv7sYX2uF5UpQOwieAeOExEYAWWfu7ZZUA==", + "dev": true, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-number": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", + "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", + "engines": { + "node": ">=0.12.0" + } + }, + "node_modules/is-number-object": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/is-number-object/-/is-number-object-1.0.7.tgz", + "integrity": "sha512-k1U0IRzLMo7ZlYIfzRu23Oh6MiIFasgpb9X76eqfFZAqwH44UI4KTBvBYIZ1dSL9ZzChTB9ShHfLkR4pdW5krQ==", + "dev": true, + "dependencies": { + "has-tostringtag": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-plain-obj": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-2.1.0.tgz", + "integrity": "sha512-YWnfyRwxL/+SsrWYfOpUtz5b3YD+nyfkHvjbcanzk8zgyO4ASD67uVMRt8k5bM4lLMDnXfriRhOpemw+NfT1eA==", + "engines": { + "node": ">=8" + } + }, + "node_modules/is-regex": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.1.4.tgz", + "integrity": "sha512-kvRdxDsxZjhzUX07ZnLydzS1TU/TJlTUHHY4YLL87e37oUA49DfkLqgy+VjFocowy29cKvcSiu+kIv728jTTVg==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.2", + "has-tostringtag": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-shared-array-buffer": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-shared-array-buffer/-/is-shared-array-buffer-1.0.2.tgz", + "integrity": "sha512-sqN2UDu1/0y6uvXyStCOzyhAjCSlHceFoMKJW8W9EU9cvic/QdsZ0kEU93HEy3IUEFZIiH/3w+AH/UQbPHNdhA==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.2" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-string": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/is-string/-/is-string-1.0.7.tgz", + "integrity": "sha512-tE2UXzivje6ofPW7l23cjDOMa09gb7xlAqG6jG5ej6uPV32TlWP3NKPigtaGeHNu9fohccRYvIiZMfOOnOYUtg==", + "dev": true, + "dependencies": { + "has-tostringtag": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-symbol": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/is-symbol/-/is-symbol-1.0.4.tgz", + "integrity": "sha512-C/CPBqKWnvdcxqIARxyOh4v1UUEOCHpgDa0WYgpKDFMszcrPcffg5uhwSgPCLD2WWxmq6isisz87tzT01tuGhg==", + "dev": true, + "dependencies": { + "has-symbols": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-typed-array": { + "version": "1.1.12", + "resolved": "https://registry.npmjs.org/is-typed-array/-/is-typed-array-1.1.12.tgz", + "integrity": "sha512-Z14TF2JNG8Lss5/HMqt0//T9JeHXttXy5pH/DBU4vi98ozO2btxzq9MwYDZYnKwU8nRsz/+GVFVRDq3DkVuSPg==", + "dev": true, + "dependencies": { + "which-typed-array": "^1.1.11" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/is-unicode-supported": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/is-unicode-supported/-/is-unicode-supported-0.1.0.tgz", + "integrity": "sha512-knxG2q4UC3u8stRGyAVJCOdxFmv5DZiRcdlIaAQXAbSfJya+OhopNotLQrstBhququ4ZpuKbDc/8S6mgXgPFPw==", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/is-weakref": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-weakref/-/is-weakref-1.0.2.tgz", + "integrity": "sha512-qctsuLZmIQ0+vSSMfoVvyFe2+GSEvnmZ2ezTup1SBse9+twCCeial6EEi3Nc2KFcf6+qz2FBPnjXsk8xhKSaPQ==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.2" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/isarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", + "integrity": "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==", + "dev": true + }, + "node_modules/isexe": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", + "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", + "dev": true, + "peer": true + }, + "node_modules/isomorphic-unfetch": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/isomorphic-unfetch/-/isomorphic-unfetch-3.1.0.tgz", + "integrity": "sha512-geDJjpoZ8N0kWexiwkX8F9NkTsXhetLPVbZFQ+JTW239QNOwvB0gniuR1Wc6f0AMTn7/mFGyXvHTifrCp/GH8Q==", + "dev": true, + "dependencies": { + "node-fetch": "^2.6.1", + "unfetch": "^4.2.0" + } + }, + "node_modules/js-cookie": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/js-cookie/-/js-cookie-2.2.1.tgz", + "integrity": "sha512-HvdH2LzI/EAZcUwA8+0nKNtWHqS+ZmijLA30RwZA0bo7ToCckjK5MkGhjED9KoRcXO6BaGI3I9UIzSA1FKFPOQ==", + "dev": true + }, + "node_modules/js-sdsl": { + "version": "4.4.2", + "resolved": "https://registry.npmjs.org/js-sdsl/-/js-sdsl-4.4.2.tgz", + "integrity": "sha512-dwXFwByc/ajSV6m5bcKAPwe4yDDF6D614pxmIi5odytzxRlwqF6nwoiCek80Ixc7Cvma5awClxrzFtxCQvcM8w==", + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/js-sdsl" + } + }, + "node_modules/js-sha3": { + "version": "0.8.0", + "resolved": "https://registry.npmjs.org/js-sha3/-/js-sha3-0.8.0.tgz", + "integrity": "sha512-gF1cRrHhIzNfToc802P800N8PpXS+evLLXfsVpowqmAFR9uwbi89WvXg2QspOmXL8QL86J4T1EpFu+yUkwJY3Q==" + }, + "node_modules/js-yaml": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", + "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", + "dependencies": { + "argparse": "^2.0.1" + }, + "bin": { + "js-yaml": "bin/js-yaml.js" + } + }, + "node_modules/json-schema-traverse": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", + "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==", + "dev": true, + "peer": true + }, + "node_modules/jsonfile": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.1.0.tgz", + "integrity": "sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ==", + "dev": true, + "peer": true, + "dependencies": { + "universalify": "^2.0.0" + }, + "optionalDependencies": { + "graceful-fs": "^4.1.6" + } + }, + "node_modules/jsonschema": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/jsonschema/-/jsonschema-1.4.1.tgz", + "integrity": "sha512-S6cATIPVv1z0IlxdN+zUk5EPjkGCdnhN4wVSBlvoUO1tOLJootbo9CquNJmbIh4yikWHiUedhRYrNPn1arpEmQ==", + "dev": true, + "peer": true, + "engines": { + "node": "*" + } + }, + "node_modules/keccak": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/keccak/-/keccak-3.0.4.tgz", + "integrity": "sha512-3vKuW0jV8J3XNTzvfyicFR5qvxrSAGl7KIhvgOu5cmWwM7tZRj3fMbj/pfIf4be7aznbc+prBWGjywox/g2Y6Q==", + "hasInstallScript": true, + "dependencies": { + "node-addon-api": "^2.0.0", + "node-gyp-build": "^4.2.0", + "readable-stream": "^3.6.0" + }, + "engines": { + "node": ">=10.0.0" + } + }, + "node_modules/kind-of": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.3.tgz", + "integrity": "sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw==", + "dev": true, + "peer": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/klaw": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/klaw/-/klaw-1.3.1.tgz", + "integrity": "sha512-TED5xi9gGQjGpNnvRWknrwAB1eL5GciPfVFOt3Vk1OJCVDQbzuSfrF3hkUQKlsgKrG1F+0t5W0m+Fje1jIt8rw==", + "optionalDependencies": { + "graceful-fs": "^4.1.9" + } + }, + "node_modules/level": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/level/-/level-8.0.0.tgz", + "integrity": "sha512-ypf0jjAk2BWI33yzEaaotpq7fkOPALKAgDBxggO6Q9HGX2MRXn0wbP1Jn/tJv1gtL867+YOjOB49WaUF3UoJNQ==", + "dependencies": { + "browser-level": "^1.0.1", + "classic-level": "^1.2.0" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/level" + } + }, + "node_modules/level-supports": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/level-supports/-/level-supports-4.0.1.tgz", + "integrity": "sha512-PbXpve8rKeNcZ9C1mUicC9auIYFyGpkV9/i6g76tLgANwWhtG2v7I4xNBUlkn3lE2/dZF3Pi0ygYGtLc4RXXdA==", + "engines": { + "node": ">=12" + } + }, + "node_modules/level-transcoder": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/level-transcoder/-/level-transcoder-1.0.1.tgz", + "integrity": "sha512-t7bFwFtsQeD8cl8NIoQ2iwxA0CL/9IFw7/9gAjOonH0PWTTiRfY7Hq+Ejbsxh86tXobDQ6IOiddjNYIfOBs06w==", + "dependencies": { + "buffer": "^6.0.3", + "module-error": "^1.0.1" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/levn": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/levn/-/levn-0.3.0.tgz", + "integrity": "sha512-0OO4y2iOHix2W6ujICbKIaEQXvFQHue65vUG3pb5EUomzPI90z9hsA1VsO/dbIIpC53J8gxM9Q4Oho0jrCM/yA==", + "dev": true, + "peer": true, + "dependencies": { + "prelude-ls": "~1.1.2", + "type-check": "~0.3.2" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/locate-path": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-2.0.0.tgz", + "integrity": "sha512-NCI2kiDkyR7VeEKm27Kda/iQHyKJe1Bu0FlTbYp3CqJu+9IFe9bLyAjMxf5ZDDbEg+iMPzB5zYyUTSm8wVTKmA==", + "dependencies": { + "p-locate": "^2.0.0", + "path-exists": "^3.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/lodash": { + "version": "4.17.21", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", + "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==" + }, + "node_modules/lodash.camelcase": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/lodash.camelcase/-/lodash.camelcase-4.3.0.tgz", + "integrity": "sha512-TwuEnCnxbc3rAvhf/LbG7tJUDzhqXyFnv3dtzLOPgCG/hODL7WFnsbwktkD7yUV0RrreP/l1PALq/YSg6VvjlA==", + "dev": true, + "peer": true + }, + "node_modules/lodash.clonedeep": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/lodash.clonedeep/-/lodash.clonedeep-4.5.0.tgz", + "integrity": "sha512-H5ZhCF25riFd9uB5UCkVKo61m3S/xZk1x4wA6yp/L3RFP6Z/eHH1ymQcGLo7J3GMPfm0V/7m1tryHuGVxpqEBQ==", + "dev": true, + "peer": true + }, + "node_modules/lodash.isequal": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/lodash.isequal/-/lodash.isequal-4.5.0.tgz", + "integrity": "sha512-pDo3lu8Jhfjqls6GkMgpahsF9kCyayhgykjyLMNFTKWrpVdAQtYyB4muAMWozBB4ig/dtWAmsMxLEI8wuz+DYQ==", + "dev": true + }, + "node_modules/lodash.truncate": { + "version": "4.4.2", + "resolved": "https://registry.npmjs.org/lodash.truncate/-/lodash.truncate-4.4.2.tgz", + "integrity": "sha512-jttmRe7bRse52OsWIMDLaXxWqRAmtIUccAQ3garviCqJjafXOfNMO0yMfNpdD6zbGaTU0P5Nz7e7gAT6cKmJRw==", + "dev": true, + "peer": true + }, + "node_modules/log-symbols": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-4.1.0.tgz", + "integrity": "sha512-8XPvpAA8uyhfteu8pIvQxpJZ7SYYdpUivZpGy6sFsBuKRY/7rQGavedeB8aK+Zkyq6upMFVL/9AW6vOYzfRyLg==", + "dependencies": { + "chalk": "^4.1.0", + "is-unicode-supported": "^0.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/log-symbols/node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/log-symbols/node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/log-symbols/node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/log-symbols/node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==" + }, + "node_modules/log-symbols/node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "engines": { + "node": ">=8" + } + }, + "node_modules/log-symbols/node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/loupe": { + "version": "2.3.7", + "resolved": "https://registry.npmjs.org/loupe/-/loupe-2.3.7.tgz", + "integrity": "sha512-zSMINGVYkdpYSOBmLi0D1Uo7JU9nVdQKrHxC8eYlV+9YKK9WePqAlL7lSlorG/U2Fw1w0hTBmaa/jrQ3UbPHtA==", + "dev": true, + "peer": true, + "dependencies": { + "get-func-name": "^2.0.1" + } + }, + "node_modules/lru_map": { + "version": "0.3.3", + "resolved": "https://registry.npmjs.org/lru_map/-/lru_map-0.3.3.tgz", + "integrity": "sha512-Pn9cox5CsMYngeDbmChANltQl+5pi6XmTrraMSzhPmMBbmgcxmqWry0U3PGapCU1yB4/LqCcom7qhHZiF/jGfQ==" + }, + "node_modules/lru-cache": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz", + "integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==", + "dependencies": { + "yallist": "^3.0.2" + } + }, + "node_modules/make-error": { + "version": "1.3.6", + "resolved": "https://registry.npmjs.org/make-error/-/make-error-1.3.6.tgz", + "integrity": "sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==", + "devOptional": true, + "peer": true + }, + "node_modules/markdown-table": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/markdown-table/-/markdown-table-1.1.3.tgz", + "integrity": "sha512-1RUZVgQlpJSPWYbFSpmudq5nHY1doEIv89gBtF0s4gW1GF2XorxcA/70M5vq7rLv0a6mhOUccRsqkwhwLCIQ2Q==", + "dev": true, + "peer": true + }, + "node_modules/mcl-wasm": { + "version": "0.7.9", + "resolved": "https://registry.npmjs.org/mcl-wasm/-/mcl-wasm-0.7.9.tgz", + "integrity": "sha512-iJIUcQWA88IJB/5L15GnJVnSQJmf/YaxxV6zRavv83HILHaJQb6y0iFyDMdDO0gN8X37tdxmAOrH/P8B6RB8sQ==", + "engines": { + "node": ">=8.9.0" + } + }, + "node_modules/md5.js": { + "version": "1.3.5", + "resolved": "https://registry.npmjs.org/md5.js/-/md5.js-1.3.5.tgz", + "integrity": "sha512-xitP+WxNPcTTOgnTJcrhM0xvdPepipPSf3I8EIpGKeFLjt3PlJLIDG3u8EX53ZIubkb+5U2+3rELYpEhHhzdkg==", + "dependencies": { + "hash-base": "^3.0.0", + "inherits": "^2.0.1", + "safe-buffer": "^5.1.2" + } + }, + "node_modules/memory-level": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/memory-level/-/memory-level-1.0.0.tgz", + "integrity": "sha512-UXzwewuWeHBz5krr7EvehKcmLFNoXxGcvuYhC41tRnkrTbJohtS7kVn9akmgirtRygg+f7Yjsfi8Uu5SGSQ4Og==", + "dependencies": { + "abstract-level": "^1.0.0", + "functional-red-black-tree": "^1.0.1", + "module-error": "^1.0.1" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/memorystream": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/memorystream/-/memorystream-0.3.1.tgz", + "integrity": "sha512-S3UwM3yj5mtUSEfP41UZmt/0SCoVYUcU1rkXv+BQ5Ig8ndL4sPoJNBUJERafdPb5jjHJGuMgytgKvKIf58XNBw==", + "engines": { + "node": ">= 0.10.0" + } + }, + "node_modules/merge2": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", + "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==", + "dev": true, + "peer": true, + "engines": { + "node": ">= 8" + } + }, + "node_modules/micro-ftch": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/micro-ftch/-/micro-ftch-0.3.1.tgz", + "integrity": "sha512-/0LLxhzP0tfiR5hcQebtudP56gUurs2CLkGarnCiB/OqEyUFQ6U3paQi/tgLv0hBJYt2rnr9MNpxz4fiiugstg==", + "dev": true, + "peer": true + }, + "node_modules/micromatch": { + "version": "4.0.5", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.5.tgz", + "integrity": "sha512-DMy+ERcEW2q8Z2Po+WNXuw3c5YaUSFjAO5GsJqfEl7UjvtIuFKO6ZrKvcItdy98dwFI2N1tg3zNIdKaQT+aNdA==", + "dev": true, + "peer": true, + "dependencies": { + "braces": "^3.0.2", + "picomatch": "^2.3.1" + }, + "engines": { + "node": ">=8.6" + } + }, + "node_modules/mime-db": { + "version": "1.52.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", + "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", + "dev": true, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/mime-types": { + "version": "2.1.35", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", + "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", + "dev": true, + "dependencies": { + "mime-db": "1.52.0" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/minimalistic-assert": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/minimalistic-assert/-/minimalistic-assert-1.0.1.tgz", + "integrity": "sha512-UtJcAD4yEaGtjPezWuO9wC4nwUnVH/8/Im3yEHQP4b67cXlD/Qr9hdITCU1xDbSEXg2XKNaP8jsReV7vQd00/A==" + }, + "node_modules/minimalistic-crypto-utils": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/minimalistic-crypto-utils/-/minimalistic-crypto-utils-1.0.1.tgz", + "integrity": "sha512-JIYlbt6g8i5jKfJ3xz7rF0LXmv2TkDxBLUkiBeZ7bAx4GnnNMr8xFpGnOxn6GhTEHx3SjRrZEoU+j04prX1ktg==" + }, + "node_modules/minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, + "node_modules/minimist": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz", + "integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==", + "dev": true, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/mkdirp": { + "version": "0.5.6", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.6.tgz", + "integrity": "sha512-FP+p8RB8OWpF3YZBCrP5gtADmtXApB5AMLn+vdyA+PyxCjrCs00mjyUozssO33cwDeT3wNGdLxJ5M//YqtHAJw==", + "dev": true, + "peer": true, + "dependencies": { + "minimist": "^1.2.6" + }, + "bin": { + "mkdirp": "bin/cmd.js" + } + }, + "node_modules/mnemonist": { + "version": "0.38.5", + "resolved": "https://registry.npmjs.org/mnemonist/-/mnemonist-0.38.5.tgz", + "integrity": "sha512-bZTFT5rrPKtPJxj8KSV0WkPyNxl72vQepqqVUAW2ARUpUSF2qXMB6jZj7hW5/k7C1rtpzqbD/IIbJwLXUjCHeg==", + "dependencies": { + "obliterator": "^2.0.0" + } + }, + "node_modules/mocha": { + "version": "10.2.0", + "resolved": "https://registry.npmjs.org/mocha/-/mocha-10.2.0.tgz", + "integrity": "sha512-IDY7fl/BecMwFHzoqF2sg/SHHANeBoMMXFlS9r0OXKDssYE1M5O43wUY/9BVPeIvfH2zmEbBfseqN9gBQZzXkg==", + "dependencies": { + "ansi-colors": "4.1.1", + "browser-stdout": "1.3.1", + "chokidar": "3.5.3", + "debug": "4.3.4", + "diff": "5.0.0", + "escape-string-regexp": "4.0.0", + "find-up": "5.0.0", + "glob": "7.2.0", + "he": "1.2.0", + "js-yaml": "4.1.0", + "log-symbols": "4.1.0", + "minimatch": "5.0.1", + "ms": "2.1.3", + "nanoid": "3.3.3", + "serialize-javascript": "6.0.0", + "strip-json-comments": "3.1.1", + "supports-color": "8.1.1", + "workerpool": "6.2.1", + "yargs": "16.2.0", + "yargs-parser": "20.2.4", + "yargs-unparser": "2.0.0" + }, + "bin": { + "_mocha": "bin/_mocha", + "mocha": "bin/mocha.js" + }, + "engines": { + "node": ">= 14.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/mochajs" + } + }, + "node_modules/mocha/node_modules/ansi-colors": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-4.1.1.tgz", + "integrity": "sha512-JoX0apGbHaUJBNl6yF+p6JAFYZ666/hhCGKN5t9QFjbJQKUU/g8MNbFDbvfrgKXvI1QpZplPOnwIo99lX/AAmA==", + "engines": { + "node": ">=6" + } + }, + "node_modules/mocha/node_modules/brace-expansion": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", + "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", + "dependencies": { + "balanced-match": "^1.0.0" + } + }, + "node_modules/mocha/node_modules/escape-string-regexp": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", + "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/mocha/node_modules/find-up": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", + "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==", + "dependencies": { + "locate-path": "^6.0.0", + "path-exists": "^4.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/mocha/node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "engines": { + "node": ">=8" + } + }, + "node_modules/mocha/node_modules/locate-path": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", + "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==", + "dependencies": { + "p-locate": "^5.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/mocha/node_modules/minimatch": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.0.1.tgz", + "integrity": "sha512-nLDxIFRyhDblz3qMuq+SoRZED4+miJ/G+tdDrjkkkRnjAsBexeGpgjLEQ0blJy7rHhR2b93rhQY4SvyWu9v03g==", + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/mocha/node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==" + }, + "node_modules/mocha/node_modules/p-limit": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", + "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", + "dependencies": { + "yocto-queue": "^0.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/mocha/node_modules/p-locate": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz", + "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==", + "dependencies": { + "p-limit": "^3.0.2" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/mocha/node_modules/path-exists": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", + "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", + "engines": { + "node": ">=8" + } + }, + "node_modules/mocha/node_modules/supports-color": { + "version": "8.1.1", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", + "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/supports-color?sponsor=1" + } + }, + "node_modules/module-error": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/module-error/-/module-error-1.0.2.tgz", + "integrity": "sha512-0yuvsqSCv8LbaOKhnsQ/T5JhyFlCYLPXK3U2sgV10zoKQwzs/MyfuQUOZQ1V/6OCOJsK/TRgNVrPuPDqtdMFtA==", + "engines": { + "node": ">=10" + } + }, + "node_modules/ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" + }, + "node_modules/nanoid": { + "version": "3.3.3", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.3.tgz", + "integrity": "sha512-p1sjXuopFs0xg+fPASzQ28agW1oHD7xDsd9Xkf3T15H3c/cifrFHVwrh74PdoklAPi+i7MdRsE47vm2r6JoB+w==", + "bin": { + "nanoid": "bin/nanoid.cjs" + }, + "engines": { + "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" + } + }, + "node_modules/napi-macros": { + "version": "2.2.2", + "resolved": "https://registry.npmjs.org/napi-macros/-/napi-macros-2.2.2.tgz", + "integrity": "sha512-hmEVtAGYzVQpCKdbQea4skABsdXW4RUh5t5mJ2zzqowJS2OyXZTU1KhDVFhx+NlWZ4ap9mqR9TcDO3LTTttd+g==" + }, + "node_modules/neo-async": { + "version": "2.6.2", + "resolved": "https://registry.npmjs.org/neo-async/-/neo-async-2.6.2.tgz", + "integrity": "sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw==", + "dev": true, + "peer": true + }, + "node_modules/node-addon-api": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-2.0.2.tgz", + "integrity": "sha512-Ntyt4AIXyaLIuMHF6IOoTakB3K+RWxwtsHNRxllEoA6vPwP9o4866g6YWDLUdnucilZhmkxiHwHr11gAENw+QA==" + }, + "node_modules/node-emoji": { + "version": "1.11.0", + "resolved": "https://registry.npmjs.org/node-emoji/-/node-emoji-1.11.0.tgz", + "integrity": "sha512-wo2DpQkQp7Sjm2A0cq+sN7EHKO6Sl0ctXeBdFZrL9T9+UywORbufTcTZxom8YqpLQt/FqNMUkOpkZrJVYSKD3A==", + "dev": true, + "peer": true, + "dependencies": { + "lodash": "^4.17.21" + } + }, + "node_modules/node-fetch": { + "version": "2.7.0", + "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.7.0.tgz", + "integrity": "sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A==", + "dev": true, + "dependencies": { + "whatwg-url": "^5.0.0" + }, + "engines": { + "node": "4.x || >=6.0.0" + }, + "peerDependencies": { + "encoding": "^0.1.0" + }, + "peerDependenciesMeta": { + "encoding": { + "optional": true + } + } + }, + "node_modules/node-gyp-build": { + "version": "4.7.1", + "resolved": "https://registry.npmjs.org/node-gyp-build/-/node-gyp-build-4.7.1.tgz", + "integrity": "sha512-wTSrZ+8lsRRa3I3H8Xr65dLWSgCvY2l4AOnaeKdPA9TB/WYMPaTcrzf3rXvFoVvjKNVnu0CcWSx54qq9GKRUYg==", + "bin": { + "node-gyp-build": "bin.js", + "node-gyp-build-optional": "optional.js", + "node-gyp-build-test": "build-test.js" + } + }, + "node_modules/nofilter": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/nofilter/-/nofilter-3.1.0.tgz", + "integrity": "sha512-l2NNj07e9afPnhAhvgVrCD/oy2Ai1yfLpuo3EpiO1jFTsB4sFz6oIfAfSZyQzVpkZQ9xS8ZS5g1jCBgq4Hwo0g==", + "dev": true, + "engines": { + "node": ">=12.19" + } + }, + "node_modules/nopt": { + "version": "3.0.6", + "resolved": "https://registry.npmjs.org/nopt/-/nopt-3.0.6.tgz", + "integrity": "sha512-4GUt3kSEYmk4ITxzB/b9vaIDfUVWN/Ml1Fwl11IlnIG2iaJ9O6WXZ9SrYM9NLI8OCBieN2Y8SWC2oJV0RQ7qYg==", + "dev": true, + "peer": true, + "dependencies": { + "abbrev": "1" + }, + "bin": { + "nopt": "bin/nopt.js" + } + }, + "node_modules/normalize-path": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", + "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/number-to-bn": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/number-to-bn/-/number-to-bn-1.7.0.tgz", + "integrity": "sha512-wsJ9gfSz1/s4ZsJN01lyonwuxA1tml6X1yBDnfpMglypcBRFZZkus26EdPSlqS5GJfYddVZa22p3VNb3z5m5Ig==", + "dev": true, + "peer": true, + "dependencies": { + "bn.js": "4.11.6", + "strip-hex-prefix": "1.0.0" + }, + "engines": { + "node": ">=6.5.0", + "npm": ">=3" + } + }, + "node_modules/number-to-bn/node_modules/bn.js": { + "version": "4.11.6", + "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.11.6.tgz", + "integrity": "sha512-XWwnNNFCuuSQ0m3r3C4LE3EiORltHd9M05pq6FOlVeiophzRbMo50Sbz1ehl8K3Z+jw9+vmgnXefY1hz8X+2wA==", + "dev": true, + "peer": true + }, + "node_modules/object-assign": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", + "integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==", + "dev": true, + "peer": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/object-inspect": { + "version": "1.13.1", + "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.1.tgz", + "integrity": "sha512-5qoj1RUiKOMsCCNLV1CBiPYE10sziTsnmNxkAI/rZhiD63CF7IqdFGC/XzjWjpSgLf0LxXX3bDFIh0E18f6UhQ==", + "dev": true, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/object-keys": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz", + "integrity": "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==", + "dev": true, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/object.assign": { + "version": "4.1.5", + "resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.5.tgz", + "integrity": "sha512-byy+U7gp+FVwmyzKPYhW2h5l3crpmGsxl7X2s8y43IgxvG4g3QZ6CffDtsNQy1WsmZpQbO+ybo0AlW7TY6DcBQ==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.5", + "define-properties": "^1.2.1", + "has-symbols": "^1.0.3", + "object-keys": "^1.1.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/obliterator": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/obliterator/-/obliterator-2.0.4.tgz", + "integrity": "sha512-lgHwxlxV1qIg1Eap7LgIeoBWIMFibOjbrYPIPJZcI1mmGAI2m3lNYpK12Y+GBdPQ0U1hRwSord7GIaawz962qQ==" + }, + "node_modules/once": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", + "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", + "dependencies": { + "wrappy": "1" + } + }, + "node_modules/optionator": { + "version": "0.8.3", + "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.8.3.tgz", + "integrity": "sha512-+IW9pACdk3XWmmTXG8m3upGUJst5XRGzxMRjXzAuJ1XnIFNvfhjjIuYkDvysnPQ7qzqVzLt78BCruntqRhWQbA==", + "dev": true, + "peer": true, + "dependencies": { + "deep-is": "~0.1.3", + "fast-levenshtein": "~2.0.6", + "levn": "~0.3.0", + "prelude-ls": "~1.1.2", + "type-check": "~0.3.2", + "word-wrap": "~1.2.3" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/ordinal": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/ordinal/-/ordinal-1.0.3.tgz", + "integrity": "sha512-cMddMgb2QElm8G7vdaa02jhUNbTSrhsgAGUz1OokD83uJTwSUn+nKoNoKVVaRa08yF6sgfO7Maou1+bgLd9rdQ==", + "dev": true, + "peer": true + }, + "node_modules/os-tmpdir": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/os-tmpdir/-/os-tmpdir-1.0.2.tgz", + "integrity": "sha512-D2FR03Vir7FIu45XBY20mTb+/ZSWB00sjU9jdQXt83gDrI4Ztz5Fs7/yy74g2N5SVQY4xY1qDr4rNddwYRVX0g==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/p-limit": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-1.3.0.tgz", + "integrity": "sha512-vvcXsLAJ9Dr5rQOPk7toZQZJApBl2K4J6dANSsEuh6QI41JYcsS/qhTGa9ErIUUgK3WNQoJYvylxvjqmiqEA9Q==", + "dependencies": { + "p-try": "^1.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/p-locate": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-2.0.0.tgz", + "integrity": "sha512-nQja7m7gSKuewoVRen45CtVfODR3crN3goVQ0DDZ9N3yHxgpkuBhZqsaiotSQRrADUrne346peY7kT3TSACykg==", + "dependencies": { + "p-limit": "^1.1.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/p-map": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/p-map/-/p-map-4.0.0.tgz", + "integrity": "sha512-/bjOqmgETBYB5BoEeGVea8dmvHb2m9GLy1E9W43yeyfP6QQCZGFNa+XRceJEuDB6zqr+gKpIAmlLebMpykw/MQ==", + "dependencies": { + "aggregate-error": "^3.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/p-try": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/p-try/-/p-try-1.0.0.tgz", + "integrity": "sha512-U1etNYuMJoIz3ZXSrrySFjsXQTWOx2/jdi86L+2pRvph/qMKL6sbcCYdH23fqsbm8TH2Gn0OybpT4eSFlCVHww==", + "engines": { + "node": ">=4" + } + }, + "node_modules/parse-cache-control": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/parse-cache-control/-/parse-cache-control-1.0.1.tgz", + "integrity": "sha512-60zvsJReQPX5/QP0Kzfd/VrpjScIQ7SHBW6bFCYfEP+fp0Eppr1SHhIO5nd1PjZtvclzSzES9D/p5nFJurwfWg==", + "dev": true, + "peer": true + }, + "node_modules/path-exists": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-3.0.0.tgz", + "integrity": "sha512-bpC7GYwiDYQ4wYLe+FA8lhRjhQCMcQGuSgGGqDkg/QerRWw9CmGRT0iSOVRSZJ29NMLZgIzqaljJ63oaL4NIJQ==", + "engines": { + "node": ">=4" + } + }, + "node_modules/path-is-absolute": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", + "integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/path-parse": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz", + "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==" + }, + "node_modules/path-type": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz", + "integrity": "sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==", + "dev": true, + "peer": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/pathval": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/pathval/-/pathval-1.1.1.tgz", + "integrity": "sha512-Dp6zGqpTdETdR63lehJYPeIOqpiNBNtc7BpWSLrOje7UaIsE5aY92r/AunQA7rsXvet3lrJ3JnZX29UPTKXyKQ==", + "dev": true, + "peer": true, + "engines": { + "node": "*" + } + }, + "node_modules/pbkdf2": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/pbkdf2/-/pbkdf2-3.1.2.tgz", + "integrity": "sha512-iuh7L6jA7JEGu2WxDwtQP1ddOpaJNC4KlDEFfdQajSGgGPNi4OyDc2R7QnbY2bR9QjBVGwgvTdNJZoE7RaxUMA==", + "dependencies": { + "create-hash": "^1.1.2", + "create-hmac": "^1.1.4", + "ripemd160": "^2.0.1", + "safe-buffer": "^5.0.1", + "sha.js": "^2.4.8" + }, + "engines": { + "node": ">=0.12" + } + }, + "node_modules/picomatch": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", + "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", + "engines": { + "node": ">=8.6" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/pify": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/pify/-/pify-4.0.1.tgz", + "integrity": "sha512-uB80kBFb/tfd68bVleG9T5GGsGPjJrLAUpR5PZIrhBnIaRTQRjqdJSsIKkOP6OAIFbj7GOrcudc5pNjZ+geV2g==", + "dev": true, + "peer": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/prelude-ls": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.1.2.tgz", + "integrity": "sha512-ESF23V4SKG6lVSGZgYNpbsiaAkdab6ZgOxe52p7+Kid3W3u3bxR4Vfd/o21dmN7jSt0IwgZ4v5MUd26FEtXE9w==", + "dev": true, + "peer": true, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/prettier": { + "version": "2.8.8", + "resolved": "https://registry.npmjs.org/prettier/-/prettier-2.8.8.tgz", + "integrity": "sha512-tdN8qQGvNjw4CHbY+XXk0JgCXn9QiF21a55rBe5LJAU+kDyC4WQn4+awm2Xfk2lQMk5fKup9XgzTZtGkjBdP9Q==", + "dev": true, + "peer": true, + "bin": { + "prettier": "bin-prettier.js" + }, + "engines": { + "node": ">=10.13.0" + }, + "funding": { + "url": "https://github.com/prettier/prettier?sponsor=1" + } + }, + "node_modules/process-nextick-args": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz", + "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==", + "dev": true, + "peer": true + }, + "node_modules/promise": { + "version": "8.3.0", + "resolved": "https://registry.npmjs.org/promise/-/promise-8.3.0.tgz", + "integrity": "sha512-rZPNPKTOYVNEEKFaq1HqTgOwZD+4/YHS5ukLzQCypkj+OkYx7iv0mA91lJlpPPZ8vMau3IIGj5Qlwrx+8iiSmg==", + "dev": true, + "peer": true, + "dependencies": { + "asap": "~2.0.6" + } + }, + "node_modules/proper-lockfile": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/proper-lockfile/-/proper-lockfile-4.1.2.tgz", + "integrity": "sha512-TjNPblN4BwAWMXU8s9AEz4JmQxnD1NNL7bNOY/AKUzyamc379FWASUhc/K1pL2noVb+XmZKLL68cjzLsiOAMaA==", + "dev": true, + "dependencies": { + "graceful-fs": "^4.2.4", + "retry": "^0.12.0", + "signal-exit": "^3.0.2" + } + }, + "node_modules/proper-lockfile/node_modules/retry": { + "version": "0.12.0", + "resolved": "https://registry.npmjs.org/retry/-/retry-0.12.0.tgz", + "integrity": "sha512-9LkiTwjUh6rT555DtE9rTX+BKByPfrMzEAtnlEtdEwr3Nkffwiihqe2bWADg+OQRjt9gl6ICdmB/ZFDCGAtSow==", + "dev": true, + "engines": { + "node": ">= 4" + } + }, + "node_modules/proxy-from-env": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz", + "integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==", + "dev": true + }, + "node_modules/punycode": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz", + "integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==", + "dev": true, + "peer": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/qs": { + "version": "6.11.2", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.11.2.tgz", + "integrity": "sha512-tDNIz22aBzCDxLtVH++VnTfzxlfeK5CbqohpSqpJgj1Wg/cQbStNAz3NuqCs5vV+pjBsK4x4pN9HlVh7rcYRiA==", + "dev": true, + "peer": true, + "dependencies": { + "side-channel": "^1.0.4" + }, + "engines": { + "node": ">=0.6" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/queue-microtask": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", + "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ] + }, + "node_modules/randombytes": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz", + "integrity": "sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ==", + "dependencies": { + "safe-buffer": "^5.1.0" + } + }, + "node_modules/raw-body": { + "version": "2.5.2", + "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.5.2.tgz", + "integrity": "sha512-8zGqypfENjCIqGhgXToC8aB2r7YrBX+AQAfIPs/Mlk+BtPTztOvTS01NRW/3Eh60J+a48lt8qsCzirQ6loCVfA==", + "dependencies": { + "bytes": "3.1.2", + "http-errors": "2.0.0", + "iconv-lite": "0.4.24", + "unpipe": "1.0.0" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/readable-stream": { + "version": "3.6.2", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz", + "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==", + "dependencies": { + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/readdirp": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", + "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==", + "dependencies": { + "picomatch": "^2.2.1" + }, + "engines": { + "node": ">=8.10.0" + } + }, + "node_modules/rechoir": { + "version": "0.6.2", + "resolved": "https://registry.npmjs.org/rechoir/-/rechoir-0.6.2.tgz", + "integrity": "sha512-HFM8rkZ+i3zrV+4LQjwQ0W+ez98pApMGM3HUrN04j3CqzPOzl9nmP15Y8YXNm8QHGv/eacOVEjqhmWpkRV0NAw==", + "dev": true, + "peer": true, + "dependencies": { + "resolve": "^1.1.6" + }, + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/recursive-readdir": { + "version": "2.2.3", + "resolved": "https://registry.npmjs.org/recursive-readdir/-/recursive-readdir-2.2.3.tgz", + "integrity": "sha512-8HrF5ZsXk5FAH9dgsx3BlUer73nIhuj+9OrQwEbLTPOBzGkL1lsFCR01am+v+0m2Cmbs1nP12hLDl5FA7EszKA==", + "dev": true, + "peer": true, + "dependencies": { + "minimatch": "^3.0.5" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/reduce-flatten": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/reduce-flatten/-/reduce-flatten-2.0.0.tgz", + "integrity": "sha512-EJ4UNY/U1t2P/2k6oqotuX2Cc3T6nxJwsM0N0asT7dhrtH1ltUxDn4NalSYmPE2rCkVpcf/X6R0wDwcFpzhd4w==", + "dev": true, + "peer": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/regexp.prototype.flags": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/regexp.prototype.flags/-/regexp.prototype.flags-1.5.1.tgz", + "integrity": "sha512-sy6TXMN+hnP/wMy+ISxg3krXx7BAtWVO4UouuCN/ziM9UEne0euamVNafDfvC83bRNr95y0V5iijeDQFUNpvrg==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.2", + "define-properties": "^1.2.0", + "set-function-name": "^2.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/req-cwd": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/req-cwd/-/req-cwd-2.0.0.tgz", + "integrity": "sha512-ueoIoLo1OfB6b05COxAA9UpeoscNpYyM+BqYlA7H6LVF4hKGPXQQSSaD2YmvDVJMkk4UDpAHIeU1zG53IqjvlQ==", + "dev": true, + "peer": true, + "dependencies": { + "req-from": "^2.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/req-from": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/req-from/-/req-from-2.0.0.tgz", + "integrity": "sha512-LzTfEVDVQHBRfjOUMgNBA+V6DWsSnoeKzf42J7l0xa/B4jyPOuuF5MlNSmomLNGemWTnV2TIdjSSLnEn95fOQA==", + "dev": true, + "peer": true, + "dependencies": { + "resolve-from": "^3.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/require-directory": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", + "integrity": "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/require-from-string": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.2.tgz", + "integrity": "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/resolve": { + "version": "1.17.0", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.17.0.tgz", + "integrity": "sha512-ic+7JYiV8Vi2yzQGFWOkiZD5Z9z7O2Zhm9XMaTxdJExKasieFCr+yXZ/WmXsckHiKl12ar0y6XiXDx3m4RHn1w==", + "dependencies": { + "path-parse": "^1.0.6" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/resolve-from": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-3.0.0.tgz", + "integrity": "sha512-GnlH6vxLymXJNMBo7XP1fJIzBFbdYt49CuTwmB/6N53t+kMPRMFKz783LlQ4tv28XoQfMWinAJX6WCGf2IlaIw==", + "dev": true, + "peer": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/retry": { + "version": "0.13.1", + "resolved": "https://registry.npmjs.org/retry/-/retry-0.13.1.tgz", + "integrity": "sha512-XQBQ3I8W1Cge0Seh+6gjj03LbmRFWuoszgK9ooCpwYIrhhoO80pfq4cUkU5DkknwfOfFteRwlZ56PYOGYyFWdg==", + "dev": true, + "engines": { + "node": ">= 4" + } + }, + "node_modules/reusify": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.0.4.tgz", + "integrity": "sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==", + "dev": true, + "peer": true, + "engines": { + "iojs": ">=1.0.0", + "node": ">=0.10.0" + } + }, + "node_modules/rimraf": { + "version": "2.7.1", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.7.1.tgz", + "integrity": "sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w==", + "dependencies": { + "glob": "^7.1.3" + }, + "bin": { + "rimraf": "bin.js" + } + }, + "node_modules/ripemd160": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/ripemd160/-/ripemd160-2.0.2.tgz", + "integrity": "sha512-ii4iagi25WusVoiC4B4lq7pbXfAp3D9v5CwfkY33vffw2+pkDjY1D8GaN7spsxvCSx8dkPqOZCEZyfxcmJG2IA==", + "dependencies": { + "hash-base": "^3.0.0", + "inherits": "^2.0.1" + } + }, + "node_modules/rlp": { + "version": "2.2.7", + "resolved": "https://registry.npmjs.org/rlp/-/rlp-2.2.7.tgz", + "integrity": "sha512-d5gdPmgQ0Z+AklL2NVXr/IoSjNZFfTVvQWzL/AM2AOcSzYP2xjlb0AC8YyCLc41MSNf6P6QVtjgPdmVtzb+4lQ==", + "dependencies": { + "bn.js": "^5.2.0" + }, + "bin": { + "rlp": "bin/rlp" + } + }, + "node_modules/run-parallel": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz", + "integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "peer": true, + "dependencies": { + "queue-microtask": "^1.2.2" + } + }, + "node_modules/run-parallel-limit": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/run-parallel-limit/-/run-parallel-limit-1.1.0.tgz", + "integrity": "sha512-jJA7irRNM91jaKc3Hcl1npHsFLOXOoTkPCUL1JEa1R82O2miplXXRaGdjW/KM/98YQWDhJLiSs793CnXfblJUw==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "dependencies": { + "queue-microtask": "^1.2.2" + } + }, + "node_modules/rustbn.js": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/rustbn.js/-/rustbn.js-0.2.0.tgz", + "integrity": "sha512-4VlvkRUuCJvr2J6Y0ImW7NvTCriMi7ErOAqWk1y69vAdoNIzCF3yPmgeNzx+RQTLEDFq5sHfscn1MwHxP9hNfA==" + }, + "node_modules/safe-array-concat": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/safe-array-concat/-/safe-array-concat-1.0.1.tgz", + "integrity": "sha512-6XbUAseYE2KtOuGueyeobCySj9L4+66Tn6KQMOPQJrAJEowYKW/YR/MGJZl7FdydUdaFu4LYyDZjxf4/Nmo23Q==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.2", + "get-intrinsic": "^1.2.1", + "has-symbols": "^1.0.3", + "isarray": "^2.0.5" + }, + "engines": { + "node": ">=0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/safe-array-concat/node_modules/isarray": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-2.0.5.tgz", + "integrity": "sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw==", + "dev": true + }, + "node_modules/safe-buffer": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ] + }, + "node_modules/safe-regex-test": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/safe-regex-test/-/safe-regex-test-1.0.0.tgz", + "integrity": "sha512-JBUUzyOgEwXQY1NuPtvcj/qcBDbDmEvWufhlnXZIm75DEHp+afM1r1ujJpJsV/gSM4t59tpDyPi1sd6ZaPFfsA==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.2", + "get-intrinsic": "^1.1.3", + "is-regex": "^1.1.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/safer-buffer": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", + "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==" + }, + "node_modules/sc-istanbul": { + "version": "0.4.6", + "resolved": "https://registry.npmjs.org/sc-istanbul/-/sc-istanbul-0.4.6.tgz", + "integrity": "sha512-qJFF/8tW/zJsbyfh/iT/ZM5QNHE3CXxtLJbZsL+CzdJLBsPD7SedJZoUA4d8iAcN2IoMp/Dx80shOOd2x96X/g==", + "dev": true, + "peer": true, + "dependencies": { + "abbrev": "1.0.x", + "async": "1.x", + "escodegen": "1.8.x", + "esprima": "2.7.x", + "glob": "^5.0.15", + "handlebars": "^4.0.1", + "js-yaml": "3.x", + "mkdirp": "0.5.x", + "nopt": "3.x", + "once": "1.x", + "resolve": "1.1.x", + "supports-color": "^3.1.0", + "which": "^1.1.1", + "wordwrap": "^1.0.0" + }, + "bin": { + "istanbul": "lib/cli.js" + } + }, + "node_modules/sc-istanbul/node_modules/argparse": { + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", + "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", + "dev": true, + "peer": true, + "dependencies": { + "sprintf-js": "~1.0.2" + } + }, + "node_modules/sc-istanbul/node_modules/glob": { + "version": "5.0.15", + "resolved": "https://registry.npmjs.org/glob/-/glob-5.0.15.tgz", + "integrity": "sha512-c9IPMazfRITpmAAKi22dK1VKxGDX9ehhqfABDriL/lzO92xcUKEJPQHrVA/2YHSNFB4iFlykVmWvwo48nr3OxA==", + "dev": true, + "peer": true, + "dependencies": { + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "2 || 3", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + }, + "engines": { + "node": "*" + } + }, + "node_modules/sc-istanbul/node_modules/has-flag": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-1.0.0.tgz", + "integrity": "sha512-DyYHfIYwAJmjAjSSPKANxI8bFY9YtFrgkAfinBojQ8YJTOuOuav64tMUJv584SES4xl74PmuaevIyaLESHdTAA==", + "dev": true, + "peer": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/sc-istanbul/node_modules/js-yaml": { + "version": "3.14.1", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.1.tgz", + "integrity": "sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g==", + "dev": true, + "peer": true, + "dependencies": { + "argparse": "^1.0.7", + "esprima": "^4.0.0" + }, + "bin": { + "js-yaml": "bin/js-yaml.js" + } + }, + "node_modules/sc-istanbul/node_modules/js-yaml/node_modules/esprima": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", + "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==", + "dev": true, + "peer": true, + "bin": { + "esparse": "bin/esparse.js", + "esvalidate": "bin/esvalidate.js" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/sc-istanbul/node_modules/resolve": { + "version": "1.1.7", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.1.7.tgz", + "integrity": "sha512-9znBF0vBcaSN3W2j7wKvdERPwqTxSpCq+if5C0WoTCyV9n24rua28jeuQ2pL/HOf+yUe/Mef+H/5p60K0Id3bg==", + "dev": true, + "peer": true + }, + "node_modules/sc-istanbul/node_modules/supports-color": { + "version": "3.2.3", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-3.2.3.tgz", + "integrity": "sha512-Jds2VIYDrlp5ui7t8abHN2bjAu4LV/q4N2KivFPpGH0lrka0BMq/33AmECUXlKPcHigkNaqfXRENFju+rlcy+A==", + "dev": true, + "peer": true, + "dependencies": { + "has-flag": "^1.0.0" + }, + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/scrypt-js": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/scrypt-js/-/scrypt-js-3.0.1.tgz", + "integrity": "sha512-cdwTTnqPu0Hyvf5in5asVdZocVDTNRmR7XEcJuIzMjJeSHybHl7vpB66AzwTaIg6CLSbtjcxc8fqcySfnTkccA==" + }, + "node_modules/secp256k1": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/secp256k1/-/secp256k1-4.0.3.tgz", + "integrity": "sha512-NLZVf+ROMxwtEj3Xa562qgv2BK5e2WNmXPiOdVIPLgs6lyTzMvBq0aWTYMI5XCP9jZMVKOcqZLw/Wc4vDkuxhA==", + "hasInstallScript": true, + "dependencies": { + "elliptic": "^6.5.4", + "node-addon-api": "^2.0.0", + "node-gyp-build": "^4.2.0" + }, + "engines": { + "node": ">=10.0.0" + } + }, + "node_modules/semver": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "bin": { + "semver": "bin/semver.js" + } + }, + "node_modules/serialize-javascript": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-6.0.0.tgz", + "integrity": "sha512-Qr3TosvguFt8ePWqsvRfrKyQXIiW+nGbYpy8XK24NQHE83caxWt+mIymTT19DGFbNWNLfEwsrkSmN64lVWB9ag==", + "dependencies": { + "randombytes": "^2.1.0" + } + }, + "node_modules/set-function-length": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/set-function-length/-/set-function-length-1.1.1.tgz", + "integrity": "sha512-VoaqjbBJKiWtg4yRcKBQ7g7wnGnLV3M8oLvVWwOk2PdYY6PEFegR1vezXR0tw6fZGF9csVakIRjrJiy2veSBFQ==", + "dev": true, + "dependencies": { + "define-data-property": "^1.1.1", + "get-intrinsic": "^1.2.1", + "gopd": "^1.0.1", + "has-property-descriptors": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/set-function-name": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/set-function-name/-/set-function-name-2.0.1.tgz", + "integrity": "sha512-tMNCiqYVkXIZgc2Hnoy2IvC/f8ezc5koaRFkCjrpWzGpCd3qbZXPzVy9MAZzK1ch/X0jvSkojys3oqJN0qCmdA==", + "dev": true, + "dependencies": { + "define-data-property": "^1.0.1", + "functions-have-names": "^1.2.3", + "has-property-descriptors": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/setimmediate": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/setimmediate/-/setimmediate-1.0.5.tgz", + "integrity": "sha512-MATJdZp8sLqDl/68LfQmbP8zKPLQNV6BIZoIgrscFDQ+RsvK/BxeDQOgyxKKoh0y/8h3BqVFnCqQ/gd+reiIXA==" + }, + "node_modules/setprototypeof": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz", + "integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==" + }, + "node_modules/sha.js": { + "version": "2.4.11", + "resolved": "https://registry.npmjs.org/sha.js/-/sha.js-2.4.11.tgz", + "integrity": "sha512-QMEp5B7cftE7APOjk5Y6xgrbWu+WkLVQwk8JNjZ8nKRciZaByEW6MubieAiToS7+dwvrjGhH8jRXz3MVd0AYqQ==", + "dependencies": { + "inherits": "^2.0.1", + "safe-buffer": "^5.0.1" + }, + "bin": { + "sha.js": "bin.js" + } + }, + "node_modules/sha1": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/sha1/-/sha1-1.1.1.tgz", + "integrity": "sha512-dZBS6OrMjtgVkopB1Gmo4RQCDKiZsqcpAQpkV/aaj+FCrCg8r4I4qMkDPQjBgLIxlmu9k4nUbWq6ohXahOneYA==", + "dev": true, + "peer": true, + "dependencies": { + "charenc": ">= 0.0.1", + "crypt": ">= 0.0.1" + }, + "engines": { + "node": "*" + } + }, + "node_modules/shelljs": { + "version": "0.8.5", + "resolved": "https://registry.npmjs.org/shelljs/-/shelljs-0.8.5.tgz", + "integrity": "sha512-TiwcRcrkhHvbrZbnRcFYMLl30Dfov3HKqzp5tO5b4pt6G/SezKcYhmDg15zXVBswHmctSAQKznqNW2LO5tTDow==", + "dev": true, + "peer": true, + "dependencies": { + "glob": "^7.0.0", + "interpret": "^1.0.0", + "rechoir": "^0.6.2" + }, + "bin": { + "shjs": "bin/shjs" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/side-channel": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.0.4.tgz", + "integrity": "sha512-q5XPytqFEIKHkGdiMIrY10mvLRvnQh42/+GoBlFW3b2LXLE2xxJpZFdm94we0BaoV3RwJyGqg5wS7epxTv0Zvw==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.0", + "get-intrinsic": "^1.0.2", + "object-inspect": "^1.9.0" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/signal-exit": { + "version": "3.0.7", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz", + "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==", + "dev": true + }, + "node_modules/slash": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", + "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", + "dev": true, + "peer": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/slice-ansi": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-4.0.0.tgz", + "integrity": "sha512-qMCMfhY040cVHT43K9BFygqYbUPFZKHOg7K73mtTWJRb8pyP3fzf4Ixd5SzdEJQ6MRUg/WBnOLxghZtKKurENQ==", + "dev": true, + "peer": true, + "dependencies": { + "ansi-styles": "^4.0.0", + "astral-regex": "^2.0.0", + "is-fullwidth-code-point": "^3.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/slice-ansi?sponsor=1" + } + }, + "node_modules/slice-ansi/node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "peer": true, + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/slice-ansi/node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "peer": true, + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/slice-ansi/node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true, + "peer": true + }, + "node_modules/slice-ansi/node_modules/is-fullwidth-code-point": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "dev": true, + "peer": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/solc": { + "version": "0.7.3", + "resolved": "https://registry.npmjs.org/solc/-/solc-0.7.3.tgz", + "integrity": "sha512-GAsWNAjGzIDg7VxzP6mPjdurby3IkGCjQcM8GFYZT6RyaoUZKmMU6Y7YwG+tFGhv7dwZ8rmR4iwFDrrD99JwqA==", + "dependencies": { + "command-exists": "^1.2.8", + "commander": "3.0.2", + "follow-redirects": "^1.12.1", + "fs-extra": "^0.30.0", + "js-sha3": "0.8.0", + "memorystream": "^0.3.1", + "require-from-string": "^2.0.0", + "semver": "^5.5.0", + "tmp": "0.0.33" + }, + "bin": { + "solcjs": "solcjs" + }, + "engines": { + "node": ">=8.0.0" + } + }, + "node_modules/solc/node_modules/fs-extra": { + "version": "0.30.0", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-0.30.0.tgz", + "integrity": "sha512-UvSPKyhMn6LEd/WpUaV9C9t3zATuqoqfWc3QdPhPLb58prN9tqYPlPWi8Krxi44loBoUzlobqZ3+8tGpxxSzwA==", + "dependencies": { + "graceful-fs": "^4.1.2", + "jsonfile": "^2.1.0", + "klaw": "^1.0.0", + "path-is-absolute": "^1.0.0", + "rimraf": "^2.2.8" + } + }, + "node_modules/solc/node_modules/jsonfile": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-2.4.0.tgz", + "integrity": "sha512-PKllAqbgLgxHaj8TElYymKCAgrASebJrWpTnEkOaTowt23VKXXN0sUeriJ+eh7y6ufb/CC5ap11pz71/cM0hUw==", + "optionalDependencies": { + "graceful-fs": "^4.1.6" + } + }, + "node_modules/solc/node_modules/semver": { + "version": "5.7.2", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.2.tgz", + "integrity": "sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g==", + "bin": { + "semver": "bin/semver" + } + }, + "node_modules/solidity-ast": { + "version": "0.4.55", + "resolved": "https://registry.npmjs.org/solidity-ast/-/solidity-ast-0.4.55.tgz", + "integrity": "sha512-qeEU/r/K+V5lrAw8iswf2/yfWAnSGs3WKPHI+zAFKFjX0dIBVXEU/swQ8eJQYHf6PJWUZFO2uWV4V1wEOkeQbA==", + "dev": true, + "dependencies": { + "array.prototype.findlast": "^1.2.2" + } + }, + "node_modules/solidity-coverage": { + "version": "0.8.5", + "resolved": "https://registry.npmjs.org/solidity-coverage/-/solidity-coverage-0.8.5.tgz", + "integrity": "sha512-6C6N6OV2O8FQA0FWA95FdzVH+L16HU94iFgg5wAFZ29UpLFkgNI/DRR2HotG1bC0F4gAc/OMs2BJI44Q/DYlKQ==", + "dev": true, + "peer": true, + "dependencies": { + "@ethersproject/abi": "^5.0.9", + "@solidity-parser/parser": "^0.16.0", + "chalk": "^2.4.2", + "death": "^1.1.0", + "detect-port": "^1.3.0", + "difflib": "^0.2.4", + "fs-extra": "^8.1.0", + "ghost-testrpc": "^0.0.2", + "global-modules": "^2.0.0", + "globby": "^10.0.1", + "jsonschema": "^1.2.4", + "lodash": "^4.17.15", + "mocha": "10.2.0", + "node-emoji": "^1.10.0", + "pify": "^4.0.1", + "recursive-readdir": "^2.2.2", + "sc-istanbul": "^0.4.5", + "semver": "^7.3.4", + "shelljs": "^0.8.3", + "web3-utils": "^1.3.6" + }, + "bin": { + "solidity-coverage": "plugins/bin.js" + }, + "peerDependencies": { + "hardhat": "^2.11.0" + } + }, + "node_modules/solidity-coverage/node_modules/@solidity-parser/parser": { + "version": "0.16.2", + "resolved": "https://registry.npmjs.org/@solidity-parser/parser/-/parser-0.16.2.tgz", + "integrity": "sha512-PI9NfoA3P8XK2VBkK5oIfRgKDsicwDZfkVq9ZTBCQYGOP1N2owgY2dyLGyU5/J/hQs8KRk55kdmvTLjy3Mu3vg==", + "dev": true, + "peer": true, + "dependencies": { + "antlr4ts": "^0.5.0-alpha.4" + } + }, + "node_modules/solidity-coverage/node_modules/fs-extra": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-8.1.0.tgz", + "integrity": "sha512-yhlQgA6mnOJUKOsRUFsgJdQCvkKhcz8tlZG5HBQfReYZy46OwLcY+Zia0mtdHsOo9y/hP+CxMN0TU9QxoOtG4g==", + "dev": true, + "peer": true, + "dependencies": { + "graceful-fs": "^4.2.0", + "jsonfile": "^4.0.0", + "universalify": "^0.1.0" + }, + "engines": { + "node": ">=6 <7 || >=8" + } + }, + "node_modules/solidity-coverage/node_modules/jsonfile": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-4.0.0.tgz", + "integrity": "sha512-m6F1R3z8jjlf2imQHS2Qez5sjKWQzbuuhuJ/FKYFRZvPE3PuHcSMVZzfsLhGVOkfd20obL5SWEBew5ShlquNxg==", + "dev": true, + "peer": true, + "optionalDependencies": { + "graceful-fs": "^4.1.6" + } + }, + "node_modules/solidity-coverage/node_modules/lru-cache": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", + "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", + "dev": true, + "peer": true, + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/solidity-coverage/node_modules/semver": { + "version": "7.5.4", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz", + "integrity": "sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==", + "dev": true, + "peer": true, + "dependencies": { + "lru-cache": "^6.0.0" + }, + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/solidity-coverage/node_modules/universalify": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/universalify/-/universalify-0.1.2.tgz", + "integrity": "sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg==", + "dev": true, + "peer": true, + "engines": { + "node": ">= 4.0.0" + } + }, + "node_modules/solidity-coverage/node_modules/yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", + "dev": true, + "peer": true + }, + "node_modules/source-map": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.2.0.tgz", + "integrity": "sha512-CBdZ2oa/BHhS4xj5DlhjWNHcan57/5YuvfdLf17iVmIpd9KRm+DFLmC6nBNj+6Ua7Kt3TmOjDpQT1aTYOQtoUA==", + "dev": true, + "optional": true, + "peer": true, + "dependencies": { + "amdefine": ">=0.0.4" + }, + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/source-map-support": { + "version": "0.5.21", + "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.21.tgz", + "integrity": "sha512-uBHU3L3czsIyYXKX88fdrGovxdSCoTGDRZ6SYXtSRxLZUzHg5P/66Ht6uoUlHu9EZod+inXhKo3qQgwXUT/y1w==", + "dependencies": { + "buffer-from": "^1.0.0", + "source-map": "^0.6.0" + } + }, + "node_modules/source-map-support/node_modules/source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/sprintf-js": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", + "integrity": "sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==", + "dev": true, + "peer": true + }, + "node_modules/stacktrace-parser": { + "version": "0.1.10", + "resolved": "https://registry.npmjs.org/stacktrace-parser/-/stacktrace-parser-0.1.10.tgz", + "integrity": "sha512-KJP1OCML99+8fhOHxwwzyWrlUuVX5GQ0ZpJTd1DFXhdkrvg1szxfHhawXUZ3g9TkXORQd4/WG68jMlQZ2p8wlg==", + "dependencies": { + "type-fest": "^0.7.1" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/stacktrace-parser/node_modules/type-fest": { + "version": "0.7.1", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.7.1.tgz", + "integrity": "sha512-Ne2YiiGN8bmrmJJEuTWTLJR32nh/JdL1+PSicowtNb0WFpn59GK8/lfD61bVtzguz7b3PBt74nxpv/Pw5po5Rg==", + "engines": { + "node": ">=8" + } + }, + "node_modules/statuses": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.1.tgz", + "integrity": "sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/string_decoder": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", + "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", + "dependencies": { + "safe-buffer": "~5.2.0" + } + }, + "node_modules/string-format": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/string-format/-/string-format-2.0.0.tgz", + "integrity": "sha512-bbEs3scLeYNXLecRRuk6uJxdXUSj6le/8rNPHChIJTn2V79aXVTR1EH2OH5zLKKoz0V02fOUKZZcw01pLUShZA==", + "dev": true, + "peer": true + }, + "node_modules/string-width": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-2.1.1.tgz", + "integrity": "sha512-nOqH59deCq9SRHlxq1Aw85Jnt4w6KvLKqWVik6oA9ZklXLNIOlqg4F2yrT1MVaTjAqvVwdfeZ7w7aCvJD7ugkw==", + "dev": true, + "peer": true, + "dependencies": { + "is-fullwidth-code-point": "^2.0.0", + "strip-ansi": "^4.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/string-width/node_modules/ansi-regex": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-3.0.1.tgz", + "integrity": "sha512-+O9Jct8wf++lXxxFc4hc8LsjaSq0HFzzL7cVsw8pRDIPdjKD2mT4ytDZlLuSBZ4cLKZFXIrMGO7DbQCtMJJMKw==", + "dev": true, + "peer": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/string-width/node_modules/strip-ansi": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-4.0.0.tgz", + "integrity": "sha512-4XaJ2zQdCzROZDivEVIDPkcQn8LMFSa8kj8Gxb/Lnwzv9A8VctNZ+lfivC/sV3ivW8ElJTERXZoPBRrZKkNKow==", + "dev": true, + "peer": true, + "dependencies": { + "ansi-regex": "^3.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/string.prototype.trim": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/string.prototype.trim/-/string.prototype.trim-1.2.8.tgz", + "integrity": "sha512-lfjY4HcixfQXOfaqCvcBuOIapyaroTXhbkfJN3gcB1OtyupngWK4sEET9Knd0cXd28kTUqu/kHoV4HKSJdnjiQ==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.2", + "define-properties": "^1.2.0", + "es-abstract": "^1.22.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/string.prototype.trimend": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/string.prototype.trimend/-/string.prototype.trimend-1.0.7.tgz", + "integrity": "sha512-Ni79DqeB72ZFq1uH/L6zJ+DKZTkOtPIHovb3YZHQViE+HDouuU4mBrLOLDn5Dde3RF8qw5qVETEjhu9locMLvA==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.2", + "define-properties": "^1.2.0", + "es-abstract": "^1.22.1" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/string.prototype.trimstart": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/string.prototype.trimstart/-/string.prototype.trimstart-1.0.7.tgz", + "integrity": "sha512-NGhtDFu3jCEm7B4Fy0DpLewdJQOZcQ0rGbwQ/+stjnrp2i+rlKeCvos9hOIeCmqwratM47OBxY7uFZzjxHXmrg==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.2", + "define-properties": "^1.2.0", + "es-abstract": "^1.22.1" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-hex-prefix": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/strip-hex-prefix/-/strip-hex-prefix-1.0.0.tgz", + "integrity": "sha512-q8d4ue7JGEiVcypji1bALTos+0pWtyGlivAWyPuTkHzuTCJqrK9sWxYQZUq6Nq3cuyv3bm734IhHvHtGGURU6A==", + "dependencies": { + "is-hex-prefixed": "1.0.0" + }, + "engines": { + "node": ">=6.5.0", + "npm": ">=3" + } + }, + "node_modules/strip-json-comments": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", + "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==", + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/supports-color": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", + "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "dependencies": { + "has-flag": "^3.0.0" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/sync-request": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/sync-request/-/sync-request-6.1.0.tgz", + "integrity": "sha512-8fjNkrNlNCrVc/av+Jn+xxqfCjYaBoHqCsDz6mt030UMxJGr+GSfCV1dQt2gRtlL63+VPidwDVLr7V2OcTSdRw==", + "dev": true, + "peer": true, + "dependencies": { + "http-response-object": "^3.0.1", + "sync-rpc": "^1.2.1", + "then-request": "^6.0.0" + }, + "engines": { + "node": ">=8.0.0" + } + }, + "node_modules/sync-rpc": { + "version": "1.3.6", + "resolved": "https://registry.npmjs.org/sync-rpc/-/sync-rpc-1.3.6.tgz", + "integrity": "sha512-J8jTXuZzRlvU7HemDgHi3pGnh/rkoqR/OZSjhTyyZrEkkYQbk7Z33AXp37mkPfPpfdOuj7Ex3H/TJM1z48uPQw==", + "dev": true, + "peer": true, + "dependencies": { + "get-port": "^3.1.0" + } + }, + "node_modules/table": { + "version": "6.8.1", + "resolved": "https://registry.npmjs.org/table/-/table-6.8.1.tgz", + "integrity": "sha512-Y4X9zqrCftUhMeH2EptSSERdVKt/nEdijTOacGD/97EKjhQ/Qs8RTlEGABSJNNN8lac9kheH+af7yAkEWlgneA==", + "dev": true, + "peer": true, + "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" + }, + "engines": { + "node": ">=10.0.0" + } + }, + "node_modules/table-layout": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/table-layout/-/table-layout-1.0.2.tgz", + "integrity": "sha512-qd/R7n5rQTRFi+Zf2sk5XVVd9UQl6ZkduPFC3S7WEGJAmetDTjY3qPN50eSKzwuzEyQKy5TN2TiZdkIjos2L6A==", + "dev": true, + "peer": true, + "dependencies": { + "array-back": "^4.0.1", + "deep-extend": "~0.6.0", + "typical": "^5.2.0", + "wordwrapjs": "^4.0.0" + }, + "engines": { + "node": ">=8.0.0" + } + }, + "node_modules/table-layout/node_modules/array-back": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/array-back/-/array-back-4.0.2.tgz", + "integrity": "sha512-NbdMezxqf94cnNfWLL7V/im0Ub+Anbb0IoZhvzie8+4HJ4nMQuzHuy49FkGYCJK2yAloZ3meiB6AVMClbrI1vg==", + "dev": true, + "peer": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/table-layout/node_modules/typical": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/typical/-/typical-5.2.0.tgz", + "integrity": "sha512-dvdQgNDNJo+8B2uBQoqdb11eUCE1JQXhvjC/CZtgvZseVd5TYMXnq0+vuUemXbd/Se29cTaUuPX3YIc2xgbvIg==", + "dev": true, + "peer": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/table/node_modules/is-fullwidth-code-point": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "dev": true, + "peer": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/table/node_modules/string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dev": true, + "peer": true, + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/then-request": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/then-request/-/then-request-6.0.2.tgz", + "integrity": "sha512-3ZBiG7JvP3wbDzA9iNY5zJQcHL4jn/0BWtXIkagfz7QgOL/LqjCEOBQuJNZfu0XYnv5JhKh+cDxCPM4ILrqruA==", + "dev": true, + "peer": true, + "dependencies": { + "@types/concat-stream": "^1.6.0", + "@types/form-data": "0.0.33", + "@types/node": "^8.0.0", + "@types/qs": "^6.2.31", + "caseless": "~0.12.0", + "concat-stream": "^1.6.0", + "form-data": "^2.2.0", + "http-basic": "^8.1.1", + "http-response-object": "^3.0.1", + "promise": "^8.0.0", + "qs": "^6.4.0" + }, + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/then-request/node_modules/@types/node": { + "version": "8.10.66", + "resolved": "https://registry.npmjs.org/@types/node/-/node-8.10.66.tgz", + "integrity": "sha512-tktOkFUA4kXx2hhhrB8bIFb5TbwzS4uOhKEmwiD+NoiL0qtP2OQ9mFldbgD4dV1djrlBYP6eBuQZiWjuHUpqFw==", + "dev": true, + "peer": true + }, + "node_modules/then-request/node_modules/form-data": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-2.5.1.tgz", + "integrity": "sha512-m21N3WOmEEURgk6B9GLOE4RuWOFf28Lhh9qGYeNlGq4VDXUlJy2th2slBNU8Gp8EzloYZOibZJ7t5ecIrFSjVA==", + "dev": true, + "peer": true, + "dependencies": { + "asynckit": "^0.4.0", + "combined-stream": "^1.0.6", + "mime-types": "^2.1.12" + }, + "engines": { + "node": ">= 0.12" + } + }, + "node_modules/tmp": { + "version": "0.0.33", + "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.0.33.tgz", + "integrity": "sha512-jRCJlojKnZ3addtTOjdIqoRuPEKBvNXcGYqzO6zWZX8KfKEpnGY5jfggJQ3EjKuu8D4bJRr0y+cYJFmYbImXGw==", + "dependencies": { + "os-tmpdir": "~1.0.2" + }, + "engines": { + "node": ">=0.6.0" + } + }, + "node_modules/to-regex-range": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", + "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", + "dependencies": { + "is-number": "^7.0.0" + }, + "engines": { + "node": ">=8.0" + } + }, + "node_modules/toidentifier": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz", + "integrity": "sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==", + "engines": { + "node": ">=0.6" + } + }, + "node_modules/tr46": { + "version": "0.0.3", + "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz", + "integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==", + "dev": true + }, + "node_modules/ts-command-line-args": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/ts-command-line-args/-/ts-command-line-args-2.5.1.tgz", + "integrity": "sha512-H69ZwTw3rFHb5WYpQya40YAX2/w7Ut75uUECbgBIsLmM+BNuYnxsltfyyLMxy6sEeKxgijLTnQtLd0nKd6+IYw==", + "dev": true, + "peer": true, + "dependencies": { + "chalk": "^4.1.0", + "command-line-args": "^5.1.1", + "command-line-usage": "^6.1.0", + "string-format": "^2.0.0" + }, + "bin": { + "write-markdown": "dist/write-markdown.js" + } + }, + "node_modules/ts-command-line-args/node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "peer": true, + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/ts-command-line-args/node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "peer": true, + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/ts-command-line-args/node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "peer": true, + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/ts-command-line-args/node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true, + "peer": true + }, + "node_modules/ts-command-line-args/node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true, + "peer": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/ts-command-line-args/node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "peer": true, + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/ts-essentials": { + "version": "7.0.3", + "resolved": "https://registry.npmjs.org/ts-essentials/-/ts-essentials-7.0.3.tgz", + "integrity": "sha512-8+gr5+lqO3G84KdiTSMRLtuyJ+nTBVRKuCrK4lidMPdVeEp0uqC875uE5NMcaA7YYMN7XsNiFQuMvasF8HT/xQ==", + "dev": true, + "peer": true, + "peerDependencies": { + "typescript": ">=3.7.0" + } + }, + "node_modules/ts-node": { + "version": "10.9.2", + "resolved": "https://registry.npmjs.org/ts-node/-/ts-node-10.9.2.tgz", + "integrity": "sha512-f0FFpIdcHgn8zcPSbf1dRevwt047YMnaiJM3u2w2RewrB+fob/zePZcrOyQoLMMO7aBIddLcQIEK5dYjkLnGrQ==", + "devOptional": true, + "peer": true, + "dependencies": { + "@cspotcode/source-map-support": "^0.8.0", + "@tsconfig/node10": "^1.0.7", + "@tsconfig/node12": "^1.0.7", + "@tsconfig/node14": "^1.0.0", + "@tsconfig/node16": "^1.0.2", + "acorn": "^8.4.1", + "acorn-walk": "^8.1.1", + "arg": "^4.1.0", + "create-require": "^1.1.0", + "diff": "^4.0.1", + "make-error": "^1.1.1", + "v8-compile-cache-lib": "^3.0.1", + "yn": "3.1.1" + }, + "bin": { + "ts-node": "dist/bin.js", + "ts-node-cwd": "dist/bin-cwd.js", + "ts-node-esm": "dist/bin-esm.js", + "ts-node-script": "dist/bin-script.js", + "ts-node-transpile-only": "dist/bin-transpile.js", + "ts-script": "dist/bin-script-deprecated.js" + }, + "peerDependencies": { + "@swc/core": ">=1.2.50", + "@swc/wasm": ">=1.2.50", + "@types/node": "*", + "typescript": ">=2.7" + }, + "peerDependenciesMeta": { + "@swc/core": { + "optional": true + }, + "@swc/wasm": { + "optional": true + } + } + }, + "node_modules/ts-node/node_modules/diff": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/diff/-/diff-4.0.2.tgz", + "integrity": "sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==", + "devOptional": true, + "peer": true, + "engines": { + "node": ">=0.3.1" + } + }, + "node_modules/tslib": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.4.0.tgz", + "integrity": "sha512-d6xOpEDfsi2CZVlPQzGeux8XMwLT9hssAsaPYExaQMuYskwb+x1x7J371tWlbBdWHroy99KnVB6qIkUbs5X3UQ==", + "dev": true + }, + "node_modules/tsort": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/tsort/-/tsort-0.0.1.tgz", + "integrity": "sha512-Tyrf5mxF8Ofs1tNoxA13lFeZ2Zrbd6cKbuH3V+MQ5sb6DtBj5FjrXVsRWT8YvNAQTqNoz66dz1WsbigI22aEnw==" + }, + "node_modules/tweetnacl": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/tweetnacl/-/tweetnacl-1.0.3.tgz", + "integrity": "sha512-6rt+RN7aOi1nGMyC4Xa5DdYiukl2UWCbcJft7YhxReBGQD7OAM8Pbxw6YMo4r2diNEA8FEmu32YOn9rhaiE5yw==" + }, + "node_modules/tweetnacl-util": { + "version": "0.15.1", + "resolved": "https://registry.npmjs.org/tweetnacl-util/-/tweetnacl-util-0.15.1.tgz", + "integrity": "sha512-RKJBIj8lySrShN4w6i/BonWp2Z/uxwC3h4y7xsRrpP59ZboCd0GpEVsOnMDYLMmKBpYhb5TgHzZXy7wTfYFBRw==" + }, + "node_modules/type-check": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.3.2.tgz", + "integrity": "sha512-ZCmOJdvOWDBYJlzAoFkC+Q0+bUyEOS1ltgp1MGU03fqHG+dbi9tBFU2Rd9QKiDZFAYrhPh2JUf7rZRIuHRKtOg==", + "dev": true, + "peer": true, + "dependencies": { + "prelude-ls": "~1.1.2" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/type-detect": { + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/type-detect/-/type-detect-4.0.8.tgz", + "integrity": "sha512-0fr/mIH1dlO+x7TlcMy+bIDqKPsw/70tVyeHW787goQjhmqaZe10uwLujubK9q9Lg6Fiho1KUKDYz0Z7k7g5/g==", + "dev": true, + "peer": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/type-fest": { + "version": "0.21.3", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.21.3.tgz", + "integrity": "sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w==", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/typechain": { + "version": "8.3.2", + "resolved": "https://registry.npmjs.org/typechain/-/typechain-8.3.2.tgz", + "integrity": "sha512-x/sQYr5w9K7yv3es7jo4KTX05CLxOf7TRWwoHlrjRh8H82G64g+k7VuWPJlgMo6qrjfCulOdfBjiaDtmhFYD/Q==", + "dev": true, + "peer": true, + "dependencies": { + "@types/prettier": "^2.1.1", + "debug": "^4.3.1", + "fs-extra": "^7.0.0", + "glob": "7.1.7", + "js-sha3": "^0.8.0", + "lodash": "^4.17.15", + "mkdirp": "^1.0.4", + "prettier": "^2.3.1", + "ts-command-line-args": "^2.2.0", + "ts-essentials": "^7.0.1" + }, + "bin": { + "typechain": "dist/cli/cli.js" + }, + "peerDependencies": { + "typescript": ">=4.3.0" + } + }, + "node_modules/typechain/node_modules/fs-extra": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-7.0.1.tgz", + "integrity": "sha512-YJDaCJZEnBmcbw13fvdAM9AwNOJwOzrE4pqMqBq5nFiEqXUqHwlK4B+3pUw6JNvfSPtX05xFHtYy/1ni01eGCw==", + "dev": true, + "peer": true, + "dependencies": { + "graceful-fs": "^4.1.2", + "jsonfile": "^4.0.0", + "universalify": "^0.1.0" + }, + "engines": { + "node": ">=6 <7 || >=8" + } + }, + "node_modules/typechain/node_modules/glob": { + "version": "7.1.7", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.7.tgz", + "integrity": "sha512-OvD9ENzPLbegENnYP5UUfJIirTg4+XwMWGaQfQTY0JenxNvvIKP3U3/tAQSPIu/lHxXYSZmpXlUHeqAIdKzBLQ==", + "dev": true, + "peer": true, + "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" + }, + "engines": { + "node": "*" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/typechain/node_modules/jsonfile": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-4.0.0.tgz", + "integrity": "sha512-m6F1R3z8jjlf2imQHS2Qez5sjKWQzbuuhuJ/FKYFRZvPE3PuHcSMVZzfsLhGVOkfd20obL5SWEBew5ShlquNxg==", + "dev": true, + "peer": true, + "optionalDependencies": { + "graceful-fs": "^4.1.6" + } + }, + "node_modules/typechain/node_modules/mkdirp": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.4.tgz", + "integrity": "sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==", + "dev": true, + "peer": true, + "bin": { + "mkdirp": "bin/cmd.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/typechain/node_modules/universalify": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/universalify/-/universalify-0.1.2.tgz", + "integrity": "sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg==", + "dev": true, + "peer": true, + "engines": { + "node": ">= 4.0.0" + } + }, + "node_modules/typed-array-buffer": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/typed-array-buffer/-/typed-array-buffer-1.0.0.tgz", + "integrity": "sha512-Y8KTSIglk9OZEr8zywiIHG/kmQ7KWyjseXs1CbSo8vC42w7hg2HgYTxSWwP0+is7bWDc1H+Fo026CpHFwm8tkw==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.2", + "get-intrinsic": "^1.2.1", + "is-typed-array": "^1.1.10" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/typed-array-byte-length": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/typed-array-byte-length/-/typed-array-byte-length-1.0.0.tgz", + "integrity": "sha512-Or/+kvLxNpeQ9DtSydonMxCx+9ZXOswtwJn17SNLvhptaXYDJvkFFP5zbfU/uLmvnBJlI4yrnXRxpdWH/M5tNA==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.2", + "for-each": "^0.3.3", + "has-proto": "^1.0.1", + "is-typed-array": "^1.1.10" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/typed-array-byte-offset": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/typed-array-byte-offset/-/typed-array-byte-offset-1.0.0.tgz", + "integrity": "sha512-RD97prjEt9EL8YgAgpOkf3O4IF9lhJFr9g0htQkm0rchFp/Vx7LW5Q8fSXXub7BXAODyUQohRMyOc3faCPd0hg==", + "dev": true, + "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" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/typed-array-length": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/typed-array-length/-/typed-array-length-1.0.4.tgz", + "integrity": "sha512-KjZypGq+I/H7HI5HlOoGHkWUUGq+Q0TPhQurLbyrVrvnKTBgzLhIJ7j6J/XTQOi0d1RjyZ0wdas8bKs2p0x3Ng==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.2", + "for-each": "^0.3.3", + "is-typed-array": "^1.1.9" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/typedarray": { + "version": "0.0.6", + "resolved": "https://registry.npmjs.org/typedarray/-/typedarray-0.0.6.tgz", + "integrity": "sha512-/aCDEGatGvZ2BIk+HmLf4ifCJFwvKFNb9/JeZPMulfgFracn9QFcAf5GO8B/mweUjSoblS5In0cWhqpfs/5PQA==", + "dev": true, + "peer": true + }, + "node_modules/typescript": { + "version": "5.3.3", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.3.3.tgz", + "integrity": "sha512-pXWcraxM0uxAS+tN0AG/BF2TyqmHO014Z070UsJ+pFvYuRSq8KH8DmWpnbXe0pEPDHXZV3FcAbJkijJ5oNEnWw==", + "devOptional": true, + "peer": true, + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, + "engines": { + "node": ">=14.17" + } + }, + "node_modules/typical": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/typical/-/typical-4.0.0.tgz", + "integrity": "sha512-VAH4IvQ7BDFYglMd7BPRDfLgxZZX4O4TFcRDA6EN5X7erNJJq+McIEp8np9aVtxrCJ6qx4GTYVfOWNjcqwZgRw==", + "dev": true, + "peer": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/uglify-js": { + "version": "3.17.4", + "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-3.17.4.tgz", + "integrity": "sha512-T9q82TJI9e/C1TAxYvfb16xO120tMVFZrGA3f9/P4424DNu6ypK103y0GPFVa17yotwSyZW5iYXgjYHkGrJW/g==", + "dev": true, + "optional": true, + "peer": true, + "bin": { + "uglifyjs": "bin/uglifyjs" + }, + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/unbox-primitive": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/unbox-primitive/-/unbox-primitive-1.0.2.tgz", + "integrity": "sha512-61pPlCD9h51VoreyJ0BReideM3MDKMKnh6+V9L08331ipq6Q8OFXZYiqP6n/tbHx4s5I9uRhcye6BrbkizkBDw==", + "dev": true, + "dependencies": { + "call-bind": "^1.0.2", + "has-bigints": "^1.0.2", + "has-symbols": "^1.0.3", + "which-boxed-primitive": "^1.0.2" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/undici": { + "version": "5.28.2", + "resolved": "https://registry.npmjs.org/undici/-/undici-5.28.2.tgz", + "integrity": "sha512-wh1pHJHnUeQV5Xa8/kyQhO7WFa8M34l026L5P/+2TYiakvGy5Rdc8jWZVyG7ieht/0WgJLEd3kcU5gKx+6GC8w==", + "dependencies": { + "@fastify/busboy": "^2.0.0" + }, + "engines": { + "node": ">=14.0" + } + }, + "node_modules/undici-types": { + "version": "5.26.5", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-5.26.5.tgz", + "integrity": "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==" + }, + "node_modules/unfetch": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/unfetch/-/unfetch-4.2.0.tgz", + "integrity": "sha512-F9p7yYCn6cIW9El1zi0HI6vqpeIvBsr3dSuRO6Xuppb1u5rXpCPmMvLSyECLhybr9isec8Ohl0hPekMVrEinDA==", + "dev": true + }, + "node_modules/universalify": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.1.tgz", + "integrity": "sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==", + "dev": true, + "peer": true, + "engines": { + "node": ">= 10.0.0" + } + }, + "node_modules/unpipe": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", + "integrity": "sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/uri-js": { + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", + "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==", + "dev": true, + "peer": true, + "dependencies": { + "punycode": "^2.1.0" + } + }, + "node_modules/utf8": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/utf8/-/utf8-3.0.0.tgz", + "integrity": "sha512-E8VjFIQ/TyQgp+TZfS6l8yp/xWppSAHzidGiRrqe4bK4XP9pTRyKFgGJpO3SN7zdX4DeomTrwaseCHovfpFcqQ==", + "dev": true, + "peer": true + }, + "node_modules/util-deprecate": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", + "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==" + }, + "node_modules/uuid": { + "version": "8.3.2", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz", + "integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==", + "bin": { + "uuid": "dist/bin/uuid" + } + }, + "node_modules/v8-compile-cache-lib": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/v8-compile-cache-lib/-/v8-compile-cache-lib-3.0.1.tgz", + "integrity": "sha512-wa7YjyUGfNZngI/vtK0UHAN+lgDCxBPCylVXGp0zu59Fz5aiGtNXaq3DhIov063MorB+VfufLh3JlF2KdTK3xg==", + "devOptional": true, + "peer": true + }, + "node_modules/web3-utils": { + "version": "1.10.3", + "resolved": "https://registry.npmjs.org/web3-utils/-/web3-utils-1.10.3.tgz", + "integrity": "sha512-OqcUrEE16fDBbGoQtZXWdavsPzbGIDc5v3VrRTZ0XrIpefC/viZ1ZU9bGEemazyS0catk/3rkOOxpzTfY+XsyQ==", + "dev": true, + "peer": true, + "dependencies": { + "@ethereumjs/util": "^8.1.0", + "bn.js": "^5.2.1", + "ethereum-bloom-filters": "^1.0.6", + "ethereum-cryptography": "^2.1.2", + "ethjs-unit": "0.1.6", + "number-to-bn": "1.7.0", + "randombytes": "^2.1.0", + "utf8": "3.0.0" + }, + "engines": { + "node": ">=8.0.0" + } + }, + "node_modules/web3-utils/node_modules/@noble/curves": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@noble/curves/-/curves-1.1.0.tgz", + "integrity": "sha512-091oBExgENk/kGj3AZmtBDMpxQPDtxQABR2B9lb1JbVTs6ytdzZNwvhxQ4MWasRNEzlbEH8jCWFCwhF/Obj5AA==", + "dev": true, + "peer": true, + "dependencies": { + "@noble/hashes": "1.3.1" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/web3-utils/node_modules/@noble/hashes": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/@noble/hashes/-/hashes-1.3.1.tgz", + "integrity": "sha512-EbqwksQwz9xDRGfDST86whPBgM65E0OH/pCgqW0GBVzO22bNE+NuIbeTb714+IfSjU3aRk47EUvXIb5bTsenKA==", + "dev": true, + "peer": true, + "engines": { + "node": ">= 16" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/web3-utils/node_modules/ethereum-cryptography": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ethereum-cryptography/-/ethereum-cryptography-2.1.2.tgz", + "integrity": "sha512-Z5Ba0T0ImZ8fqXrJbpHcbpAvIswRte2wGNR/KePnu8GbbvgJ47lMxT/ZZPG6i9Jaht4azPDop4HaM00J0J59ug==", + "dev": true, + "peer": true, + "dependencies": { + "@noble/curves": "1.1.0", + "@noble/hashes": "1.3.1", + "@scure/bip32": "1.3.1", + "@scure/bip39": "1.2.1" + } + }, + "node_modules/webidl-conversions": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz", + "integrity": "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==", + "dev": true + }, + "node_modules/whatwg-url": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz", + "integrity": "sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==", + "dev": true, + "dependencies": { + "tr46": "~0.0.3", + "webidl-conversions": "^3.0.0" + } + }, + "node_modules/which": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/which/-/which-1.3.1.tgz", + "integrity": "sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==", + "dev": true, + "peer": true, + "dependencies": { + "isexe": "^2.0.0" + }, + "bin": { + "which": "bin/which" + } + }, + "node_modules/which-boxed-primitive": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/which-boxed-primitive/-/which-boxed-primitive-1.0.2.tgz", + "integrity": "sha512-bwZdv0AKLpplFY2KZRX6TvyuN7ojjr7lwkg6ml0roIy9YeuSr7JS372qlNW18UQYzgYK9ziGcerWqZOmEn9VNg==", + "dev": true, + "dependencies": { + "is-bigint": "^1.0.1", + "is-boolean-object": "^1.1.0", + "is-number-object": "^1.0.4", + "is-string": "^1.0.5", + "is-symbol": "^1.0.3" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/which-typed-array": { + "version": "1.1.13", + "resolved": "https://registry.npmjs.org/which-typed-array/-/which-typed-array-1.1.13.tgz", + "integrity": "sha512-P5Nra0qjSncduVPEAr7xhoF5guty49ArDTwzJ/yNuPIbZppyRxFQsRCWrocxIY+CnMVG+qfbU2FmDKyvSGClow==", + "dev": true, + "dependencies": { + "available-typed-arrays": "^1.0.5", + "call-bind": "^1.0.4", + "for-each": "^0.3.3", + "gopd": "^1.0.1", + "has-tostringtag": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/word-wrap": { + "version": "1.2.5", + "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.5.tgz", + "integrity": "sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==", + "dev": true, + "peer": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/wordwrap": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/wordwrap/-/wordwrap-1.0.0.tgz", + "integrity": "sha512-gvVzJFlPycKc5dZN4yPkP8w7Dc37BtP1yczEneOb4uq34pXZcvrtRTmWV8W+Ume+XCxKgbjM+nevkyFPMybd4Q==", + "dev": true, + "peer": true + }, + "node_modules/wordwrapjs": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/wordwrapjs/-/wordwrapjs-4.0.1.tgz", + "integrity": "sha512-kKlNACbvHrkpIw6oPeYDSmdCTu2hdMHoyXLTcUKala++lx5Y+wjJ/e474Jqv5abnVmwxw08DiTuHmw69lJGksA==", + "dev": true, + "peer": true, + "dependencies": { + "reduce-flatten": "^2.0.0", + "typical": "^5.2.0" + }, + "engines": { + "node": ">=8.0.0" + } + }, + "node_modules/wordwrapjs/node_modules/typical": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/typical/-/typical-5.2.0.tgz", + "integrity": "sha512-dvdQgNDNJo+8B2uBQoqdb11eUCE1JQXhvjC/CZtgvZseVd5TYMXnq0+vuUemXbd/Se29cTaUuPX3YIc2xgbvIg==", + "dev": true, + "peer": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/workerpool": { + "version": "6.2.1", + "resolved": "https://registry.npmjs.org/workerpool/-/workerpool-6.2.1.tgz", + "integrity": "sha512-ILEIE97kDZvF9Wb9f6h5aXK4swSlKGUcOEGiIYb2OOu/IrDU9iwj0fD//SsA6E5ibwJxpEvhullJY4Sl4GcpAw==" + }, + "node_modules/wrap-ansi": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", + "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", + "dependencies": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, + "node_modules/wrap-ansi/node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/wrap-ansi/node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/wrap-ansi/node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==" + }, + "node_modules/wrap-ansi/node_modules/is-fullwidth-code-point": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "engines": { + "node": ">=8" + } + }, + "node_modules/wrap-ansi/node_modules/string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/wrappy": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", + "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==" + }, + "node_modules/ws": { + "version": "8.5.0", + "resolved": "https://registry.npmjs.org/ws/-/ws-8.5.0.tgz", + "integrity": "sha512-BWX0SWVgLPzYwF8lTzEy1egjhS4S4OEAHfsO8o65WOVsrnSRGaSiUaa9e0ggGlkMTtBlmOpEXiie9RUcBO86qg==", + "dev": true, + "engines": { + "node": ">=10.0.0" + }, + "peerDependencies": { + "bufferutil": "^4.0.1", + "utf-8-validate": "^5.0.2" + }, + "peerDependenciesMeta": { + "bufferutil": { + "optional": true + }, + "utf-8-validate": { + "optional": true + } + } + }, + "node_modules/y18n": { + "version": "5.0.8", + "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", + "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==", + "engines": { + "node": ">=10" + } + }, + "node_modules/yallist": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz", + "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==" + }, + "node_modules/yargs": { + "version": "16.2.0", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-16.2.0.tgz", + "integrity": "sha512-D1mvvtDG0L5ft/jGWkLpG1+m0eQxOfaBvTNELraWj22wSVUMWxZUvYgJYcKh6jGGIkJFhH4IZPQhR4TKpc8mBw==", + "dependencies": { + "cliui": "^7.0.2", + "escalade": "^3.1.1", + "get-caller-file": "^2.0.5", + "require-directory": "^2.1.1", + "string-width": "^4.2.0", + "y18n": "^5.0.5", + "yargs-parser": "^20.2.2" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/yargs-parser": { + "version": "20.2.4", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-20.2.4.tgz", + "integrity": "sha512-WOkpgNhPTlE73h4VFAFsOnomJVaovO8VqLDzy5saChRBFQFBoMYirowyW+Q9HB4HFF4Z7VZTiG3iSzJJA29yRA==", + "engines": { + "node": ">=10" + } + }, + "node_modules/yargs-unparser": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/yargs-unparser/-/yargs-unparser-2.0.0.tgz", + "integrity": "sha512-7pRTIA9Qc1caZ0bZ6RYRGbHJthJWuakf+WmHK0rVeLkNrrGhfoabBNdue6kdINI6r4if7ocq9aD/n7xwKOdzOA==", + "dependencies": { + "camelcase": "^6.0.0", + "decamelize": "^4.0.0", + "flat": "^5.0.2", + "is-plain-obj": "^2.1.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/yargs/node_modules/is-fullwidth-code-point": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "engines": { + "node": ">=8" + } + }, + "node_modules/yargs/node_modules/string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/yn": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/yn/-/yn-3.1.1.tgz", + "integrity": "sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q==", + "devOptional": true, + "peer": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/yocto-queue": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", + "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + } + } +} diff --git a/package.json b/package.json new file mode 100644 index 0000000..51bff4e --- /dev/null +++ b/package.json @@ -0,0 +1,28 @@ +{ + "name": "options-token", + "version": "1.0.0", + "description": "An options token representing the right to purchase the underlying token at an oracle-specified rate. It's similar to a call option but with a variable strike price that's always at a certain discount to the market price. It also has no expiry date.", + "main": "index.js", + "directories": { + "lib": "lib", + "test": "test" + }, + "scripts": { + "test": "echo \"Error: no test specified\" && exit 1" + }, + "author": "", + "license": "AGPL-3.0", + "devDependencies": { + "@nomicfoundation/hardhat-ethers": "^3.0.5", + "@nomicfoundation/hardhat-foundry": "^1.1.1", + "@nomicfoundation/hardhat-toolbox": "^4.0.0", + "@openzeppelin/hardhat-upgrades": "^3.0.1", + "dotenv": "^16.3.1", + "ethers": "^6.9.2", + "hardhat": "^2.19.4", + "hardhat-contract-sizer": "^2.10.0" + }, + "dependencies": { + "@nomicfoundation/hardhat-network-helpers": "^1.0.10" + } +} diff --git a/remappings.txt b/remappings.txt new file mode 100644 index 0000000..faaa3df --- /dev/null +++ b/remappings.txt @@ -0,0 +1,6 @@ +create3-factory/=lib/create3-factory/src/ +v3-core/=lib/v3-core/contracts/ +oz-upgradeable/=lib/openzeppelin-contracts-upgradeable/contracts/ +oz/=lib/openzeppelin-contracts/contracts/ +vault/=lib/vault-v2/src + diff --git a/script/Deploy.s.sol b/script/Deploy.s.sol deleted file mode 100644 index b577ed7..0000000 --- a/script/Deploy.s.sol +++ /dev/null @@ -1,60 +0,0 @@ -// SPDX-License-Identifier: AGPL-3.0 -pragma solidity ^0.8.13; - -import {ERC20} from "solmate/tokens/ERC20.sol"; - -import {OptionsToken} from "../src/OptionsToken.sol"; -import {CREATE3Script} from "./base/CREATE3Script.sol"; -import {BalancerOracle} from "../src/oracles/BalancerOracle.sol"; -import {IERC20Mintable} from "../src/interfaces/IERC20Mintable.sol"; -import {IBalancerTwapOracle} from "../src/interfaces/IBalancerTwapOracle.sol"; - -contract DeployScript is CREATE3Script { - constructor() CREATE3Script(vm.envString("VERSION")) {} - - function run() public returns (OptionsToken optionsToken, BalancerOracle oracle) { - uint256 deployerPrivateKey = uint256(vm.envBytes32("PRIVATE_KEY")); - - vm.startBroadcast(deployerPrivateKey); - - { - IBalancerTwapOracle balancerPriceOracle = IBalancerTwapOracle(vm.envAddress("BALANCER_POOL")); - address owner = vm.envAddress("OWNER"); - uint16 multiplier = uint16(vm.envUint("ORACLE_MULTIPLIER")); - uint56 secs = uint56(vm.envUint("ORACLE_SECS")); - uint56 ago = uint56(vm.envUint("ORACLE_AGO")); - uint128 minPrice = uint128(vm.envUint("ORACLE_MIN_PRICE")); - - oracle = BalancerOracle( - create3.deploy( - getCreate3ContractSalt("BalancerOracle"), - bytes.concat( - type(BalancerOracle).creationCode, - abi.encode(balancerPriceOracle, owner, multiplier, secs, ago, minPrice) - ) - ) - ); - } - - { - string memory name = vm.envString("OT_NAME"); - string memory symbol = vm.envString("OT_SYMBOL"); - address owner = vm.envAddress("OWNER"); - address tokenAdmin = getCreate3Contract("TokenAdmin"); - ERC20 paymentToken = ERC20(vm.envAddress("OT_PAYMENT_TOKEN")); - IERC20Mintable underlyingToken = IERC20Mintable(getCreate3Contract("TimelessToken")); - address treasury = vm.envAddress("TREASURY"); - bytes memory constructorParams = - abi.encode(name, symbol, owner, tokenAdmin, paymentToken, underlyingToken, oracle, treasury); - - optionsToken = OptionsToken( - create3.deploy( - getCreate3ContractSalt("OptionsToken"), - bytes.concat(type(OptionsToken).creationCode, constructorParams) - ) - ); - } - - vm.stopBroadcast(); - } -} diff --git a/script/base/CREATE3Script.sol b/script/base/CREATE3Script.sol deleted file mode 100644 index f058af1..0000000 --- a/script/base/CREATE3Script.sol +++ /dev/null @@ -1,27 +0,0 @@ -// SPDX-License-Identifier: AGPL-3.0 -pragma solidity ^0.8.13; - -import {CREATE3Factory} from "create3-factory/CREATE3Factory.sol"; - -import "forge-std/Script.sol"; - -abstract contract CREATE3Script is Script { - CREATE3Factory internal constant create3 = CREATE3Factory(0x9fBB3DF7C40Da2e5A0dE984fFE2CCB7C47cd0ABf); - - string internal version; - - constructor(string memory version_) { - version = version_; - } - - function getCreate3Contract(string memory name) internal view virtual returns (address) { - uint256 deployerPrivateKey = uint256(vm.envBytes32("PRIVATE_KEY")); - address deployer = vm.addr(deployerPrivateKey); - - return create3.getDeployed(deployer, getCreate3ContractSalt(name)); - } - - function getCreate3ContractSalt(string memory name) internal view virtual returns (bytes32) { - return keccak256(bytes(string.concat(name, "-v", version))); - } -} diff --git a/scripts/00_deploySwapper.ts b/scripts/00_deploySwapper.ts new file mode 100644 index 0000000..60c015a --- /dev/null +++ b/scripts/00_deploySwapper.ts @@ -0,0 +1,65 @@ +import { ethers, upgrades } from "hardhat"; +import config from './config.json'; + +async function main() { + const contractsToDeploy = config.CONTRACTS_TO_DEPLOY; + const paymentToken = config.OT_PAYMENT_TOKEN; + const underlyingToken = config.OT_UNDERLYING_TOKEN; + const veloRouter = config.VELO_ROUTER; + const path1 = [{ + from: paymentToken, + to: underlyingToken, + stable: false, + }]; + + const path2 = [{ + from: underlyingToken, + to: paymentToken, + stable: false, + }]; + + const strategists: string[] = [ + "0x1E71AEE6081f62053123140aacC7a06021D77348", // bongo + "0x81876677843D00a7D792E1617459aC2E93202576", // degenicus + "0x4C3490dF15edFa178333445ce568EC6D99b5d71c", // eidolon + "0xb26cd6633db6b0c9ae919049c1437271ae496d15", // zokunei + "0x60BC5E0440C867eEb4CbcE84bB1123fad2b262B1", // goober + ]; + const multisigRoles: string[] = [ + "0x90c75c11735A7eeeD06E993fC7aF6838d86A1Ba7", // super admin + "0xC17DfA7Eb4300871D5f022c107E07F98c750472e", // admin + "0x30d65Ae22BBbe44208Dd8964DDE31Def0Fc1B9ee", // guardian + ]; + + // ReaperSwapper + let swapper; + if(contractsToDeploy.includes("Swapper")){ + const Swapper = await ethers.getContractFactory("ReaperSwapper"); + const initializerArguments = [ + strategists, + multisigRoles[2], + multisigRoles[0], + ]; + swapper = await upgrades.deployProxy( + Swapper, + initializerArguments, + { kind: "uups", timeout: 0 }, + ); + + await swapper.waitForDeployment(); + console.log("Swapper deployed to:", await swapper.getAddress()); + } + else { + swapper = await ethers.getContractAt("ReaperSwapper", config.SWAPPER); + } + + await swapper.updateVeloSwapPath(paymentToken, underlyingToken, veloRouter, path1); + await swapper.updateVeloSwapPath(underlyingToken, paymentToken, veloRouter, path2); +} + +// We recommend this pattern to be able to use async/await everywhere +// and properly handle errors. +main().catch((error) => { + console.error(error); + process.exitCode = 1; + }); \ No newline at end of file diff --git a/scripts/01_deployOptionsTokenInfra.ts b/scripts/01_deployOptionsTokenInfra.ts new file mode 100644 index 0000000..6e2f92e --- /dev/null +++ b/scripts/01_deployOptionsTokenInfra.ts @@ -0,0 +1,159 @@ +import { ethers, upgrades } from "hardhat"; +import { getImplementationAddress } from '@openzeppelin/upgrades-core'; +import config from './config.json'; + + + +async function main() { + const swapper = await ethers.getContractAt("ReaperSwapper", config.SWAPPER); + const contractsToDeploy = config.CONTRACTS_TO_DEPLOY; + const thenaPair = config.ORACLE_SOURCE; + const targetToken = config.OT_UNDERLYING_TOKEN; + const owner = config.OWNER; + const secs = config.ORACLE_SECS; + const minPrice = config.ORACLE_MIN_PRICE; + + const veloRouter = config.VELO_ROUTER; + const addressProvider = config.ADDRESS_PROVIDER; + + //Oracle + let oracle; + if(contractsToDeploy.includes("ThenaOracle")){ + oracle = await ethers.deployContract( + "ThenaOracle", + [thenaPair, targetToken, owner, secs, minPrice] + ); + await oracle.waitForDeployment(); + console.log(`Oracle deployed to: ${await oracle.getAddress()}`); + } + else{ + try{ + oracle = await ethers.getContractAt("ThenaOracle", config.ORACLE); + } + catch(error){ + console.log("ThenaOracle NOT available due to lack of configuration"); + } + } + + // OptionsToken + let optionsToken; + if(contractsToDeploy.includes("OptionsToken")){ + const tokenName = config.OT_NAME; + const symbol = config.OT_SYMBOL; + const tokenAdmin = config.OT_TOKEN_ADMIN; + const OptionsToken = await ethers.getContractFactory("OptionsToken"); + optionsToken = await upgrades.deployProxy( + OptionsToken, + [tokenName, symbol, tokenAdmin], + { kind: "uups", initializer: "initialize" } + ); + + await optionsToken.waitForDeployment(); + console.log(`OptionsToken deployed to: ${await optionsToken.getAddress()}`); + console.log(`Implementation: ${await getImplementationAddress(ethers.provider, await optionsToken.getAddress())}`); + } + else{ + try{ + optionsToken = await ethers.getContractAt("OptionsToken", config.OPTIONS_TOKEN); + } + catch(error){ + console.log("OptionsToken NOT available due to lack of configuration"); + } + } + + const swapProps = { + swapper: await swapper.getAddress(), + exchangeAddress: veloRouter, + exchangeTypes: 2, /* VeloSolid */ + maxSwapSlippage: 500 /* 5% */ + }; + + // Exercise + let exercise; + if(contractsToDeploy.includes("DiscountExercise")){ + const paymentToken = config.OT_PAYMENT_TOKEN; + const multiplier = config.MULTIPLIER; + const feeRecipients = String(config.FEE_RECIPIENTS).split(","); + const feeBps = String(config.FEE_BPS).split(","); + const instantExitFee = config.INSTANT_EXIT_FEE; + const minAmountToTriggerSwap = config.MIN_AMOUNT_TO_TRIGGER_SWAP; + + exercise = await ethers.deployContract( + "DiscountExercise", + [ + await optionsToken.getAddress(), + owner, + paymentToken, + targetToken, + await oracle.getAddress(), + multiplier, + instantExitFee, + minAmountToTriggerSwap, + feeRecipients, + feeBps, + swapProps + ] + ); + await exercise.waitForDeployment(); + console.log(`Exercise deployed to: ${await exercise.getAddress()}`); + + // Set exercise + const exerciseAddress = await exercise.getAddress(); + await optionsToken.setExerciseContract(exerciseAddress, true); + console.log(`Exercise set to: ${exerciseAddress}`); + } + else{ + try{ + exercise = await ethers.getContractAt("DiscountExercise", config.DISCOUNT_EXERCISE); + } + catch(error){ + console.log("DiscountExercise NOT available due to lack of configuration"); + } + + } + + + // OptionsCompounder + let optionsCompounder; + const strats = String(config.STRATS).split(","); + if(contractsToDeploy.includes("OptionsCompounder")){ + + const OptionsCompounder = await ethers.getContractFactory("OptionsCompounder"); + + // console.log("Proxy deployment: ", [optionsToken, addressProvider, swapper, swapProps, oracle]); + console.log("Proxy deployment: ", [await optionsToken.getAddress(), addressProvider, await swapper.getAddress(), swapProps, await oracle.getAddress(), strats]); + + optionsCompounder = await upgrades.deployProxy( + OptionsCompounder, + [await optionsToken.getAddress(), addressProvider, await swapper.getAddress(), swapProps, await oracle.getAddress(), strats], + { kind: "uups", initializer: "initialize" } + ); + + // const optionsCompounder = await upgrades.deployProxy( + // OptionsCompounder, + // [optionsToken, addressProvider, swapper, swapProps, oracle], + // { kind: "uups", initializer: "initialize" } + // ); + + await optionsCompounder.waitForDeployment(); + console.log(`OptionsCompounder deployed to: ${await optionsCompounder.getAddress()}`); + console.log(`Implementation: ${await getImplementationAddress(ethers.provider, await optionsCompounder.getAddress())}`); + } + else{ + try{ + optionsCompounder = await ethers.getContractAt("OptionsCompounder", config.OPTIONS_COMPOUNDER); + await optionsCompounder.setStrats(strats); + } + catch(error){ + console.log("OptionsCompounder NOT available due to lack of configuration"); + } + } + +} + +// We recommend this pattern to be able to use async/await everywhere +// and properly handle errors. +main().catch((error) => { + console.error(error); + process.exitCode = 1; +}); diff --git a/scripts/config.json b/scripts/config.json new file mode 100644 index 0000000..c6a6bc3 --- /dev/null +++ b/scripts/config.json @@ -0,0 +1,32 @@ +{ + "VERSION": "1.1.0", + "OWNER": "0xF29dA3595351dBFd0D647857C46F8D63Fc2e68C5", + "ORACLE_SOURCE": "0x1349e6986084b65708D68A72C8EfCBD7Cf4D3FB2", + "ORACLE_SECS": 1800, + "ORACLE_MIN_PRICE": 10000000, + "OT_NAME": "ICL Call Option Token", + "OT_SYMBOL": "oICL", + "OT_PAYMENT_TOKEN": "0xDfc7C877a950e49D2610114102175A06C2e3167a", + "OT_UNDERLYING_TOKEN": "0x95177295a394f2b9b04545fff58f4af0673e839d", + "OT_TOKEN_ADMIN": "0xF29dA3595351dBFd0D647857C46F8D63Fc2e68C5", + "VELO_ROUTER": "0x3a63171DD9BebF4D07BC782FECC7eb0b890C2A45", + "ADDRESS_PROVIDER": "0xEDc83309549e36f3c7FD8c2C5C54B4c8e5FA00FC", + "MULTIPLIER": 5000, + "FEE_RECIPIENTS": [ + "0xd93E25A8B1D645b15f8c736E1419b4819Ff9e6EF" + ], + "FEE_BPS": [ + 10000 + ], + "STRATS": [ + "0xF29dA3595351dBFd0D647857C46F8D63Fc2e68C5" + ], + "INSTANT_EXIT_FEE": 1000, + "MIN_AMOUNT_TO_TRIGGER_SWAP": 1e15, + "CONTRACTS_TO_DEPLOY": [], + "SWAPPER": "0x63D170618A8Ed1987F3CA6391b5e2F6a4554Cf53", + "DISCOUNT_EXERCISE": "0xcb727532e24dFe22E74D3892b998f5e915676Da8", + "OPTIONS_TOKEN": "0x3B6eA0fA8A487c90007ce120a83920fd52b06f6D", + "OPTIONS_COMPOUNDER": "0xf6cf2065C35595c12B532e54ACDe5A4597e32e6e", + "ORACLE": "0xDaA2c821428f62e1B08009a69CE824253CCEE5f9" +} \ No newline at end of file diff --git a/src/OptionsCompounder.sol b/src/OptionsCompounder.sol new file mode 100644 index 0000000..0ef67c0 --- /dev/null +++ b/src/OptionsCompounder.sol @@ -0,0 +1,420 @@ +// SPDX-License-Identifier: BUSL-1.1 + +pragma solidity ^0.8.0; + +/* Imports */ +import {IFlashLoanReceiver} from "./interfaces/IFlashLoanReceiver.sol"; +import {ILendingPoolAddressesProvider} from "./interfaces/ILendingPoolAddressesProvider.sol"; +import {ILendingPool} from "./interfaces/ILendingPool.sol"; +import {IOracle} from "./interfaces/IOracle.sol"; +import {IOptionsToken} from "./interfaces/IOptionsToken.sol"; +import {DiscountExerciseParams, DiscountExercise} from "./exercise/DiscountExercise.sol"; +import {IERC20} from "oz/token/ERC20/IERC20.sol"; +import {FixedPointMathLib} from "solmate/utils/FixedPointMathLib.sol"; +import {OwnableUpgradeable} from "oz-upgradeable/access/OwnableUpgradeable.sol"; +import {UUPSUpgradeable} from "oz-upgradeable/proxy/utils/UUPSUpgradeable.sol"; +import {SafeERC20} from "oz/token/ERC20/utils/SafeERC20.sol"; +import {ExchangeType, SwapProps, SwapHelper} from "./helpers/SwapHelper.sol"; +import "./interfaces/IOptionsCompounder.sol"; + +/** + * @title Consumes options tokens, exercise them with flashloaned asset and converts gain to strategy want token + * @author Eidolon, xRave110 + * @dev Abstract contract which shall be inherited by the strategy + */ +contract OptionsCompounder is IFlashLoanReceiver, OwnableUpgradeable, UUPSUpgradeable, SwapHelper { + using FixedPointMathLib for uint256; + using SafeERC20 for IERC20; + + /* Internal struct */ + struct FlashloanParams { + uint256 optionsAmount; + address exerciserContract; + address sender; + uint256 initialBalance; + uint256 minPaymentAmount; + } + + /* Modifier */ + modifier onlyStrat() { + if (!_isStratAvailable(msg.sender)) { + revert OptionsCompounder__OnlyStratAllowed(); + } + _; + } + + /* Constants */ + uint8 constant MAX_NR_OF_FLASHLOAN_ASSETS = 1; + + uint256 public constant UPGRADE_TIMELOCK = 48 hours; + uint256 public constant FUTURE_NEXT_PROPOSAL_TIME = 365 days * 100; + + /* Storages */ + ILendingPoolAddressesProvider private addressProvider; + ILendingPool private lendingPool; + bool private flashloanFinished; + IOracle private oracle; + IOptionsToken public optionsToken; + + uint256 public upgradeProposalTime; + address public nextImplementation; + address[] private strats; + + /* Events */ + event OTokenCompounded(uint256 indexed gainInPayment, uint256 indexed returned); + + /* Modifiers */ + + /// @custom:oz-upgrades-unsafe-allow constructor + constructor() { + _disableInitializers(); + } + + /** + * @notice Initializes params + * @dev Replaces constructor due to upgradeable nature of the contract. Can be executed only once at init. + * @param _optionsToken - option token address which allows to redeem underlying token via operation "exercise" + * @param _addressProvider - address lending pool address provider - necessary for flashloan operations + * @param _swapProps - swap properites for all swaps in the contract + * @param _oracle - oracles used in all swaps in the contract + * @param _strats - list of strategies used to call harvestOTokens() + * + */ + function initialize( + address _optionsToken, + address _addressProvider, + SwapProps memory _swapProps, + IOracle _oracle, + address[] memory _strats + ) public initializer { + __Ownable_init(); + _setOptionsToken(_optionsToken); + _setSwapProps(_swapProps); + _setOracle(_oracle); + _setStrats(_strats); + flashloanFinished = true; + _setAddressProvider(_addressProvider); + __UUPSUpgradeable_init(); + _clearUpgradeCooldown(); + } + + /** + * Setters ********************************** + */ + /** + * @notice Sets option token address + * @dev Can be executed only by admins + * @param _optionsToken - address of option token contract + */ + function setOptionsToken(address _optionsToken) external onlyOwner { + _setOptionsToken(_optionsToken); + } + + function _setOptionsToken(address _optionsToken) internal { + if (_optionsToken == address(0)) { + revert OptionsCompounder__ParamHasAddressZero(); + } + optionsToken = IOptionsToken(_optionsToken); + } + + function setSwapProps(SwapProps memory _swapProps) external override onlyOwner { + _setSwapProps(_swapProps); + } + + function setOracle(IOracle _oracle) external onlyOwner { + _setOracle(_oracle); + } + + function _setOracle(IOracle _oracle) internal { + if (address(_oracle) == address(0)) { + revert OptionsCompounder__ParamHasAddressZero(); + } + oracle = _oracle; + } + + function setAddressProvider(address _addressProvider) external onlyOwner { + _setAddressProvider(_addressProvider); + } + + function _setAddressProvider(address _addressProvider) internal { + if (_addressProvider == address(0)) { + revert OptionsCompounder__ParamHasAddressZero(); + } + addressProvider = ILendingPoolAddressesProvider(_addressProvider); + lendingPool = ILendingPool(addressProvider.getLendingPool()); + } + + function setStrats(address[] memory _strats) external onlyOwner { + _setStrats(_strats); + } + + function _setStrats(address[] memory _strats) internal { + _deleteStrats(); + for (uint256 idx = 0; idx < _strats.length; idx++) { + _addStrat(_strats[idx]); + } + } + + function addStrat(address _strat) external onlyOwner { + _addStrat(_strat); + } + + /** + * @dev Function will be used sporadically with number of strategies less than 10, so no need to add any gas optimization + */ + function _addStrat(address _strat) internal { + if (_strat == address(0)) { + revert OptionsCompounder__ParamHasAddressZero(); + } + if (!_isStratAvailable(_strat)) { + strats.push(_strat); + } + } + + function _deleteStrats() internal { + if (strats.length != 0) { + delete strats; + } + } + + /** + * @notice Function initiates flashloan to get assets for exercising options. + * @dev Can be executed only by keeper role. Reentrance protected. + * @param amount - amount of option tokens to exercise + * @param exerciseContract - address of exercise contract (DiscountContract) + * @param minWantAmount - minimal amount of want when the flashloan is considered as profitable + */ + function harvestOTokens(uint256 amount, address exerciseContract, uint256 minWantAmount) external onlyStrat { + _harvestOTokens(amount, exerciseContract, minWantAmount); + } + + /** + * @notice Function initiates flashloan to get assets for exercising options. + * @dev Can be executed only by keeper role. Reentrance protected. + * @param amount - amount of option tokens to exercise + * @param exerciseContract - address of exercise contract (DiscountContract) + * @param minPaymentAmount - minimal amount of want when the flashloan is considered as profitable + */ + function _harvestOTokens(uint256 amount, address exerciseContract, uint256 minPaymentAmount) private { + /* Check exercise contract validity */ + if (optionsToken.isExerciseContract(exerciseContract) == false) { + revert OptionsCompounder__NotExerciseContract(); + } + /* Reentrance protection */ + if (flashloanFinished == false) { + revert OptionsCompounder__FlashloanNotFinished(); + } + if (minPaymentAmount == 0) { + revert OptionsCompounder__WrongMinPaymentAmount(); + } + /* Locals */ + IERC20 paymentToken = DiscountExercise(exerciseContract).paymentToken(); + + address[] memory assets = new address[](1); + assets[0] = address(paymentToken); + + uint256[] memory amounts = new uint256[](1); + amounts[0] = DiscountExercise(exerciseContract).getPaymentAmount(amount); + + // 0 = no debt, 1 = stable, 2 = variable + uint256[] memory modes = new uint256[](1); + modes[0] = 0; + + /* necesary params used during flashloan execution */ + bytes memory params = + abi.encode(FlashloanParams(amount, exerciseContract, msg.sender, paymentToken.balanceOf(address(this)), minPaymentAmount)); + flashloanFinished = false; + lendingPool.flashLoan( + address(this), // receiver + assets, + amounts, + modes, + address(this), // onBehalf + params, + 0 // referal code + ); + } + + /** + * @notice Exercise option tokens with flash loaned token and compound rewards + * in underlying tokens to stratefy want token + * @dev Function is called after this contract has received the flash loaned amount + * @param assets - list of assets flash loaned (only one asset allowed in this case) + * @param amounts - list of amounts flash loaned (only one amount allowed in this case) + * @param premiums - list of premiums for flash loaned assets (only one premium allowed in this case) + * @param params - encoded data about options amount, exercise contract address, initial balance and minimal want amount + * @return bool - value that returns whether flashloan operation went well + */ + function executeOperation(address[] calldata assets, uint256[] calldata amounts, uint256[] calldata premiums, address, bytes calldata params) + external + override + returns (bool) + { + if (flashloanFinished != false || msg.sender != address(lendingPool)) { + revert OptionsCompounder__FlashloanNotTriggered(); + } + if (assets.length > MAX_NR_OF_FLASHLOAN_ASSETS || amounts.length > MAX_NR_OF_FLASHLOAN_ASSETS || premiums.length > MAX_NR_OF_FLASHLOAN_ASSETS) + { + revert OptionsCompounder__TooMuchAssetsLoaned(); + } + /* Later the gain can be local variable */ + _exerciseOptionAndReturnDebt(assets[0], amounts[0], premiums[0], params); + flashloanFinished = true; + return true; + } + + /** + * @dev Private function that helps to execute flashloan and makes it more modular + * Emits event with gain from the option exercise after repayment of all debt from flashloan + * and amount of repaid assets + * @param asset - list of assets flash loaned (only one asset allowed in this case) + * @param amount - list of amounts flash loaned (only one amount allowed in this case) + * @param premium - list of premiums for flash loaned assets (only one premium allowed in this case) + * @param params - encoded data about options amount, exercise contract address, initial balance and minimal want amount + */ + function _exerciseOptionAndReturnDebt(address asset, uint256 amount, uint256 premium, bytes calldata params) private { + FlashloanParams memory flashloanParams = abi.decode(params, (FlashloanParams)); + uint256 assetBalance = 0; + uint256 minAmountOut = 0; + + /* Get underlying and payment tokens to make sure there is no change between + harvest and excersice */ + IERC20 underlyingToken = DiscountExercise(flashloanParams.exerciserContract).underlyingToken(); + { + IERC20 paymentToken = DiscountExercise(flashloanParams.exerciserContract).paymentToken(); + + /* Asset and paymentToken should be the same addresses */ + if (asset != address(paymentToken)) { + revert OptionsCompounder__AssetNotEqualToPaymentToken(); + } + } + { + IERC20(address(optionsToken)).safeTransferFrom(flashloanParams.sender, address(this), flashloanParams.optionsAmount); + bytes memory exerciseParams = + abi.encode(DiscountExerciseParams({maxPaymentAmount: amount, deadline: block.timestamp, isInstantExit: false})); + if (underlyingToken.balanceOf(flashloanParams.exerciserContract) < flashloanParams.optionsAmount) { + revert OptionsCompounder__NotEnoughUnderlyingTokens(); + } + /* Approve spending payment token */ + IERC20(asset).approve(flashloanParams.exerciserContract, amount); + /* Exercise in order to get underlying token */ + optionsToken.exercise(flashloanParams.optionsAmount, address(this), flashloanParams.exerciserContract, exerciseParams); + + /* Approve spending payment token to 0 for safety */ + IERC20(asset).approve(flashloanParams.exerciserContract, 0); + } + + { + uint256 balanceOfUnderlyingToken = 0; + uint256 swapAmountOut = 0; + balanceOfUnderlyingToken = underlyingToken.balanceOf(address(this)); + minAmountOut = _getMinAmountOutData(balanceOfUnderlyingToken, swapProps.maxSwapSlippage, address(oracle)); + + /* Approve the underlying token to make swap */ + underlyingToken.approve(swapProps.swapper, balanceOfUnderlyingToken); + + /* Swap underlying token to payment token (asset) */ + swapAmountOut = _generalSwap( + swapProps.exchangeTypes, address(underlyingToken), asset, balanceOfUnderlyingToken, minAmountOut, swapProps.exchangeAddress + ); + + if (swapAmountOut == 0) { + revert OptionsCompounder__AmountOutIsZero(); + } + + /* Approve the underlying token to 0 for safety */ + underlyingToken.approve(swapProps.swapper, 0); + } + + /* Calculate profit and revert if it is not profitable */ + { + uint256 gainInPaymentToken = 0; + + uint256 totalAmountToPay = amount + premium; + assetBalance = IERC20(asset).balanceOf(address(this)); + + if ( + ( + (assetBalance < flashloanParams.initialBalance) + || (assetBalance - flashloanParams.initialBalance) < (totalAmountToPay + flashloanParams.minPaymentAmount) + ) + ) { + revert OptionsCompounder__FlashloanNotProfitableEnough(); + } + + /* Protected against underflows by statement above */ + gainInPaymentToken = assetBalance - totalAmountToPay - flashloanParams.initialBalance; + + /* Approve lending pool to spend borrowed tokens + premium */ + IERC20(asset).approve(address(lendingPool), totalAmountToPay); + IERC20(asset).safeTransfer(flashloanParams.sender, gainInPaymentToken); + + emit OTokenCompounded(gainInPaymentToken, totalAmountToPay); + } + } + + /** + * @dev This function must be called prior to upgrading the implementation. + * It's required to wait UPGRADE_TIMELOCK seconds before executing the upgrade. + */ + function initiateUpgradeCooldown(address _nextImplementation) external onlyOwner { + upgradeProposalTime = block.timestamp; + nextImplementation = _nextImplementation; + } + + /** + * @dev This function is called: + * - in initialize() + * - as part of a successful upgrade + * - manually to clear the upgrade cooldown. + */ + function _clearUpgradeCooldown() internal { + upgradeProposalTime = block.timestamp + FUTURE_NEXT_PROPOSAL_TIME; + } + + function clearUpgradeCooldown() external onlyOwner { + _clearUpgradeCooldown(); + } + + /** + * @dev This function must be overriden simply for access control purposes. + * Only the owner can upgrade the implementation once the timelock + * has passed. + */ + function _authorizeUpgrade(address _nextImplementation) internal override onlyOwner { + require(upgradeProposalTime + UPGRADE_TIMELOCK < block.timestamp, "Upgrade cooldown not initiated or still ongoing"); + require(_nextImplementation == nextImplementation, "Incorrect implementation"); + _clearUpgradeCooldown(); + } + + /** + * Getters ********************************** + */ + function isStratAvailable(address strat) external view returns (bool) { + return _isStratAvailable(strat); + } + + function _isStratAvailable(address strat) internal view returns (bool) { + bool isStrat = false; + address[] memory _strats = strats; + for (uint256 idx = 0; idx < _strats.length; idx++) { + if (strat == _strats[idx]) { + isStrat = true; + break; + } + } + return isStrat; + } + + function getStrats() external view returns (address[] memory) { + return strats; + } + + function ADDRESSES_PROVIDER() external view returns (ILendingPoolAddressesProvider) { + return addressProvider; + } + + function LENDING_POOL() external view returns (ILendingPool) { + return lendingPool; + } +} diff --git a/src/OptionsToken.sol b/src/OptionsToken.sol index 062bcc2..a19e8ee 100644 --- a/src/OptionsToken.sol +++ b/src/OptionsToken.sol @@ -1,90 +1,70 @@ // SPDX-License-Identifier: AGPL-3.0 pragma solidity ^0.8.13; -import {Owned} from "solmate/auth/Owned.sol"; -import {ERC20} from "solmate/tokens/ERC20.sol"; -import {SafeTransferLib} from "solmate/utils/SafeTransferLib.sol"; -import {FixedPointMathLib} from "solmate/utils/FixedPointMathLib.sol"; - +import {OwnableUpgradeable} from "oz-upgradeable/access/OwnableUpgradeable.sol"; +import {ERC20Upgradeable} from "oz-upgradeable/token/ERC20/ERC20Upgradeable.sol"; +import {UUPSUpgradeable} from "oz-upgradeable/proxy/utils/UUPSUpgradeable.sol"; +import {PausableUpgradeable} from "oz-upgradeable/security/PausableUpgradeable.sol"; +import {IOptionsToken} from "./interfaces/IOptionsToken.sol"; import {IOracle} from "./interfaces/IOracle.sol"; -import {IERC20Mintable} from "./interfaces/IERC20Mintable.sol"; +import {IExercise} from "./interfaces/IExercise.sol"; /// @title Options Token -/// @author zefram.eth -/// @notice Options token representing the right to purchase the underlying token -/// at an oracle-specified rate. Similar to call options but with a variable strike -/// price that's always at a certain discount to the market price. -/// @dev Assumes the underlying token and the payment token both use 18 decimals. -contract OptionsToken is ERC20, Owned, IERC20Mintable { - /// ----------------------------------------------------------------------- - /// Library usage - /// ----------------------------------------------------------------------- - - using SafeTransferLib for ERC20; - using FixedPointMathLib for uint256; - +/// @author Eidolon & lookee +/// @notice Options token representing the right to perform an advantageous action, +/// such as purchasing the underlying token at a discount to the market price. +contract OptionsToken is IOptionsToken, ERC20Upgradeable, OwnableUpgradeable, UUPSUpgradeable, PausableUpgradeable { /// ----------------------------------------------------------------------- /// Errors /// ----------------------------------------------------------------------- - error OptionsToken__PastDeadline(); error OptionsToken__NotTokenAdmin(); - error OptionsToken__SlippageTooHigh(); + error OptionsToken__NotExerciseContract(); + error Upgradeable__Unauthorized(); /// ----------------------------------------------------------------------- /// Events /// ----------------------------------------------------------------------- - event Exercise(address indexed sender, address indexed recipient, uint256 amount, uint256 paymentAmount); + event Exercise(address indexed sender, address indexed recipient, uint256 amount, address data0, uint256 data1, uint256 data2); event SetOracle(IOracle indexed newOracle); - event SetTreasury(address indexed newTreasury); + event SetExerciseContract(address indexed _address, bool _isExercise); /// ----------------------------------------------------------------------- - /// Immutable parameters + /// Constant parameters /// ----------------------------------------------------------------------- - /// @notice The contract that has the right to mint options tokens - address public immutable tokenAdmin; - - /// @notice The token paid by the options token holder during redemption - ERC20 public immutable paymentToken; - - /// @notice The underlying token purchased during redemption - IERC20Mintable public immutable underlyingToken; + uint256 public constant UPGRADE_TIMELOCK = 48 hours; + uint256 public constant FUTURE_NEXT_PROPOSAL_TIME = 365 days * 100; /// ----------------------------------------------------------------------- /// Storage variables /// ----------------------------------------------------------------------- - /// @notice The oracle contract that provides the current price to purchase - /// the underlying token while exercising options (the strike price) - IOracle public oracle; + /// @notice The contract that has the right to mint options tokens + address public tokenAdmin; + + mapping(address => bool) public isExerciseContract; + uint256 public upgradeProposalTime; + address public nextImplementation; - /// @notice The treasury address which receives tokens paid during redemption - address public treasury; + /// @custom:oz-upgrades-unsafe-allow constructor + constructor() { + _disableInitializers(); + } /// ----------------------------------------------------------------------- - /// Constructor + /// Initializer /// ----------------------------------------------------------------------- - constructor( - string memory name_, - string memory symbol_, - address owner_, - address tokenAdmin_, - ERC20 paymentToken_, - IERC20Mintable underlyingToken_, - IOracle oracle_, - address treasury_ - ) ERC20(name_, symbol_, 18) Owned(owner_) { + function initialize(string memory name_, string memory symbol_, address tokenAdmin_) external initializer { + __UUPSUpgradeable_init(); + __ERC20_init(name_, symbol_); + __Ownable_init(); + __Pausable_init(); tokenAdmin = tokenAdmin_; - paymentToken = paymentToken_; - underlyingToken = underlyingToken_; - oracle = oracle_; - treasury = treasury_; - emit SetOracle(oracle_); - emit SetTreasury(treasury_); + _clearUpgradeCooldown(); } /// ----------------------------------------------------------------------- @@ -112,83 +92,116 @@ contract OptionsToken is ERC20, Owned, IERC20Mintable { _mint(to, amount); } - /// @notice Exercises options tokens to purchase the underlying tokens. - /// @dev The options tokens are not burnt but sent to address(0) to avoid messing up the - /// inflation schedule. - /// The oracle may revert if it cannot give a secure result. - /// @param amount The amount of options tokens to exercise - /// @param maxPaymentAmount The maximum acceptable amount to pay. Used for slippage protection. - /// @param recipient The recipient of the purchased underlying tokens - /// @return paymentAmount The amount paid to the treasury to purchase the underlying tokens - function exercise(uint256 amount, uint256 maxPaymentAmount, address recipient) - external - virtual - returns (uint256 paymentAmount) - { - return _exercise(amount, maxPaymentAmount, recipient); - } - - /// @notice Exercises options tokens to purchase the underlying tokens. - /// @dev The options tokens are not burnt but sent to address(0) to avoid messing up the - /// inflation schedule. - /// The oracle may revert if it cannot give a secure result. + /// @notice Exercises options tokens, burning them and giving the reward to the recipient. /// @param amount The amount of options tokens to exercise - /// @param maxPaymentAmount The maximum acceptable amount to pay. Used for slippage protection. - /// @param recipient The recipient of the purchased underlying tokens - /// @param deadline The Unix timestamp (in seconds) after which the call will revert - /// @return paymentAmount The amount paid to the treasury to purchase the underlying tokens - function exercise(uint256 amount, uint256 maxPaymentAmount, address recipient, uint256 deadline) + /// @param recipient The recipient of the reward + /// @param option The address of the Exercise contract with the redemption logic + /// @param params Extra parameters to be used by the exercise function + /// @return paymentAmount token amount paid for exercising + /// @return data0 address data to return by different exerciser contracts + /// @return data1 integer data to return by different exerciser contracts + /// @return data2 additional integer data to return by different exerciser contracts + function exercise(uint256 amount, address recipient, address option, bytes calldata params) external virtual - returns (uint256 paymentAmount) + whenNotPaused + returns ( + uint256 paymentAmount, + address, + uint256, + uint256 // misc data + ) { - if (block.timestamp > deadline) revert OptionsToken__PastDeadline(); - return _exercise(amount, maxPaymentAmount, recipient); + return _exercise(amount, recipient, option, params); } /// ----------------------------------------------------------------------- /// Owner functions /// ----------------------------------------------------------------------- - /// @notice Sets the oracle contract. Only callable by the owner. - /// @param oracle_ The new oracle contract - function setOracle(IOracle oracle_) external onlyOwner { - oracle = oracle_; - emit SetOracle(oracle_); + /// @notice Adds a new Exercise contract to the available options. + /// @param _address Address of the Exercise contract, that implements BaseExercise. + /// @param _isExercise Whether oToken holders should be allowed to exercise using this option. + function setExerciseContract(address _address, bool _isExercise) external onlyOwner { + isExerciseContract[_address] = _isExercise; + emit SetExerciseContract(_address, _isExercise); + } + + /// @notice Pauses functionality related to exercises of contracts. + function pause() external onlyOwner { + _pause(); } - /// @notice Sets the treasury address. Only callable by the owner. - /// @param treasury_ The new treasury address - function setTreasury(address treasury_) external onlyOwner { - treasury = treasury_; - emit SetTreasury(treasury_); + /// @notice Unpauses functionality related to exercises of contracts. + function unpause() external onlyOwner { + _unpause(); } /// ----------------------------------------------------------------------- /// Internal functions /// ----------------------------------------------------------------------- - function _exercise(uint256 amount, uint256 maxPaymentAmount, address recipient) + function _exercise(uint256 amount, address recipient, address option, bytes calldata params) internal virtual - returns (uint256 paymentAmount) + returns ( + uint256 paymentAmount, + address data0, + uint256 data1, + uint256 data2 // misc data + ) { // skip if amount is zero - if (amount == 0) return 0; + if (amount == 0) return (0, address(0), 0, 0); - // transfer options tokens from msg.sender to address(0) - // we transfer instead of burn because TokenAdmin cares about totalSupply - // which we don't want to change in order to follow the emission schedule - transfer(address(0), amount); + // revert if the exercise contract is not whitelisted + if (!isExerciseContract[option]) revert OptionsToken__NotExerciseContract(); - // transfer payment tokens from msg.sender to the treasury - paymentAmount = amount.mulWadUp(oracle.getPrice()); - if (paymentAmount > maxPaymentAmount) revert OptionsToken__SlippageTooHigh(); - paymentToken.safeTransferFrom(msg.sender, treasury, paymentAmount); + // burn options tokens + _burn(msg.sender, amount); - // mint underlying tokens to recipient - underlyingToken.mint(recipient, amount); + // give rewards to recipient + (paymentAmount, data0, data1, data2) = IExercise(option).exercise(msg.sender, amount, recipient, params); + + // emit event + emit Exercise(msg.sender, recipient, amount, data0, data1, data2); + } + + /// ----------------------------------------------------------------------- + /// UUPS functions + /// ----------------------------------------------------------------------- + + /** + * @dev This function must be called prior to upgrading the implementation. + * It's required to wait UPGRADE_TIMELOCK seconds before executing the upgrade. + */ + function initiateUpgradeCooldown(address _nextImplementation) external onlyOwner { + upgradeProposalTime = block.timestamp; + nextImplementation = _nextImplementation; + } + + /** + * @dev This function is called: + * - in initialize() + * - as part of a successful upgrade + * - manually to clear the upgrade cooldown. + */ + function _clearUpgradeCooldown() internal { + upgradeProposalTime = block.timestamp + FUTURE_NEXT_PROPOSAL_TIME; + } + + function clearUpgradeCooldown() external onlyOwner { + _clearUpgradeCooldown(); + } - emit Exercise(msg.sender, recipient, amount, paymentAmount); + /** + * @dev This function must be overriden simply for access control purposes. + * Only the owner can upgrade the implementation once the timelock + * has passed. + */ + function _authorizeUpgrade(address _nextImplementation) internal override onlyOwner { + require(upgradeProposalTime + UPGRADE_TIMELOCK < block.timestamp, "Upgrade cooldown not initiated or still ongoing"); + require(_nextImplementation == nextImplementation, "Incorrect implementation"); + _clearUpgradeCooldown(); } } diff --git a/src/exercise/BaseExercise.sol b/src/exercise/BaseExercise.sol new file mode 100644 index 0000000..9de0cc6 --- /dev/null +++ b/src/exercise/BaseExercise.sol @@ -0,0 +1,96 @@ +// SPDX-License-Identifier: AGPL-3.0 +pragma solidity ^0.8.13; + +import {IExercise} from "../interfaces/IExercise.sol"; +import {IOptionsToken} from "../OptionsToken.sol"; +import {SafeERC20} from "oz/token/ERC20/utils/SafeERC20.sol"; +import {IERC20} from "oz/token/ERC20/IERC20.sol"; +import {FixedPointMathLib} from "solmate/utils/FixedPointMathLib.sol"; +import {Owned} from "solmate/auth/Owned.sol"; + +abstract contract BaseExercise is IExercise, Owned { + using SafeERC20 for IERC20; + using FixedPointMathLib for uint256; + + error Exercise__NotOToken(); + error Exercise__feeArrayLengthMismatch(); + error Exercise__InvalidFeeAmounts(); + + event SetFees(address[] feeRecipients, uint256[] feeBPS); + event DistributeFees(address[] feeRecipients, uint256[] feeBPS, uint256 totalAmount); + + uint256 public constant FEE_DENOMINATOR = 10_000; + + IOptionsToken public immutable oToken; + + /// @notice The fee addresses which receive any tokens paid during redemption + address[] public feeRecipients; + + /// @notice The fee percentage in basis points, feeRecipients[n] receives + /// feeBPS[n] * fee / 10_000 in fees + uint256[] public feeBPS; + + constructor(IOptionsToken _oToken, address[] memory _feeRecipients, uint256[] memory _feeBPS) { + oToken = _oToken; + _setFees(_feeRecipients, _feeBPS); + } + + modifier onlyOToken() { + if (msg.sender != address(oToken)) revert Exercise__NotOToken(); + _; + } + + /// @notice Called by the oToken and handles rewarding logic for the user. + /// @dev *Must* have onlyOToken modifier. + /// @param from Wallet that is exercising tokens + /// @param amount Amount of tokens being exercised + /// @param recipient Wallet that will receive the rewards for exercising the oTokens + /// @param params Extraneous parameters that the function may use - abi.encoded struct + /// @dev Additional returns are reserved for future use + function exercise(address from, uint256 amount, address recipient, bytes memory params) + external + virtual + returns (uint256 paymentAmount, address, uint256, uint256); + + function setFees(address[] memory _feeRecipients, uint256[] memory _feeBPS) external onlyOwner { + _setFees(_feeRecipients, _feeBPS); + } + + function _setFees(address[] memory _feeRecipients, uint256[] memory _feeBPS) internal { + if (_feeRecipients.length != _feeBPS.length) revert Exercise__feeArrayLengthMismatch(); + uint256 totalBPS = 0; + for (uint256 i = 0; i < _feeBPS.length; i++) { + totalBPS += _feeBPS[i]; + } + if (totalBPS != FEE_DENOMINATOR) revert Exercise__InvalidFeeAmounts(); + feeRecipients = _feeRecipients; + feeBPS = _feeBPS; + emit SetFees(_feeRecipients, _feeBPS); + } + + /// @notice Distributes fees to the fee recipients from a token holder who has approved + /// @dev Sends the residual amount to the last fee recipient to avoid rounding errors + function distributeFeesFrom(uint256 totalAmount, IERC20 token, address from) internal virtual { + uint256 remaining = totalAmount; + for (uint256 i = 0; i < feeRecipients.length - 1; i++) { + uint256 feeAmount = totalAmount * feeBPS[i] / FEE_DENOMINATOR; + token.safeTransferFrom(from, feeRecipients[i], feeAmount); + remaining -= feeAmount; + } + token.safeTransferFrom(from, feeRecipients[feeRecipients.length - 1], remaining); + emit DistributeFees(feeRecipients, feeBPS, totalAmount); + } + + /// @notice Distributes fees to the fee recipients from token balance of exercise contract + /// @dev Sends the residual amount to the last fee recipient to avoid rounding errors + function distributeFees(uint256 totalAmount, IERC20 token) internal virtual { + uint256 remaining = totalAmount; + for (uint256 i = 0; i < feeRecipients.length - 1; i++) { + uint256 feeAmount = totalAmount * feeBPS[i] / FEE_DENOMINATOR; + token.safeTransfer(feeRecipients[i], feeAmount); + remaining -= feeAmount; + } + token.safeTransfer(feeRecipients[feeRecipients.length - 1], remaining); + emit DistributeFees(feeRecipients, feeBPS, totalAmount); + } +} diff --git a/src/exercise/DiscountExercise.sol b/src/exercise/DiscountExercise.sol new file mode 100644 index 0000000..1643a47 --- /dev/null +++ b/src/exercise/DiscountExercise.sol @@ -0,0 +1,291 @@ +// SPDX-License-Identifier: AGPL-3.0 +pragma solidity ^0.8.13; + +import {Owned} from "solmate/auth/Owned.sol"; +import {IERC20} from "oz/token/ERC20/IERC20.sol"; +import {Pausable} from "oz/security/Pausable.sol"; +import {SafeERC20} from "oz/token/ERC20/utils/SafeERC20.sol"; +import {FixedPointMathLib} from "solmate/utils/FixedPointMathLib.sol"; + +import {BaseExercise} from "../exercise/BaseExercise.sol"; +import {IOracle} from "../interfaces/IOracle.sol"; +import {OptionsToken} from "../OptionsToken.sol"; + +import {ExchangeType, SwapProps, SwapHelper} from "../helpers/SwapHelper.sol"; + +struct DiscountExerciseParams { + uint256 maxPaymentAmount; + uint256 deadline; + bool isInstantExit; +} + +/// @title Options Token Exercise Contract +/// @author @lookee, @eidolon +/// @notice Contract that allows the holder of options tokens to exercise them, +/// in this case, by purchasing the underlying token at a discount to the market price. +/// @dev Assumes the underlying token and the payment token both use 18 decimals. +contract DiscountExercise is BaseExercise, SwapHelper, Pausable { + /// Library usage + using SafeERC20 for IERC20; + using FixedPointMathLib for uint256; + + /// Errors + error Exercise__SlippageTooHigh(); + error Exercise__PastDeadline(); + error Exercise__MultiplierOutOfRange(); + error Exercise__InvalidOracle(); + error Exercise__FeeGreaterThanMax(); + error Exercise__AmountOutIsZero(); + error Exercise__ZapMultiplierIncompatible(); + + /// Events + event Exercised(address indexed sender, address indexed recipient, uint256 amount, uint256 paymentAmount); + event SetOracle(IOracle indexed newOracle); + event SetTreasury(address indexed newTreasury); + event SetMultiplier(uint256 indexed newMultiplier); + event Claimed(uint256 indexed amount); + event SetInstantFee(uint256 indexed instantFee); + event SetMinAmountToTrigger(uint256 minAmountToTrigger); + + /// Constants + /// Immutable parameters + + /// @notice The token paid by the options token holder during redemption + IERC20 public immutable paymentToken; + + /// @notice The underlying token purchased during redemption + IERC20 public immutable underlyingToken; + + /// Storage variables + + /// @notice The oracle contract that provides the current price to purchase + /// the underlying token while exercising options (the strike price) + IOracle public oracle; + + /// @notice The multiplier applied to the TWAP value. Encodes the discount of + /// the options token. Uses 4 decimals. + uint256 public multiplier; + + /// @notice The amount of payment tokens the user can claim + /// Used when the contract does not have enough tokens to pay the user + mapping(address => uint256) public credit; + + /// @notice The fee amount gathered in the contract to be swapped and distributed + uint256 private feeAmount; + + /// @notice Minimal trigger to swap, if the trigger is not reached then feeAmount counts the ammount to swap and distribute + uint256 public minAmountToTriggerSwap; + + /// @notice configurable parameter that determines what is the fee for zap (instant exit) feature + uint256 public instantExitFee; + + constructor( + OptionsToken oToken_, + address owner_, + IERC20 paymentToken_, + IERC20 underlyingToken_, + IOracle oracle_, + uint256 multiplier_, + uint256 instantExitFee_, + uint256 minAmountToTriggerSwap_, + address[] memory feeRecipients_, + uint256[] memory feeBPS_, + SwapProps memory swapProps_ + ) BaseExercise(oToken_, feeRecipients_, feeBPS_) Owned(owner_) SwapHelper() { + paymentToken = paymentToken_; + underlyingToken = underlyingToken_; + + _setSwapProps(swapProps_); + _setOracle(oracle_); + _setMultiplier(multiplier_); + _setInstantExitFee(instantExitFee_); + _setMinAmountToTriggerSwap(minAmountToTriggerSwap_); + + emit SetOracle(oracle_); + } + + /// External functions + + /// @notice Exercises options tokens to purchase the underlying tokens. + /// @dev The oracle may revert if it cannot give a secure result. + /// @param from The user that is exercising their options tokens + /// @param amount The amount of options tokens to exercise + /// @param recipient The recipient of the purchased underlying tokens + /// @param params Extra parameters to be used by the exercise function + function exercise(address from, uint256 amount, address recipient, bytes memory params) + external + virtual + override + onlyOToken + whenNotPaused + returns (uint256 paymentAmount, address, uint256, uint256) + { + DiscountExerciseParams memory _params = abi.decode(params, (DiscountExerciseParams)); + if (_params.isInstantExit) { + return _zap(from, amount, recipient, _params); + } else { + return _redeem(from, amount, recipient, _params); + } + } + + /// @notice Transfers not claimed tokens to the sender. + /// @dev When contract doesn't have funds during exercise, this function allows to claim the tokens once contract is funded + /// @param to Destination address for token transfer + function claim(address to) external whenNotPaused { + uint256 amount = credit[msg.sender]; + if (amount == 0) return; + credit[msg.sender] = 0; + underlyingToken.safeTransfer(to, amount); + emit Claimed(amount); + } + + /// Owner functions + function setSwapProps(SwapProps memory _swapProps) external virtual override onlyOwner { + _setSwapProps(_swapProps); + } + + /// @notice Sets the oracle contract. Only callable by the owner. + /// @param oracle_ The new oracle contract + function setOracle(IOracle oracle_) external onlyOwner { + _setOracle(oracle_); + } + + function _setOracle(IOracle oracle_) internal { + (address paymentToken_, address underlyingToken_) = oracle_.getTokens(); + if (paymentToken_ != address(paymentToken) || underlyingToken_ != address(underlyingToken)) { + revert Exercise__InvalidOracle(); + } + oracle = oracle_; + emit SetOracle(oracle_); + } + + /// @notice Sets the discount multiplier. + /// @param multiplier_ The new multiplier + function setMultiplier(uint256 multiplier_) external onlyOwner { + _setMultiplier(multiplier_); + } + + function _setMultiplier(uint256 multiplier_) internal { + if ( + multiplier_ > BPS_DENOM * 2 // over 200% + || multiplier_ < BPS_DENOM / 10 // under 10% + ) revert Exercise__MultiplierOutOfRange(); + multiplier = multiplier_; + emit SetMultiplier(multiplier_); + } + + /// @notice Sets the discount instantExitFee. + /// @param _instantExitFee The new instantExitFee + function setInstantExitFee(uint256 _instantExitFee) external onlyOwner { + _setInstantExitFee(_instantExitFee); + } + + function _setInstantExitFee(uint256 _instantExitFee) internal { + if (_instantExitFee > BPS_DENOM) { + revert Exercise__FeeGreaterThanMax(); + } + instantExitFee = _instantExitFee; + emit SetInstantFee(_instantExitFee); + } + + /// @notice Sets the discount minAmountToTriggerSwap. + /// @param _minAmountToTriggerSwap The new minAmountToTriggerSwap + function setMinAmountToTriggerSwap(uint256 _minAmountToTriggerSwap) external onlyOwner { + _setMinAmountToTriggerSwap(_minAmountToTriggerSwap); + } + + function _setMinAmountToTriggerSwap(uint256 _minAmountToTriggerSwap) internal { + minAmountToTriggerSwap = _minAmountToTriggerSwap; + emit SetMinAmountToTrigger(_minAmountToTriggerSwap); + } + + function pause() external onlyOwner { + _pause(); + } + + function unpause() external onlyOwner { + _unpause(); + } + + /// Internal functions + function _zap(address from, uint256 amount, address recipient, DiscountExerciseParams memory params) + internal + returns (uint256 paymentAmount, address, uint256, uint256) + { + if (block.timestamp > params.deadline) revert Exercise__PastDeadline(); + if (multiplier >= BPS_DENOM) revert Exercise__ZapMultiplierIncompatible(); + uint256 discountedUnderlying = amount.mulDivUp(BPS_DENOM - multiplier, BPS_DENOM); + uint256 fee = discountedUnderlying.mulDivUp(instantExitFee, BPS_DENOM); + uint256 underlyingAmount = discountedUnderlying - fee; + uint256 balance = underlyingToken.balanceOf(address(this)); + + // Fee amount in underlying tokens charged for zapping + feeAmount += fee; + + if (feeAmount >= minAmountToTriggerSwap && balance >= (feeAmount + underlyingAmount)) { + uint256 minAmountOut = _getMinAmountOutData(feeAmount, swapProps.maxSwapSlippage, address(oracle)); + /* Approve the underlying token to make swap */ + underlyingToken.approve(swapProps.swapper, feeAmount); + /* Swap underlying token to payment token (asset) */ + uint256 amountOut = _generalSwap( + swapProps.exchangeTypes, address(underlyingToken), address(paymentToken), feeAmount, minAmountOut, swapProps.exchangeAddress + ); + + if (amountOut == 0) { + revert Exercise__AmountOutIsZero(); + } + + feeAmount = 0; + underlyingToken.approve(swapProps.swapper, 0); + + // transfer payment tokens from user to the set receivers + distributeFees(paymentToken.balanceOf(address(this)), paymentToken); + } + + // transfer underlying tokens to recipient without the bonus + _pay(recipient, underlyingAmount); + + emit Exercised(from, recipient, underlyingAmount, paymentAmount); + } + + /// Internal functions + function _redeem(address from, uint256 amount, address recipient, DiscountExerciseParams memory params) + internal + virtual + returns (uint256 paymentAmount, address, uint256, uint256) + { + if (block.timestamp > params.deadline) revert Exercise__PastDeadline(); + // apply multiplier to price + paymentAmount = _getPaymentAmount(amount); + if (paymentAmount > params.maxPaymentAmount) revert Exercise__SlippageTooHigh(); + // transfer payment tokens from user to the set receivers + distributeFeesFrom(paymentAmount, paymentToken, from); + // transfer underlying tokens to recipient + _pay(recipient, amount); + + emit Exercised(from, recipient, amount, paymentAmount); + } + + function _pay(address to, uint256 amount) internal returns (uint256 remainingAmount) { + uint256 balance = underlyingToken.balanceOf(address(this)); + if (amount > balance) { + underlyingToken.safeTransfer(to, balance); + remainingAmount = amount - balance; + } else { + underlyingToken.safeTransfer(to, amount); + } + credit[to] += remainingAmount; + } + + /// View functions + + /// @notice Returns the amount of payment tokens required to exercise the given amount of options tokens. + /// @param amount The amount of options tokens to exercise + function getPaymentAmount(uint256 amount) external view returns (uint256 paymentAmount) { + return _getPaymentAmount(amount); + } + + function _getPaymentAmount(uint256 amount) internal view returns (uint256 paymentAmount) { + paymentAmount = amount.mulWadUp(oracle.getPrice().mulDivUp(multiplier, BPS_DENOM)); + } +} diff --git a/src/helpers/SwapHelper.sol b/src/helpers/SwapHelper.sol new file mode 100644 index 0000000..87c7af5 --- /dev/null +++ b/src/helpers/SwapHelper.sol @@ -0,0 +1,98 @@ +// SPDX-License-Identifier: AGPL-3.0 + +pragma solidity ^0.8.13; + +import {ISwapperSwaps, MinAmountOutData, MinAmountOutKind} from "vault-v2/ReaperSwapper.sol"; +import {IOracle} from "../interfaces/IOracle.sol"; +import {FixedPointMathLib} from "solmate/utils/FixedPointMathLib.sol"; + +enum ExchangeType { + UniV2, + Bal, + VeloSolid, + UniV3 +} + +struct SwapProps { + address swapper; + address exchangeAddress; + ExchangeType exchangeTypes; + uint256 maxSwapSlippage; +} + +abstract contract SwapHelper { + using FixedPointMathLib for uint256; + + /// @notice The denominator for converting the multiplier into a decimal number. + /// i.e. multiplier uses 4 decimals. + uint256 internal constant BPS_DENOM = 10_000; + SwapProps public swapProps; + + error SwapHelper__SlippageGreaterThanMax(); + error SwapHelper__ParamHasAddressZero(); + error SwapHelper__InvalidExchangeType(uint256 exType); + + constructor() {} + + /** + * @dev Override function shall have proper access control + * @param _swapProps - swap properties + */ + function setSwapProps(SwapProps memory _swapProps) external virtual; + + function _setSwapProps(SwapProps memory _swapProps) internal { + if (_swapProps.maxSwapSlippage > BPS_DENOM) { + revert SwapHelper__SlippageGreaterThanMax(); + } + if (_swapProps.exchangeAddress == address(0)) { + revert SwapHelper__ParamHasAddressZero(); + } + if (_swapProps.swapper == address(0)) { + revert SwapHelper__ParamHasAddressZero(); + } + swapProps = _swapProps; + } + + /** + * @dev Private function that allow to swap via multiple exchange types + * @param exType - type of exchange + * @param tokenIn - address of token in + * @param tokenOut - address of token out + * @param amount - amount of tokenIn to swap + * @param minAmountOut - minimal acceptable amount of tokenOut + * @param exchangeAddress - address of the exchange + */ + function _generalSwap(ExchangeType exType, address tokenIn, address tokenOut, uint256 amount, uint256 minAmountOut, address exchangeAddress) + internal + returns (uint256) + { + ISwapperSwaps _swapper = ISwapperSwaps(swapProps.swapper); + MinAmountOutData memory minAmountOutData = MinAmountOutData(MinAmountOutKind.Absolute, minAmountOut); + if (exType == ExchangeType.UniV2) { + return _swapper.swapUniV2(tokenIn, tokenOut, amount, minAmountOutData, exchangeAddress); + } else if (exType == ExchangeType.Bal) { + return _swapper.swapBal(tokenIn, tokenOut, amount, minAmountOutData, exchangeAddress); + } else if (exType == ExchangeType.VeloSolid) { + return _swapper.swapVelo(tokenIn, tokenOut, amount, minAmountOutData, exchangeAddress); + } else if (exType == ExchangeType.UniV3) { + return _swapper.swapUniV3(tokenIn, tokenOut, amount, minAmountOutData, exchangeAddress); + } else { + revert SwapHelper__InvalidExchangeType(uint256(exType)); + } + } + + /** + * @dev Private function that calculates minimal amount token out of swap using oracles + * @param _amountIn - amount of token to be swapped + * @param _maxSlippage - max allowed slippage + */ + function _getMinAmountOutData(uint256 _amountIn, uint256 _maxSlippage, address _oracle) internal view returns (uint256) { + uint256 minAmountOut = 0; + /* Get price from oracle */ + uint256 price = IOracle(_oracle).getPrice(); + /* Deduct slippage amount from predicted amount */ + minAmountOut = (_amountIn.mulWadUp(price) * (BPS_DENOM - _maxSlippage)) / BPS_DENOM; + + return minAmountOut; + } +} diff --git a/src/interfaces/IAlgebraPool.sol b/src/interfaces/IAlgebraPool.sol new file mode 100644 index 0000000..5e2f44f --- /dev/null +++ b/src/interfaces/IAlgebraPool.sol @@ -0,0 +1,24 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +pragma solidity >=0.5.0; + +/** + * @title The minimal interface we use for a Algebra Pool + * @dev The pool interface is broken up into many smaller pieces. + * Credit to Uniswap Labs under GPL-2.0-or-later license: + * https://github.com/Uniswap/v3-core/tree/main/contracts/interfaces + */ +interface IAlgebraPool { + /// @notice The first of the two tokens of the pool, sorted by address + /// @return The token contract address + function token0() external view returns (address); + + /// @notice The second of the two tokens of the pool, sorted by address + /// @return The token contract address + function token1() external view returns (address); + + function getTimepoints(uint32[] calldata secondsAgos) + external + view + returns (int56[] memory tickCumulatives, uint160[] memory secondsPerLiquidityCumulativeX128s, uint112[] memory volatilityCumulatives, uint256[] memory volumePerAvgLiquiditys); + +} \ No newline at end of file diff --git a/src/interfaces/IBalancer2TokensPool.sol b/src/interfaces/IBalancer2TokensPool.sol new file mode 100644 index 0000000..0f8b086 --- /dev/null +++ b/src/interfaces/IBalancer2TokensPool.sol @@ -0,0 +1,8 @@ +// SPDX-License-Identifier: AGPL-3.0 +pragma solidity ^0.8.13; + +import "../interfaces/IBalancerTwapOracle.sol"; + +interface IBalancer2TokensPool is IBalancerTwapOracle { + function getNormalizedWeights() external view returns (uint256[] memory); +} diff --git a/src/interfaces/IBalancerTwapOracle.sol b/src/interfaces/IBalancerTwapOracle.sol index dec77ba..59466bb 100644 --- a/src/interfaces/IBalancerTwapOracle.sol +++ b/src/interfaces/IBalancerTwapOracle.sol @@ -14,6 +14,8 @@ pragma solidity ^0.8.0; +import {IVault} from "../interfaces/IBalancerVault.sol"; + /** * @dev Interface for querying historical data from a Pool that can be used as a Price Oracle. * @@ -25,6 +27,13 @@ pragma solidity ^0.8.0; * is not older than the largest safe query window. */ interface IBalancerTwapOracle { + /** + * @notice Returns the Balancer Vault + */ + function getVault() external view returns (IVault); + + function getPoolId() external view returns (bytes32); + // The three values that can be queried: // // - PAIR_PRICE: the price of the tokens in the Pool, expressed as the price of the second token in units of the @@ -47,10 +56,7 @@ interface IBalancerTwapOracle { * @dev Returns the time average weighted price corresponding to each of `queries`. Prices are represented as 18 * decimal fixed point values. */ - function getTimeWeightedAverage(OracleAverageQuery[] memory queries) - external - view - returns (uint256[] memory results); + function getTimeWeightedAverage(OracleAverageQuery[] memory queries) external view returns (uint256[] memory results); /** * @dev Returns latest sample of `variable`. Prices are represented as 18 decimal fixed point values. @@ -82,10 +88,7 @@ interface IBalancerTwapOracle { /** * @dev Returns the accumulators corresponding to each of `queries`. */ - function getPastAccumulators(OracleAccumulatorQuery[] memory queries) - external - view - returns (int256[] memory results); + function getPastAccumulators(OracleAccumulatorQuery[] memory queries) external view returns (int256[] memory results); /** * @dev Information for an Accumulator query. diff --git a/src/interfaces/IBalancerVault.sol b/src/interfaces/IBalancerVault.sol new file mode 100644 index 0000000..eb4630c --- /dev/null +++ b/src/interfaces/IBalancerVault.sol @@ -0,0 +1,164 @@ +// SPDX-License-Identifier: GPL-3.0-or-later +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. + +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. + +// You should have received a copy of the GNU General Public License +// along with this program. If not, see . + +pragma experimental ABIEncoderV2; + +pragma solidity >=0.7.0 <0.9.0; + +/** + * @dev This is an empty interface used to represent either ERC20-conforming token contracts or ETH (using the zero + * address sentinel value). We're just relying on the fact that `interface` can be used to declare new address-like + * types. + * + * This concept is unrelated to a Pool's Asset Managers. + */ +interface IAsset { +// solhint-disable-previous-line no-empty-blocks +} + +/** + * @dev Minimal interface for interacting with Balancer's vault. + */ +interface IVault { + /** + * @dev Called by users to join a Pool, which transfers tokens from `sender` into the Pool's balance. This will + * trigger custom Pool behavior, which will typically grant something in return to `recipient` - often tokenized + * Pool shares. + * + * If the caller is not `sender`, it must be an authorized relayer for them. + * + * The `assets` and `maxAmountsIn` arrays must have the same length, and each entry indicates the maximum amount + * to send for each asset. The amounts to send are decided by the Pool and not the Vault: it just enforces + * these maximums. + * + * If joining a Pool that holds WETH, it is possible to send ETH directly: the Vault will do the wrapping. To enable + * this mechanism, the IAsset sentinel value (the zero address) must be passed in the `assets` array instead of the + * WETH address. Note that it is not possible to combine ETH and WETH in the same join. Any excess ETH will be sent + * back to the caller (not the sender, which is important for relayers). + * + * `assets` must have the same length and order as the array returned by `getPoolTokens`. This prevents issues when + * interacting with Pools that register and deregister tokens frequently. If sending ETH however, the array must be + * sorted *before* replacing the WETH address with the ETH sentinel value (the zero address), which means the final + * `assets` array might not be sorted. Pools with no registered tokens cannot be joined. + * + * If `fromInternalBalance` is true, the caller's Internal Balance will be preferred: ERC20 transfers will only + * be made for the difference between the requested amount and Internal Balance (if any). Note that ETH cannot be + * withdrawn from Internal Balance: attempting to do so will trigger a revert. + * + * This causes the Vault to call the `IBasePool.onJoinPool` hook on the Pool's contract, where Pools implement + * their own custom logic. This typically requires additional information from the user (such as the expected number + * of Pool shares). This can be encoded in the `userData` argument, which is ignored by the Vault and passed + * directly to the Pool's contract, as is `recipient`. + * + * Emits a `PoolBalanceChanged` event. + */ + function joinPool(bytes32 poolId, address sender, address recipient, JoinPoolRequest memory request) external payable; + + struct JoinPoolRequest { + IAsset[] assets; + uint256[] maxAmountsIn; + bytes userData; + bool fromInternalBalance; + } + + enum PoolSpecialization { + GENERAL, + MINIMAL_SWAP_INFO, + TWO_TOKEN + } + + /** + * @dev Returns a Pool's contract address and specialization setting. + */ + function getPool(bytes32 poolId) external view returns (address, PoolSpecialization); + + /** + * @dev Returns a Pool's registered tokens, the total balance for each, and the latest block when *any* of + * the tokens' `balances` changed. + * + * The order of the `tokens` array is the same order that will be used in `joinPool`, `exitPool`, as well as in all + * Pool hooks (where applicable). Calls to `registerTokens` and `deregisterTokens` may change this order. + * + * If a Pool only registers tokens once, and these are sorted in ascending order, they will be stored in the same + * order as passed to `registerTokens`. + * + * Total balances include both tokens held by the Vault and those withdrawn by the Pool's Asset Managers. These are + * the amounts used by joins, exits and swaps. For a detailed breakdown of token balances, use `getPoolTokenInfo` + * instead. + */ + function getPoolTokens(bytes32 poolId) external view returns (address[] memory tokens, uint256[] memory, uint256); + + /** + * @dev All tokens in a swap are either sent from the `sender` account to the Vault, or from the Vault to the + * `recipient` account. + * + * If the caller is not `sender`, it must be an authorized relayer for them. + * + * If `fromInternalBalance` is true, the `sender`'s Internal Balance will be preferred, performing an ERC20 + * transfer for the difference between the requested amount and the User's Internal Balance (if any). The `sender` + * must have allowed the Vault to use their tokens via `IERC20.approve()`. This matches the behavior of + * `joinPool`. + * + * If `toInternalBalance` is true, tokens will be deposited to `recipient`'s internal balance instead of + * transferred. This matches the behavior of `exitPool`. + * + * Note that ETH cannot be deposited to or withdrawn from Internal Balance: attempting to do so will trigger a + * revert. + */ + struct FundManagement { + address sender; + bool fromInternalBalance; + address payable recipient; + bool toInternalBalance; + } + + enum SwapKind { + GIVEN_IN, + GIVEN_OUT + } + + /** + * @dev Performs a swap with a single Pool. + * + * If the swap is 'given in' (the number of tokens to send to the Pool is known), it returns the amount of tokens + * taken from the Pool, which must be greater than or equal to `limit`. + * + * If the swap is 'given out' (the number of tokens to take from the Pool is known), it returns the amount of tokens + * sent to the Pool, which must be less than or equal to `limit`. + * + * Internal Balance usage and the recipient are determined by the `funds` struct. + * + * Emits a `Swap` event. + */ + function swap(SingleSwap memory singleSwap, FundManagement memory funds, uint256 limit, uint256 deadline) external payable returns (uint256); + + /** + * @dev Data for a single swap executed by `swap`. `amount` is either `amountIn` or `amountOut` depending on + * the `kind` value. + * + * `assetIn` and `assetOut` are either token addresses, or the IAsset sentinel value for ETH (the zero address). + * Note that Pools never interact with ETH directly: it will be wrapped to or unwrapped from WETH by the Vault. + * + * The `userData` field is ignored by the Vault, but forwarded to the Pool in the `onSwap` hook, and may be + * used to extend swap behavior. + */ + struct SingleSwap { + bytes32 poolId; + SwapKind kind; + IAsset assetIn; + IAsset assetOut; + uint256 amount; + bytes userData; + } +} diff --git a/src/interfaces/IExercise.sol b/src/interfaces/IExercise.sol new file mode 100644 index 0000000..f21ed28 --- /dev/null +++ b/src/interfaces/IExercise.sol @@ -0,0 +1,15 @@ +// SPDX-License-Identifier: AGPL-3.0 +pragma solidity ^0.8.13; + +interface IExercise { + /// @notice Exercise the options token + /// @param from Address to exercise options tokens from. + /// @param amount The amount of options tokens to exercise + /// @param recipient The address that receives the underlying tokens + /// @param params Additional parameters for the exercise + /// @return paymentAmount The amount of underlying tokens to pay to the exercise contract + /// @dev Additional returns are reserved for future use + function exercise(address from, uint256 amount, address recipient, bytes memory params) + external + returns (uint256 paymentAmount, address, uint256, uint256); +} diff --git a/src/interfaces/IFlashLoanReceiver.sol b/src/interfaces/IFlashLoanReceiver.sol new file mode 100644 index 0000000..1d52f67 --- /dev/null +++ b/src/interfaces/IFlashLoanReceiver.sol @@ -0,0 +1,28 @@ +// SPDX-License-Identifier: agpl-3.0 +pragma solidity ^0.8.0; + +import {ILendingPoolAddressesProvider} from "./ILendingPoolAddressesProvider.sol"; +import {ILendingPool} from "./ILendingPool.sol"; + +/** + * @title IFlashLoanReceiver interface + * @notice Interface for the Aave fee IFlashLoanReceiver. + * @author Aave + * @dev implement this interface to develop a flashloan-compatible flashLoanReceiver contract + **/ +interface IFlashLoanReceiver { + function executeOperation( + address[] calldata assets, + uint256[] calldata amounts, + uint256[] calldata premiums, + address initiator, + bytes calldata params + ) external returns (bool); + + function ADDRESSES_PROVIDER() + external + view + returns (ILendingPoolAddressesProvider); + + function LENDING_POOL() external view returns (ILendingPool); +} diff --git a/src/interfaces/ILendingPool.sol b/src/interfaces/ILendingPool.sol new file mode 100644 index 0000000..a03f9da --- /dev/null +++ b/src/interfaces/ILendingPool.sol @@ -0,0 +1,437 @@ +// SPDX-License-Identifier: agpl-3.0 +pragma solidity ^0.8.0; + +import {ILendingPoolAddressesProvider} from "./ILendingPoolAddressesProvider.sol"; +import {DataTypes} from "../libraries/DataTypes.sol"; + +interface ILendingPool { + /** + * @dev Emitted on deposit() + * @param reserve The address of the underlying asset of the reserve + * @param user The address initiating the deposit + * @param onBehalfOf The beneficiary of the deposit, receiving the aTokens + * @param amount The amount deposited + * @param referral The referral code used + **/ + event Deposit( + address indexed reserve, + address user, + address indexed onBehalfOf, + uint256 amount, + uint16 indexed referral + ); + + /** + * @dev Emitted on withdraw() + * @param reserve The address of the underlyng asset being withdrawn + * @param user The address initiating the withdrawal, owner of aTokens + * @param to Address that will receive the underlying + * @param amount The amount to be withdrawn + **/ + event Withdraw( + address indexed reserve, + address indexed user, + address indexed to, + uint256 amount + ); + + /** + * @dev Emitted on borrow() and flashLoan() when debt needs to be opened + * @param reserve The address of the underlying asset being borrowed + * @param user The address of the user initiating the borrow(), receiving the funds on borrow() or just + * initiator of the transaction on flashLoan() + * @param onBehalfOf The address that will be getting the debt + * @param amount The amount borrowed out + * @param borrowRateMode The rate mode: 1 for Stable, 2 for Variable + * @param borrowRate The numeric rate at which the user has borrowed + * @param referral The referral code used + **/ + event Borrow( + address indexed reserve, + address user, + address indexed onBehalfOf, + uint256 amount, + uint256 borrowRateMode, + uint256 borrowRate, + uint16 indexed referral + ); + + /** + * @dev Emitted on repay() + * @param reserve The address of the underlying asset of the reserve + * @param user The beneficiary of the repayment, getting his debt reduced + * @param repayer The address of the user initiating the repay(), providing the funds + * @param amount The amount repaid + **/ + event Repay( + address indexed reserve, + address indexed user, + address indexed repayer, + uint256 amount + ); + + /** + * @dev Emitted on swapBorrowRateMode() + * @param reserve The address of the underlying asset of the reserve + * @param user The address of the user swapping his rate mode + * @param rateMode The rate mode that the user wants to swap to + **/ + event Swap(address indexed reserve, address indexed user, uint256 rateMode); + + /** + * @dev Emitted on setUserUseReserveAsCollateral() + * @param reserve The address of the underlying asset of the reserve + * @param user The address of the user enabling the usage as collateral + **/ + event ReserveUsedAsCollateralEnabled( + address indexed reserve, + address indexed user + ); + + /** + * @dev Emitted on setUserUseReserveAsCollateral() + * @param reserve The address of the underlying asset of the reserve + * @param user The address of the user enabling the usage as collateral + **/ + event ReserveUsedAsCollateralDisabled( + address indexed reserve, + address indexed user + ); + + /** + * @dev Emitted on rebalanceStableBorrowRate() + * @param reserve The address of the underlying asset of the reserve + * @param user The address of the user for which the rebalance has been executed + **/ + event RebalanceStableBorrowRate( + address indexed reserve, + address indexed user + ); + + /** + * @dev Emitted on flashLoan() + * @param target The address of the flash loan receiver contract + * @param initiator The address initiating the flash loan + * @param asset The address of the asset being flash borrowed + * @param amount The amount flash borrowed + * @param premium The fee flash borrowed + * @param referralCode The referral code used + **/ + event FlashLoan( + address indexed target, + address indexed initiator, + address indexed asset, + uint256 amount, + uint256 premium, + uint16 referralCode + ); + + /** + * @dev Emitted when the pause is triggered. + */ + event Paused(); + + /** + * @dev Emitted when the pause is lifted. + */ + event Unpaused(); + + /** + * @dev Emitted when a borrower is liquidated. This event is emitted by the LendingPool via + * LendingPoolCollateral manager using a DELEGATECALL + * This allows to have the events in the generated ABI for LendingPool. + * @param collateralAsset The address of the underlying asset used as collateral, to receive as result of the liquidation + * @param debtAsset The address of the underlying borrowed asset to be repaid with the liquidation + * @param user The address of the borrower getting liquidated + * @param debtToCover The debt amount of borrowed `asset` the liquidator wants to cover + * @param liquidatedCollateralAmount The amount of collateral received by the liiquidator + * @param liquidator The address of the liquidator + * @param receiveAToken `true` if the liquidators wants to receive the collateral aTokens, `false` if he wants + * to receive the underlying collateral asset directly + **/ + event LiquidationCall( + address indexed collateralAsset, + address indexed debtAsset, + address indexed user, + uint256 debtToCover, + uint256 liquidatedCollateralAmount, + address liquidator, + bool receiveAToken + ); + + /** + * @dev Emitted when the state of a reserve is updated. NOTE: This event is actually declared + * in the ReserveLogic library and emitted in the updateInterestRates() function. Since the function is internal, + * the event will actually be fired by the LendingPool contract. The event is therefore replicated here so it + * gets added to the LendingPool ABI + * @param reserve The address of the underlying asset of the reserve + * @param liquidityRate The new liquidity rate + * @param stableBorrowRate The new stable borrow rate + * @param variableBorrowRate The new variable borrow rate + * @param liquidityIndex The new liquidity index + * @param variableBorrowIndex The new variable borrow index + **/ + event ReserveDataUpdated( + address indexed reserve, + uint256 liquidityRate, + uint256 stableBorrowRate, + uint256 variableBorrowRate, + uint256 liquidityIndex, + uint256 variableBorrowIndex + ); + + /** + * @dev Deposits an `amount` of underlying asset into the reserve, receiving in return overlying aTokens. + * - E.g. User deposits 100 USDC and gets in return 100 aUSDC + * @param asset The address of the underlying asset to deposit + * @param amount The amount to be deposited + * @param onBehalfOf The address that will receive the aTokens, same as msg.sender if the user + * wants to receive them on his own wallet, or a different address if the beneficiary of aTokens + * is a different wallet + * @param referralCode Code used to register the integrator originating the operation, for potential rewards. + * 0 if the action is executed directly by the user, without any middle-man + **/ + function deposit( + address asset, + uint256 amount, + address onBehalfOf, + uint16 referralCode + ) external; + + /** + * @dev Withdraws an `amount` of underlying asset from the reserve, burning the equivalent aTokens owned + * E.g. User has 100 aUSDC, calls withdraw() and receives 100 USDC, burning the 100 aUSDC + * @param asset The address of the underlying asset to withdraw + * @param amount The underlying amount to be withdrawn + * - Send the value type(uint256).max in order to withdraw the whole aToken balance + * @param to Address that will receive the underlying, same as msg.sender if the user + * wants to receive it on his own wallet, or a different address if the beneficiary is a + * different wallet + * @return The final amount withdrawn + **/ + function withdraw( + address asset, + uint256 amount, + address to + ) external returns (uint256); + + /** + * @dev Allows users to borrow a specific `amount` of the reserve underlying asset, provided that the borrower + * already deposited enough collateral, or he was given enough allowance by a credit delegator on the + * corresponding debt token (StableDebtToken or VariableDebtToken) + * - E.g. User borrows 100 USDC passing as `onBehalfOf` his own address, receiving the 100 USDC in his wallet + * and 100 stable/variable debt tokens, depending on the `interestRateMode` + * @param asset The address of the underlying asset to borrow + * @param amount The amount to be borrowed + * @param interestRateMode The interest rate mode at which the user wants to borrow: 1 for Stable, 2 for Variable + * @param referralCode Code used to register the integrator originating the operation, for potential rewards. + * 0 if the action is executed directly by the user, without any middle-man + * @param onBehalfOf Address of the user who will receive the debt. Should be the address of the borrower itself + * calling the function if he wants to borrow against his own collateral, or the address of the credit delegator + * if he has been given credit delegation allowance + **/ + function borrow( + address asset, + uint256 amount, + uint256 interestRateMode, + uint16 referralCode, + address onBehalfOf + ) external; + + /** + * @notice Repays a borrowed `amount` on a specific reserve, burning the equivalent debt tokens owned + * - E.g. User repays 100 USDC, burning 100 variable/stable debt tokens of the `onBehalfOf` address + * @param asset The address of the borrowed underlying asset previously borrowed + * @param amount The amount to repay + * - Send the value type(uint256).max in order to repay the whole debt for `asset` on the specific `debtMode` + * @param rateMode The interest rate mode at of the debt the user wants to repay: 1 for Stable, 2 for Variable + * @param onBehalfOf Address of the user who will get his debt reduced/removed. Should be the address of the + * user calling the function if he wants to reduce/remove his own debt, or the address of any other + * other borrower whose debt should be removed + * @return The final amount repaid + **/ + function repay( + address asset, + uint256 amount, + uint256 rateMode, + address onBehalfOf + ) external returns (uint256); + + /** + * @dev Allows a borrower to swap his debt between stable and variable mode, or viceversa + * @param asset The address of the underlying asset borrowed + * @param rateMode The rate mode that the user wants to swap to + **/ + function swapBorrowRateMode(address asset, uint256 rateMode) external; + + /** + * @dev Rebalances the stable interest rate of a user to the current stable rate defined on the reserve. + * - Users can be rebalanced if the following conditions are satisfied: + * 1. Usage ratio is above 95% + * 2. the current deposit APY is below REBALANCE_UP_THRESHOLD * maxVariableBorrowRate, which means that too much has been + * borrowed at a stable rate and depositors are not earning enough + * @param asset The address of the underlying asset borrowed + * @param user The address of the user to be rebalanced + **/ + function rebalanceStableBorrowRate(address asset, address user) external; + + /** + * @dev Allows depositors to enable/disable a specific deposited asset as collateral + * @param asset The address of the underlying asset deposited + * @param useAsCollateral `true` if the user wants to use the deposit as collateral, `false` otherwise + **/ + function setUserUseReserveAsCollateral( + address asset, + bool useAsCollateral + ) external; + + /** + * @dev Function to liquidate a non-healthy position collateral-wise, with Health Factor below 1 + * - The caller (liquidator) covers `debtToCover` amount of debt of the user getting liquidated, and receives + * a proportionally amount of the `collateralAsset` plus a bonus to cover market risk + * @param collateralAsset The address of the underlying asset used as collateral, to receive as result of the liquidation + * @param debtAsset The address of the underlying borrowed asset to be repaid with the liquidation + * @param user The address of the borrower getting liquidated + * @param debtToCover The debt amount of borrowed `asset` the liquidator wants to cover + * @param receiveAToken `true` if the liquidators wants to receive the collateral aTokens, `false` if he wants + * to receive the underlying collateral asset directly + **/ + function liquidationCall( + address collateralAsset, + address debtAsset, + address user, + uint256 debtToCover, + bool receiveAToken + ) external; + + /** + * @dev Allows smartcontracts to access the liquidity of the pool within one transaction, + * as long as the amount taken plus a fee is returned. + * IMPORTANT There are security concerns for developers of flashloan receiver contracts that must be kept into consideration. + * For further details please visit https://developers.aave.com + * @param receiverAddress The address of the contract receiving the funds, implementing the IFlashLoanReceiver interface + * @param assets The addresses of the assets being flash-borrowed + * @param amounts The amounts amounts being flash-borrowed + * @param modes Types of the debt to open if the flash loan is not returned: + * 0 -> Don't open any debt, just revert if funds can't be transferred from the receiver + * 1 -> Open debt at stable rate for the value of the amount flash-borrowed to the `onBehalfOf` address + * 2 -> Open debt at variable rate for the value of the amount flash-borrowed to the `onBehalfOf` address + * @param onBehalfOf The address that will receive the debt in the case of using on `modes` 1 or 2 + * @param params Variadic packed params to pass to the receiver as extra information + * @param referralCode Code used to register the integrator originating the operation, for potential rewards. + * 0 if the action is executed directly by the user, without any middle-man + **/ + function flashLoan( + address receiverAddress, + address[] calldata assets, + uint256[] calldata amounts, + uint256[] calldata modes, + address onBehalfOf, + bytes calldata params, + uint16 referralCode + ) external; + + /** + * @dev Returns the user account data across all the reserves + * @param user The address of the user + * @return totalCollateralETH the total collateral in ETH of the user + * @return totalDebtETH the total debt in ETH of the user + * @return availableBorrowsETH the borrowing power left of the user + * @return currentLiquidationThreshold the liquidation threshold of the user + * @return ltv the loan to value of the user + * @return healthFactor the current health factor of the user + **/ + function getUserAccountData( + address user + ) + external + view + returns ( + uint256 totalCollateralETH, + uint256 totalDebtETH, + uint256 availableBorrowsETH, + uint256 currentLiquidationThreshold, + uint256 ltv, + uint256 healthFactor + ); + + function initReserve( + address reserve, + address aTokenAddress, + address stableDebtAddress, + address variableDebtAddress, + address interestRateStrategyAddress + ) external; + + function setReserveInterestRateStrategyAddress( + address reserve, + address rateStrategyAddress + ) external; + + function setConfiguration(address reserve, uint256 configuration) external; + + /** + * @dev Returns the configuration of the reserve + * @param asset The address of the underlying asset of the reserve + * @return The configuration of the reserve + **/ + function getConfiguration( + address asset + ) external view returns (DataTypes.ReserveConfigurationMap memory); + + /** + * @dev Returns the configuration of the user across all the reserves + * @param user The user address + * @return The configuration of the user + **/ + function getUserConfiguration( + address user + ) external view returns (DataTypes.UserConfigurationMap memory); + + /** + * @dev Returns the normalized income normalized income of the reserve + * @param asset The address of the underlying asset of the reserve + * @return The reserve's normalized income + */ + function getReserveNormalizedIncome( + address asset + ) external view returns (uint256); + + /** + * @dev Returns the normalized variable debt per unit of asset + * @param asset The address of the underlying asset of the reserve + * @return The reserve normalized variable debt + */ + function getReserveNormalizedVariableDebt( + address asset + ) external view returns (uint256); + + /** + * @dev Returns the state and configuration of the reserve + * @param asset The address of the underlying asset of the reserve + * @return The state of the reserve + **/ + function getReserveData( + address asset + ) external view returns (DataTypes.ReserveData memory); + + function finalizeTransfer( + address asset, + address from, + address to, + uint256 amount, + uint256 balanceFromAfter, + uint256 balanceToBefore + ) external; + + function getReservesList() external view returns (address[] memory); + + function getAddressesProvider() + external + view + returns (ILendingPoolAddressesProvider); + + function setPause(bool val) external; + + function paused() external view returns (bool); +} diff --git a/src/interfaces/ILendingPoolAddressesProvider.sol b/src/interfaces/ILendingPoolAddressesProvider.sol new file mode 100644 index 0000000..daab118 --- /dev/null +++ b/src/interfaces/ILendingPoolAddressesProvider.sol @@ -0,0 +1,60 @@ +// SPDX-License-Identifier: agpl-3.0 +pragma solidity ^0.8.0; + +/** + * @title LendingPoolAddressesProvider contract + * @dev Main registry of addresses part of or connected to the protocol, including permissioned roles + * - Acting also as factory of proxies and admin of those, so with right to change its implementations + * - Owned by the Aave Governance + * @author Aave + **/ +interface ILendingPoolAddressesProvider { + event MarketIdSet(string newMarketId); + event LendingPoolUpdated(address indexed newAddress); + event ConfigurationAdminUpdated(address indexed newAddress); + event EmergencyAdminUpdated(address indexed newAddress); + event LendingPoolConfiguratorUpdated(address indexed newAddress); + event LendingPoolCollateralManagerUpdated(address indexed newAddress); + event PriceOracleUpdated(address indexed newAddress); + event LendingRateOracleUpdated(address indexed newAddress); + event ProxyCreated(bytes32 id, address indexed newAddress); + event AddressSet(bytes32 id, address indexed newAddress, bool hasProxy); + + function getMarketId() external view returns (string memory); + + function setMarketId(string calldata marketId) external; + + function setAddress(bytes32 id, address newAddress) external; + + function setAddressAsProxy(bytes32 id, address impl) external; + + function getAddress(bytes32 id) external view returns (address); + + function getLendingPool() external view returns (address); + + function setLendingPoolImpl(address pool) external; + + function getLendingPoolConfigurator() external view returns (address); + + function setLendingPoolConfiguratorImpl(address configurator) external; + + function getLendingPoolCollateralManager() external view returns (address); + + function setLendingPoolCollateralManager(address manager) external; + + function getPoolAdmin() external view returns (address); + + function setPoolAdmin(address admin) external; + + function getEmergencyAdmin() external view returns (address); + + function setEmergencyAdmin(address admin) external; + + function getPriceOracle() external view returns (address); + + function setPriceOracle(address priceOracle) external; + + function getLendingRateOracle() external view returns (address); + + function setLendingRateOracle(address lendingRateOracle) external; +} diff --git a/src/interfaces/IOptionsCompounder.sol b/src/interfaces/IOptionsCompounder.sol new file mode 100644 index 0000000..1685df4 --- /dev/null +++ b/src/interfaces/IOptionsCompounder.sol @@ -0,0 +1,22 @@ +// SPDX-License-Identifier: BUSL1.1 + +pragma solidity ^0.8.0; + +/* Errors */ +error OptionsCompounder__NotExerciseContract(); +error OptionsCompounder__TooMuchAssetsLoaned(); +error OptionsCompounder__FlashloanNotProfitableEnough(); +error OptionsCompounder__AssetNotEqualToPaymentToken(); +error OptionsCompounder__FlashloanNotFinished(); +error OptionsCompounder__OnlyStratAllowed(); +error OptionsCompounder__FlashloanNotTriggered(); +error OptionsCompounder__InvalidExchangeType(uint256 exchangeTypes); +error OptionsCompounder__SlippageGreaterThanMax(); +error OptionsCompounder__ParamHasAddressZero(); +error OptionsCompounder__NotEnoughUnderlyingTokens(); +error OptionsCompounder__WrongMinPaymentAmount(); +error OptionsCompounder__AmountOutIsZero(); + +interface IOptionsCompounder { + function harvestOTokens(uint256 amount, address exerciseContract, uint256 minWantAmount) external; +} diff --git a/src/interfaces/IOptionsToken.sol b/src/interfaces/IOptionsToken.sol new file mode 100644 index 0000000..0b8b29b --- /dev/null +++ b/src/interfaces/IOptionsToken.sol @@ -0,0 +1,14 @@ +// SPDX-License-Identifier: AGPL-3.0 +pragma solidity ^0.8.13; + +import {IERC20Mintable} from "./IERC20Mintable.sol"; + +interface IOptionsToken is IERC20Mintable { + function exercise(uint256 amount, address recipient, address option, bytes calldata params) + external + returns (uint256 paymentAmount, address, uint256, uint256); + + function setExerciseContract(address _address, bool _isExercise) external; + + function isExerciseContract(address) external returns (bool); +} diff --git a/src/interfaces/IOracle.sol b/src/interfaces/IOracle.sol index de4f7d2..562f5c4 100644 --- a/src/interfaces/IOracle.sol +++ b/src/interfaces/IOracle.sol @@ -11,4 +11,7 @@ interface IOracle { /// For example, if the payment token is $2 and the strike price is $4, the return value /// would be 2e18. function getPrice() external view returns (uint256 price); + + /// @notice This is important for verifying compatibility between oracle and exercise. + function getTokens() external view returns (address paymentToken, address underlyingToken); } diff --git a/src/interfaces/ISwapperSwaps.sol b/src/interfaces/ISwapperSwaps.sol new file mode 100644 index 0000000..2cf6215 --- /dev/null +++ b/src/interfaces/ISwapperSwaps.sol @@ -0,0 +1,128 @@ +// SPDX-License-Identifier: BUSL1.1 + +pragma solidity ^0.8.0; + +enum MinAmountOutKind { + Absolute, + ChainlinkBased +} + +struct MinAmountOutData { + MinAmountOutKind kind; + uint256 absoluteOrBPSValue; // for type "ChainlinkBased", value must be in BPS +} + +struct UniV3SwapData { + address[] path; + uint24[] fees; +} + +interface ISwapperSwaps { + function swapUniV2( + address _from, + address _to, + uint256 _amount, + MinAmountOutData memory _minAmountOutData, + address _router, + uint256 _deadline, + bool _tryCatchActive + ) external returns (uint256); + + function swapUniV2( + address _from, + address _to, + uint256 _amount, + MinAmountOutData memory _minAmountOutData, + address _router, + uint256 _deadline + ) external returns (uint256); + + function swapUniV2( + address _from, + address _to, + uint256 _amount, + MinAmountOutData memory _minAmountOutData, + address _router + ) external returns (uint256); + + function swapBal( + address _from, + address _to, + uint256 _amount, + MinAmountOutData memory _minAmountOutData, + address _vault, + uint256 _deadline, + bool _tryCatchActive + ) external returns (uint256); + + function swapBal( + address _from, + address _to, + uint256 _amount, + MinAmountOutData memory _minAmountOutData, + address _vault, + uint256 _deadline + ) external returns (uint256); + + function swapBal( + address _from, + address _to, + uint256 _amount, + MinAmountOutData memory _minAmountOutData, + address _vault + ) external returns (uint256); + + function swapThenaRam( + address _from, + address _to, + uint256 _amount, + MinAmountOutData memory _minAmountOutData, + address _router, + uint256 _deadline, + bool _tryCatchActive + ) external returns (uint256); + + function swapThenaRam( + address _from, + address _to, + uint256 _amount, + MinAmountOutData memory _minAmountOutData, + address _router, + uint256 _deadline + ) external returns (uint256); + + function swapThenaRam( + address _from, + address _to, + uint256 _amount, + MinAmountOutData memory _minAmountOutData, + address _router + ) external returns (uint256); + + function swapUniV3( + address _from, + address _to, + uint256 _amount, + MinAmountOutData memory _minAmountOutData, + address _router, + uint256 _deadline, + bool _tryCatchActive + ) external returns (uint256); + + function swapUniV3( + address _from, + address _to, + uint256 _amount, + MinAmountOutData memory _minAmountOutData, + address _router, + uint256 _deadline + ) external returns (uint256); + + function swapUniV3( + address _from, + address _to, + uint256 _amount, + MinAmountOutData memory _minAmountOutData, + address _router + ) external returns (uint256); +} diff --git a/src/interfaces/IThenaPair.sol b/src/interfaces/IThenaPair.sol new file mode 100644 index 0000000..76db264 --- /dev/null +++ b/src/interfaces/IThenaPair.sol @@ -0,0 +1,23 @@ +pragma solidity >=0.5; + +interface IThenaPair { + function getReserves() external view returns (uint112 _reserve0, uint112 _reserve1, uint32 _blockTimestampLast); + + function reserve0CumulativeLast() external view returns (uint256); + + function reserve1CumulativeLast() external view returns (uint256); + + function currentCumulativePrices() external view returns (uint256 reserve0Cumulative, uint256 reserve1Cumulative, uint256 blockTimestamp); + + function stable() external view returns (bool); + + function observationLength() external view returns (uint256); + + function observations(uint256) external view returns (uint256 timestamp, uint256 reserve0Cumulative, uint256 reserve1Cumulative); + + function token0() external view returns (address); + + function token1() external view returns (address); + + function sync() external; +} diff --git a/src/libraries/DataTypes.sol b/src/libraries/DataTypes.sol new file mode 100644 index 0000000..8f9354a --- /dev/null +++ b/src/libraries/DataTypes.sol @@ -0,0 +1,53 @@ +// SPDX-License-Identifier: agpl-3.0 +pragma solidity ^0.8.0; + +library DataTypes { + // refer to the whitepaper, section 1.1 basic concepts for a formal description of these properties. + struct ReserveData { + //stores the reserve configuration + ReserveConfigurationMap configuration; + //the liquidity index. Expressed in ray + uint128 liquidityIndex; + //variable borrow index. Expressed in ray + uint128 variableBorrowIndex; + //the current supply rate. Expressed in ray + uint128 currentLiquidityRate; + //the current variable borrow rate. Expressed in ray + uint128 currentVariableBorrowRate; + //the current stable borrow rate. Expressed in ray + uint128 currentStableBorrowRate; + uint40 lastUpdateTimestamp; + //tokens addresses + address aTokenAddress; + address stableDebtTokenAddress; + address variableDebtTokenAddress; + //address of the interest rate strategy + address interestRateStrategyAddress; + //the id of the reserve. Represents the position in the list of the active reserves + uint8 id; + } + + struct ReserveConfigurationMap { + //bit 0-15: LTV + //bit 16-31: Liq. threshold + //bit 32-47: Liq. bonus + //bit 48-55: Decimals + //bit 56: Reserve is active + //bit 57: reserve is frozen + //bit 58: borrowing is enabled + //bit 59: stable rate borrowing enabled + //bit 60-63: reserved + //bit 64-79: reserve factor + uint256 data; + } + + struct UserConfigurationMap { + uint256 data; + } + + enum InterestRateMode { + NONE, + STABLE, + VARIABLE + } +} diff --git a/src/oracles/AlgebraOracle.sol b/src/oracles/AlgebraOracle.sol new file mode 100644 index 0000000..79ecf89 --- /dev/null +++ b/src/oracles/AlgebraOracle.sol @@ -0,0 +1,157 @@ +// SPDX-License-Identifier: AGPL-3.0 +pragma solidity ^0.8.13; + +import {Owned} from "solmate/auth/Owned.sol"; +import {FixedPointMathLib} from "solmate/utils/FixedPointMathLib.sol"; + +import {IOracle} from "../interfaces/IOracle.sol"; +import {ERC20} from "solmate/tokens/ERC20.sol"; +import {IAlgebraPool} from "../interfaces/IAlgebraPool.sol"; +import {TickMath} from "v3-core/libraries/TickMath.sol"; +import {FullMath} from "v3-core/libraries/FullMath.sol"; + +/// @title Oracle using Algebra TWAP oracle as data source +/// @notice The oracle contract that provides the current price to purchase +/// the underlying token while exercising options. Uses Algebra TWAP oracle +/// as data source, and then applies a multiplier & lower bound. +/// @notice Same as UniswapV3, with a minimal interface change. +/// (observe -> getTimepoints) +contract AlgebraOracle is IOracle, Owned { + /// ----------------------------------------------------------------------- + /// Library usage + /// ----------------------------------------------------------------------- + + using FixedPointMathLib for uint256; + + /// ----------------------------------------------------------------------- + /// Errors + /// ----------------------------------------------------------------------- + + error AlgebraOracle__InvalidParams(); + error AlgebraOracle__InvalidWindow(); + error AlgebraOracle__BelowMinPrice(); + + /// ----------------------------------------------------------------------- + /// Events + /// ----------------------------------------------------------------------- + + event SetParams(uint56 secs, uint56 ago, uint128 minPrice); + + /// ----------------------------------------------------------------------- + /// Immutable parameters + /// ----------------------------------------------------------------------- + + uint256 internal constant MIN_SECS = 20 minutes; + + /// @notice The Algebra Pool contract (provides the oracle) + IAlgebraPool public immutable algebraPool; + + /// ----------------------------------------------------------------------- + /// Storage variables + /// ----------------------------------------------------------------------- + + /// @notice The size of the window to take the TWAP value over in seconds. + uint32 public secs; + + /// @notice The number of seconds in the past to take the TWAP from. The window + /// would be (block.timestamp - secs - ago, block.timestamp - ago]. + uint32 public ago; + + /// @notice The minimum value returned by getPrice(). Maintains a floor for the + /// price to mitigate potential attacks on the TWAP oracle. + uint128 public minPrice; + + /// @notice Whether the price of token0 should be returned (in units of token1). + /// If false, the price is returned in units of token0. + bool public isToken0; + + /// ----------------------------------------------------------------------- + /// Constructor + /// ----------------------------------------------------------------------- + + constructor(IAlgebraPool algebraPool_, address token, address owner_, uint32 secs_, uint32 ago_, uint128 minPrice_) Owned(owner_) { + if (ERC20(algebraPool_.token0()).decimals() != 18 || ERC20(algebraPool_.token1()).decimals() != 18) revert AlgebraOracle__InvalidParams(); + if (algebraPool_.token0() != token && algebraPool_.token1() != token) revert AlgebraOracle__InvalidParams(); + if (secs_ < MIN_SECS) revert AlgebraOracle__InvalidWindow(); + algebraPool = algebraPool_; + isToken0 = token == algebraPool_.token0(); + secs = secs_; + ago = ago_; + minPrice = minPrice_; + + emit SetParams(secs_, ago_, minPrice_); + } + + /// ----------------------------------------------------------------------- + /// IOracle + /// ----------------------------------------------------------------------- + + /// @inheritdoc IOracle + function getPrice() external view override returns (uint256 price) { + /// ----------------------------------------------------------------------- + /// Validation + /// ----------------------------------------------------------------------- + + // The Algebra pool reverts on invalid TWAP queries, so we don't need to + + /// ----------------------------------------------------------------------- + /// Computation + /// ----------------------------------------------------------------------- + + // query Algebra oracle to get TWAP tick + { + uint32 _twapDuration = secs; + uint32 _twapAgo = ago; + uint32[] memory secondsAgo = new uint32[](2); + secondsAgo[0] = _twapDuration + _twapAgo; + secondsAgo[1] = _twapAgo; + + (int56[] memory tickCumulatives,,,) = algebraPool.getTimepoints(secondsAgo); + int24 tick = int24((tickCumulatives[1] - tickCumulatives[0]) / int56(int32(_twapDuration))); + + uint256 decimalPrecision = 1e18; + + // from https://optimistic.etherscan.io/address/0xB210CE856631EeEB767eFa666EC7C1C57738d438#code#F5#L49 + uint160 sqrtRatioX96 = TickMath.getSqrtRatioAtTick(tick); + + // Calculate quoteAmount with better precision if it doesn't overflow when multiplied by itself + if (sqrtRatioX96 <= type(uint128).max) { + uint256 ratioX192 = uint256(sqrtRatioX96) * sqrtRatioX96; + price = isToken0 ? FullMath.mulDiv(ratioX192, decimalPrecision, 1 << 192) : FullMath.mulDiv(1 << 192, decimalPrecision, ratioX192); + } else { + uint256 ratioX128 = FullMath.mulDiv(sqrtRatioX96, sqrtRatioX96, 1 << 64); + price = isToken0 ? FullMath.mulDiv(ratioX128, decimalPrecision, 1 << 128) : FullMath.mulDiv(1 << 128, decimalPrecision, ratioX128); + } + } + + // apply minimum price + if (price < minPrice) revert AlgebraOracle__BelowMinPrice(); + } + + /// @inheritdoc IOracle + function getTokens() external view override returns (address paymentToken, address underlyingToken) { + if (isToken0) { + return (algebraPool.token1(), algebraPool.token0()); + } else { + return (algebraPool.token0(), algebraPool.token1()); + } + } + + /// ----------------------------------------------------------------------- + /// Owner functions + /// ----------------------------------------------------------------------- + + /// @notice Updates the oracle parameters. Only callable by the owner. + /// @param secs_ The size of the window to take the TWAP value over in seconds. + /// @param ago_ The number of seconds in the past to take the TWAP from. The window + /// would be (block.timestamp - secs - ago, block.timestamp - ago]. + /// @param minPrice_ The minimum value returned by getPrice(). Maintains a floor for the + /// price to mitigate potential attacks on the TWAP oracle. + function setParams(uint32 secs_, uint32 ago_, uint128 minPrice_) external onlyOwner { + if (secs_ < MIN_SECS) revert AlgebraOracle__InvalidWindow(); + secs = secs_; + ago = ago_; + minPrice = minPrice_; + emit SetParams(secs_, ago_, minPrice_); + } +} diff --git a/src/oracles/BalancerOracle.sol b/src/oracles/BalancerOracle.sol index d5f04b4..6eba82e 100644 --- a/src/oracles/BalancerOracle.sol +++ b/src/oracles/BalancerOracle.sol @@ -3,19 +3,18 @@ pragma solidity ^0.8.13; import {Owned} from "solmate/auth/Owned.sol"; import {FixedPointMathLib} from "solmate/utils/FixedPointMathLib.sol"; +import {ERC20} from "solmate/tokens/ERC20.sol"; import {IOracle} from "../interfaces/IOracle.sol"; +import {IVault} from "../interfaces/IBalancerVault.sol"; import {IBalancerTwapOracle} from "../interfaces/IBalancerTwapOracle.sol"; /// @title Oracle using Balancer TWAP oracle as data source /// @author zefram.eth /// @notice The oracle contract that provides the current price to purchase /// the underlying token while exercising options. Uses Balancer TWAP oracle -/// as data source, and then applies a multiplier & lower bound. -/// @dev IMPORTANT: The Balancer pool must use the payment token of the options -/// token as the first token and the underlying token as the second token, due to -/// how the Balancer oracle represents the price. -/// Furthermore, the payment token and the underlying token must use 18 decimals. +/// as data source. +/// @dev IMPORTANT: The payment token and the underlying token must use 18 decimals. /// This is because the Balancer oracle returns the TWAP value in 18 decimals /// and the OptionsToken contract also expects 18 decimals. contract BalancerOracle is IOracle, Owned { @@ -29,37 +28,34 @@ contract BalancerOracle is IOracle, Owned { /// Errors /// ----------------------------------------------------------------------- + error BalancerOracle__InvalidParams(); + error BalancerOracle__InvalidWindow(); error BalancerOracle__TWAPOracleNotReady(); + error BalancerOracle__BelowMinPrice(); /// ----------------------------------------------------------------------- /// Events /// ----------------------------------------------------------------------- - event SetParams(uint16 multiplier, uint56 secs, uint56 ago, uint128 minPrice); - - /// ----------------------------------------------------------------------- - /// Constants - /// ----------------------------------------------------------------------- - - /// @notice The denominator for converting the multiplier into a decimal number. - /// i.e. multiplier uses 4 decimals. - uint256 internal constant MULTIPLIER_DENOM = 10000; + event SetParams(uint56 secs, uint56 ago, uint128 minPrice); /// ----------------------------------------------------------------------- /// Immutable parameters /// ----------------------------------------------------------------------- + uint256 internal constant MIN_SECS = 20 minutes; + /// @notice The Balancer TWAP oracle contract (usually a pool with oracle support) IBalancerTwapOracle public immutable balancerTwapOracle; + /// @notice Whether the price of token0 should be returned (in units of token1). + /// If false, the price of token1 is returned. + bool public immutable isToken0; + /// ----------------------------------------------------------------------- /// Storage variables /// ----------------------------------------------------------------------- - /// @notice The multiplier applied to the TWAP value. Encodes the discount of - /// the options token. Uses 4 decimals. - uint16 public multiplier; - /// @notice The size of the window to take the TWAP value over in seconds. uint56 public secs; @@ -75,21 +71,23 @@ contract BalancerOracle is IOracle, Owned { /// Constructor /// ----------------------------------------------------------------------- - constructor( - IBalancerTwapOracle balancerTwapOracle_, - address owner_, - uint16 multiplier_, - uint56 secs_, - uint56 ago_, - uint128 minPrice_ - ) Owned(owner_) { + constructor(IBalancerTwapOracle balancerTwapOracle_, address token, address owner_, uint56 secs_, uint56 ago_, uint128 minPrice_) Owned(owner_) { balancerTwapOracle = balancerTwapOracle_; - multiplier = multiplier_; + + IVault vault = balancerTwapOracle.getVault(); + (address[] memory poolTokens,,) = vault.getPoolTokens(balancerTwapOracle_.getPoolId()); + + if (ERC20(poolTokens[0]).decimals() != 18 || ERC20(poolTokens[1]).decimals() != 18) revert BalancerOracle__InvalidParams(); + if (token != poolTokens[0] && token != poolTokens[1]) revert BalancerOracle__InvalidParams(); + if (secs_ < MIN_SECS) revert BalancerOracle__InvalidWindow(); + + isToken0 = poolTokens[0] == token; + secs = secs_; ago = ago_; minPrice = minPrice_; - emit SetParams(multiplier_, secs_, ago_, minPrice_); + emit SetParams(secs_, ago_, minPrice_); } /// ----------------------------------------------------------------------- @@ -102,7 +100,6 @@ contract BalancerOracle is IOracle, Owned { /// Storage loads /// ----------------------------------------------------------------------- - uint256 multiplier_ = multiplier; uint256 secs_ = secs; uint256 ago_ = ago; uint256 minPrice_ = minPrice; @@ -124,19 +121,30 @@ contract BalancerOracle is IOracle, Owned { // query Balancer oracle to get TWAP value { IBalancerTwapOracle.OracleAverageQuery[] memory queries = new IBalancerTwapOracle.OracleAverageQuery[](1); - queries[0] = IBalancerTwapOracle.OracleAverageQuery({ - variable: IBalancerTwapOracle.Variable.PAIR_PRICE, - secs: secs_, - ago: ago_ - }); + queries[0] = IBalancerTwapOracle.OracleAverageQuery({variable: IBalancerTwapOracle.Variable.PAIR_PRICE, secs: secs_, ago: ago_}); price = balancerTwapOracle.getTimeWeightedAverage(queries)[0]; } - // apply multiplier to price - price = price.mulDivUp(multiplier_, MULTIPLIER_DENOM); + if (isToken0) { + // convert price to token0 + price = uint256(1e18).divWadUp(price); + } - // bound price above minPrice - price = price < minPrice_ ? minPrice_ : price; + // apply min price + if (price < minPrice_) revert BalancerOracle__BelowMinPrice(); + } + + /// @inheritdoc IOracle + function getTokens() external view override returns (address paymentToken, address underlyingToken) { + IVault vault = balancerTwapOracle.getVault(); + (address[] memory poolTokens,,) = vault.getPoolTokens(balancerTwapOracle.getPoolId()); + if (isToken0) { + paymentToken = poolTokens[1]; + underlyingToken = poolTokens[0]; + } else { + paymentToken = poolTokens[0]; + underlyingToken = poolTokens[1]; + } } /// ----------------------------------------------------------------------- @@ -144,18 +152,16 @@ contract BalancerOracle is IOracle, Owned { /// ----------------------------------------------------------------------- /// @notice Updates the oracle parameters. Only callable by the owner. - /// @param multiplier_ The multiplier applied to the TWAP value. Encodes the discount of - /// the options token. Uses 4 decimals. /// @param secs_ The size of the window to take the TWAP value over in seconds. /// @param ago_ The number of seconds in the past to take the TWAP from. The window /// would be (block.timestamp - secs - ago, block.timestamp - ago]. /// @param minPrice_ The minimum value returned by getPrice(). Maintains a floor for the /// price to mitigate potential attacks on the TWAP oracle. - function setParams(uint16 multiplier_, uint56 secs_, uint56 ago_, uint128 minPrice_) external onlyOwner { - multiplier = multiplier_; + function setParams(uint56 secs_, uint56 ago_, uint128 minPrice_) external onlyOwner { + if (secs_ < MIN_SECS) revert BalancerOracle__InvalidWindow(); secs = secs_; ago = ago_; minPrice = minPrice_; - emit SetParams(multiplier_, secs_, ago_, minPrice_); + emit SetParams(secs_, ago_, minPrice_); } } diff --git a/src/oracles/ThenaOracle.sol b/src/oracles/ThenaOracle.sol new file mode 100644 index 0000000..ab43a82 --- /dev/null +++ b/src/oracles/ThenaOracle.sol @@ -0,0 +1,153 @@ +// SPDX-License-Identifier: AGPL-3.0 +pragma solidity ^0.8.13; + +import {Owned} from "solmate/auth/Owned.sol"; +import {FixedPointMathLib} from "solmate/utils/FixedPointMathLib.sol"; +import {ERC20} from "solmate/tokens/ERC20.sol"; +import {IOracle} from "../interfaces/IOracle.sol"; +import {IThenaPair} from "../interfaces/IThenaPair.sol"; + +/// @title Oracle using Thena TWAP oracle as data source +/// @author zefram.eth/lookee/Eidolon +/// @notice The oracle contract that provides the current price to purchase +/// the underlying token while exercising options. Uses Thena TWAP oracle +/// as data source, and then applies a lower bound. +contract ThenaOracle is IOracle, Owned { + /// ----------------------------------------------------------------------- + /// Library usage + /// ----------------------------------------------------------------------- + + using FixedPointMathLib for uint256; + + /// ----------------------------------------------------------------------- + /// Errors + /// ----------------------------------------------------------------------- + + error ThenaOracle__InvalidParams(); + error ThenaOracle__InvalidWindow(); + error ThenaOracle__StablePairsUnsupported(); + error ThenaOracle__Overflow(); + error ThenaOracle__BelowMinPrice(); + + /// ----------------------------------------------------------------------- + /// Events + /// ----------------------------------------------------------------------- + + event SetParams(uint56 secs, uint128 minPrice); + + /// ----------------------------------------------------------------------- + /// Immutable parameters + /// ----------------------------------------------------------------------- + + uint256 internal constant MIN_SECS = 20 minutes; + + /// @notice The Thena TWAP oracle contract (usually a pool with oracle support) + IThenaPair public immutable thenaPair; + + /// ----------------------------------------------------------------------- + /// Storage variables + /// ----------------------------------------------------------------------- + + /// @notice The size of the window to take the TWAP value over in seconds. + uint56 public secs; + + /// @notice The minimum value returned by getPrice(). Maintains a floor for the + /// price to mitigate potential attacks on the TWAP oracle. + uint128 public minPrice; + + /// @notice Whether the price should be returned in terms of token0. + /// If false, the price is returned in terms of token1. + bool public isToken0; + + /// ----------------------------------------------------------------------- + /// Constructor + /// ----------------------------------------------------------------------- + + constructor(IThenaPair thenaPair_, address token, address owner_, uint56 secs_, uint128 minPrice_) Owned(owner_) { + if (ERC20(thenaPair_.token0()).decimals() != 18 || ERC20(thenaPair_.token1()).decimals() != 18) revert ThenaOracle__InvalidParams(); + if (thenaPair_.stable()) revert ThenaOracle__StablePairsUnsupported(); + if (thenaPair_.token0() != token && thenaPair_.token1() != token) revert ThenaOracle__InvalidParams(); + if (secs_ < MIN_SECS) revert ThenaOracle__InvalidWindow(); + + thenaPair = thenaPair_; + isToken0 = thenaPair_.token0() == token; + secs = secs_; + minPrice = minPrice_; + + emit SetParams(secs_, minPrice_); + } + + /// ----------------------------------------------------------------------- + /// IOracle + /// ----------------------------------------------------------------------- + + /// @inheritdoc IOracle + function getPrice() external view override returns (uint256 price) { + /// ----------------------------------------------------------------------- + /// Storage loads + /// ----------------------------------------------------------------------- + + uint256 secs_ = secs; + + /// ----------------------------------------------------------------------- + /// Computation + /// ----------------------------------------------------------------------- + + // query Thena oracle to get TWAP value + { + (uint256 reserve0CumulativeCurrent, uint256 reserve1CumulativeCurrent, uint256 blockTimestampCurrent) = + thenaPair.currentCumulativePrices(); + uint256 observationLength = IThenaPair(thenaPair).observationLength(); + (uint256 blockTimestampLast, uint256 reserve0CumulativeLast, uint256 reserve1CumulativeLast) = + thenaPair.observations(observationLength - 1); + uint32 T = uint32(blockTimestampCurrent - blockTimestampLast); + if (T < secs_) { + (blockTimestampLast, reserve0CumulativeLast, reserve1CumulativeLast) = thenaPair.observations(observationLength - 2); + T = uint32(blockTimestampCurrent - blockTimestampLast); + } + uint112 reserve0 = safe112((reserve0CumulativeCurrent - reserve0CumulativeLast) / T); + uint112 reserve1 = safe112((reserve1CumulativeCurrent - reserve1CumulativeLast) / T); + + if (!isToken0) { + price = uint256(reserve0).divWadDown(reserve1); + } else { + price = uint256(reserve1).divWadDown(reserve0); + } + } + + if (price < minPrice) revert ThenaOracle__BelowMinPrice(); + } + + /// @inheritdoc IOracle + function getTokens() external view override returns (address paymentToken, address underlyingToken) { + if (isToken0) { + return (thenaPair.token1(), thenaPair.token0()); + } else { + return (thenaPair.token0(), thenaPair.token1()); + } + } + + /// ----------------------------------------------------------------------- + /// Owner functions + /// ----------------------------------------------------------------------- + + /// @notice Updates the oracle parameters. Only callable by the owner. + /// @param secs_ The size of the window to take the TWAP value over in seconds. + /// @param minPrice_ The minimum value returned by getPrice(). Maintains a floor for the + /// price to mitigate potential attacks on the TWAP oracle. + function setParams(uint56 secs_, uint128 minPrice_) external onlyOwner { + if (secs_ < MIN_SECS) revert ThenaOracle__InvalidWindow(); + secs = secs_; + minPrice = minPrice_; + emit SetParams(secs_, minPrice_); + } + + /// ----------------------------------------------------------------------- + /// Util functions + /// ----------------------------------------------------------------------- + + function safe112(uint256 n) internal pure returns (uint112) { + if (n >= 2 ** 112) revert ThenaOracle__Overflow(); + return uint112(n); + } +} diff --git a/src/oracles/UniswapV3Oracle.sol b/src/oracles/UniswapV3Oracle.sol new file mode 100644 index 0000000..cbad255 --- /dev/null +++ b/src/oracles/UniswapV3Oracle.sol @@ -0,0 +1,156 @@ +// SPDX-License-Identifier: AGPL-3.0 +pragma solidity ^0.8.13; + +import {Owned} from "solmate/auth/Owned.sol"; +import {FixedPointMathLib} from "solmate/utils/FixedPointMathLib.sol"; + +import {IOracle} from "../interfaces/IOracle.sol"; +import {ERC20} from "solmate/tokens/ERC20.sol"; +import {IUniswapV3Pool} from "v3-core/interfaces/IUniswapV3Pool.sol"; +import {TickMath} from "v3-core/libraries/TickMath.sol"; +import {FullMath} from "v3-core/libraries/FullMath.sol"; + +/// @title Oracle using Uniswap TWAP oracle as data source +/// @author zefram.eth & lookeey +/// @notice The oracle contract that provides the current price to purchase +/// the underlying token while exercising options. Uses UniswapV3 TWAP oracle +/// as data source, and then applies a multiplier & lower bound. +contract UniswapV3Oracle is IOracle, Owned { + /// ----------------------------------------------------------------------- + /// Library usage + /// ----------------------------------------------------------------------- + + using FixedPointMathLib for uint256; + + /// ----------------------------------------------------------------------- + /// Errors + /// ----------------------------------------------------------------------- + + error UniswapOracle__InvalidParams(); + error UniswapOracle__InvalidWindow(); + error UniswapOracle__BelowMinPrice(); + + /// ----------------------------------------------------------------------- + /// Events + /// ----------------------------------------------------------------------- + + event SetParams(uint56 secs, uint56 ago, uint128 minPrice); + + /// ----------------------------------------------------------------------- + /// Immutable parameters + /// ----------------------------------------------------------------------- + + uint256 internal constant MIN_SECS = 20 minutes; + + /// @notice The UniswapV3 Pool contract (provides the oracle) + IUniswapV3Pool public immutable uniswapPool; + + /// ----------------------------------------------------------------------- + /// Storage variables + /// ----------------------------------------------------------------------- + + /// @notice The size of the window to take the TWAP value over in seconds. + uint32 public secs; + + /// @notice The number of seconds in the past to take the TWAP from. The window + /// would be (block.timestamp - secs - ago, block.timestamp - ago]. + uint32 public ago; + + /// @notice The minimum value returned by getPrice(). Maintains a floor for the + /// price to mitigate potential attacks on the TWAP oracle. + uint128 public minPrice; + + /// @notice Whether the price of token0 should be returned (in units of token1). + /// If false, the price is returned in units of token0. + bool public isToken0; + + /// ----------------------------------------------------------------------- + /// Constructor + /// ----------------------------------------------------------------------- + + constructor(IUniswapV3Pool uniswapPool_, address token, address owner_, uint32 secs_, uint32 ago_, uint128 minPrice_) Owned(owner_) { + if (ERC20(uniswapPool_.token0()).decimals() != 18 || ERC20(uniswapPool_.token1()).decimals() != 18) revert UniswapOracle__InvalidParams(); //|| ERC20(uniswapPool_.token1()).decimals() != 18 + if (uniswapPool_.token0() != token && uniswapPool_.token1() != token) revert UniswapOracle__InvalidParams(); + if (secs_ < MIN_SECS) revert UniswapOracle__InvalidWindow(); + uniswapPool = uniswapPool_; + isToken0 = token == uniswapPool_.token0(); + secs = secs_; + ago = ago_; + minPrice = minPrice_; + + emit SetParams(secs_, ago_, minPrice_); + } + + /// ----------------------------------------------------------------------- + /// IOracle + /// ----------------------------------------------------------------------- + + /// @inheritdoc IOracle + function getPrice() external view override returns (uint256 price) { + /// ----------------------------------------------------------------------- + /// Validation + /// ----------------------------------------------------------------------- + + // The UniswapV3 pool reverts on invalid TWAP queries, so we don't need to + + /// ----------------------------------------------------------------------- + /// Computation + /// ----------------------------------------------------------------------- + + // query Uniswap oracle to get TWAP tick + { + uint32 _twapDuration = secs; + uint32 _twapAgo = ago; + uint32[] memory secondsAgo = new uint32[](2); + secondsAgo[0] = _twapDuration + _twapAgo; + secondsAgo[1] = _twapAgo; + + (int56[] memory tickCumulatives,) = uniswapPool.observe(secondsAgo); + int24 tick = int24((tickCumulatives[1] - tickCumulatives[0]) / int56(int32(_twapDuration))); + + uint256 decimalPrecision = 1e18; + + // from https://optimistic.etherscan.io/address/0xB210CE856631EeEB767eFa666EC7C1C57738d438#code#F5#L49 + uint160 sqrtRatioX96 = TickMath.getSqrtRatioAtTick(tick); + + // Calculate quoteAmount with better precision if it doesn't overflow when multiplied by itself + if (sqrtRatioX96 <= type(uint128).max) { + uint256 ratioX192 = uint256(sqrtRatioX96) * sqrtRatioX96; + price = isToken0 ? FullMath.mulDiv(ratioX192, decimalPrecision, 1 << 192) : FullMath.mulDiv(1 << 192, decimalPrecision, ratioX192); + } else { + uint256 ratioX128 = FullMath.mulDiv(sqrtRatioX96, sqrtRatioX96, 1 << 64); + price = isToken0 ? FullMath.mulDiv(ratioX128, decimalPrecision, 1 << 128) : FullMath.mulDiv(1 << 128, decimalPrecision, ratioX128); + } + } + + // apply minimum price + if (price < minPrice) revert UniswapOracle__BelowMinPrice(); + } + + /// @inheritdoc IOracle + function getTokens() external view override returns (address paymentToken, address underlyingToken) { + if (isToken0) { + return (uniswapPool.token1(), uniswapPool.token0()); + } else { + return (uniswapPool.token0(), uniswapPool.token1()); + } + } + + /// ----------------------------------------------------------------------- + /// Owner functions + /// ----------------------------------------------------------------------- + + /// @notice Updates the oracle parameters. Only callable by the owner. + /// @param secs_ The size of the window to take the TWAP value over in seconds. + /// @param ago_ The number of seconds in the past to take the TWAP from. The window + /// would be (block.timestamp - secs - ago, block.timestamp - ago]. + /// @param minPrice_ The minimum value returned by getPrice(). Maintains a floor for the + /// price to mitigate potential attacks on the TWAP oracle. + function setParams(uint32 secs_, uint32 ago_, uint128 minPrice_) external onlyOwner { + if (secs_ < MIN_SECS) revert UniswapOracle__InvalidWindow(); + secs = secs_; + ago = ago_; + minPrice = minPrice_; + emit SetParams(secs_, ago_, minPrice_); + } +} diff --git a/test/BalancerOracle.t.sol b/test/BalancerOracle.t.sol new file mode 100644 index 0000000..05cc0c6 --- /dev/null +++ b/test/BalancerOracle.t.sol @@ -0,0 +1,197 @@ +// SPDX-License-Identifier: AGPL-3.0 +pragma solidity ^0.8.13; + +import "forge-std/Test.sol"; +import "forge-std/console.sol"; +import {BalancerOracle} from "../src/oracles/BalancerOracle.sol"; +import {IBalancerTwapOracle} from "../src/interfaces/IBalancerTwapOracle.sol"; +import {IVault, IAsset} from "../src/interfaces/IBalancerVault.sol"; +import {IBalancer2TokensPool} from "../src/interfaces/IBalancer2TokensPool.sol"; +import {FixedPointMathLib} from "solmate/utils/FixedPointMathLib.sol"; +import {IERC20} from "forge-std/interfaces/IERC20.sol"; + +struct Params { + IBalancerTwapOracle pair; + address token; + address owner; + uint32 secs; + uint32 ago; + uint128 minPrice; +} + +contract BalancerOracleTest is Test { + using stdStorage for StdStorage; + using FixedPointMathLib for uint256; + + string MAINNET_RPC_URL = vm.envString("MAINNET_RPC_URL"); + uint32 FORK_BLOCK = 18764758; + + address TOKEN_ADDRESS = 0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2; + address PAYMENT_ADDRESS = 0xfd0205066521550D7d7AB19DA8F72bb004b4C341; + address POOL_ADDRESS = 0x9232a548DD9E81BaC65500b5e0d918F8Ba93675C; + + uint256 MULTIPLIER_DENOM = 10000; + + uint256 opFork; + + Params _default; + + function setUp() public { + _default = Params(IBalancerTwapOracle(POOL_ADDRESS), TOKEN_ADDRESS, address(this), 30 minutes, 0, 1000); + opFork = vm.createSelectFork(MAINNET_RPC_URL, FORK_BLOCK); + } + + function test_priceWithinAcceptableRange() public { + BalancerOracle oracle = new BalancerOracle( + _default.pair, + _default.token, + _default.owner, + _default.secs, + _default.ago, + _default.minPrice + ); + + uint256 oraclePrice = oracle.getPrice(); + + uint256 spotPrice = getSpotPrice(address(_default.pair), _default.token); + assertApproxEqRel(oraclePrice, spotPrice, 0.01 ether, "Price delta too big"); // 1% + } + + function test_priceToken1() public { + IVault vault = _default.pair.getVault(); + (address[] memory poolTokens,,) = vault.getPoolTokens(_default.pair.getPoolId()); + + BalancerOracle oracleToken0 = new BalancerOracle( + _default.pair, + poolTokens[0], + _default.owner, + _default.secs, + _default.ago, + _default.minPrice + ); + + BalancerOracle oracleToken1 = new BalancerOracle( + _default.pair, + poolTokens[1], + _default.owner, + _default.secs, + _default.ago, + _default.minPrice + ); + + uint256 priceToken0 = oracleToken0.getPrice(); + uint256 priceToken1 = oracleToken1.getPrice(); + + assertEq(priceToken1, uint256(1e18).divWadUp(priceToken0), "incorrect price"); // 1% + } + + function test_singleBlockManipulation() public { + IVault vault = _default.pair.getVault(); + BalancerOracle oracle = new BalancerOracle( + _default.pair, + _default.token, + _default.owner, + _default.secs, + _default.ago, + _default.minPrice + ); + + address manipulator1 = makeAddr("manipulator"); + deal(TOKEN_ADDRESS, manipulator1, 1000000 ether); + + vm.startPrank(manipulator1); + IERC20(TOKEN_ADDRESS).approve(address(vault), 1000000 ether); + + (address[] memory tokens, uint256[] memory reserves,) = vault.getPoolTokens(_default.pair.getPoolId()); + + // swap 1 token to update oracle to latest block + swap(address(_default.pair), tokens[0], tokens[1], 1, manipulator1); + + // register initial oracle price + uint256 price_1 = oracle.getPrice(); + + swap(address(_default.pair), tokens[0], tokens[1], reserves[0] / 10, manipulator1); + + vm.stopPrank(); + + // check price variation + assertEq(oracle.getPrice(), price_1, "single block price variation"); + } + + function test_priceManipulation(uint256 skipTime) public { + skipTime = bound(skipTime, 1, _default.secs); + IVault vault = _default.pair.getVault(); + BalancerOracle oracle = new BalancerOracle( + _default.pair, + _default.token, + _default.owner, + _default.secs, + _default.ago, + _default.minPrice + ); + + address manipulator1 = makeAddr("manipulator"); + deal(TOKEN_ADDRESS, manipulator1, 1000000 ether); + + vm.startPrank(manipulator1); + + // swap 1 token to update oracle to latest block + IERC20(TOKEN_ADDRESS).approve(address(vault), 1000000 ether); + swap(address(_default.pair), TOKEN_ADDRESS, PAYMENT_ADDRESS, 1, manipulator1); + + // register initial oracle price + uint256 price_1 = oracle.getPrice(); + + // perform a large swap (25% of reserves) + (address[] memory tokens, uint256[] memory reserves,) = vault.getPoolTokens(_default.pair.getPoolId()); + swap(address(_default.pair), tokens[0], tokens[1], reserves[0] / 4, manipulator1); + + vm.stopPrank(); + + // wait + skip(skipTime); + // update block + vm.roll(block.number + 1); + + // oracle price is only updated on swaps + assertEq(price_1, oracle.getPrice(), "price updated"); + + // perform additional, smaller swap + address manipulator2 = makeAddr("manipulator2"); + deal(PAYMENT_ADDRESS, manipulator2, 1); + vm.startPrank(manipulator2); + IERC20(PAYMENT_ADDRESS).approve(address(vault), 1); + swap(address(_default.pair), tokens[1], tokens[0], 1, manipulator2); + vm.stopPrank(); + + // weighted average of the first recorded oracle price and the current spot price + // weighted by the time since the last update + uint256 spotAverage = + ((price_1 * (_default.secs - skipTime)) + (getSpotPrice(address(_default.pair), _default.token) * skipTime)) / _default.secs; + + assertApproxEqRel(spotAverage, oracle.getPrice(), 0.01 ether, "price variance too large"); + } + + function getSpotPrice(address pool, address token) internal view returns (uint256 price) { + IVault vault = IBalancerTwapOracle(pool).getVault(); + bytes32 poolId = IBalancerTwapOracle(pool).getPoolId(); + (address[] memory poolTokens,,) = vault.getPoolTokens(poolId); + + bool isToken0 = token == poolTokens[0]; + (, uint256[] memory balances,) = vault.getPoolTokens(poolId); + uint256[] memory weights = IBalancer2TokensPool(pool).getNormalizedWeights(); + + price = isToken0 + ? (balances[1] * weights[0]).divWadDown(balances[0] * weights[1]) + : (balances[0] * weights[1]).divWadDown(balances[1] * weights[0]); + } + + function swap(address pool, address tokenIn, address tokenOut, uint256 amountIn, address sender) internal returns (uint256 amountOut) { + bytes32 poolId = IBalancerTwapOracle(pool).getPoolId(); + IVault.SingleSwap memory singleSwap = IVault.SingleSwap(poolId, IVault.SwapKind.GIVEN_IN, IAsset(tokenIn), IAsset(tokenOut), amountIn, ""); + + IVault.FundManagement memory funds = IVault.FundManagement(sender, false, payable(sender), false); + + return IVault(IBalancer2TokensPool(pool).getVault()).swap(singleSwap, funds, 0, type(uint256).max); + } +} diff --git a/test/Common.sol b/test/Common.sol new file mode 100644 index 0000000..bdba858 --- /dev/null +++ b/test/Common.sol @@ -0,0 +1,246 @@ +// SPDX-License-Identifier: BUSL-1.1 + +pragma solidity ^0.8.0; + +import "forge-std/Test.sol"; +import {ReaperVaultV2} from "vault-v2/ReaperVaultV2.sol"; +import {IERC20} from "oz/token/ERC20/IERC20.sol"; +import {ReaperSwapper, MinAmountOutData, MinAmountOutKind, IVeloRouter, ISwapRouter, UniV3SwapData} from "vault-v2/ReaperSwapper.sol"; +import {OptionsToken} from "../src/OptionsToken.sol"; +import {SwapProps, ExchangeType} from "../src/helpers/SwapHelper.sol"; +import {ERC1967Proxy} from "oz/proxy/ERC1967/ERC1967Proxy.sol"; +import {ERC20} from "solmate/tokens/ERC20.sol"; +import {DiscountExerciseParams, DiscountExercise} from "../src/exercise/DiscountExercise.sol"; +import {IOracle} from "../src/interfaces/IOracle.sol"; +import {ThenaOracle, IThenaPair} from "../src/oracles/ThenaOracle.sol"; +import {IUniswapV3Factory} from "vault-v2/interfaces/IUniswapV3Factory.sol"; +import {IUniswapV3Pool, UniswapV3Oracle} from "../src/oracles/UniswapV3Oracle.sol"; +import {MockBalancerTwapOracle} from "../test/mocks/MockBalancerTwapOracle.sol"; +import {BalancerOracle} from "../src/oracles/BalancerOracle.sol"; + +error Common__NotYetImplemented(); + +/* Constants */ +uint256 constant NON_ZERO_PROFIT = 1; +uint16 constant PRICE_MULTIPLIER = 5000; // 0.5 +uint256 constant INSTANT_EXIT_FEE = 500; // 0.05 +uint56 constant ORACLE_SECS = 30 minutes; +uint56 constant ORACLE_AGO = 2 minutes; +uint128 constant ORACLE_MIN_PRICE = 1e7; +uint56 constant ORACLE_LARGEST_SAFETY_WINDOW = 24 hours; +uint256 constant ORACLE_MIN_PRICE_DENOM = 10000; +uint256 constant BPS_DENOM = 10_000; +uint256 constant MAX_SUPPLY = 1e27; // the max supply of the options token & the underlying token + +uint256 constant AMOUNT = 2e18; // 2 ETH +address constant REWARDER = 0x6A0406B8103Ec68EE9A713A073C7bD587c5e04aD; +uint256 constant MIN_OATH_FOR_FUZZING = 1e19; + +/* OP */ +address constant OP_POOL_ADDRESSES_PROVIDER_V2 = 0xdDE5dC81e40799750B92079723Da2acAF9e1C6D6; // Granary (aavev2) +// AAVEv3 - 0xa97684ead0e402dC232d5A977953DF7ECBaB3CDb; +address constant OP_WETH = 0x4200000000000000000000000000000000000006; +address constant OP_OATHV1 = 0x39FdE572a18448F8139b7788099F0a0740f51205; +address constant OP_OATHV2 = 0x00e1724885473B63bCE08a9f0a52F35b0979e35A; +address constant OP_CUSDC = 0xEC8FEa79026FfEd168cCf5C627c7f486D77b765F; +address constant OP_GUSDC = 0x7A0FDDBA78FF45D353B1630B77f4D175A00df0c0; +address constant OP_GOP = 0x30091e843deb234EBb45c7E1Da4bBC4C33B3f0B4; +address constant OP_SOOP = 0x8cD6b19A07d754bF36AdEEE79EDF4F2134a8F571; +address constant OP_USDC = 0x7F5c764cBc14f9669B88837ca1490cCa17c31607; +address constant OP_OP = 0x4200000000000000000000000000000000000042; +/* Balancer */ +address constant OP_DATA_PROVIDER = 0x9546F673eF71Ff666ae66d01Fd6E7C6Dae5a9995; +bytes32 constant OP_OATHV1_ETH_BPT = 0xd20f6f1d8a675cdca155cb07b5dc9042c467153f0002000000000000000000bc; // OATHv1/ETH BPT +bytes32 constant OP_OATHV2_ETH_BPT = 0xd13d81af624956327a24d0275cbe54b0ee0e9070000200000000000000000109; // OATHv2/ETH BPT +bytes32 constant OP_BTC_WETH_USDC_BPT = 0x5028497af0c9a54ea8c6d42a054c0341b9fc6168000100000000000000000004; +bytes32 constant OP_WETH_OP_USDC_BPT = 0x39965c9dab5448482cf7e002f583c812ceb53046000100000000000000000003; +address constant OP_BEETX_VAULT = 0xBA12222222228d8Ba445958a75a0704d566BF2C8; +/* Uniswap */ +address constant OP_UNIV3_ROUTERV = 0xE592427A0AEce92De3Edee1F18E0157C05861564; +address constant OP_UNIV3_FACTORY = 0x1F98431c8aD98523631AE4a59f267346ea31F984; +/* Velodrome */ +address constant OP_VELO_OATHV2_ETH_PAIR = 0xc3439bC1A747e545887192d6b7F8BE47f608473F; +address constant OP_VELO_ROUTER = 0xa062aE8A9c5e11aaA026fc2670B0D65cCc8B2858; +address constant OP_VELO_FACTORY = 0xF1046053aa5682b4F9a81b5481394DA16BE5FF5a; + +/* BSC */ +address constant BSC_LENDING_POOL = 0xad441B19a9948c3a3f38C0AB6CCbd853036851d2; +address constant BSC_ADDRESS_PROVIDER = 0xcD2f1565e6d2A83A167FDa6abFc10537d4e984f0; +address constant BSC_DATA_PROVIDER = 0xFa0AC9b741F0868B2a8C4a6001811a5153019818; +address constant BSC_HBR = 0x42c95788F791a2be3584446854c8d9BB01BE88A9; +address constant BSC_USDT = 0x55d398326f99059fF775485246999027B3197955; +address constant BSC_BTCB = 0x7130d2A12B9BCbFAe4f2634d864A1Ee1Ce3Ead9c; +address constant BSC_WBNB = 0xbb4CdB9CBd36B01bD1cBaEBF2De08d9173bc095c; +address constant BSC_GUSDT = 0x686C55C8344E902CD8143Cf4BDF2c5089Be273c5; +address constant BSC_THENA_ROUTER = 0xd4ae6eCA985340Dd434D38F470aCCce4DC78D109; +address constant BSC_THENA_FACTORY = 0x2c788FE40A417612cb654b14a944cd549B5BF130; +address constant BSC_UNIV3_ROUTERV2 = 0xB971eF87ede563556b2ED4b1C0b0019111Dd85d2; +address constant BSC_UNIV3_FACTORY = 0xdB1d10011AD0Ff90774D0C6Bb92e5C5c8b4461F7; +address constant BSC_PANCAKE_ROUTER = 0x1b81D678ffb9C0263b24A97847620C99d213eB14; +address constant BSC_PANCAKE_FACTORY = 0x0BFbCF9fa4f9C56B0F40a671Ad40E0805A091865; +address constant BSC_REWARDER = 0x071c626C75248E4F672bAb8c21c089166F49B615; + +/* ARB */ +address constant ARB_USDCE = 0xFF970A61A04b1cA14834A43f5dE4533eBDDB5CC8; +address constant ARB_USDC = 0xaf88d065e77c8cC2239327C5EDb3A432268e5831; +address constant ARB_RAM = 0xAAA6C1E32C55A7Bfa8066A6FAE9b42650F262418; +address constant ARB_WETH = 0x82aF49447D8a07e3bd95BD0d56f35241523fBab1; +/* Ramses */ +address constant ARB_RAM_ROUTER = 0xAAA87963EFeB6f7E0a2711F397663105Acb1805e; +address constant ARB_RAM_ROUTERV2 = 0xAA23611badAFB62D37E7295A682D21960ac85A90; //univ3 +address constant ARB_RAM_FACTORYV2 = 0xAA2cd7477c451E703f3B9Ba5663334914763edF8; + +/* MODE */ +address constant MODE_MODE = 0xDfc7C877a950e49D2610114102175A06C2e3167a; +address constant MODE_USDC = 0xd988097fb8612cc24eeC14542bC03424c656005f; +address constant MODE_WETH = 0x4200000000000000000000000000000000000006; +/* Velodrome */ +address constant MODE_VELO_USDC_MODE_PAIR = 0x283bA4E204DFcB6381BCBf2cb5d0e765A2B57bC2; // DECIMALS ISSUE +address constant MODE_VELO_WETH_MODE_PAIR = 0x0fba984c97539B3fb49ACDA6973288D0EFA903DB; +address constant MODE_VELO_ROUTER = 0x3a63171DD9BebF4D07BC782FECC7eb0b890C2A45; +address constant MODE_VELO_FACTORY = 0x31832f2a97Fd20664D76Cc421207669b55CE4BC0; +address constant MODE_ADDRESS_PROVIDER = 0xEDc83309549e36f3c7FD8c2C5C54B4c8e5FA00FC; + +contract Common is Test { + IERC20 nativeToken; + IERC20 paymentToken; + IERC20 underlyingToken; + IERC20 wantToken; + IVeloRouter veloRouter; + ISwapRouter swapRouter; + IUniswapV3Factory univ3Factory; + ReaperSwapper reaperSwapper; + MockBalancerTwapOracle underlyingPaymentMock; + + address[] treasuries; + uint256[] feeBPS; + bytes32 paymentUnderlyingBpt; + bytes32 paymentWantBpt; + + address veloFactory; + address pool; + address addressProvider; + address dataProvider; + address rewarder; + address balancerVault; + address owner; + address gWantAddress; + address tokenAdmin; + address strategist = address(4); + address vault; + address management1; + address management2; + address management3; + address keeper; + + uint256 targetLtv = 0.77 ether; + uint256 maxLtv = 0.771 ether; + + OptionsToken optionsToken; + ERC1967Proxy tmpProxy; + OptionsToken optionsTokenProxy; + DiscountExercise exerciser; + + uint256 maxUnderlyingAmount; + + function fixture_setupAccountsAndFees(uint256 fee1, uint256 fee2) public { + /* Setup accounts */ + owner = makeAddr("owner"); + tokenAdmin = makeAddr("tokenAdmin"); + treasuries = new address[](2); + treasuries[0] = makeAddr("treasury1"); + treasuries[1] = makeAddr("treasury2"); + vault = makeAddr("vault"); + management1 = makeAddr("management1"); + management2 = makeAddr("management2"); + management3 = makeAddr("management3"); + keeper = makeAddr("keeper"); + + feeBPS = new uint256[](2); + feeBPS[0] = fee1; + feeBPS[1] = fee2; + } + + /* Functions */ + function fixture_prepareOptionToken(uint256 _amount, address _compounder, address _strategy, OptionsToken _optionsToken, address _tokenAdmin) + public + { + /* Mint options tokens and transfer them to the strategy (rewards simulation) */ + vm.prank(_tokenAdmin); + _optionsToken.mint(_strategy, _amount); + vm.prank(_strategy); + _optionsToken.approve(_compounder, _amount); + } + + function fixture_updateSwapperPaths(ExchangeType exchangeType) public { + address[2] memory paths = [address(underlyingToken), address(paymentToken)]; + + if (exchangeType == ExchangeType.Bal) { + /* Configure balancer like dexes */ + reaperSwapper.updateBalSwapPoolID(paths[0], paths[1], balancerVault, paymentUnderlyingBpt); + reaperSwapper.updateBalSwapPoolID(paths[1], paths[0], balancerVault, paymentUnderlyingBpt); + } else if (exchangeType == ExchangeType.VeloSolid) { + /* Configure thena ram like dexes */ + IVeloRouter.Route[] memory veloPath = new IVeloRouter.Route[](1); + veloPath[0] = IVeloRouter.Route(paths[0], paths[1], false); + reaperSwapper.updateVeloSwapPath(paths[0], paths[1], address(veloRouter), veloPath); + veloPath[0] = IVeloRouter.Route(paths[1], paths[0], false); + reaperSwapper.updateVeloSwapPath(paths[1], paths[0], address(veloRouter), veloPath); + } else if (exchangeType == ExchangeType.UniV3) { + /* Configure univ3 like dexes */ + uint24[] memory univ3Fees = new uint24[](1); + univ3Fees[0] = 500; + address[] memory univ3Path = new address[](2); + + univ3Path[0] = paths[0]; + univ3Path[1] = paths[1]; + UniV3SwapData memory swapPathAndFees = UniV3SwapData(univ3Path, univ3Fees); + reaperSwapper.updateUniV3SwapPath(paths[0], paths[1], address(swapRouter), swapPathAndFees); + } else { + revert Common__NotYetImplemented(); + } + } + + function fixture_getMockedOracle(ExchangeType exchangeType) public returns (IOracle) { + IOracle oracle; + address[] memory _tokens = new address[](2); + _tokens[0] = address(paymentToken); + _tokens[1] = address(underlyingToken); + if (exchangeType == ExchangeType.Bal) { + BalancerOracle underlyingPaymentOracle; + underlyingPaymentMock = new MockBalancerTwapOracle(_tokens); + underlyingPaymentOracle = + new BalancerOracle(underlyingPaymentMock, address(underlyingToken), owner, ORACLE_SECS, ORACLE_AGO, ORACLE_MIN_PRICE); + oracle = underlyingPaymentOracle; + } else if (exchangeType == ExchangeType.VeloSolid) { + IVeloRouter router = IVeloRouter(payable(address(veloRouter))); + ThenaOracle underlyingPaymentOracle; + address pair = router.poolFor(address(underlyingToken), address(paymentToken), false); + underlyingPaymentOracle = new ThenaOracle(IThenaPair(pair), address(underlyingToken), owner, ORACLE_SECS, ORACLE_MIN_PRICE); + oracle = IOracle(address(underlyingPaymentOracle)); + } else if (exchangeType == ExchangeType.UniV3) { + IUniswapV3Pool univ3Pool = IUniswapV3Pool(univ3Factory.getPool(address(underlyingToken), address(paymentToken), 500)); + UniswapV3Oracle univ3Oracle = + new UniswapV3Oracle(univ3Pool, address(paymentToken), owner, uint32(ORACLE_SECS), uint32(ORACLE_AGO), ORACLE_MIN_PRICE); + oracle = IOracle(address(univ3Oracle)); + } else { + revert Common__NotYetImplemented(); + } + return oracle; + } + + function fixture_getSwapProps(ExchangeType exchangeType, uint256 slippage) public view returns (SwapProps memory) { + SwapProps memory swapProps; + + if (exchangeType == ExchangeType.Bal) { + swapProps = SwapProps(address(reaperSwapper), address(swapRouter), ExchangeType.Bal, slippage); + } else if (exchangeType == ExchangeType.VeloSolid) { + swapProps = SwapProps(address(reaperSwapper), address(veloRouter), ExchangeType.VeloSolid, slippage); + } else if (exchangeType == ExchangeType.UniV3) { + swapProps = SwapProps(address(reaperSwapper), address(swapRouter), ExchangeType.UniV3, slippage); + } else { + revert Common__NotYetImplemented(); + } + return swapProps; + } +} diff --git a/test/ItBsc_OptionsToken.t.solNOT b/test/ItBsc_OptionsToken.t.solNOT new file mode 100644 index 0000000..8508344 --- /dev/null +++ b/test/ItBsc_OptionsToken.t.solNOT @@ -0,0 +1,407 @@ +// SPDX-License-Identifier: AGPL-3.0 +pragma solidity ^0.8.13; + +import "forge-std/Test.sol"; + +import {FixedPointMathLib} from "solmate/utils/FixedPointMathLib.sol"; +import {IERC20} from "oz/token/ERC20/IERC20.sol"; +import {ERC1967Proxy} from "oz/proxy/ERC1967/ERC1967Proxy.sol"; + +import {OptionsToken} from "../src/OptionsToken.sol"; +import {DiscountExerciseParams, DiscountExercise, BaseExercise, SwapProps, ExchangeType} from "../src/exercise/DiscountExercise.sol"; +import {TestERC20} from "./mocks/TestERC20.sol"; +import {ThenaOracle} from "../src/oracles/ThenaOracle.sol"; +import {MockBalancerTwapOracle} from "./mocks/MockBalancerTwapOracle.sol"; + +import {ReaperSwapper, MinAmountOutData, MinAmountOutKind, IThenaRamRouter, ISwapRouter, UniV3SwapData} from "vault-v2/ReaperSwapper.sol"; + +contract OptionsTokenTest is Test { + using FixedPointMathLib for uint256; + + uint256 constant FORK_BLOCK = 36349190; + string MAINNET_URL = vm.envString("BSC_RPC_URL"); + + address constant BSC_THENA_ROUTER = 0xd4ae6eCA985340Dd434D38F470aCCce4DC78D109; + address constant BSC_UNIV3_ROUTERV2 = 0xB971eF87ede563556b2ED4b1C0b0019111Dd85d2; + address constant BSC_HBR = 0x42c95788F791a2be3584446854c8d9BB01BE88A9; + address constant BSC_WBNB = 0xbb4CdB9CBd36B01bD1cBaEBF2De08d9173bc095c; + address constant ORACLE_CONTRACT = 0x733D732943aC1333771017e7c9D7b2d5abAdE5C4; + + uint16 constant PRICE_MULTIPLIER = 5000; // 0.5 + uint56 constant ORACLE_SECS = 30 minutes; + uint56 constant ORACLE_AGO = 2 minutes; + uint128 constant ORACLE_MIN_PRICE = 1e17; + uint56 constant ORACLE_LARGEST_SAFETY_WINDOW = 24 hours; + uint256 constant ORACLE_INIT_TWAP_VALUE = 1e19; + uint256 constant ORACLE_MIN_PRICE_DENOM = 10000; + + uint256 constant MAX_SUPPLY = 1e27; // the max supply of the options token & the underlying token + uint256 constant INSTANT_EXIT_FEE = 500; + + address owner; + address tokenAdmin; + address[] feeRecipients_; + uint256[] feeBPS_; + + OptionsToken optionsToken; + DiscountExercise exerciser; + ThenaOracle oracle; + MockBalancerTwapOracle balancerTwapOracle; + IERC20 paymentToken; + address underlyingToken; + ReaperSwapper reaperSwapper; + + function fixture_getSwapProps(ExchangeType exchangeType, uint256 slippage) public view returns (SwapProps memory) { + SwapProps memory swapProps; + + if (exchangeType == ExchangeType.ThenaRam) { + swapProps = SwapProps(address(reaperSwapper), BSC_THENA_ROUTER, ExchangeType.ThenaRam, slippage); + } else if (exchangeType == ExchangeType.UniV3) { + swapProps = SwapProps(address(reaperSwapper), BSC_UNIV3_ROUTERV2, ExchangeType.UniV3, slippage); + } else { + // revert + } + return swapProps; + } + + function fixture_updateSwapperPaths(ExchangeType exchangeType) public { + address[2] memory paths = [address(underlyingToken), address(paymentToken)]; + + if (exchangeType == ExchangeType.ThenaRam) { + /* Configure thena ram like dexes */ + IThenaRamRouter.route[] memory thenaPath = new IThenaRamRouter.route[](1); + thenaPath[0] = IThenaRamRouter.route(paths[0], paths[1], false); + reaperSwapper.updateThenaRamSwapPath(paths[0], paths[1], address(BSC_THENA_ROUTER), thenaPath); + thenaPath[0] = IThenaRamRouter.route(paths[1], paths[0], false); + reaperSwapper.updateThenaRamSwapPath(paths[1], paths[0], address(BSC_THENA_ROUTER), thenaPath); + } else if (exchangeType == ExchangeType.UniV3) { + /* Configure univ3 like dexes */ + uint24[] memory univ3Fees = new uint24[](1); + univ3Fees[0] = 500; + address[] memory univ3Path = new address[](2); + + univ3Path[0] = paths[0]; + univ3Path[1] = paths[1]; + UniV3SwapData memory swapPathAndFees = UniV3SwapData(univ3Path, univ3Fees); + reaperSwapper.updateUniV3SwapPath(paths[0], paths[1], address(BSC_UNIV3_ROUTERV2), swapPathAndFees); + } else { + // revert + } + } + + function setUp() public { + uint256 bscFork = vm.createFork(MAINNET_URL, FORK_BLOCK); + vm.selectFork(bscFork); + + // set up accounts + owner = makeAddr("owner"); + tokenAdmin = makeAddr("tokenAdmin"); + + feeRecipients_ = new address[](2); + feeRecipients_[0] = makeAddr("feeRecipient"); + feeRecipients_[1] = makeAddr("feeRecipient2"); + + feeBPS_ = new uint256[](2); + feeBPS_[0] = 1000; // 10% + feeBPS_[1] = 9000; // 90% + + // deploy contracts + paymentToken = IERC20(BSC_WBNB); + underlyingToken = BSC_HBR; + + address implementation = address(new OptionsToken()); + ERC1967Proxy proxy = new ERC1967Proxy(implementation, ""); + optionsToken = OptionsToken(address(proxy)); + optionsToken.initialize("TIT Call Option Token", "oTIT", tokenAdmin); + optionsToken.transferOwnership(owner); + + /* Reaper deployment and configuration */ + address[] memory strategists = new address[](1); + strategists[0] = makeAddr("strategist"); + reaperSwapper = new ReaperSwapper(); + ERC1967Proxy tmpProxy = new ERC1967Proxy(address(reaperSwapper), ""); + reaperSwapper = ReaperSwapper(address(tmpProxy)); + reaperSwapper.initialize(strategists, address(this), address(this)); + + fixture_updateSwapperPaths(ExchangeType.ThenaRam); + + SwapProps memory swapProps = fixture_getSwapProps(ExchangeType.ThenaRam, 200); + + address[] memory tokens = new address[](2); + tokens[0] = address(paymentToken); + tokens[1] = underlyingToken; + + balancerTwapOracle = new MockBalancerTwapOracle(tokens); + console.log(tokens[0], tokens[1]); + oracle = ThenaOracle(ORACLE_CONTRACT); + exerciser = new DiscountExercise( + optionsToken, + owner, + IERC20(address(paymentToken)), + IERC20(underlyingToken), + oracle, + PRICE_MULTIPLIER, + INSTANT_EXIT_FEE, + feeRecipients_, + feeBPS_, + swapProps + ); + deal(underlyingToken, address(exerciser), 1e20 ether); + + // add exerciser to the list of options + vm.startPrank(owner); + optionsToken.setExerciseContract(address(exerciser), true); + vm.stopPrank(); + + // set up contracts + balancerTwapOracle.setTwapValue(ORACLE_INIT_TWAP_VALUE); + paymentToken.approve(address(exerciser), type(uint256).max); + } + + function test_onlyTokenAdminCanMint(uint256 amount, address hacker) public { + vm.assume(hacker != tokenAdmin); + + // try minting as non token admin + vm.startPrank(hacker); + vm.expectRevert(OptionsToken.OptionsToken__NotTokenAdmin.selector); + optionsToken.mint(address(this), amount); + vm.stopPrank(); + + // mint as token admin + vm.prank(tokenAdmin); + optionsToken.mint(address(this), amount); + + // verify balance + assertEqDecimal(optionsToken.balanceOf(address(this)), amount, 18); + } + + function test_discountExerciseHappyPath(uint256 amount, address recipient) public { + amount = bound(amount, 100, MAX_SUPPLY); + vm.assume(recipient != address(0)); + + // mint options tokens + vm.prank(tokenAdmin); + optionsToken.mint(address(this), amount); + + // mint payment tokens + uint256 expectedPaymentAmount = amount.mulWadUp(oracle.getPrice().mulDivUp(PRICE_MULTIPLIER, ORACLE_MIN_PRICE_DENOM)); + deal(address(paymentToken), address(this), expectedPaymentAmount); + + // exercise options tokens + DiscountExerciseParams memory params = + DiscountExerciseParams({maxPaymentAmount: expectedPaymentAmount, deadline: type(uint256).max, isInstantExit: false}); + (uint256 paymentAmount,,,) = optionsToken.exercise(amount, recipient, address(exerciser), abi.encode(params)); + + // verify options tokens were transferred + assertEqDecimal(optionsToken.balanceOf(address(this)), 0, 18, "user still has options tokens"); + assertEqDecimal(optionsToken.totalSupply(), 0, 18, "option tokens not burned"); + + // verify payment tokens were transferred + assertEqDecimal(paymentToken.balanceOf(address(this)), 0, 18, "user still has payment tokens"); + uint256 paymentFee1 = expectedPaymentAmount.mulDivDown(feeBPS_[0], 10000); + uint256 paymentFee2 = expectedPaymentAmount - paymentFee1; + assertEqDecimal(paymentToken.balanceOf(feeRecipients_[0]), paymentFee1, 18, "fee recipient 1 didn't receive payment tokens"); + assertEqDecimal(paymentToken.balanceOf(feeRecipients_[1]), paymentFee2, 18, "fee recipient 2 didn't receive payment tokens"); + assertEqDecimal(expectedPaymentAmount, paymentAmount, 18, "exercise returned wrong value"); + } + + function test_instantExitExerciseHappyPath(uint256 amount, address recipient) public { + amount = bound(amount, 1e16, 1e22); + vm.assume(recipient != address(0)); + + // mint options tokens + vm.prank(tokenAdmin); + optionsToken.mint(address(this), amount); + + // mint payment tokens + uint256 expectedPaymentAmount = amount.mulWadUp(ORACLE_INIT_TWAP_VALUE.mulDivUp(PRICE_MULTIPLIER, ORACLE_MIN_PRICE_DENOM)); + uint256 discountedUnderlying = amount.mulDivUp(PRICE_MULTIPLIER, 10_000); + uint256 expectedUnderlyingAmount = discountedUnderlying - amount.mulDivUp(INSTANT_EXIT_FEE, 10_000); + deal(address(paymentToken), address(this), expectedPaymentAmount); + console.log("discountedUnderlying:", discountedUnderlying); + console.log("expectedUnderlyingAmount:", expectedUnderlyingAmount); + + // exercise options tokens + DiscountExerciseParams memory params = + DiscountExerciseParams({maxPaymentAmount: expectedPaymentAmount, deadline: type(uint256).max, isInstantExit: true}); + (uint256 paymentAmount,,,) = optionsToken.exercise(amount, recipient, address(exerciser), abi.encode(params)); + + // verify options tokens were transferred + assertEqDecimal(optionsToken.balanceOf(address(this)), 0, 18, "user still has options tokens"); + assertEqDecimal(optionsToken.totalSupply(), 0, 18, "option tokens not burned"); + + // verify payment tokens were transferred + assertEq(paymentToken.balanceOf(address(this)), expectedPaymentAmount, "user lost payment tokens during instant exit"); + uint256 calcPaymentAmount = exerciser.getPaymentAmount(amount); + uint256 totalFee = calcPaymentAmount.mulDivUp(INSTANT_EXIT_FEE, 10_000); + uint256 fee1 = totalFee.mulDivDown(feeBPS_[0], 10_000); + uint256 fee2 = totalFee - fee1; + console.log("paymentFee1: ", fee1); + console.log("paymentFee2: ", fee2); + assertApproxEqRel(IERC20(paymentToken).balanceOf(feeRecipients_[0]), fee1, 10e16, "fee recipient 1 didn't receive payment tokens"); + assertApproxEqRel(IERC20(paymentToken).balanceOf(feeRecipients_[1]), fee2, 10e16, "fee recipient 2 didn't receive payment tokens"); + assertEqDecimal(paymentAmount, 0, 18, "exercise returned wrong value"); + assertApproxEqAbs(IERC20(underlyingToken).balanceOf(recipient), expectedUnderlyingAmount, 1, "Recipient got wrong amount of underlying token"); + } + + function test_exerciseMinPrice(uint256 amount, address recipient) public { + amount = bound(amount, 1, MAX_SUPPLY); + vm.assume(recipient != address(0)); + + // mint options tokens + vm.prank(tokenAdmin); + optionsToken.mint(address(this), amount); + + // set TWAP value such that the strike price is below the oracle's minPrice value + balancerTwapOracle.setTwapValue(ORACLE_MIN_PRICE - 1); + + // mint payment tokens + uint256 expectedPaymentAmount = amount.mulWadUp(ORACLE_MIN_PRICE); + deal(address(paymentToken), address(this), expectedPaymentAmount); + + // exercise options tokens + DiscountExerciseParams memory params = + DiscountExerciseParams({maxPaymentAmount: expectedPaymentAmount, deadline: type(uint256).max, isInstantExit: false}); + vm.expectRevert(bytes4(keccak256("ThenaOracle__BelowMinPrice()"))); + optionsToken.exercise(amount, recipient, address(exerciser), abi.encode(params)); + } + + function test_priceMultiplier(uint256 amount, uint256 multiplier) public { + amount = bound(amount, 1, MAX_SUPPLY / 2); + + vm.prank(owner); + exerciser.setMultiplier(10000); // full price + + // mint options tokens + vm.prank(tokenAdmin); + optionsToken.mint(address(this), amount * 2); + + // mint payment tokens + uint256 expectedPaymentAmount = amount.mulWadUp(ORACLE_INIT_TWAP_VALUE); + deal(address(paymentToken), address(this), expectedPaymentAmount); + + // exercise options tokens + DiscountExerciseParams memory params = + DiscountExerciseParams({maxPaymentAmount: expectedPaymentAmount, deadline: type(uint256).max, isInstantExit: false}); + (uint256 paidAmount,,,) = optionsToken.exercise(amount, address(this), address(exerciser), abi.encode(params)); + + // update multiplier + multiplier = bound(multiplier, 1000, 20000); + vm.prank(owner); + exerciser.setMultiplier(multiplier); + + // exercise options tokens + uint256 newPrice = oracle.getPrice().mulDivUp(multiplier, 10000); + uint256 newExpectedPaymentAmount = amount.mulWadUp(newPrice); + params.maxPaymentAmount = newExpectedPaymentAmount; + + deal(address(paymentToken), address(this), newExpectedPaymentAmount); + (uint256 newPaidAmount,,,) = optionsToken.exercise(amount, address(this), address(exerciser), abi.encode(params)); + // verify payment tokens were transferred + assertEqDecimal(paymentToken.balanceOf(address(this)), 0, 18, "user still has payment tokens"); + assertEq(newPaidAmount, paidAmount.mulDivUp(multiplier, 10000), "incorrect discount"); + } + + function test_exerciseHighSlippage(uint256 amount, address recipient) public { + amount = bound(amount, 1, MAX_SUPPLY); + vm.assume(recipient != address(0)); + + // mint options tokens + vm.prank(tokenAdmin); + optionsToken.mint(address(this), amount); + + // mint payment tokens + uint256 expectedPaymentAmount = amount.mulWadUp(ORACLE_INIT_TWAP_VALUE.mulDivUp(PRICE_MULTIPLIER, ORACLE_MIN_PRICE_DENOM)); + deal(address(paymentToken), address(this), expectedPaymentAmount); + + // exercise options tokens which should fail + DiscountExerciseParams memory params = + DiscountExerciseParams({maxPaymentAmount: expectedPaymentAmount - 1, deadline: type(uint256).max, isInstantExit: false}); + vm.expectRevert(DiscountExercise.Exercise__SlippageTooHigh.selector); + optionsToken.exercise(amount, recipient, address(exerciser), abi.encode(params)); + } + + // function test_exerciseTwapOracleNotReady(uint256 amount, address recipient) public { + // amount = bound(amount, 1, MAX_SUPPLY); + + // // mint options tokens + // vm.prank(tokenAdmin); + // optionsToken.mint(address(this), amount); + + // // mint payment tokens + // uint256 expectedPaymentAmount = amount.mulWadUp(ORACLE_INIT_TWAP_VALUE.mulDivUp(PRICE_MULTIPLIER, ORACLE_MIN_PRICE_DENOM)); + // deal(address(paymentToken), address(this), expectedPaymentAmount); + + // // update oracle params + // // such that the TWAP window becomes (block.timestamp - ORACLE_LARGEST_SAFETY_WINDOW - ORACLE_SECS, block.timestamp - ORACLE_LARGEST_SAFETY_WINDOW] + // // which is outside of the largest safety window + // // vm.prank(owner); + // // oracle.setParams(ORACLE_SECS, ORACLE_LARGEST_SAFETY_WINDOW, ORACLE_MIN_PRICE); + + // // exercise options tokens which should fail + // DiscountExerciseParams memory params = + // DiscountExerciseParams({maxPaymentAmount: expectedPaymentAmount, deadline: type(uint256).max, isInstantExit: false}); + // vm.expectRevert(ThenaOracle.ThenaOracle__TWAPOracleNotReady.selector); + // optionsToken.exercise(amount, recipient, address(exerciser), abi.encode(params)); + // } + + function test_exercisePastDeadline(uint256 amount, address recipient, uint256 deadline) public { + amount = bound(amount, 0, MAX_SUPPLY); + deadline = bound(deadline, 0, block.timestamp - 1); + + // mint options tokens + vm.prank(tokenAdmin); + optionsToken.mint(address(this), amount); + + // mint payment tokens + uint256 expectedPaymentAmount = amount.mulWadUp(ORACLE_INIT_TWAP_VALUE.mulDivUp(PRICE_MULTIPLIER, ORACLE_MIN_PRICE_DENOM)); + deal(address(paymentToken), address(this), expectedPaymentAmount); + + // exercise options tokens + DiscountExerciseParams memory params = + DiscountExerciseParams({maxPaymentAmount: expectedPaymentAmount, deadline: deadline, isInstantExit: false}); + if (amount != 0) { + vm.expectRevert(DiscountExercise.Exercise__PastDeadline.selector); + } + optionsToken.exercise(amount, recipient, address(exerciser), abi.encode(params)); + } + + function test_exerciseNotOToken(uint256 amount, address recipient) public { + amount = bound(amount, 0, MAX_SUPPLY); + + // mint options tokens + vm.prank(tokenAdmin); + optionsToken.mint(address(this), amount); + + uint256 expectedPaymentAmount = amount.mulWadUp(ORACLE_INIT_TWAP_VALUE.mulDivUp(PRICE_MULTIPLIER, ORACLE_MIN_PRICE_DENOM)); + deal(address(paymentToken), address(this), expectedPaymentAmount); + + // exercise options tokens which should fail + DiscountExerciseParams memory params = + DiscountExerciseParams({maxPaymentAmount: expectedPaymentAmount, deadline: type(uint256).max, isInstantExit: false}); + vm.expectRevert(BaseExercise.Exercise__NotOToken.selector); + exerciser.exercise(address(this), amount, recipient, abi.encode(params)); + } + + function test_exerciseNotExerciseContract(uint256 amount, address recipient) public { + amount = bound(amount, 1, MAX_SUPPLY); + + // mint options tokens + vm.prank(tokenAdmin); + optionsToken.mint(address(this), amount); + + // set option inactive + vm.prank(owner); + optionsToken.setExerciseContract(address(exerciser), false); + + // mint payment tokens + uint256 expectedPaymentAmount = amount.mulWadUp(ORACLE_INIT_TWAP_VALUE.mulDivUp(PRICE_MULTIPLIER, ORACLE_MIN_PRICE_DENOM)); + deal(address(paymentToken), address(this), expectedPaymentAmount); + + // exercise options tokens which should fail + DiscountExerciseParams memory params = + DiscountExerciseParams({maxPaymentAmount: expectedPaymentAmount, deadline: type(uint256).max, isInstantExit: false}); + vm.expectRevert(OptionsToken.OptionsToken__NotExerciseContract.selector); + optionsToken.exercise(amount, recipient, address(exerciser), abi.encode(params)); + } +} diff --git a/test/ItMode_OptionsCompounder.t.sol b/test/ItMode_OptionsCompounder.t.sol new file mode 100644 index 0000000..2d67930 --- /dev/null +++ b/test/ItMode_OptionsCompounder.t.sol @@ -0,0 +1,364 @@ +// SPDX-License-Identifier: BUSL-1.1 + +pragma solidity ^0.8.13; + +import "./Common.sol"; + +import {BalancerOracle} from "../src/oracles/BalancerOracle.sol"; +import {MockBalancerTwapOracle} from "../test/mocks/MockBalancerTwapOracle.sol"; +// import {CErc20I} from "./strategies/interfaces/CErc20I.sol"; +// import {Helper} from "./mocks/HelperFunctions.sol"; +import {FixedPointMathLib} from "solmate/utils/FixedPointMathLib.sol"; +// import {IAToken} from "./strategies/interfaces/IAToken.sol"; +// import {ReaperStrategyGranary, Externals} from "./strategies/ReaperStrategyGranary.sol"; +import {OptionsCompounder} from "../src/OptionsCompounder.sol"; +// import {MockedLendingPool} from "../test/mocks/MockedStrategy.sol"; +import {ReaperSwapper, MinAmountOutData, MinAmountOutKind, IVeloRouter, ISwapRouter, UniV3SwapData} from "vault-v2/ReaperSwapper.sol"; + +contract ItModeOptionsCompounder is Common { + using FixedPointMathLib for uint256; + + /* Variable assignment (depends on chain) */ + uint256 constant FORK_BLOCK = 9260950; + string MAINNET_URL = vm.envString("MODE_RPC_URL"); + + /* Contract variables */ + OptionsCompounder optionsCompounder; + // ReaperStrategyGranary strategy; + IOracle oracle; + + address[] strategies; + + function setUp() public { + /* Common assignments */ + ExchangeType exchangeType = ExchangeType.VeloSolid; + nativeToken = IERC20(MODE_WETH); + paymentToken = IERC20(MODE_MODE); + underlyingToken = IERC20(MODE_WETH); + addressProvider = MODE_ADDRESS_PROVIDER; + veloRouter = IVeloRouter(MODE_VELO_ROUTER); + veloFactory = MODE_VELO_FACTORY; + + /* Setup network */ + uint256 fork = vm.createFork(MAINNET_URL, FORK_BLOCK); + vm.selectFork(fork); + + /* Setup accounts */ + fixture_setupAccountsAndFees(3000, 7000); + vm.deal(address(this), AMOUNT * 3); + vm.deal(owner, AMOUNT * 3); + + /* Setup roles */ + address[] memory strategists = new address[](1); + strategists[0] = strategist; + + /* Variables */ + + /** + * Contract deployments and configurations *** + */ + + /* Reaper deployment and configuration */ + reaperSwapper = new ReaperSwapper(); + tmpProxy = new ERC1967Proxy(address(reaperSwapper), ""); + reaperSwapper = ReaperSwapper(address(tmpProxy)); + reaperSwapper.initialize(strategists, address(this), address(this)); + + /* Configure swapper */ + fixture_updateSwapperPaths(exchangeType); + + /* Oracle mocks deployment */ + oracle = fixture_getMockedOracle(exchangeType); + + /* Option token deployment */ + vm.startPrank(owner); + optionsToken = new OptionsToken(); + tmpProxy = new ERC1967Proxy(address(optionsToken), ""); + optionsTokenProxy = OptionsToken(address(tmpProxy)); + optionsTokenProxy.initialize("TIT Call Option Token", "oTIT", tokenAdmin); + + /* Exercise contract deployment */ + SwapProps memory swapProps = fixture_getSwapProps(exchangeType, 200); + uint256 minAmountToTriggerSwap = 1e5; + exerciser = new DiscountExercise( + optionsTokenProxy, + owner, + paymentToken, + underlyingToken, + oracle, + PRICE_MULTIPLIER, + INSTANT_EXIT_FEE, + minAmountToTriggerSwap, + treasuries, + feeBPS, + swapProps + ); + /* Add exerciser to the list of options */ + + optionsTokenProxy.setExerciseContract(address(exerciser), true); + + /* Strategy deployment */ + strategies.push(makeAddr("strategy")); + optionsCompounder = new OptionsCompounder(); + tmpProxy = new ERC1967Proxy(address(optionsCompounder), ""); + optionsCompounder = OptionsCompounder(address(tmpProxy)); + console.log("Initializing..."); + optionsCompounder.initialize(address(optionsTokenProxy), address(addressProvider), swapProps, oracle, strategies); + + vm.stopPrank(); + + /* Prepare EOA and contracts for tests */ + console.log("Dealing payment token.."); + uint256 maxPaymentAmount = AMOUNT * 2; + deal(address(nativeToken), address(this), maxPaymentAmount); + + console.log("Calculation max amount of underlying.."); + maxUnderlyingAmount = maxPaymentAmount.divWadUp(oracle.getPrice()); + console.log("Max underlying amount to distribute: ", maxUnderlyingAmount); + deal(address(underlyingToken), address(exerciser), maxUnderlyingAmount); + underlyingToken.transfer(address(exerciser), maxUnderlyingAmount); + + /* Set up contracts */ + paymentToken.approve(address(exerciser), type(uint256).max); + } + + function test_modeFlashloanPositiveScenario(uint256 amount) public { + console.log("test_modeFlashloanPositiveScenario"); + /* Test vectors definition */ + amount = bound(amount, maxUnderlyingAmount / 10, underlyingToken.balanceOf(address(exerciser))); + uint256 minAmount = 5; + + /* Prepare option tokens - distribute them to the specified strategy + and approve for spending */ + fixture_prepareOptionToken(amount, address(optionsCompounder), strategies[0], optionsTokenProxy, tokenAdmin); + + /* Check balances before compounding */ + uint256 paymentTokenBalance = paymentToken.balanceOf(strategies[0]); + + // vm.startPrank(address(strategy)); + /* already approved in fixture_prepareOptionToken */ + vm.prank(strategies[0]); + optionsCompounder.harvestOTokens(amount, address(exerciser), minAmount); + // vm.stopPrank(); + + /* Assertions */ + assertGt(paymentToken.balanceOf(strategies[0]), paymentTokenBalance + minAmount, "Gain not greater than 0"); + assertEq(optionsTokenProxy.balanceOf(address(optionsCompounder)), 0, "Options token balance in compounder is 0"); + assertEq(paymentToken.balanceOf(address(optionsCompounder)), 0, "Payment token balance in compounder is 0"); + } + + function test_accessControlFunctionsChecks(address hacker, address randomOption, uint256 amount) public { + /* Test vectors definition */ + amount = bound(amount, maxUnderlyingAmount / 10, underlyingToken.balanceOf(address(exerciser))); + vm.assume(randomOption != address(0)); + vm.assume(hacker != owner); + address addressProvider = makeAddr("AddressProvider"); + address[] memory strats = new address[](2); + strats[0] = makeAddr("strat1"); + strats[1] = makeAddr("strat2"); + + SwapProps memory swapProps = SwapProps(address(reaperSwapper), address(swapRouter), ExchangeType.UniV3, 200); + + /* Hacker tries to perform harvest */ + vm.startPrank(hacker); + + /* Hacker tries to manipulate contract configuration */ + vm.expectRevert("Ownable: caller is not the owner"); + optionsCompounder.setOptionsToken(randomOption); + + vm.expectRevert("Ownable: caller is not the owner"); + optionsCompounder.setSwapProps(swapProps); + + vm.expectRevert("Ownable: caller is not the owner"); + optionsCompounder.setOracle(oracle); + + vm.expectRevert("Ownable: caller is not the owner"); + optionsCompounder.setAddressProvider(addressProvider); + + vm.expectRevert("Ownable: caller is not the owner"); + optionsCompounder.setStrats(strats); + + vm.expectRevert("Ownable: caller is not the owner"); + optionsCompounder.addStrat(strats[0]); + + vm.expectRevert(bytes4(keccak256("OptionsCompounder__OnlyStratAllowed()"))); + optionsCompounder.harvestOTokens(amount, address(exerciser), 1); + + vm.stopPrank(); + + /* Admin tries to set different option token */ + vm.startPrank(owner); + optionsCompounder.setOptionsToken(randomOption); + vm.stopPrank(); + assertEq(address(optionsCompounder.optionsToken()), randomOption); + } + + function test_stratsSettings(address randomOption, uint256 amount) public { + /* Test vectors definition */ + amount = bound(amount, maxUnderlyingAmount / 10, underlyingToken.balanceOf(address(exerciser)) / 10); + vm.assume(randomOption != address(0)); + address[] memory strats = new address[](2); + uint256 minAmount = 5; + strats[0] = makeAddr("strat1"); + strats[1] = makeAddr("strat2"); + + fixture_prepareOptionToken(3 * amount, address(optionsCompounder), strats[0], optionsTokenProxy, tokenAdmin); + fixture_prepareOptionToken(3 * amount, address(optionsCompounder), strats[1], optionsTokenProxy, tokenAdmin); + + vm.startPrank(strats[0]); + vm.expectRevert(bytes4(keccak256("OptionsCompounder__OnlyStratAllowed()"))); + optionsCompounder.harvestOTokens(amount, address(exerciser), minAmount); + vm.stopPrank(); + + address[] memory receivedStrategies = optionsCompounder.getStrats(); + for (uint256 idx = 0; idx < receivedStrategies.length; idx++) { + console.log("Strat: %s %s", idx, receivedStrategies[idx]); + } + + vm.prank(owner); + optionsCompounder.setStrats(strats); + + receivedStrategies = optionsCompounder.getStrats(); + for (uint256 idx = 0; idx < receivedStrategies.length; idx++) { + console.log("Strat: %s %s", idx, receivedStrategies[idx]); + } + + assertEq(receivedStrategies.length, 2); + + vm.prank(strats[0]); + optionsCompounder.harvestOTokens(amount, address(exerciser), minAmount); + + vm.prank(strats[1]); + optionsCompounder.harvestOTokens(amount, address(exerciser), minAmount); + + vm.prank(owner); + optionsCompounder.addStrat(strats[1]); + + receivedStrategies = optionsCompounder.getStrats(); + for (uint256 idx = 0; idx < receivedStrategies.length; idx++) { + console.log("Strat: %s %s", idx, receivedStrategies[idx]); + } + assertEq(receivedStrategies.length, 2); + + address[] memory tmpStrats; + vm.prank(owner); + optionsCompounder.setStrats(tmpStrats); + + receivedStrategies = optionsCompounder.getStrats(); + for (uint256 idx = 0; idx < receivedStrategies.length; idx++) { + console.log("Strat: %s %s", idx, receivedStrategies[idx]); + } + assertEq(receivedStrategies.length, 0); + + vm.startPrank(strats[0]); + vm.expectRevert(bytes4(keccak256("OptionsCompounder__OnlyStratAllowed()"))); + optionsCompounder.harvestOTokens(amount, address(exerciser), minAmount); + vm.startPrank(strats[1]); + vm.expectRevert(bytes4(keccak256("OptionsCompounder__OnlyStratAllowed()"))); + optionsCompounder.harvestOTokens(amount, address(exerciser), minAmount); + vm.stopPrank(); + + vm.prank(owner); + optionsCompounder.addStrat(strats[1]); + + receivedStrategies = optionsCompounder.getStrats(); + for (uint256 idx = 0; idx < receivedStrategies.length; idx++) { + console.log("Strat: %s %s", idx, receivedStrategies[idx]); + } + assertEq(receivedStrategies.length, 1); + + vm.startPrank(strats[1]); + optionsCompounder.harvestOTokens(amount, address(exerciser), minAmount); + vm.stopPrank(); + + /* Admin tries to set different option token */ + vm.startPrank(owner); + optionsCompounder.setOptionsToken(randomOption); + vm.stopPrank(); + assertEq(address(optionsCompounder.optionsToken()), randomOption); + } + + function test_flashloanNegativeScenario_highTwapValueAndMultiplier(uint256 amount) public { + /* Test vectors definition */ + amount = bound(amount, maxUnderlyingAmount / 10, underlyingToken.balanceOf(address(exerciser))); + + /* Prepare option tokens - distribute them to the specified strategy + and approve for spending */ + fixture_prepareOptionToken(amount, address(optionsCompounder), strategies[0], optionsTokenProxy, tokenAdmin); + + /* Decrease option discount in order to make redemption not profitable */ + /* Notice: Multiplier must be higher than denom because of oracle inaccuracy (initTwap) or just change initTwap */ + vm.startPrank(owner); + exerciser.setMultiplier(9999); + vm.stopPrank(); + /* Increase TWAP price to make flashloan not profitable */ + + vm.startPrank(strategies[0]); + /* Notice: additional protection is in exerciser: Exercise__SlippageTooHigh */ + vm.expectRevert(bytes4(keccak256("OptionsCompounder__FlashloanNotProfitableEnough()"))); + /* Already approved in fixture_prepareOptionToken */ + optionsCompounder.harvestOTokens(amount, address(exerciser), NON_ZERO_PROFIT); + vm.stopPrank(); + } + + function test_flashloanNegativeScenario_tooHighMinAmounOfWantExpected(uint256 amount, uint256 minAmountOfPayment) public { + /* Test vectors definition */ + amount = bound(amount, maxUnderlyingAmount / 10, underlyingToken.balanceOf(address(exerciser))); + /* Decrease option discount in order to make redemption not profitable */ + /* Notice: Multiplier must be higher than denom because of oracle inaccuracy (initTwap) or just change initTwap */ + vm.startPrank(owner); + exerciser.setMultiplier(9000); + vm.stopPrank(); + /* Too high expectation of profit - together with high exerciser multiplier makes flashloan not profitable */ + uint256 paymentAmount = exerciser.getPaymentAmount(amount); + + minAmountOfPayment = bound(minAmountOfPayment, 1e22, UINT256_MAX - paymentAmount); + + /* Prepare option tokens - distribute them to the specified strategy + and approve for spending */ + fixture_prepareOptionToken(amount, address(optionsCompounder), strategies[0], optionsTokenProxy, tokenAdmin); + + vm.startPrank(strategies[0]); + /* Notice: additional protection is in exerciser: Exercise__SlippageTooHigh */ + vm.expectRevert(bytes4(keccak256("OptionsCompounder__FlashloanNotProfitableEnough()"))); + /* Already approved in fixture_prepareOptionToken */ + optionsCompounder.harvestOTokens(amount, address(exerciser), minAmountOfPayment); + vm.stopPrank(); + } + + function test_callExecuteOperationWithoutFlashloanTrigger(uint256 amount, address executor) public { + /* Test vectors definition */ + amount = bound(amount, maxUnderlyingAmount / 10, underlyingToken.balanceOf(address(exerciser))); + + /* Argument creation */ + address[] memory assets = new address[](1); + assets[0] = address(paymentToken); + uint256[] memory amounts = new uint256[](1); + amounts[0] = DiscountExercise(exerciser).getPaymentAmount(amount); + uint256[] memory premiums = new uint256[](1); + bytes memory params; + + vm.startPrank(executor); + /* Assertion */ + vm.expectRevert(bytes4(keccak256("OptionsCompounder__FlashloanNotTriggered()"))); + optionsCompounder.executeOperation(assets, amounts, premiums, msg.sender, params); + vm.stopPrank(); + } + + function test_harvestCallWithWrongExerciseContract(uint256 amount, address fuzzedExerciser) public { + /* Test vectors definition */ + amount = bound(amount, maxUnderlyingAmount / 10, underlyingToken.balanceOf(address(exerciser))); + + vm.assume(fuzzedExerciser != address(exerciser)); + + /* Prepare option tokens - distribute them to the specified strategy + and approve for spending */ + fixture_prepareOptionToken(amount, address(optionsCompounder), strategies[0], optionsTokenProxy, tokenAdmin); + + /* Assertion */ + vm.startPrank(strategies[0]); + vm.expectRevert(bytes4(keccak256("OptionsCompounder__NotExerciseContract()"))); + optionsCompounder.harvestOTokens(amount, fuzzedExerciser, NON_ZERO_PROFIT); + vm.stopPrank(); + } +} diff --git a/test/ItMode_OptionsToken.t.sol b/test/ItMode_OptionsToken.t.sol new file mode 100644 index 0000000..25f4ce0 --- /dev/null +++ b/test/ItMode_OptionsToken.t.sol @@ -0,0 +1,265 @@ +// SPDX-License-Identifier: AGPL-3.0 +pragma solidity ^0.8.13; + +import "forge-std/Test.sol"; + +import {FixedPointMathLib} from "solmate/utils/FixedPointMathLib.sol"; +import {IERC20} from "oz/token/ERC20/IERC20.sol"; +import {ERC1967Proxy} from "oz/proxy/ERC1967/ERC1967Proxy.sol"; + +import {OptionsToken} from "../src/OptionsToken.sol"; +import {DiscountExerciseParams, DiscountExercise, BaseExercise} from "../src/exercise/DiscountExercise.sol"; +// import {SwapProps, ExchangeType} from "../src/helpers/SwapHelper.sol"; +import {TestERC20} from "./mocks/TestERC20.sol"; +import {ThenaOracle} from "../src/oracles/ThenaOracle.sol"; +import {MockBalancerTwapOracle} from "./mocks/MockBalancerTwapOracle.sol"; + +import {ReaperSwapper, MinAmountOutData, MinAmountOutKind, IVeloRouter, ISwapRouter, UniV3SwapData} from "vault-v2/ReaperSwapper.sol"; + +import "./Common.sol"; + +contract ModeOptionsTokenTest is Test, Common { + using FixedPointMathLib for uint256; + + uint256 constant FORK_BLOCK = 9260950; + string MAINNET_URL = vm.envString("MODE_RPC_URL"); + uint256 constant ORACLE_INIT_TWAP_VALUE = 1e19; + + address[] feeRecipients_; + uint256[] feeBPS_; + + ThenaOracle oracle; + MockBalancerTwapOracle balancerTwapOracle; + + function setUp() public { + /* Common assignments */ + ExchangeType exchangeType = ExchangeType.VeloSolid; + // nativeToken = IERC20(OP_WETH); + paymentToken = IERC20(MODE_MODE); + underlyingToken = IERC20(MODE_WETH); + // wantToken = IERC20(OP_OP); + // paymentUnderlyingBpt = OP_OATHV2_ETH_BPT; + // paymentWantBpt = OP_WETH_OP_USDC_BPT; + // balancerVault = OP_BEETX_VAULT; + // swapRouter = ISwapRouter(OP_BEETX_VAULT); + // univ3Factory = IUniswapV3Factory(OP_UNIV3_FACTORY); + veloRouter = IVeloRouter(MODE_VELO_ROUTER); + veloFactory = MODE_VELO_FACTORY; + + /* Setup network */ + uint256 fork = vm.createFork(MAINNET_URL, FORK_BLOCK); + vm.selectFork(fork); + + // set up accounts + owner = makeAddr("owner"); + tokenAdmin = makeAddr("tokenAdmin"); + + uint256 minAmountToTriggerSwap = 1e5; + + feeRecipients_ = new address[](2); + feeRecipients_[0] = makeAddr("feeRecipient"); + feeRecipients_[1] = makeAddr("feeRecipient2"); + + feeBPS_ = new uint256[](2); + feeBPS_[0] = 1000; // 10% + feeBPS_[1] = 9000; // 90% + + address implementation = address(new OptionsToken()); + ERC1967Proxy proxy = new ERC1967Proxy(implementation, ""); + optionsToken = OptionsToken(address(proxy)); + optionsToken.initialize("TIT Call Option Token", "oTIT", tokenAdmin); + optionsToken.transferOwnership(owner); + + /* Reaper deployment and configuration */ + address[] memory strategists = new address[](1); + strategists[0] = makeAddr("strategist"); + reaperSwapper = new ReaperSwapper(); + ERC1967Proxy tmpProxy = new ERC1967Proxy(address(reaperSwapper), ""); + reaperSwapper = ReaperSwapper(address(tmpProxy)); + reaperSwapper.initialize(strategists, address(this), address(this)); + + fixture_updateSwapperPaths(exchangeType); + + SwapProps memory swapProps = fixture_getSwapProps(exchangeType, 200); + + address[] memory tokens = new address[](2); + tokens[0] = address(paymentToken); + tokens[1] = address(underlyingToken); + + balancerTwapOracle = new MockBalancerTwapOracle(tokens); + console.log(tokens[0], tokens[1]); + oracle = new ThenaOracle(IThenaPair(MODE_VELO_WETH_MODE_PAIR), address(underlyingToken), owner, ORACLE_SECS, uint128(ORACLE_MIN_PRICE_DENOM)); + exerciser = new DiscountExercise( + optionsToken, + owner, + IERC20(address(paymentToken)), + underlyingToken, + oracle, + PRICE_MULTIPLIER, + INSTANT_EXIT_FEE, + minAmountToTriggerSwap, + feeRecipients_, + feeBPS_, + swapProps + ); + deal(address(underlyingToken), address(exerciser), 1e20 ether); + + /* add exerciser to the list of options */ + vm.startPrank(owner); + optionsToken.setExerciseContract(address(exerciser), true); + vm.stopPrank(); + + // set up contracts + balancerTwapOracle.setTwapValue(ORACLE_INIT_TWAP_VALUE); + paymentToken.approve(address(exerciser), type(uint256).max); + } + + function test_modeRedeemPositiveScenario(uint256 amount) public { + amount = bound(amount, 100, MAX_SUPPLY); + address recipient = makeAddr("recipient"); + + // mint options tokens + vm.prank(tokenAdmin); + optionsToken.mint(address(this), amount); + + // mint payment tokens + uint256 expectedPaymentAmount = amount.mulWadUp(oracle.getPrice().mulDivUp(PRICE_MULTIPLIER, ORACLE_MIN_PRICE_DENOM)); + deal(address(paymentToken), address(this), expectedPaymentAmount); + + // exercise options tokens + DiscountExerciseParams memory params = + DiscountExerciseParams({maxPaymentAmount: expectedPaymentAmount, deadline: type(uint256).max, isInstantExit: false}); + (uint256 paymentAmount,,,) = optionsToken.exercise(amount, recipient, address(exerciser), abi.encode(params)); + + // verify options tokens were transferred + assertEqDecimal(optionsToken.balanceOf(address(this)), 0, 18, "user still has options tokens"); + assertEqDecimal(optionsToken.totalSupply(), 0, 18, "option tokens not burned"); + + // verify payment tokens were transferred + assertEqDecimal(paymentToken.balanceOf(address(this)), 0, 18, "user still has payment tokens"); + uint256 paymentFee1 = expectedPaymentAmount.mulDivDown(feeBPS_[0], 10000); + uint256 paymentFee2 = expectedPaymentAmount - paymentFee1; + assertEqDecimal(paymentToken.balanceOf(feeRecipients_[0]), paymentFee1, 18, "fee recipient 1 didn't receive payment tokens"); + assertEqDecimal(paymentToken.balanceOf(feeRecipients_[1]), paymentFee2, 18, "fee recipient 2 didn't receive payment tokens"); + assertEqDecimal(expectedPaymentAmount, paymentAmount, 18, "exercise returned wrong value"); + } + + function test_modeZapPositiveScenario(uint256 amount) public { + amount = bound(amount, 1e16, 1e18 - 1); + address recipient = makeAddr("recipient"); + + // mint options tokens + vm.prank(tokenAdmin); + optionsToken.mint(address(this), amount); + + // mint payment tokens + uint256 expectedPaymentAmount = amount.mulWadUp(oracle.getPrice().mulDivUp(PRICE_MULTIPLIER, ORACLE_MIN_PRICE_DENOM)); + uint256 discountedUnderlying = amount.mulDivUp(PRICE_MULTIPLIER, 10_000); + uint256 expectedUnderlyingAmount = discountedUnderlying - discountedUnderlying.mulDivUp(INSTANT_EXIT_FEE, 10_000); + deal(address(paymentToken), address(this), expectedPaymentAmount); + + // Calculate total fee from zapping + uint256 calcPaymentAmount = exerciser.getPaymentAmount(amount); + uint256 totalFee = calcPaymentAmount.mulDivUp(INSTANT_EXIT_FEE, 10_000); + + vm.prank(owner); + exerciser.setMinAmountToTriggerSwap(discountedUnderlying.mulDivUp(INSTANT_EXIT_FEE, BPS_DENOM) + 1); + + // exercise options tokens + DiscountExerciseParams memory params = + DiscountExerciseParams({maxPaymentAmount: expectedPaymentAmount, deadline: type(uint256).max, isInstantExit: true}); + (uint256 paymentAmount,,,) = optionsToken.exercise(amount, recipient, address(exerciser), abi.encode(params)); + console.log("Exercise 1"); + assertEqDecimal(optionsToken.balanceOf(address(this)), 0, 18, "user still has options tokens"); + assertEqDecimal(optionsToken.totalSupply(), 0, 18, "option tokens not burned"); + + // verify payment tokens were not transferred + assertEq(paymentToken.balanceOf(address(this)), expectedPaymentAmount, "user lost payment tokens during instant exit"); + // verify whether distributions not happened + assertEq(IERC20(paymentToken).balanceOf(feeRecipients_[0]), 0, "fee recipient 1 received payment tokens but shouldn't"); + assertEq(IERC20(paymentToken).balanceOf(feeRecipients_[1]), 0, "fee recipient 2 received payment tokens but shouldn't"); + assertEqDecimal(paymentAmount, 0, 18, "exercise returned wrong value"); + uint256 balanceAfterFirstExercise = underlyingToken.balanceOf(recipient); + assertApproxEqAbs(balanceAfterFirstExercise, expectedUnderlyingAmount, 1, "recipient got wrong amount of underlying token"); + + /*---------- Second call -----------*/ + amount = bound(amount, 1e18, 2e18); + // mint options tokens + vm.prank(tokenAdmin); + optionsToken.mint(address(this), amount); + + // mint payment tokens + expectedPaymentAmount = amount.mulWadUp(oracle.getPrice().mulDivUp(PRICE_MULTIPLIER, ORACLE_MIN_PRICE_DENOM)); + discountedUnderlying = amount.mulDivUp(PRICE_MULTIPLIER, 10_000); + expectedUnderlyingAmount = discountedUnderlying - discountedUnderlying.mulDivUp(INSTANT_EXIT_FEE, 10_000); + deal(address(paymentToken), address(this), expectedPaymentAmount); + + (paymentAmount,,,) = optionsToken.exercise(amount, recipient, address(exerciser), abi.encode(params)); + + // verify options tokens were transferred + console.log("Exercise 2"); + assertEqDecimal(optionsToken.balanceOf(address(this)), 0, 18, "user still has options tokens"); + assertEqDecimal(optionsToken.totalSupply(), 0, 18, "option tokens not burned"); + + // verify payment tokens were not transferred + assertEq(paymentToken.balanceOf(address(this)), expectedPaymentAmount, "user lost payment tokens during instant exit"); + + // verify fee is distributed + calcPaymentAmount = exerciser.getPaymentAmount(amount); + totalFee += calcPaymentAmount.mulDivUp(INSTANT_EXIT_FEE, 10_000); + uint256 fee1 = totalFee.mulDivDown(feeBPS_[0], 10_000); + uint256 fee2 = totalFee - fee1; + console.log("paymentFee1: ", fee1); + console.log("paymentFee2: ", fee2); + assertApproxEqRel(IERC20(paymentToken).balanceOf(feeRecipients_[0]), fee1, 5e16, "fee recipient 1 didn't receive payment tokens"); + assertApproxEqRel(IERC20(paymentToken).balanceOf(feeRecipients_[1]), fee2, 5e16, "fee recipient 2 didn't receive payment tokens"); + assertEqDecimal(paymentAmount, 0, 18, "exercise returned wrong value"); + assertApproxEqAbs( + underlyingToken.balanceOf(recipient), + expectedUnderlyingAmount + balanceAfterFirstExercise, + 1, + "Recipient got wrong amount of underlying token" + ); + } + + function test_modeExerciseNotOToken(uint256 amount) public { + amount = bound(amount, 0, MAX_SUPPLY); + address recipient = makeAddr("recipient"); + + // mint options tokens + vm.prank(tokenAdmin); + optionsToken.mint(address(this), amount); + + uint256 expectedPaymentAmount = amount.mulWadUp(ORACLE_INIT_TWAP_VALUE.mulDivUp(PRICE_MULTIPLIER, ORACLE_MIN_PRICE_DENOM)); + deal(address(paymentToken), address(this), expectedPaymentAmount); + + // exercise options tokens which should fail + DiscountExerciseParams memory params = + DiscountExerciseParams({maxPaymentAmount: expectedPaymentAmount, deadline: type(uint256).max, isInstantExit: false}); + vm.expectRevert(BaseExercise.Exercise__NotOToken.selector); + exerciser.exercise(address(this), amount, recipient, abi.encode(params)); + } + + function test_modeExerciseNotExerciseContract(uint256 amount) public { + amount = bound(amount, 1, MAX_SUPPLY); + address recipient = makeAddr("recipient"); + + // mint options tokens + vm.prank(tokenAdmin); + optionsToken.mint(address(this), amount); + + // set option inactive + vm.prank(owner); + optionsToken.setExerciseContract(address(exerciser), false); + + // mint payment tokens + uint256 expectedPaymentAmount = amount.mulWadUp(ORACLE_INIT_TWAP_VALUE.mulDivUp(PRICE_MULTIPLIER, ORACLE_MIN_PRICE_DENOM)); + deal(address(paymentToken), address(this), expectedPaymentAmount); + + // exercise options tokens which should fail + DiscountExerciseParams memory params = + DiscountExerciseParams({maxPaymentAmount: expectedPaymentAmount, deadline: type(uint256).max, isInstantExit: false}); + vm.expectRevert(OptionsToken.OptionsToken__NotExerciseContract.selector); + optionsToken.exercise(amount, recipient, address(exerciser), abi.encode(params)); + } +} diff --git a/test/ItOp_OptionsCompounder.t.solNOT b/test/ItOp_OptionsCompounder.t.solNOT new file mode 100644 index 0000000..471def8 --- /dev/null +++ b/test/ItOp_OptionsCompounder.t.solNOT @@ -0,0 +1,415 @@ +// SPDX-License-Identifier: BUSL-1.1 + +pragma solidity ^0.8.13; + +import "./Common.sol"; + +// import {ReaperStrategySonne} from "./strategies/ReaperStrategySonne.sol"; +// import {ReaperStrategyGranary} from "./strategies/ReaperStrategyGranary.sol"; +import {CErc20I} from "./strategies/interfaces/CErc20I.sol"; +import {OptionsToken} from "optionsToken/src/OptionsToken.sol"; +import {Helper} from "./mocks/HelperFunctions.sol"; +import {FixedPointMathLib} from "solmate/utils/FixedPointMathLib.sol"; +import {IERC20} from "oz/token/ERC20/IERC20.sol"; +import {IAToken} from "./strategies/interfaces/IAToken.sol"; +import {IOracle} from "optionsToken/src/interfaces/IOracle.sol"; +import {OptionsCompounder} from "../src/OptionsCompounder.sol"; +import {MockedLendingPool} from "../test/mocks/MockedStrategy.sol"; + +contract OptionsTokenTest is Common { + using FixedPointMathLib for uint256; + + /* Variable assignment (depends on chain) */ + uint256 FORK_BLOCK = 115072010; + string MAINNET_URL = vm.envString("OP_RPC_URL_MAINNET"); + + /* Contract variables */ + OptionsCompounder optionsCompounder; + IOracle underlyingPaymentOracle; + UniswapV3Oracle paymentWantOracle; + address strategy = makeAddr("strategy"); + Helper helper; + uint256 initTwap; + IOracle oracle; + + /* Functions */ + function setUp() public { + /* Common assignments */ + ExchangeType exchangeType = ExchangeType.Bal; + nativeToken = IERC20(OP_WETH); + paymentToken = nativeToken; + underlyingToken = IERC20(OP_OATHV2); + wantToken = IERC20(OP_OP); + paymentUnderlyingBpt = OP_OATHV2_ETH_BPT; + paymentWantBpt = OP_WETH_OP_USDC_BPT; + balancerVault = OP_BEETX_VAULT; + swapRouter = ISwapRouter(OP_BEETX_VAULT); + univ3Factory = IUniswapV3Factory(OP_UNIV3_FACTORY); + + /* Setup network */ + uint256 optimismFork = vm.createFork(MAINNET_URL, FORK_BLOCK); + vm.selectFork(optimismFork); + + /* Setup accounts */ + fixture_setupAccountsAndFees(2500, 7500); + vm.deal(address(this), AMOUNT * 3); + vm.deal(owner, AMOUNT * 3); + + /* Setup roles */ + address[] memory strategists = new address[](1); + // address[] memory multisigRoles = new address[](3); + // address[] memory keepers = new address[](1); + strategists[0] = strategist; + // multisigRoles[0] = management1; + // multisigRoles[1] = management2; + // multisigRoles[2] = management3; + // keepers[0] = keeper; + + /* Variables */ + SwapProps memory swapProps = fixture_getSwapProps(exchangeType, 500); + + /**** Contract deployments and configurations ****/ + helper = new Helper(); + + /* Reaper deployment and configuration */ + reaperSwapper = new ReaperSwapper(); + tmpProxy = new ERC1967Proxy(address(reaperSwapper), ""); + reaperSwapper = ReaperSwapper(address(tmpProxy)); + reaperSwapper.initialize(strategists, address(this), address(this)); + + /* Configure swapper */ + fixture_updateSwapperPaths(exchangeType); + + /* Oracle mocks deployment */ + oracle = fixture_getMockedOracle(exchangeType); + + /* Option token deployment */ + vm.startPrank(owner); + optionsToken = new OptionsToken(); + tmpProxy = new ERC1967Proxy(address(optionsToken), ""); + optionsTokenProxy = OptionsToken(address(tmpProxy)); + optionsTokenProxy.initialize( + "TIT Call Option Token", + "oTIT", + tokenAdmin + ); + /* Exercise contract deployment */ + exerciser = new DiscountExercise( + optionsTokenProxy, + owner, + paymentToken, + underlyingToken, + oracle, + PRICE_MULTIPLIER, + treasuries, + feeBPS + ); + /* Add exerciser to the list of options */ + optionsTokenProxy.setExerciseContract(address(exerciser), true); + + /* Deployment */ + optionsCompounder = new OptionsCompounder(); + tmpProxy = new ERC1967Proxy(address(optionsCompounder), ""); + optionsCompounder = OptionsCompounder(address(tmpProxy)); + MockedLendingPool addressProviderAndLendingPoolMock = new MockedLendingPool( + address(optionsCompounder) + ); + optionsCompounder.initialize( + address(optionsTokenProxy), + address(addressProviderAndLendingPoolMock), + address(reaperSwapper), + swapProps, + oracle + ); + vm.stopPrank(); + + /* Prepare EOA and contracts for tests */ + helper.wrapEth{value: AMOUNT * 2}(address(nativeToken)); + + MinAmountOutData memory minAmountOutData = MinAmountOutData( + MinAmountOutKind.Absolute, + 0 + ); + paymentToken.approve(address(reaperSwapper), AMOUNT); + reaperSwapper.swapBal( + address(paymentToken), + address(underlyingToken), + AMOUNT, + minAmountOutData, + OP_BEETX_VAULT + ); + uint256 underlyingBalance = underlyingToken.balanceOf(address(this)); + paymentToken.transfer( + address(addressProviderAndLendingPoolMock), + paymentToken.balanceOf(address(this)) + ); + initTwap = AMOUNT.mulDivUp(1e18, underlyingBalance); // Inaccurate solution but it is not crucial to have real accurate oracle price + underlyingToken.transfer(address(exerciser), underlyingBalance); + + /* Set up contracts - added here to calculate initTwap after swap */ + underlyingPaymentMock.setTwapValue(initTwap); + paymentToken.approve(address(exerciser), type(uint256).max); + } + + function test_flashloanPositiveScenario(uint256 amount) public { + /* Test vectors definition */ + amount = bound( + amount, + MIN_OATH_FOR_FUZZING, + underlyingToken.balanceOf(address(exerciser)) + ); + uint256 minAmount = 200; + /* Prepare option tokens - distribute them to the specified strategy + and approve for spending */ + fixture_prepareOptionToken( + amount, + address(optionsCompounder), + strategy, + optionsTokenProxy, + tokenAdmin + ); + + /* Check balances before compounding */ + uint256 paymentTokenBalance = paymentToken.balanceOf( + address(optionsCompounder) + ); + + vm.startPrank(strategy); + /* already approved in fixture_prepareOptionToken */ + optionsCompounder.harvestOTokens( + amount, + address(exerciser), + NON_ZERO_PROFIT + ); + vm.stopPrank(); + + /* Assertions */ + assertGt( + paymentToken.balanceOf(strategy), + paymentTokenBalance + minAmount, + "Gain not greater than 0" + ); + assertEq( + optionsTokenProxy.balanceOf(address(optionsCompounder)), + 0, + "Options token balance in compounder is 0" + ); + assertEq( + paymentToken.balanceOf(address(optionsCompounder)), + 0, + "Payment token balance in compounder is 0" + ); + } + + function test_accessControlFunctionsChecks( + address hacker, + address randomOption, + uint256 amount + ) public { + /* Test vectors definition */ + amount = bound( + amount, + MIN_OATH_FOR_FUZZING, + underlyingToken.balanceOf(address(exerciser)) + ); + vm.assume(randomOption != address(0)); + vm.assume(hacker != owner); + SwapProps memory swapProps = SwapProps( + address(swapRouter), + ExchangeType.UniV3, + 200 + ); + /* Hacker tries to perform harvest */ + vm.startPrank(hacker); + // vm.expectRevert(bytes4(keccak256("OptionsCompounder__OnlyStratAllowed()"))); + // optionsCompounder.harvestOTokens(amount, address(exerciser), NON_ZERO_PROFIT); + + /* Hacker tries to manipulate contract configuration */ + vm.expectRevert("Ownable: caller is not the owner"); + optionsCompounder.setOptionToken(randomOption); + + vm.expectRevert("Ownable: caller is not the owner"); + optionsCompounder.configSwapProps(swapProps); + + vm.expectRevert("Ownable: caller is not the owner"); + optionsCompounder.setOracle(oracle); + + vm.expectRevert("Ownable: caller is not the owner"); + optionsCompounder.setAddressProvider(strategy); + vm.stopPrank(); + + /* Admin tries to set different option token */ + vm.startPrank(owner); + optionsCompounder.setOptionToken(randomOption); + vm.stopPrank(); + assertEq( + address(optionsCompounder.getOptionTokenAddress()), + randomOption + ); + } + + function test_flashloanNegativeScenario_highTwapValueAndMultiplier( + uint256 amount + ) public { + /* Test vectors definition */ + amount = bound( + amount, + MIN_OATH_FOR_FUZZING, + 1000 * MIN_OATH_FOR_FUZZING + ); + + /* Prepare option tokens - distribute them to the specified strategy + and approve for spending */ + fixture_prepareOptionToken( + amount, + address(optionsCompounder), + strategy, + optionsTokenProxy, + tokenAdmin + ); + + /* Decrease option discount in order to make redemption not profitable */ + /* Notice: Multiplier must be higher than denom because of oracle inaccuracy (initTwap) or just change initTwap */ + vm.startPrank(owner); + exerciser.setMultiplier(9999); + vm.stopPrank(); + /* Increase TWAP price to make flashloan not profitable */ + underlyingPaymentMock.setTwapValue(initTwap + ((initTwap * 10) / 100)); + + /* Notice: additional protection is in exerciser: Exercise__SlippageTooHigh */ + vm.expectRevert( + bytes4( + keccak256("OptionsCompounder__FlashloanNotProfitableEnough()") + ) + ); + + vm.startPrank(strategy); + /* Already approved in fixture_prepareOptionToken */ + optionsCompounder.harvestOTokens( + amount, + address(exerciser), + NON_ZERO_PROFIT + ); + vm.stopPrank(); + } + + function test_flashloanNegativeScenario_tooHighMinAmounOfWantExpected( + uint256 amount, + uint256 minAmountOfPayment + ) public { + /* Test vectors definition */ + amount = bound( + amount, + MIN_OATH_FOR_FUZZING, + underlyingToken.balanceOf(address(exerciser)) + ); + /* Decrease option discount in order to make redemption not profitable */ + /* Notice: Multiplier must be higher than denom because of oracle inaccuracy (initTwap) or just change initTwap */ + vm.startPrank(owner); + exerciser.setMultiplier(9000); + vm.stopPrank(); + /* Too high expectation of profit - together with high exerciser multiplier makes flashloan not profitable */ + uint256 paymentAmount = exerciser.getPaymentAmount(amount); + + minAmountOfPayment = bound( + minAmountOfPayment, + 1e22, + UINT256_MAX - paymentAmount + ); + + /* Prepare option tokens - distribute them to the specified strategy + and approve for spending */ + fixture_prepareOptionToken( + amount, + address(optionsCompounder), + address(this), + optionsTokenProxy, + tokenAdmin + ); + + /* Notice: additional protection is in exerciser: Exercise__SlippageTooHigh */ + vm.expectRevert( + bytes4( + keccak256("OptionsCompounder__FlashloanNotProfitableEnough()") + ) + ); + /* Already approved in fixture_prepareOptionToken */ + // vm.startPrank(strategy); + optionsCompounder.harvestOTokens( + amount, + address(exerciser), + minAmountOfPayment + ); + // vm.stopPrank(); + } + + function test_callExecuteOperationWithoutFlashloanTrigger( + uint256 amount, + address executor + ) public { + /* Test vectors definition */ + amount = bound( + amount, + MIN_OATH_FOR_FUZZING, + underlyingToken.balanceOf(address(exerciser)) + ); + + /* Argument creation */ + address[] memory assets = new address[](1); + assets[0] = address(paymentToken); + uint256[] memory amounts = new uint256[](1); + amounts[0] = DiscountExercise(exerciser).getPaymentAmount(amount); + uint256[] memory premiums = new uint256[](1); + bytes memory params; + + vm.startPrank(executor); + /* Assertion */ + vm.expectRevert( + bytes4(keccak256("OptionsCompounder__FlashloanNotTriggered()")) + ); + optionsCompounder.executeOperation( + assets, + amounts, + premiums, + msg.sender, + params + ); + vm.stopPrank(); + } + + function test_harvestCallWithWrongExerciseContract( + uint256 amount, + address fuzzedExerciser + ) public { + /* Test vectors definition */ + amount = bound( + amount, + MIN_OATH_FOR_FUZZING, + underlyingToken.balanceOf(address(exerciser)) + ); + + vm.assume(fuzzedExerciser != address(exerciser)); + + /* Prepare option tokens - distribute them to the specified strategy + and approve for spending */ + fixture_prepareOptionToken( + amount, + address(optionsCompounder), + strategy, + optionsTokenProxy, + tokenAdmin + ); + + vm.startPrank(strategy); + /* Assertion */ + vm.expectRevert( + bytes4(keccak256("OptionsCompounder__NotExerciseContract()")) + ); + optionsCompounder.harvestOTokens( + amount, + fuzzedExerciser, + NON_ZERO_PROFIT + ); + vm.stopPrank(); + } +} diff --git a/test/ItOp_OptionsToken.t.solNOT b/test/ItOp_OptionsToken.t.solNOT new file mode 100644 index 0000000..9f33caa --- /dev/null +++ b/test/ItOp_OptionsToken.t.solNOT @@ -0,0 +1,315 @@ +// SPDX-License-Identifier: AGPL-3.0 +pragma solidity ^0.8.13; + +import "forge-std/Test.sol"; + +import {FixedPointMathLib} from "solmate/utils/FixedPointMathLib.sol"; +import {IERC20} from "oz/token/ERC20/IERC20.sol"; +import {ERC1967Proxy} from "oz/proxy/ERC1967/ERC1967Proxy.sol"; + +import {OptionsToken} from "../src/OptionsToken.sol"; +import {DiscountExerciseParams, DiscountExercise, BaseExercise} from "../src/exercise/DiscountExercise.sol"; +// import {SwapProps, ExchangeType} from "../src/helpers/SwapHelper.sol"; +import {TestERC20} from "./mocks/TestERC20.sol"; +import {ThenaOracle} from "../src/oracles/ThenaOracle.sol"; +import {MockBalancerTwapOracle} from "./mocks/MockBalancerTwapOracle.sol"; + +import {ReaperSwapper, MinAmountOutData, MinAmountOutKind, IVeloRouter, ISwapRouter, UniV3SwapData} from "vault-v2/ReaperSwapper.sol"; + +import "./Common.sol"; + +contract OpOptionsTokenTest is Test, Common { + using FixedPointMathLib for uint256; + + uint256 constant FORK_BLOCK = 121377470; + string MAINNET_URL = vm.envString("OPTIMISM_RPC_URL"); + + uint16 constant PRICE_MULTIPLIER = 5000; // 0.5 + uint56 constant ORACLE_SECS = 30 minutes; + uint56 constant ORACLE_AGO = 2 minutes; + uint128 constant ORACLE_MIN_PRICE = 1e17; + uint56 constant ORACLE_LARGEST_SAFETY_WINDOW = 24 hours; + uint256 constant ORACLE_INIT_TWAP_VALUE = 1e19; + uint128 constant ORACLE_MIN_PRICE_DENOM = 10000; + + uint256 constant MAX_SUPPLY = 1e27; // the max supply of the options token & the underlying token + uint256 constant INSTANT_EXIT_FEE = 500; + + address[] feeRecipients_; + uint256[] feeBPS_; + + ThenaOracle oracle; + MockBalancerTwapOracle balancerTwapOracle; + + // function fixture_getSwapProps(ExchangeType exchangeType, uint256 slippage) public view returns (SwapProps memory) { + // SwapProps memory swapProps; + + // if (exchangeType == ExchangeType.ThenaRam) { + // swapProps = SwapProps(address(reaperSwapper), BSC_THENA_ROUTER, ExchangeType.ThenaRam, slippage); + // } else if (exchangeType == ExchangeType.UniV3) { + // swapProps = SwapProps(address(reaperSwapper), BSC_UNIV3_ROUTERV2, ExchangeType.UniV3, slippage); + // } else { + // // revert + // } + // return swapProps; + // } + + // function fixture_updateSwapperPaths(ExchangeType exchangeType) public { + // address[2] memory paths = [address(underlyingToken), address(paymentToken)]; + + // if (exchangeType == ExchangeType.ThenaRam) { + // /* Configure thena ram like dexes */ + // IThenaRamRouter.route[] memory thenaPath = new IThenaRamRouter.route[](1); + // thenaPath[0] = IThenaRamRouter.route(paths[0], paths[1], false); + // reaperSwapper.updateThenaRamSwapPath(paths[0], paths[1], address(BSC_THENA_ROUTER), thenaPath); + // thenaPath[0] = IThenaRamRouter.route(paths[1], paths[0], false); + // reaperSwapper.updateThenaRamSwapPath(paths[1], paths[0], address(BSC_THENA_ROUTER), thenaPath); + // } else if (exchangeType == ExchangeType.UniV3) { + // /* Configure univ3 like dexes */ + // uint24[] memory univ3Fees = new uint24[](1); + // univ3Fees[0] = 500; + // address[] memory univ3Path = new address[](2); + + // univ3Path[0] = paths[0]; + // univ3Path[1] = paths[1]; + // UniV3SwapData memory swapPathAndFees = UniV3SwapData(univ3Path, univ3Fees); + // reaperSwapper.updateUniV3SwapPath(paths[0], paths[1], address(BSC_UNIV3_ROUTERV2), swapPathAndFees); + // } else { + // // revert + // } + // } + + function setUp() public { + /* Common assignments */ + ExchangeType exchangeType = ExchangeType.VeloSolid; + nativeToken = IERC20(OP_WETH); + paymentToken = nativeToken; + underlyingToken = IERC20(OP_OATHV2); + wantToken = IERC20(OP_OP); + paymentUnderlyingBpt = OP_OATHV2_ETH_BPT; + paymentWantBpt = OP_WETH_OP_USDC_BPT; + balancerVault = OP_BEETX_VAULT; + swapRouter = ISwapRouter(OP_BEETX_VAULT); + univ3Factory = IUniswapV3Factory(OP_UNIV3_FACTORY); + veloRouter = IVeloRouter(OP_VELO_ROUTER); + veloFactory = OP_VELO_FACTORY; + + /* Setup network */ + uint256 fork = vm.createFork(MAINNET_URL, FORK_BLOCK); + vm.selectFork(fork); + + // set up accounts + owner = makeAddr("owner"); + tokenAdmin = makeAddr("tokenAdmin"); + + feeRecipients_ = new address[](2); + feeRecipients_[0] = makeAddr("feeRecipient"); + feeRecipients_[1] = makeAddr("feeRecipient2"); + + feeBPS_ = new uint256[](2); + feeBPS_[0] = 1000; // 10% + feeBPS_[1] = 9000; // 90% + + address implementation = address(new OptionsToken()); + ERC1967Proxy proxy = new ERC1967Proxy(implementation, ""); + optionsToken = OptionsToken(address(proxy)); + optionsToken.initialize("TIT Call Option Token", "oTIT", tokenAdmin); + optionsToken.transferOwnership(owner); + + /* Reaper deployment and configuration */ + address[] memory strategists = new address[](1); + strategists[0] = makeAddr("strategist"); + reaperSwapper = new ReaperSwapper(); + ERC1967Proxy tmpProxy = new ERC1967Proxy(address(reaperSwapper), ""); + reaperSwapper = ReaperSwapper(address(tmpProxy)); + reaperSwapper.initialize(strategists, address(this), address(this)); + + fixture_updateSwapperPaths(ExchangeType.VeloSolid); + + SwapProps memory swapProps = fixture_getSwapProps(ExchangeType.VeloSolid, 200); + + address[] memory tokens = new address[](2); + tokens[0] = address(paymentToken); + tokens[1] = address(underlyingToken); + + balancerTwapOracle = new MockBalancerTwapOracle(tokens); + console.log(tokens[0], tokens[1]); + oracle = new ThenaOracle(IThenaPair(OP_VELO_OATHV2_ETH_PAIR), address(underlyingToken), owner, ORACLE_SECS, ORACLE_MIN_PRICE_DENOM); + exerciser = new DiscountExercise( + optionsToken, + owner, + IERC20(address(paymentToken)), + underlyingToken, + oracle, + PRICE_MULTIPLIER, + INSTANT_EXIT_FEE, + feeRecipients_, + feeBPS_, + swapProps + ); + deal(address(underlyingToken), address(exerciser), 1e20 ether); + + // add exerciser to the list of options + vm.startPrank(owner); + optionsToken.setExerciseContract(address(exerciser), true); + vm.stopPrank(); + + // set up contracts + balancerTwapOracle.setTwapValue(ORACLE_INIT_TWAP_VALUE); + paymentToken.approve(address(exerciser), type(uint256).max); + } + + function test_opRedeemPositiveScenario(uint256 amount, address recipient) public { + amount = bound(amount, 100, MAX_SUPPLY); + vm.assume(recipient != address(0)); + + // mint options tokens + vm.prank(tokenAdmin); + optionsToken.mint(address(this), amount); + + // mint payment tokens + uint256 expectedPaymentAmount = amount.mulWadUp(oracle.getPrice().mulDivUp(PRICE_MULTIPLIER, ORACLE_MIN_PRICE_DENOM)); + deal(address(paymentToken), address(this), expectedPaymentAmount); + + // exercise options tokens + DiscountExerciseParams memory params = + DiscountExerciseParams({maxPaymentAmount: expectedPaymentAmount, deadline: type(uint256).max, isInstantExit: false}); + (uint256 paymentAmount,,,) = optionsToken.exercise(amount, recipient, address(exerciser), abi.encode(params)); + + // verify options tokens were transferred + assertEqDecimal(optionsToken.balanceOf(address(this)), 0, 18, "user still has options tokens"); + assertEqDecimal(optionsToken.totalSupply(), 0, 18, "option tokens not burned"); + + // verify payment tokens were transferred + assertEqDecimal(paymentToken.balanceOf(address(this)), 0, 18, "user still has payment tokens"); + uint256 paymentFee1 = expectedPaymentAmount.mulDivDown(feeBPS_[0], 10000); + uint256 paymentFee2 = expectedPaymentAmount - paymentFee1; + assertEqDecimal(paymentToken.balanceOf(feeRecipients_[0]), paymentFee1, 18, "fee recipient 1 didn't receive payment tokens"); + assertEqDecimal(paymentToken.balanceOf(feeRecipients_[1]), paymentFee2, 18, "fee recipient 2 didn't receive payment tokens"); + assertEqDecimal(expectedPaymentAmount, paymentAmount, 18, "exercise returned wrong value"); + } + + function test_opZapPositiveScenario(uint256 amount) public { + amount = bound(amount, 1e10, 1e18 - 1); + address recipient = makeAddr("recipient"); + + // mint options tokens + vm.prank(tokenAdmin); + optionsToken.mint(address(this), amount); + + console.log("Fee recipient1 balance: ", IERC20(paymentToken).balanceOf(feeRecipients_[0])); + console.log("Fee recipient2 balance: ", IERC20(paymentToken).balanceOf(feeRecipients_[1])); + + // mint payment tokens + uint256 expectedPaymentAmount = amount.mulWadUp(oracle.getPrice().mulDivUp(PRICE_MULTIPLIER, ORACLE_MIN_PRICE_DENOM)); + console.log("[OUT] Amount: %e\tmultiplier: %e", amount, PRICE_MULTIPLIER); + uint256 discountedUnderlying = amount.mulDivUp(PRICE_MULTIPLIER, 10_000); + uint256 expectedUnderlyingAmount = discountedUnderlying - discountedUnderlying.mulDivUp(INSTANT_EXIT_FEE, 10_000); + deal(address(paymentToken), address(this), expectedPaymentAmount); + + console.log("discountedUnderlying:", discountedUnderlying); + console.log("expectedUnderlyingAmount:", expectedUnderlyingAmount); + + // Calculate total fee from zapping + uint256 calcPaymentAmount = exerciser.getPaymentAmount(amount); + uint256 totalFee = calcPaymentAmount.mulDivUp(INSTANT_EXIT_FEE, 10_000); + + vm.prank(owner); + exerciser.setMinAmountToTriggerSwap(discountedUnderlying.mulDivUp(INSTANT_EXIT_FEE, BPS_DENOM) + 1); + + // exercise options tokens + DiscountExerciseParams memory params = + DiscountExerciseParams({maxPaymentAmount: expectedPaymentAmount, deadline: type(uint256).max, isInstantExit: true}); + (uint256 paymentAmount,,,) = optionsToken.exercise(amount, recipient, address(exerciser), abi.encode(params)); + console.log("Exercise 1"); + assertEqDecimal(optionsToken.balanceOf(address(this)), 0, 18, "user still has options tokens"); + assertEqDecimal(optionsToken.totalSupply(), 0, 18, "option tokens not burned"); + + // verify payment tokens were not transferred + assertEq(paymentToken.balanceOf(address(this)), expectedPaymentAmount, "user lost payment tokens during instant exit"); + //verify whether distributions not happened + assertEq(IERC20(paymentToken).balanceOf(feeRecipients_[0]), 0, "fee recipient 1 received payment tokens but shouldn't"); + assertEq(IERC20(paymentToken).balanceOf(feeRecipients_[1]), 0, "fee recipient 2 received payment tokens but shouldn't"); + assertEqDecimal(paymentAmount, 0, 18, "exercise returned wrong value"); + uint256 balanceAfterFirstExercise = underlyingToken.balanceOf(recipient); + assertApproxEqAbs(balanceAfterFirstExercise, expectedUnderlyingAmount, 1, "Recipient got wrong amount of underlying token"); + + /*---------- Second call -----------*/ + amount = bound(amount, 1e18, 1e24); + // mint options tokens + vm.prank(tokenAdmin); + optionsToken.mint(address(this), amount); + + // mint payment tokens + expectedPaymentAmount = amount.mulWadUp(oracle.getPrice().mulDivUp(PRICE_MULTIPLIER, ORACLE_MIN_PRICE_DENOM)); + discountedUnderlying = amount.mulDivUp(PRICE_MULTIPLIER, 10_000); + expectedUnderlyingAmount = discountedUnderlying - discountedUnderlying.mulDivUp(INSTANT_EXIT_FEE, 10_000); + deal(address(paymentToken), address(this), expectedPaymentAmount); + + (paymentAmount,,,) = optionsToken.exercise(amount, recipient, address(exerciser), abi.encode(params)); + + // verify options tokens were transferred + console.log("Exercise 2"); + assertEqDecimal(optionsToken.balanceOf(address(this)), 0, 18, "user still has options tokens"); + assertEqDecimal(optionsToken.totalSupply(), 0, 18, "option tokens not burned"); + + // verify payment tokens were not transferred + assertEq(paymentToken.balanceOf(address(this)), expectedPaymentAmount, "user lost payment tokens during instant exit"); + + // verify fee is distributed + calcPaymentAmount = exerciser.getPaymentAmount(amount); + totalFee += calcPaymentAmount.mulDivUp(INSTANT_EXIT_FEE, 10_000); + uint256 fee1 = totalFee.mulDivDown(feeBPS_[0], 10_000); + uint256 fee2 = totalFee - fee1; + console.log("paymentFee1: ", fee1); + console.log("paymentFee2: ", fee2); + assertApproxEqRel(IERC20(paymentToken).balanceOf(feeRecipients_[0]), fee1, 5e16, "fee recipient 1 didn't receive payment tokens"); + assertApproxEqRel(IERC20(paymentToken).balanceOf(feeRecipients_[1]), fee2, 5e16, "fee recipient 2 didn't receive payment tokens"); + assertEqDecimal(paymentAmount, 0, 18, "exercise returned wrong value"); + assertApproxEqAbs( + underlyingToken.balanceOf(recipient), + expectedUnderlyingAmount + balanceAfterFirstExercise, + 1, + "Recipient got wrong amount of underlying token" + ); + } + + function test_exerciseNotOToken(uint256 amount, address recipient) public { + amount = bound(amount, 0, MAX_SUPPLY); + + // mint options tokens + vm.prank(tokenAdmin); + optionsToken.mint(address(this), amount); + + uint256 expectedPaymentAmount = amount.mulWadUp(ORACLE_INIT_TWAP_VALUE.mulDivUp(PRICE_MULTIPLIER, ORACLE_MIN_PRICE_DENOM)); + deal(address(paymentToken), address(this), expectedPaymentAmount); + + // exercise options tokens which should fail + DiscountExerciseParams memory params = + DiscountExerciseParams({maxPaymentAmount: expectedPaymentAmount, deadline: type(uint256).max, isInstantExit: false}); + vm.expectRevert(BaseExercise.Exercise__NotOToken.selector); + exerciser.exercise(address(this), amount, recipient, abi.encode(params)); + } + + function test_exerciseNotExerciseContract(uint256 amount, address recipient) public { + amount = bound(amount, 1, MAX_SUPPLY); + + // mint options tokens + vm.prank(tokenAdmin); + optionsToken.mint(address(this), amount); + + // set option inactive + vm.prank(owner); + optionsToken.setExerciseContract(address(exerciser), false); + + // mint payment tokens + uint256 expectedPaymentAmount = amount.mulWadUp(ORACLE_INIT_TWAP_VALUE.mulDivUp(PRICE_MULTIPLIER, ORACLE_MIN_PRICE_DENOM)); + deal(address(paymentToken), address(this), expectedPaymentAmount); + + // exercise options tokens which should fail + DiscountExerciseParams memory params = + DiscountExerciseParams({maxPaymentAmount: expectedPaymentAmount, deadline: type(uint256).max, isInstantExit: false}); + vm.expectRevert(OptionsToken.OptionsToken__NotExerciseContract.selector); + optionsToken.exercise(amount, recipient, address(exerciser), abi.encode(params)); + } +} diff --git a/test/OptionsToken.t.sol b/test/OptionsToken.t.sol index bd8c3d4..d09b8b7 100644 --- a/test/OptionsToken.t.sol +++ b/test/OptionsToken.t.sol @@ -4,17 +4,22 @@ pragma solidity ^0.8.13; import "forge-std/Test.sol"; import {FixedPointMathLib} from "solmate/utils/FixedPointMathLib.sol"; +import {IERC20} from "oz/token/ERC20/IERC20.sol"; +import {ERC1967Proxy} from "oz/proxy/ERC1967/ERC1967Proxy.sol"; import {OptionsToken} from "../src/OptionsToken.sol"; -import {TestERC20Mintable} from "./mocks/TestERC20Mintable.sol"; +import {DiscountExerciseParams, DiscountExercise, BaseExercise, SwapProps, ExchangeType} from "../src/exercise/DiscountExercise.sol"; +import {TestERC20} from "./mocks/TestERC20.sol"; +import {IOracle} from "../src/interfaces/IOracle.sol"; import {BalancerOracle} from "../src/oracles/BalancerOracle.sol"; -import {IERC20Mintable} from "../src/interfaces/IERC20Mintable.sol"; import {MockBalancerTwapOracle} from "./mocks/MockBalancerTwapOracle.sol"; +import {ReaperSwapperMock} from "./mocks/ReaperSwapperMock.sol"; + contract OptionsTokenTest is Test { using FixedPointMathLib for uint256; - uint16 constant ORACLE_MULTIPLIER = 5000; // 0.5 + uint16 constant PRICE_MULTIPLIER = 5000; // 0.5 uint56 constant ORACLE_SECS = 30 minutes; uint56 constant ORACLE_AGO = 2 minutes; uint128 constant ORACLE_MIN_PRICE = 1e17; @@ -22,35 +27,86 @@ contract OptionsTokenTest is Test { uint256 constant ORACLE_INIT_TWAP_VALUE = 1e19; uint256 constant ORACLE_MIN_PRICE_DENOM = 10000; uint256 constant MAX_SUPPLY = 1e27; // the max supply of the options token & the underlying token + uint256 constant INSTANT_EXIT_FEE = 500; + uint256 constant BPS_DENOM = 10_000; address owner; address tokenAdmin; - address treasury; + address[] feeRecipients_; + uint256[] feeBPS_; OptionsToken optionsToken; - BalancerOracle oracle; + DiscountExercise exerciser; + IOracle oracle; MockBalancerTwapOracle balancerTwapOracle; - TestERC20Mintable paymentToken; - IERC20Mintable underlyingToken; + TestERC20 paymentToken; + address underlyingToken; + ReaperSwapperMock reaperSwapper; function setUp() public { // set up accounts owner = makeAddr("owner"); tokenAdmin = makeAddr("tokenAdmin"); - treasury = makeAddr("treasury"); + + feeRecipients_ = new address[](2); + feeRecipients_[0] = makeAddr("feeRecipient"); + feeRecipients_[1] = makeAddr("feeRecipient2"); + + feeBPS_ = new uint256[](2); + feeBPS_[0] = 1000; // 10% + feeBPS_[1] = 9000; // 90% // deploy contracts - balancerTwapOracle = new MockBalancerTwapOracle(); - oracle = - new BalancerOracle(balancerTwapOracle, owner, ORACLE_MULTIPLIER, ORACLE_SECS, ORACLE_AGO, ORACLE_MIN_PRICE); - paymentToken = new TestERC20Mintable(); - underlyingToken = IERC20Mintable(address(new TestERC20Mintable())); - optionsToken = - new OptionsToken("TIT Call Option Token", "oTIT", owner, tokenAdmin, paymentToken, underlyingToken, oracle, treasury); + paymentToken = new TestERC20(); + underlyingToken = address(new TestERC20()); + + address implementation = address(new OptionsToken()); + ERC1967Proxy proxy = new ERC1967Proxy(implementation, ""); + optionsToken = OptionsToken(address(proxy)); + optionsToken.initialize("TIT Call Option Token", "oTIT", tokenAdmin); + optionsToken.transferOwnership(owner); + + /* Reaper deployment and configuration */ + uint256 slippage = 500; // 5% + uint256 minAmountToTriggerSwap = 1e5; + + address[] memory tokens = new address[](2); + tokens[0] = address(paymentToken); + tokens[1] = underlyingToken; + + balancerTwapOracle = new MockBalancerTwapOracle(tokens); + console.log(tokens[0], tokens[1]); + oracle = IOracle(new BalancerOracle(balancerTwapOracle, underlyingToken, owner, ORACLE_SECS, ORACLE_AGO, ORACLE_MIN_PRICE)); + + reaperSwapper = new ReaperSwapperMock(oracle, address(underlyingToken), address(paymentToken)); + deal(underlyingToken, address(reaperSwapper), 1e27); + deal(address(paymentToken), address(reaperSwapper), 1e27); + + SwapProps memory swapProps = SwapProps(address(reaperSwapper), address(reaperSwapper), ExchangeType.Bal, slippage); + + exerciser = new DiscountExercise( + optionsToken, + owner, + IERC20(address(paymentToken)), + IERC20(underlyingToken), + oracle, + PRICE_MULTIPLIER, + INSTANT_EXIT_FEE, + minAmountToTriggerSwap, + feeRecipients_, + feeBPS_, + swapProps + ); + deal(underlyingToken, address(exerciser), 1e27); + + // add exerciser to the list of options + vm.startPrank(owner); + optionsToken.setExerciseContract(address(exerciser), true); + vm.stopPrank(); // set up contracts balancerTwapOracle.setTwapValue(ORACLE_INIT_TWAP_VALUE); - paymentToken.approve(address(optionsToken), type(uint256).max); + paymentToken.approve(address(exerciser), type(uint256).max); } function test_onlyTokenAdminCanMint(uint256 amount, address hacker) public { @@ -58,7 +114,7 @@ contract OptionsTokenTest is Test { // try minting as non token admin vm.startPrank(hacker); - vm.expectRevert(bytes4(keccak256("OptionsToken__NotTokenAdmin()"))); + vm.expectRevert(OptionsToken.OptionsToken__NotTokenAdmin.selector); optionsToken.mint(address(this), amount); vm.stopPrank(); @@ -70,119 +126,446 @@ contract OptionsTokenTest is Test { assertEqDecimal(optionsToken.balanceOf(address(this)), amount, 18); } - function test_exerciseHappyPath(uint256 amount, address recipient) public { - amount = bound(amount, 0, MAX_SUPPLY); + function test_redeemPositiveScenario(uint256 amount) public { + amount = bound(amount, 100, MAX_SUPPLY); + address recipient = makeAddr("recipient"); // mint options tokens vm.prank(tokenAdmin); optionsToken.mint(address(this), amount); // mint payment tokens - uint256 expectedPaymentAmount = - amount.mulWadUp(ORACLE_INIT_TWAP_VALUE.mulDivUp(ORACLE_MULTIPLIER, ORACLE_MIN_PRICE_DENOM)); - paymentToken.mint(address(this), expectedPaymentAmount); + uint256 expectedPaymentAmount = amount.mulWadUp(oracle.getPrice().mulDivUp(PRICE_MULTIPLIER, ORACLE_MIN_PRICE_DENOM)); + deal(address(paymentToken), address(this), expectedPaymentAmount); // exercise options tokens - uint256 actualPaymentAmount = optionsToken.exercise(amount, expectedPaymentAmount, recipient); + DiscountExerciseParams memory params = + DiscountExerciseParams({maxPaymentAmount: expectedPaymentAmount, deadline: type(uint256).max, isInstantExit: false}); + (uint256 paymentAmount,,,) = optionsToken.exercise(amount, recipient, address(exerciser), abi.encode(params)); // verify options tokens were transferred assertEqDecimal(optionsToken.balanceOf(address(this)), 0, 18, "user still has options tokens"); - assertEqDecimal(optionsToken.balanceOf(address(0)), amount, 18, "address(0) didn't get options tokens"); - assertEqDecimal(optionsToken.totalSupply(), amount, 18, "total supply changed"); + assertEqDecimal(optionsToken.totalSupply(), 0, 18, "option tokens not burned"); // verify payment tokens were transferred assertEqDecimal(paymentToken.balanceOf(address(this)), 0, 18, "user still has payment tokens"); - assertEqDecimal( - paymentToken.balanceOf(treasury), expectedPaymentAmount, 18, "treasury didn't receive payment tokens" - ); - assertEqDecimal(actualPaymentAmount, expectedPaymentAmount, 18, "exercise returned wrong value"); + uint256 paymentFee1 = expectedPaymentAmount.mulDivDown(feeBPS_[0], 10000); + uint256 paymentFee2 = expectedPaymentAmount - paymentFee1; + assertEqDecimal(paymentToken.balanceOf(feeRecipients_[0]), paymentFee1, 18, "fee recipient 1 didn't receive payment tokens"); + assertEqDecimal(paymentToken.balanceOf(feeRecipients_[1]), paymentFee2, 18, "fee recipient 2 didn't receive payment tokens"); + assertEqDecimal(expectedPaymentAmount, paymentAmount, 18, "exercise returned wrong value"); } - function test_exerciseMinPrice(uint256 amount, address recipient) public { - amount = bound(amount, 0, MAX_SUPPLY); + function test_zapPositiveScenario(uint256 amount) public { + amount = bound(amount, 1e16, 1e22); + address recipient = makeAddr("recipient"); + + // mint options tokens + vm.prank(tokenAdmin); + optionsToken.mint(address(this), amount); + + // mint payment tokens + uint256 expectedPaymentAmount = amount.mulWadUp(ORACLE_INIT_TWAP_VALUE.mulDivUp(PRICE_MULTIPLIER, ORACLE_MIN_PRICE_DENOM)); + uint256 discountedUnderlying = amount.mulDivUp(PRICE_MULTIPLIER, 10_000); + uint256 expectedUnderlyingAmount = discountedUnderlying - discountedUnderlying.mulDivUp(INSTANT_EXIT_FEE, 10_000); + deal(address(paymentToken), address(this), expectedPaymentAmount); + console.log("discountedUnderlying:", discountedUnderlying); + console.log("expectedUnderlyingAmount:", expectedUnderlyingAmount); + + // exercise options tokens + DiscountExerciseParams memory params = + DiscountExerciseParams({maxPaymentAmount: expectedPaymentAmount, deadline: type(uint256).max, isInstantExit: true}); + (uint256 paymentAmount,,,) = optionsToken.exercise(amount, recipient, address(exerciser), abi.encode(params)); + + // verify options tokens were transferred + assertEqDecimal(optionsToken.balanceOf(address(this)), 0, 18, "user still has options tokens"); + assertEqDecimal(optionsToken.totalSupply(), 0, 18, "option tokens not burned"); + + // verify payment tokens were transferred + assertEq(paymentToken.balanceOf(address(this)), expectedPaymentAmount, "user lost payment tokens during instant exit"); + uint256 calcPaymentAmount = exerciser.getPaymentAmount(amount); + uint256 totalFee = calcPaymentAmount.mulDivUp(INSTANT_EXIT_FEE, 10_000); + uint256 fee1 = totalFee.mulDivDown(feeBPS_[0], 10_000); + uint256 fee2 = totalFee - fee1; + console.log("paymentFee1: ", fee1); + console.log("paymentFee2: ", fee2); + assertApproxEqRel(paymentToken.balanceOf(feeRecipients_[0]), fee1, 10e16, "fee recipient 1 didn't receive payment tokens"); + assertApproxEqRel(paymentToken.balanceOf(feeRecipients_[1]), fee2, 10e16, "fee recipient 2 didn't receive payment tokens"); + assertEqDecimal(paymentAmount, 0, 18, "exercise returned wrong value"); + assertApproxEqAbs(IERC20(underlyingToken).balanceOf(recipient), expectedUnderlyingAmount, 1, "Recipient got wrong amount of underlying token"); + } + + function test_exerciseMinPrice(uint256 amount) public { + amount = bound(amount, 1, MAX_SUPPLY); + address recipient = makeAddr("recipient"); // mint options tokens vm.prank(tokenAdmin); optionsToken.mint(address(this), amount); // set TWAP value such that the strike price is below the oracle's minPrice value - balancerTwapOracle.setTwapValue(0); + balancerTwapOracle.setTwapValue(ORACLE_MIN_PRICE - 1); // mint payment tokens uint256 expectedPaymentAmount = amount.mulWadUp(ORACLE_MIN_PRICE); - paymentToken.mint(address(this), expectedPaymentAmount); + deal(address(paymentToken), address(this), expectedPaymentAmount); // exercise options tokens - uint256 actualPaymentAmount = optionsToken.exercise(amount, expectedPaymentAmount, recipient); + DiscountExerciseParams memory params = + DiscountExerciseParams({maxPaymentAmount: expectedPaymentAmount, deadline: type(uint256).max, isInstantExit: false}); + vm.expectRevert(bytes4(keccak256("BalancerOracle__BelowMinPrice()"))); + optionsToken.exercise(amount, recipient, address(exerciser), abi.encode(params)); + } - // verify options tokens were transferred - assertEqDecimal(optionsToken.balanceOf(address(this)), 0, 18, "user still has options tokens"); - assertEqDecimal(optionsToken.balanceOf(address(0)), amount, 18, "address(0) didn't get options tokens"); - assertEqDecimal(optionsToken.totalSupply(), amount, 18, "total supply changed"); + function test_priceMultiplier(uint256 amount, uint256 multiplier) public { + amount = bound(amount, 1, MAX_SUPPLY / 2); + + vm.prank(owner); + exerciser.setMultiplier(10000); // full price + // mint options tokens + vm.prank(tokenAdmin); + optionsToken.mint(address(this), amount * 2); + + // mint payment tokens + uint256 expectedPaymentAmount = amount.mulWadUp(ORACLE_INIT_TWAP_VALUE); + deal(address(paymentToken), address(this), expectedPaymentAmount); + + // exercise options tokens + DiscountExerciseParams memory params = + DiscountExerciseParams({maxPaymentAmount: expectedPaymentAmount, deadline: type(uint256).max, isInstantExit: false}); + (uint256 paidAmount,,,) = optionsToken.exercise(amount, address(this), address(exerciser), abi.encode(params)); + + // update multiplier + multiplier = bound(multiplier, 1000, 20000); + vm.prank(owner); + exerciser.setMultiplier(multiplier); + + // exercise options tokens + uint256 newPrice = oracle.getPrice().mulDivUp(multiplier, 10000); + uint256 newExpectedPaymentAmount = amount.mulWadUp(newPrice); + params.maxPaymentAmount = newExpectedPaymentAmount; + + deal(address(paymentToken), address(this), newExpectedPaymentAmount); + (uint256 newPaidAmount,,,) = optionsToken.exercise(amount, address(this), address(exerciser), abi.encode(params)); // verify payment tokens were transferred assertEqDecimal(paymentToken.balanceOf(address(this)), 0, 18, "user still has payment tokens"); - assertEqDecimal( - paymentToken.balanceOf(treasury), expectedPaymentAmount, 18, "treasury didn't receive payment tokens" - ); - assertEqDecimal(actualPaymentAmount, expectedPaymentAmount, 18, "exercise returned wrong value"); + assertEq(newPaidAmount, paidAmount.mulDivUp(multiplier, 10000), "incorrect discount"); } function test_exerciseHighSlippage(uint256 amount, address recipient) public { amount = bound(amount, 1, MAX_SUPPLY); + vm.assume(recipient != address(0)); // mint options tokens vm.prank(tokenAdmin); optionsToken.mint(address(this), amount); // mint payment tokens - uint256 expectedPaymentAmount = - amount.mulWadUp(ORACLE_INIT_TWAP_VALUE.mulDivUp(ORACLE_MULTIPLIER, ORACLE_MIN_PRICE_DENOM)); - paymentToken.mint(address(this), expectedPaymentAmount); + uint256 expectedPaymentAmount = amount.mulWadUp(ORACLE_INIT_TWAP_VALUE.mulDivUp(PRICE_MULTIPLIER, ORACLE_MIN_PRICE_DENOM)); + deal(address(paymentToken), address(this), expectedPaymentAmount); // exercise options tokens which should fail - vm.expectRevert(bytes4(keccak256("OptionsToken__SlippageTooHigh()"))); - optionsToken.exercise(amount, expectedPaymentAmount - 1, recipient); + DiscountExerciseParams memory params = + DiscountExerciseParams({maxPaymentAmount: expectedPaymentAmount - 1, deadline: type(uint256).max, isInstantExit: false}); + vm.expectRevert(DiscountExercise.Exercise__SlippageTooHigh.selector); + optionsToken.exercise(amount, recipient, address(exerciser), abi.encode(params)); } - function test_exerciseTwapOracleNotReady(uint256 amount, address recipient) public { - amount = bound(amount, 1, MAX_SUPPLY); + // function test_exerciseTwapOracleNotReady(uint256 amount, address recipient) public { + // amount = bound(amount, 1, MAX_SUPPLY); + + // // mint options tokens + // vm.prank(tokenAdmin); + // optionsToken.mint(address(this), amount); + + // // mint payment tokens + // uint256 expectedPaymentAmount = amount.mulWadUp(ORACLE_INIT_TWAP_VALUE.mulDivUp(PRICE_MULTIPLIER, ORACLE_MIN_PRICE_DENOM)); + // deal(address(paymentToken), address(this), expectedPaymentAmount); + + // // update oracle params + // // such that the TWAP window becomes (block.timestamp - ORACLE_LARGEST_SAFETY_WINDOW - ORACLE_SECS, block.timestamp - ORACLE_LARGEST_SAFETY_WINDOW] + // // which is outside of the largest safety window + // // vm.prank(owner); + // // oracle.setParams(ORACLE_SECS, ORACLE_LARGEST_SAFETY_WINDOW, ORACLE_MIN_PRICE); + + // // exercise options tokens which should fail + // DiscountExerciseParams memory params = + // DiscountExerciseParams({maxPaymentAmount: expectedPaymentAmount, deadline: type(uint256).max, isInstantExit: false}); + // vm.expectRevert(ThenaOracle.ThenaOracle__TWAPOracleNotReady.selector); + // optionsToken.exercise(amount, recipient, address(exerciser), abi.encode(params)); + // } + + function test_exercisePastDeadline(uint256 amount, uint256 deadline) public { + amount = bound(amount, 0, MAX_SUPPLY); + deadline = bound(deadline, 0, block.timestamp - 1); + address recipient = makeAddr("recipient"); // mint options tokens vm.prank(tokenAdmin); optionsToken.mint(address(this), amount); // mint payment tokens - uint256 expectedPaymentAmount = - amount.mulWadUp(ORACLE_INIT_TWAP_VALUE.mulDivUp(ORACLE_MULTIPLIER, ORACLE_MIN_PRICE_DENOM)); - paymentToken.mint(address(this), expectedPaymentAmount); + uint256 expectedPaymentAmount = amount.mulWadUp(ORACLE_INIT_TWAP_VALUE.mulDivUp(PRICE_MULTIPLIER, ORACLE_MIN_PRICE_DENOM)); + deal(address(paymentToken), address(this), expectedPaymentAmount); + + // exercise options tokens + DiscountExerciseParams memory params = + DiscountExerciseParams({maxPaymentAmount: expectedPaymentAmount, deadline: deadline, isInstantExit: false}); + if (amount != 0) { + vm.expectRevert(DiscountExercise.Exercise__PastDeadline.selector); + } + optionsToken.exercise(amount, recipient, address(exerciser), abi.encode(params)); + } + + function test_exerciseNotOToken(uint256 amount) public { + amount = bound(amount, 0, MAX_SUPPLY); + address recipient = makeAddr("recipient"); + + // mint options tokens + vm.prank(tokenAdmin); + optionsToken.mint(address(this), amount); + + uint256 expectedPaymentAmount = amount.mulWadUp(ORACLE_INIT_TWAP_VALUE.mulDivUp(PRICE_MULTIPLIER, ORACLE_MIN_PRICE_DENOM)); + deal(address(paymentToken), address(this), expectedPaymentAmount); + + // exercise options tokens which should fail + DiscountExerciseParams memory params = + DiscountExerciseParams({maxPaymentAmount: expectedPaymentAmount, deadline: type(uint256).max, isInstantExit: false}); + vm.expectRevert(BaseExercise.Exercise__NotOToken.selector); + exerciser.exercise(address(this), amount, recipient, abi.encode(params)); + } + + function test_exerciseNotExerciseContract(uint256 amount) public { + amount = bound(amount, 1, MAX_SUPPLY); + address recipient = makeAddr("recipient"); + + // mint options tokens + vm.prank(tokenAdmin); + optionsToken.mint(address(this), amount); - // update oracle params - // such that the TWAP window becomes (block.timestamp - ORACLE_LARGEST_SAFETY_WINDOW - ORACLE_SECS, block.timestamp - ORACLE_LARGEST_SAFETY_WINDOW] - // which is outside of the largest safety window + // set option inactive vm.prank(owner); - oracle.setParams(ORACLE_MULTIPLIER, ORACLE_SECS, ORACLE_LARGEST_SAFETY_WINDOW, ORACLE_MIN_PRICE); + optionsToken.setExerciseContract(address(exerciser), false); + + // mint payment tokens + uint256 expectedPaymentAmount = amount.mulWadUp(ORACLE_INIT_TWAP_VALUE.mulDivUp(PRICE_MULTIPLIER, ORACLE_MIN_PRICE_DENOM)); + deal(address(paymentToken), address(this), expectedPaymentAmount); // exercise options tokens which should fail - vm.expectRevert(bytes4(keccak256("BalancerOracle__TWAPOracleNotReady()"))); - optionsToken.exercise(amount, expectedPaymentAmount, recipient); + DiscountExerciseParams memory params = + DiscountExerciseParams({maxPaymentAmount: expectedPaymentAmount, deadline: type(uint256).max, isInstantExit: false}); + vm.expectRevert(OptionsToken.OptionsToken__NotExerciseContract.selector); + optionsToken.exercise(amount, recipient, address(exerciser), abi.encode(params)); } - function test_exercisePastDeadline(uint256 amount, address recipient, uint256 deadline) public { - amount = bound(amount, 0, MAX_SUPPLY); - deadline = bound(deadline, 0, block.timestamp - 1); + function test_exerciseWhenPaused(uint256 amount) public { + amount = bound(amount, 100, 1 ether); + address recipient = makeAddr("recipient"); + + // mint options tokens + vm.prank(tokenAdmin); + optionsToken.mint(address(this), 3 * amount); + + // mint payment tokens + uint256 expectedPaymentAmount = 3 * amount.mulWadUp(oracle.getPrice().mulDivUp(PRICE_MULTIPLIER, ORACLE_MIN_PRICE_DENOM)); + deal(address(paymentToken), address(this), expectedPaymentAmount); + + // exercise options tokens + DiscountExerciseParams memory params = + DiscountExerciseParams({maxPaymentAmount: expectedPaymentAmount, deadline: type(uint256).max, isInstantExit: false}); + optionsToken.exercise(amount, recipient, address(exerciser), abi.encode(params)); + + /* Only owner can pause */ + vm.startPrank(recipient); + vm.expectRevert(bytes("UNAUTHORIZED")); // Ownable: caller is not the owner + exerciser.pause(); + vm.stopPrank(); + + vm.prank(owner); + exerciser.pause(); + vm.expectRevert(bytes("Pausable: paused")); + optionsToken.exercise(amount, recipient, address(exerciser), abi.encode(params)); + + vm.prank(owner); + exerciser.unpause(); + optionsToken.exercise(amount, recipient, address(exerciser), abi.encode(params)); + } + + function test_oTokenWhenPaused(uint256 amount) public { + amount = bound(amount, 100, 1 ether); + address recipient = makeAddr("recipient"); + + // mint options tokens + vm.prank(tokenAdmin); + optionsToken.mint(address(this), 3 * amount); + + // mint payment tokens + uint256 expectedPaymentAmount = 3 * amount.mulWadUp(oracle.getPrice().mulDivUp(PRICE_MULTIPLIER, ORACLE_MIN_PRICE_DENOM)); + deal(address(paymentToken), address(this), expectedPaymentAmount); + + // exercise options tokens + DiscountExerciseParams memory params = + DiscountExerciseParams({maxPaymentAmount: expectedPaymentAmount, deadline: type(uint256).max, isInstantExit: false}); + optionsToken.exercise(amount, recipient, address(exerciser), abi.encode(params)); + + /* Only owner can pause */ + vm.startPrank(recipient); + vm.expectRevert(bytes("Ownable: caller is not the owner")); // Ownable: caller is not the owner + optionsToken.pause(); + vm.stopPrank(); + + vm.prank(owner); + optionsToken.pause(); + vm.expectRevert(bytes("Pausable: paused")); + optionsToken.exercise(amount, recipient, address(exerciser), abi.encode(params)); + + vm.prank(owner); + optionsToken.unpause(); + optionsToken.exercise(amount, recipient, address(exerciser), abi.encode(params)); + } + + function test_exerciserConfigAccesses() public { + uint256 slippage = 555; // 5.55% + address[] memory tokens = new address[](2); + tokens[0] = address(paymentToken); + tokens[1] = underlyingToken; + balancerTwapOracle = new MockBalancerTwapOracle(tokens); + oracle = IOracle(new BalancerOracle(balancerTwapOracle, underlyingToken, owner, ORACLE_SECS, ORACLE_AGO, ORACLE_MIN_PRICE)); + + reaperSwapper = new ReaperSwapperMock(oracle, address(underlyingToken), address(paymentToken)); + SwapProps memory swapProps = SwapProps(address(reaperSwapper), address(reaperSwapper), ExchangeType.Bal, slippage); + + vm.expectRevert(bytes("UNAUTHORIZED")); + exerciser.setSwapProps(swapProps); + + vm.prank(owner); + exerciser.setSwapProps(swapProps); + + vm.expectRevert(bytes("UNAUTHORIZED")); + exerciser.setOracle(oracle); + + vm.prank(owner); + exerciser.setOracle(oracle); + + vm.expectRevert(bytes("UNAUTHORIZED")); + exerciser.setMultiplier(3333); + + vm.prank(owner); + exerciser.setMultiplier(3333); + + vm.expectRevert(bytes("UNAUTHORIZED")); + exerciser.setInstantExitFee(1444); + + vm.prank(owner); + exerciser.setInstantExitFee(1444); + + vm.expectRevert(bytes("UNAUTHORIZED")); + exerciser.setMinAmountToTriggerSwap(1e16); + + vm.prank(owner); + exerciser.setMinAmountToTriggerSwap(1e16); + } + + function test_zapWhenExerciseUnderfunded(uint256 amount) public { + amount = bound(amount, 1e16, 1e22); + address recipient = makeAddr("recipient"); + + uint256 remainingAmount = 4e15; // mint options tokens vm.prank(tokenAdmin); optionsToken.mint(address(this), amount); // mint payment tokens - uint256 expectedPaymentAmount = - amount.mulWadUp(ORACLE_INIT_TWAP_VALUE.mulDivUp(ORACLE_MULTIPLIER, ORACLE_MIN_PRICE_DENOM)); - paymentToken.mint(address(this), expectedPaymentAmount); + uint256 expectedPaymentAmount = amount.mulWadUp(ORACLE_INIT_TWAP_VALUE.mulDivUp(PRICE_MULTIPLIER, ORACLE_MIN_PRICE_DENOM)); + uint256 discountedUnderlying = amount.mulDivUp(PRICE_MULTIPLIER, 10_000); + uint256 expectedUnderlyingAmount = discountedUnderlying - discountedUnderlying.mulDivUp(INSTANT_EXIT_FEE, 10_000); + deal(address(paymentToken), address(this), expectedPaymentAmount); + console.log("discountedUnderlying:", discountedUnderlying); + console.log("expectedUnderlyingAmount:", expectedUnderlyingAmount); + uint256 calcPaymentAmount = exerciser.getPaymentAmount(amount); + uint256 totalFee = calcPaymentAmount.mulDivUp(INSTANT_EXIT_FEE, 10_000); + uint256 fee1 = totalFee.mulDivDown(feeBPS_[0], 10_000); + uint256 fee2 = totalFee - fee1; + console.log("expected paymentFee1: ", fee1); + console.log("expected paymentFee2: ", fee2); + + // Simulate sitiation when exerciser has less underlying amount than expected from exercise action + vm.prank(address(exerciser)); + // IERC20(underlyingToken).transfer(address(this), 1e27 - (discountedUnderlying - 1)); + IERC20(underlyingToken).transfer(address(this), 1e27 - remainingAmount); + console.log("Balance of exerciser:", IERC20(underlyingToken).balanceOf(address(exerciser))); // exercise options tokens - vm.expectRevert(bytes4(keccak256("OptionsToken__PastDeadline()"))); - optionsToken.exercise(amount, expectedPaymentAmount, recipient, deadline); + DiscountExerciseParams memory params = + DiscountExerciseParams({maxPaymentAmount: expectedPaymentAmount, deadline: type(uint256).max, isInstantExit: true}); + (uint256 paymentAmount,,,) = optionsToken.exercise(amount, recipient, address(exerciser), abi.encode(params)); + + // verify options tokens were transferred + assertEqDecimal(optionsToken.balanceOf(address(this)), 0, 18, "user still has options tokens"); + assertEqDecimal(optionsToken.totalSupply(), 0, 18, "option tokens not burned"); + + // verify payment tokens were transferred + assertEq(paymentToken.balanceOf(address(this)), expectedPaymentAmount, "user lost payment tokens during instant exit"); + + assertEq(paymentToken.balanceOf(feeRecipients_[0]), 0, "fee recipient 1 didn't receive payment tokens"); + assertEq(paymentToken.balanceOf(feeRecipients_[1]), 0, "fee recipient 2 didn't receive payment tokens"); + assertEqDecimal(paymentAmount, 0, 18, "exercise returned wrong value"); + assertEq(IERC20(underlyingToken).balanceOf(recipient), remainingAmount, "Recipient got wrong amount of underlying token"); + } + + function test_modeZapRedeemWithDifferentMultipliers(uint256 multiplier) public { + multiplier = bound(multiplier, BPS_DENOM / 10, BPS_DENOM - 1); + // multiplier = 8000; + uint256 amount = 1000e18; + + address recipient = makeAddr("recipient"); + + // mint options tokens + vm.prank(tokenAdmin); + optionsToken.mint(address(this), 2 * amount); + + vm.prank(owner); + exerciser.setMultiplier(multiplier); + uint256 expectedPaymentAmount = amount.mulWadUp(ORACLE_INIT_TWAP_VALUE) * 4; + deal(address(paymentToken), address(this), expectedPaymentAmount); + + uint256 underlyingBalance = + IERC20(underlyingToken).balanceOf(address(this)) + paymentToken.balanceOf(address(this)).divWadUp(oracle.getPrice()); + console.log("Price: ", oracle.getPrice()); + console.log("Balance before: ", underlyingBalance); + console.log("Underlying amount before: ", IERC20(underlyingToken).balanceOf(address(this))); + + // exercise options tokens -> redeem + DiscountExerciseParams memory params = + DiscountExerciseParams({maxPaymentAmount: expectedPaymentAmount, deadline: type(uint256).max, isInstantExit: false}); + (uint256 paymentAmount,,,) = optionsToken.exercise(amount, address(this), address(exerciser), abi.encode(params)); + + uint256 underlyingBalanceAfterRedeem = + IERC20(underlyingToken).balanceOf(address(this)) + paymentToken.balanceOf(address(this)).divWadUp(oracle.getPrice()); + console.log("Price: ", oracle.getPrice()); + console.log("Underlying amount after redeem: ", IERC20(underlyingToken).balanceOf(address(this))); + console.log("Balance after redeem: ", underlyingBalanceAfterRedeem); + + assertGt(underlyingBalanceAfterRedeem, underlyingBalance, "Redeem not profitable"); + uint256 redeemProfit = underlyingBalanceAfterRedeem - underlyingBalance; + + // exercise options tokens -> zap + params = DiscountExerciseParams({maxPaymentAmount: expectedPaymentAmount, deadline: type(uint256).max, isInstantExit: true}); + (paymentAmount,,,) = optionsToken.exercise(amount, address(this), address(exerciser), abi.encode(params)); + + uint256 underlyingBalanceAfterZap = + IERC20(underlyingToken).balanceOf(address(this)) + paymentToken.balanceOf(address(this)).divWadUp(oracle.getPrice()); + console.log("Price: ", oracle.getPrice()); + console.log("Underlying amount after zap: ", IERC20(underlyingToken).balanceOf(address(this))); + console.log("Balance after zap: ", underlyingBalanceAfterZap); + + assertGt(underlyingBalanceAfterZap, underlyingBalanceAfterRedeem, "Zap not profitable"); + uint256 zapProfit = underlyingBalanceAfterZap - underlyingBalanceAfterRedeem; + + assertGt(redeemProfit, zapProfit, "Profits from zap is greater than profits from redeem"); + + assertEq(redeemProfit - redeemProfit.mulDivUp(INSTANT_EXIT_FEE, BPS_DENOM), zapProfit, "Zap profit is different than redeem profit minus fee"); } } diff --git a/test/ThenaOracle.t.sol b/test/ThenaOracle.t.sol new file mode 100644 index 0000000..0b592be --- /dev/null +++ b/test/ThenaOracle.t.sol @@ -0,0 +1,209 @@ +// SPDX-License-Identifier: AGPL-3.0 +pragma solidity ^0.8.13; + +import "forge-std/Test.sol"; +import {ThenaOracle} from "../src/oracles/ThenaOracle.sol"; +import {IThenaPair} from "../src/interfaces/IThenaPair.sol"; +import {IThenaRouter} from "./interfaces/IThenaRouter.sol"; +import {FixedPointMathLib} from "solmate/utils/FixedPointMathLib.sol"; +import {IERC20} from "forge-std/interfaces/IERC20.sol"; + +struct Params { + IThenaPair pair; + address token; + address owner; + uint32 secs; + uint128 minPrice; +} + +contract ThenaOracleTest is Test { + using stdStorage for StdStorage; + using FixedPointMathLib for uint256; + + string BSC_RPC_URL = vm.envString("BSC_RPC_URL"); + uint32 FORK_BLOCK = 33672842; + + address POOL_ADDRESS = 0x56EDFf25385B1DaE39d816d006d14CeCf96026aF; + address TOKEN_ADDRESS = 0x4d2d32d8652058Bf98c772953E1Df5c5c85D9F45; + address PAYMENT_TOKEN_ADDRESS = 0x55d398326f99059fF775485246999027B3197955; + address THENA_ROUTER = 0xd4ae6eCA985340Dd434D38F470aCCce4DC78D109; + + uint256 MULTIPLIER_DENOM = 10000; + + uint256 bscFork; + + Params _default; + + function setUp() public { + _default = Params(IThenaPair(POOL_ADDRESS), TOKEN_ADDRESS, address(this), 30 minutes, 1000); + bscFork = vm.createSelectFork(BSC_RPC_URL, FORK_BLOCK); + } + + function test_priceWithinAcceptableRange() public { + ThenaOracle oracle = new ThenaOracle( + _default.pair, + _default.token, + _default.owner, + _default.secs, + _default.minPrice + ); + + uint256 oraclePrice = oracle.getPrice(); + + uint256 spotPrice = getSpotPrice(_default.pair, _default.token); + assertApproxEqRel(oraclePrice, spotPrice, 0.01 ether, "Price delta too large"); // 1% + } + + function test_priceToken1() public { + ThenaOracle oracleToken0 = new ThenaOracle( + _default.pair, + IThenaPair(_default.pair).token0(), + _default.owner, + _default.secs, + _default.minPrice + ); + + ThenaOracle oracleToken1 = new ThenaOracle( + _default.pair, + IThenaPair(_default.pair).token1(), + _default.owner, + _default.secs, + _default.minPrice + ); + + uint256 priceToken0 = oracleToken0.getPrice(); + uint256 priceToken1 = oracleToken1.getPrice(); + + assertApproxEqAbs(priceToken1, uint256(1e18).divWadDown(priceToken0), 1, "incorrect price"); // 1% + } + + function test_revertMinPrice() public { + ThenaOracle oracle = new ThenaOracle( + _default.pair, + _default.token, + _default.owner, + _default.secs, + _default.minPrice + ); + + // clean twap for test + skip(1 hours); + _default.pair.sync(); + skip(1 hours); + _default.pair.sync(); + skip(1 hours); + + // register initial oracle price + uint256 price = oracle.getPrice(); + + // drag price below min + uint256 amountIn = 10000000; + deal(TOKEN_ADDRESS, address(this), amountIn); + IERC20(TOKEN_ADDRESS).approve(THENA_ROUTER, amountIn); + IThenaRouter(THENA_ROUTER).swapExactTokensForTokensSimple( + amountIn, 0, TOKEN_ADDRESS, PAYMENT_TOKEN_ADDRESS, false, address(this), type(uint32).max + ); + + ThenaOracle oracleMinPrice = new ThenaOracle( + _default.pair, + _default.token, + _default.owner, + _default.secs, + uint128(price) + ); + + skip(_default.secs); + + vm.expectRevert(ThenaOracle.ThenaOracle__BelowMinPrice.selector); + oracleMinPrice.getPrice(); + } + + function test_singleBlockManipulation() public { + ThenaOracle oracle = new ThenaOracle( + _default.pair, + _default.token, + _default.owner, + _default.secs, + _default.minPrice + ); + + address manipulator = makeAddr("manipulator"); + deal(TOKEN_ADDRESS, manipulator, 1000000 ether); + + // register initial oracle price + uint256 price_1 = oracle.getPrice(); + + // perform a large swap + vm.startPrank(manipulator); + IERC20(TOKEN_ADDRESS).approve(THENA_ROUTER, 1000000 ether); + + (uint256 reserve0, uint256 reserve1,) = _default.pair.getReserves(); + IThenaRouter(THENA_ROUTER).swapExactTokensForTokensSimple( + (TOKEN_ADDRESS == _default.pair.token0() ? reserve0 : reserve1) / 10, + 0, + TOKEN_ADDRESS, + PAYMENT_TOKEN_ADDRESS, + false, + manipulator, + type(uint32).max + ); + vm.stopPrank(); + + // price should not have changed + assertEq(oracle.getPrice(), price_1, "single block price variation"); + } + + function test_priceManipulation(uint256 skipTime) public { + skipTime = bound(skipTime, 1, _default.secs); + ThenaOracle oracle = new ThenaOracle( + _default.pair, + _default.token, + _default.owner, + _default.secs, + _default.minPrice + ); + + // clean twap for test + skip(1 hours); + _default.pair.sync(); + skip(1 hours); + _default.pair.sync(); + skip(1 hours); + + // register initial oracle price + uint256 price_1 = oracle.getPrice(); + + // perform a large swap + address manipulator = makeAddr("manipulator"); + deal(TOKEN_ADDRESS, manipulator, 2 ** 128); + vm.startPrank(manipulator); + (uint256 reserve0, uint256 reserve1,) = _default.pair.getReserves(); + uint256 amountIn = (TOKEN_ADDRESS == _default.pair.token0() ? reserve0 : reserve1) / 4; + IERC20(TOKEN_ADDRESS).approve(THENA_ROUTER, amountIn); + IThenaRouter(THENA_ROUTER).swapExactTokensForTokensSimple( + amountIn, 0, TOKEN_ADDRESS, PAYMENT_TOKEN_ADDRESS, false, manipulator, type(uint32).max + ); + vm.stopPrank(); + + // wait + skip(skipTime); + + uint256 expectedMinPrice = (price_1 * (_default.secs - skipTime) + getSpotPrice(_default.pair, _default.token) * skipTime) / _default.secs; + + assertGeDecimal(oracle.getPrice(), expectedMinPrice, 18, "price variation too large"); + } + + function getSpotPrice(IThenaPair pair, address token) internal view returns (uint256 price) { + bool isToken0 = token == pair.token0(); + (uint112 reserve0, uint112 reserve1,) = pair.getReserves(); + if (isToken0) { + price = uint256(reserve1).divWadDown(reserve0); + } else { + price = uint256(reserve0).divWadDown(reserve1); + } + } + + function max(uint256 x, uint256 y) internal pure returns (uint256 z) { + z = x > y ? x : y; + } +} diff --git a/test/UniswapV3Oracle.t.sol b/test/UniswapV3Oracle.t.sol new file mode 100644 index 0000000..afec76f --- /dev/null +++ b/test/UniswapV3Oracle.t.sol @@ -0,0 +1,203 @@ +// SPDX-License-Identifier: AGPL-3.0 +pragma solidity ^0.8.13; + +import "forge-std/Test.sol"; +import {UniswapV3Oracle} from "../src/oracles/UniswapV3Oracle.sol"; +import {IUniswapV3Pool} from "v3-core/interfaces/IUniswapV3Pool.sol"; +import {TickMath} from "v3-core/libraries/TickMath.sol"; +import {FullMath} from "v3-core/libraries/FullMath.sol"; +import {ISwapRouter} from "./interfaces/ISwapRouter.sol"; +import {IUniswapV3Pool} from "v3-core/interfaces/IUniswapV3Pool.sol"; +import {MockUniswapPool} from "./mocks/MockUniswapPool.sol"; +import {FixedPointMathLib} from "solmate/utils/FixedPointMathLib.sol"; +import {IERC20} from "forge-std/interfaces/IERC20.sol"; + +struct Params { + IUniswapV3Pool pool; + address token; + address owner; + uint32 secs; + uint32 ago; + uint128 minPrice; +} + +contract UniswapOracleTest is Test { + using stdStorage for StdStorage; + + // mock config + Params _mock; + MockUniswapPool mockV3Pool; + // observation on 2023-09-20 11:26 UTC-3, UNIWETH Ethereum Pool + int56[2] sampleCumulatives = [int56(-4072715107990), int56(-4072608557758)]; + // expected price in terms of token0 + uint256 expectedPriceToken0 = 372078200928347021722; + + string OPTIMISM_RPC_URL = vm.envString("OPTIMISM_RPC_URL"); + uint32 FORK_BLOCK = 112198905; + + address SWAP_ROUTER_ADDRESS = 0xE592427A0AEce92De3Edee1F18E0157C05861564; + address WETH_OP_POOL_ADDRESS = 0x68F5C0A2DE713a54991E01858Fd27a3832401849; + address OP_ADDRESS = 0x4200000000000000000000000000000000000042; + address WETH_ADDRESS = 0x4200000000000000000000000000000000000006; + uint24 POOL_FEE = 3000; + + uint256 opFork; + + ISwapRouter swapRouter; + Params _default; + + function setUp() public { + opFork = vm.createSelectFork(OPTIMISM_RPC_URL, FORK_BLOCK); + mockV3Pool = new MockUniswapPool(); + mockV3Pool.setCumulatives(sampleCumulatives); + mockV3Pool.setToken0(OP_ADDRESS); + mockV3Pool.setToken1(WETH_ADDRESS); + + _default = Params(IUniswapV3Pool(WETH_OP_POOL_ADDRESS), OP_ADDRESS, address(this), 30 minutes, 0, 1000); + swapRouter = ISwapRouter(SWAP_ROUTER_ADDRESS); + } + + /// ---------------------------------------------------------------------- + /// Mock tests + /// ---------------------------------------------------------------------- + + function test_PriceTokens() public { + UniswapV3Oracle oracle0 = new UniswapV3Oracle(mockV3Pool, OP_ADDRESS, _default.owner, _default.secs, _default.ago, _default.minPrice); + UniswapV3Oracle oracle1 = new UniswapV3Oracle(mockV3Pool, WETH_ADDRESS, _default.owner, _default.secs, _default.ago, _default.minPrice); + + uint256 price0 = oracle0.getPrice(); + uint256 price1 = oracle1.getPrice(); + assertEq(price0, expectedPriceToken0); + uint256 expectedPriceToken1 = FixedPointMathLib.divWadDown(1e18, price0); + assertEq(price1, expectedPriceToken1); //precision + } + + /// ---------------------------------------------------------------------- + /// Fork tests + /// ---------------------------------------------------------------------- + + function test_priceWithinAcceptableRange() public { + UniswapV3Oracle oracle = new UniswapV3Oracle(_default.pool, _default.token, _default.owner, _default.secs, _default.ago, _default.minPrice); + + uint256 oraclePrice = oracle.getPrice(); + + (uint160 sqrtRatioX96,,,,,,) = IUniswapV3Pool(WETH_OP_POOL_ADDRESS).slot0(); + uint256 spotPrice = computePriceFromX96(sqrtRatioX96); + assertApproxEqRel(oraclePrice, spotPrice, 0.01 ether, "Price delta too big"); // 1% + } + + function test_revertMinPrice() public { + UniswapV3Oracle oracle = new UniswapV3Oracle(_default.pool, _default.token, _default.owner, _default.secs, _default.ago, _default.minPrice); + + skip(_default.secs); + + uint256 price = oracle.getPrice(); + + uint256 amountIn = 100000 ether; + deal(OP_ADDRESS, address(this), amountIn); + ISwapRouter.ExactInputSingleParams memory paramsIn = ISwapRouter.ExactInputSingleParams({ + tokenIn: OP_ADDRESS, + tokenOut: WETH_ADDRESS, + fee: POOL_FEE, + recipient: address(this), + deadline: block.timestamp, + amountIn: amountIn, + amountOutMinimum: 0, + sqrtPriceLimitX96: 0 + }); + IERC20(OP_ADDRESS).approve(address(swapRouter), amountIn); + swapRouter.exactInputSingle(paramsIn); + + // deploy a new oracle with a minPrice that is too high + UniswapV3Oracle oracleMinPrice = + new UniswapV3Oracle(_default.pool, _default.token, _default.owner, _default.secs, _default.ago, uint128(price)); + + skip(_default.secs); + + vm.expectRevert(UniswapV3Oracle.UniswapOracle__BelowMinPrice.selector); + oracleMinPrice.getPrice(); + } + + function test_singleBlockManipulation() public { + UniswapV3Oracle oracle = new UniswapV3Oracle(_default.pool, _default.token, _default.owner, _default.secs, _default.ago, _default.minPrice); + + address manipulator = makeAddr("manipulator"); + deal(OP_ADDRESS, manipulator, 1000000 ether); + + // register initial oracle price + uint256 price_1 = oracle.getPrice(); + + // perform a large swap + vm.startPrank(manipulator); + uint256 reserve = IERC20(OP_ADDRESS).balanceOf(WETH_OP_POOL_ADDRESS); + uint256 amountIn = reserve / 4; + ISwapRouter.ExactInputSingleParams memory paramsIn = ISwapRouter.ExactInputSingleParams({ + tokenIn: OP_ADDRESS, + tokenOut: WETH_ADDRESS, + fee: POOL_FEE, + recipient: manipulator, + deadline: block.timestamp, + amountIn: amountIn, + amountOutMinimum: 0, + sqrtPriceLimitX96: 0 + }); + IERC20(OP_ADDRESS).approve(address(swapRouter), amountIn); + swapRouter.exactInputSingle(paramsIn); + vm.stopPrank(); + + // price should not have changed + assertEqDecimal(price_1, oracle.getPrice(), 18); + } + + function test_priceManipulation(uint256 skipTime) public { + skipTime = bound(skipTime, 1, _default.secs); + + UniswapV3Oracle oracle = new UniswapV3Oracle(_default.pool, _default.token, _default.owner, _default.secs, _default.ago, _default.minPrice); + + address manipulator = makeAddr("manipulator"); + deal(OP_ADDRESS, manipulator, 1000000 ether); + + // register initial oracle price + uint256 price_1 = oracle.getPrice(); + + // perform a large swap + vm.startPrank(manipulator); + uint256 reserve = IERC20(OP_ADDRESS).balanceOf(WETH_OP_POOL_ADDRESS); + uint256 amountIn = reserve / 4; + ISwapRouter.ExactInputSingleParams memory paramsIn = ISwapRouter.ExactInputSingleParams({ + tokenIn: OP_ADDRESS, + tokenOut: WETH_ADDRESS, + fee: POOL_FEE, + recipient: manipulator, + deadline: block.timestamp, + amountIn: amountIn, + amountOutMinimum: 0, + sqrtPriceLimitX96: 0 + }); + IERC20(OP_ADDRESS).approve(address(swapRouter), amountIn); + swapRouter.exactInputSingle(paramsIn); + vm.stopPrank(); + + // wait + skip(skipTime); + + (uint160 sqrtRatioX96,,,,,,) = IUniswapV3Pool(WETH_OP_POOL_ADDRESS).slot0(); + uint256 spotPrice = computePriceFromX96(sqrtRatioX96); + uint256 expectedPrice = (price_1 * (_default.secs - skipTime) + spotPrice * skipTime) / _default.secs; + + assertApproxEqRel(oracle.getPrice(), expectedPrice, 0.001 ether, "price variance too large"); + } + + function computePriceFromX96(uint160 sqrtRatioX96) internal view returns (uint256 price) { + bool isToken0 = OP_ADDRESS == IUniswapV3Pool(WETH_OP_POOL_ADDRESS).token0(); + uint256 decimals = 1e18; + + if (sqrtRatioX96 <= type(uint128).max) { + uint256 ratioX192 = uint256(sqrtRatioX96) * sqrtRatioX96; + price = isToken0 ? FullMath.mulDiv(ratioX192, decimals, 1 << 192) : FullMath.mulDiv(1 << 192, decimals, ratioX192); + } else { + uint256 ratioX128 = FullMath.mulDiv(sqrtRatioX96, sqrtRatioX96, 1 << 64); + price = isToken0 ? FullMath.mulDiv(ratioX128, decimals, 1 << 128) : FullMath.mulDiv(1 << 128, decimals, ratioX128); + } + } +} diff --git a/test/interfaces/ISwapRouter.sol b/test/interfaces/ISwapRouter.sol new file mode 100644 index 0000000..339cf15 --- /dev/null +++ b/test/interfaces/ISwapRouter.sol @@ -0,0 +1,67 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +pragma solidity >=0.7.5; +pragma abicoder v2; + +import "v3-core/interfaces/callback/IUniswapV3SwapCallback.sol"; + +/// @title Router token swapping functionality +/// @notice Functions for swapping tokens via Uniswap V3 +interface ISwapRouter is IUniswapV3SwapCallback { + struct ExactInputSingleParams { + address tokenIn; + address tokenOut; + uint24 fee; + address recipient; + uint256 deadline; + uint256 amountIn; + uint256 amountOutMinimum; + uint160 sqrtPriceLimitX96; + } + + /// @notice Swaps `amountIn` of one token for as much as possible of another token + /// @param params The parameters necessary for the swap, encoded as `ExactInputSingleParams` in calldata + /// @return amountOut The amount of the received token + function exactInputSingle(ExactInputSingleParams calldata params) external payable returns (uint256 amountOut); + + struct ExactInputParams { + bytes path; + address recipient; + uint256 deadline; + uint256 amountIn; + uint256 amountOutMinimum; + } + + /// @notice Swaps `amountIn` of one token for as much as possible of another along the specified path + /// @param params The parameters necessary for the multi-hop swap, encoded as `ExactInputParams` in calldata + /// @return amountOut The amount of the received token + function exactInput(ExactInputParams calldata params) external payable returns (uint256 amountOut); + + struct ExactOutputSingleParams { + address tokenIn; + address tokenOut; + uint24 fee; + address recipient; + uint256 deadline; + uint256 amountOut; + uint256 amountInMaximum; + uint160 sqrtPriceLimitX96; + } + + /// @notice Swaps as little as possible of one token for `amountOut` of another token + /// @param params The parameters necessary for the swap, encoded as `ExactOutputSingleParams` in calldata + /// @return amountIn The amount of the input token + function exactOutputSingle(ExactOutputSingleParams calldata params) external payable returns (uint256 amountIn); + + struct ExactOutputParams { + bytes path; + address recipient; + uint256 deadline; + uint256 amountOut; + uint256 amountInMaximum; + } + + /// @notice Swaps as little as possible of one token for `amountOut` of another along the specified path (reversed) + /// @param params The parameters necessary for the multi-hop swap, encoded as `ExactOutputParams` in calldata + /// @return amountIn The amount of the input token + function exactOutput(ExactOutputParams calldata params) external payable returns (uint256 amountIn); +} diff --git a/test/interfaces/IThenaRouter.sol b/test/interfaces/IThenaRouter.sol new file mode 100644 index 0000000..a455d15 --- /dev/null +++ b/test/interfaces/IThenaRouter.sol @@ -0,0 +1,11 @@ +interface IThenaRouter { + function swapExactTokensForTokensSimple( + uint256 amountIn, + uint256 amountOutMin, + address tokenFrom, + address tokenTo, + bool stable, + address to, + uint256 deadline + ) external returns (uint256[] memory amounts); +} diff --git a/test/mocks/MockBalancerTwapOracle.sol b/test/mocks/MockBalancerTwapOracle.sol index 0ace245..5e0dd81 100644 --- a/test/mocks/MockBalancerTwapOracle.sol +++ b/test/mocks/MockBalancerTwapOracle.sol @@ -2,9 +2,40 @@ pragma solidity ^0.8.11; import {IBalancerTwapOracle} from "../../src/interfaces/IBalancerTwapOracle.sol"; +import {IVault} from "../../src/interfaces/IBalancerTwapOracle.sol"; + +contract MockVault is IVault { + address[] tokens = new address[](2); + + constructor(address[] memory _tokens) { + tokens = _tokens; + } + + function joinPool(bytes32 poolId, address sender, address recipient, JoinPoolRequest memory request) external payable override {} + + function getPool(bytes32 poolId) external view override returns (address, PoolSpecialization) {} + + function getPoolTokens(bytes32 poolId) external view override returns (address[] memory tokens_, uint256[] memory, uint256) { + tokens_ = new address[](2); + tokens_[0] = tokens[0]; + tokens_[1] = tokens[1]; + } + + function swap(SingleSwap memory singleSwap, FundManagement memory funds, uint256 limit, uint256 deadline) + external + payable + override + returns (uint256) + {} +} contract MockBalancerTwapOracle is IBalancerTwapOracle { uint256 twapValue; + IVault mockVault; + + constructor(address[] memory tokens) { + mockVault = new MockVault(tokens); + } function setTwapValue(uint256 value) external { twapValue = value; @@ -21,10 +52,6 @@ contract MockBalancerTwapOracle is IBalancerTwapOracle { results[0] = twapValue; } - function getLatest(IBalancerTwapOracle.Variable variable) external view override returns (uint256) { - // not implemented - } - function getLargestSafeQueryWindow() external pure override returns (uint256) { return 24 hours; // simulates an oracle that can look back at most 24 hours } @@ -34,7 +61,13 @@ contract MockBalancerTwapOracle is IBalancerTwapOracle { view override returns (int256[] memory results) - { - // not implemented + {} + + function getLatest(IBalancerTwapOracle.Variable variable) external view override returns (uint256) {} + + function getVault() external view override returns (IVault) { + return mockVault; } + + function getPoolId() external view override returns (bytes32) {} } diff --git a/test/mocks/MockUniswapPool.sol b/test/mocks/MockUniswapPool.sol new file mode 100644 index 0000000..9ef5cd4 --- /dev/null +++ b/test/mocks/MockUniswapPool.sol @@ -0,0 +1,141 @@ +// SPDX-License-Identifier: AGPL-3.0 +pragma solidity ^0.8.11; + +import {IUniswapV3Pool} from "v3-core/interfaces/IUniswapV3Pool.sol"; + +contract MockUniswapPool is IUniswapV3Pool { + int56[2] cumulatives; + address public token0; + address public token1; + + function setCumulatives(int56[2] memory value) external { + cumulatives = value; + } + + function setToken0(address value) external { + token0 = value; + } + + function setToken1(address value) external { + token1 = value; + } + + function observe(uint32[] calldata secondsAgos) + external + view + returns (int56[] memory tickCumulatives, uint160[] memory secondsPerLiquidityCumulativeX128s) + { + secondsAgos; + secondsPerLiquidityCumulativeX128s; + + tickCumulatives = new int56[](2); + tickCumulatives[0] = cumulatives[0]; + tickCumulatives[1] = cumulatives[1]; + } + + // mandatory overrides + + function snapshotCumulativesInside(int24 tickLower, int24 tickUpper) + external + view + override + returns (int56 tickCumulativeInside, uint160 secondsPerLiquidityInsideX128, uint32 secondsInside) + {} + + function factory() external view override returns (address) {} + + function fee() external view override returns (uint24) {} + + function tickSpacing() external view override returns (int24) {} + + function maxLiquidityPerTick() external view override returns (uint128) {} + + function slot0() + external + view + override + returns ( + uint160 sqrtPriceX96, + int24 tick, + uint16 observationIndex, + uint16 observationCardinality, + uint16 observationCardinalityNext, + uint8 feeProtocol, + bool unlocked + ) + {} + + function feeGrowthGlobal0X128() external view override returns (uint256) {} + + function feeGrowthGlobal1X128() external view override returns (uint256) {} + + function protocolFees() external view override returns (uint128, uint128) {} + + function liquidity() external view override returns (uint128) {} + + function ticks(int24 tick) + external + view + override + returns ( + uint128 liquidityGross, + int128 liquidityNet, + uint256 feeGrowthOutside0X128, + uint256 feeGrowthOutside1X128, + int56 tickCumulativeOutside, + uint160 secondsPerLiquidityOutsideX128, + uint32 secondsOutside, + bool initialized + ) + {} + + function tickBitmap(int16 wordPosition) external view override returns (uint256) {} + + function positions(bytes32 key) + external + view + override + returns (uint128 _liquidity, uint256 feeGrowthInside0LastX128, uint256 feeGrowthInside1LastX128, uint128 tokensOwed0, uint128 tokensOwed1) + {} + + function observations(uint256 index) + external + view + override + returns (uint32 blockTimestamp, int56 tickCumulative, uint160 secondsPerLiquidityCumulativeX128, bool initialized) + {} + + function initialize(uint160 sqrtPriceX96) external override {} + + function mint(address recipient, int24 tickLower, int24 tickUpper, uint128 amount, bytes calldata data) + external + override + returns (uint256 amount0, uint256 amount1) + {} + + function collect(address recipient, int24 tickLower, int24 tickUpper, uint128 amount0Requested, uint128 amount1Requested) + external + override + returns (uint128 amount0, uint128 amount1) + {} + + function burn(int24 tickLower, int24 tickUpper, uint128 amount) external override returns (uint256 amount0, uint256 amount1) {} + + function swap(address recipient, bool zeroForOne, int256 amountSpecified, uint160 sqrtPriceLimitX96, bytes calldata data) + external + override + returns (int256 amount0, int256 amount1) + {} + + function flash(address recipient, uint256 amount0, uint256 amount1, bytes calldata data) external override {} + + function increaseObservationCardinalityNext(uint16 observationCardinalityNext) external override {} + + function setFeeProtocol(uint8 feeProtocol0, uint8 feeProtocol1) external override {} + + function collectProtocol(address recipient, uint128 amount0Requested, uint128 amount1Requested) + external + override + returns (uint128 amount0, uint128 amount1) + {} +} diff --git a/test/mocks/OptionsTokenV2.sol b/test/mocks/OptionsTokenV2.sol new file mode 100644 index 0000000..36c95ca --- /dev/null +++ b/test/mocks/OptionsTokenV2.sol @@ -0,0 +1,195 @@ +// SPDX-License-Identifier: AGPL-3.0 +pragma solidity ^0.8.13; + +import {OwnableUpgradeable} from "oz-upgradeable/access/OwnableUpgradeable.sol"; +import {ERC20Upgradeable} from "oz-upgradeable/token/ERC20/ERC20Upgradeable.sol"; +import {UUPSUpgradeable} from "oz-upgradeable/proxy/utils/UUPSUpgradeable.sol"; + +import {IOptionsToken} from "../../src/interfaces/IOptionsToken.sol"; +import {IOracle} from "../../src/interfaces/IOracle.sol"; +import {IExercise} from "../../src/interfaces/IExercise.sol"; + +/// @title Options Token +/// @author Eidolon & lookee +/// @notice Options token representing the right to perform an advantageous action, +/// such as purchasing the underlying token at a discount to the market price. +contract OptionsTokenV2 is IOptionsToken, ERC20Upgradeable, OwnableUpgradeable, UUPSUpgradeable { + /// ----------------------------------------------------------------------- + /// Errors + /// ----------------------------------------------------------------------- + + error OptionsToken__NotTokenAdmin(); + error OptionsToken__NotExerciseContract(); + error Upgradeable__Unauthorized(); + + /// ----------------------------------------------------------------------- + /// Events + /// ----------------------------------------------------------------------- + + event Exercise( + address indexed sender, address indexed recipient, uint256 amount, address data0, uint256 data1, uint256 data2 + ); + event SetOracle(IOracle indexed newOracle); + event SetExerciseContract(address indexed _address, bool _isExercise); + + /// ----------------------------------------------------------------------- + /// Constant parameters + /// ----------------------------------------------------------------------- + + uint256 public constant UPGRADE_TIMELOCK = 48 hours; + uint256 public constant FUTURE_NEXT_PROPOSAL_TIME = 365 days * 100; + + /// ----------------------------------------------------------------------- + /// Storage variables + /// ----------------------------------------------------------------------- + + /// @notice The contract that has the right to mint options tokens + address public tokenAdmin; + + mapping(address => bool) public isExerciseContract; + uint256 public upgradeProposalTime; + address public nextImplementation; + + uint256 public constant newVar = 123456; + + /// @custom:oz-upgrades-unsafe-allow constructor + constructor() { + _disableInitializers(); + } + + /// ----------------------------------------------------------------------- + /// Initializer + /// ----------------------------------------------------------------------- + + function initialize(string memory name_, string memory symbol_, address tokenAdmin_) external initializer { + __UUPSUpgradeable_init(); + __ERC20_init(name_, symbol_); + __Ownable_init(); + tokenAdmin = tokenAdmin_; + + _clearUpgradeCooldown(); + } + + /// ----------------------------------------------------------------------- + /// External functions + /// ----------------------------------------------------------------------- + + /// @notice Called by the token admin to mint options tokens + /// @param to The address that will receive the minted options tokens + /// @param amount The amount of options tokens that will be minted + function mint(address to, uint256 amount) external virtual override { + /// ----------------------------------------------------------------------- + /// Verification + /// ----------------------------------------------------------------------- + + if (msg.sender != tokenAdmin) revert OptionsToken__NotTokenAdmin(); + + /// ----------------------------------------------------------------------- + /// State updates + /// ----------------------------------------------------------------------- + + // skip if amount is zero + if (amount == 0) return; + + // mint options tokens + _mint(to, amount); + } + + /// @notice Exercises options tokens, burning them and giving the reward to the recipient. + /// @param amount The amount of options tokens to exercise + /// @param recipient The recipient of the reward + /// @param option The address of the Exercise contract with the redemption logic + /// @param params Extra parameters to be used by the exercise function + function exercise(uint256 amount, address recipient, address option, bytes calldata params) + external + virtual + returns ( + uint256 paymentAmount, + address, + uint256, + uint256 // misc data + ) + { + return _exercise(amount, recipient, option, params); + } + + /// ----------------------------------------------------------------------- + /// Owner functions + /// ----------------------------------------------------------------------- + + /// @notice Adds a new Exercise contract to the available options. + /// @param _address Address of the Exercise contract, that implements BaseExercise. + /// @param _isExercise Whether oToken holders should be allowed to exercise using this option. + function setExerciseContract(address _address, bool _isExercise) external onlyOwner { + isExerciseContract[_address] = _isExercise; + emit SetExerciseContract(_address, _isExercise); + } + + /// ----------------------------------------------------------------------- + /// Internal functions + /// ----------------------------------------------------------------------- + + function _exercise(uint256 amount, address recipient, address option, bytes calldata params) + internal + virtual + returns ( + uint256 paymentAmount, + address data0, + uint256 data1, + uint256 data2 // misc data + ) + { + // skip if amount is zero + if (amount == 0) return (0, address(0), 0, 0); + + // revert if the exercise contract is not whitelisted + if (!isExerciseContract[option]) revert OptionsToken__NotExerciseContract(); + + // burn options tokens + _burn(msg.sender, amount); + + // give rewards to recipient + (paymentAmount, data0, data1, data2) = IExercise(option).exercise(msg.sender, amount, recipient, params); + + // emit event + emit Exercise(msg.sender, recipient, amount, data0, data1, data2); + } + + /// ----------------------------------------------------------------------- + /// UUPS functions + /// ----------------------------------------------------------------------- + + /** + * @dev This function must be called prior to upgrading the implementation. + * It's required to wait UPGRADE_TIMELOCK seconds before executing the upgrade. + */ + function initiateUpgradeCooldown(address _nextImplementation) external onlyOwner { + upgradeProposalTime = block.timestamp; + nextImplementation = _nextImplementation; + } + + /** + * @dev This function is called: + * - in initialize() + * - as part of a successful upgrade + * - manually to clear the upgrade cooldown. + */ + function _clearUpgradeCooldown() internal { + upgradeProposalTime = block.timestamp + FUTURE_NEXT_PROPOSAL_TIME; + } + + function clearUpgradeCooldown() external onlyOwner { + _clearUpgradeCooldown(); + } + + /** + * @dev This function must be overriden simply for access control purposes. + * Only the owner can upgrade the implementation once the timelock + * has passed. + */ + function _authorizeUpgrade(address _nextImplementation) internal override onlyOwner { + require(upgradeProposalTime + UPGRADE_TIMELOCK < block.timestamp, "Upgrade cooldown not initiated or still ongoing"); + require(_nextImplementation == nextImplementation, "Incorrect implementation"); + _clearUpgradeCooldown(); + } +} diff --git a/test/mocks/ReaperSwapperMock.sol b/test/mocks/ReaperSwapperMock.sol new file mode 100644 index 0000000..2afa2f7 --- /dev/null +++ b/test/mocks/ReaperSwapperMock.sol @@ -0,0 +1,71 @@ +//SPDX-License-Identifier: AGPL-3.0 +pragma solidity ^0.8.0; + +import {IOracle} from "../../src/interfaces/IOracle.sol"; + +import {ISwapperSwaps, MinAmountOutData, MinAmountOutKind} from "vault-v2/ReaperSwapper.sol"; +import {IERC20} from "oz/token/ERC20/IERC20.sol"; +import {FixedPointMathLib} from "solmate/utils/FixedPointMathLib.sol"; + +import "forge-std/console.sol"; + +contract ReaperSwapperMock { + using FixedPointMathLib for uint256; + + IOracle oracle; + address underlyingToken; + address paymentToken; + + constructor(IOracle _oracle, address _underlyingToken, address _paymentToken) { + oracle = _oracle; + underlyingToken = _underlyingToken; + paymentToken = _paymentToken; + } + + function swapUniV2(address tokenIn, address tokenOut, uint256 amount, MinAmountOutData memory minAmountOutData, address exchangeAddress) + public + returns (uint256) + { + console.log("Called Univ2"); + return _swap(tokenIn, tokenOut, amount, minAmountOutData, exchangeAddress); + } + + function swapBal(address tokenIn, address tokenOut, uint256 amount, MinAmountOutData memory minAmountOutData, address exchangeAddress) + public + returns (uint256) + { + console.log("Called Bal"); + return _swap(tokenIn, tokenOut, amount, minAmountOutData, exchangeAddress); + } + + function swapVelo(address tokenIn, address tokenOut, uint256 amount, MinAmountOutData memory minAmountOutData, address exchangeAddress) + public + returns (uint256) + { + console.log("Called Velo"); + return _swap(tokenIn, tokenOut, amount, minAmountOutData, exchangeAddress); + } + + function swapUniV3(address tokenIn, address tokenOut, uint256 amount, MinAmountOutData memory minAmountOutData, address exchangeAddress) + public + returns (uint256) + { + console.log("Called Univ3"); + return _swap(tokenIn, tokenOut, amount, minAmountOutData, exchangeAddress); + } + + function _swap(address tokenIn, address tokenOut, uint256 amount, MinAmountOutData memory minAmountOutData, address exchangeAddress) + private + returns (uint256) + { + (address oraclePaymentToken, address oracleUnderlyingToken) = oracle.getTokens(); + require(tokenIn == address(oracleUnderlyingToken) || tokenIn == address(oraclePaymentToken), "Not allowed token in"); + require(tokenOut == address(oracleUnderlyingToken) || tokenOut == address(oraclePaymentToken), "Not allowed token"); + IERC20(tokenIn).transferFrom(msg.sender, address(this), amount); + console.log("Price from oracle is: %e", oracle.getPrice()); + uint256 amountToSend = (oracleUnderlyingToken == tokenIn) ? amount.mulWadUp(oracle.getPrice()) : (amount * 1e18) / oracle.getPrice(); + console.log("Amount to send is : %e", amountToSend); + IERC20(tokenOut).transfer(msg.sender, amountToSend); + return amountToSend; + } +} diff --git a/test/mocks/TestERC20Mintable.sol b/test/mocks/TestERC20.sol similarity index 80% rename from test/mocks/TestERC20Mintable.sol rename to test/mocks/TestERC20.sol index 901683f..b9a5f3b 100644 --- a/test/mocks/TestERC20Mintable.sol +++ b/test/mocks/TestERC20.sol @@ -3,7 +3,7 @@ pragma solidity ^0.8.11; import {ERC20} from "solmate/tokens/ERC20.sol"; -contract TestERC20Mintable is ERC20("", "", 18) { +contract TestERC20 is ERC20("", "", 18) { function mint(address to, uint256 amount) external { _mint(to, amount); } diff --git a/test_hardhat/OptionsToken.ts b/test_hardhat/OptionsToken.ts new file mode 100644 index 0000000..3c014bd --- /dev/null +++ b/test_hardhat/OptionsToken.ts @@ -0,0 +1,51 @@ +import {expect} from "chai"; +import {ethers, upgrades} from "hardhat"; +import {AddressZero} from "@ethersproject/constants"; +import { time } from "@nomicfoundation/hardhat-network-helpers"; + +describe("OptionsToken", function() { + it('upgrades seamlessly', async () => { + const OptionsToken = await ethers.getContractFactory("OptionsToken"); + const OptionsTokenV2 = await ethers.getContractFactory("OptionsTokenV2"); + + const signerAddress = await (await ethers.getSigners())[0].getAddress(); + + const instance = await upgrades.deployProxy(OptionsToken, ["TEST", "TEST", signerAddress]); + const newImpl = await upgrades.prepareUpgrade(instance, OptionsTokenV2); + await instance.initiateUpgradeCooldown(newImpl); + await time.increase(60 * 60 * 48); + await instance.upgradeTo(newImpl); + + const instanceV2 = OptionsTokenV2.attach(await instance.getAddress()); + + const value = await (instanceV2 as any).newVar(); + expect(value.toString()).to.equal('123456'); + + await instance.mint(signerAddress, 1000); + expect(await instance.balanceOf(signerAddress)).to.equal(1000); + }); + + it('prevents upgrading before the given timelock', async () => { + const OptionsToken = await ethers.getContractFactory("OptionsToken"); + const OptionsTokenV2 = await ethers.getContractFactory("OptionsTokenV2"); + + const instance = await upgrades.deployProxy(OptionsToken, ["TEST", "TEST", AddressZero]); + const newImpl = await upgrades.prepareUpgrade(instance, OptionsTokenV2); + await instance.initiateUpgradeCooldown(newImpl); + await time.increase(60 * 60 * 48 - 1); + + await expect(instance.upgradeTo(newImpl)).to.be.revertedWith('Upgrade cooldown not initiated or still ongoing'); + }); + + it('requires correct contract to be set before an upgrade', async () => { + const OptionsToken = await ethers.getContractFactory("OptionsToken"); + const OptionsTokenV2 = await ethers.getContractFactory("OptionsTokenV2"); + + const instance = await upgrades.deployProxy(OptionsToken, ["TEST", "TEST", AddressZero]); + const newImpl = await upgrades.prepareUpgrade(instance, OptionsTokenV2); + await instance.initiateUpgradeCooldown(AddressZero); + await time.increase(60 * 60 * 48); + + await expect(instance.upgradeTo(newImpl)).to.be.revertedWith('Incorrect implementation'); + }); +}); diff --git a/tsconfig.json b/tsconfig.json new file mode 100644 index 0000000..574e785 --- /dev/null +++ b/tsconfig.json @@ -0,0 +1,11 @@ +{ + "compilerOptions": { + "target": "es2020", + "module": "commonjs", + "esModuleInterop": true, + "forceConsistentCasingInFileNames": true, + "strict": true, + "skipLibCheck": true, + "resolveJsonModule": true + } +}