From 3a9dcabc5404f78a96ad8adfe202ac513959b84b Mon Sep 17 00:00:00 2001 From: Shriya Tyagi Date: Sat, 16 Dec 2023 04:20:02 +0530 Subject: [PATCH] feat: initial setup --- .env.example | 11 + .gitignore | 67 + hardhat.config.ts | 154 + package-lock.json | 9078 +++++++++++++++++ package.json | 19 + scripts/constant/abi/basics/erc20.json | 222 + scripts/constant/abi/connectors/auth.json | 97 + scripts/constant/abi/connectors/basic.json | 133 + scripts/constant/abi/connectors/compound.json | 445 + .../constant/abi/connectors/instapool-c.json | 114 + .../constant/abi/connectors/instapool.json | 1 + scripts/constant/abi/connectors/maker.json | 483 + scripts/constant/abi/connectors/uniswap.json | 177 + .../abi/core/InstaImplementations.json | 206 + scripts/constant/abi/core/connectorsV2.json | 250 + scripts/constant/abi/core/instaIndex.json | 380 + scripts/constant/abi/read/compound.json | 186 + scripts/constant/abi/read/core.json | 312 + scripts/constant/abi/read/erc20.json | 68 + scripts/constant/abi/read/maker.json | 263 + scripts/constant/abi/read/uniswap.json | 281 + scripts/constant/abis.ts | 16 + scripts/constant/constant.ts | 6 + scripts/constant/deployAddress.ts | 0 scripts/deployment/connectors.ts | 71 + scripts/deployment/deploy.ts | 23 + .../deployment/deployAndVerifyOnSourcify.ts | 24 + scripts/deployment/deployCompoundMapping.ts | 79 + scripts/deployment/deployConnector.ts | 37 + scripts/deployment/deployConnectorsFromCmd.ts | 88 + .../deployment/deployConnectorsFromCmdAll.ts | 86 + .../deployInstaMappingController.ts | 37 + scripts/deployment/deployManually.ts | 23 + scripts/deployment/deployMappingContract.ts | 34 + scripts/tests/addLiquidity.ts | 42 + scripts/tests/arbitrum/addresses.ts | 11 + scripts/tests/arbitrum/tokens.ts | 60 + scripts/tests/avalanche/addresses.ts | 11 + scripts/tests/avalanche/tokens.ts | 87 + scripts/tests/buildDSAv2.ts | 33 + scripts/tests/command.ts | 33 + scripts/tests/deployAndEnableConnector.ts | 51 + scripts/tests/encodeFlashcastData.ts | 13 + scripts/tests/encodeSpells.ts | 17 + scripts/tests/fantom/addresses.ts | 11 + scripts/tests/fantom/tokens.ts | 64 + scripts/tests/getMasterSigner.ts | 39 + scripts/tests/global-test.ts | 44 + scripts/tests/impersonate.ts | 14 + scripts/tests/mainnet/addresses.ts | 11 + scripts/tests/mainnet/tokens.ts | 164 + scripts/tests/optimism/addresses.ts | 10 + scripts/tests/optimism/tokens.ts | 88 + scripts/tests/polygon/addresses.ts | 10 + scripts/tests/polygon/tokens.ts | 101 + scripts/tests/run-tests.ts | 68 + scripts/tests/run_test_through_cmd.ts | 63 + test/.gitkeep | 0 test/arbitrum/aave/aaveV3-import-test.ts | 303 + test/arbitrum/aave/v3-test.ts | 292 + test/arbitrum/arb-claim/test.ts | 80 + .../compound/compound.iii.rewards.test.ts | 444 + test/arbitrum/compound/compound.iii.test.ts | 651 ++ test/arbitrum/connext/connext.test.ts | 154 + test/arbitrum/dsa-spell/dsa-spell.test.ts | 180 + test/arbitrum/hop/hop.test.ts | 142 + test/arbitrum/sushiswap/sushiswap.test.ts | 148 + test/arbitrum/swap/swap-test.ts | 151 + .../uniswap-sell-beta/uniswap-sell-beta.ts | 129 + test/arbitrum/uniswap/uniswap-swap.test.ts | 138 + test/avalanche/0x/zeroEx.test.ts | 159 + test/avalanche/1inch/oneInch.test.ts | 160 + test/avalanche/aave/aaveV3-import-test.ts | 353 + test/avalanche/aave/v3-test.ts | 273 + test/avalanche/dsa-spell/dsa-spell.test.ts | 185 + .../pangolin/pangolin_exchange.test.ts | 282 + .../avalanche/pangolin/pangolin_stake.test.ts | 821 ++ test/avalanche/sushiswap/sushiswap.test.ts | 148 + test/avalanche/swap/swap-test.ts | 188 + test/fantom/aave/v3-test.ts | 264 + test/fantom/dsa-spell/dsa-spell.test.ts | 182 + test/fantom/swap/swap-test.ts | 175 + test/mainnet/0x/zeroEx.test.ts | 161 + test/mainnet/aave/v1.test.ts | 168 + test/mainnet/aave/v2.test.ts | 226 + test/mainnet/b.protocol/b.compound.test.ts | 138 + test/mainnet/b.protocol/b.liquity.test.ts | 189 + test/mainnet/b.protocol/b.maker.test.ts | 336 + .../mainnet/basic-ERC1155/ERC1155-transfer.ts | 135 + test/mainnet/basic-ERC721/ERC721-transfer.ts | 135 + test/mainnet/basic_ERC4626/ERC4626.test.ts | 318 + .../compound-import/compound-import.test.ts | 254 + .../compound-v3-import.test.ts | 401 + .../compound/compound.iii.rewards.test.ts | 444 + test/mainnet/compound/compound.iii.test.ts | 653 ++ test/mainnet/compound/compound.test.ts | 138 + test/mainnet/connext/connext.test.ts | 154 + test/mainnet/crv_usd/crv_usd.test.ts | 445 + test/mainnet/dsa-spell/dsa-spell.test.ts | 176 + .../mainnet/euler-import/euler-import.test.ts | 268 + .../euler-rewards/euler-rewards.test.ts | 72 + test/mainnet/euler/euler.test.ts | 538 + test/mainnet/hop/hop.test.ts | 127 + test/mainnet/instaLite/instaLite.test.ts | 101 + test/mainnet/instapool/instapool.test.ts | 119 + test/mainnet/kyberswap/kyberswap.test.ts | 167 + test/mainnet/lido_stETH/abi.json | 731 ++ test/mainnet/lido_stETH/lidoStEth.test.ts | 103 + test/mainnet/liquity/liquity.contracts.ts | 95 + test/mainnet/liquity/liquity.helpers.ts | 343 + test/mainnet/liquity/liquity.test.ts | 2752 +++++ test/mainnet/mappings/mappings.test.ts | 182 + test/mainnet/morpho/morpho-aave-v3.test.ts | 484 + test/mainnet/morpho/morpho-aave.test.ts | 294 + test/mainnet/morpho/morpho-compound.test.ts | 272 + test/mainnet/mstable/mstable.helpers.ts | 160 + test/mainnet/mstable/mstable.test.ts | 234 + test/mainnet/mstable/mstable.utils.ts | 137 + test/mainnet/notional/notional.contracts.ts | 116 + test/mainnet/notional/notional.helpers.ts | 332 + test/mainnet/notional/notional.test.ts | 513 + test/mainnet/paraswap/paraswap.test.ts | 177 + .../mainnet/pooltogether/pooltogether.test.ts | 1238 +++ test/mainnet/spark/spark.test.ts | 301 + test/mainnet/sushiswap/sushiswap.test.ts | 147 + .../sushiswapIncentive.test.ts | 245 + test/mainnet/swap/swap-test.ts | 211 + test/mainnet/ubiquity/ubiquity.test.ts | 462 + test/mainnet/ubiquity/utils.ts | 55 + test/mainnet/uniswap/uniswap-swap.test.ts | 150 + test/mainnet/uniswap/uniswap.test.ts | 366 + .../mainnet/uniswapStake/uniswapStake.test.ts | 357 + .../uniswapV3Router/uniswapV3Router.test.ts | 308 + test/mainnet/yearn/yearn.test.ts | 189 + test/optimism/aave/aaveV3-import-test.ts | 464 + test/optimism/aave/v3-test.ts | 268 + test/optimism/connext/connext.test.ts | 154 + test/optimism/dsa-spell/dsa-spell.test.ts | 180 + test/optimism/swap/swap-test.ts | 179 + test/optimism/uniswap/uniswap-swap.test.ts | 157 + test/polygon/0x/zeroEx.test.ts | 159 + test/polygon/aave/aaveV3-import-test.ts | 368 + test/polygon/aave/v3-test.ts | 264 + .../compound/compound.iii.rewards.test.ts | 448 + test/polygon/compound/compound.iii.test.ts | 661 ++ test/polygon/connext/connext.test.ts | 123 + test/polygon/dsa-spell/dsa-spell.test.ts | 185 + test/polygon/hop/hop.test.ts | 240 + test/polygon/mstable/mstable.helpers.ts | 159 + test/polygon/mstable/mstable.test.ts | 234 + test/polygon/mstable/mstable.utils.ts | 137 + test/polygon/paraswap/paraswap.test.ts | 171 + .../polygon/pooltogether/pooltogether.test.ts | 561 + test/polygon/quickswap/quickswap.test.ts | 137 + test/polygon/sushiswap/sushiswap.test.ts | 148 + test/polygon/swap/swap-test.ts | 203 + test/polygon/uniswap/uniswap-swap.test.ts | 139 + .../uniswapV3Router/uniswapV3Router.test.ts | 156 + tsconfig.json | 11 + 159 files changed, 43241 insertions(+) create mode 100644 .env.example create mode 100644 .gitignore create mode 100644 hardhat.config.ts create mode 100644 package-lock.json create mode 100644 package.json create mode 100644 scripts/constant/abi/basics/erc20.json create mode 100644 scripts/constant/abi/connectors/auth.json create mode 100644 scripts/constant/abi/connectors/basic.json create mode 100644 scripts/constant/abi/connectors/compound.json create mode 100644 scripts/constant/abi/connectors/instapool-c.json create mode 100644 scripts/constant/abi/connectors/instapool.json create mode 100644 scripts/constant/abi/connectors/maker.json create mode 100644 scripts/constant/abi/connectors/uniswap.json create mode 100644 scripts/constant/abi/core/InstaImplementations.json create mode 100644 scripts/constant/abi/core/connectorsV2.json create mode 100644 scripts/constant/abi/core/instaIndex.json create mode 100644 scripts/constant/abi/read/compound.json create mode 100644 scripts/constant/abi/read/core.json create mode 100644 scripts/constant/abi/read/erc20.json create mode 100644 scripts/constant/abi/read/maker.json create mode 100644 scripts/constant/abi/read/uniswap.json create mode 100644 scripts/constant/abis.ts create mode 100644 scripts/constant/constant.ts create mode 100644 scripts/constant/deployAddress.ts create mode 100644 scripts/deployment/connectors.ts create mode 100644 scripts/deployment/deploy.ts create mode 100644 scripts/deployment/deployAndVerifyOnSourcify.ts create mode 100644 scripts/deployment/deployCompoundMapping.ts create mode 100644 scripts/deployment/deployConnector.ts create mode 100644 scripts/deployment/deployConnectorsFromCmd.ts create mode 100644 scripts/deployment/deployConnectorsFromCmdAll.ts create mode 100644 scripts/deployment/deployInstaMappingController.ts create mode 100644 scripts/deployment/deployManually.ts create mode 100644 scripts/deployment/deployMappingContract.ts create mode 100644 scripts/tests/addLiquidity.ts create mode 100644 scripts/tests/arbitrum/addresses.ts create mode 100644 scripts/tests/arbitrum/tokens.ts create mode 100644 scripts/tests/avalanche/addresses.ts create mode 100644 scripts/tests/avalanche/tokens.ts create mode 100644 scripts/tests/buildDSAv2.ts create mode 100644 scripts/tests/command.ts create mode 100644 scripts/tests/deployAndEnableConnector.ts create mode 100644 scripts/tests/encodeFlashcastData.ts create mode 100644 scripts/tests/encodeSpells.ts create mode 100644 scripts/tests/fantom/addresses.ts create mode 100644 scripts/tests/fantom/tokens.ts create mode 100644 scripts/tests/getMasterSigner.ts create mode 100644 scripts/tests/global-test.ts create mode 100644 scripts/tests/impersonate.ts create mode 100644 scripts/tests/mainnet/addresses.ts create mode 100644 scripts/tests/mainnet/tokens.ts create mode 100644 scripts/tests/optimism/addresses.ts create mode 100644 scripts/tests/optimism/tokens.ts create mode 100644 scripts/tests/polygon/addresses.ts create mode 100644 scripts/tests/polygon/tokens.ts create mode 100644 scripts/tests/run-tests.ts create mode 100644 scripts/tests/run_test_through_cmd.ts create mode 100644 test/.gitkeep create mode 100644 test/arbitrum/aave/aaveV3-import-test.ts create mode 100644 test/arbitrum/aave/v3-test.ts create mode 100644 test/arbitrum/arb-claim/test.ts create mode 100644 test/arbitrum/compound/compound.iii.rewards.test.ts create mode 100644 test/arbitrum/compound/compound.iii.test.ts create mode 100644 test/arbitrum/connext/connext.test.ts create mode 100644 test/arbitrum/dsa-spell/dsa-spell.test.ts create mode 100644 test/arbitrum/hop/hop.test.ts create mode 100644 test/arbitrum/sushiswap/sushiswap.test.ts create mode 100644 test/arbitrum/swap/swap-test.ts create mode 100644 test/arbitrum/uniswap-sell-beta/uniswap-sell-beta.ts create mode 100644 test/arbitrum/uniswap/uniswap-swap.test.ts create mode 100644 test/avalanche/0x/zeroEx.test.ts create mode 100644 test/avalanche/1inch/oneInch.test.ts create mode 100644 test/avalanche/aave/aaveV3-import-test.ts create mode 100644 test/avalanche/aave/v3-test.ts create mode 100644 test/avalanche/dsa-spell/dsa-spell.test.ts create mode 100644 test/avalanche/pangolin/pangolin_exchange.test.ts create mode 100644 test/avalanche/pangolin/pangolin_stake.test.ts create mode 100644 test/avalanche/sushiswap/sushiswap.test.ts create mode 100644 test/avalanche/swap/swap-test.ts create mode 100644 test/fantom/aave/v3-test.ts create mode 100644 test/fantom/dsa-spell/dsa-spell.test.ts create mode 100644 test/fantom/swap/swap-test.ts create mode 100644 test/mainnet/0x/zeroEx.test.ts create mode 100644 test/mainnet/aave/v1.test.ts create mode 100644 test/mainnet/aave/v2.test.ts create mode 100644 test/mainnet/b.protocol/b.compound.test.ts create mode 100644 test/mainnet/b.protocol/b.liquity.test.ts create mode 100644 test/mainnet/b.protocol/b.maker.test.ts create mode 100644 test/mainnet/basic-ERC1155/ERC1155-transfer.ts create mode 100644 test/mainnet/basic-ERC721/ERC721-transfer.ts create mode 100644 test/mainnet/basic_ERC4626/ERC4626.test.ts create mode 100644 test/mainnet/compound-import/compound-import.test.ts create mode 100644 test/mainnet/compound-import/compound-v3-import.test.ts create mode 100644 test/mainnet/compound/compound.iii.rewards.test.ts create mode 100644 test/mainnet/compound/compound.iii.test.ts create mode 100644 test/mainnet/compound/compound.test.ts create mode 100644 test/mainnet/connext/connext.test.ts create mode 100644 test/mainnet/crv_usd/crv_usd.test.ts create mode 100644 test/mainnet/dsa-spell/dsa-spell.test.ts create mode 100644 test/mainnet/euler-import/euler-import.test.ts create mode 100644 test/mainnet/euler-rewards/euler-rewards.test.ts create mode 100644 test/mainnet/euler/euler.test.ts create mode 100644 test/mainnet/hop/hop.test.ts create mode 100644 test/mainnet/instaLite/instaLite.test.ts create mode 100644 test/mainnet/instapool/instapool.test.ts create mode 100644 test/mainnet/kyberswap/kyberswap.test.ts create mode 100644 test/mainnet/lido_stETH/abi.json create mode 100644 test/mainnet/lido_stETH/lidoStEth.test.ts create mode 100644 test/mainnet/liquity/liquity.contracts.ts create mode 100644 test/mainnet/liquity/liquity.helpers.ts create mode 100644 test/mainnet/liquity/liquity.test.ts create mode 100644 test/mainnet/mappings/mappings.test.ts create mode 100644 test/mainnet/morpho/morpho-aave-v3.test.ts create mode 100644 test/mainnet/morpho/morpho-aave.test.ts create mode 100644 test/mainnet/morpho/morpho-compound.test.ts create mode 100644 test/mainnet/mstable/mstable.helpers.ts create mode 100644 test/mainnet/mstable/mstable.test.ts create mode 100644 test/mainnet/mstable/mstable.utils.ts create mode 100644 test/mainnet/notional/notional.contracts.ts create mode 100644 test/mainnet/notional/notional.helpers.ts create mode 100644 test/mainnet/notional/notional.test.ts create mode 100644 test/mainnet/paraswap/paraswap.test.ts create mode 100644 test/mainnet/pooltogether/pooltogether.test.ts create mode 100644 test/mainnet/spark/spark.test.ts create mode 100644 test/mainnet/sushiswap/sushiswap.test.ts create mode 100644 test/mainnet/sushiswapIncentive/sushiswapIncentive.test.ts create mode 100644 test/mainnet/swap/swap-test.ts create mode 100644 test/mainnet/ubiquity/ubiquity.test.ts create mode 100644 test/mainnet/ubiquity/utils.ts create mode 100644 test/mainnet/uniswap/uniswap-swap.test.ts create mode 100644 test/mainnet/uniswap/uniswap.test.ts create mode 100644 test/mainnet/uniswapStake/uniswapStake.test.ts create mode 100644 test/mainnet/uniswapV3Router/uniswapV3Router.test.ts create mode 100644 test/mainnet/yearn/yearn.test.ts create mode 100644 test/optimism/aave/aaveV3-import-test.ts create mode 100644 test/optimism/aave/v3-test.ts create mode 100644 test/optimism/connext/connext.test.ts create mode 100644 test/optimism/dsa-spell/dsa-spell.test.ts create mode 100644 test/optimism/swap/swap-test.ts create mode 100644 test/optimism/uniswap/uniswap-swap.test.ts create mode 100644 test/polygon/0x/zeroEx.test.ts create mode 100644 test/polygon/aave/aaveV3-import-test.ts create mode 100644 test/polygon/aave/v3-test.ts create mode 100644 test/polygon/compound/compound.iii.rewards.test.ts create mode 100644 test/polygon/compound/compound.iii.test.ts create mode 100644 test/polygon/connext/connext.test.ts create mode 100644 test/polygon/dsa-spell/dsa-spell.test.ts create mode 100644 test/polygon/hop/hop.test.ts create mode 100644 test/polygon/mstable/mstable.helpers.ts create mode 100644 test/polygon/mstable/mstable.test.ts create mode 100644 test/polygon/mstable/mstable.utils.ts create mode 100644 test/polygon/paraswap/paraswap.test.ts create mode 100644 test/polygon/pooltogether/pooltogether.test.ts create mode 100644 test/polygon/quickswap/quickswap.test.ts create mode 100644 test/polygon/sushiswap/sushiswap.test.ts create mode 100644 test/polygon/swap/swap-test.ts create mode 100644 test/polygon/uniswap/uniswap-swap.test.ts create mode 100644 test/polygon/uniswapV3Router/uniswapV3Router.test.ts create mode 100644 tsconfig.json diff --git a/.env.example b/.env.example new file mode 100644 index 0000000..8a266b3 --- /dev/null +++ b/.env.example @@ -0,0 +1,11 @@ +ETHERSCAN_API_KEY="" +PRIVATE_KEY="" +TENDERLY_PROJECT="" +TENDERLY_USERNAME="" +ALCHEMY_ID="" +MAIN_ETHSCAN_KEY= +OPT_ETHSCAN_KEY= +POLY_ETHSCAN_KEY= +ARB_ETHSCAN_KEY= +AVAX_ETHSCAN_KEY= +FTM_ETHSCAN_KEY= \ No newline at end of file diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..2aed425 --- /dev/null +++ b/.gitignore @@ -0,0 +1,67 @@ +lib-cov +*.seed +*.log +*.csv +*.dat +*.out +*.pid +*.gz +*.swp + +pids +logs +results +tmp + +# Optional npm cache directory +.npm + +#Build +coverage +public/css/main.css +.nyc_output/* + +#Libraries from npm packages +public/js/lib/bootstrap.min* +public/js/lib/jquery.min* +public/js/lib/popper.min* + +# API keys and secrets +.env + +# Dependency directory +node_modules +bower_components + +# Editors +.idea +.vscode +*.iml +modules.xml +*.ipr + +# Folder config file +Desktop.ini + +# Recycle Bin used on file shares +$RECYCLE.BIN/ + +# OS metadata +.DS_Store +Thumbs.db +.DocumentRevisions-V100 +.fseventsd +.Spotlight-V100 +.TemporaryItems +.Trashes +.VolumeIcon.icns +.com.apple.timemachine.donotpresent + +# truffle +build/contracts + +# buidler +artifacts +cache +typechain + diff --git a/hardhat.config.ts b/hardhat.config.ts new file mode 100644 index 0000000..e8631f0 --- /dev/null +++ b/hardhat.config.ts @@ -0,0 +1,154 @@ +import "@typechain/hardhat"; +import { resolve } from "path"; +import { config as dotenvConfig } from "dotenv"; +import { HardhatUserConfig } from "hardhat/config"; +import { NetworkUserConfig } from "hardhat/types"; +import Web3 from "web3"; +import { network } from "hardhat"; +import bigNumber from "bignumber.js"; +import "./scripts/tests/run_test_through_cmd"; + +dotenvConfig({ path: resolve(__dirname, "./.env") }); + +const chainIds = { + ganache: 1337, + hardhat: 31337, + mainnet: 1, + avalanche: 43114, + polygon: 137, + arbitrum: 42161, + optimism: 10, + fantom: 250, + base: 8453, +}; + +const alchemyApiKey = process.env.ALCHEMY_API_KEY; +if (!alchemyApiKey) { + throw new Error("Please set your ALCHEMY_API_KEY in a .env file"); +} + +const PRIVATE_KEY = process.env.PRIVATE_KEY; +const mnemonic = process.env.MNEMONIC ?? "test test test test test test test test test test test junk"; + +const networkGasPriceConfig: Record = { + mainnet: 41, + polygon: 50, + avalanche: 40, + arbitrum: 1, + optimism: 0.001, + fantom: 210, + base: 0.0005 +}; + +function createConfig(network: string) { + return { + url: getNetworkUrl(network), + accounts: !!PRIVATE_KEY ? [`0x${PRIVATE_KEY}`] : { mnemonic }, + gasPrice: new bigNumber(networkGasPriceConfig[network]).times(1e9).toNumber() // Update the mapping above + }; +} + +function getNetworkUrl(networkType: string) { + if (networkType === "avalanche") return "https://api.avax.network/ext/bc/C/rpc"; + else if (networkType === "polygon") return `https://polygon-mainnet.g.alchemy.com/v2/${alchemyApiKey}`; + else if (networkType === "arbitrum") return `https://arb-mainnet.g.alchemy.com/v2/${alchemyApiKey}`; + else if (networkType === "optimism") return `https://opt-mainnet.g.alchemy.com/v2/${alchemyApiKey}`; + else if (networkType === "fantom") return `https://rpc.ftm.tools/`; + else if (networkType === "base") return `https://1rpc.io/base`; + else return `https://eth-mainnet.alchemyapi.io/v2/${alchemyApiKey}`; +} + +/** + * @type import('hardhat/config').HardhatUserConfig + */ +const config: any = { + solidity: { + compilers: [ + { + version: "0.8.22", + settings: { + optimizer: { + enabled: true, + runs: 200 + } + } + }, + { + version: "0.7.6", + settings: { + optimizer: { + enabled: true, + runs: 200 + } + } + }, + { + version: "0.6.0" + }, + { + version: "0.6.2" + }, + { + version: "0.6.5" + } + ] + }, + networks: { + hardhat: { + accounts: { + mnemonic + }, + chainId: chainIds.hardhat, + forking: { + url: String(getNetworkUrl(String(process.env.networkType))) + } + }, + mainnet: createConfig("mainnet"), + polygon: createConfig("polygon"), + avalanche: createConfig("avalanche"), + arbitrum: createConfig("arbitrum"), + optimism: createConfig("optimism"), + fantom: createConfig("fantom"), + base: createConfig("base") + }, + paths: { + artifacts: "./artifacts", + cache: "./cache", + sources: "./contracts", + tests: "./test" + }, + etherscan: { + apiKey: { + mainnet: String(process.env.MAIN_ETHSCAN_KEY), + optimisticEthereum: String(process.env.OPT_ETHSCAN_KEY), + polygon: String(process.env.POLY_ETHSCAN_KEY), + arbitrumOne: String(process.env.ARB_ETHSCAN_KEY), + avalanche: String(process.env.AVAX_ETHSCAN_KEY), + opera: String(process.env.FTM_ETHSCAN_KEY), + base: String(process.env.BASE_ETHSCAN_KEY), + }, + customChains: [ + { + network: "base", + chainId: 8453, + urls: { + apiURL: "https://api.basescan.org/api", + browserURL: "https://basescan.org" + } + } + ] + }, + typechain: { + outDir: "typechain", + target: "ethers-v5" + }, + mocha: { + timeout: 10000 * 1000 // 10,000 seconds + } + // tenderly: { + // project: process.env.TENDERLY_PROJECT, + // username: process.env.TENDERLY_USERNAME, + // }, +}; + +export default config; diff --git a/package-lock.json b/package-lock.json new file mode 100644 index 0000000..565759d --- /dev/null +++ b/package-lock.json @@ -0,0 +1,9078 @@ +{ + "name": "dsa-connectors-2.0", + "version": "1.0.0", + "lockfileVersion": 2, + "requires": true, + "packages": { + "": { + "name": "dsa-connectors-2.0", + "version": "1.0.0", + "dependencies": { + "@openzeppelin/contracts": "^4.9.5", + "@typechain/ethers-v5": "^10.2.1", + "@typechain/hardhat": "^6.1.6", + "bignumber.js": "^4.0.4", + "dotenv": "^16.3.1", + "ts-node": "^10.9.2", + "web3": "^4.3.0" + }, + "devDependencies": { + "hardhat": "^2.19.2", + "typechain": "^8.3.2" + } + }, + "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==" + }, + "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/@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==", + "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==", + "bin": { + "rlp": "bin/rlp" + }, + "engines": { + "node": ">=14" + } + }, + "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/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/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==", + "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==" + }, + "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==", + "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/@noble/curves": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@noble/curves/-/curves-1.1.0.tgz", + "integrity": "sha512-091oBExgENk/kGj3AZmtBDMpxQPDtxQABR2B9lb1JbVTs6ytdzZNwvhxQ4MWasRNEzlbEH8jCWFCwhF/Obj5AA==", + "dependencies": { + "@noble/hashes": "1.3.1" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/@noble/curves/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==", + "engines": { + "node": ">= 16" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, + "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/@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/@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/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/@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-blockchain/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/@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-ethash/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/@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-evm/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/@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/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/@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-trie/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/@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-tx/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/@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-util/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/@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/ethereumjs-vm/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/@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/contracts": { + "version": "4.9.5", + "resolved": "https://registry.npmjs.org/@openzeppelin/contracts/-/contracts-4.9.5.tgz", + "integrity": "sha512-ZK+W5mVhRppff9BE6YdR8CC52C8zAvsVAiWhEtQ5+oNxFE6h1WdeWo+FJSF8KKvtxxVYZ7MTP/5KoVpAU3aSWg==" + }, + "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.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/@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/@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/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/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/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/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/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/@tsconfig/node10": { + "version": "1.0.9", + "resolved": "https://registry.npmjs.org/@tsconfig/node10/-/node10-1.0.9.tgz", + "integrity": "sha512-jNsYVVxU8v5g43Erja32laIDHXeoNvFEpX33OK4d6hljo3jDhCBDhx5dhCCTMWUojscpAagGiRkBKxpdl9fxqA==" + }, + "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==" + }, + "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==" + }, + "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==" + }, + "node_modules/@typechain/ethers-v5": { + "version": "10.2.1", + "resolved": "https://registry.npmjs.org/@typechain/ethers-v5/-/ethers-v5-10.2.1.tgz", + "integrity": "sha512-n3tQmCZjRE6IU4h6lqUGiQ1j866n5MTCBJreNEHHVWXa2u9GJTaeYyU1/k+1qLutkyw+sS6VAN+AbeiTqsxd/A==", + "dependencies": { + "lodash": "^4.17.15", + "ts-essentials": "^7.0.1" + }, + "peerDependencies": { + "@ethersproject/abi": "^5.0.0", + "@ethersproject/providers": "^5.0.0", + "ethers": "^5.1.3", + "typechain": "^8.1.1", + "typescript": ">=4.3.0" + } + }, + "node_modules/@typechain/hardhat": { + "version": "6.1.6", + "resolved": "https://registry.npmjs.org/@typechain/hardhat/-/hardhat-6.1.6.tgz", + "integrity": "sha512-BiVnegSs+ZHVymyidtK472syodx1sXYlYJJixZfRstHVGYTi8V1O7QG4nsjyb0PC/LORcq7sfBUcHto1y6UgJA==", + "dependencies": { + "fs-extra": "^9.1.0" + }, + "peerDependencies": { + "@ethersproject/abi": "^5.4.7", + "@ethersproject/providers": "^5.4.7", + "@typechain/ethers-v5": "^10.2.1", + "ethers": "^5.4.7", + "hardhat": "^2.9.9", + "typechain": "^8.1.1" + } + }, + "node_modules/@typechain/hardhat/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==", + "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/@typechain/hardhat/node_modules/jsonfile": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.1.0.tgz", + "integrity": "sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ==", + "dependencies": { + "universalify": "^2.0.0" + }, + "optionalDependencies": { + "graceful-fs": "^4.1.6" + } + }, + "node_modules/@typechain/hardhat/node_modules/universalify": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.1.tgz", + "integrity": "sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==", + "engines": { + "node": ">= 10.0.0" + } + }, + "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/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/node": { + "version": "20.10.4", + "resolved": "https://registry.npmjs.org/@types/node/-/node-20.10.4.tgz", + "integrity": "sha512-D08YG6rr8X90YB56tSIuBaddy/UXAA9RKJoFvrsnogAum/0pmjkgi4+2nx96A330FmioegBWmEYQ+syqCFaveg==", + "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==" + }, + "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/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/@types/ws": { + "version": "8.5.3", + "resolved": "https://registry.npmjs.org/@types/ws/-/ws-8.5.3.tgz", + "integrity": "sha512-6YOoWjruKj1uLf3INHH7D3qTXwFfEsg1kf3c0uDdSBJwfa/llkwIjrAGV7j7mVgGNbzTQ3HiHKKDXl6bJPD97w==", + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/abitype": { + "version": "0.7.1", + "resolved": "https://registry.npmjs.org/abitype/-/abitype-0.7.1.tgz", + "integrity": "sha512-VBkRHTDZf9Myaek/dO3yMmOzB/y2s3Zo6nVU7yaw1G+TvCHAjwaJzNGN9yo4K5D8bU/VZXKP1EJpRhFr862PlQ==", + "peerDependencies": { + "typescript": ">=4.9.4", + "zod": "^3 >=3.19.1" + }, + "peerDependenciesMeta": { + "zod": { + "optional": 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.2", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.11.2.tgz", + "integrity": "sha512-nc0Axzp/0FILLEVsm4fNwLCwMttvhEI263QtVPQcbpfZZ3ts0hLsZGOpE6czNlid7CJ9MlyH8reXkpsf3YUY4w==", + "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==", + "engines": { + "node": ">=0.4.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": "3.0.0", + "resolved": "https://registry.npmjs.org/aes-js/-/aes-js-3.0.0.tgz", + "integrity": "sha512-H7wUZRn8WpTq9jocdxQ2c8x2sKo9ZVmzfRE13GiNJXfp7NcKYEdvl3vspKjXox6RIG2VtaRe4JFvxG4rqp2Zuw==" + }, + "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/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/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==" + }, + "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==", + "engines": { + "node": ">=6" + } + }, + "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==", + "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==", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "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/bignumber.js": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/bignumber.js/-/bignumber.js-4.1.0.tgz", + "integrity": "sha512-eJzYkFYy9L4JzXsbymsFn3p54D+llV27oTQ+ziJG7WFRheJcNZilgVXMG0LoZtlQSKBsJdWtLFqOD0u+U0jZKA==", + "engines": { + "node": "*" + } + }, + "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==", + "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/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/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/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/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/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/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==", + "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==", + "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==", + "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==", + "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/concat-map": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", + "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==" + }, + "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/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==" + }, + "node_modules/cross-fetch": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/cross-fetch/-/cross-fetch-4.0.0.tgz", + "integrity": "sha512-e4a5N8lVvuLgAWgnCrLr2PP0YyDOTHa9H/Rj54dirp61qXnNq46m82bRhNqIA5VccJtWBvPTFRV3TtvHUKPB1g==", + "dependencies": { + "node-fetch": "^2.6.12" + } + }, + "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-extend": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/deep-extend/-/deep-extend-0.6.0.tgz", + "integrity": "sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA==", + "engines": { + "node": ">=4.0.0" + } + }, + "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==", + "dependencies": { + "get-intrinsic": "^1.2.1", + "gopd": "^1.0.1", + "has-property-descriptors": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "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/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/dotenv": { + "version": "16.3.1", + "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.3.1.tgz", + "integrity": "sha512-IPzF4w4/Rd94bA9imS68tZBaYyBWSCE47V1RGuMrB94iyTOIEwRmVL2x/4An+6mETpLrKJ5hQkB8W4kFAadeIQ==", + "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/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/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/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/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-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/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-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/ethereumjs-util/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/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/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/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==", + "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.3", + "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.3.tgz", + "integrity": "sha512-1VzOtuEM8pC9SFU1E+8KfTjZyMztRsgEfwQl44z8A25uy13jSzTj6dyK2Df52iV0vgHCfBwLhDWevLn95w5v6Q==", + "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==", + "dependencies": { + "is-callable": "^1.1.3" + } + }, + "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": "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/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==", + "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/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-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==", + "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/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/gopd": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.0.1.tgz", + "integrity": "sha512-d65bNlIadxvpb/A2abVdlqKqV563juRnZ1Wtk6s1sIR8uNsXR70xqIzVqxVf1eTqDunwT2MkczEeaezCKTZhwA==", + "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/hardhat": { + "version": "2.19.2", + "resolved": "https://registry.npmjs.org/hardhat/-/hardhat-2.19.2.tgz", + "integrity": "sha512-CRU3+0Cc8Qh9UpxKd8cLADDPes7ZDtKj4dTK+ERtLBomEzhRPLWklJn4VKOwjre9/k8GNd/e9DYxpfuzcxbXPQ==", + "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/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==", + "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==", + "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==", + "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==", + "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-base/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/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==", + "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/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-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/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/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/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-arguments": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/is-arguments/-/is-arguments-1.1.1.tgz", + "integrity": "sha512-8Q7EARjzEnKpt/PCD7e1cgUS0a6X8u5tdSiMqXhojOdoV9TsMsiO+9VLC5vAmO8N7/GmXn7yjR8qnA6bVAEzfA==", + "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-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-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==", + "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": "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/is-generator-function": { + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/is-generator-function/-/is-generator-function-1.0.10.tgz", + "integrity": "sha512-jsEjy9l3yiXEQ+PsXdmBwEPcOxaXWLspKdplFUVI9vq1iZgIekeC0L167qeu86czQaxed3q/Uzuw0swL0irL8A==", + "dependencies": { + "has-tostringtag": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "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-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-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-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==", + "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/isomorphic-ws": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/isomorphic-ws/-/isomorphic-ws-5.0.0.tgz", + "integrity": "sha512-muId7Zzn9ywDsyXgTIafTry2sV3nySZeUDe6YedVd1Hvuuep5AsIlqK+XefWpYTyJG5e503F2xIuT2lcU6rCSw==", + "peerDependencies": { + "ws": "*" + } + }, + "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/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/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/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/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==" + }, + "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/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==" + }, + "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/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/mkdirp": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.4.tgz", + "integrity": "sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==", + "bin": { + "mkdirp": "bin/cmd.js" + }, + "engines": { + "node": ">=10" + } + }, + "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/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-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==", + "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/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/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/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/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/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/prettier": { + "version": "2.8.8", + "resolved": "https://registry.npmjs.org/prettier/-/prettier-2.8.8.tgz", + "integrity": "sha512-tdN8qQGvNjw4CHbY+XXk0JgCXn9QiF21a55rBe5LJAU+kDyC4WQn4+awm2Xfk2lQMk5fKup9XgzTZtGkjBdP9Q==", + "bin": { + "prettier": "bin-prettier.js" + }, + "engines": { + "node": ">=10.13.0" + }, + "funding": { + "url": "https://github.com/prettier/prettier?sponsor=1" + } + }, + "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/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==", + "engines": { + "node": ">=6" + } + }, + "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/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-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-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/safer-buffer": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", + "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==" + }, + "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==", + "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/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/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/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/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/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_decoder/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/string-format": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/string-format/-/string-format-2.0.0.tgz", + "integrity": "sha512-bbEs3scLeYNXLecRRuk6uJxdXUSj6le/8rNPHChIJTn2V79aXVTR1EH2OH5zLKKoz0V02fOUKZZcw01pLUShZA==" + }, + "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/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/table-layout": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/table-layout/-/table-layout-1.0.2.tgz", + "integrity": "sha512-qd/R7n5rQTRFi+Zf2sk5XVVd9UQl6ZkduPFC3S7WEGJAmetDTjY3qPN50eSKzwuzEyQKy5TN2TiZdkIjos2L6A==", + "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==", + "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==", + "engines": { + "node": ">=8" + } + }, + "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==" + }, + "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==", + "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==", + "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==", + "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==", + "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==" + }, + "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==", + "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==", + "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==", + "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==", + "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==", + "engines": { + "node": ">=0.3.1" + } + }, + "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/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-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==", + "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/glob": { + "version": "7.1.7", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.7.tgz", + "integrity": "sha512-OvD9ENzPLbegENnYP5UUfJIirTg4+XwMWGaQfQTY0JenxNvvIKP3U3/tAQSPIu/lHxXYSZmpXlUHeqAIdKzBLQ==", + "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/typescript": { + "version": "5.3.3", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.3.3.tgz", + "integrity": "sha512-pXWcraxM0uxAS+tN0AG/BF2TyqmHO014Z070UsJ+pFvYuRSq8KH8DmWpnbXe0pEPDHXZV3FcAbJkijJ5oNEnWw==", + "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==", + "engines": { + "node": ">=8" + } + }, + "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/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/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/util": { + "version": "0.12.5", + "resolved": "https://registry.npmjs.org/util/-/util-0.12.5.tgz", + "integrity": "sha512-kZf/K6hEIrWHI6XqOFUiiMa+79wE/D8Q+NCNAWclkyg3b4d2k7s0QGepNjiABc+aR3N1PAyHL7p6UcLY6LmrnA==", + "dependencies": { + "inherits": "^2.0.3", + "is-arguments": "^1.0.4", + "is-generator-function": "^1.0.7", + "is-typed-array": "^1.1.3", + "which-typed-array": "^1.1.2" + } + }, + "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==" + }, + "node_modules/web3": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/web3/-/web3-4.3.0.tgz", + "integrity": "sha512-YiLCsb5wmgJlSxRLzt7Z7H+CmlVVIKD8VaUQaZ+xKVG3Q7CpsO5Z6jmeKnlr6M9c6fDDsDnRM6G8g+nchZehbA==", + "dependencies": { + "web3-core": "^4.3.2", + "web3-errors": "^1.1.4", + "web3-eth": "^4.3.1", + "web3-eth-abi": "^4.1.4", + "web3-eth-accounts": "^4.1.0", + "web3-eth-contract": "^4.1.4", + "web3-eth-ens": "^4.0.8", + "web3-eth-iban": "^4.0.7", + "web3-eth-personal": "^4.0.8", + "web3-net": "^4.0.7", + "web3-providers-http": "^4.1.0", + "web3-providers-ws": "^4.0.7", + "web3-rpc-methods": "^1.1.4", + "web3-types": "^1.3.1", + "web3-utils": "^4.1.0", + "web3-validator": "^2.0.3" + }, + "engines": { + "node": ">=14.0.0", + "npm": ">=6.12.0" + } + }, + "node_modules/web3-core": { + "version": "4.3.2", + "resolved": "https://registry.npmjs.org/web3-core/-/web3-core-4.3.2.tgz", + "integrity": "sha512-uIMVd/j4BgOnwfpY8ZT+QKubOyM4xohEhFZXz9xB8wimXWMMlYVlIK/TbfHqFolS9uOerdSGhsMbcK9lETae8g==", + "dependencies": { + "web3-errors": "^1.1.4", + "web3-eth-accounts": "^4.1.0", + "web3-eth-iban": "^4.0.7", + "web3-providers-http": "^4.1.0", + "web3-providers-ws": "^4.0.7", + "web3-types": "^1.3.1", + "web3-utils": "^4.1.0", + "web3-validator": "^2.0.3" + }, + "engines": { + "node": ">=14", + "npm": ">=6.12.0" + }, + "optionalDependencies": { + "web3-providers-ipc": "^4.0.7" + } + }, + "node_modules/web3-errors": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/web3-errors/-/web3-errors-1.1.4.tgz", + "integrity": "sha512-WahtszSqILez+83AxGecVroyZsMuuRT+KmQp4Si5P4Rnqbczno1k748PCrZTS1J4UCPmXMG2/Vt+0Bz2zwXkwQ==", + "dependencies": { + "web3-types": "^1.3.1" + }, + "engines": { + "node": ">=14", + "npm": ">=6.12.0" + } + }, + "node_modules/web3-eth": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/web3-eth/-/web3-eth-4.3.1.tgz", + "integrity": "sha512-zJir3GOXooHQT85JB8SrufE+Voo5TtXdjhf1D8IGXmxM8MrhI8AT+Pgt4siBTupJcu5hF17iGmTP/Nj2XnaibQ==", + "dependencies": { + "setimmediate": "^1.0.5", + "web3-core": "^4.3.0", + "web3-errors": "^1.1.3", + "web3-eth-abi": "^4.1.4", + "web3-eth-accounts": "^4.1.0", + "web3-net": "^4.0.7", + "web3-providers-ws": "^4.0.7", + "web3-rpc-methods": "^1.1.3", + "web3-types": "^1.3.0", + "web3-utils": "^4.0.7", + "web3-validator": "^2.0.3" + }, + "engines": { + "node": ">=14", + "npm": ">=6.12.0" + } + }, + "node_modules/web3-eth-abi": { + "version": "4.1.4", + "resolved": "https://registry.npmjs.org/web3-eth-abi/-/web3-eth-abi-4.1.4.tgz", + "integrity": "sha512-YLOBVVxxxLYKXjaiwZjEWYEnkMmmrm0nswZsvzSsINy/UgbWbzfoiZU+zn4YNWIEhORhx1p37iS3u/dP6VyC2w==", + "dependencies": { + "abitype": "0.7.1", + "web3-errors": "^1.1.3", + "web3-types": "^1.3.0", + "web3-utils": "^4.0.7", + "web3-validator": "^2.0.3" + }, + "engines": { + "node": ">=14", + "npm": ">=6.12.0" + } + }, + "node_modules/web3-eth-accounts": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/web3-eth-accounts/-/web3-eth-accounts-4.1.0.tgz", + "integrity": "sha512-UFtAsOANsvihTQ6SSvOKguupmQkResyR9M9JNuOxYpKh7+3W+sTnbLXw2UbOSYIsKlc1mpqqW9bVr1SjqHDpUQ==", + "dependencies": { + "@ethereumjs/rlp": "^4.0.1", + "crc-32": "^1.2.2", + "ethereum-cryptography": "^2.0.0", + "web3-errors": "^1.1.3", + "web3-types": "^1.3.0", + "web3-utils": "^4.0.7", + "web3-validator": "^2.0.3" + }, + "engines": { + "node": ">=14", + "npm": ">=6.12.0" + } + }, + "node_modules/web3-eth-accounts/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==", + "engines": { + "node": ">= 16" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/web3-eth-accounts/node_modules/@scure/bip32": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/@scure/bip32/-/bip32-1.3.1.tgz", + "integrity": "sha512-osvveYtyzdEVbt3OfwwXFr4P2iVBL5u1Q3q4ONBfDY/UpOuXmOlbgwc1xECEboY8wIays8Yt6onaWMUdUbfl0A==", + "dependencies": { + "@noble/curves": "~1.1.0", + "@noble/hashes": "~1.3.1", + "@scure/base": "~1.1.0" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/web3-eth-accounts/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==", + "dependencies": { + "@noble/hashes": "~1.3.0", + "@scure/base": "~1.1.0" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/web3-eth-accounts/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==", + "dependencies": { + "@noble/curves": "1.1.0", + "@noble/hashes": "1.3.1", + "@scure/bip32": "1.3.1", + "@scure/bip39": "1.2.1" + } + }, + "node_modules/web3-eth-contract": { + "version": "4.1.4", + "resolved": "https://registry.npmjs.org/web3-eth-contract/-/web3-eth-contract-4.1.4.tgz", + "integrity": "sha512-tJ4z6QLgtu8EQu2sXnLA7g427oxmngnbAUh+9kJKbP6Yep/oe+z79PqJv7H3MwqwUNW9T+/FeB2PnSQSyxz6ig==", + "dependencies": { + "web3-core": "^4.3.2", + "web3-errors": "^1.1.4", + "web3-eth": "^4.3.1", + "web3-eth-abi": "^4.1.4", + "web3-types": "^1.3.1", + "web3-utils": "^4.1.0", + "web3-validator": "^2.0.3" + }, + "engines": { + "node": ">=14", + "npm": ">=6.12.0" + } + }, + "node_modules/web3-eth-ens": { + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/web3-eth-ens/-/web3-eth-ens-4.0.8.tgz", + "integrity": "sha512-nj0JfeD45BbzVJcVYpUJnSo8iwDcY9CQ7CZhhIVVOFjvpMAPw0zEwjTvZEIQyCW61OoDG9xcBzwxe2tZoYhMRw==", + "dependencies": { + "@adraffy/ens-normalize": "^1.8.8", + "web3-core": "^4.3.0", + "web3-errors": "^1.1.3", + "web3-eth": "^4.3.1", + "web3-eth-contract": "^4.1.2", + "web3-net": "^4.0.7", + "web3-types": "^1.3.0", + "web3-utils": "^4.0.7", + "web3-validator": "^2.0.3" + }, + "engines": { + "node": ">=14", + "npm": ">=6.12.0" + } + }, + "node_modules/web3-eth-iban": { + "version": "4.0.7", + "resolved": "https://registry.npmjs.org/web3-eth-iban/-/web3-eth-iban-4.0.7.tgz", + "integrity": "sha512-8weKLa9KuKRzibC87vNLdkinpUE30gn0IGY027F8doeJdcPUfsa4IlBgNC4k4HLBembBB2CTU0Kr/HAOqMeYVQ==", + "dependencies": { + "web3-errors": "^1.1.3", + "web3-types": "^1.3.0", + "web3-utils": "^4.0.7", + "web3-validator": "^2.0.3" + }, + "engines": { + "node": ">=14", + "npm": ">=6.12.0" + } + }, + "node_modules/web3-eth-personal": { + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/web3-eth-personal/-/web3-eth-personal-4.0.8.tgz", + "integrity": "sha512-sXeyLKJ7ddQdMxz1BZkAwImjqh7OmKxhXoBNF3isDmD4QDpMIwv/t237S3q4Z0sZQamPa/pHebJRWVuvP8jZdw==", + "dependencies": { + "web3-core": "^4.3.0", + "web3-eth": "^4.3.1", + "web3-rpc-methods": "^1.1.3", + "web3-types": "^1.3.0", + "web3-utils": "^4.0.7", + "web3-validator": "^2.0.3" + }, + "engines": { + "node": ">=14", + "npm": ">=6.12.0" + } + }, + "node_modules/web3-net": { + "version": "4.0.7", + "resolved": "https://registry.npmjs.org/web3-net/-/web3-net-4.0.7.tgz", + "integrity": "sha512-SzEaXFrBjY25iQGk5myaOfO9ZyfTwQEa4l4Ps4HDNVMibgZji3WPzpjq8zomVHMwi8bRp6VV7YS71eEsX7zLow==", + "dependencies": { + "web3-core": "^4.3.0", + "web3-rpc-methods": "^1.1.3", + "web3-types": "^1.3.0", + "web3-utils": "^4.0.7" + }, + "engines": { + "node": ">=14", + "npm": ">=6.12.0" + } + }, + "node_modules/web3-providers-http": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/web3-providers-http/-/web3-providers-http-4.1.0.tgz", + "integrity": "sha512-6qRUGAhJfVQM41E5t+re5IHYmb5hSaLc02BE2MaRQsz2xKA6RjmHpOA5h/+ojJxEpI9NI2CrfDKOAgtJfoUJQg==", + "dependencies": { + "cross-fetch": "^4.0.0", + "web3-errors": "^1.1.3", + "web3-types": "^1.3.0", + "web3-utils": "^4.0.7" + }, + "engines": { + "node": ">=14", + "npm": ">=6.12.0" + } + }, + "node_modules/web3-providers-ipc": { + "version": "4.0.7", + "resolved": "https://registry.npmjs.org/web3-providers-ipc/-/web3-providers-ipc-4.0.7.tgz", + "integrity": "sha512-YbNqY4zUvIaK2MHr1lQFE53/8t/ejHtJchrWn9zVbFMGXlTsOAbNoIoZWROrg1v+hCBvT2c9z8xt7e/+uz5p1g==", + "optional": true, + "dependencies": { + "web3-errors": "^1.1.3", + "web3-types": "^1.3.0", + "web3-utils": "^4.0.7" + }, + "engines": { + "node": ">=14", + "npm": ">=6.12.0" + } + }, + "node_modules/web3-providers-ws": { + "version": "4.0.7", + "resolved": "https://registry.npmjs.org/web3-providers-ws/-/web3-providers-ws-4.0.7.tgz", + "integrity": "sha512-n4Dal9/rQWjS7d6LjyEPM2R458V8blRm0eLJupDEJOOIBhGYlxw5/4FthZZ/cqB7y/sLVi7K09DdYx2MeRtU5w==", + "dependencies": { + "@types/ws": "8.5.3", + "isomorphic-ws": "^5.0.0", + "web3-errors": "^1.1.3", + "web3-types": "^1.3.0", + "web3-utils": "^4.0.7", + "ws": "^8.8.1" + }, + "engines": { + "node": ">=14", + "npm": ">=6.12.0" + } + }, + "node_modules/web3-providers-ws/node_modules/ws": { + "version": "8.15.1", + "resolved": "https://registry.npmjs.org/ws/-/ws-8.15.1.tgz", + "integrity": "sha512-W5OZiCjXEmk0yZ66ZN82beM5Sz7l7coYxpRkzS+p9PP+ToQry8szKh+61eNktr7EA9DOwvFGhfC605jDHbP6QQ==", + "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/web3-rpc-methods": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/web3-rpc-methods/-/web3-rpc-methods-1.1.4.tgz", + "integrity": "sha512-LTFNg4LFaeU8K9ecuT8fHDp/LOXyxCneeZjCrRYIW1u82Ly52SrY55FIzMIISGoG/iT5Wh7UiHOB3CQsWLBmbQ==", + "dependencies": { + "web3-core": "^4.3.2", + "web3-types": "^1.3.1", + "web3-validator": "^2.0.3" + }, + "engines": { + "node": ">=14", + "npm": ">=6.12.0" + } + }, + "node_modules/web3-types": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/web3-types/-/web3-types-1.3.1.tgz", + "integrity": "sha512-8fXi7h/t95VKRtgU4sxprLPZpsTh3jYDfSghshIDBgUD/OoGe5S+syP24SUzBZYllZ/L+hMr2gdp/0bGJa8pYQ==", + "engines": { + "node": ">=14", + "npm": ">=6.12.0" + } + }, + "node_modules/web3-utils": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/web3-utils/-/web3-utils-4.1.0.tgz", + "integrity": "sha512-+VJWR6FtCsgwuJr5tvSvQlSEG06586df8h2CxGc9tcNtIDyJKNkSDDWJkdNPvyDhhXFzQYFh8QOGymD1CIP6fw==", + "dependencies": { + "ethereum-cryptography": "^2.0.0", + "web3-errors": "^1.1.4", + "web3-types": "^1.3.1", + "web3-validator": "^2.0.3" + }, + "engines": { + "node": ">=14", + "npm": ">=6.12.0" + } + }, + "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==", + "engines": { + "node": ">= 16" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/web3-utils/node_modules/@scure/bip32": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/@scure/bip32/-/bip32-1.3.1.tgz", + "integrity": "sha512-osvveYtyzdEVbt3OfwwXFr4P2iVBL5u1Q3q4ONBfDY/UpOuXmOlbgwc1xECEboY8wIays8Yt6onaWMUdUbfl0A==", + "dependencies": { + "@noble/curves": "~1.1.0", + "@noble/hashes": "~1.3.1", + "@scure/base": "~1.1.0" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/web3-utils/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==", + "dependencies": { + "@noble/hashes": "~1.3.0", + "@scure/base": "~1.1.0" + }, + "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==", + "dependencies": { + "@noble/curves": "1.1.0", + "@noble/hashes": "1.3.1", + "@scure/bip32": "1.3.1", + "@scure/bip39": "1.2.1" + } + }, + "node_modules/web3-validator": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/web3-validator/-/web3-validator-2.0.3.tgz", + "integrity": "sha512-fJbAQh+9LSNWy+l5Ze6HABreml8fra98o5+vS073T35jUcLbRZ0IOjF/ZPJhJNbJDt+jP1vseZsc3z3uX9mxxQ==", + "dependencies": { + "ethereum-cryptography": "^2.0.0", + "util": "^0.12.5", + "web3-errors": "^1.1.3", + "web3-types": "^1.3.0", + "zod": "^3.21.4" + }, + "engines": { + "node": ">=14", + "npm": ">=6.12.0" + } + }, + "node_modules/web3-validator/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==", + "engines": { + "node": ">= 16" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/web3-validator/node_modules/@scure/bip32": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/@scure/bip32/-/bip32-1.3.1.tgz", + "integrity": "sha512-osvveYtyzdEVbt3OfwwXFr4P2iVBL5u1Q3q4ONBfDY/UpOuXmOlbgwc1xECEboY8wIays8Yt6onaWMUdUbfl0A==", + "dependencies": { + "@noble/curves": "~1.1.0", + "@noble/hashes": "~1.3.1", + "@scure/base": "~1.1.0" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/web3-validator/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==", + "dependencies": { + "@noble/hashes": "~1.3.0", + "@scure/base": "~1.1.0" + }, + "funding": { + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/web3-validator/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==", + "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==" + }, + "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==", + "dependencies": { + "tr46": "~0.0.3", + "webidl-conversions": "^3.0.0" + } + }, + "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==", + "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/wordwrapjs": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/wordwrapjs/-/wordwrapjs-4.0.1.tgz", + "integrity": "sha512-kKlNACbvHrkpIw6oPeYDSmdCTu2hdMHoyXLTcUKala++lx5Y+wjJ/e474Jqv5abnVmwxw08DiTuHmw69lJGksA==", + "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==", + "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/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": "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/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/yn": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/yn/-/yn-3.1.1.tgz", + "integrity": "sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q==", + "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" + } + }, + "node_modules/zod": { + "version": "3.22.4", + "resolved": "https://registry.npmjs.org/zod/-/zod-3.22.4.tgz", + "integrity": "sha512-iC+8Io04lddc+mVqQ9AZ7OQ2MrUKGN+oIQyq1vemgt46jwCwLfhq7/pwnBnNXXXZb8VTVLKwp9EDkx+ryxIWmg==", + "funding": { + "url": "https://github.com/sponsors/colinhacks" + } + } + }, + "dependencies": { + "@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==" + }, + "@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==" + }, + "@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==", + "requires": { + "@chainsafe/as-sha256": "^0.3.1" + } + }, + "@chainsafe/ssz": { + "version": "0.9.4", + "resolved": "https://registry.npmjs.org/@chainsafe/ssz/-/ssz-0.9.4.tgz", + "integrity": "sha512-77Qtg2N1ayqs4Bg/wvnWfg5Bta7iy7IRh8XqXh7oNMeP2HBbBwx8m6yTpA8p0EHItWPEBkgZd5S5/LSlp3GXuQ==", + "requires": { + "@chainsafe/as-sha256": "^0.3.1", + "@chainsafe/persistent-merkle-tree": "^0.4.2", + "case": "^1.6.3" + } + }, + "@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==", + "requires": { + "@jridgewell/trace-mapping": "0.3.9" + } + }, + "@ethereumjs/rlp": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/@ethereumjs/rlp/-/rlp-4.0.1.tgz", + "integrity": "sha512-tqsQiBQDQdmPWE1xkkBq4rlSW5QZpLOUJ5RJh2/9fug+q9tnUhuZoVLk7s0scUIKTOzEtR72DFBXI4WiZcMpvw==" + }, + "@ethersproject/abi": { + "version": "5.7.0", + "resolved": "https://registry.npmjs.org/@ethersproject/abi/-/abi-5.7.0.tgz", + "integrity": "sha512-351ktp42TiRcYB3H1OP8yajPeAQstMW/yCFokj/AthP9bLHzQFPlOrxOcwYEDkUAICmOHljvN4K39OMTMUa9RA==", + "requires": { + "@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" + } + }, + "@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==", + "requires": { + "@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" + } + }, + "@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==", + "requires": { + "@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" + } + }, + "@ethersproject/address": { + "version": "5.7.0", + "resolved": "https://registry.npmjs.org/@ethersproject/address/-/address-5.7.0.tgz", + "integrity": "sha512-9wYhYt7aghVGo758POM5nqcOMaE168Q6aRLJZwUmiqSrAungkG74gSSeKEIR7ukixesdRZGPgVqme6vmxs1fkA==", + "requires": { + "@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" + } + }, + "@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==", + "requires": { + "@ethersproject/bytes": "^5.7.0" + } + }, + "@ethersproject/basex": { + "version": "5.7.0", + "resolved": "https://registry.npmjs.org/@ethersproject/basex/-/basex-5.7.0.tgz", + "integrity": "sha512-ywlh43GwZLv2Voc2gQVTKBoVQ1mti3d8HK5aMxsfu/nRDnMmNqaSJ3r3n85HBByT8OpoY96SXM1FogC533T4zw==", + "requires": { + "@ethersproject/bytes": "^5.7.0", + "@ethersproject/properties": "^5.7.0" + } + }, + "@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==", + "requires": { + "@ethersproject/bytes": "^5.7.0", + "@ethersproject/logger": "^5.7.0", + "bn.js": "^5.2.1" + } + }, + "@ethersproject/bytes": { + "version": "5.7.0", + "resolved": "https://registry.npmjs.org/@ethersproject/bytes/-/bytes-5.7.0.tgz", + "integrity": "sha512-nsbxwgFXWh9NyYWo+U8atvmMsSdKJprTcICAkvbBffT75qDocbuggBU0SJiVK2MuTrp0q+xvLkTnGMPK1+uA9A==", + "requires": { + "@ethersproject/logger": "^5.7.0" + } + }, + "@ethersproject/constants": { + "version": "5.7.0", + "resolved": "https://registry.npmjs.org/@ethersproject/constants/-/constants-5.7.0.tgz", + "integrity": "sha512-DHI+y5dBNvkpYUMiRQyxRBYBefZkJfo70VUkUAsRjcPs47muV9evftfZ0PJVCXYbAiCgght0DtcF9srFQmIgWA==", + "requires": { + "@ethersproject/bignumber": "^5.7.0" + } + }, + "@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==", + "requires": { + "@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" + } + }, + "@ethersproject/hash": { + "version": "5.7.0", + "resolved": "https://registry.npmjs.org/@ethersproject/hash/-/hash-5.7.0.tgz", + "integrity": "sha512-qX5WrQfnah1EFnO5zJv1v46a8HW0+E5xuBBDTwMFZLuVTx0tbU2kkx15NqdjxecrLGatQN9FGQKpb1FKdHCt+g==", + "requires": { + "@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" + } + }, + "@ethersproject/hdnode": { + "version": "5.7.0", + "resolved": "https://registry.npmjs.org/@ethersproject/hdnode/-/hdnode-5.7.0.tgz", + "integrity": "sha512-OmyYo9EENBPPf4ERhR7oj6uAtUAhYGqOnIS+jE5pTXvdKBS99ikzq1E7Iv0ZQZ5V36Lqx1qZLeak0Ra16qpeOg==", + "requires": { + "@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" + } + }, + "@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==", + "requires": { + "@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" + } + }, + "@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==", + "requires": { + "@ethersproject/bytes": "^5.7.0", + "js-sha3": "0.8.0" + } + }, + "@ethersproject/logger": { + "version": "5.7.0", + "resolved": "https://registry.npmjs.org/@ethersproject/logger/-/logger-5.7.0.tgz", + "integrity": "sha512-0odtFdXu/XHtjQXJYA3u9G0G8btm0ND5Cu8M7i5vhEcE8/HmF4Lbdqanwyv4uQTr2tx6b7fQRmgLrsnpQlmnig==" + }, + "@ethersproject/networks": { + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/@ethersproject/networks/-/networks-5.7.1.tgz", + "integrity": "sha512-n/MufjFYv3yFcUyfhnXotyDlNdFb7onmkSy8aQERi2PjNcnWQ66xXxa3XlS8nCcA8aJKJjIIMNJTC7tu80GwpQ==", + "requires": { + "@ethersproject/logger": "^5.7.0" + } + }, + "@ethersproject/pbkdf2": { + "version": "5.7.0", + "resolved": "https://registry.npmjs.org/@ethersproject/pbkdf2/-/pbkdf2-5.7.0.tgz", + "integrity": "sha512-oR/dBRZR6GTyaofd86DehG72hY6NpAjhabkhxgr3X2FpJtJuodEl2auADWBZfhDHgVCbu3/H/Ocq2uC6dpNjjw==", + "requires": { + "@ethersproject/bytes": "^5.7.0", + "@ethersproject/sha2": "^5.7.0" + } + }, + "@ethersproject/properties": { + "version": "5.7.0", + "resolved": "https://registry.npmjs.org/@ethersproject/properties/-/properties-5.7.0.tgz", + "integrity": "sha512-J87jy8suntrAkIZtecpxEPxY//szqr1mlBaYlQ0r4RCaiD2hjheqF9s1LVE8vVuJCXisjIP+JgtK/Do54ej4Sw==", + "requires": { + "@ethersproject/logger": "^5.7.0" + } + }, + "@ethersproject/providers": { + "version": "5.7.2", + "resolved": "https://registry.npmjs.org/@ethersproject/providers/-/providers-5.7.2.tgz", + "integrity": "sha512-g34EWZ1WWAVgr4aptGlVBF8mhl3VWjv+8hoAnzStu8Ah22VHBsuGzP17eb6xDVRzw895G4W7vvx60lFFur/1Rg==", + "requires": { + "@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" + } + }, + "@ethersproject/random": { + "version": "5.7.0", + "resolved": "https://registry.npmjs.org/@ethersproject/random/-/random-5.7.0.tgz", + "integrity": "sha512-19WjScqRA8IIeWclFme75VMXSBvi4e6InrUNuaR4s5pTF2qNhcGdCUwdxUVGtDDqC00sDLCO93jPQoDUH4HVmQ==", + "requires": { + "@ethersproject/bytes": "^5.7.0", + "@ethersproject/logger": "^5.7.0" + } + }, + "@ethersproject/rlp": { + "version": "5.7.0", + "resolved": "https://registry.npmjs.org/@ethersproject/rlp/-/rlp-5.7.0.tgz", + "integrity": "sha512-rBxzX2vK8mVF7b0Tol44t5Tb8gomOHkj5guL+HhzQ1yBh/ydjGnpw6at+X6Iw0Kp3OzzzkcKp8N9r0W4kYSs9w==", + "requires": { + "@ethersproject/bytes": "^5.7.0", + "@ethersproject/logger": "^5.7.0" + } + }, + "@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==", + "requires": { + "@ethersproject/bytes": "^5.7.0", + "@ethersproject/logger": "^5.7.0", + "hash.js": "1.1.7" + } + }, + "@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==", + "requires": { + "@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" + } + }, + "@ethersproject/solidity": { + "version": "5.7.0", + "resolved": "https://registry.npmjs.org/@ethersproject/solidity/-/solidity-5.7.0.tgz", + "integrity": "sha512-HmabMd2Dt/raavyaGukF4XxizWKhKQ24DoLtdNbBmNKUOPqwjsKQSdV9GQtj9CBEea9DlzETlVER1gYeXXBGaA==", + "requires": { + "@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" + } + }, + "@ethersproject/strings": { + "version": "5.7.0", + "resolved": "https://registry.npmjs.org/@ethersproject/strings/-/strings-5.7.0.tgz", + "integrity": "sha512-/9nu+lj0YswRNSH0NXYqrh8775XNyEdUQAuf3f+SmOrnVewcJ5SBNAjF7lpgehKi4abvNNXyf+HX86czCdJ8Mg==", + "requires": { + "@ethersproject/bytes": "^5.7.0", + "@ethersproject/constants": "^5.7.0", + "@ethersproject/logger": "^5.7.0" + } + }, + "@ethersproject/transactions": { + "version": "5.7.0", + "resolved": "https://registry.npmjs.org/@ethersproject/transactions/-/transactions-5.7.0.tgz", + "integrity": "sha512-kmcNicCp1lp8qanMTC3RIikGgoJ80ztTyvtsFvCYpSCfkjhD0jZ2LOrnbcuxuToLIUYYf+4XwD1rP+B/erDIhQ==", + "requires": { + "@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" + } + }, + "@ethersproject/units": { + "version": "5.7.0", + "resolved": "https://registry.npmjs.org/@ethersproject/units/-/units-5.7.0.tgz", + "integrity": "sha512-pD3xLMy3SJu9kG5xDGI7+xhTEmGXlEqXU4OfNapmfnxLVY4EMSSRp7j1k7eezutBPH7RBN/7QPnwR7hzNlEFeg==", + "requires": { + "@ethersproject/bignumber": "^5.7.0", + "@ethersproject/constants": "^5.7.0", + "@ethersproject/logger": "^5.7.0" + } + }, + "@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==", + "requires": { + "@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" + } + }, + "@ethersproject/web": { + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/@ethersproject/web/-/web-5.7.1.tgz", + "integrity": "sha512-Gueu8lSvyjBWL4cYsWsjh6MtMwM0+H4HvqFPZfB6dV8ctbP9zFAO73VG1cMWae0FLPCtz0peKPpZY8/ugJJX2w==", + "requires": { + "@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" + } + }, + "@ethersproject/wordlists": { + "version": "5.7.0", + "resolved": "https://registry.npmjs.org/@ethersproject/wordlists/-/wordlists-5.7.0.tgz", + "integrity": "sha512-S2TFNJNfHWVHNE6cNDjbVlZ6MgE17MIxMbMg2zv3wn+3XSJGosL1m9ZVv3GXCf/2ymSsQ+hRI5IzoMJTG6aoVA==", + "requires": { + "@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" + } + }, + "@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==" + }, + "@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==" + }, + "@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==" + }, + "@jridgewell/trace-mapping": { + "version": "0.3.9", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.9.tgz", + "integrity": "sha512-3Belt6tdc8bPgAtbcmdtNJlirVoTmEb5e2gC94PnkwEW9jI6CAHUeoG85tjWP5WquqfavoMtMwiG4P926ZKKuQ==", + "requires": { + "@jridgewell/resolve-uri": "^3.0.3", + "@jridgewell/sourcemap-codec": "^1.4.10" + } + }, + "@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==", + "requires": { + "ethereumjs-abi": "^0.6.8", + "ethereumjs-util": "^6.2.1", + "ethjs-util": "^0.1.6", + "tweetnacl": "^1.0.3", + "tweetnacl-util": "^0.15.1" + } + }, + "@noble/curves": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@noble/curves/-/curves-1.1.0.tgz", + "integrity": "sha512-091oBExgENk/kGj3AZmtBDMpxQPDtxQABR2B9lb1JbVTs6ytdzZNwvhxQ4MWasRNEzlbEH8jCWFCwhF/Obj5AA==", + "requires": { + "@noble/hashes": "1.3.1" + }, + "dependencies": { + "@noble/hashes": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/@noble/hashes/-/hashes-1.3.1.tgz", + "integrity": "sha512-EbqwksQwz9xDRGfDST86whPBgM65E0OH/pCgqW0GBVzO22bNE+NuIbeTb714+IfSjU3aRk47EUvXIb5bTsenKA==" + } + } + }, + "@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==" + }, + "@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==" + }, + "@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==", + "requires": { + "@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" + }, + "dependencies": { + "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==", + "requires": { + "@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" + } + } + } + }, + "@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==", + "requires": { + "@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" + }, + "dependencies": { + "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==", + "requires": { + "@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" + } + } + } + }, + "@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==", + "requires": { + "@nomicfoundation/ethereumjs-util": "9.0.2", + "crc-32": "^1.2.0" + } + }, + "@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==", + "requires": { + "@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" + }, + "dependencies": { + "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==", + "requires": { + "@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" + } + } + } + }, + "@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==", + "requires": { + "@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" + }, + "dependencies": { + "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==", + "requires": { + "@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" + } + } + } + }, + "@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==" + }, + "@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==", + "requires": { + "@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" + }, + "dependencies": { + "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==", + "requires": { + "@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" + } + } + } + }, + "@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==", + "requires": { + "@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" + }, + "dependencies": { + "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==", + "requires": { + "@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" + } + } + } + }, + "@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==", + "requires": { + "@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" + }, + "dependencies": { + "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==", + "requires": { + "@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" + } + } + } + }, + "@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==", + "requires": { + "@chainsafe/ssz": "^0.10.0", + "@nomicfoundation/ethereumjs-rlp": "5.0.2", + "ethereum-cryptography": "0.1.3" + }, + "dependencies": { + "@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==", + "requires": { + "@chainsafe/as-sha256": "^0.3.1" + } + }, + "@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==", + "requires": { + "@chainsafe/as-sha256": "^0.3.1", + "@chainsafe/persistent-merkle-tree": "^0.5.0" + } + }, + "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==", + "requires": { + "@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" + } + } + } + }, + "@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==", + "requires": { + "@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" + }, + "dependencies": { + "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==", + "requires": { + "@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" + } + } + } + }, + "@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==", + "requires": { + "@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" + } + }, + "@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==", + "optional": true + }, + "@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==", + "optional": true + }, + "@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==", + "optional": true + }, + "@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==", + "optional": true + }, + "@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==", + "optional": true + }, + "@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==", + "optional": true + }, + "@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==", + "optional": true + }, + "@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==", + "optional": true + }, + "@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==", + "optional": true + }, + "@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==", + "optional": true + }, + "@openzeppelin/contracts": { + "version": "4.9.5", + "resolved": "https://registry.npmjs.org/@openzeppelin/contracts/-/contracts-4.9.5.tgz", + "integrity": "sha512-ZK+W5mVhRppff9BE6YdR8CC52C8zAvsVAiWhEtQ5+oNxFE6h1WdeWo+FJSF8KKvtxxVYZ7MTP/5KoVpAU3aSWg==" + }, + "@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==" + }, + "@scure/bip32": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/@scure/bip32/-/bip32-1.1.5.tgz", + "integrity": "sha512-XyNh1rB0SkEqd3tXcXMi+Xe1fvg+kUIcoRIEujP1Jgv7DqW2r9lg3Ah0NkFaCs9sTkQAQA8kw7xiRXzENi9Rtw==", + "requires": { + "@noble/hashes": "~1.2.0", + "@noble/secp256k1": "~1.7.0", + "@scure/base": "~1.1.0" + } + }, + "@scure/bip39": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@scure/bip39/-/bip39-1.1.1.tgz", + "integrity": "sha512-t+wDck2rVkh65Hmv280fYdVdY25J9YeEUIgn2LG1WM6gxFkGzcksoDiUkWVpVp3Oex9xGC68JU2dSbUfwZ2jPg==", + "requires": { + "@noble/hashes": "~1.2.0", + "@scure/base": "~1.1.0" + } + }, + "@sentry/core": { + "version": "5.30.0", + "resolved": "https://registry.npmjs.org/@sentry/core/-/core-5.30.0.tgz", + "integrity": "sha512-TmfrII8w1PQZSZgPpUESqjB+jC6MvZJZdLtE/0hZ+SrnKhW3x5WlYLvTXZpcWePYBku7rl2wn1RZu6uT0qCTeg==", + "requires": { + "@sentry/hub": "5.30.0", + "@sentry/minimal": "5.30.0", + "@sentry/types": "5.30.0", + "@sentry/utils": "5.30.0", + "tslib": "^1.9.3" + } + }, + "@sentry/hub": { + "version": "5.30.0", + "resolved": "https://registry.npmjs.org/@sentry/hub/-/hub-5.30.0.tgz", + "integrity": "sha512-2tYrGnzb1gKz2EkMDQcfLrDTvmGcQPuWxLnJKXJvYTQDGLlEvi2tWz1VIHjunmOvJrB5aIQLhm+dcMRwFZDCqQ==", + "requires": { + "@sentry/types": "5.30.0", + "@sentry/utils": "5.30.0", + "tslib": "^1.9.3" + } + }, + "@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==", + "requires": { + "@sentry/hub": "5.30.0", + "@sentry/types": "5.30.0", + "tslib": "^1.9.3" + } + }, + "@sentry/node": { + "version": "5.30.0", + "resolved": "https://registry.npmjs.org/@sentry/node/-/node-5.30.0.tgz", + "integrity": "sha512-Br5oyVBF0fZo6ZS9bxbJZG4ApAjRqAnqFFurMVJJdunNb80brh7a5Qva2kjhm+U6r9NJAB5OmDyPkA1Qnt+QVg==", + "requires": { + "@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" + } + }, + "@sentry/tracing": { + "version": "5.30.0", + "resolved": "https://registry.npmjs.org/@sentry/tracing/-/tracing-5.30.0.tgz", + "integrity": "sha512-dUFowCr0AIMwiLD7Fs314Mdzcug+gBVo/+NCMyDw8tFxJkwWAKl7Qa2OZxLQ0ZHjakcj1hNKfCQJ9rhyfOl4Aw==", + "requires": { + "@sentry/hub": "5.30.0", + "@sentry/minimal": "5.30.0", + "@sentry/types": "5.30.0", + "@sentry/utils": "5.30.0", + "tslib": "^1.9.3" + } + }, + "@sentry/types": { + "version": "5.30.0", + "resolved": "https://registry.npmjs.org/@sentry/types/-/types-5.30.0.tgz", + "integrity": "sha512-R8xOqlSTZ+htqrfteCWU5Nk0CDN5ApUTvrlvBuiH1DyP6czDZ4ktbZB0hAgBlVcK0U+qpD3ag3Tqqpa5Q67rPw==" + }, + "@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==", + "requires": { + "@sentry/types": "5.30.0", + "tslib": "^1.9.3" + } + }, + "@tsconfig/node10": { + "version": "1.0.9", + "resolved": "https://registry.npmjs.org/@tsconfig/node10/-/node10-1.0.9.tgz", + "integrity": "sha512-jNsYVVxU8v5g43Erja32laIDHXeoNvFEpX33OK4d6hljo3jDhCBDhx5dhCCTMWUojscpAagGiRkBKxpdl9fxqA==" + }, + "@tsconfig/node12": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/@tsconfig/node12/-/node12-1.0.11.tgz", + "integrity": "sha512-cqefuRsh12pWyGsIoBKJA9luFu3mRxCA+ORZvA4ktLSzIuCUtWVxGIuXigEwO5/ywWFMZ2QEGKWvkZG1zDMTag==" + }, + "@tsconfig/node14": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/@tsconfig/node14/-/node14-1.0.3.tgz", + "integrity": "sha512-ysT8mhdixWK6Hw3i1V2AeRqZ5WfXg1G43mqoYlM2nc6388Fq5jcXyr5mRsqViLx/GJYdoL0bfXD8nmF+Zn/Iow==" + }, + "@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==" + }, + "@typechain/ethers-v5": { + "version": "10.2.1", + "resolved": "https://registry.npmjs.org/@typechain/ethers-v5/-/ethers-v5-10.2.1.tgz", + "integrity": "sha512-n3tQmCZjRE6IU4h6lqUGiQ1j866n5MTCBJreNEHHVWXa2u9GJTaeYyU1/k+1qLutkyw+sS6VAN+AbeiTqsxd/A==", + "requires": { + "lodash": "^4.17.15", + "ts-essentials": "^7.0.1" + } + }, + "@typechain/hardhat": { + "version": "6.1.6", + "resolved": "https://registry.npmjs.org/@typechain/hardhat/-/hardhat-6.1.6.tgz", + "integrity": "sha512-BiVnegSs+ZHVymyidtK472syodx1sXYlYJJixZfRstHVGYTi8V1O7QG4nsjyb0PC/LORcq7sfBUcHto1y6UgJA==", + "requires": { + "fs-extra": "^9.1.0" + }, + "dependencies": { + "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==", + "requires": { + "at-least-node": "^1.0.0", + "graceful-fs": "^4.2.0", + "jsonfile": "^6.0.1", + "universalify": "^2.0.0" + } + }, + "jsonfile": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.1.0.tgz", + "integrity": "sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ==", + "requires": { + "graceful-fs": "^4.1.6", + "universalify": "^2.0.0" + } + }, + "universalify": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.1.tgz", + "integrity": "sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==" + } + } + }, + "@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==", + "requires": { + "@types/node": "*" + } + }, + "@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==" + }, + "@types/node": { + "version": "20.10.4", + "resolved": "https://registry.npmjs.org/@types/node/-/node-20.10.4.tgz", + "integrity": "sha512-D08YG6rr8X90YB56tSIuBaddy/UXAA9RKJoFvrsnogAum/0pmjkgi4+2nx96A330FmioegBWmEYQ+syqCFaveg==", + "requires": { + "undici-types": "~5.26.4" + } + }, + "@types/pbkdf2": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/@types/pbkdf2/-/pbkdf2-3.1.2.tgz", + "integrity": "sha512-uRwJqmiXmh9++aSu1VNEn3iIxWOhd8AHXNSdlaLfdAAdSTY9jYVeGWnzejM3dvrkbqE3/hyQkQQ29IFATEGlew==", + "requires": { + "@types/node": "*" + } + }, + "@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==" + }, + "@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==", + "requires": { + "@types/node": "*", + "safe-buffer": "~5.1.1" + } + }, + "@types/secp256k1": { + "version": "4.0.6", + "resolved": "https://registry.npmjs.org/@types/secp256k1/-/secp256k1-4.0.6.tgz", + "integrity": "sha512-hHxJU6PAEUn0TP4S/ZOzuTUvJWuZ6eIKeNKb5RBpODvSl6hp1Wrw4s7ATY50rklRCScUDpHzVA/DQdSjJ3UoYQ==", + "requires": { + "@types/node": "*" + } + }, + "@types/ws": { + "version": "8.5.3", + "resolved": "https://registry.npmjs.org/@types/ws/-/ws-8.5.3.tgz", + "integrity": "sha512-6YOoWjruKj1uLf3INHH7D3qTXwFfEsg1kf3c0uDdSBJwfa/llkwIjrAGV7j7mVgGNbzTQ3HiHKKDXl6bJPD97w==", + "requires": { + "@types/node": "*" + } + }, + "abitype": { + "version": "0.7.1", + "resolved": "https://registry.npmjs.org/abitype/-/abitype-0.7.1.tgz", + "integrity": "sha512-VBkRHTDZf9Myaek/dO3yMmOzB/y2s3Zo6nVU7yaw1G+TvCHAjwaJzNGN9yo4K5D8bU/VZXKP1EJpRhFr862PlQ==", + "requires": {} + }, + "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==", + "requires": { + "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" + } + }, + "acorn": { + "version": "8.11.2", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.11.2.tgz", + "integrity": "sha512-nc0Axzp/0FILLEVsm4fNwLCwMttvhEI263QtVPQcbpfZZ3ts0hLsZGOpE6czNlid7CJ9MlyH8reXkpsf3YUY4w==" + }, + "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==" + }, + "adm-zip": { + "version": "0.4.16", + "resolved": "https://registry.npmjs.org/adm-zip/-/adm-zip-0.4.16.tgz", + "integrity": "sha512-TFi4HBKSGfIKsK5YCkKaaFG2m4PEDyViZmEwof3MTIgzimHLto6muaHVpbrljdIvIrFZzEq/p4nafOeLcYegrg==" + }, + "aes-js": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/aes-js/-/aes-js-3.0.0.tgz", + "integrity": "sha512-H7wUZRn8WpTq9jocdxQ2c8x2sKo9ZVmzfRE13GiNJXfp7NcKYEdvl3vspKjXox6RIG2VtaRe4JFvxG4rqp2Zuw==" + }, + "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==", + "requires": { + "debug": "4" + } + }, + "aggregate-error": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/aggregate-error/-/aggregate-error-3.1.0.tgz", + "integrity": "sha512-4I7Td01quW/RpocfNayFdFVk1qSuoh0E7JrbRJ16nH01HhKFQ88INq9Sd+nd72zqRySlr9BmDA8xlEJ6vJMrYA==", + "requires": { + "clean-stack": "^2.0.0", + "indent-string": "^4.0.0" + } + }, + "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==" + }, + "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==", + "requires": { + "type-fest": "^0.21.3" + } + }, + "ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==" + }, + "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==", + "requires": { + "color-convert": "^1.9.0" + } + }, + "anymatch": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz", + "integrity": "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==", + "requires": { + "normalize-path": "^3.0.0", + "picomatch": "^2.0.4" + } + }, + "arg": { + "version": "4.1.3", + "resolved": "https://registry.npmjs.org/arg/-/arg-4.1.3.tgz", + "integrity": "sha512-58S9QDqG0Xx27YwPSt9fJxivjYl432YCwfDMfZ+71RAqUrZef7LrKQZ3LHLOwCS4FLNBplP533Zx895SeOCHvA==" + }, + "argparse": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", + "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==" + }, + "array-back": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/array-back/-/array-back-3.1.0.tgz", + "integrity": "sha512-TkuxA4UCOvxuDK6NZYXCalszEzj+TLszyASooky+i742l9TqsOdYCMJJupxRic61hwquNtppB3hgcuq9SVSH1Q==" + }, + "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==" + }, + "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==" + }, + "balanced-match": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==" + }, + "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==", + "requires": { + "safe-buffer": "^5.0.1" + } + }, + "base64-js": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz", + "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==" + }, + "bech32": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/bech32/-/bech32-1.1.4.tgz", + "integrity": "sha512-s0IrSOzLlbvX7yp4WBfPITzpAU8sqQcpsmwXDiKwrG4r491vwCO/XpejasRNl0piBMe/DvP4Tz0mIS/X1DPJBQ==" + }, + "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==" + }, + "bignumber.js": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/bignumber.js/-/bignumber.js-4.1.0.tgz", + "integrity": "sha512-eJzYkFYy9L4JzXsbymsFn3p54D+llV27oTQ+ziJG7WFRheJcNZilgVXMG0LoZtlQSKBsJdWtLFqOD0u+U0jZKA==" + }, + "binary-extensions": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.2.0.tgz", + "integrity": "sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA==" + }, + "blakejs": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/blakejs/-/blakejs-1.2.1.tgz", + "integrity": "sha512-QXUSXI3QVc/gJME0dBpXrag1kbzOqCjCX8/b54ntNyW6sjtoqxqRk3LTmXzaJoh71zMsDCjM+47jS7XiwN/+fQ==" + }, + "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==" + }, + "brace-expansion": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "requires": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "braces": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz", + "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==", + "requires": { + "fill-range": "^7.0.1" + } + }, + "brorand": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/brorand/-/brorand-1.1.0.tgz", + "integrity": "sha512-cKV8tMCEpQs4hK/ik71d6LrPOnpkpGBR0wzxqr68g2m/LB2GxVYQroAjMJZRVM1Y4BCjCKc3vAamxSzOY2RP+w==" + }, + "browser-level": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/browser-level/-/browser-level-1.0.1.tgz", + "integrity": "sha512-XECYKJ+Dbzw0lbydyQuJzwNXtOpbMSq737qxJN11sIRTErOMShvDpbzTlgju7orJKvx4epULolZAuJGLzCmWRQ==", + "requires": { + "abstract-level": "^1.0.2", + "catering": "^2.1.1", + "module-error": "^1.0.2", + "run-parallel-limit": "^1.1.0" + } + }, + "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==" + }, + "browserify-aes": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/browserify-aes/-/browserify-aes-1.2.0.tgz", + "integrity": "sha512-+7CHXqGuspUn/Sl5aO7Ea0xWGAtETPXNSAjHo48JfLdPWcMng33Xe4znFvQweqc/uzk5zSOI3H52CYnjCfb5hA==", + "requires": { + "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" + } + }, + "bs58": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/bs58/-/bs58-4.0.1.tgz", + "integrity": "sha512-Ok3Wdf5vOIlBrgCvTq96gBkJw+JUEzdBgyaza5HLtPm7yTHkjRy8+JzNyHF7BHa0bNWOQIp3m5YF0nnFcOIKLw==", + "requires": { + "base-x": "^3.0.2" + } + }, + "bs58check": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/bs58check/-/bs58check-2.1.2.tgz", + "integrity": "sha512-0TS1jicxdU09dwJMNZtVAfzPi6Q6QeN0pM1Fkzrjn+XYHvzMKPU3pHVpva+769iNVSfIYWf7LJ6WR+BuuMf8cA==", + "requires": { + "bs58": "^4.0.0", + "create-hash": "^1.1.0", + "safe-buffer": "^5.1.2" + } + }, + "buffer": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/buffer/-/buffer-6.0.3.tgz", + "integrity": "sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA==", + "requires": { + "base64-js": "^1.3.1", + "ieee754": "^1.2.1" + } + }, + "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==" + }, + "buffer-xor": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/buffer-xor/-/buffer-xor-1.0.3.tgz", + "integrity": "sha512-571s0T7nZWK6vB67HI5dyUF7wXiNcfaPPPTl6zYCNApANjIvYJTg7hlud/+cJpdAhS7dVzqMLmfhfHR3rAcOjQ==" + }, + "bytes": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz", + "integrity": "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==" + }, + "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==", + "requires": { + "function-bind": "^1.1.2", + "get-intrinsic": "^1.2.1", + "set-function-length": "^1.1.1" + } + }, + "camelcase": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-6.3.0.tgz", + "integrity": "sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==" + }, + "case": { + "version": "1.6.3", + "resolved": "https://registry.npmjs.org/case/-/case-1.6.3.tgz", + "integrity": "sha512-mzDSXIPaFwVDvZAHqZ9VlbyF4yyXRuX6IvB06WvPYkqJVO24kX1PPhv9bfpKNFZyxYFmmgo03HUiD8iklmJYRQ==" + }, + "catering": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/catering/-/catering-2.1.1.tgz", + "integrity": "sha512-K7Qy8O9p76sL3/3m7/zLKbRkyOlSZAgzEaLhyj2mXS8PsCud2Eo4hAb8aLtZqHh0QGqLcb9dlJSu6lHRVENm1w==" + }, + "chalk": { + "version": "2.4.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", + "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", + "requires": { + "ansi-styles": "^3.2.1", + "escape-string-regexp": "^1.0.5", + "supports-color": "^5.3.0" + } + }, + "chokidar": { + "version": "3.5.3", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.5.3.tgz", + "integrity": "sha512-Dr3sfKRP6oTcjf2JmUmFJfeVMvXBdegxB0iVQ5eb2V10uFJUCAS8OByZdVAyVb8xXNz3GjjTgj9kLWsZTqE6kw==", + "requires": { + "anymatch": "~3.1.2", + "braces": "~3.0.2", + "fsevents": "~2.3.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" + } + }, + "ci-info": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-2.0.0.tgz", + "integrity": "sha512-5tK7EtrZ0N+OLFMthtqOj4fI2Jeb88C4CAZPu25LDVUgXJ0A3Js4PMGqrn0JU1W0Mh1/Z8wZzYPxqUrXeBboCQ==" + }, + "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==", + "requires": { + "inherits": "^2.0.1", + "safe-buffer": "^5.0.1" + } + }, + "classic-level": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/classic-level/-/classic-level-1.3.0.tgz", + "integrity": "sha512-iwFAJQYtqRTRM0F6L8h4JCt00ZSGdOyqh7yVrhhjrOpFhmBjNlRUey64MCiyo6UmQHMJ+No3c81nujPv+n9yrg==", + "requires": { + "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" + } + }, + "clean-stack": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/clean-stack/-/clean-stack-2.2.0.tgz", + "integrity": "sha512-4diC9HaTE+KRAMWhDhrGOECgWZxoevMc5TlkObMqNSsVU62PYzXZ/SMTjzyGAFF1YusgxGcSWTEXBhp0CPwQ1A==" + }, + "cliui": { + "version": "7.0.4", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-7.0.4.tgz", + "integrity": "sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ==", + "requires": { + "string-width": "^4.2.0", + "strip-ansi": "^6.0.0", + "wrap-ansi": "^7.0.0" + } + }, + "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==", + "requires": { + "color-name": "1.1.3" + } + }, + "color-name": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", + "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==" + }, + "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==" + }, + "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==", + "requires": { + "array-back": "^3.1.0", + "find-replace": "^3.0.0", + "lodash.camelcase": "^4.3.0", + "typical": "^4.0.0" + } + }, + "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==", + "requires": { + "array-back": "^4.0.2", + "chalk": "^2.4.2", + "table-layout": "^1.0.2", + "typical": "^5.2.0" + }, + "dependencies": { + "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==" + }, + "typical": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/typical/-/typical-5.2.0.tgz", + "integrity": "sha512-dvdQgNDNJo+8B2uBQoqdb11eUCE1JQXhvjC/CZtgvZseVd5TYMXnq0+vuUemXbd/Se29cTaUuPX3YIc2xgbvIg==" + } + } + }, + "commander": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/commander/-/commander-3.0.2.tgz", + "integrity": "sha512-Gar0ASD4BDyKC4hl4DwHqDrmvjoxWKZigVnAbn5H1owvm4CxCPdb0HQDehwNYMJpla5+M2tPmPARzhtYuwpHow==" + }, + "concat-map": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", + "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==" + }, + "cookie": { + "version": "0.4.2", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.4.2.tgz", + "integrity": "sha512-aSWTXFzaKWkvHO1Ny/s+ePFpvKsPnjc551iI41v3ny/ow6tBG5Vd+FuqGNhh1LxOmVzOlGUriIlOaokOvhaStA==" + }, + "crc-32": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/crc-32/-/crc-32-1.2.2.tgz", + "integrity": "sha512-ROmzCKrTnOwybPcJApAA6WBWij23HVfGVNKqqrZpuyZOHqK2CwHSvpGuyt/UNNvaIjEd8X5IFGp4Mh+Ie1IHJQ==" + }, + "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==", + "requires": { + "cipher-base": "^1.0.1", + "inherits": "^2.0.1", + "md5.js": "^1.3.4", + "ripemd160": "^2.0.1", + "sha.js": "^2.4.0" + } + }, + "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==", + "requires": { + "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" + } + }, + "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==" + }, + "cross-fetch": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/cross-fetch/-/cross-fetch-4.0.0.tgz", + "integrity": "sha512-e4a5N8lVvuLgAWgnCrLr2PP0YyDOTHa9H/Rj54dirp61qXnNq46m82bRhNqIA5VccJtWBvPTFRV3TtvHUKPB1g==", + "requires": { + "node-fetch": "^2.6.12" + } + }, + "debug": { + "version": "4.3.4", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", + "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", + "requires": { + "ms": "2.1.2" + } + }, + "decamelize": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-4.0.0.tgz", + "integrity": "sha512-9iE1PgSik9HeIIw2JO94IidnE3eBoQrFJ3w7sFuzSX4DpmZ3v5sZpUiV5Swcf6mQEF+Y0ru8Neo+p+nyh2J+hQ==" + }, + "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==" + }, + "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==", + "requires": { + "get-intrinsic": "^1.2.1", + "gopd": "^1.0.1", + "has-property-descriptors": "^1.0.0" + } + }, + "depd": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz", + "integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==" + }, + "diff": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/diff/-/diff-5.0.0.tgz", + "integrity": "sha512-/VTCrvm5Z0JGty/BWHljh+BAiw3IK+2j87NGMu8Nwc/f48WoDAC395uomO9ZD117ZOBaHmkX1oyLvkVM/aIT3w==" + }, + "dotenv": { + "version": "16.3.1", + "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.3.1.tgz", + "integrity": "sha512-IPzF4w4/Rd94bA9imS68tZBaYyBWSCE47V1RGuMrB94iyTOIEwRmVL2x/4An+6mETpLrKJ5hQkB8W4kFAadeIQ==" + }, + "elliptic": { + "version": "6.5.4", + "resolved": "https://registry.npmjs.org/elliptic/-/elliptic-6.5.4.tgz", + "integrity": "sha512-iLhC6ULemrljPZb+QutR5TQGB+pdW6KGD5RSegS+8sorOZT+rdQFbsQFJgvN3eRqNALqJer4oQ16YvJHlU8hzQ==", + "requires": { + "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" + }, + "dependencies": { + "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==" + } + } + }, + "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==" + }, + "enquirer": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/enquirer/-/enquirer-2.4.1.tgz", + "integrity": "sha512-rRqJg/6gd538VHvR3PSrdRBb/1Vy2YfzHqzvbhGIQpDRKIa4FgV/54b5Q1xYSxOOwKvjXweS26E0Q+nAMwp2pQ==", + "requires": { + "ansi-colors": "^4.1.1", + "strip-ansi": "^6.0.1" + } + }, + "env-paths": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/env-paths/-/env-paths-2.2.1.tgz", + "integrity": "sha512-+h1lkLKhZMTYjog1VEpJNG7NZJWcuc2DDk/qsqSTRRCOXiLjeQ1d1/udrUGhqMxUgAlwKNZ0cf2uqan5GLuS2A==" + }, + "escalade": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.1.tgz", + "integrity": "sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw==" + }, + "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==" + }, + "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==", + "requires": { + "@noble/hashes": "1.2.0", + "@noble/secp256k1": "1.7.1", + "@scure/bip32": "1.1.5", + "@scure/bip39": "1.1.1" + } + }, + "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==", + "requires": { + "bn.js": "^4.11.8", + "ethereumjs-util": "^6.0.0" + }, + "dependencies": { + "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==" + } + } + }, + "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==", + "requires": { + "@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" + }, + "dependencies": { + "@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==", + "requires": { + "@types/node": "*" + } + }, + "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==" + }, + "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==", + "requires": { + "@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" + } + } + } + }, + "ethers": { + "version": "5.7.2", + "resolved": "https://registry.npmjs.org/ethers/-/ethers-5.7.2.tgz", + "integrity": "sha512-wswUsmWo1aOK8rR7DIKiWSw9DbLWe6x98Jrn8wcTflTVvaXhAMaB5zGAXy0GYQEQp9iO1iSHWVyARQm11zUtyg==", + "requires": { + "@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" + } + }, + "ethjs-util": { + "version": "0.1.6", + "resolved": "https://registry.npmjs.org/ethjs-util/-/ethjs-util-0.1.6.tgz", + "integrity": "sha512-CUnVOQq7gSpDHZVVrQW8ExxUETWrnrvXYvYz55wOU8Uj4VCgw56XC2B/fVqQN+f7gmrnRHSLVnFAwsCuNwji8w==", + "requires": { + "is-hex-prefixed": "1.0.0", + "strip-hex-prefix": "1.0.0" + } + }, + "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==", + "requires": { + "md5.js": "^1.3.4", + "safe-buffer": "^5.1.1" + } + }, + "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==", + "requires": { + "to-regex-range": "^5.0.1" + } + }, + "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==", + "requires": { + "array-back": "^3.0.1" + } + }, + "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==", + "requires": { + "locate-path": "^2.0.0" + } + }, + "flat": { + "version": "5.0.2", + "resolved": "https://registry.npmjs.org/flat/-/flat-5.0.2.tgz", + "integrity": "sha512-b6suED+5/3rTpUBdG1gupIl8MPFCAMA0QXwmljLhvCUKcUvdE4gWky9zpuGCcXHOsz4J9wPGNWq6OKpmIzz3hQ==" + }, + "follow-redirects": { + "version": "1.15.3", + "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.3.tgz", + "integrity": "sha512-1VzOtuEM8pC9SFU1E+8KfTjZyMztRsgEfwQl44z8A25uy13jSzTj6dyK2Df52iV0vgHCfBwLhDWevLn95w5v6Q==" + }, + "for-each": { + "version": "0.3.3", + "resolved": "https://registry.npmjs.org/for-each/-/for-each-0.3.3.tgz", + "integrity": "sha512-jqYfLp7mo9vIyQf8ykW2v7A+2N4QjeCeI5+Dz9XraiO1ign81wjiH7Fb9vSOWvQfNtmSa4H2RoQTrrXivdUZmw==", + "requires": { + "is-callable": "^1.1.3" + } + }, + "fp-ts": { + "version": "1.19.3", + "resolved": "https://registry.npmjs.org/fp-ts/-/fp-ts-1.19.3.tgz", + "integrity": "sha512-H5KQDspykdHuztLTg+ajGN0Z2qUjcEf3Ybxc6hLt0k7/zPkn29XnKnxlBPyW2XIddWrGaJBzBl4VLYOtk39yZg==" + }, + "fs-extra": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-7.0.1.tgz", + "integrity": "sha512-YJDaCJZEnBmcbw13fvdAM9AwNOJwOzrE4pqMqBq5nFiEqXUqHwlK4B+3pUw6JNvfSPtX05xFHtYy/1ni01eGCw==", + "requires": { + "graceful-fs": "^4.1.2", + "jsonfile": "^4.0.0", + "universalify": "^0.1.0" + } + }, + "fs.realpath": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", + "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==" + }, + "fsevents": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", + "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", + "optional": true + }, + "function-bind": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", + "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==" + }, + "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==" + }, + "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==" + }, + "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==", + "requires": { + "function-bind": "^1.1.2", + "has-proto": "^1.0.1", + "has-symbols": "^1.0.3", + "hasown": "^2.0.0" + } + }, + "glob": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.0.tgz", + "integrity": "sha512-lmLf6gtyrPq8tTjSmrO94wBeQbFR3HbLHbuyD69wuyQkImp2hWqMGB47OX65FBkPffO641IP9jWa1z4ivqG26Q==", + "requires": { + "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" + } + }, + "glob-parent": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", + "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", + "requires": { + "is-glob": "^4.0.1" + } + }, + "gopd": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.0.1.tgz", + "integrity": "sha512-d65bNlIadxvpb/A2abVdlqKqV563juRnZ1Wtk6s1sIR8uNsXR70xqIzVqxVf1eTqDunwT2MkczEeaezCKTZhwA==", + "requires": { + "get-intrinsic": "^1.1.3" + } + }, + "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==" + }, + "hardhat": { + "version": "2.19.2", + "resolved": "https://registry.npmjs.org/hardhat/-/hardhat-2.19.2.tgz", + "integrity": "sha512-CRU3+0Cc8Qh9UpxKd8cLADDPes7ZDtKj4dTK+ERtLBomEzhRPLWklJn4VKOwjre9/k8GNd/e9DYxpfuzcxbXPQ==", + "requires": { + "@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" + } + }, + "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==" + }, + "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==", + "requires": { + "get-intrinsic": "^1.2.2" + } + }, + "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==" + }, + "has-symbols": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.3.tgz", + "integrity": "sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A==" + }, + "has-tostringtag": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.0.tgz", + "integrity": "sha512-kFjcSNhnlGV1kyoGk7OXKSawH5JOb/LzUc5w9B02hOTO0dfFRjbHQKvg1d6cf3HbeUmtU9VbbV3qzZ2Teh97WQ==", + "requires": { + "has-symbols": "^1.0.2" + } + }, + "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==", + "requires": { + "inherits": "^2.0.4", + "readable-stream": "^3.6.0", + "safe-buffer": "^5.2.0" + }, + "dependencies": { + "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==" + } + } + }, + "hash.js": { + "version": "1.1.7", + "resolved": "https://registry.npmjs.org/hash.js/-/hash.js-1.1.7.tgz", + "integrity": "sha512-taOaskGt4z4SOANNseOviYDvjEJinIkRgmp7LbKP2YTTmVxWBl87s/uzK9r+44BclBSp2X7K1hqeNfz9JbBeXA==", + "requires": { + "inherits": "^2.0.3", + "minimalistic-assert": "^1.0.1" + } + }, + "hasown": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.0.tgz", + "integrity": "sha512-vUptKVTpIJhcczKBbgnS+RtcuYMB8+oNzPK2/Hp3hanz8JmpATdmmgLgSaadVREkDm+e2giHwY3ZRkyjSIDDFA==", + "requires": { + "function-bind": "^1.1.2" + } + }, + "he": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/he/-/he-1.2.0.tgz", + "integrity": "sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==" + }, + "hmac-drbg": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/hmac-drbg/-/hmac-drbg-1.0.1.tgz", + "integrity": "sha512-Tti3gMqLdZfhOQY1Mzf/AanLiqh1WTiJgEj26ZuYQ9fbkLomzGchCws4FyrSd4VkpBfiNhaE1On+lOz894jvXg==", + "requires": { + "hash.js": "^1.0.3", + "minimalistic-assert": "^1.0.0", + "minimalistic-crypto-utils": "^1.0.1" + } + }, + "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==", + "requires": { + "depd": "2.0.0", + "inherits": "2.0.4", + "setprototypeof": "1.2.0", + "statuses": "2.0.1", + "toidentifier": "1.0.1" + } + }, + "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==", + "requires": { + "agent-base": "6", + "debug": "4" + } + }, + "iconv-lite": { + "version": "0.4.24", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", + "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", + "requires": { + "safer-buffer": ">= 2.1.2 < 3" + } + }, + "ieee754": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz", + "integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==" + }, + "immutable": { + "version": "4.3.4", + "resolved": "https://registry.npmjs.org/immutable/-/immutable-4.3.4.tgz", + "integrity": "sha512-fsXeu4J4i6WNWSikpI88v/PcVflZz+6kMhUfIwc5SY+poQRPnaf5V7qds6SUyUN3cVxEzuCab7QIoLOQ+DQ1wA==" + }, + "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==" + }, + "inflight": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", + "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==", + "requires": { + "once": "^1.3.0", + "wrappy": "1" + } + }, + "inherits": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==" + }, + "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==", + "requires": { + "fp-ts": "^1.0.0" + } + }, + "is-arguments": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/is-arguments/-/is-arguments-1.1.1.tgz", + "integrity": "sha512-8Q7EARjzEnKpt/PCD7e1cgUS0a6X8u5tdSiMqXhojOdoV9TsMsiO+9VLC5vAmO8N7/GmXn7yjR8qnA6bVAEzfA==", + "requires": { + "call-bind": "^1.0.2", + "has-tostringtag": "^1.0.0" + } + }, + "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==", + "requires": { + "binary-extensions": "^2.0.0" + } + }, + "is-buffer": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-2.0.5.tgz", + "integrity": "sha512-i2R6zNFDwgEHJyQUtJEk0XFi1i0dPFn/oqjK3/vPCcDeJvW5NQ83V8QbicfF1SupOaB0h8ntgBC2YiE7dfyctQ==" + }, + "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==" + }, + "is-extglob": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", + "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==" + }, + "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==" + }, + "is-generator-function": { + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/is-generator-function/-/is-generator-function-1.0.10.tgz", + "integrity": "sha512-jsEjy9l3yiXEQ+PsXdmBwEPcOxaXWLspKdplFUVI9vq1iZgIekeC0L167qeu86czQaxed3q/Uzuw0swL0irL8A==", + "requires": { + "has-tostringtag": "^1.0.0" + } + }, + "is-glob": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", + "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", + "requires": { + "is-extglob": "^2.1.1" + } + }, + "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==" + }, + "is-number": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", + "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==" + }, + "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==" + }, + "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==", + "requires": { + "which-typed-array": "^1.1.11" + } + }, + "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==" + }, + "isomorphic-ws": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/isomorphic-ws/-/isomorphic-ws-5.0.0.tgz", + "integrity": "sha512-muId7Zzn9ywDsyXgTIafTry2sV3nySZeUDe6YedVd1Hvuuep5AsIlqK+XefWpYTyJG5e503F2xIuT2lcU6rCSw==", + "requires": {} + }, + "js-sdsl": { + "version": "4.4.2", + "resolved": "https://registry.npmjs.org/js-sdsl/-/js-sdsl-4.4.2.tgz", + "integrity": "sha512-dwXFwByc/ajSV6m5bcKAPwe4yDDF6D614pxmIi5odytzxRlwqF6nwoiCek80Ixc7Cvma5awClxrzFtxCQvcM8w==" + }, + "js-sha3": { + "version": "0.8.0", + "resolved": "https://registry.npmjs.org/js-sha3/-/js-sha3-0.8.0.tgz", + "integrity": "sha512-gF1cRrHhIzNfToc802P800N8PpXS+evLLXfsVpowqmAFR9uwbi89WvXg2QspOmXL8QL86J4T1EpFu+yUkwJY3Q==" + }, + "js-yaml": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", + "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", + "requires": { + "argparse": "^2.0.1" + } + }, + "jsonfile": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-4.0.0.tgz", + "integrity": "sha512-m6F1R3z8jjlf2imQHS2Qez5sjKWQzbuuhuJ/FKYFRZvPE3PuHcSMVZzfsLhGVOkfd20obL5SWEBew5ShlquNxg==", + "requires": { + "graceful-fs": "^4.1.6" + } + }, + "keccak": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/keccak/-/keccak-3.0.4.tgz", + "integrity": "sha512-3vKuW0jV8J3XNTzvfyicFR5qvxrSAGl7KIhvgOu5cmWwM7tZRj3fMbj/pfIf4be7aznbc+prBWGjywox/g2Y6Q==", + "requires": { + "node-addon-api": "^2.0.0", + "node-gyp-build": "^4.2.0", + "readable-stream": "^3.6.0" + } + }, + "klaw": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/klaw/-/klaw-1.3.1.tgz", + "integrity": "sha512-TED5xi9gGQjGpNnvRWknrwAB1eL5GciPfVFOt3Vk1OJCVDQbzuSfrF3hkUQKlsgKrG1F+0t5W0m+Fje1jIt8rw==", + "requires": { + "graceful-fs": "^4.1.9" + } + }, + "level": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/level/-/level-8.0.0.tgz", + "integrity": "sha512-ypf0jjAk2BWI33yzEaaotpq7fkOPALKAgDBxggO6Q9HGX2MRXn0wbP1Jn/tJv1gtL867+YOjOB49WaUF3UoJNQ==", + "requires": { + "browser-level": "^1.0.1", + "classic-level": "^1.2.0" + } + }, + "level-supports": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/level-supports/-/level-supports-4.0.1.tgz", + "integrity": "sha512-PbXpve8rKeNcZ9C1mUicC9auIYFyGpkV9/i6g76tLgANwWhtG2v7I4xNBUlkn3lE2/dZF3Pi0ygYGtLc4RXXdA==" + }, + "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==", + "requires": { + "buffer": "^6.0.3", + "module-error": "^1.0.1" + } + }, + "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==", + "requires": { + "p-locate": "^2.0.0", + "path-exists": "^3.0.0" + } + }, + "lodash": { + "version": "4.17.21", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", + "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==" + }, + "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==" + }, + "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==", + "requires": { + "chalk": "^4.1.0", + "is-unicode-supported": "^0.1.0" + }, + "dependencies": { + "ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "requires": { + "color-convert": "^2.0.1" + } + }, + "chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "requires": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + } + }, + "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==", + "requires": { + "color-name": "~1.1.4" + } + }, + "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==" + }, + "has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==" + }, + "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==", + "requires": { + "has-flag": "^4.0.0" + } + } + } + }, + "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==" + }, + "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==", + "requires": { + "yallist": "^3.0.2" + } + }, + "make-error": { + "version": "1.3.6", + "resolved": "https://registry.npmjs.org/make-error/-/make-error-1.3.6.tgz", + "integrity": "sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==" + }, + "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==" + }, + "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==", + "requires": { + "hash-base": "^3.0.0", + "inherits": "^2.0.1", + "safe-buffer": "^5.1.2" + } + }, + "memory-level": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/memory-level/-/memory-level-1.0.0.tgz", + "integrity": "sha512-UXzwewuWeHBz5krr7EvehKcmLFNoXxGcvuYhC41tRnkrTbJohtS7kVn9akmgirtRygg+f7Yjsfi8Uu5SGSQ4Og==", + "requires": { + "abstract-level": "^1.0.0", + "functional-red-black-tree": "^1.0.1", + "module-error": "^1.0.1" + } + }, + "memorystream": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/memorystream/-/memorystream-0.3.1.tgz", + "integrity": "sha512-S3UwM3yj5mtUSEfP41UZmt/0SCoVYUcU1rkXv+BQ5Ig8ndL4sPoJNBUJERafdPb5jjHJGuMgytgKvKIf58XNBw==" + }, + "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==" + }, + "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==" + }, + "minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "requires": { + "brace-expansion": "^1.1.7" + } + }, + "mkdirp": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.4.tgz", + "integrity": "sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==" + }, + "mnemonist": { + "version": "0.38.5", + "resolved": "https://registry.npmjs.org/mnemonist/-/mnemonist-0.38.5.tgz", + "integrity": "sha512-bZTFT5rrPKtPJxj8KSV0WkPyNxl72vQepqqVUAW2ARUpUSF2qXMB6jZj7hW5/k7C1rtpzqbD/IIbJwLXUjCHeg==", + "requires": { + "obliterator": "^2.0.0" + } + }, + "mocha": { + "version": "10.2.0", + "resolved": "https://registry.npmjs.org/mocha/-/mocha-10.2.0.tgz", + "integrity": "sha512-IDY7fl/BecMwFHzoqF2sg/SHHANeBoMMXFlS9r0OXKDssYE1M5O43wUY/9BVPeIvfH2zmEbBfseqN9gBQZzXkg==", + "requires": { + "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" + }, + "dependencies": { + "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==" + }, + "brace-expansion": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", + "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", + "requires": { + "balanced-match": "^1.0.0" + } + }, + "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==" + }, + "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==", + "requires": { + "locate-path": "^6.0.0", + "path-exists": "^4.0.0" + } + }, + "has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==" + }, + "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==", + "requires": { + "p-locate": "^5.0.0" + } + }, + "minimatch": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.0.1.tgz", + "integrity": "sha512-nLDxIFRyhDblz3qMuq+SoRZED4+miJ/G+tdDrjkkkRnjAsBexeGpgjLEQ0blJy7rHhR2b93rhQY4SvyWu9v03g==", + "requires": { + "brace-expansion": "^2.0.1" + } + }, + "ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==" + }, + "p-limit": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", + "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", + "requires": { + "yocto-queue": "^0.1.0" + } + }, + "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==", + "requires": { + "p-limit": "^3.0.2" + } + }, + "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==" + }, + "supports-color": { + "version": "8.1.1", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", + "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", + "requires": { + "has-flag": "^4.0.0" + } + } + } + }, + "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==" + }, + "ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" + }, + "nanoid": { + "version": "3.3.3", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.3.tgz", + "integrity": "sha512-p1sjXuopFs0xg+fPASzQ28agW1oHD7xDsd9Xkf3T15H3c/cifrFHVwrh74PdoklAPi+i7MdRsE47vm2r6JoB+w==" + }, + "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-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-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==", + "requires": { + "whatwg-url": "^5.0.0" + } + }, + "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==" + }, + "normalize-path": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", + "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==" + }, + "obliterator": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/obliterator/-/obliterator-2.0.4.tgz", + "integrity": "sha512-lgHwxlxV1qIg1Eap7LgIeoBWIMFibOjbrYPIPJZcI1mmGAI2m3lNYpK12Y+GBdPQ0U1hRwSord7GIaawz962qQ==" + }, + "once": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", + "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", + "requires": { + "wrappy": "1" + } + }, + "os-tmpdir": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/os-tmpdir/-/os-tmpdir-1.0.2.tgz", + "integrity": "sha512-D2FR03Vir7FIu45XBY20mTb+/ZSWB00sjU9jdQXt83gDrI4Ztz5Fs7/yy74g2N5SVQY4xY1qDr4rNddwYRVX0g==" + }, + "p-limit": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-1.3.0.tgz", + "integrity": "sha512-vvcXsLAJ9Dr5rQOPk7toZQZJApBl2K4J6dANSsEuh6QI41JYcsS/qhTGa9ErIUUgK3WNQoJYvylxvjqmiqEA9Q==", + "requires": { + "p-try": "^1.0.0" + } + }, + "p-locate": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-2.0.0.tgz", + "integrity": "sha512-nQja7m7gSKuewoVRen45CtVfODR3crN3goVQ0DDZ9N3yHxgpkuBhZqsaiotSQRrADUrne346peY7kT3TSACykg==", + "requires": { + "p-limit": "^1.1.0" + } + }, + "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==", + "requires": { + "aggregate-error": "^3.0.0" + } + }, + "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==" + }, + "path-exists": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-3.0.0.tgz", + "integrity": "sha512-bpC7GYwiDYQ4wYLe+FA8lhRjhQCMcQGuSgGGqDkg/QerRWw9CmGRT0iSOVRSZJ29NMLZgIzqaljJ63oaL4NIJQ==" + }, + "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==" + }, + "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==" + }, + "pbkdf2": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/pbkdf2/-/pbkdf2-3.1.2.tgz", + "integrity": "sha512-iuh7L6jA7JEGu2WxDwtQP1ddOpaJNC4KlDEFfdQajSGgGPNi4OyDc2R7QnbY2bR9QjBVGwgvTdNJZoE7RaxUMA==", + "requires": { + "create-hash": "^1.1.2", + "create-hmac": "^1.1.4", + "ripemd160": "^2.0.1", + "safe-buffer": "^5.0.1", + "sha.js": "^2.4.8" + } + }, + "picomatch": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", + "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==" + }, + "prettier": { + "version": "2.8.8", + "resolved": "https://registry.npmjs.org/prettier/-/prettier-2.8.8.tgz", + "integrity": "sha512-tdN8qQGvNjw4CHbY+XXk0JgCXn9QiF21a55rBe5LJAU+kDyC4WQn4+awm2Xfk2lQMk5fKup9XgzTZtGkjBdP9Q==" + }, + "queue-microtask": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", + "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==" + }, + "randombytes": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz", + "integrity": "sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ==", + "requires": { + "safe-buffer": "^5.1.0" + } + }, + "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==", + "requires": { + "bytes": "3.1.2", + "http-errors": "2.0.0", + "iconv-lite": "0.4.24", + "unpipe": "1.0.0" + } + }, + "readable-stream": { + "version": "3.6.2", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz", + "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==", + "requires": { + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" + } + }, + "readdirp": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", + "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==", + "requires": { + "picomatch": "^2.2.1" + } + }, + "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==" + }, + "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==" + }, + "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==" + }, + "resolve": { + "version": "1.17.0", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.17.0.tgz", + "integrity": "sha512-ic+7JYiV8Vi2yzQGFWOkiZD5Z9z7O2Zhm9XMaTxdJExKasieFCr+yXZ/WmXsckHiKl12ar0y6XiXDx3m4RHn1w==", + "requires": { + "path-parse": "^1.0.6" + } + }, + "rimraf": { + "version": "2.7.1", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.7.1.tgz", + "integrity": "sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w==", + "requires": { + "glob": "^7.1.3" + } + }, + "ripemd160": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/ripemd160/-/ripemd160-2.0.2.tgz", + "integrity": "sha512-ii4iagi25WusVoiC4B4lq7pbXfAp3D9v5CwfkY33vffw2+pkDjY1D8GaN7spsxvCSx8dkPqOZCEZyfxcmJG2IA==", + "requires": { + "hash-base": "^3.0.0", + "inherits": "^2.0.1" + } + }, + "rlp": { + "version": "2.2.7", + "resolved": "https://registry.npmjs.org/rlp/-/rlp-2.2.7.tgz", + "integrity": "sha512-d5gdPmgQ0Z+AklL2NVXr/IoSjNZFfTVvQWzL/AM2AOcSzYP2xjlb0AC8YyCLc41MSNf6P6QVtjgPdmVtzb+4lQ==", + "requires": { + "bn.js": "^5.2.0" + } + }, + "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==", + "requires": { + "queue-microtask": "^1.2.2" + } + }, + "rustbn.js": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/rustbn.js/-/rustbn.js-0.2.0.tgz", + "integrity": "sha512-4VlvkRUuCJvr2J6Y0ImW7NvTCriMi7ErOAqWk1y69vAdoNIzCF3yPmgeNzx+RQTLEDFq5sHfscn1MwHxP9hNfA==" + }, + "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==" + }, + "safer-buffer": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", + "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==" + }, + "scrypt-js": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/scrypt-js/-/scrypt-js-3.0.1.tgz", + "integrity": "sha512-cdwTTnqPu0Hyvf5in5asVdZocVDTNRmR7XEcJuIzMjJeSHybHl7vpB66AzwTaIg6CLSbtjcxc8fqcySfnTkccA==" + }, + "secp256k1": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/secp256k1/-/secp256k1-4.0.3.tgz", + "integrity": "sha512-NLZVf+ROMxwtEj3Xa562qgv2BK5e2WNmXPiOdVIPLgs6lyTzMvBq0aWTYMI5XCP9jZMVKOcqZLw/Wc4vDkuxhA==", + "requires": { + "elliptic": "^6.5.4", + "node-addon-api": "^2.0.0", + "node-gyp-build": "^4.2.0" + } + }, + "semver": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==" + }, + "serialize-javascript": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-6.0.0.tgz", + "integrity": "sha512-Qr3TosvguFt8ePWqsvRfrKyQXIiW+nGbYpy8XK24NQHE83caxWt+mIymTT19DGFbNWNLfEwsrkSmN64lVWB9ag==", + "requires": { + "randombytes": "^2.1.0" + } + }, + "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==", + "requires": { + "define-data-property": "^1.1.1", + "get-intrinsic": "^1.2.1", + "gopd": "^1.0.1", + "has-property-descriptors": "^1.0.0" + } + }, + "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==" + }, + "setprototypeof": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz", + "integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==" + }, + "sha.js": { + "version": "2.4.11", + "resolved": "https://registry.npmjs.org/sha.js/-/sha.js-2.4.11.tgz", + "integrity": "sha512-QMEp5B7cftE7APOjk5Y6xgrbWu+WkLVQwk8JNjZ8nKRciZaByEW6MubieAiToS7+dwvrjGhH8jRXz3MVd0AYqQ==", + "requires": { + "inherits": "^2.0.1", + "safe-buffer": "^5.0.1" + } + }, + "solc": { + "version": "0.7.3", + "resolved": "https://registry.npmjs.org/solc/-/solc-0.7.3.tgz", + "integrity": "sha512-GAsWNAjGzIDg7VxzP6mPjdurby3IkGCjQcM8GFYZT6RyaoUZKmMU6Y7YwG+tFGhv7dwZ8rmR4iwFDrrD99JwqA==", + "requires": { + "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" + }, + "dependencies": { + "fs-extra": { + "version": "0.30.0", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-0.30.0.tgz", + "integrity": "sha512-UvSPKyhMn6LEd/WpUaV9C9t3zATuqoqfWc3QdPhPLb58prN9tqYPlPWi8Krxi44loBoUzlobqZ3+8tGpxxSzwA==", + "requires": { + "graceful-fs": "^4.1.2", + "jsonfile": "^2.1.0", + "klaw": "^1.0.0", + "path-is-absolute": "^1.0.0", + "rimraf": "^2.2.8" + } + }, + "jsonfile": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-2.4.0.tgz", + "integrity": "sha512-PKllAqbgLgxHaj8TElYymKCAgrASebJrWpTnEkOaTowt23VKXXN0sUeriJ+eh7y6ufb/CC5ap11pz71/cM0hUw==", + "requires": { + "graceful-fs": "^4.1.6" + } + }, + "semver": { + "version": "5.7.2", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.2.tgz", + "integrity": "sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g==" + } + } + }, + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==" + }, + "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==", + "requires": { + "buffer-from": "^1.0.0", + "source-map": "^0.6.0" + } + }, + "stacktrace-parser": { + "version": "0.1.10", + "resolved": "https://registry.npmjs.org/stacktrace-parser/-/stacktrace-parser-0.1.10.tgz", + "integrity": "sha512-KJP1OCML99+8fhOHxwwzyWrlUuVX5GQ0ZpJTd1DFXhdkrvg1szxfHhawXUZ3g9TkXORQd4/WG68jMlQZ2p8wlg==", + "requires": { + "type-fest": "^0.7.1" + }, + "dependencies": { + "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==" + } + } + }, + "statuses": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.1.tgz", + "integrity": "sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==" + }, + "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==", + "requires": { + "safe-buffer": "~5.2.0" + }, + "dependencies": { + "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==" + } + } + }, + "string-format": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/string-format/-/string-format-2.0.0.tgz", + "integrity": "sha512-bbEs3scLeYNXLecRRuk6uJxdXUSj6le/8rNPHChIJTn2V79aXVTR1EH2OH5zLKKoz0V02fOUKZZcw01pLUShZA==" + }, + "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==", + "requires": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + } + }, + "strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "requires": { + "ansi-regex": "^5.0.1" + } + }, + "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==", + "requires": { + "is-hex-prefixed": "1.0.0" + } + }, + "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==" + }, + "supports-color": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", + "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "requires": { + "has-flag": "^3.0.0" + } + }, + "table-layout": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/table-layout/-/table-layout-1.0.2.tgz", + "integrity": "sha512-qd/R7n5rQTRFi+Zf2sk5XVVd9UQl6ZkduPFC3S7WEGJAmetDTjY3qPN50eSKzwuzEyQKy5TN2TiZdkIjos2L6A==", + "requires": { + "array-back": "^4.0.1", + "deep-extend": "~0.6.0", + "typical": "^5.2.0", + "wordwrapjs": "^4.0.0" + }, + "dependencies": { + "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==" + }, + "typical": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/typical/-/typical-5.2.0.tgz", + "integrity": "sha512-dvdQgNDNJo+8B2uBQoqdb11eUCE1JQXhvjC/CZtgvZseVd5TYMXnq0+vuUemXbd/Se29cTaUuPX3YIc2xgbvIg==" + } + } + }, + "tmp": { + "version": "0.0.33", + "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.0.33.tgz", + "integrity": "sha512-jRCJlojKnZ3addtTOjdIqoRuPEKBvNXcGYqzO6zWZX8KfKEpnGY5jfggJQ3EjKuu8D4bJRr0y+cYJFmYbImXGw==", + "requires": { + "os-tmpdir": "~1.0.2" + } + }, + "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==", + "requires": { + "is-number": "^7.0.0" + } + }, + "toidentifier": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz", + "integrity": "sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==" + }, + "tr46": { + "version": "0.0.3", + "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz", + "integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==" + }, + "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==", + "requires": { + "chalk": "^4.1.0", + "command-line-args": "^5.1.1", + "command-line-usage": "^6.1.0", + "string-format": "^2.0.0" + }, + "dependencies": { + "ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "requires": { + "color-convert": "^2.0.1" + } + }, + "chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "requires": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + } + }, + "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==", + "requires": { + "color-name": "~1.1.4" + } + }, + "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==" + }, + "has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==" + }, + "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==", + "requires": { + "has-flag": "^4.0.0" + } + } + } + }, + "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==", + "requires": {} + }, + "ts-node": { + "version": "10.9.2", + "resolved": "https://registry.npmjs.org/ts-node/-/ts-node-10.9.2.tgz", + "integrity": "sha512-f0FFpIdcHgn8zcPSbf1dRevwt047YMnaiJM3u2w2RewrB+fob/zePZcrOyQoLMMO7aBIddLcQIEK5dYjkLnGrQ==", + "requires": { + "@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" + }, + "dependencies": { + "diff": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/diff/-/diff-4.0.2.tgz", + "integrity": "sha512-58lmxKSA4BNyLz+HHMUzlOEpg09FV+ev6ZMe3vJihgdxzgcwZ8VoEEPmALCZG9LmqfVoNMMKpttIYTVG6uDY7A==" + } + } + }, + "tslib": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", + "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==" + }, + "tsort": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/tsort/-/tsort-0.0.1.tgz", + "integrity": "sha512-Tyrf5mxF8Ofs1tNoxA13lFeZ2Zrbd6cKbuH3V+MQ5sb6DtBj5FjrXVsRWT8YvNAQTqNoz66dz1WsbigI22aEnw==" + }, + "tweetnacl": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/tweetnacl/-/tweetnacl-1.0.3.tgz", + "integrity": "sha512-6rt+RN7aOi1nGMyC4Xa5DdYiukl2UWCbcJft7YhxReBGQD7OAM8Pbxw6YMo4r2diNEA8FEmu32YOn9rhaiE5yw==" + }, + "tweetnacl-util": { + "version": "0.15.1", + "resolved": "https://registry.npmjs.org/tweetnacl-util/-/tweetnacl-util-0.15.1.tgz", + "integrity": "sha512-RKJBIj8lySrShN4w6i/BonWp2Z/uxwC3h4y7xsRrpP59ZboCd0GpEVsOnMDYLMmKBpYhb5TgHzZXy7wTfYFBRw==" + }, + "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==" + }, + "typechain": { + "version": "8.3.2", + "resolved": "https://registry.npmjs.org/typechain/-/typechain-8.3.2.tgz", + "integrity": "sha512-x/sQYr5w9K7yv3es7jo4KTX05CLxOf7TRWwoHlrjRh8H82G64g+k7VuWPJlgMo6qrjfCulOdfBjiaDtmhFYD/Q==", + "requires": { + "@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" + }, + "dependencies": { + "glob": { + "version": "7.1.7", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.7.tgz", + "integrity": "sha512-OvD9ENzPLbegENnYP5UUfJIirTg4+XwMWGaQfQTY0JenxNvvIKP3U3/tAQSPIu/lHxXYSZmpXlUHeqAIdKzBLQ==", + "requires": { + "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" + } + } + } + }, + "typescript": { + "version": "5.3.3", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.3.3.tgz", + "integrity": "sha512-pXWcraxM0uxAS+tN0AG/BF2TyqmHO014Z070UsJ+pFvYuRSq8KH8DmWpnbXe0pEPDHXZV3FcAbJkijJ5oNEnWw==", + "peer": true + }, + "typical": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/typical/-/typical-4.0.0.tgz", + "integrity": "sha512-VAH4IvQ7BDFYglMd7BPRDfLgxZZX4O4TFcRDA6EN5X7erNJJq+McIEp8np9aVtxrCJ6qx4GTYVfOWNjcqwZgRw==" + }, + "undici": { + "version": "5.28.2", + "resolved": "https://registry.npmjs.org/undici/-/undici-5.28.2.tgz", + "integrity": "sha512-wh1pHJHnUeQV5Xa8/kyQhO7WFa8M34l026L5P/+2TYiakvGy5Rdc8jWZVyG7ieht/0WgJLEd3kcU5gKx+6GC8w==", + "requires": { + "@fastify/busboy": "^2.0.0" + } + }, + "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==" + }, + "universalify": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/universalify/-/universalify-0.1.2.tgz", + "integrity": "sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg==" + }, + "unpipe": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", + "integrity": "sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==" + }, + "util": { + "version": "0.12.5", + "resolved": "https://registry.npmjs.org/util/-/util-0.12.5.tgz", + "integrity": "sha512-kZf/K6hEIrWHI6XqOFUiiMa+79wE/D8Q+NCNAWclkyg3b4d2k7s0QGepNjiABc+aR3N1PAyHL7p6UcLY6LmrnA==", + "requires": { + "inherits": "^2.0.3", + "is-arguments": "^1.0.4", + "is-generator-function": "^1.0.7", + "is-typed-array": "^1.1.3", + "which-typed-array": "^1.1.2" + } + }, + "util-deprecate": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", + "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==" + }, + "uuid": { + "version": "8.3.2", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz", + "integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==" + }, + "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==" + }, + "web3": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/web3/-/web3-4.3.0.tgz", + "integrity": "sha512-YiLCsb5wmgJlSxRLzt7Z7H+CmlVVIKD8VaUQaZ+xKVG3Q7CpsO5Z6jmeKnlr6M9c6fDDsDnRM6G8g+nchZehbA==", + "requires": { + "web3-core": "^4.3.2", + "web3-errors": "^1.1.4", + "web3-eth": "^4.3.1", + "web3-eth-abi": "^4.1.4", + "web3-eth-accounts": "^4.1.0", + "web3-eth-contract": "^4.1.4", + "web3-eth-ens": "^4.0.8", + "web3-eth-iban": "^4.0.7", + "web3-eth-personal": "^4.0.8", + "web3-net": "^4.0.7", + "web3-providers-http": "^4.1.0", + "web3-providers-ws": "^4.0.7", + "web3-rpc-methods": "^1.1.4", + "web3-types": "^1.3.1", + "web3-utils": "^4.1.0", + "web3-validator": "^2.0.3" + } + }, + "web3-core": { + "version": "4.3.2", + "resolved": "https://registry.npmjs.org/web3-core/-/web3-core-4.3.2.tgz", + "integrity": "sha512-uIMVd/j4BgOnwfpY8ZT+QKubOyM4xohEhFZXz9xB8wimXWMMlYVlIK/TbfHqFolS9uOerdSGhsMbcK9lETae8g==", + "requires": { + "web3-errors": "^1.1.4", + "web3-eth-accounts": "^4.1.0", + "web3-eth-iban": "^4.0.7", + "web3-providers-http": "^4.1.0", + "web3-providers-ipc": "^4.0.7", + "web3-providers-ws": "^4.0.7", + "web3-types": "^1.3.1", + "web3-utils": "^4.1.0", + "web3-validator": "^2.0.3" + } + }, + "web3-errors": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/web3-errors/-/web3-errors-1.1.4.tgz", + "integrity": "sha512-WahtszSqILez+83AxGecVroyZsMuuRT+KmQp4Si5P4Rnqbczno1k748PCrZTS1J4UCPmXMG2/Vt+0Bz2zwXkwQ==", + "requires": { + "web3-types": "^1.3.1" + } + }, + "web3-eth": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/web3-eth/-/web3-eth-4.3.1.tgz", + "integrity": "sha512-zJir3GOXooHQT85JB8SrufE+Voo5TtXdjhf1D8IGXmxM8MrhI8AT+Pgt4siBTupJcu5hF17iGmTP/Nj2XnaibQ==", + "requires": { + "setimmediate": "^1.0.5", + "web3-core": "^4.3.0", + "web3-errors": "^1.1.3", + "web3-eth-abi": "^4.1.4", + "web3-eth-accounts": "^4.1.0", + "web3-net": "^4.0.7", + "web3-providers-ws": "^4.0.7", + "web3-rpc-methods": "^1.1.3", + "web3-types": "^1.3.0", + "web3-utils": "^4.0.7", + "web3-validator": "^2.0.3" + } + }, + "web3-eth-abi": { + "version": "4.1.4", + "resolved": "https://registry.npmjs.org/web3-eth-abi/-/web3-eth-abi-4.1.4.tgz", + "integrity": "sha512-YLOBVVxxxLYKXjaiwZjEWYEnkMmmrm0nswZsvzSsINy/UgbWbzfoiZU+zn4YNWIEhORhx1p37iS3u/dP6VyC2w==", + "requires": { + "abitype": "0.7.1", + "web3-errors": "^1.1.3", + "web3-types": "^1.3.0", + "web3-utils": "^4.0.7", + "web3-validator": "^2.0.3" + } + }, + "web3-eth-accounts": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/web3-eth-accounts/-/web3-eth-accounts-4.1.0.tgz", + "integrity": "sha512-UFtAsOANsvihTQ6SSvOKguupmQkResyR9M9JNuOxYpKh7+3W+sTnbLXw2UbOSYIsKlc1mpqqW9bVr1SjqHDpUQ==", + "requires": { + "@ethereumjs/rlp": "^4.0.1", + "crc-32": "^1.2.2", + "ethereum-cryptography": "^2.0.0", + "web3-errors": "^1.1.3", + "web3-types": "^1.3.0", + "web3-utils": "^4.0.7", + "web3-validator": "^2.0.3" + }, + "dependencies": { + "@noble/hashes": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/@noble/hashes/-/hashes-1.3.1.tgz", + "integrity": "sha512-EbqwksQwz9xDRGfDST86whPBgM65E0OH/pCgqW0GBVzO22bNE+NuIbeTb714+IfSjU3aRk47EUvXIb5bTsenKA==" + }, + "@scure/bip32": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/@scure/bip32/-/bip32-1.3.1.tgz", + "integrity": "sha512-osvveYtyzdEVbt3OfwwXFr4P2iVBL5u1Q3q4ONBfDY/UpOuXmOlbgwc1xECEboY8wIays8Yt6onaWMUdUbfl0A==", + "requires": { + "@noble/curves": "~1.1.0", + "@noble/hashes": "~1.3.1", + "@scure/base": "~1.1.0" + } + }, + "@scure/bip39": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/@scure/bip39/-/bip39-1.2.1.tgz", + "integrity": "sha512-Z3/Fsz1yr904dduJD0NpiyRHhRYHdcnyh73FZWiV+/qhWi83wNJ3NWolYqCEN+ZWsUz2TWwajJggcRE9r1zUYg==", + "requires": { + "@noble/hashes": "~1.3.0", + "@scure/base": "~1.1.0" + } + }, + "ethereum-cryptography": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ethereum-cryptography/-/ethereum-cryptography-2.1.2.tgz", + "integrity": "sha512-Z5Ba0T0ImZ8fqXrJbpHcbpAvIswRte2wGNR/KePnu8GbbvgJ47lMxT/ZZPG6i9Jaht4azPDop4HaM00J0J59ug==", + "requires": { + "@noble/curves": "1.1.0", + "@noble/hashes": "1.3.1", + "@scure/bip32": "1.3.1", + "@scure/bip39": "1.2.1" + } + } + } + }, + "web3-eth-contract": { + "version": "4.1.4", + "resolved": "https://registry.npmjs.org/web3-eth-contract/-/web3-eth-contract-4.1.4.tgz", + "integrity": "sha512-tJ4z6QLgtu8EQu2sXnLA7g427oxmngnbAUh+9kJKbP6Yep/oe+z79PqJv7H3MwqwUNW9T+/FeB2PnSQSyxz6ig==", + "requires": { + "web3-core": "^4.3.2", + "web3-errors": "^1.1.4", + "web3-eth": "^4.3.1", + "web3-eth-abi": "^4.1.4", + "web3-types": "^1.3.1", + "web3-utils": "^4.1.0", + "web3-validator": "^2.0.3" + } + }, + "web3-eth-ens": { + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/web3-eth-ens/-/web3-eth-ens-4.0.8.tgz", + "integrity": "sha512-nj0JfeD45BbzVJcVYpUJnSo8iwDcY9CQ7CZhhIVVOFjvpMAPw0zEwjTvZEIQyCW61OoDG9xcBzwxe2tZoYhMRw==", + "requires": { + "@adraffy/ens-normalize": "^1.8.8", + "web3-core": "^4.3.0", + "web3-errors": "^1.1.3", + "web3-eth": "^4.3.1", + "web3-eth-contract": "^4.1.2", + "web3-net": "^4.0.7", + "web3-types": "^1.3.0", + "web3-utils": "^4.0.7", + "web3-validator": "^2.0.3" + } + }, + "web3-eth-iban": { + "version": "4.0.7", + "resolved": "https://registry.npmjs.org/web3-eth-iban/-/web3-eth-iban-4.0.7.tgz", + "integrity": "sha512-8weKLa9KuKRzibC87vNLdkinpUE30gn0IGY027F8doeJdcPUfsa4IlBgNC4k4HLBembBB2CTU0Kr/HAOqMeYVQ==", + "requires": { + "web3-errors": "^1.1.3", + "web3-types": "^1.3.0", + "web3-utils": "^4.0.7", + "web3-validator": "^2.0.3" + } + }, + "web3-eth-personal": { + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/web3-eth-personal/-/web3-eth-personal-4.0.8.tgz", + "integrity": "sha512-sXeyLKJ7ddQdMxz1BZkAwImjqh7OmKxhXoBNF3isDmD4QDpMIwv/t237S3q4Z0sZQamPa/pHebJRWVuvP8jZdw==", + "requires": { + "web3-core": "^4.3.0", + "web3-eth": "^4.3.1", + "web3-rpc-methods": "^1.1.3", + "web3-types": "^1.3.0", + "web3-utils": "^4.0.7", + "web3-validator": "^2.0.3" + } + }, + "web3-net": { + "version": "4.0.7", + "resolved": "https://registry.npmjs.org/web3-net/-/web3-net-4.0.7.tgz", + "integrity": "sha512-SzEaXFrBjY25iQGk5myaOfO9ZyfTwQEa4l4Ps4HDNVMibgZji3WPzpjq8zomVHMwi8bRp6VV7YS71eEsX7zLow==", + "requires": { + "web3-core": "^4.3.0", + "web3-rpc-methods": "^1.1.3", + "web3-types": "^1.3.0", + "web3-utils": "^4.0.7" + } + }, + "web3-providers-http": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/web3-providers-http/-/web3-providers-http-4.1.0.tgz", + "integrity": "sha512-6qRUGAhJfVQM41E5t+re5IHYmb5hSaLc02BE2MaRQsz2xKA6RjmHpOA5h/+ojJxEpI9NI2CrfDKOAgtJfoUJQg==", + "requires": { + "cross-fetch": "^4.0.0", + "web3-errors": "^1.1.3", + "web3-types": "^1.3.0", + "web3-utils": "^4.0.7" + } + }, + "web3-providers-ipc": { + "version": "4.0.7", + "resolved": "https://registry.npmjs.org/web3-providers-ipc/-/web3-providers-ipc-4.0.7.tgz", + "integrity": "sha512-YbNqY4zUvIaK2MHr1lQFE53/8t/ejHtJchrWn9zVbFMGXlTsOAbNoIoZWROrg1v+hCBvT2c9z8xt7e/+uz5p1g==", + "optional": true, + "requires": { + "web3-errors": "^1.1.3", + "web3-types": "^1.3.0", + "web3-utils": "^4.0.7" + } + }, + "web3-providers-ws": { + "version": "4.0.7", + "resolved": "https://registry.npmjs.org/web3-providers-ws/-/web3-providers-ws-4.0.7.tgz", + "integrity": "sha512-n4Dal9/rQWjS7d6LjyEPM2R458V8blRm0eLJupDEJOOIBhGYlxw5/4FthZZ/cqB7y/sLVi7K09DdYx2MeRtU5w==", + "requires": { + "@types/ws": "8.5.3", + "isomorphic-ws": "^5.0.0", + "web3-errors": "^1.1.3", + "web3-types": "^1.3.0", + "web3-utils": "^4.0.7", + "ws": "^8.8.1" + }, + "dependencies": { + "ws": { + "version": "8.15.1", + "resolved": "https://registry.npmjs.org/ws/-/ws-8.15.1.tgz", + "integrity": "sha512-W5OZiCjXEmk0yZ66ZN82beM5Sz7l7coYxpRkzS+p9PP+ToQry8szKh+61eNktr7EA9DOwvFGhfC605jDHbP6QQ==", + "requires": {} + } + } + }, + "web3-rpc-methods": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/web3-rpc-methods/-/web3-rpc-methods-1.1.4.tgz", + "integrity": "sha512-LTFNg4LFaeU8K9ecuT8fHDp/LOXyxCneeZjCrRYIW1u82Ly52SrY55FIzMIISGoG/iT5Wh7UiHOB3CQsWLBmbQ==", + "requires": { + "web3-core": "^4.3.2", + "web3-types": "^1.3.1", + "web3-validator": "^2.0.3" + } + }, + "web3-types": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/web3-types/-/web3-types-1.3.1.tgz", + "integrity": "sha512-8fXi7h/t95VKRtgU4sxprLPZpsTh3jYDfSghshIDBgUD/OoGe5S+syP24SUzBZYllZ/L+hMr2gdp/0bGJa8pYQ==" + }, + "web3-utils": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/web3-utils/-/web3-utils-4.1.0.tgz", + "integrity": "sha512-+VJWR6FtCsgwuJr5tvSvQlSEG06586df8h2CxGc9tcNtIDyJKNkSDDWJkdNPvyDhhXFzQYFh8QOGymD1CIP6fw==", + "requires": { + "ethereum-cryptography": "^2.0.0", + "web3-errors": "^1.1.4", + "web3-types": "^1.3.1", + "web3-validator": "^2.0.3" + }, + "dependencies": { + "@noble/hashes": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/@noble/hashes/-/hashes-1.3.1.tgz", + "integrity": "sha512-EbqwksQwz9xDRGfDST86whPBgM65E0OH/pCgqW0GBVzO22bNE+NuIbeTb714+IfSjU3aRk47EUvXIb5bTsenKA==" + }, + "@scure/bip32": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/@scure/bip32/-/bip32-1.3.1.tgz", + "integrity": "sha512-osvveYtyzdEVbt3OfwwXFr4P2iVBL5u1Q3q4ONBfDY/UpOuXmOlbgwc1xECEboY8wIays8Yt6onaWMUdUbfl0A==", + "requires": { + "@noble/curves": "~1.1.0", + "@noble/hashes": "~1.3.1", + "@scure/base": "~1.1.0" + } + }, + "@scure/bip39": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/@scure/bip39/-/bip39-1.2.1.tgz", + "integrity": "sha512-Z3/Fsz1yr904dduJD0NpiyRHhRYHdcnyh73FZWiV+/qhWi83wNJ3NWolYqCEN+ZWsUz2TWwajJggcRE9r1zUYg==", + "requires": { + "@noble/hashes": "~1.3.0", + "@scure/base": "~1.1.0" + } + }, + "ethereum-cryptography": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ethereum-cryptography/-/ethereum-cryptography-2.1.2.tgz", + "integrity": "sha512-Z5Ba0T0ImZ8fqXrJbpHcbpAvIswRte2wGNR/KePnu8GbbvgJ47lMxT/ZZPG6i9Jaht4azPDop4HaM00J0J59ug==", + "requires": { + "@noble/curves": "1.1.0", + "@noble/hashes": "1.3.1", + "@scure/bip32": "1.3.1", + "@scure/bip39": "1.2.1" + } + } + } + }, + "web3-validator": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/web3-validator/-/web3-validator-2.0.3.tgz", + "integrity": "sha512-fJbAQh+9LSNWy+l5Ze6HABreml8fra98o5+vS073T35jUcLbRZ0IOjF/ZPJhJNbJDt+jP1vseZsc3z3uX9mxxQ==", + "requires": { + "ethereum-cryptography": "^2.0.0", + "util": "^0.12.5", + "web3-errors": "^1.1.3", + "web3-types": "^1.3.0", + "zod": "^3.21.4" + }, + "dependencies": { + "@noble/hashes": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/@noble/hashes/-/hashes-1.3.1.tgz", + "integrity": "sha512-EbqwksQwz9xDRGfDST86whPBgM65E0OH/pCgqW0GBVzO22bNE+NuIbeTb714+IfSjU3aRk47EUvXIb5bTsenKA==" + }, + "@scure/bip32": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/@scure/bip32/-/bip32-1.3.1.tgz", + "integrity": "sha512-osvveYtyzdEVbt3OfwwXFr4P2iVBL5u1Q3q4ONBfDY/UpOuXmOlbgwc1xECEboY8wIays8Yt6onaWMUdUbfl0A==", + "requires": { + "@noble/curves": "~1.1.0", + "@noble/hashes": "~1.3.1", + "@scure/base": "~1.1.0" + } + }, + "@scure/bip39": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/@scure/bip39/-/bip39-1.2.1.tgz", + "integrity": "sha512-Z3/Fsz1yr904dduJD0NpiyRHhRYHdcnyh73FZWiV+/qhWi83wNJ3NWolYqCEN+ZWsUz2TWwajJggcRE9r1zUYg==", + "requires": { + "@noble/hashes": "~1.3.0", + "@scure/base": "~1.1.0" + } + }, + "ethereum-cryptography": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ethereum-cryptography/-/ethereum-cryptography-2.1.2.tgz", + "integrity": "sha512-Z5Ba0T0ImZ8fqXrJbpHcbpAvIswRte2wGNR/KePnu8GbbvgJ47lMxT/ZZPG6i9Jaht4azPDop4HaM00J0J59ug==", + "requires": { + "@noble/curves": "1.1.0", + "@noble/hashes": "1.3.1", + "@scure/bip32": "1.3.1", + "@scure/bip39": "1.2.1" + } + } + } + }, + "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==" + }, + "whatwg-url": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz", + "integrity": "sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==", + "requires": { + "tr46": "~0.0.3", + "webidl-conversions": "^3.0.0" + } + }, + "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==", + "requires": { + "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" + } + }, + "wordwrapjs": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/wordwrapjs/-/wordwrapjs-4.0.1.tgz", + "integrity": "sha512-kKlNACbvHrkpIw6oPeYDSmdCTu2hdMHoyXLTcUKala++lx5Y+wjJ/e474Jqv5abnVmwxw08DiTuHmw69lJGksA==", + "requires": { + "reduce-flatten": "^2.0.0", + "typical": "^5.2.0" + }, + "dependencies": { + "typical": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/typical/-/typical-5.2.0.tgz", + "integrity": "sha512-dvdQgNDNJo+8B2uBQoqdb11eUCE1JQXhvjC/CZtgvZseVd5TYMXnq0+vuUemXbd/Se29cTaUuPX3YIc2xgbvIg==" + } + } + }, + "workerpool": { + "version": "6.2.1", + "resolved": "https://registry.npmjs.org/workerpool/-/workerpool-6.2.1.tgz", + "integrity": "sha512-ILEIE97kDZvF9Wb9f6h5aXK4swSlKGUcOEGiIYb2OOu/IrDU9iwj0fD//SsA6E5ibwJxpEvhullJY4Sl4GcpAw==" + }, + "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==", + "requires": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, + "dependencies": { + "ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "requires": { + "color-convert": "^2.0.1" + } + }, + "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==", + "requires": { + "color-name": "~1.1.4" + } + }, + "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==" + } + } + }, + "wrappy": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", + "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==" + }, + "ws": { + "version": "7.4.6", + "resolved": "https://registry.npmjs.org/ws/-/ws-7.4.6.tgz", + "integrity": "sha512-YmhHDO4MzaDLB+M9ym/mDA5z0naX8j7SIlT8f8z+I0VtzsRbekxEutHSme7NPS2qE8StCYQNUnfWdXta/Yu85A==", + "requires": {} + }, + "y18n": { + "version": "5.0.8", + "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", + "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==" + }, + "yallist": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz", + "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==" + }, + "yargs": { + "version": "16.2.0", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-16.2.0.tgz", + "integrity": "sha512-D1mvvtDG0L5ft/jGWkLpG1+m0eQxOfaBvTNELraWj22wSVUMWxZUvYgJYcKh6jGGIkJFhH4IZPQhR4TKpc8mBw==", + "requires": { + "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" + } + }, + "yargs-parser": { + "version": "20.2.4", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-20.2.4.tgz", + "integrity": "sha512-WOkpgNhPTlE73h4VFAFsOnomJVaovO8VqLDzy5saChRBFQFBoMYirowyW+Q9HB4HFF4Z7VZTiG3iSzJJA29yRA==" + }, + "yargs-unparser": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/yargs-unparser/-/yargs-unparser-2.0.0.tgz", + "integrity": "sha512-7pRTIA9Qc1caZ0bZ6RYRGbHJthJWuakf+WmHK0rVeLkNrrGhfoabBNdue6kdINI6r4if7ocq9aD/n7xwKOdzOA==", + "requires": { + "camelcase": "^6.0.0", + "decamelize": "^4.0.0", + "flat": "^5.0.2", + "is-plain-obj": "^2.1.0" + } + }, + "yn": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/yn/-/yn-3.1.1.tgz", + "integrity": "sha512-Ux4ygGWsu2c7isFWe8Yu1YluJmqVhxqK2cLXNQA5AcC3QfbGNpM7fu0Y8b/z16pXLnFxZYvWhd3fhBY9DLmC6Q==" + }, + "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==" + }, + "zod": { + "version": "3.22.4", + "resolved": "https://registry.npmjs.org/zod/-/zod-3.22.4.tgz", + "integrity": "sha512-iC+8Io04lddc+mVqQ9AZ7OQ2MrUKGN+oIQyq1vemgt46jwCwLfhq7/pwnBnNXXXZb8VTVLKwp9EDkx+ryxIWmg==" + } + } +} diff --git a/package.json b/package.json new file mode 100644 index 0000000..50c6697 --- /dev/null +++ b/package.json @@ -0,0 +1,19 @@ +{ + "name": "dsa-connectors-2.0", + "version": "1.0.0", + "description": "DSA Connectors 2.0", + "directories": {}, + "devDependencies": { + "hardhat": "^2.19.2", + "typechain": "^8.3.2" + }, + "dependencies": { + "@openzeppelin/contracts": "^4.9.5", + "@typechain/ethers-v5": "^10.2.1", + "@typechain/hardhat": "^6.1.6", + "bignumber.js": "^4.0.4", + "dotenv": "^16.3.1", + "ts-node": "^10.9.2", + "web3": "^4.3.0" + } +} diff --git a/scripts/constant/abi/basics/erc20.json b/scripts/constant/abi/basics/erc20.json new file mode 100644 index 0000000..405d6b3 --- /dev/null +++ b/scripts/constant/abi/basics/erc20.json @@ -0,0 +1,222 @@ +[ + { + "constant": true, + "inputs": [], + "name": "name", + "outputs": [ + { + "name": "", + "type": "string" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "name": "_spender", + "type": "address" + }, + { + "name": "_value", + "type": "uint256" + } + ], + "name": "approve", + "outputs": [ + { + "name": "", + "type": "bool" + } + ], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": true, + "inputs": [], + "name": "totalSupply", + "outputs": [ + { + "name": "", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "name": "_from", + "type": "address" + }, + { + "name": "_to", + "type": "address" + }, + { + "name": "_value", + "type": "uint256" + } + ], + "name": "transferFrom", + "outputs": [ + { + "name": "", + "type": "bool" + } + ], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": true, + "inputs": [], + "name": "decimals", + "outputs": [ + { + "name": "", + "type": "uint8" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [ + { + "name": "_owner", + "type": "address" + } + ], + "name": "balanceOf", + "outputs": [ + { + "name": "balance", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [], + "name": "symbol", + "outputs": [ + { + "name": "", + "type": "string" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "name": "_to", + "type": "address" + }, + { + "name": "_value", + "type": "uint256" + } + ], + "name": "transfer", + "outputs": [ + { + "name": "", + "type": "bool" + } + ], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": true, + "inputs": [ + { + "name": "_owner", + "type": "address" + }, + { + "name": "_spender", + "type": "address" + } + ], + "name": "allowance", + "outputs": [ + { + "name": "", + "type": "uint256" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "payable": true, + "stateMutability": "payable", + "type": "fallback" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "name": "owner", + "type": "address" + }, + { + "indexed": true, + "name": "spender", + "type": "address" + }, + { + "indexed": false, + "name": "value", + "type": "uint256" + } + ], + "name": "Approval", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "name": "from", + "type": "address" + }, + { + "indexed": true, + "name": "to", + "type": "address" + }, + { + "indexed": false, + "name": "value", + "type": "uint256" + } + ], + "name": "Transfer", + "type": "event" + } +] diff --git a/scripts/constant/abi/connectors/auth.json b/scripts/constant/abi/connectors/auth.json new file mode 100644 index 0000000..7647b9b --- /dev/null +++ b/scripts/constant/abi/connectors/auth.json @@ -0,0 +1,97 @@ +[ + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "_msgSender", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "_authority", + "type": "address" + } + ], + "name": "LogAddAuth", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "_msgSender", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "_authority", + "type": "address" + } + ], + "name": "LogRemoveAuth", + "type": "event" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "authority", + "type": "address" + } + ], + "name": "add", + "outputs": [], + "stateMutability": "payable", + "type": "function" + }, + { + "inputs": [], + "name": "connectorID", + "outputs": [ + { + "internalType": "uint256", + "name": "_type", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "_id", + "type": "uint256" + } + ], + "stateMutability": "pure", + "type": "function" + }, + { + "inputs": [], + "name": "name", + "outputs": [ + { + "internalType": "string", + "name": "", + "type": "string" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "authority", + "type": "address" + } + ], + "name": "remove", + "outputs": [], + "stateMutability": "payable", + "type": "function" + } +] diff --git a/scripts/constant/abi/connectors/basic.json b/scripts/constant/abi/connectors/basic.json new file mode 100644 index 0000000..693e80d --- /dev/null +++ b/scripts/constant/abi/connectors/basic.json @@ -0,0 +1,133 @@ +[ + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "erc20", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "tokenAmt", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "getId", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "setId", + "type": "uint256" + } + ], + "name": "LogDeposit", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "erc20", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "tokenAmt", + "type": "uint256" + }, + { + "indexed": true, + "internalType": "address", + "name": "to", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "getId", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "setId", + "type": "uint256" + } + ], + "name": "LogWithdraw", + "type": "event" + }, + { + "inputs": [], + "name": "connectorID", + "outputs": [ + { "internalType": "uint256", "name": "_type", "type": "uint256" }, + { "internalType": "uint256", "name": "_id", "type": "uint256" } + ], + "stateMutability": "pure", + "type": "function" + }, + { + "inputs": [ + { "internalType": "address", "name": "erc20", "type": "address" }, + { "internalType": "uint256", "name": "tokenAmt", "type": "uint256" }, + { "internalType": "uint256", "name": "getId", "type": "uint256" }, + { "internalType": "uint256", "name": "setId", "type": "uint256" } + ], + "name": "deposit", + "outputs": [], + "stateMutability": "payable", + "type": "function" + }, + { + "inputs": [], + "name": "getEthAddr", + "outputs": [{ "internalType": "address", "name": "", "type": "address" }], + "stateMutability": "pure", + "type": "function" + }, + { + "inputs": [], + "name": "getEventAddr", + "outputs": [{ "internalType": "address", "name": "", "type": "address" }], + "stateMutability": "pure", + "type": "function" + }, + { + "inputs": [], + "name": "getMemoryAddr", + "outputs": [{ "internalType": "address", "name": "", "type": "address" }], + "stateMutability": "pure", + "type": "function" + }, + { + "inputs": [], + "name": "name", + "outputs": [{ "internalType": "string", "name": "", "type": "string" }], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { "internalType": "address", "name": "erc20", "type": "address" }, + { "internalType": "uint256", "name": "tokenAmt", "type": "uint256" }, + { "internalType": "address payable", "name": "to", "type": "address" }, + { "internalType": "uint256", "name": "getId", "type": "uint256" }, + { "internalType": "uint256", "name": "setId", "type": "uint256" } + ], + "name": "withdraw", + "outputs": [], + "stateMutability": "payable", + "type": "function" + } +] diff --git a/scripts/constant/abi/connectors/compound.json b/scripts/constant/abi/connectors/compound.json new file mode 100644 index 0000000..6b076e8 --- /dev/null +++ b/scripts/constant/abi/connectors/compound.json @@ -0,0 +1,445 @@ +[ + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "token", + "type": "address" + }, + { + "indexed": false, + "internalType": "address", + "name": "cToken", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "tokenAmt", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "getId", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "setId", + "type": "uint256" + } + ], + "name": "LogBorrow", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "token", + "type": "address" + }, + { + "indexed": false, + "internalType": "address", + "name": "cToken", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "tokenAmt", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "getId", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "setId", + "type": "uint256" + } + ], + "name": "LogDeposit", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "token", + "type": "address" + }, + { + "indexed": false, + "internalType": "address", + "name": "cToken", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "tokenAmt", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "cTokenAmt", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "getId", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "setId", + "type": "uint256" + } + ], + "name": "LogDepositCToken", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "borrower", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "tokenToPay", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "tokenInReturn", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "tokenAmt", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "getId", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "setId", + "type": "uint256" + } + ], + "name": "LogLiquidate", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "token", + "type": "address" + }, + { + "indexed": false, + "internalType": "address", + "name": "cToken", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "tokenAmt", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "getId", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "setId", + "type": "uint256" + } + ], + "name": "LogPayback", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "borrower", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "token", + "type": "address" + }, + { + "indexed": false, + "internalType": "address", + "name": "cToken", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "tokenAmt", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "getId", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "setId", + "type": "uint256" + } + ], + "name": "LogPaybackBehalf", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "token", + "type": "address" + }, + { + "indexed": false, + "internalType": "address", + "name": "cToken", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "tokenAmt", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "getId", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "setId", + "type": "uint256" + } + ], + "name": "LogWithdraw", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "token", + "type": "address" + }, + { + "indexed": false, + "internalType": "address", + "name": "cToken", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "cTokenAmt", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "getId", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "setId", + "type": "uint256" + } + ], + "name": "LogWithdrawCToken", + "type": "event" + }, + { + "inputs": [ + { "internalType": "address", "name": "token", "type": "address" }, + { "internalType": "uint256", "name": "amt", "type": "uint256" }, + { "internalType": "uint256", "name": "getId", "type": "uint256" }, + { "internalType": "uint256", "name": "setId", "type": "uint256" } + ], + "name": "borrow", + "outputs": [], + "stateMutability": "payable", + "type": "function" + }, + { + "inputs": [], + "name": "connectorID", + "outputs": [ + { "internalType": "uint256", "name": "_type", "type": "uint256" }, + { "internalType": "uint256", "name": "_id", "type": "uint256" } + ], + "stateMutability": "pure", + "type": "function" + }, + { + "inputs": [ + { "internalType": "address", "name": "token", "type": "address" }, + { "internalType": "uint256", "name": "amt", "type": "uint256" }, + { "internalType": "uint256", "name": "getId", "type": "uint256" }, + { "internalType": "uint256", "name": "setId", "type": "uint256" } + ], + "name": "deposit", + "outputs": [], + "stateMutability": "payable", + "type": "function" + }, + { + "inputs": [ + { "internalType": "address", "name": "token", "type": "address" }, + { "internalType": "uint256", "name": "amt", "type": "uint256" }, + { "internalType": "uint256", "name": "getId", "type": "uint256" }, + { "internalType": "uint256", "name": "setId", "type": "uint256" } + ], + "name": "depositCToken", + "outputs": [], + "stateMutability": "payable", + "type": "function" + }, + { + "inputs": [ + { "internalType": "address", "name": "borrower", "type": "address" }, + { "internalType": "address", "name": "tokenToPay", "type": "address" }, + { "internalType": "address", "name": "tokenInReturn", "type": "address" }, + { "internalType": "uint256", "name": "amt", "type": "uint256" }, + { "internalType": "uint256", "name": "getId", "type": "uint256" }, + { "internalType": "uint256", "name": "setId", "type": "uint256" } + ], + "name": "liquidate", + "outputs": [], + "stateMutability": "payable", + "type": "function" + }, + { + "inputs": [], + "name": "name", + "outputs": [{ "internalType": "string", "name": "", "type": "string" }], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { "internalType": "address", "name": "token", "type": "address" }, + { "internalType": "uint256", "name": "amt", "type": "uint256" }, + { "internalType": "uint256", "name": "getId", "type": "uint256" }, + { "internalType": "uint256", "name": "setId", "type": "uint256" } + ], + "name": "payback", + "outputs": [], + "stateMutability": "payable", + "type": "function" + }, + { + "inputs": [ + { "internalType": "address", "name": "borrower", "type": "address" }, + { "internalType": "address", "name": "token", "type": "address" }, + { "internalType": "uint256", "name": "amt", "type": "uint256" }, + { "internalType": "uint256", "name": "getId", "type": "uint256" }, + { "internalType": "uint256", "name": "setId", "type": "uint256" } + ], + "name": "paybackBehalf", + "outputs": [], + "stateMutability": "payable", + "type": "function" + }, + { + "inputs": [ + { "internalType": "address", "name": "token", "type": "address" }, + { "internalType": "uint256", "name": "amt", "type": "uint256" }, + { "internalType": "uint256", "name": "getId", "type": "uint256" }, + { "internalType": "uint256", "name": "setId", "type": "uint256" } + ], + "name": "withdraw", + "outputs": [], + "stateMutability": "payable", + "type": "function" + }, + { + "inputs": [ + { "internalType": "address", "name": "token", "type": "address" }, + { "internalType": "uint256", "name": "cTokenAmt", "type": "uint256" }, + { "internalType": "uint256", "name": "getId", "type": "uint256" }, + { "internalType": "uint256", "name": "setId", "type": "uint256" } + ], + "name": "withdrawCToken", + "outputs": [], + "stateMutability": "payable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "setId", + "type": "uint256" + } + ], + "name": "ClaimComp", + "outputs": [], + "stateMutability": "payable", + "type": "function" + } +] diff --git a/scripts/constant/abi/connectors/instapool-c.json b/scripts/constant/abi/connectors/instapool-c.json new file mode 100644 index 0000000..8102f82 --- /dev/null +++ b/scripts/constant/abi/connectors/instapool-c.json @@ -0,0 +1,114 @@ +[ + { + "anonymous": false, + "inputs": [ + { "indexed": false, "internalType": "address", "name": "token", "type": "address" }, + { "indexed": false, "internalType": "uint256", "name": "tokenAmt", "type": "uint256" } + ], + "name": "LogFlashBorrow", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { "indexed": false, "internalType": "address[]", "name": "token", "type": "address[]" }, + { "indexed": false, "internalType": "uint256[]", "name": "tokenAmts", "type": "uint256[]" } + ], + "name": "LogFlashMultiBorrow", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { "indexed": false, "internalType": "address[]", "name": "token", "type": "address[]" }, + { "indexed": false, "internalType": "uint256[]", "name": "tokenAmts", "type": "uint256[]" } + ], + "name": "LogFlashMultiPayback", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { "indexed": false, "internalType": "address", "name": "token", "type": "address" }, + { "indexed": false, "internalType": "uint256", "name": "tokenAmt", "type": "uint256" } + ], + "name": "LogFlashPayback", + "type": "event" + }, + { + "inputs": [ + { "internalType": "address", "name": "token", "type": "address" }, + { "internalType": "uint256", "name": "amt", "type": "uint256" }, + { "internalType": "uint256", "name": "route", "type": "uint256" }, + { "internalType": "bytes", "name": "data", "type": "bytes" }, + { "internalType": "bytes", "name": "extraData", "type": "bytes" } + ], + "name": "flashBorrowAndCast", + "outputs": [ + { "internalType": "string", "name": "_eventName", "type": "string" }, + { "internalType": "bytes", "name": "_eventParam", "type": "bytes" } + ], + "stateMutability": "payable", + "type": "function" + }, + { + "inputs": [ + { "internalType": "address[]", "name": "tokens_", "type": "address[]" }, + { "internalType": "uint256[]", "name": "amts_", "type": "uint256[]" }, + { "internalType": "uint256", "name": "route", "type": "uint256" }, + { "internalType": "bytes", "name": "data", "type": "bytes" }, + { "internalType": "bytes", "name": "extraData", "type": "bytes" } + ], + "name": "flashMultiBorrowAndCast", + "outputs": [ + { "internalType": "string", "name": "_eventName", "type": "string" }, + { "internalType": "bytes", "name": "_eventParam", "type": "bytes" } + ], + "stateMutability": "payable", + "type": "function" + }, + { + "inputs": [ + { "internalType": "address[]", "name": "tokens_", "type": "address[]" }, + { "internalType": "uint256[]", "name": "amts_", "type": "uint256[]" }, + { "internalType": "uint256[]", "name": "getIds", "type": "uint256[]" }, + { "internalType": "uint256[]", "name": "setIds", "type": "uint256[]" } + ], + "name": "flashMultiPayback", + "outputs": [ + { "internalType": "string", "name": "_eventName", "type": "string" }, + { "internalType": "bytes", "name": "_eventParam", "type": "bytes" } + ], + "stateMutability": "payable", + "type": "function" + }, + { + "inputs": [ + { "internalType": "address", "name": "token", "type": "address" }, + { "internalType": "uint256", "name": "amt", "type": "uint256" }, + { "internalType": "uint256", "name": "getId", "type": "uint256" }, + { "internalType": "uint256", "name": "setId", "type": "uint256" } + ], + "name": "flashPayback", + "outputs": [ + { "internalType": "string", "name": "_eventName", "type": "string" }, + { "internalType": "bytes", "name": "_eventParam", "type": "bytes" } + ], + "stateMutability": "payable", + "type": "function" + }, + { + "inputs": [], + "name": "instaPool", + "outputs": [{ "internalType": "contract InstaFlashV4Interface", "name": "", "type": "address" }], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "name", + "outputs": [{ "internalType": "string", "name": "", "type": "string" }], + "stateMutability": "view", + "type": "function" + } +] diff --git a/scripts/constant/abi/connectors/instapool.json b/scripts/constant/abi/connectors/instapool.json new file mode 100644 index 0000000..75a04f4 --- /dev/null +++ b/scripts/constant/abi/connectors/instapool.json @@ -0,0 +1 @@ +[{"type":"event","name":"LogFlashBorrow","inputs":[{"type":"address","name":"token","internalType":"address","indexed":false},{"type":"uint256","name":"tokenAmt","internalType":"uint256","indexed":false}],"anonymous":false},{"type":"event","name":"LogFlashMultiBorrow","inputs":[{"type":"address[]","name":"token","internalType":"address[]","indexed":false},{"type":"uint256[]","name":"tokenAmts","internalType":"uint256[]","indexed":false}],"anonymous":false},{"type":"event","name":"LogFlashMultiPayback","inputs":[{"type":"address[]","name":"token","internalType":"address[]","indexed":false},{"type":"uint256[]","name":"tokenAmts","internalType":"uint256[]","indexed":false}],"anonymous":false},{"type":"event","name":"LogFlashPayback","inputs":[{"type":"address","name":"token","internalType":"address","indexed":false},{"type":"uint256","name":"tokenAmt","internalType":"uint256","indexed":false}],"anonymous":false},{"type":"function","stateMutability":"payable","outputs":[{"type":"string","name":"_eventName","internalType":"string"},{"type":"bytes","name":"_eventParam","internalType":"bytes"}],"name":"flashBorrowAndCast","inputs":[{"type":"address","name":"token","internalType":"address"},{"type":"uint256","name":"amt","internalType":"uint256"},{"type":"uint256","name":"route","internalType":"uint256"},{"type":"bytes","name":"data","internalType":"bytes"}]},{"type":"function","stateMutability":"payable","outputs":[{"type":"string","name":"_eventName","internalType":"string"},{"type":"bytes","name":"_eventParam","internalType":"bytes"}],"name":"flashMultiBorrowAndCast","inputs":[{"type":"address[]","name":"tokens","internalType":"address[]"},{"type":"uint256[]","name":"amts","internalType":"uint256[]"},{"type":"uint256","name":"route","internalType":"uint256"},{"type":"bytes","name":"data","internalType":"bytes"}]},{"type":"function","stateMutability":"payable","outputs":[{"type":"string","name":"_eventName","internalType":"string"},{"type":"bytes","name":"_eventParam","internalType":"bytes"}],"name":"flashMultiPayback","inputs":[{"type":"address[]","name":"tokens","internalType":"address[]"},{"type":"uint256[]","name":"amts","internalType":"uint256[]"},{"type":"uint256[]","name":"getId","internalType":"uint256[]"},{"type":"uint256[]","name":"setId","internalType":"uint256[]"}]},{"type":"function","stateMutability":"payable","outputs":[{"type":"string","name":"_eventName","internalType":"string"},{"type":"bytes","name":"_eventParam","internalType":"bytes"}],"name":"flashPayback","inputs":[{"type":"address","name":"token","internalType":"address"},{"type":"uint256","name":"amt","internalType":"uint256"},{"type":"uint256","name":"getId","internalType":"uint256"},{"type":"uint256","name":"setId","internalType":"uint256"}]},{"type":"function","stateMutability":"view","outputs":[{"type":"address","name":"","internalType":"contract InstaFlashV2Interface"}],"name":"instaPool","inputs":[]},{"type":"function","stateMutability":"view","outputs":[{"type":"string","name":"","internalType":"string"}],"name":"name","inputs":[]}] \ No newline at end of file diff --git a/scripts/constant/abi/connectors/maker.json b/scripts/constant/abi/connectors/maker.json new file mode 100644 index 0000000..2d287ba --- /dev/null +++ b/scripts/constant/abi/connectors/maker.json @@ -0,0 +1,483 @@ +[ + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "uint256", + "name": "vault", + "type": "uint256" + }, + { + "indexed": true, + "internalType": "bytes32", + "name": "ilk", + "type": "bytes32" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "tokenAmt", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "getId", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "setId", + "type": "uint256" + } + ], + "name": "LogBorrow", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "uint256", + "name": "vault", + "type": "uint256" + }, + { + "indexed": true, + "internalType": "bytes32", + "name": "ilk", + "type": "bytes32" + } + ], + "name": "LogClose", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "uint256", + "name": "vault", + "type": "uint256" + }, + { + "indexed": true, + "internalType": "bytes32", + "name": "ilk", + "type": "bytes32" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "tokenAmt", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "getId", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "setId", + "type": "uint256" + } + ], + "name": "LogDeposit", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "uint256", + "name": "tokenAmt", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "getId", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "setId", + "type": "uint256" + } + ], + "name": "LogDepositDai", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "uint256", + "name": "vault", + "type": "uint256" + }, + { + "indexed": true, + "internalType": "bytes32", + "name": "ilk", + "type": "bytes32" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "tokenAmt", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "getId", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "setId", + "type": "uint256" + } + ], + "name": "LogExitDai", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "uint256", + "name": "vault", + "type": "uint256" + }, + { + "indexed": true, + "internalType": "bytes32", + "name": "ilk", + "type": "bytes32" + } + ], + "name": "LogOpen", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "uint256", + "name": "vault", + "type": "uint256" + }, + { + "indexed": true, + "internalType": "bytes32", + "name": "ilk", + "type": "bytes32" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "tokenAmt", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "getId", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "setId", + "type": "uint256" + } + ], + "name": "LogPayback", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "uint256", + "name": "vault", + "type": "uint256" + }, + { + "indexed": true, + "internalType": "bytes32", + "name": "ilk", + "type": "bytes32" + }, + { + "indexed": false, + "internalType": "address", + "name": "newOwner", + "type": "address" + } + ], + "name": "LogTransfer", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "uint256", + "name": "vault", + "type": "uint256" + }, + { + "indexed": true, + "internalType": "bytes32", + "name": "ilk", + "type": "bytes32" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "tokenAmt", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "getId", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "setId", + "type": "uint256" + } + ], + "name": "LogWithdraw", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "uint256", + "name": "tokenAmt", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "getId", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "setId", + "type": "uint256" + } + ], + "name": "LogWithdrawDai", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "uint256", + "name": "vault", + "type": "uint256" + }, + { + "indexed": true, + "internalType": "bytes32", + "name": "ilk", + "type": "bytes32" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "tokenAmt", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "getId", + "type": "uint256" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "setId", + "type": "uint256" + } + ], + "name": "LogWithdrawLiquidated", + "type": "event" + }, + { + "inputs": [ + { "internalType": "uint256", "name": "vault", "type": "uint256" }, + { "internalType": "uint256", "name": "amt", "type": "uint256" }, + { "internalType": "uint256", "name": "getId", "type": "uint256" }, + { "internalType": "uint256", "name": "setId", "type": "uint256" } + ], + "name": "borrow", + "outputs": [], + "stateMutability": "payable", + "type": "function" + }, + { + "inputs": [ + { "internalType": "uint256", "name": "vault", "type": "uint256" } + ], + "name": "close", + "outputs": [], + "stateMutability": "payable", + "type": "function" + }, + { + "inputs": [], + "name": "connectorID", + "outputs": [ + { "internalType": "uint256", "name": "_type", "type": "uint256" }, + { "internalType": "uint256", "name": "_id", "type": "uint256" } + ], + "stateMutability": "pure", + "type": "function" + }, + { + "inputs": [ + { "internalType": "uint256", "name": "vault", "type": "uint256" }, + { "internalType": "uint256", "name": "amt", "type": "uint256" }, + { "internalType": "uint256", "name": "getId", "type": "uint256" }, + { "internalType": "uint256", "name": "setId", "type": "uint256" } + ], + "name": "deposit", + "outputs": [], + "stateMutability": "payable", + "type": "function" + }, + { + "inputs": [ + { "internalType": "uint256", "name": "vault", "type": "uint256" }, + { "internalType": "uint256", "name": "depositAmt", "type": "uint256" }, + { "internalType": "uint256", "name": "borrowAmt", "type": "uint256" }, + { "internalType": "uint256", "name": "getIdDeposit", "type": "uint256" }, + { "internalType": "uint256", "name": "getIdBorrow", "type": "uint256" }, + { "internalType": "uint256", "name": "setIdDeposit", "type": "uint256" }, + { "internalType": "uint256", "name": "setIdBorrow", "type": "uint256" } + ], + "name": "depositAndBorrow", + "outputs": [], + "stateMutability": "payable", + "type": "function" + }, + { + "inputs": [ + { "internalType": "uint256", "name": "amt", "type": "uint256" }, + { "internalType": "uint256", "name": "getId", "type": "uint256" }, + { "internalType": "uint256", "name": "setId", "type": "uint256" } + ], + "name": "depositDai", + "outputs": [], + "stateMutability": "payable", + "type": "function" + }, + { + "inputs": [ + { "internalType": "uint256", "name": "vault", "type": "uint256" }, + { "internalType": "uint256", "name": "amt", "type": "uint256" }, + { "internalType": "uint256", "name": "getId", "type": "uint256" }, + { "internalType": "uint256", "name": "setId", "type": "uint256" } + ], + "name": "exitDai", + "outputs": [], + "stateMutability": "payable", + "type": "function" + }, + { + "inputs": [], + "name": "name", + "outputs": [{ "internalType": "string", "name": "", "type": "string" }], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { "internalType": "string", "name": "colType", "type": "string" } + ], + "name": "open", + "outputs": [ + { "internalType": "uint256", "name": "vault", "type": "uint256" } + ], + "stateMutability": "payable", + "type": "function" + }, + { + "inputs": [ + { "internalType": "uint256", "name": "vault", "type": "uint256" }, + { "internalType": "uint256", "name": "amt", "type": "uint256" }, + { "internalType": "uint256", "name": "getId", "type": "uint256" }, + { "internalType": "uint256", "name": "setId", "type": "uint256" } + ], + "name": "payback", + "outputs": [], + "stateMutability": "payable", + "type": "function" + }, + { + "inputs": [ + { "internalType": "uint256", "name": "vault", "type": "uint256" }, + { "internalType": "uint256", "name": "amt", "type": "uint256" }, + { "internalType": "uint256", "name": "getId", "type": "uint256" }, + { "internalType": "uint256", "name": "setId", "type": "uint256" } + ], + "name": "withdraw", + "outputs": [], + "stateMutability": "payable", + "type": "function" + }, + { + "inputs": [ + { "internalType": "uint256", "name": "amt", "type": "uint256" }, + { "internalType": "uint256", "name": "getId", "type": "uint256" }, + { "internalType": "uint256", "name": "setId", "type": "uint256" } + ], + "name": "withdrawDai", + "outputs": [], + "stateMutability": "payable", + "type": "function" + }, + { + "inputs": [ + { "internalType": "uint256", "name": "vault", "type": "uint256" }, + { "internalType": "uint256", "name": "amt", "type": "uint256" }, + { "internalType": "uint256", "name": "getId", "type": "uint256" }, + { "internalType": "uint256", "name": "setId", "type": "uint256" } + ], + "name": "withdrawLiquidated", + "outputs": [], + "stateMutability": "payable", + "type": "function" + } +] diff --git a/scripts/constant/abi/connectors/uniswap.json b/scripts/constant/abi/connectors/uniswap.json new file mode 100644 index 0000000..b5913a1 --- /dev/null +++ b/scripts/constant/abi/connectors/uniswap.json @@ -0,0 +1,177 @@ +[ + { + "inputs": [ + { + "internalType": "address", + "name": "buyAddr", + "type": "address" + }, + { + "internalType": "address", + "name": "sellAddr", + "type": "address" + }, + { + "internalType": "uint256", + "name": "buyAmt", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "unitAmt", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "getId", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "setId", + "type": "uint256" + } + ], + "name": "buy", + "outputs": [], + "stateMutability": "payable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "tokenA", + "type": "address" + }, + { + "internalType": "address", + "name": "tokenB", + "type": "address" + }, + { + "internalType": "uint256", + "name": "amtA", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "unitAmt", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "slippage", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "getId", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "setId", + "type": "uint256" + } + ], + "name": "deposit", + "outputs": [], + "stateMutability": "payable", + "type": "function" + }, + { + "inputs": [], + "name": "name", + "outputs": [ + { + "internalType": "string", + "name": "", + "type": "string" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "buyAddr", + "type": "address" + }, + { + "internalType": "address", + "name": "sellAddr", + "type": "address" + }, + { + "internalType": "uint256", + "name": "sellAmt", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "unitAmt", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "getId", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "setId", + "type": "uint256" + } + ], + "name": "sell", + "outputs": [], + "stateMutability": "payable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "tokenA", + "type": "address" + }, + { + "internalType": "address", + "name": "tokenB", + "type": "address" + }, + { + "internalType": "uint256", + "name": "uniAmt", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "unitAmtA", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "unitAmtB", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "getId", + "type": "uint256" + }, + { + "internalType": "uint256[]", + "name": "setIds", + "type": "uint256[]" + } + ], + "name": "withdraw", + "outputs": [], + "stateMutability": "payable", + "type": "function" + } +] diff --git a/scripts/constant/abi/core/InstaImplementations.json b/scripts/constant/abi/core/InstaImplementations.json new file mode 100644 index 0000000..c1bae96 --- /dev/null +++ b/scripts/constant/abi/core/InstaImplementations.json @@ -0,0 +1,206 @@ +{ + "_format": "hh-sol-artifact-1", + "contractName": "InstaImplementations", + "sourceName": "contracts/v2/registry/implementations.sol", + "abi": [ + { + "inputs": [ + { + "internalType": "address", + "name": "_instaIndex", + "type": "address" + } + ], + "stateMutability": "nonpayable", + "type": "constructor" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "implementation", + "type": "address" + }, + { + "indexed": false, + "internalType": "bytes4[]", + "name": "sigs", + "type": "bytes4[]" + } + ], + "name": "LogAddImplementation", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "implementation", + "type": "address" + }, + { + "indexed": false, + "internalType": "bytes4[]", + "name": "sigs", + "type": "bytes4[]" + } + ], + "name": "LogRemoveImplementation", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "oldImplementation", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "newImplementation", + "type": "address" + } + ], + "name": "LogSetDefaultImplementation", + "type": "event" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "_implementation", + "type": "address" + }, + { + "internalType": "bytes4[]", + "name": "_sigs", + "type": "bytes4[]" + } + ], + "name": "addImplementation", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "defaultImplementation", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes4", + "name": "_sig", + "type": "bytes4" + } + ], + "name": "getImplementation", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "_impl", + "type": "address" + } + ], + "name": "getImplementationSigs", + "outputs": [ + { + "internalType": "bytes4[]", + "name": "", + "type": "bytes4[]" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes4", + "name": "_sig", + "type": "bytes4" + } + ], + "name": "getSigImplementation", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "instaIndex", + "outputs": [ + { + "internalType": "contract IndexInterface", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "_implementation", + "type": "address" + } + ], + "name": "removeImplementation", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "_defaultImplementation", + "type": "address" + } + ], + "name": "setDefaultImplementation", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + } + ], + "bytecode": "0x60a060405234801561001057600080fd5b506040516116383803806116388339818101604052602081101561003357600080fd5b8101908080519060200190929190505050808073ffffffffffffffffffffffffffffffffffffffff1660808173ffffffffffffffffffffffffffffffffffffffff1660601b81525050505060805160601c61158e6100aa6000398061039f528061085b5280610c5d5280610dfb525061158e6000f3fe608060405234801561001057600080fd5b50600436106100885760003560e01c8063b39c45931161005b578063b39c4593146101e2578063dc9cc64514610216578063ef7e5c7b1461028d578063f0c01b421461030457610088565b806322175a321461008d5780637c16ffc4146100d157806389396dc814610115578063a41098bf146101ae575b600080fd5b6100cf600480360360208110156100a357600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff16906020019092919050505061039d565b005b610113600480360360208110156100e757600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff169060200190929190505050610859565b005b6101576004803603602081101561012b57600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff169060200190929190505050610b82565b6040518080602001828103825283818151815260200191508051906020019060200280838360005b8381101561019a57808201518184015260208101905061017f565b505050509050019250505060405180910390f35b6101b6610c5b565b604051808273ffffffffffffffffffffffffffffffffffffffff16815260200191505060405180910390f35b6101ea610c7f565b604051808273ffffffffffffffffffffffffffffffffffffffff16815260200191505060405180910390f35b6102616004803603602081101561022c57600080fd5b8101908080357bffffffffffffffffffffffffffffffffffffffffffffffffffffffff19169060200190929190505050610ca3565b604051808273ffffffffffffffffffffffffffffffffffffffff16815260200191505060405180910390f35b6102d8600480360360208110156102a357600080fd5b8101908080357bffffffffffffffffffffffffffffffffffffffffffffffffffffffff19169060200190929190505050610d7e565b604051808273ffffffffffffffffffffffffffffffffffffffff16815260200191505060405180910390f35b61039b6004803603604081101561031a57600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff1690602001909291908035906020019064010000000081111561035757600080fd5b82018360208201111561036957600080fd5b8035906020019184602083028401116401000000008311171561038b57600080fd5b9091929391929390505050610df9565b005b7f000000000000000000000000000000000000000000000000000000000000000073ffffffffffffffffffffffffffffffffffffffff1663ee97f7f36040518163ffffffff1660e01b815260040160206040518083038186803b15801561040357600080fd5b505afa158015610417573d6000803e3d6000fd5b505050506040513d602081101561042d57600080fd5b810190808051906020019092919050505073ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff16146104de576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252601b8152602001807f496d706c656d656e746174696f6e733a206e6f742d6d6173746572000000000081525060200191505060405180910390fd5b600073ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff161415610564576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252602b8152602001806114cd602b913960400191505060405180910390fd5b6000600260008373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020805490501415610600576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252602b8152602001806114f8602b913960400191505060405180910390fd5b6060600260008373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000208054806020026020016040519081016040528092919081815260200182805480156106cd57602002820191906000526020600020906000905b82829054906101000a900460e01b7bffffffffffffffffffffffffffffffffffffffffffffffffffffffff19168152602001906004019060208260030104928301926001038202915080841161067a5790505b5050505050905060005b815181101561077a5760008282815181106106ee57fe5b6020026020010151905060016000827bffffffffffffffffffffffffffffffffffffffffffffffffffffffff19167bffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916815260200190815260200160002060006101000a81549073ffffffffffffffffffffffffffffffffffffffff02191690555080806001019150506106d7565b50600260008373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060006107c69190611305565b8173ffffffffffffffffffffffffffffffffffffffff167fb7d759e6cdda23e8a1749bce345fc77355b8a22eeaf92c6e4e7257d959c162c7826040518080602001828103825283818151815260200191508051906020019060200280838360005b83811015610842578082015181840152602081019050610827565b505050509050019250505060405180910390a25050565b7f000000000000000000000000000000000000000000000000000000000000000073ffffffffffffffffffffffffffffffffffffffff1663ee97f7f36040518163ffffffff1660e01b815260040160206040518083038186803b1580156108bf57600080fd5b505afa1580156108d3573d6000803e3d6000fd5b505050506040513d60208110156108e957600080fd5b810190808051906020019092919050505073ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff161461099a576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252601b8152602001807f496d706c656d656e746174696f6e733a206e6f742d6d6173746572000000000081525060200191505060405180910390fd5b600073ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff161415610a20576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260398152602001806114656039913960400191505060405180910390fd5b60008054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff161415610ac5576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260368152602001806115236036913960400191505060405180910390fd5b8073ffffffffffffffffffffffffffffffffffffffff1660008054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff167f3b337225b8d68d037e0c721876335a3832bd08162c943fe5f88e8d428597ca8f60405160405180910390a3806000806101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff16021790555050565b6060600260008373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020805480602002602001604051908101604052809291908181526020018280548015610c4f57602002820191906000526020600020906000905b82829054906101000a900460e01b7bffffffffffffffffffffffffffffffffffffffffffffffffffffffff191681526020019060040190602082600301049283019260010382029150808411610bfc5790505b50505050509050919050565b7f000000000000000000000000000000000000000000000000000000000000000081565b60008054906101000a900473ffffffffffffffffffffffffffffffffffffffff1681565b60008060016000847bffffffffffffffffffffffffffffffffffffffffffffffffffffffff19167bffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916815260200190815260200160002060009054906101000a900473ffffffffffffffffffffffffffffffffffffffff169050600073ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff1614610d545780610d76565b60008054906101000a900473ffffffffffffffffffffffffffffffffffffffff165b915050919050565b600060016000837bffffffffffffffffffffffffffffffffffffffffffffffffffffffff19167bffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916815260200190815260200160002060009054906101000a900473ffffffffffffffffffffffffffffffffffffffff169050919050565b7f000000000000000000000000000000000000000000000000000000000000000073ffffffffffffffffffffffffffffffffffffffff1663ee97f7f36040518163ffffffff1660e01b815260040160206040518083038186803b158015610e5f57600080fd5b505afa158015610e73573d6000803e3d6000fd5b505050506040513d6020811015610e8957600080fd5b810190808051906020019092919050505073ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff1614610f3a576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252601b8152602001807f496d706c656d656e746174696f6e733a206e6f742d6d6173746572000000000081525060200191505060405180910390fd5b600073ffffffffffffffffffffffffffffffffffffffff168373ffffffffffffffffffffffffffffffffffffffff161415610fc0576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252602b8152602001806114cd602b913960400191505060405180910390fd5b6000600260008573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020805490501461105b576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252602f81526020018061149e602f913960400191505060405180910390fd5b60005b8282905081101561123357600083838381811061107757fe5b905060200201357bffffffffffffffffffffffffffffffffffffffffffffffffffffffff19169050600073ffffffffffffffffffffffffffffffffffffffff1660016000837bffffffffffffffffffffffffffffffffffffffffffffffffffffffff19167bffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916815260200190815260200160002060009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1614611195576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260238152602001806114426023913960400191505060405180910390fd5b8460016000837bffffffffffffffffffffffffffffffffffffffffffffffffffffffff19167bffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916815260200190815260200160002060006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff16021790555050808060010191505061105e565b508181600260008673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020919061128292919061132d565b508273ffffffffffffffffffffffffffffffffffffffff167ff9c512a86be00aaec236065d0a439f064133f367de889c782448c3578a3f30c5838360405180806020018281038252848482818152602001925060200280828437600081840152601f19601f820116905080830192505050935050505060405180910390a2505050565b50805460008255600701600890049060005260206000209081019061132a91906113f9565b50565b828054828255906000526020600020906007016008900481019282156113e85791602002820160005b838211156113b65783357bffffffffffffffffffffffffffffffffffffffffffffffffffffffff191683826101000a81548163ffffffff021916908360e01c02179055509260200192600401602081600301049283019260010302611356565b80156113e65782816101000a81549063ffffffff02191690556004016020816003010492830192600103026113b6565b505b5090506113f59190611416565b5090565b5b808211156114125760008160009055506001016113fa565b5090565b5b8082111561143d57600081816101000a81549063ffffffff021916905550600101611417565b509056fe496d706c656d656e746174696f6e733a205f73696720616c7265616479206164646564496d706c656d656e746174696f6e733a205f64656661756c74496d706c656d656e746174696f6e2061646472657373206e6f742076616c6964496d706c656d656e746174696f6e733a205f696d706c656d656e746174696f6e20616c72656164792061646465642e496d706c656d656e746174696f6e733a205f696d706c656d656e746174696f6e206e6f742076616c69642e496d706c656d656e746174696f6e733a205f696d706c656d656e746174696f6e206e6f7420666f756e642e496d706c656d656e746174696f6e733a205f64656661756c74496d706c656d656e746174696f6e2063616e6e6f742062652073616d65a2646970667358221220d42dcb568934728d15f318f76a6028fe848a484b097ea24581f45425fcde152864736f6c63430007000033", + "deployedBytecode": "0x608060405234801561001057600080fd5b50600436106100885760003560e01c8063b39c45931161005b578063b39c4593146101e2578063dc9cc64514610216578063ef7e5c7b1461028d578063f0c01b421461030457610088565b806322175a321461008d5780637c16ffc4146100d157806389396dc814610115578063a41098bf146101ae575b600080fd5b6100cf600480360360208110156100a357600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff16906020019092919050505061039d565b005b610113600480360360208110156100e757600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff169060200190929190505050610859565b005b6101576004803603602081101561012b57600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff169060200190929190505050610b82565b6040518080602001828103825283818151815260200191508051906020019060200280838360005b8381101561019a57808201518184015260208101905061017f565b505050509050019250505060405180910390f35b6101b6610c5b565b604051808273ffffffffffffffffffffffffffffffffffffffff16815260200191505060405180910390f35b6101ea610c7f565b604051808273ffffffffffffffffffffffffffffffffffffffff16815260200191505060405180910390f35b6102616004803603602081101561022c57600080fd5b8101908080357bffffffffffffffffffffffffffffffffffffffffffffffffffffffff19169060200190929190505050610ca3565b604051808273ffffffffffffffffffffffffffffffffffffffff16815260200191505060405180910390f35b6102d8600480360360208110156102a357600080fd5b8101908080357bffffffffffffffffffffffffffffffffffffffffffffffffffffffff19169060200190929190505050610d7e565b604051808273ffffffffffffffffffffffffffffffffffffffff16815260200191505060405180910390f35b61039b6004803603604081101561031a57600080fd5b81019080803573ffffffffffffffffffffffffffffffffffffffff1690602001909291908035906020019064010000000081111561035757600080fd5b82018360208201111561036957600080fd5b8035906020019184602083028401116401000000008311171561038b57600080fd5b9091929391929390505050610df9565b005b7f000000000000000000000000000000000000000000000000000000000000000073ffffffffffffffffffffffffffffffffffffffff1663ee97f7f36040518163ffffffff1660e01b815260040160206040518083038186803b15801561040357600080fd5b505afa158015610417573d6000803e3d6000fd5b505050506040513d602081101561042d57600080fd5b810190808051906020019092919050505073ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff16146104de576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252601b8152602001807f496d706c656d656e746174696f6e733a206e6f742d6d6173746572000000000081525060200191505060405180910390fd5b600073ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff161415610564576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252602b8152602001806114cd602b913960400191505060405180910390fd5b6000600260008373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020805490501415610600576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252602b8152602001806114f8602b913960400191505060405180910390fd5b6060600260008373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000208054806020026020016040519081016040528092919081815260200182805480156106cd57602002820191906000526020600020906000905b82829054906101000a900460e01b7bffffffffffffffffffffffffffffffffffffffffffffffffffffffff19168152602001906004019060208260030104928301926001038202915080841161067a5790505b5050505050905060005b815181101561077a5760008282815181106106ee57fe5b6020026020010151905060016000827bffffffffffffffffffffffffffffffffffffffffffffffffffffffff19167bffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916815260200190815260200160002060006101000a81549073ffffffffffffffffffffffffffffffffffffffff02191690555080806001019150506106d7565b50600260008373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060006107c69190611305565b8173ffffffffffffffffffffffffffffffffffffffff167fb7d759e6cdda23e8a1749bce345fc77355b8a22eeaf92c6e4e7257d959c162c7826040518080602001828103825283818151815260200191508051906020019060200280838360005b83811015610842578082015181840152602081019050610827565b505050509050019250505060405180910390a25050565b7f000000000000000000000000000000000000000000000000000000000000000073ffffffffffffffffffffffffffffffffffffffff1663ee97f7f36040518163ffffffff1660e01b815260040160206040518083038186803b1580156108bf57600080fd5b505afa1580156108d3573d6000803e3d6000fd5b505050506040513d60208110156108e957600080fd5b810190808051906020019092919050505073ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff161461099a576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252601b8152602001807f496d706c656d656e746174696f6e733a206e6f742d6d6173746572000000000081525060200191505060405180910390fd5b600073ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff161415610a20576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260398152602001806114656039913960400191505060405180910390fd5b60008054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff161415610ac5576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260368152602001806115236036913960400191505060405180910390fd5b8073ffffffffffffffffffffffffffffffffffffffff1660008054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff167f3b337225b8d68d037e0c721876335a3832bd08162c943fe5f88e8d428597ca8f60405160405180910390a3806000806101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff16021790555050565b6060600260008373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020805480602002602001604051908101604052809291908181526020018280548015610c4f57602002820191906000526020600020906000905b82829054906101000a900460e01b7bffffffffffffffffffffffffffffffffffffffffffffffffffffffff191681526020019060040190602082600301049283019260010382029150808411610bfc5790505b50505050509050919050565b7f000000000000000000000000000000000000000000000000000000000000000081565b60008054906101000a900473ffffffffffffffffffffffffffffffffffffffff1681565b60008060016000847bffffffffffffffffffffffffffffffffffffffffffffffffffffffff19167bffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916815260200190815260200160002060009054906101000a900473ffffffffffffffffffffffffffffffffffffffff169050600073ffffffffffffffffffffffffffffffffffffffff168173ffffffffffffffffffffffffffffffffffffffff1614610d545780610d76565b60008054906101000a900473ffffffffffffffffffffffffffffffffffffffff165b915050919050565b600060016000837bffffffffffffffffffffffffffffffffffffffffffffffffffffffff19167bffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916815260200190815260200160002060009054906101000a900473ffffffffffffffffffffffffffffffffffffffff169050919050565b7f000000000000000000000000000000000000000000000000000000000000000073ffffffffffffffffffffffffffffffffffffffff1663ee97f7f36040518163ffffffff1660e01b815260040160206040518083038186803b158015610e5f57600080fd5b505afa158015610e73573d6000803e3d6000fd5b505050506040513d6020811015610e8957600080fd5b810190808051906020019092919050505073ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff1614610f3a576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252601b8152602001807f496d706c656d656e746174696f6e733a206e6f742d6d6173746572000000000081525060200191505060405180910390fd5b600073ffffffffffffffffffffffffffffffffffffffff168373ffffffffffffffffffffffffffffffffffffffff161415610fc0576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252602b8152602001806114cd602b913960400191505060405180910390fd5b6000600260008573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020805490501461105b576040517f08c379a000000000000000000000000000000000000000000000000000000000815260040180806020018281038252602f81526020018061149e602f913960400191505060405180910390fd5b60005b8282905081101561123357600083838381811061107757fe5b905060200201357bffffffffffffffffffffffffffffffffffffffffffffffffffffffff19169050600073ffffffffffffffffffffffffffffffffffffffff1660016000837bffffffffffffffffffffffffffffffffffffffffffffffffffffffff19167bffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916815260200190815260200160002060009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1614611195576040517f08c379a00000000000000000000000000000000000000000000000000000000081526004018080602001828103825260238152602001806114426023913960400191505060405180910390fd5b8460016000837bffffffffffffffffffffffffffffffffffffffffffffffffffffffff19167bffffffffffffffffffffffffffffffffffffffffffffffffffffffff1916815260200190815260200160002060006101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff16021790555050808060010191505061105e565b508181600260008673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020919061128292919061132d565b508273ffffffffffffffffffffffffffffffffffffffff167ff9c512a86be00aaec236065d0a439f064133f367de889c782448c3578a3f30c5838360405180806020018281038252848482818152602001925060200280828437600081840152601f19601f820116905080830192505050935050505060405180910390a2505050565b50805460008255600701600890049060005260206000209081019061132a91906113f9565b50565b828054828255906000526020600020906007016008900481019282156113e85791602002820160005b838211156113b65783357bffffffffffffffffffffffffffffffffffffffffffffffffffffffff191683826101000a81548163ffffffff021916908360e01c02179055509260200192600401602081600301049283019260010302611356565b80156113e65782816101000a81549063ffffffff02191690556004016020816003010492830192600103026113b6565b505b5090506113f59190611416565b5090565b5b808211156114125760008160009055506001016113fa565b5090565b5b8082111561143d57600081816101000a81549063ffffffff021916905550600101611417565b509056fe496d706c656d656e746174696f6e733a205f73696720616c7265616479206164646564496d706c656d656e746174696f6e733a205f64656661756c74496d706c656d656e746174696f6e2061646472657373206e6f742076616c6964496d706c656d656e746174696f6e733a205f696d706c656d656e746174696f6e20616c72656164792061646465642e496d706c656d656e746174696f6e733a205f696d706c656d656e746174696f6e206e6f742076616c69642e496d706c656d656e746174696f6e733a205f696d706c656d656e746174696f6e206e6f7420666f756e642e496d706c656d656e746174696f6e733a205f64656661756c74496d706c656d656e746174696f6e2063616e6e6f742062652073616d65a2646970667358221220d42dcb568934728d15f318f76a6028fe848a484b097ea24581f45425fcde152864736f6c63430007000033", + "linkReferences": {}, + "deployedLinkReferences": {} +} diff --git a/scripts/constant/abi/core/connectorsV2.json b/scripts/constant/abi/core/connectorsV2.json new file mode 100644 index 0000000..6cc683b --- /dev/null +++ b/scripts/constant/abi/core/connectorsV2.json @@ -0,0 +1,250 @@ +[ + { + "inputs": [ + { + "internalType": "address", + "name": "_instaIndex", + "type": "address" + } + ], + "stateMutability": "nonpayable", + "type": "constructor" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "bytes32", + "name": "connectorNameHash", + "type": "bytes32" + }, + { + "indexed": false, + "internalType": "string", + "name": "connectorName", + "type": "string" + }, + { + "indexed": true, + "internalType": "address", + "name": "connector", + "type": "address" + } + ], + "name": "LogConnectorAdded", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "bytes32", + "name": "connectorNameHash", + "type": "bytes32" + }, + { + "indexed": false, + "internalType": "string", + "name": "connectorName", + "type": "string" + }, + { + "indexed": true, + "internalType": "address", + "name": "connector", + "type": "address" + } + ], + "name": "LogConnectorRemoved", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "bytes32", + "name": "connectorNameHash", + "type": "bytes32" + }, + { + "indexed": false, + "internalType": "string", + "name": "connectorName", + "type": "string" + }, + { + "indexed": true, + "internalType": "address", + "name": "oldConnector", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "newConnector", + "type": "address" + } + ], + "name": "LogConnectorUpdated", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "addr", + "type": "address" + }, + { + "indexed": true, + "internalType": "bool", + "name": "isChief", + "type": "bool" + } + ], + "name": "LogController", + "type": "event" + }, + { + "inputs": [ + { + "internalType": "string[]", + "name": "_connectorNames", + "type": "string[]" + }, + { + "internalType": "address[]", + "name": "_connectors", + "type": "address[]" + } + ], + "name": "addConnectors", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "name": "chief", + "outputs": [ + { + "internalType": "bool", + "name": "", + "type": "bool" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "string", + "name": "", + "type": "string" + } + ], + "name": "connectors", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "instaIndex", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "string[]", + "name": "_connectorNames", + "type": "string[]" + } + ], + "name": "isConnectors", + "outputs": [ + { + "internalType": "bool", + "name": "isOk", + "type": "bool" + }, + { + "internalType": "address[]", + "name": "_connectors", + "type": "address[]" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "string[]", + "name": "_connectorNames", + "type": "string[]" + } + ], + "name": "removeConnectors", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "_chiefAddress", + "type": "address" + } + ], + "name": "toggleChief", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "string[]", + "name": "_connectorNames", + "type": "string[]" + }, + { + "internalType": "address[]", + "name": "_connectors", + "type": "address[]" + } + ], + "name": "updateConnectors", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + } + ] \ No newline at end of file diff --git a/scripts/constant/abi/core/instaIndex.json b/scripts/constant/abi/core/instaIndex.json new file mode 100644 index 0000000..70b1075 --- /dev/null +++ b/scripts/constant/abi/core/instaIndex.json @@ -0,0 +1,380 @@ +[ + { + "anonymous": false, + "inputs": [ + { + "indexed": false, + "internalType": "address", + "name": "sender", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "owner", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "account", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "origin", + "type": "address" + } + ], + "name": "LogAccountCreated", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "_newAccount", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "_connectors", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "_check", + "type": "address" + } + ], + "name": "LogNewAccount", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "uint256", + "name": "accountVersion", + "type": "uint256" + }, + { + "indexed": true, + "internalType": "address", + "name": "check", + "type": "address" + } + ], + "name": "LogNewCheck", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "master", + "type": "address" + } + ], + "name": "LogNewMaster", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "master", + "type": "address" + } + ], + "name": "LogUpdateMaster", + "type": "event" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "name": "account", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "_newAccount", + "type": "address" + }, + { + "internalType": "address", + "name": "_connectors", + "type": "address" + }, + { + "internalType": "address", + "name": "_check", + "type": "address" + } + ], + "name": "addNewAccount", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "_owner", + "type": "address" + }, + { + "internalType": "uint256", + "name": "accountVersion", + "type": "uint256" + }, + { + "internalType": "address", + "name": "_origin", + "type": "address" + } + ], + "name": "build", + "outputs": [ + { + "internalType": "address", + "name": "_account", + "type": "address" + } + ], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "_owner", + "type": "address" + }, + { + "internalType": "uint256", + "name": "accountVersion", + "type": "uint256" + }, + { + "internalType": "address[]", + "name": "_targets", + "type": "address[]" + }, + { + "internalType": "bytes[]", + "name": "_datas", + "type": "bytes[]" + }, + { + "internalType": "address", + "name": "_origin", + "type": "address" + } + ], + "name": "buildWithCast", + "outputs": [ + { + "internalType": "address", + "name": "_account", + "type": "address" + } + ], + "stateMutability": "payable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "accountVersion", + "type": "uint256" + }, + { + "internalType": "address", + "name": "_newCheck", + "type": "address" + } + ], + "name": "changeCheck", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "_newMaster", + "type": "address" + } + ], + "name": "changeMaster", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "name": "check", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "name": "connectors", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "version", + "type": "uint256" + }, + { + "internalType": "address", + "name": "query", + "type": "address" + } + ], + "name": "isClone", + "outputs": [ + { + "internalType": "bool", + "name": "result", + "type": "bool" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "list", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "master", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "_master", + "type": "address" + }, + { + "internalType": "address", + "name": "_list", + "type": "address" + }, + { + "internalType": "address", + "name": "_account", + "type": "address" + }, + { + "internalType": "address", + "name": "_connectors", + "type": "address" + } + ], + "name": "setBasics", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "updateMaster", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [], + "name": "versionCount", + "outputs": [ + { + "internalType": "uint256", + "name": "", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + } + ] \ No newline at end of file diff --git a/scripts/constant/abi/read/compound.json b/scripts/constant/abi/read/compound.json new file mode 100644 index 0000000..7dff5d0 --- /dev/null +++ b/scripts/constant/abi/read/compound.json @@ -0,0 +1,186 @@ +[ + { + "inputs": [], + "name": "getCETHAddress", + "outputs": [{ "internalType": "address", "name": "", "type": "address" }], + "stateMutability": "pure", + "type": "function" + }, + { + "inputs": [], + "name": "getCompReadAddress", + "outputs": [{ "internalType": "address", "name": "", "type": "address" }], + "stateMutability": "pure", + "type": "function" + }, + { + "inputs": [], + "name": "getCompToken", + "outputs": [ + { + "internalType": "contract TokenInterface", + "name": "", + "type": "address" + } + ], + "stateMutability": "pure", + "type": "function" + }, + { + "inputs": [ + { "internalType": "address", "name": "owner", "type": "address" }, + { "internalType": "address[]", "name": "cAddress", "type": "address[]" } + ], + "name": "getCompoundData", + "outputs": [ + { + "components": [ + { + "internalType": "uint256", + "name": "tokenPriceInEth", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "tokenPriceInUsd", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "exchangeRateStored", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "balanceOfUser", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "borrowBalanceStoredUser", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "supplyRatePerBlock", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "borrowRatePerBlock", + "type": "uint256" + } + ], + "internalType": "struct Helpers.CompData[]", + "name": "", + "type": "tuple[]" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "getComptroller", + "outputs": [ + { + "internalType": "contract ComptrollerLensInterface", + "name": "", + "type": "address" + } + ], + "stateMutability": "pure", + "type": "function" + }, + { + "inputs": [], + "name": "getOracleAddress", + "outputs": [{ "internalType": "address", "name": "", "type": "address" }], + "stateMutability": "pure", + "type": "function" + }, + { + "inputs": [ + { "internalType": "address", "name": "owner", "type": "address" }, + { "internalType": "address[]", "name": "cAddress", "type": "address[]" } + ], + "name": "getPosition", + "outputs": [ + { + "components": [ + { + "internalType": "uint256", + "name": "tokenPriceInEth", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "tokenPriceInUsd", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "exchangeRateStored", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "balanceOfUser", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "borrowBalanceStoredUser", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "supplyRatePerBlock", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "borrowRatePerBlock", + "type": "uint256" + } + ], + "internalType": "struct Helpers.CompData[]", + "name": "", + "type": "tuple[]" + }, + { + "components": [ + { "internalType": "uint256", "name": "balance", "type": "uint256" }, + { "internalType": "uint256", "name": "votes", "type": "uint256" }, + { "internalType": "address", "name": "delegate", "type": "address" }, + { "internalType": "uint256", "name": "allocated", "type": "uint256" } + ], + "internalType": "struct CompReadInterface.CompBalanceMetadataExt", + "name": "", + "type": "tuple" + } + ], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { "internalType": "address", "name": "cToken", "type": "address" }, + { "internalType": "address", "name": "token", "type": "address" } + ], + "name": "getPriceInEth", + "outputs": [ + { "internalType": "uint256", "name": "priceInETH", "type": "uint256" }, + { "internalType": "uint256", "name": "priceInUSD", "type": "uint256" } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "name", + "outputs": [{ "internalType": "string", "name": "", "type": "string" }], + "stateMutability": "view", + "type": "function" + } +] diff --git a/scripts/constant/abi/read/core.json b/scripts/constant/abi/read/core.json new file mode 100644 index 0000000..d85b23d --- /dev/null +++ b/scripts/constant/abi/read/core.json @@ -0,0 +1,312 @@ +[ + { + "inputs": [ + { "internalType": "address", "name": "_index", "type": "address" }, + { "internalType": "address", "name": "gnosisFactory", "type": "address" } + ], + "stateMutability": "nonpayable", + "type": "constructor" + }, + { + "inputs": [], + "name": "connectors", + "outputs": [{ "internalType": "address", "name": "", "type": "address" }], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [{ "internalType": "uint64", "name": "id", "type": "uint64" }], + "name": "getAccount", + "outputs": [ + { "internalType": "address", "name": "account", "type": "address" } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { "internalType": "address", "name": "account", "type": "address" } + ], + "name": "getAccountAuthorities", + "outputs": [ + { "internalType": "address[]", "name": "", "type": "address[]" } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { "internalType": "address", "name": "account", "type": "address" } + ], + "name": "getAccountAuthoritiesTypes", + "outputs": [ + { + "components": [ + { "internalType": "address", "name": "owner", "type": "address" }, + { "internalType": "uint256", "name": "authType", "type": "uint256" } + ], + "internalType": "struct AccountResolver.AuthType[]", + "name": "", + "type": "tuple[]" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { "internalType": "address", "name": "account", "type": "address" } + ], + "name": "getAccountDetails", + "outputs": [ + { + "components": [ + { "internalType": "uint256", "name": "ID", "type": "uint256" }, + { "internalType": "address", "name": "account", "type": "address" }, + { "internalType": "uint256", "name": "version", "type": "uint256" }, + { + "internalType": "address[]", + "name": "authorities", + "type": "address[]" + } + ], + "internalType": "struct AccountResolver.AccountData", + "name": "", + "type": "tuple" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [{ "internalType": "uint256", "name": "id", "type": "uint256" }], + "name": "getAccountIdDetails", + "outputs": [ + { + "components": [ + { "internalType": "uint256", "name": "ID", "type": "uint256" }, + { "internalType": "address", "name": "account", "type": "address" }, + { "internalType": "uint256", "name": "version", "type": "uint256" }, + { + "internalType": "address[]", + "name": "authorities", + "type": "address[]" + } + ], + "internalType": "struct AccountResolver.AccountData", + "name": "", + "type": "tuple" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { "internalType": "address[]", "name": "accounts", "type": "address[]" } + ], + "name": "getAccountVersions", + "outputs": [ + { "internalType": "uint256[]", "name": "", "type": "uint256[]" } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { "internalType": "address", "name": "authority", "type": "address" } + ], + "name": "getAuthorityAccounts", + "outputs": [ + { "internalType": "address[]", "name": "", "type": "address[]" } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { "internalType": "address", "name": "authority", "type": "address" } + ], + "name": "getAuthorityDetails", + "outputs": [ + { + "components": [ + { "internalType": "uint64[]", "name": "IDs", "type": "uint64[]" }, + { + "internalType": "address[]", + "name": "accounts", + "type": "address[]" + }, + { + "internalType": "uint256[]", + "name": "versions", + "type": "uint256[]" + } + ], + "internalType": "struct AccountResolver.AuthorityData", + "name": "", + "type": "tuple" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { "internalType": "address", "name": "authority", "type": "address" } + ], + "name": "getAuthorityIDs", + "outputs": [{ "internalType": "uint64[]", "name": "", "type": "uint64[]" }], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address[]", + "name": "authorities", + "type": "address[]" + } + ], + "name": "getAuthorityTypes", + "outputs": [ + { + "components": [ + { "internalType": "address", "name": "owner", "type": "address" }, + { "internalType": "uint256", "name": "authType", "type": "uint256" } + ], + "internalType": "struct AccountResolver.AuthType[]", + "name": "", + "type": "tuple[]" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { "internalType": "address", "name": "_addr", "type": "address" } + ], + "name": "getContractCode", + "outputs": [{ "internalType": "bytes", "name": "o_code", "type": "bytes" }], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "getEnabledConnectors", + "outputs": [ + { "internalType": "address[]", "name": "", "type": "address[]" } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "getEnabledConnectorsData", + "outputs": [ + { + "components": [ + { "internalType": "address", "name": "connector", "type": "address" }, + { + "internalType": "uint256", + "name": "connectorID", + "type": "uint256" + }, + { "internalType": "string", "name": "name", "type": "string" } + ], + "internalType": "struct ConnectorsResolver.ConnectorsData[]", + "name": "", + "type": "tuple[]" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { "internalType": "address", "name": "account", "type": "address" } + ], + "name": "getID", + "outputs": [{ "internalType": "uint256", "name": "id", "type": "uint256" }], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [{ "internalType": "uint256", "name": "id", "type": "uint256" }], + "name": "getIDAuthorities", + "outputs": [ + { "internalType": "address[]", "name": "", "type": "address[]" } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "getStaticConnectors", + "outputs": [ + { "internalType": "address[]", "name": "", "type": "address[]" } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "getStaticConnectorsData", + "outputs": [ + { + "components": [ + { "internalType": "address", "name": "connector", "type": "address" }, + { + "internalType": "uint256", + "name": "connectorID", + "type": "uint256" + }, + { "internalType": "string", "name": "name", "type": "string" } + ], + "internalType": "struct ConnectorsResolver.ConnectorsData[]", + "name": "", + "type": "tuple[]" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "index", + "outputs": [{ "internalType": "address", "name": "", "type": "address" }], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { "internalType": "address", "name": "account", "type": "address" } + ], + "name": "isShield", + "outputs": [{ "internalType": "bool", "name": "shield", "type": "bool" }], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "list", + "outputs": [{ "internalType": "address", "name": "", "type": "address" }], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "name", + "outputs": [{ "internalType": "string", "name": "", "type": "string" }], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "version", + "outputs": [{ "internalType": "uint256", "name": "", "type": "uint256" }], + "stateMutability": "view", + "type": "function" + } +] diff --git a/scripts/constant/abi/read/erc20.json b/scripts/constant/abi/read/erc20.json new file mode 100644 index 0000000..46d41c3 --- /dev/null +++ b/scripts/constant/abi/read/erc20.json @@ -0,0 +1,68 @@ +[ + { + "inputs": [ + { + "internalType": "address", + "name": "owner", + "type": "address" + }, + { + "internalType": "address", + "name": "spender", + "type": "address" + }, + { + "internalType": "address[]", + "name": "tknAddress", + "type": "address[]" + } + ], + "name": "getAllowances", + "outputs": [ + { + "internalType": "uint256[]", + "name": "", + "type": "uint256[]" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "owner", + "type": "address" + }, + { + "internalType": "address[]", + "name": "tknAddress", + "type": "address[]" + } + ], + "name": "getBalances", + "outputs": [ + { + "internalType": "uint256[]", + "name": "", + "type": "uint256[]" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "name", + "outputs": [ + { + "internalType": "string", + "name": "", + "type": "string" + } + ], + "stateMutability": "view", + "type": "function" + } +] diff --git a/scripts/constant/abi/read/maker.json b/scripts/constant/abi/read/maker.json new file mode 100644 index 0000000..aa473f2 --- /dev/null +++ b/scripts/constant/abi/read/maker.json @@ -0,0 +1,263 @@ +[ + { + "inputs": [ + { + "internalType": "string[]", + "name": "name", + "type": "string[]" + } + ], + "name": "getColInfo", + "outputs": [ + { + "components": [ + { + "internalType": "uint256", + "name": "borrowRate", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "price", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "liquidationRatio", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "debtCeiling", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "totalDebt", + "type": "uint256" + } + ], + "internalType": "struct Helpers.ColInfo[]", + "name": "", + "type": "tuple[]" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "owner", + "type": "address" + } + ], + "name": "getDaiPosition", + "outputs": [ + { + "internalType": "uint256", + "name": "amt", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "dsr", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "getDsrRate", + "outputs": [ + { + "internalType": "uint256", + "name": "dsr", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "getMcdAddresses", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "pure", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "uint256", + "name": "id", + "type": "uint256" + } + ], + "name": "getVaultById", + "outputs": [ + { + "components": [ + { + "internalType": "uint256", + "name": "id", + "type": "uint256" + }, + { + "internalType": "address", + "name": "owner", + "type": "address" + }, + { + "internalType": "string", + "name": "colType", + "type": "string" + }, + { + "internalType": "uint256", + "name": "collateral", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "art", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "debt", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "liquidatedCol", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "borrowRate", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "colPrice", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "liquidationRatio", + "type": "uint256" + }, + { + "internalType": "address", + "name": "vaultAddress", + "type": "address" + } + ], + "internalType": "struct Helpers.VaultData", + "name": "", + "type": "tuple" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "owner", + "type": "address" + } + ], + "name": "getVaults", + "outputs": [ + { + "components": [ + { + "internalType": "uint256", + "name": "id", + "type": "uint256" + }, + { + "internalType": "address", + "name": "owner", + "type": "address" + }, + { + "internalType": "string", + "name": "colType", + "type": "string" + }, + { + "internalType": "uint256", + "name": "collateral", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "art", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "debt", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "liquidatedCol", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "borrowRate", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "colPrice", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "liquidationRatio", + "type": "uint256" + }, + { + "internalType": "address", + "name": "vaultAddress", + "type": "address" + } + ], + "internalType": "struct Helpers.VaultData[]", + "name": "", + "type": "tuple[]" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "name", + "outputs": [ + { + "internalType": "string", + "name": "", + "type": "string" + } + ], + "stateMutability": "view", + "type": "function" + } +] diff --git a/scripts/constant/abi/read/uniswap.json b/scripts/constant/abi/read/uniswap.json new file mode 100644 index 0000000..5b60402 --- /dev/null +++ b/scripts/constant/abi/read/uniswap.json @@ -0,0 +1,281 @@ +[ + { + "inputs": [ + { + "internalType": "address", + "name": "buyAddr", + "type": "address" + }, + { + "internalType": "address", + "name": "sellAddr", + "type": "address" + }, + { + "internalType": "uint256", + "name": "sellAmt", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "slippage", + "type": "uint256" + } + ], + "name": "getBuyAmount", + "outputs": [ + { + "internalType": "uint256", + "name": "buyAmt", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "unitAmt", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "tokenA", + "type": "address" + }, + { + "internalType": "address", + "name": "tokenB", + "type": "address" + }, + { + "internalType": "uint256", + "name": "amtA", + "type": "uint256" + } + ], + "name": "getDepositAmount", + "outputs": [ + { + "internalType": "uint256", + "name": "amtB", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "unitAmt", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "tokenA", + "type": "address" + }, + { + "internalType": "address", + "name": "tokenB", + "type": "address" + }, + { + "internalType": "uint256", + "name": "amtA", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "amtB", + "type": "uint256" + } + ], + "name": "getDepositAmountNewPool", + "outputs": [ + { + "internalType": "uint256", + "name": "unitAmt", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "getEthAddr", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "pure", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "owner", + "type": "address" + }, + { + "components": [ + { + "internalType": "address", + "name": "tokenA", + "type": "address" + }, + { + "internalType": "address", + "name": "tokenB", + "type": "address" + } + ], + "internalType": "struct Resolver.TokenPair[]", + "name": "tokenPairs", + "type": "tuple[]" + } + ], + "name": "getPosition", + "outputs": [ + { + "components": [ + { + "internalType": "uint256", + "name": "tokenAShareAmt", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "tokenBShareAmt", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "uniAmt", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "totalSupply", + "type": "uint256" + } + ], + "internalType": "struct Resolver.PoolData[]", + "name": "", + "type": "tuple[]" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "buyAddr", + "type": "address" + }, + { + "internalType": "address", + "name": "sellAddr", + "type": "address" + }, + { + "internalType": "uint256", + "name": "buyAmt", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "slippage", + "type": "uint256" + } + ], + "name": "getSellAmount", + "outputs": [ + { + "internalType": "uint256", + "name": "sellAmt", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "unitAmt", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "tokenA", + "type": "address" + }, + { + "internalType": "address", + "name": "tokenB", + "type": "address" + }, + { + "internalType": "uint256", + "name": "uniAmt", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "slippage", + "type": "uint256" + } + ], + "name": "getWithdrawAmounts", + "outputs": [ + { + "internalType": "uint256", + "name": "amtA", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "amtB", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "unitAmtA", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "unitAmtB", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [], + "name": "name", + "outputs": [ + { + "internalType": "string", + "name": "", + "type": "string" + } + ], + "stateMutability": "view", + "type": "function" + } +] diff --git a/scripts/constant/abis.ts b/scripts/constant/abis.ts new file mode 100644 index 0000000..c2a7070 --- /dev/null +++ b/scripts/constant/abis.ts @@ -0,0 +1,16 @@ +export const abis: Record = { + core: { + connectorsV2: require("./abi/core/connectorsV2.json"), + instaIndex: require("./abi/core/instaIndex.json") + }, + connectors: { + "Basic-v1": require("./abi/connectors/basic.json"), + basic: require("./abi/connectors/basic.json"), + auth: require("./abi/connectors/auth.json"), + "INSTAPOOL-A": require("./abi/connectors/instapool.json"), + "INSTAPOOL-C": require("./abi/connectors/instapool-c.json") + }, + basic: { + erc20: require("./abi/basics/erc20.json") + } +}; diff --git a/scripts/constant/constant.ts b/scripts/constant/constant.ts new file mode 100644 index 0000000..9e659d7 --- /dev/null +++ b/scripts/constant/constant.ts @@ -0,0 +1,6 @@ +export const constants = { + address_zero: "0x0000000000000000000000000000000000000000", + native_address: "0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE", + max_value: + "115792089237316195423570985008687907853269984665640564039457584007913129639935", +}; diff --git a/scripts/constant/deployAddress.ts b/scripts/constant/deployAddress.ts new file mode 100644 index 0000000..e69de29 diff --git a/scripts/deployment/connectors.ts b/scripts/deployment/connectors.ts new file mode 100644 index 0000000..4c4f143 --- /dev/null +++ b/scripts/deployment/connectors.ts @@ -0,0 +1,71 @@ +export const connectMapping: Record = { + mainnet: { + "1INCH-A": "ConnectV2OneInch", + "1INCH-B": "ConnectV2OneProto", + "AAVE-V1-A": "ConnectV2AaveV1", + "AAVE-V2-A": "ConnectV2AaveV2", + "AUTHORITY-A": "ConnectV2Auth", + "BASIC-A": "ConnectV2Basic", + "BASIC-D": "ConnectV2BasicERC4626", + "COMP-A": "ConnectV2COMP", + "COMPOUND-A": "ConnectV2Compound", + "DYDX-A": "ConnectV2Dydx", + "FEE-A": "ConnectV2Fee", + "GELATO-A": "ConnectV2Gelato", + "MAKERDAO-A": "ConnectV2Maker", + "UNISWAP-A": "ConnectV2UniswapV2", + }, + polygon: { + "QUICKSWAP-A": "ConnectV2Paraswap", + "UniswapV3-v1": "ConnectV2UniswapV3Polygon", + "Uniswap-V3-Staker-v1.1": "ConnectV2UniswapV3StakerPolygon", + "Paraswap-v5": "ConnectV2ParaswapV5Polygon", + "1INCH-V4": "ConnectV2OneInchV4Polygon", + }, + avalanche: { + "ZEROEX-A": "ConnectV2ZeroExAvalanche", + }, +}; + +export const connectors: Record> = { + mainnet: [ + 'ConnectV2ZeroEx', + 'ConnectV2ApproveTokens', + 'ConnectV2Auth', + 'ConnectV2Basic', + 'ConnectV2BasicERC721', + 'ConnectV2BasicERC1155', + 'ConnectV2CompoundV3', + 'ConnectV2CompoundV3Rewards', + 'ConnectV2InstaDexSimulation', + 'ConnectV2DSASpell', + 'ConnectV2SwapAggregator', + 'ConnectV2UniswapV3', + 'ConnectV2UniswapV3AutoRouter', + 'ConnectV2UniswapV3Swap', + ], + polygon: [ + "QUICKSWAP-A", + "UniswapV3-v1", + "Uniswap-V3-Staker-v1.1", + "Paraswap-v5", + "1INCH-V4", + ], + avalanche: ["ZEROEX-A"], + base: [ + 'ConnectV2ZeroExBase', + 'ConnectV2ApproveTokensBase', + 'ConnectV2AuthBase', + 'ConnectV2BasicBase', + 'ConnectV2BasicERC721Base', + 'ConnectV2BasicERC1155Base', + 'ConnectV2CompoundV3Base', + 'ConnectV2CompoundV3RewardsBase', + 'ConnectV2InstaDexSimulationBase', + 'ConnectV2DSASpellBase', + 'ConnectV2SwapAggregatorBase', + 'ConnectV2UniswapV3Base', + 'ConnectV2UniswapV3AutoRouterBase', + 'ConnectV2UniswapV3SwapBase', + ], +}; diff --git a/scripts/deployment/deploy.ts b/scripts/deployment/deploy.ts new file mode 100644 index 0000000..233c72e --- /dev/null +++ b/scripts/deployment/deploy.ts @@ -0,0 +1,23 @@ +import { ethers } from "hardhat"; +import { deployConnector } from "./deployConnector"; +import { connectMapping } from "./connectors"; + +async function main() { + if (process.env.connectorName) { + await deployConnector(); + } else { + const addressMapping: Record = {}; + + for (const key in connectMapping) { + addressMapping[key] = await deployConnector(connectMapping[key]); + } + console.log(addressMapping); + } +} + +main() + .then(() => process.exit(0)) + .catch((error) => { + console.error(error); + process.exit(1); + }); diff --git a/scripts/deployment/deployAndVerifyOnSourcify.ts b/scripts/deployment/deployAndVerifyOnSourcify.ts new file mode 100644 index 0000000..50e877d --- /dev/null +++ b/scripts/deployment/deployAndVerifyOnSourcify.ts @@ -0,0 +1,24 @@ +import hre from "hardhat"; +const { ethers, deployments, getUnnamedAccounts } = hre; +const { deploy } = deployments; + +async function main() { + const deployer = (await getUnnamedAccounts())[0]; + const connector = "// Add connector name over here Eg: ConnectV2InstaPoolV3Avalanche" + + const connectorInstance = await deploy(connector, { + from: deployer, + }); + console.log(`${connector} deployed: `, connectorInstance.address); + + await hre.run("sourcify", { + address: connectorInstance.address, + }); +} + +main() + .then(() => process.exit(0)) + .catch((error) => { + console.error(error); + process.exit(1); + }); diff --git a/scripts/deployment/deployCompoundMapping.ts b/scripts/deployment/deployCompoundMapping.ts new file mode 100644 index 0000000..a5c78cd --- /dev/null +++ b/scripts/deployment/deployCompoundMapping.ts @@ -0,0 +1,79 @@ +import hre from "hardhat"; +const { ethers } = hre; +async function main() { + const CONNECTORS_V2 = "0x97b0B3A8bDeFE8cB9563a3c610019Ad10DB8aD11"; + + const ctokenMapping = { + "ETH-A": "0x4ddc2d193948926d02f9b1fe9e1daa0718270ed5", + "BAT-A": "0x6c8c6b02e7b2be14d4fa6022dfd6d75921d90e4e", + "COMP-A": "0x70e36f6bf80a52b3b46b3af8e106cc0ed743e8e4", + "DAI-A": "0x5d3a536e4d6dbd6114cc1ead35777bab948e3643", + "REP-A": "0x158079ee67fce2f58472a96584a73c7ab9ac95c1", + "UNI-A": "0x35a18000230da775cac24873d00ff85bccded550", + "USDC-A": "0x39aa39c021dfbae8fac545936693ac917d5e7563", + "USDT-A": "0xf650c3d88d12db855b8bf7d11be6c55a4e07dcc9", + "WBTC-A": "0xc11b1268c1a384e55c48c2391d8d480264a3a7f4", + "WBTC-B": "0xccF4429DB6322D5C611ee964527D42E5d685DD6a", + "ZRX-A": "0xb3319f5d18bc0d84dd1b4825dcde5d5f7266d407", + "YFI-A": "0x80a2ae356fc9ef4305676f7a3e2ed04e12c33946", + "SUSHI-A": "0x4b0181102a0112a2ef11abee5563bb4a3176c9d7", + "MKR-A": "0x95b4ef2869ebd94beb4eee400a99824bf5dc325b", + "AAVE-A": "0xe65cdb6479bac1e22340e4e755fae7e509ecd06c", + "TUSD-A": "0x12392f67bdf24fae0af363c24ac620a2f67dad86", + "LINK-A": "0xface851a4921ce59e912d19329929ce6da6eb0c7", + }; + + const tokenMapping = { + "ETH-A": "0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE", + "BAT-A": "0x0D8775F648430679A709E98d2b0Cb6250d2887EF", + "COMP-A": "0xc00e94cb662c3520282e6f5717214004a7f26888", + "DAI-A": "0x6b175474e89094c44da98b954eedeac495271d0f", + "REP-A": "0x1985365e9f78359a9B6AD760e32412f4a445E862", + "UNI-A": "0x1f9840a85d5af5bf1d1762f925bdaddc4201f984", + "USDC-A": "0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48", + "USDT-A": "0xdac17f958d2ee523a2206206994597c13d831ec7", + "WBTC-A": "0x2260fac5e5542a773aa44fbcfedf7c193bc2c599", + "WBTC-B": "0x2260fac5e5542a773aa44fbcfedf7c193bc2c599", + "ZRX-A": "0xe41d2489571d322189246dafa5ebde1f4699f498", + "YFI-A": "0x0bc529c00C6401aEF6D220BE8C6Ea1667F6Ad93e", + "SUSHI-A": "0x6B3595068778DD592e39A122f4f5a5cF09C90fE2", + "MKR-A": "0x9f8F72aA9304c8B593d555F12eF6589cC3A579A2", + "AAVE-A": "0x7Fc66500c84A76Ad7e9c93437bFc5Ac33E2DDaE9", + "TUSD-A": "0x0000000000085d4780B73119b644AE5ecd22b376", + "LINK-A": "0x514910771af9ca656af840dff83e8264ecf986ca", + }; + + const Mapping = await ethers.getContractFactory("InstaCompoundMapping"); + const mapping = await Mapping.deploy( + CONNECTORS_V2, + Object.keys(ctokenMapping), + Object.values(tokenMapping), + Object.values(ctokenMapping) + ); + await mapping.deployed(); + + console.log(`InstaCompoundMapping Deployed: ${mapping.address}`); + + try { + await hre.run("verify:verify", { + address: mapping.address, + constructorArguments: [ + CONNECTORS_V2, + Object.keys(ctokenMapping), + Object.values(tokenMapping), + Object.values(ctokenMapping), + ], + }); + } catch (error) { + console.log(`Failed to verify: InstaCompoundMapping@${mapping.address}`); + console.log(error); + console.log(); + } +} + +main() + .then(() => process.exit(0)) + .catch((error) => { + console.error(error); + process.exit(1); + }); diff --git a/scripts/deployment/deployConnector.ts b/scripts/deployment/deployConnector.ts new file mode 100644 index 0000000..1b92548 --- /dev/null +++ b/scripts/deployment/deployConnector.ts @@ -0,0 +1,37 @@ +import hre, { ethers } from "hardhat"; +import { execScript } from "../tests/command"; + +export const deployConnector = async (connectorName?: string) => { + connectorName = String(process.env.connectorName) ?? connectorName; + const Connector = await ethers.getContractFactory(connectorName); + const connector = await Connector.deploy(); + await connector.deployed(); + + console.log(`${connectorName} Deployed: ${connector.address}`); + + const chain = String(hre.network.name); + if (chain !== "hardhat") { + const allPaths = await hre.artifacts.getArtifactPaths(); + + let connectorPath; + for (const path of allPaths) + if (path.split("/").includes(connectorName + ".json")) + connectorPath = path.slice(path.indexOf("contracts"), path.indexOf(connectorName) - 1) + `:${connectorName}`; + + try { + await execScript({ + cmd: "npx", + args: ["hardhat", "verify", "--network", `${chain}`, `${connector.address}`, "--contract", `${connectorPath}`], + env: { + networkType: chain + } + }); + } catch (error) { + console.log(`Failed to verify: ${connectorName}@${connector.address}`); + console.log(error); + console.log(); + } + } + + return connector.address; +}; diff --git a/scripts/deployment/deployConnectorsFromCmd.ts b/scripts/deployment/deployConnectorsFromCmd.ts new file mode 100644 index 0000000..086a09e --- /dev/null +++ b/scripts/deployment/deployConnectorsFromCmd.ts @@ -0,0 +1,88 @@ +import { execScript } from "../tests/command"; +import inquirer from "inquirer"; +import { connectors, connectMapping } from "./connectors"; +import { join } from "path"; + +let start: number, end: number, runchain: string; + +// async function connectorSelect(chain: string) { +// let { connector } = await inquirer.prompt([ +// { +// name: "connector", +// message: "Which connector do you want to deploy?", +// type: "list", +// choices: connectors[chain], +// }, +// ]); + +// return connector; +// } + +async function deployRunner() { + let { chain } = await inquirer.prompt([ + { + name: "chain", + message: "What chain do you want to deploy on?", + type: "list", + choices: ["mainnet", "polygon", "avalanche", "arbitrum", "optimism", "fantom", "base"] + } + ]); + + // let connector = await connectorSelect(chain); + + // let { choice } = await inquirer.prompt([ + // { + // name: "choice", + // message: "Do you wanna select again?", + // type: "list", + // choices: ["yes", "no"], + // }, + // ]); + + // if (choice === "yes") { + // connector = await connectorSelect(chain); + // } + // connector = connectMapping[chain][connector]; + + let { connector } = await inquirer.prompt([ + { + name: "connector", + message: "Enter the connector contract name? (ex: ConnectV2Paraswap)", + type: "input" + } + ]); + + let { choice } = await inquirer.prompt([ + { + name: "choice", + message: "Do you wanna try deploy on hardhat first?", + type: "list", + choices: ["yes", "no"] + } + ]); + + runchain = choice === "yes" ? "hardhat" : chain; + + console.log(`Deploying ${connector} on ${runchain}, press (ctrl + c) to stop`); + + start = Date.now(); + await execScript({ + cmd: "npx", + args: ["hardhat", "run", "scripts/deployment/deploy.ts", "--network", `${runchain}`], + env: { + connectorName: connector, + networkType: chain + } + }); + end = Date.now(); +} + +deployRunner() + .then(() => { + console.log(`Done successfully, total time taken: ${(end - start) / 1000} sec`); + process.exit(0); + }) + .catch((err) => { + console.log("error:", err); + process.exit(1); + }); diff --git a/scripts/deployment/deployConnectorsFromCmdAll.ts b/scripts/deployment/deployConnectorsFromCmdAll.ts new file mode 100644 index 0000000..e8c2921 --- /dev/null +++ b/scripts/deployment/deployConnectorsFromCmdAll.ts @@ -0,0 +1,86 @@ +import { execScript } from "../tests/command"; +import inquirer from "inquirer"; +import { connectors, connectMapping } from "./connectors"; +import { join } from "path"; + +let start: number, end: number, runchain: string; + +// async function connectorSelect(chain: string) { +// let { connector } = await inquirer.prompt([ +// { +// name: "connector", +// message: "Which connector do you want to deploy?", +// type: "list", +// choices: connectors[chain], +// }, +// ]); + +// return connector; +// } + +async function deployRunner() { + let { chain } = await inquirer.prompt([ + { + name: "chain", + message: "What chain do you want to deploy on?", + type: "list", + choices: ["mainnet", "polygon", "avalanche", "arbitrum", "optimism", "fantom", "base"] + } + ]); + + // let connector = await connectorSelect(chain); + + // let { choice } = await inquirer.prompt([ + // { + // name: "choice", + // message: "Do you wanna select again?", + // type: "list", + // choices: ["yes", "no"], + // }, + // ]); + + // if (choice === "yes") { + // connector = await connectorSelect(chain); + // } + // connector = connectMapping[chain][connector]; + + let { choice } = await inquirer.prompt([ + { + name: "choice", + message: "Do you wanna try deploy on hardhat first?", + type: "list", + choices: ["yes", "no"] + } + ]); + + runchain = choice === "yes" ? "hardhat" : chain; + + console.log(`Deploying on ${runchain}, press (ctrl + c) to stop`); + + start = Date.now(); + for (let i = 0; i < connectors[chain].length; i++) { + try { + await execScript({ + cmd: "npx", + args: ["hardhat", "run", "scripts/deployment/deploy.ts", "--network", `${runchain}`], + env: { + connectorName: connectors[chain][i], + networkType: chain + } + }); + } catch(e) { + console.error(`Failed of ${connectors[chain][i]} connector`) + } + } + end = Date.now(); +} + +deployRunner() + .then(() => { + console.log(`Done successfully, total time taken: ${(end - start) / 1000} sec`); + process.exit(0); + }) + .catch((err) => { + console.log("error:", err); + process.exit(1); + }); diff --git a/scripts/deployment/deployInstaMappingController.ts b/scripts/deployment/deployInstaMappingController.ts new file mode 100644 index 0000000..daeed49 --- /dev/null +++ b/scripts/deployment/deployInstaMappingController.ts @@ -0,0 +1,37 @@ +import hre from "hardhat"; +const { ethers } = hre; + +async function main() { + if (hre.network.name === "mainnet") { + console.log("\n\n Deploying Contracts to mainnet. Hit ctrl + c to abort"); + } else if (hre.network.name === "hardhat") { + console.log("\n\n Deploying Contracts to hardhat."); + } + + const InstaMappingController = await ethers.getContractFactory( + "InstaMappingController" + ); + const instaMappingController = await InstaMappingController.deploy(); + await instaMappingController.deployed(); + + console.log( + "InstaMappingController deployed: ", + instaMappingController.address + ); + + if (hre.network.name === "mainnet") { + await hre.run("verify:verify", { + address: instaMappingController.address, + constructorArguments: [], + }); + } else if (hre.network.name === "hardhat") { + console.log("Contracts deployed."); + } +} + +main() + .then(() => process.exit(0)) + .catch((error) => { + console.error(error); + process.exit(1); + }); diff --git a/scripts/deployment/deployManually.ts b/scripts/deployment/deployManually.ts new file mode 100644 index 0000000..d60f73d --- /dev/null +++ b/scripts/deployment/deployManually.ts @@ -0,0 +1,23 @@ +import { Contract } from "@ethersproject/contracts"; +import hre, { ethers } from "hardhat"; + +import { Greeter__factory } from "../../typechain"; + +async function main(): Promise { + const Greeter: Greeter__factory = await ethers.getContractFactory("Greeter"); + const greeter: Contract = await Greeter.deploy("Hello, Buidler!"); + await greeter.deployed(); + + console.log("Greeter deployed to: ", greeter.address); + + await hre.run("verify:verify", { + address: greeter.address + }); +} + +main() + .then(() => process.exit(0)) + .catch((error: Error) => { + console.error(error); + process.exit(1); + }); diff --git a/scripts/deployment/deployMappingContract.ts b/scripts/deployment/deployMappingContract.ts new file mode 100644 index 0000000..69a4bc4 --- /dev/null +++ b/scripts/deployment/deployMappingContract.ts @@ -0,0 +1,34 @@ +import hre from "hardhat"; +const { ethers } = hre; + +async function main() { + if (hre.network.name === "mainnet") { + console.log("\n\n Deploying Contracts to mainnet. Hit ctrl + c to abort"); + } else if (hre.network.name === "hardhat") { + console.log("\n\n Deploying Contracts to hardhat."); + } + + const mappingContract = "CONTRACT_NAME"; + + const InstaProtocolMapping = await ethers.getContractFactory(mappingContract); + const instaProtocolMapping = await InstaProtocolMapping.deploy(); + await instaProtocolMapping.deployed(); + + console.log(`${mappingContract} deployed: `, instaProtocolMapping.address); + + if (hre.network.name === "mainnet") { + await hre.run("verify:verify", { + address: instaProtocolMapping.address, + constructorArguments: [], + }); + } else if (hre.network.name === "hardhat") { + console.log("Contracts deployed."); + } +} + +main() + .then(() => process.exit(0)) + .catch((error) => { + console.error(error); + process.exit(1); + }); diff --git a/scripts/tests/addLiquidity.ts b/scripts/tests/addLiquidity.ts new file mode 100644 index 0000000..7263e78 --- /dev/null +++ b/scripts/tests/addLiquidity.ts @@ -0,0 +1,42 @@ +import { ethers, network } from "hardhat"; + +import { impersonateAccounts } from "./impersonate"; +import { tokenMapping as mainnetMapping } from "./mainnet/tokens"; +import { tokenMapping as polygonMapping } from "./polygon/tokens"; +import { tokenMapping as avalancheMapping } from "./avalanche/tokens"; +import { tokenMapping as optimismMapping } from "./optimism/tokens"; +import { tokenMapping as arbitrumMapping } from "./arbitrum/tokens"; +import { tokenMapping as fantomMapping } from "./fantom/tokens"; + +const mineTx = async (tx: any) => { + await (await tx).wait(); +}; + +const tokenMapping: Record> = { + mainnet: mainnetMapping, + polygon: polygonMapping, + avalanche: avalancheMapping, + optimism: optimismMapping, + arbitrum: arbitrumMapping, + fantom: fantomMapping +}; + +export async function addLiquidity(tokenName: string, address: any, amt: any) { + const [signer] = await ethers.getSigners(); + tokenName = tokenName.toLowerCase(); + const chain = String(process.env.networkType); + if (!tokenMapping[chain][tokenName]) { + throw new Error(`Add liquidity doesn't support the following token: ${tokenName}`); + } + + const token = tokenMapping[chain][tokenName]; + const [impersonatedSigner] = await impersonateAccounts([token.impersonateSigner]); + + // send 2 eth to cover any tx costs. + await network.provider.send("hardhat_setBalance", [ + impersonatedSigner.address, + ethers.utils.parseEther("2").toHexString() + ]); + + await token.process(impersonatedSigner, address, amt); +} diff --git a/scripts/tests/arbitrum/addresses.ts b/scripts/tests/arbitrum/addresses.ts new file mode 100644 index 0000000..d2da378 --- /dev/null +++ b/scripts/tests/arbitrum/addresses.ts @@ -0,0 +1,11 @@ +export const addresses: Record = { + connectors: { + // basic: "0x6214f9c4F9700fc7a50B5f9aEEB819d647406Ac7", + // auth: "0xD6daA927ad756a4022858dddcc4E26137b30DB4D", + // "INSTAPOOL-A": "0x8f1e38c53af7bD2b2bE01b9580911b7Cca504F1b", + }, + core: { + connectorsV2: "0x67fCE99Dd6d8d659eea2a1ac1b8881c57eb6592B", + instaIndex: "0x1eE00C305C51Ff3bE60162456A9B533C07cD9288", + }, +}; diff --git a/scripts/tests/arbitrum/tokens.ts b/scripts/tests/arbitrum/tokens.ts new file mode 100644 index 0000000..c9e5f40 --- /dev/null +++ b/scripts/tests/arbitrum/tokens.ts @@ -0,0 +1,60 @@ +import { Provider } from "@ethersproject/abstract-provider"; +import { Signer } from "@ethersproject/abstract-signer"; +import { ethers } from "hardhat"; + +const mineTx = async (tx: any) => { + await (await tx).wait(); +}; + +export const tokens = { + eth: { + type: "token", + symbol: "ETH", + name: "Ethereum", + address: "0xeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee", + decimals: 18 + }, + dai: { + type: "token", + symbol: "DAI", + name: "DAI Stable", + address: "0xd586e7f844cea2f87f50152665bcbc2c279d8d70", + decimals: 18 + }, + usdc: { + type: "token", + symbol: "USDC", + name: "USD Coin", + address: "0xa7d7079b0fead91f3e65f86e8915cb59c1a4c664", + decimals: 6 + }, + weth: { + type: "token", + symbol: "WETH", + name: "Wrapped ETH", + address: "0x82aF49447D8a07e3bd95BD0d56f35241523fBab1", + decimals: 6 + } +}; + +export const tokenMapping: Record = { + usdc: { + impersonateSigner: "0xce2cc46682e9c6d5f174af598fb4931a9c0be68e", + address: "0xa7d7079b0fead91f3e65f86e8915cb59c1a4c664", + abi: ["function mint(address _to, uint256 _amount) external returns (bool);"], + process: async function (owner: Signer | Provider, to: any, amt: any) { + const contract = new ethers.Contract(this.address, this.abi, owner); + + await mineTx(contract.mint(to, amt)); + } + }, + dai: { + impersonateSigner: "0xc5ed2333f8a2c351fca35e5ebadb2a82f5d254c3", + abi: ["function transfer(address to, uint value)"], + address: "0xd586e7f844cea2f87f50152665bcbc2c279d8d70", + process: async function (owner: Signer | Provider, to: any, amt: any) { + const contract = new ethers.Contract(this.address, this.abi, owner); + await mineTx(contract.transfer(to, amt)); + } + } +}; diff --git a/scripts/tests/avalanche/addresses.ts b/scripts/tests/avalanche/addresses.ts new file mode 100644 index 0000000..e3919fb --- /dev/null +++ b/scripts/tests/avalanche/addresses.ts @@ -0,0 +1,11 @@ +export const addresses: Record = { + connectors: { + // basic: "0x6214f9c4F9700fc7a50B5f9aEEB819d647406Ac7", + // auth: "0xD6daA927ad756a4022858dddcc4E26137b30DB4D", + // "INSTAPOOL-A": "0x8f1e38c53af7bD2b2bE01b9580911b7Cca504F1b", + }, + core: { + connectorsV2: "0x127d8cD0E2b2E0366D522DeA53A787bfE9002C14", + instaIndex: "0x6CE3e607C808b4f4C26B7F6aDAeB619e49CAbb25", + }, +}; diff --git a/scripts/tests/avalanche/tokens.ts b/scripts/tests/avalanche/tokens.ts new file mode 100644 index 0000000..f35ad29 --- /dev/null +++ b/scripts/tests/avalanche/tokens.ts @@ -0,0 +1,87 @@ +import { Provider } from "@ethersproject/abstract-provider"; +import { Signer } from "@ethersproject/abstract-signer"; +import { ethers } from "hardhat"; + +const mineTx = async (tx: any) => { + await (await tx).wait(); +}; + +export const tokens = { + eth: { + type: "token", + symbol: "ETH", + name: "Ethereum", + address: "0xeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee", + decimals: 18, + }, + dai: { + type: "token", + symbol: "DAI", + name: "DAI Stable", + address: "0xd586e7f844cea2f87f50152665bcbc2c279d8d70", + decimals: 18, + }, + usdc: { + type: "token", + symbol: "USDC", + name: "USD Coin", + address: "0xa7d7079b0fead91f3e65f86e8915cb59c1a4c664", + decimals: 6, + }, +}; + +export const tokenMapping: Record = { + usdc: { + impersonateSigner: "0xc5ed2333f8a2c351fca35e5ebadb2a82f5d254c3", + address: "0xa7d7079b0fead91f3e65f86e8915cb59c1a4c664", + abi: [ + "function mint(address _to, uint256 _amount) external returns (bool);", + ], + process: async function (owner: Signer | Provider, to: any, amt: any) { + const contract = new ethers.Contract(this.address, this.abi, owner); + + await mineTx(contract.mint(to, amt)); + }, + }, + dai: { + impersonateSigner: "0xed2a7edd7413021d440b09d654f3b87712abab66", + address: "0xd586e7f844cea2f87f50152665bcbc2c279d8d70", + abi: ["function transfer(address to, uint value)"], + process: async function (owner: Signer | Provider, to: any, amt: any) { + const contract = new ethers.Contract(this.address, this.abi, owner); + await mineTx(contract.transfer(to, amt)); + }, + }, + usdt: { + impersonateSigner: "0xc5ed2333f8a2c351fca35e5ebadb2a82f5d254c3", + address: "0xc7198437980c041c805a1edcba50c1ce5db95118", + abi: [ + "function issue(uint amount)", + "function transfer(address to, uint value)", + ], + process: async function (owner: Signer | Provider, address: any, amt: any) { + const contract = new ethers.Contract(this.address, this.abi, owner); + + await mineTx(contract.issue(amt)); + await mineTx(contract.transfer(address, amt)); + }, + }, + wbtc: { + impersonateSigner: "0x63cdb19c13497383726ad6bbf7c6b6cf725a3164", + address: "0x50b7545627a5162f82a992c33b87adc75187b218", + abi: ["function mint(address _to, uint256 _amount) public returns (bool)"], + process: async function (owner: Signer | Provider, address: any, amt: any) { + const contract = new ethers.Contract(this.address, this.abi, owner); + await mineTx(contract.mint(address, amt)); + }, + }, + // inst: { + // impersonateSigner: "0x75e89d5979E4f6Fba9F97c104c2F0AFB3F1dcB88", + // address: "0x6f40d4a6237c257fff2db00fa0510deeecd303eb", + // abi: ["function transfer(address to, uint value)"], + // process: async function (owner: Signer | Provider, address: any, amt: any) { + // const contract = new ethers.Contract(this.address, this.abi, owner); + // await mineTx(contract.transfer(address, amt)); + // }, + // }, +}; diff --git a/scripts/tests/buildDSAv2.ts b/scripts/tests/buildDSAv2.ts new file mode 100644 index 0000000..311a5f0 --- /dev/null +++ b/scripts/tests/buildDSAv2.ts @@ -0,0 +1,33 @@ +import { ethers } from "hardhat"; + +import { addresses as addressesPolygon } from "./polygon/addresses"; +import { addresses as addressesArbitrum } from "./arbitrum/addresses"; +import { addresses as addressesAvalanche } from "./avalanche/addresses"; +import { addresses as addressesOptimism } from "./optimism/addresses"; +import { addresses as addressesFantom } from "./fantom/addresses"; +import { addresses } from "./mainnet/addresses"; +import { abis } from "../constant/abis"; +import { abi } from "../../deployements/mainnet/Implementation_m1.sol/InstaImplementationM1.json"; + +function getAddress(network: string | undefined) { + if (network === "polygon") return addressesPolygon.core.instaIndex; + else if (network === "arbitrum") return addressesArbitrum.core.instaIndex; + else if (network === "avalanche") return addressesAvalanche.core.instaIndex; + else if (network === "optimism") return addressesOptimism.core.instaIndex; + else if (network === "fantom") return addressesFantom.core.instaIndex; + else return addresses.core.instaIndex; +} + +export async function buildDSAv2(owner: any) { + const instaIndex = await ethers.getContractAt( + abis.core.instaIndex, + getAddress(String(process.env.networkType)) + ); + + const tx = await instaIndex.build(owner, 2, owner); + const receipt = await tx.wait(); + const event = receipt.events.find( + (a: { event: string }) => a.event === "LogAccountCreated" + ); + return await ethers.getContractAt(abi, event.args.account); +} diff --git a/scripts/tests/command.ts b/scripts/tests/command.ts new file mode 100644 index 0000000..51a0723 --- /dev/null +++ b/scripts/tests/command.ts @@ -0,0 +1,33 @@ +import { execFile, spawn } from "child_process"; + +interface ICommand { + readonly cmd: string; + readonly args: string[]; + readonly env: { + [param: string]: string; + }; +} + +export async function execScript(input: ICommand): Promise { + return new Promise((resolve, reject) => { + let cmdEnv = Object.create(process.env); + for (let param in input.env) { + cmdEnv[param] = input.env[param]; + } + + const proc = spawn(input.cmd, [...input.args], { + env: cmdEnv, + shell: true, + stdio: "inherit", + }); + + proc.on("exit", (code) => { + if (code !== 0) { + reject(code); + return; + } + + resolve(code); + }); + }); +} diff --git a/scripts/tests/deployAndEnableConnector.ts b/scripts/tests/deployAndEnableConnector.ts new file mode 100644 index 0000000..7a713c6 --- /dev/null +++ b/scripts/tests/deployAndEnableConnector.ts @@ -0,0 +1,51 @@ +import { addresses as addressesPolygon } from "./polygon/addresses"; +import { addresses } from "./mainnet/addresses"; +import { abis } from "../constant/abis"; +import { addresses as addressesArbitrum } from "./arbitrum/addresses"; +import { addresses as addressesAvalanche } from "./avalanche/addresses"; +import { addresses as addressesOptimism } from "./optimism/addresses"; +import { addresses as addressesFantom } from "./fantom/addresses"; + +import hre from "hardhat"; +import type { Signer, Contract } from "ethers"; +import type { ContractJSON } from "ethereum-waffle/dist/esm/ContractJSON"; + +const { ethers, waffle } = hre; +const { deployContract } = waffle; + +interface DeployInterface { + connectorName: string; + contractArtifact: ContractJSON; + signer: Signer; + connectors: Contract; +} + +function getAddress(network: string | undefined) { + if (network === "polygon") return addressesPolygon; + else if (network === "arbitrum") return addressesArbitrum; + else if (network === "avalanche") return addressesAvalanche; + else if (network === "optimism") return addressesOptimism; + else if (network === "fantom") return addressesFantom; + else return addresses; +} + +export async function deployAndEnableConnector( + { + connectorName, + contractArtifact, + signer, + connectors + } : DeployInterface +) { + const connectorInstanace: Contract = await deployContract(signer, contractArtifact); + + await connectors + .connect(signer) + .addConnectors([connectorName], [connectorInstanace.address]); + + getAddress(String(process.env.networkType)).connectors[connectorName] = + connectorInstanace.address; + abis.connectors[connectorName] = contractArtifact.abi; + + return connectorInstanace; +} diff --git a/scripts/tests/encodeFlashcastData.ts b/scripts/tests/encodeFlashcastData.ts new file mode 100644 index 0000000..759b9de --- /dev/null +++ b/scripts/tests/encodeFlashcastData.ts @@ -0,0 +1,13 @@ +import hre from "hardhat"; +const { web3 } = hre; + +import { encodeSpells } from "./encodeSpells"; + +export default function encodeFlashcastData(spells: any) { + const encodeSpellsData = encodeSpells(spells); + let argTypes = ["string[]", "bytes[]"]; + return web3.eth.abi.encodeParameters(argTypes, [ + encodeSpellsData[0], + encodeSpellsData[1], + ]); +}; diff --git a/scripts/tests/encodeSpells.ts b/scripts/tests/encodeSpells.ts new file mode 100644 index 0000000..62a7a8b --- /dev/null +++ b/scripts/tests/encodeSpells.ts @@ -0,0 +1,17 @@ +import { web3 } from "hardhat"; +import { abis } from "../constant/abis"; + +export function encodeSpells(spells: any[]) { + const targets = spells.map((a) => a.connector); + const calldatas = spells.map((a) => { + const functionName = a.method; + // console.log(functionName) + const abi = abis.connectors[a.connector].find((b: { name: any }) => { + return b.name === functionName; + }); + // console.log(functionName) + if (!abi) throw new Error("Couldn't find function"); + return web3.eth.abi.encodeFunctionCall(abi, a.args); + }); + return [targets, calldatas]; +} diff --git a/scripts/tests/fantom/addresses.ts b/scripts/tests/fantom/addresses.ts new file mode 100644 index 0000000..53113e1 --- /dev/null +++ b/scripts/tests/fantom/addresses.ts @@ -0,0 +1,11 @@ +export const addresses: Record = { + connectors: { + // basic: "0x6214f9c4F9700fc7a50B5f9aEEB819d647406Ac7", + // auth: "0xD6daA927ad756a4022858dddcc4E26137b30DB4D", + // "INSTAPOOL-A": "0x8f1e38c53af7bD2b2bE01b9580911b7Cca504F1b", + }, + core: { + connectorsV2: "0x819910794a030403F69247E1e5C0bBfF1593B968", + instaIndex: "0x2fa042BEEB7A40A7078EaA5aC755e3842248292b", + }, +}; diff --git a/scripts/tests/fantom/tokens.ts b/scripts/tests/fantom/tokens.ts new file mode 100644 index 0000000..1752391 --- /dev/null +++ b/scripts/tests/fantom/tokens.ts @@ -0,0 +1,64 @@ +import { Provider } from "@ethersproject/abstract-provider"; +import { Signer } from "@ethersproject/abstract-signer"; +import { ethers } from "hardhat"; + +const mineTx = async (tx: any) => { + await (await tx).wait(); +}; + +export const tokens = { + ftm: { + type: "token", + symbol: "FTM", + name: "Fantom", + address: "0xeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee", + decimals: 18, + }, + dai: { + type: "token", + symbol: "DAI", + name: "DAI Stable", + address: "0x8D11eC38a3EB5E956B052f67Da8Bdc9bef8Abf3E", + decimals: 18, + }, + usdc: { + type: "token", + symbol: "USDC", + name: "USD Coin", + address: "0x04068DA6C83AFCFA0e13ba15A6696662335D5B75", + decimals: 6, + }, +}; + +export const tokenMapping: Record = { + usdc: { + impersonateSigner: "0x4188663a85C92EEa35b5AD3AA5cA7CeB237C6fe9", + address: "0x04068DA6C83AFCFA0e13ba15A6696662335D5B75", + abi: [ + "function mint(address _to, uint256 _amount) external returns (bool);", + ], + process: async function (owner: Signer | Provider, to: any, amt: any) { + const contract = new ethers.Contract(this.address, this.abi, owner); + + await mineTx(contract.mint(to, amt)); + }, + }, + dai: { + impersonateSigner: "0x9bdB521a97E95177BF252C253E256A60C3e14447", + address: "0x8D11eC38a3EB5E956B052f67Da8Bdc9bef8Abf3E", + abi: ["function transfer(address to, uint value)"], + process: async function (owner: Signer | Provider, to: any, amt: any) { + const contract = new ethers.Contract(this.address, this.abi, owner); + await mineTx(contract.transfer(to, amt)); + }, + }, + // inst: { + // impersonateSigner: "0x75e89d5979E4f6Fba9F97c104c2F0AFB3F1dcB88", + // address: "0x6f40d4a6237c257fff2db00fa0510deeecd303eb", + // abi: ["function transfer(address to, uint value)"], + // process: async function (owner: Signer | Provider, address: any, amt: any) { + // const contract = new ethers.Contract(this.address, this.abi, owner); + // await mineTx(contract.transfer(address, amt)); + // }, + // }, +}; diff --git a/scripts/tests/getMasterSigner.ts b/scripts/tests/getMasterSigner.ts new file mode 100644 index 0000000..f97a21d --- /dev/null +++ b/scripts/tests/getMasterSigner.ts @@ -0,0 +1,39 @@ +import { ethers, network } from "hardhat"; +import { addresses } from "./mainnet/addresses"; +import { addresses as addressesPolygon } from "./polygon/addresses"; +import { addresses as addressesArbitrum } from "./arbitrum/addresses"; +import { addresses as addressesAvalanche } from "./avalanche/addresses"; +import { addresses as addressesOptimism } from "./optimism/addresses"; +import { addresses as addressesFantom } from "./fantom/addresses"; +import { abis } from "../constant/abis"; + +function getAddress(network: string | undefined) { + if (network === "polygon") return addressesPolygon.core.instaIndex; + else if (network === "arbitrum") return addressesArbitrum.core.instaIndex; + else if (network === "avalanche") return addressesAvalanche.core.instaIndex; + else if (network === "optimism") return addressesOptimism.core.instaIndex; + else if (network === "fantom") return addressesFantom.core.instaIndex; + else return addresses.core.instaIndex; +} + +export async function getMasterSigner() { + const [_, __, ___, wallet3] = await ethers.getSigners(); + const instaIndex = new ethers.Contract( + getAddress(String(process.env.networkType)), + abis.core.instaIndex, + wallet3 + ); + + const masterAddress = await instaIndex.master(); + await network.provider.request({ + method: "hardhat_impersonateAccount", + params: [masterAddress], + }); + + await network.provider.send("hardhat_setBalance", [ + masterAddress, + "0x8ac7230489e80000", // 1e19 wei + ]); + + return await ethers.getSigner(masterAddress); +} diff --git a/scripts/tests/global-test.ts b/scripts/tests/global-test.ts new file mode 100644 index 0000000..4c137c3 --- /dev/null +++ b/scripts/tests/global-test.ts @@ -0,0 +1,44 @@ +import { promises as fs } from "fs"; + +import { join } from "path"; +import { execScript } from "./command"; + +let start: number, end: number; + +async function testRunner() { + const chain = ["avalanche", "mainnet", "polygon", "arbitrum", "optimism"]; + start = Date.now(); + + for (let ch of chain) { + console.log(`📗Running test for %c${ch}: `, "blue"); + let path: string; + const testsPath = join(__dirname, "../../test", ch); + await fs.access(testsPath); + const availableTests = await fs.readdir(testsPath); + + if (availableTests.length !== 0) { + for (let test of availableTests) { + path = join(testsPath, test); + path += "/*"; + await execScript({ + cmd: "npx", + args: ["hardhat", "test", path], + env: { + networkType: ch, + }, + }); + } + } + } + + end = Date.now(); +} + +testRunner() + .then(() => + console.log( + `🙌 finished running the test, total time taken ${(end - start) / + 1000} sec` + ) + ) + .catch((err) => console.error("❌ failed due to error: ", err)); diff --git a/scripts/tests/impersonate.ts b/scripts/tests/impersonate.ts new file mode 100644 index 0000000..29f010d --- /dev/null +++ b/scripts/tests/impersonate.ts @@ -0,0 +1,14 @@ +import { ethers, network } from "hardhat"; + +export const impersonateAccounts = async (accounts: any) => { + const signers = []; + for (const account of accounts) { + await network.provider.request({ + method: "hardhat_impersonateAccount", + params: [account], + }); + + signers.push(await ethers.getSigner(account)); + } + return signers; +}; diff --git a/scripts/tests/mainnet/addresses.ts b/scripts/tests/mainnet/addresses.ts new file mode 100644 index 0000000..f3e74d2 --- /dev/null +++ b/scripts/tests/mainnet/addresses.ts @@ -0,0 +1,11 @@ +export const addresses: Record = { + connectors: { + "basic": "0xe5398f279175962E56fE4c5E0b62dc7208EF36c6", + "auth": "0xd1aff9f2acf800c876c409100d6f39aea93fc3d9", + "INSTAPOOL-A": "0x5806af7ab22e2916fa582ff05731bf7c682387b2", + }, + core: { + "connectorsV2": "0x97b0B3A8bDeFE8cB9563a3c610019Ad10DB8aD11", + "instaIndex": "0x2971AdFa57b20E5a416aE5a708A8655A9c74f723", + }, +}; diff --git a/scripts/tests/mainnet/tokens.ts b/scripts/tests/mainnet/tokens.ts new file mode 100644 index 0000000..a8a06e5 --- /dev/null +++ b/scripts/tests/mainnet/tokens.ts @@ -0,0 +1,164 @@ +import { Provider } from "@ethersproject/abstract-provider"; +import { Signer } from "@ethersproject/abstract-signer"; +import { ethers } from "hardhat"; + +const mineTx = async (tx: any) => { + await (await tx).wait(); +}; + +export const tokens = { + eth: { + type: "token", + symbol: "ETH", + name: "Ethereum", + address: "0xeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee", + aTokenAddress: "0x030bA81f1c18d280636F32af80b9AAd02Cf0854e", + cTokenAddress: "0x4Ddc2D193948926D02f9B1fE9e1daa0718270ED5", + decimals: 18 + }, + dai: { + type: "token", + symbol: "DAI", + name: "DAI Stable", + address: "0x6B175474E89094C44Da98b954EedeAC495271d0F", + aTokenAddress: "0x028171bCA77440897B824Ca71D1c56caC55b68A3", + cTokenAddress: "0x5d3a536E4D6DbD6114cc1Ead35777bAB948E3643", + decimals: 18 + }, + usdc: { + type: "token", + symbol: "USDC", + name: "USD Coin", + address: "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48", + aTokenAddress: "0xBcca60bB61934080951369a648Fb03DF4F96263C", + cTokenAddress: "0x39AA39c021dfbaE8faC545936693aC917d5E7563", + decimals: 6 + }, + weth: { + type: "token", + symbol: "WETH", + name: "Wrapped Ether", + address: "0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2", + aTokenAddress: "0x030bA81f1c18d280636F32af80b9AAd02Cf0854e", + cTokenAddress: "0x4Ddc2D193948926D02f9B1fE9e1daa0718270ED5", + decimals: 18 + }, + ens: { + type: "token", + symbol: "ENS", + name: "Etherem Name Services", + address: "0xC18360217D8F7Ab5e7c516566761Ea12Ce7F9D72", + aTokenAddress: "0x9a14e23A58edf4EFDcB360f68cd1b95ce2081a2F", + decimals: 18 + }, + comp: { + type: "token", + symbol: "COMP", + name: "Compound", + address: "0xc00e94Cb662C3520282E6f5717214004A7f26888", + cTokenAddress: "0x70e36f6BF80a52b3B46b3aF8e106CC0ed743E8e4", + decimals: 18 + }, + link: { + type: "token", + symbol: "LINK", + name: "ChainLink Token", + address: "0x514910771AF9Ca656af840dff83E8264EcF986CA", + aTokenAddress: "0xa06bC25B5805d5F8d82847D191Cb4Af5A3e873E0", + cTokenAddress: "0xFAce851a4921ce59e912d19329929CE6da6EB0c7", + decimals: 18 + }, + uni: { + type: "token", + symbol: "UNI", + name: "Uniswap", + address: "0x1f9840a85d5aF5bf1D1762F925BDADdC4201F984", + aTokenAddress: "0xB9D7CB55f463405CDfBe4E90a6D2Df01C2B92BF1", + cTokenAddress: "0x35A18000230DA775CAc24873d00Ff85BccdeD550", + decimals: 18 + }, + crvusd: { + type: "token", + symbol: "crvUSD", + name: "Curve.Fi USD Stablecoin", + address: "0xf939E0A03FB07F59A73314E73794Be0E57ac1b4E", + decimals: 18 + }, + sfrxeth: { + type: "token", + symbol: "sfrxETH", + name: "Staked Frax Ether", + address: "0xac3E018457B222d93114458476f3E3416Abbe38F", + decimals: 18 + }, + wsteth: { + type: "token", + symbol: "wstETH", + name: "Wrapped liquid staked Ether 2.0", + address: "0x7f39C581F595B53c5cb19bD0b3f8dA6c935E2Ca0", + decimals: 18 + }, + wbtc: { + type: "token", + symbol: "WBTC", + name: "Wrapped BTC", + address: "0x2260FAC5E5542a773Aa44fBCfeDf7C193bc2C599", + decimals: 8 + }, +}; + +export const dsaMaxValue = "115792089237316195423570985008687907853269984665640564039457584007913129639935"; + +export const tokenMapping: Record = { + usdc: { + impersonateSigner: "0xfcb19e6a322b27c06842a71e8c725399f049ae3a", + address: "0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48", + abi: [ + "function mint(address _to, uint256 _amount) external returns (bool)", + "function balanceOf(address user) external returns (uint256)" + ], + process: async function (owner: Signer | Provider, to: any, amt: any) { + const contract = new ethers.Contract(this.address, this.abi, owner); + + await mineTx(contract.mint(to, amt)); + } + }, + dai: { + impersonateSigner: "0x47ac0fb4f2d84898e4d9e7b4dab3c24507a6d503", + abi: ["function transfer(address to, uint value)"], + address: "0x6b175474e89094c44da98b954eedeac495271d0f", + process: async function (owner: Signer | Provider, to: any, amt: any) { + const contract = new ethers.Contract(this.address, this.abi, owner); + await mineTx(contract.transfer(to, amt)); + } + }, + usdt: { + impersonateSigner: "0xc6cde7c39eb2f0f0095f41570af89efc2c1ea828", + address: "0xdac17f958d2ee523a2206206994597c13d831ec7", + abi: ["function issue(uint amount)", "function transfer(address to, uint value)"], + process: async function (owner: Signer | Provider, address: any, amt: any) { + const contract = new ethers.Contract(this.address, this.abi, owner); + + await mineTx(contract.issue(amt)); + await mineTx(contract.transfer(address, amt)); + } + }, + wbtc: { + impersonateSigner: "0xCA06411bd7a7296d7dbdd0050DFc846E95fEBEB7", + address: "0x2260fac5e5542a773aa44fbcfedf7c193bc2c599", + abi: ["function mint(address _to, uint256 _amount) public returns (bool)"], + process: async function (owner: Signer | Provider, address: any, amt: any) { + const contract = new ethers.Contract(this.address, this.abi, owner); + await mineTx(contract.mint(address, amt)); + } + }, + inst: { + impersonateSigner: "0x75e89d5979E4f6Fba9F97c104c2F0AFB3F1dcB88", + address: "0x6f40d4a6237c257fff2db00fa0510deeecd303eb", + abi: ["function transfer(address to, uint value)"], + process: async function (owner: Signer | Provider, address: any, amt: any) { + const contract = new ethers.Contract(this.address, this.abi, owner); + await mineTx(contract.transfer(address, amt)); + } + } +}; diff --git a/scripts/tests/optimism/addresses.ts b/scripts/tests/optimism/addresses.ts new file mode 100644 index 0000000..ecd46a5 --- /dev/null +++ b/scripts/tests/optimism/addresses.ts @@ -0,0 +1,10 @@ +export const addresses: Record = { + connectors: { + basic: "0xe5398f279175962E56fE4c5E0b62dc7208EF36c6", + auth: "0xd1aff9f2acf800c876c409100d6f39aea93fc3d9", + }, + core: { + connectorsV2: "0x127d8cD0E2b2E0366D522DeA53A787bfE9002C14", + instaIndex: "0x6CE3e607C808b4f4C26B7F6aDAeB619e49CAbb25", + }, +}; \ No newline at end of file diff --git a/scripts/tests/optimism/tokens.ts b/scripts/tests/optimism/tokens.ts new file mode 100644 index 0000000..8d72f1d --- /dev/null +++ b/scripts/tests/optimism/tokens.ts @@ -0,0 +1,88 @@ +import { Provider } from "@ethersproject/abstract-provider"; +import { Signer } from "@ethersproject/abstract-signer"; +import { ethers } from "hardhat"; + +const mineTx = async (tx: any) => { + await (await tx).wait(); +}; + +export const tokens = { + eth: { + type: "token", + symbol: "ETH", + name: "Eth", + address: "0xeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee", + decimals: 18 + }, + dai: { + type: "token", + symbol: "DAI", + name: "DAI Stable", + address: "0x8f3Cf7ad23Cd3CaDbD9735AFf958023239c6A063", + decimals: 18 + }, + usdc: { + type: "token", + symbol: "USDC", + name: "USD Coin", + address: "0x2791Bca1f2de4661ED88A30C99A7a9449Aa84174", + decimals: 6 + }, + usdt: { + type: "token", + symbol: "USDT", + name: "Tether USD Coin", + address: "0x94b008aA00579c1307B0EF2c499aD98a8ce58e58", + decimals: 6 + } +}; + +export const tokenMapping: Record = { + usdc: { + impersonateSigner: "0x31efc4aeaa7c39e54a33fdc3c46ee2bd70ae0a09", + address: "0x2791Bca1f2de4661ED88A30C99A7a9449Aa84174", + abi: ["function mint(address _to, uint256 _amount) external returns (bool);"], + process: async function (owner: Signer | Provider, to: any, amt: any) { + const contract = new ethers.Contract(this.address, this.abi, owner); + + await mineTx(contract.mint(to, amt)); + } + }, + dai: { + impersonateSigner: "0x360537542135943E8Fc1562199AEA6d0017F104B", + address: "0xDA10009cBd5D07dd0CeCc66161FC93D7c9000da1", + abi: ["function transfer(address to, uint value)"], + process: async function (owner: Signer | Provider, to: any, amt: any) { + const contract = new ethers.Contract(this.address, this.abi, owner); + await mineTx(contract.transfer(to, amt)); + } + }, + usdt: { + impersonateSigner: "0xc858a329bf053be78d6239c4a4343b8fbd21472b", + address: "0x94b008aA00579c1307B0EF2c499aD98a8ce58e58", + abi: ["function issue(uint amount)", "function transfer(address to, uint value)"], + process: async function (owner: Signer | Provider, address: any, amt: any) { + const contract = new ethers.Contract(this.address, this.abi, owner); + await mineTx(contract.issue(amt)); + await mineTx(contract.transfer(address, amt)); + } + }, + wbtc: { + impersonateSigner: "0x3aa76aa74bdfa09d68d9ebeb462c5f40d727283f", + address: "0x68f180fcCe6836688e9084f035309E29Bf0A2095", + abi: ["function mint(address _to, uint256 _amount) public returns (bool)"], + process: async function (owner: Signer | Provider, address: any, amt: any) { + const contract = new ethers.Contract(this.address, this.abi, owner); + await mineTx(contract.mint(address, amt)); + } + } + // inst: { + // impersonateSigner: "0xf1f22f25f748f79263d44735198e023b72806ab1", + // address: "0x6f40d4A6237C257fff2dB00FA0510DeEECd303eb", + // abi: ["function transfer(address to, uint value)"], + // process: async function (owner: Signer | Provider, address: any, amt: any) { + // const contract = new ethers.Contract(this.address, this.abi, owner); + // await mineTx(contract.transfer(address, amt)); + // }, + // }, +}; diff --git a/scripts/tests/polygon/addresses.ts b/scripts/tests/polygon/addresses.ts new file mode 100644 index 0000000..7c13c34 --- /dev/null +++ b/scripts/tests/polygon/addresses.ts @@ -0,0 +1,10 @@ +export const addresses: Record = { + connectors: { + basic: "0x1cAF5EC802ca602E98139AD96A8f2B7BC524264E", + auth: "0xf6474aD0dA75A0dE15D2c915e601D9f754B9e6fe", + }, + core: { + connectorsV2: "0x2A00684bFAb9717C21271E0751BCcb7d2D763c88", + instaIndex: "0xA9B99766E6C676Cf1975c0D3166F96C0848fF5ad", + }, +}; \ No newline at end of file diff --git a/scripts/tests/polygon/tokens.ts b/scripts/tests/polygon/tokens.ts new file mode 100644 index 0000000..2b30e42 --- /dev/null +++ b/scripts/tests/polygon/tokens.ts @@ -0,0 +1,101 @@ +import { Provider } from "@ethersproject/abstract-provider"; +import { Signer } from "@ethersproject/abstract-signer"; +import { ethers } from "hardhat"; + +const mineTx = async (tx: any) => { + await (await tx).wait(); +}; + +export const tokens = { + matic: { + type: "token", + symbol: "MATIC", + name: "Matic", + address: "0xeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee", + decimals: 18, + }, + wmatic: { + type: "token", + symbol: "WMATIC", + name: "Wrapped Matic", + address: "0x0d500B1d8E8eF31E21C99d1Db9A6444d3ADf1270", + decimals: 18, + }, + eth: { + type: "token", + symbol: "ETH", + name: "Ethereum", + address: "0x7ceb23fd6bc0add59e62ac25578270cff1b9f619", + decimals: 18, + }, + dai: { + type: "token", + symbol: "DAI", + name: "DAI Stable", + address: "0x8f3Cf7ad23Cd3CaDbD9735AFf958023239c6A063", + decimals: 18, + }, + usdc: { + type: "token", + symbol: "USDC", + name: "USD Coin", + address: "0x2791Bca1f2de4661ED88A30C99A7a9449Aa84174", + decimals: 6, + }, +}; + +export const tokenMapping: Record = { + usdc: { + impersonateSigner: "0x6e7a5fafcec6bb1e78bae2a1f0b612012bf14827", + address: "0x2791Bca1f2de4661ED88A30C99A7a9449Aa84174", + abi: [ + "function mint(address _to, uint256 _amount) external returns (bool);", + ], + process: async function (owner: Signer | Provider, to: any, amt: any) { + const contract = new ethers.Contract(this.address, this.abi, owner); + + await mineTx(contract.mint(to, amt)); + }, + }, + dai: { + impersonateSigner: "0x4a35582a710e1f4b2030a3f826da20bfb6703c09", + address: "0x8f3Cf7ad23Cd3CaDbD9735AFf958023239c6A063", + abi: ["function transfer(address to, uint value)"], + process: async function (owner: Signer | Provider, to: any, amt: any) { + const contract = new ethers.Contract(this.address, this.abi, owner); + await mineTx(contract.transfer(to, amt)); + }, + }, + usdt: { + impersonateSigner: "0x0d0707963952f2fba59dd06f2b425ace40b492fe", + address: "0xc2132d05d31c914a87c6611c10748aeb04b58e8f", + abi: [ + "function issue(uint amount)", + "function transfer(address to, uint value)", + ], + process: async function (owner: Signer | Provider, address: any, amt: any) { + const contract = new ethers.Contract(this.address, this.abi, owner); + + await mineTx(contract.issue(amt)); + await mineTx(contract.transfer(address, amt)); + }, + }, + wbtc: { + impersonateSigner: "0xdc9232e2df177d7a12fdff6ecbab114e2231198d", + address: "0x1bfd67037b42cf73acf2047067bd4f2c47d9bfd6", + abi: ["function mint(address _to, uint256 _amount) public returns (bool)"], + process: async function (owner: Signer | Provider, address: any, amt: any) { + const contract = new ethers.Contract(this.address, this.abi, owner); + await mineTx(contract.mint(address, amt)); + }, + }, + inst: { + impersonateSigner: "0xf1f22f25f748f79263d44735198e023b72806ab1", + address: "0xf50d05a1402d0adafa880d36050736f9f6ee7dee", + abi: ["function transfer(address to, uint value)"], + process: async function (owner: Signer | Provider, address: any, amt: any) { + const contract = new ethers.Contract(this.address, this.abi, owner); + await mineTx(contract.transfer(address, amt)); + }, + }, +}; diff --git a/scripts/tests/run-tests.ts b/scripts/tests/run-tests.ts new file mode 100644 index 0000000..d8edc08 --- /dev/null +++ b/scripts/tests/run-tests.ts @@ -0,0 +1,68 @@ +import inquirer from "inquirer"; +import { promises as fs } from "fs"; + +import { join } from "path"; +import { execScript } from "./command"; + +let start: number, end: number; + +async function testRunner() { + const { chain } = await inquirer.prompt([ + { + name: "chain", + message: "What chain do you want to run tests on?", + type: "list", + choices: ["mainnet", "polygon", "avalanche", "arbitrum", "optimism", "fantom"], + }, + ]); + const testsPath = join(__dirname, "../../test", chain); + await fs.access(testsPath); + const availableTests = await fs.readdir(testsPath); + if (availableTests.length === 0) { + throw new Error(`No tests available for ${chain}`); + } + + const { testName } = await inquirer.prompt([ + { + name: "testName", + message: "For which connector you want to run the tests?", + type: "list", + choices: ["all", ...availableTests], + }, + ]); + start = Date.now(); + let path: string; + if (testName === "all") { + for (let test of availableTests) { + path = join(testsPath, test); + path += "/*"; + await execScript({ + cmd: "npx", + args: ["hardhat", "test", path], + env: { + networkType: chain, + }, + }); + } + } else { + path = join(testsPath, testName); + path += "/*"; + + await execScript({ + cmd: "npx", + args: ["hardhat", "test", path], + env: { + networkType: chain, + }, + }); + } + end = Date.now(); +} + +testRunner() + .then(() => + console.log( + `🙌 finished the test runner, time taken ${(end - start) / 1000} sec` + ) + ) + .catch((err) => console.error("❌ failed due to error: ", err)); diff --git a/scripts/tests/run_test_through_cmd.ts b/scripts/tests/run_test_through_cmd.ts new file mode 100644 index 0000000..6fad438 --- /dev/null +++ b/scripts/tests/run_test_through_cmd.ts @@ -0,0 +1,63 @@ +import { promises as fs } from "fs"; + +import { join } from "path"; +import { execScript } from "./command"; + +import { task } from "hardhat/config"; + + +let start: number, end: number; + +task("run_tests", "runs specified test on a specified chain") +.addPositionalParam("chain") +.addPositionalParam("test") +.setAction(async (taskArgs) => { + const chain = taskArgs.chain; + const test = taskArgs.test; + await testRunner(chain,test) + .then(() => + console.log( + `🙌 finished the test runner, time taken ${(end - start) / 1000} sec` + ) + ) + .catch((err) => console.error("❌ failed due to error: ", err)); + +}); + +async function testRunner(chain: string, testName: string) { + + const testsPath = join(__dirname, "../../test", chain); + await fs.access(testsPath); + const availableTests = await fs.readdir(testsPath); + if (availableTests.length === 0) { + throw new Error(`No tests available for ${chain}`); + } + + start = Date.now(); + let path: string; + if (testName === "all") { + for (let test of availableTests) { + path = join(testsPath, test); + path += "/*"; + await execScript({ + cmd: "npx", + args: ["hardhat", "test", path], + env: { + networkType: chain, + }, + }).catch((err)=>console.log(`failed ${test}`)) + } + } else { + path = join(testsPath, testName); + path += "/*"; + + await execScript({ + cmd: "npx", + args: ["hardhat", "test", path], + env: { + networkType: chain, + }, + }); + } + end = Date.now(); +} \ No newline at end of file diff --git a/test/.gitkeep b/test/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/test/arbitrum/aave/aaveV3-import-test.ts b/test/arbitrum/aave/aaveV3-import-test.ts new file mode 100644 index 0000000..88ce453 --- /dev/null +++ b/test/arbitrum/aave/aaveV3-import-test.ts @@ -0,0 +1,303 @@ +import { expect, should } from "chai"; +import hre, { ethers, waffle } from "hardhat"; +import type { Signer, Contract } from "ethers"; +import { ecsign, ecrecover, pubToAddress } from "ethereumjs-util"; +import { keccak256 } from "@ethersproject/keccak256"; +import { defaultAbiCoder } from "@ethersproject/abi"; +import { BigNumber } from "bignumber.js"; +import { buildDSAv2 } from "../../../scripts/tests/buildDSAv2"; +import { addresses } from "../../../scripts/tests/arbitrum/addresses"; +import { deployAndEnableConnector } from "../../../scripts/tests/deployAndEnableConnector"; +import { abis } from "../../../scripts/constant/abis"; +import { getMasterSigner } from "../../../scripts/tests/getMasterSigner"; +import { parseEther, parseUnits } from "ethers/lib/utils"; +import { encodeSpells } from "../../../scripts/tests/encodeSpells"; +import encodeFlashcastData from "../../../scripts/tests/encodeFlashcastData"; +import { ConnectV2AaveV3ImportPermitArbitrum__factory, IERC20__factory } from "../../../typechain"; + +const ABI = [ + "function DOMAIN_SEPARATOR() public view returns (bytes32)", + "function balanceOf(address account) public view returns (uint256)", + "function nonces(address owner) public view returns (uint256)" +]; + +const aDaiAddress = "0x82E64f49Ed5EC1bC6e43DAD4FC8Af9bb3A2312EE"; +const aaveAddress = "0x794a61358D6845594F94dc1DB02A252b5b4814aD"; +let account = "0xc5ed2333f8a2c351fca35e5ebadb2a82f5d254c3"; +const DAI = "0xDA10009cBd5D07dd0CeCc66161FC93D7c9000da1"; +const USDC = "0xFF970A61A04b1cA14834A43f5dE4533eBDDB5CC8"; +const mnemonic = "test test test test test test test test test test test junk"; +const connectorName = "AAVE-V3-IMPORT-PERMIT-X"; +let signer: any, wallet0: any; + +const aaveAbi = [ + { + inputs: [ + { internalType: "address", name: "asset", type: "address" }, + { internalType: "uint256", name: "amount", type: "uint256" }, + { internalType: "uint256", name: "interestRateMode", type: "uint256" }, + { internalType: "uint16", name: "referralCode", type: "uint16" }, + { internalType: "address", name: "onBehalfOf", type: "address" } + ], + name: "borrow", + outputs: [], + stateMutability: "nonpayable", + type: "function" + }, + { + inputs: [ + { internalType: "address", name: "asset", type: "address" }, + { internalType: "uint256", name: "amount", type: "uint256" }, + { internalType: "address", name: "onBehalfOf", type: "address" }, + { internalType: "uint16", name: "referralCode", type: "uint16" } + ], + name: "supply", + outputs: [], + stateMutability: "nonpayable", + type: "function" + } +]; + +const erc20Abi = [ + { + constant: false, + inputs: [ + { + name: "_spender", + type: "address" + }, + { + name: "_value", + type: "uint256" + } + ], + name: "approve", + outputs: [ + { + name: "", + type: "bool" + } + ], + payable: false, + stateMutability: "nonpayable", + type: "function" + }, + { + constant: true, + inputs: [], + name: "totalSupply", + outputs: [ + { + name: "", + type: "uint256" + } + ], + payable: false, + stateMutability: "view", + type: "function" + }, + { + constant: true, + inputs: [ + { + name: "_owner", + type: "address" + } + ], + name: "balanceOf", + outputs: [ + { + name: "balance", + type: "uint256" + } + ], + payable: false, + stateMutability: "view", + type: "function" + }, + { + constant: false, + inputs: [ + { + name: "_to", + type: "address" + }, + { + name: "_value", + type: "uint256" + } + ], + name: "transfer", + outputs: [ + { + name: "", + type: "bool" + } + ], + payable: false, + stateMutability: "nonpayable", + type: "function" + } +]; + +const token = new ethers.Contract(DAI, erc20Abi); +const aDai = new ethers.Contract(aDaiAddress, ABI); +const usdcToken = new ethers.Contract(USDC, erc20Abi); +const aave = new ethers.Contract(aaveAddress, aaveAbi); + +describe("Import Aave v3 Position for Arbitrum", function () { + let dsaWallet0: any; + let masterSigner: Signer; + let instaConnectorsV2: Contract; + let connector: any; + + const wallet = ethers.Wallet.fromMnemonic(mnemonic); + + before(async () => { + await hre.network.provider.request({ + method: "hardhat_reset", + params: [ + { + forking: { + //@ts-ignore + jsonRpcUrl: hre.config.networks.hardhat.forking.url, + blockNumber: 9333600 + } + } + ] + }); + masterSigner = await getMasterSigner(); + [wallet0] = await ethers.getSigners(); + await hre.network.provider.send("hardhat_setBalance", [account, ethers.utils.parseEther("10").toHexString()]); + + await hre.network.provider.request({ + method: "hardhat_impersonateAccount", + params: [account] + }); + + signer = await ethers.getSigner(account); + + await token.connect(signer).transfer(wallet0.address, ethers.utils.parseEther("10")); + + instaConnectorsV2 = await ethers.getContractAt(abis.core.connectorsV2, addresses.core.connectorsV2); + connector = await deployAndEnableConnector({ + connectorName, + contractArtifact: ConnectV2AaveV3ImportPermitArbitrum__factory, + signer: masterSigner, + connectors: instaConnectorsV2 + }); + }); + + describe("check user AAVE position", async () => { + it("Should create Aave v3 position of DAI(collateral) and USDC(debt)", async () => { + // approve DAI to aavePool + await token.connect(wallet0).approve(aaveAddress, parseEther("10")); + + //deposit DAI in aave + await aave.connect(wallet0).supply(DAI, parseEther("10"), wallet.address, 3228); + console.log("Supplied DAI on aave"); + + //borrow USDC from aave + await aave.connect(wallet0).borrow(USDC, parseUnits("3", 6), 2, 3228, wallet.address); + console.log("Borrowed USDC from aave"); + }); + + it("Should check position of user", async () => { + expect(await aDai.connect(wallet0).balanceOf(wallet.address)).to.be.gte( + new BigNumber(10).multipliedBy(1e18).toString() + ); + + expect(await usdcToken.connect(wallet0).balanceOf(wallet.address)).to.be.gte( + new BigNumber(3).multipliedBy(1e6).toString() + ); + }); + }); + + describe("Deployment", async () => { + it("Should set correct name", async () => { + expect(await connector.name()).to.eq("Aave-v3-import-permit-v1"); + }); + }); + + describe("DSA wallet setup", async () => { + it("Should build DSA v2", async () => { + dsaWallet0 = await buildDSAv2(wallet.address); + expect(!!dsaWallet0.address).to.be.true; + }); + + it("Deposit ETH into DSA wallet", async function () { + await wallet0.sendTransaction({ + to: dsaWallet0.address, + value: ethers.utils.parseEther("5") + }); + + expect(await ethers.provider.getBalance(dsaWallet0.address)).to.be.gte(ethers.utils.parseEther("5")); + }); + }); + + describe("Aave position migration", async () => { + it("Should migrate Aave position", async () => { + const DOMAIN_SEPARATOR = await aDai.connect(wallet0).DOMAIN_SEPARATOR(); + const PERMIT_TYPEHASH = "0x6e71edae12b1b97f4d1f60370fef10105fa2faae0126114a169c64845d6126c9"; + + let nonce = (await aDai.connect(wallet0).nonces(wallet.address)).toNumber(); + //Approving max amount + const amount = ethers.constants.MaxUint256; + const expiry = Date.now() + 20 * 60; + + const digest = keccak256( + ethers.utils.solidityPack( + ["bytes1", "bytes1", "bytes32", "bytes32"], + [ + "0x19", + "0x01", + DOMAIN_SEPARATOR, + keccak256( + defaultAbiCoder.encode( + ["bytes32", "address", "address", "uint256", "uint256", "uint256"], + [PERMIT_TYPEHASH, wallet.address, dsaWallet0.address, amount, nonce, expiry] + ) + ) + ] + ) + ); + const { v, r, s } = ecsign(Buffer.from(digest.slice(2), "hex"), Buffer.from(wallet.privateKey.slice(2), "hex")); + const amount0 = new BigNumber(await usdcToken.connect(wallet0).balanceOf(wallet.address)); + const amountB = new BigNumber(amount0.toString()).multipliedBy(5).dividedBy(1e4); + const amountWithFee = amount0.plus(amountB); + + const flashSpells = [ + { + connector: "AAVE-V3-IMPORT-PERMIT-X", + method: "importAave", + args: [ + wallet.address, + [[DAI], [USDC], false, [amountB.toFixed(0)]], + [[v], [ethers.utils.hexlify(r)], [ethers.utils.hexlify(s)], [expiry]] + ] + }, + { + connector: "INSTAPOOL-C", + method: "flashPayback", + args: [USDC, amountWithFee.toFixed(0), 0, 0] + } + ]; + + const spells = [ + { + connector: "INSTAPOOL-C", + method: "flashBorrowAndCast", + args: [USDC, amount0.toString(), 5, encodeFlashcastData(flashSpells), "0x"] + } + ]; + const tx = await dsaWallet0.connect(wallet0).cast(...encodeSpells(spells), wallet.address); + const receipt = await tx.wait(); + }); + + it("Should check DSA AAVE position", async () => { + expect(await aDai.connect(wallet0).balanceOf(dsaWallet0.address)).to.be.gte( + new BigNumber(10).multipliedBy(1e18).toString() + ); + }); + }); +}); diff --git a/test/arbitrum/aave/v3-test.ts b/test/arbitrum/aave/v3-test.ts new file mode 100644 index 0000000..85e7065 --- /dev/null +++ b/test/arbitrum/aave/v3-test.ts @@ -0,0 +1,292 @@ +import { expect, should } from "chai"; +import hre, { ethers, waffle } from "hardhat"; +import type { Signer, Contract } from "ethers"; +import { ecsign, ecrecover, pubToAddress } from "ethereumjs-util"; +import { keccak256 } from "@ethersproject/keccak256"; +import { defaultAbiCoder } from "@ethersproject/abi"; +import { BigNumber } from "bignumber.js"; +import { buildDSAv2 } from "../../../scripts/tests/buildDSAv2"; +import { addresses } from "../../../scripts/tests/arbitrum/addresses"; +import { deployAndEnableConnector } from "../../../scripts/tests/deployAndEnableConnector"; +import { abis } from "../../../scripts/constant/abis"; +import { getMasterSigner } from "../../../scripts/tests/getMasterSigner"; +import { parseEther, parseUnits } from "ethers/lib/utils"; +import { encodeSpells } from "../../../scripts/tests/encodeSpells"; +import encodeFlashcastData from "../../../scripts/tests/encodeFlashcastData"; +import { ConnectV2AaveV3Arbitrum__factory, IERC20__factory } from "../../../typechain"; + +const ABI = ["function balanceOf(address account) public view returns (uint256)"]; + +const aDaiAddress = "0x82E64f49Ed5EC1bC6e43DAD4FC8Af9bb3A2312EE"; +const aaveAddress = "0x69FA688f1Dc47d4B5d8029D5a35FB7a548310654"; +let account = "0xc5ed2333f8a2c351fca35e5ebadb2a82f5d254c3"; +const DAI = "0xDA10009cBd5D07dd0CeCc66161FC93D7c9000da1"; +const USDC = "0xFF970A61A04b1cA14834A43f5dE4533eBDDB5CC8"; +const mnemonic = "test test test test test test test test test test test junk"; +const connectorName = "AAVE-V3-X"; +let signer: any, wallet0: any; + +const aaveAbi = [ + { + inputs: [ + { internalType: "address", name: "asset", type: "address" }, + { internalType: "address", name: "user", type: "address" } + ], + name: "getUserReserveData", + outputs: [ + { internalType: "uint256", name: "currentATokenBalance", type: "uint256" }, + { internalType: "uint256", name: "currentStableDebt", type: "uint256" }, + { internalType: "uint256", name: "currentVariableDebt", type: "uint256" }, + { internalType: "uint256", name: "principalStableDebt", type: "uint256" }, + { internalType: "uint256", name: "scaledVariableDebt", type: "uint256" }, + { internalType: "uint256", name: "stableBorrowRate", type: "uint256" }, + { internalType: "uint256", name: "liquidityRate", type: "uint256" }, + { internalType: "uint40", name: "stableRateLastUpdated", type: "uint40" }, + { internalType: "bool", name: "usageAsCollateralEnabled", type: "bool" } + ], + stateMutability: "view", + type: "function" + } +]; + +const erc20Abi = [ + { + constant: false, + inputs: [ + { + name: "_spender", + type: "address" + }, + { + name: "_value", + type: "uint256" + } + ], + name: "approve", + outputs: [ + { + name: "", + type: "bool" + } + ], + payable: false, + stateMutability: "nonpayable", + type: "function" + }, + { + constant: true, + inputs: [], + name: "totalSupply", + outputs: [ + { + name: "", + type: "uint256" + } + ], + payable: false, + stateMutability: "view", + type: "function" + }, + { + constant: true, + inputs: [ + { + name: "_owner", + type: "address" + } + ], + name: "balanceOf", + outputs: [ + { + name: "balance", + type: "uint256" + } + ], + payable: false, + stateMutability: "view", + type: "function" + }, + { + constant: false, + inputs: [ + { + name: "_to", + type: "address" + }, + { + name: "_value", + type: "uint256" + } + ], + name: "transfer", + outputs: [ + { + name: "", + type: "bool" + } + ], + payable: false, + stateMutability: "nonpayable", + type: "function" + } +]; + +const token = new ethers.Contract(DAI, erc20Abi); +const aDai = new ethers.Contract(aDaiAddress, ABI); +const usdcToken = new ethers.Contract(USDC, erc20Abi); +const aave = new ethers.Contract(aaveAddress, aaveAbi); + +describe("Aave v3 Position for Arbitrum", function () { + let dsaWallet0: any; + let masterSigner: Signer; + let instaConnectorsV2: Contract; + let connector: any; + + const wallet = ethers.Wallet.fromMnemonic(mnemonic); + + before(async () => { + await hre.network.provider.request({ + method: "hardhat_reset", + params: [ + { + forking: { + //@ts-ignore + jsonRpcUrl: hre.config.networks.hardhat.forking.url, + blockNumber: 9333600 + } + } + ] + }); + masterSigner = await getMasterSigner(); + [wallet0] = await ethers.getSigners(); + await hre.network.provider.send("hardhat_setBalance", [account, ethers.utils.parseEther("10").toHexString()]); + + await hre.network.provider.request({ + method: "hardhat_impersonateAccount", + params: [account] + }); + + signer = await ethers.getSigner(account); + + await token.connect(signer).transfer(wallet0.address, ethers.utils.parseEther("10")); + + instaConnectorsV2 = await ethers.getContractAt(abis.core.connectorsV2, addresses.core.connectorsV2); + connector = await deployAndEnableConnector({ + connectorName, + contractArtifact: ConnectV2AaveV3Arbitrum__factory, + signer: masterSigner, + connectors: instaConnectorsV2 + }); + }); + + describe("Deployment", async () => { + it("Should set correct name", async () => { + expect(await connector.name()).to.eq("AaveV3-v1.2"); + }); + }); + + describe("DSA wallet setup", async () => { + it("Should build DSA v2", async () => { + dsaWallet0 = await buildDSAv2(wallet0.address); + expect(!!dsaWallet0.address).to.be.true; + }); + + it("Deposit ETH into DSA wallet", async function () { + await wallet0.sendTransaction({ + to: dsaWallet0.address, + value: ethers.utils.parseEther("5") + }); + + expect(await ethers.provider.getBalance(dsaWallet0.address)).to.be.gte(ethers.utils.parseEther("5")); + }); + }); + + describe("check user AAVE position", async () => { + it("Should create DSA Aave v3 position of DAI(collateral) and USDC(debt)", async () => { + await token.connect(signer).transfer(dsaWallet0.address, ethers.utils.parseEther("10")); + + const spells = [ + //deposit DAI in aave + { + connector: connectorName, + method: "deposit", + args: [DAI, parseEther("10"), 0, 0] + }, + //borrow USDC from aave + { + connector: connectorName, + method: "borrow", + args: [USDC, parseUnits("3", 6), 2, 0, 0] + } + ]; + const tx = await dsaWallet0.connect(wallet0).cast(...encodeSpells(spells), wallet0.address); + const receipt = await tx.wait(); + }); + + it("Should check position of dsa", async () => { + expect(await aDai.connect(wallet0).balanceOf(dsaWallet0.address)).to.be.gte( + new BigNumber(10).multipliedBy(1e18).toString() + ); + + expect(await usdcToken.connect(wallet0).balanceOf(dsaWallet0.address)).to.be.gte( + new BigNumber(3).multipliedBy(1e6).toString() + ); + + expect((await aave.connect(wallet0).getUserReserveData(USDC, dsaWallet0.address)).currentStableDebt).to.be.equal( + 0 + ); + expect((await aave.connect(wallet0).getUserReserveData(USDC, dsaWallet0.address)).currentVariableDebt).to.be.gte( + new BigNumber(3).multipliedBy(1e6).toString() + ); + console.log( + `\tstable borrow before: ${ + (await aave.connect(wallet0).getUserReserveData(USDC, dsaWallet0.address)).currentStableDebt + }` + ); + console.log( + `\tvariable borrow before: ${ + (await aave.connect(wallet0).getUserReserveData(USDC, dsaWallet0.address)).currentVariableDebt + }` + ); + }); + + it("Should swap borrowRateMode", async () => { + const spells = [ + //deposit DAI in aave + { + connector: connectorName, + method: "swapBorrowRateMode", + args: [USDC, 2] + } + ]; + const tx = await dsaWallet0.connect(wallet0).cast(...encodeSpells(spells), wallet0.address); + const receipt = await tx.wait(); + }); + + it("Should check position of dsa", async () => { + expect(await aDai.connect(wallet0).balanceOf(dsaWallet0.address)).to.be.gte( + new BigNumber(10).multipliedBy(1e18).toString() + ); + + expect(await usdcToken.connect(wallet0).balanceOf(dsaWallet0.address)).to.be.gte( + new BigNumber(3).multipliedBy(1e6).toString() + ); + expect( + (await aave.connect(wallet0).getUserReserveData(USDC, dsaWallet0.address)).currentVariableDebt + ).to.be.equal(0); + expect((await aave.connect(wallet0).getUserReserveData(USDC, dsaWallet0.address)).currentStableDebt).to.be.gte( + new BigNumber(3).multipliedBy(1e6).toString() + ); + console.log( + `\tstable borrow after: ${ + (await aave.connect(wallet0).getUserReserveData(USDC, dsaWallet0.address)).currentStableDebt + }` + ); + console.log( + `\tvariable borrow after: ${ + (await aave.connect(wallet0).getUserReserveData(USDC, dsaWallet0.address)).currentVariableDebt + }` + ); + }); + }); +}); diff --git a/test/arbitrum/arb-claim/test.ts b/test/arbitrum/arb-claim/test.ts new file mode 100644 index 0000000..734fc06 --- /dev/null +++ b/test/arbitrum/arb-claim/test.ts @@ -0,0 +1,80 @@ +import { SignerWithAddress } from "@nomiclabs/hardhat-ethers/signers"; +import { ethers } from "hardhat"; +import { ConnectV2ArbitrumAirdrop, ConnectV2ArbitrumAirdrop__factory } from "../../../typechain"; +import hre from "hardhat"; +import { deployAndEnableConnector } from "../../../scripts/tests/deployAndEnableConnector"; +import { buildDSAv2 } from "../../../scripts/tests/buildDSAv2"; +import { encodeSpells } from "../../../scripts/tests/encodeSpells"; +import { getMasterSigner } from "../../../scripts/tests/getMasterSigner"; +import { addresses } from "../../../scripts/tests/arbitrum/addresses"; +import { abis } from "../../../scripts/constant/abis"; + +describe("Arbitrum Airdrop Claim Test", () => { + let signer: SignerWithAddress; + let signer_user: any; + const user = "0x30c3D961a21c2352A6FfAfFd4e8cB8730Bf82757"; + const connectorName = "arbitrum-airdrop"; + let dsaWallet0: any; + + before(async () => { + [signer] = await ethers.getSigners(); + }); + + describe("Arbitrum Airdrop Functions", () => { + let contract: ConnectV2ArbitrumAirdrop; + + before(async () => { + await hre.network.provider.request({ + method: "hardhat_reset", + params: [ + { + forking: { + //@ts-ignore + jsonRpcUrl: hre.config.networks.hardhat.forking.url, + blockNumber: 70606643, + }, + }, + ], + }); + + const deployer = new ConnectV2ArbitrumAirdrop__factory(signer); + contract = await deployer.deploy(); + await contract.deployed(); + console.log("Contract deployed at: ", contract.address); + + await deployAndEnableConnector({ + connectorName, + contractArtifact: ConnectV2ArbitrumAirdrop__factory, + signer: signer, + connectors: await ethers.getContractAt(abis.core.connectorsV2, addresses.core.connectorsV2), + }); + + await hre.network.provider.request({ + method: 'hardhat_impersonateAccount', + params: [user], + }); + + signer_user = await ethers.getSigner(user); + dsaWallet0 = await buildDSAv2(user); + }); + + it("Claims Arbitrum Airdrop and checks claimable tokens", async () => { + const claimableBefore = await contract.claimableArbTokens(user); + console.log("Claimable tokens before: ", claimableBefore.toString()); + + const spells = [ + { + connector: connectorName, + method: "claimAirdrop", + args: ["0"], + }, + ]; + + const tx = await dsaWallet0.connect(signer_user).cast(...encodeSpells(spells), user); + await tx.wait(); + + const claimableAfter = await contract.claimableArbTokens(user); + console.log("Claimable tokens after: ", claimableAfter.toString()); + }); + }); +}); \ No newline at end of file diff --git a/test/arbitrum/compound/compound.iii.rewards.test.ts b/test/arbitrum/compound/compound.iii.rewards.test.ts new file mode 100644 index 0000000..d9b3e4e --- /dev/null +++ b/test/arbitrum/compound/compound.iii.rewards.test.ts @@ -0,0 +1,444 @@ +import { expect } from "chai"; +import hre from "hardhat"; +const { waffle, ethers } = hre; +const { provider, deployContract } = waffle; + +import { Signer, Contract } from "ethers"; +import { BigNumber } from "bignumber.js"; + +import { deployAndEnableConnector } from "../../../scripts/tests/deployAndEnableConnector"; +import { buildDSAv2 } from "../../../scripts/tests/buildDSAv2"; +import { encodeSpells } from "../../../scripts/tests/encodeSpells"; +import { getMasterSigner } from "../../../scripts/tests/getMasterSigner"; +import { addresses } from "../../../scripts/tests/arbitrum/addresses"; +import { tokens, tokenMapping } from "../../../scripts/tests/arbitrum/tokens"; +import { abis } from "../../../scripts/constant/abis"; +import { ConnectV2CompoundV3ArbitrumRewards__factory, ConnectV2CompoundV3Arbitrum__factory } from "../../../typechain"; + +describe("Compound III Arbitrum Rewards", function () { + let connectorName = "COMPOUND-V3-REWARDS-TEST-A"; + const market = "0xA5EDBDD9646f8dFF606d7448e414884C7d905dCA"; + const rewards = "0x88730d254A2f7e6AC8388c3198aFd694bA9f7fae"; + const base = "0xFF970A61A04b1cA14834A43f5dE4533eBDDB5CC8"; + const account = "0x62383739d68dd0f844103db8dfb05a7eded5bbe6"; + const wethWhale = "0xe50fA9b3c56FfB159cB0FCA61F5c9D750e8128c8"; + const baseWhale = "0x62383739d68dd0f844103db8dfb05a7eded5bbe6"; + + const ABI = [ + "function balanceOf(address account) public view returns (uint256)", + "function approve(address spender, uint256 amount) external returns(bool)", + "function transfer(address recipient, uint256 amount) external returns (bool)" + ]; + const wethContract = new ethers.Contract(tokens.weth.address, ABI); + const baseContract = new ethers.Contract(base, ABI); + + const cometABI = [ + { + inputs: [ + { internalType: "address", name: "comet", type: "address" }, + { internalType: "address", name: "src", type: "address" }, + { internalType: "bool", name: "shouldAccrue", type: "bool" } + ], + name: "claim", + outputs: [], + stateMutability: "nonpayable", + type: "function" + }, + { + inputs: [ + { internalType: "address", name: "comet", type: "address" }, + { internalType: "address", name: "src", type: "address" }, + { internalType: "address", name: "to", type: "address" }, + { internalType: "bool", name: "shouldAccrue", type: "bool" } + ], + name: "claimTo", + outputs: [], + stateMutability: "nonpayable", + type: "function" + }, + { + inputs: [ + { internalType: "address", name: "comet", type: "address" }, + { internalType: "address", name: "account", type: "address" } + ], + name: "getRewardOwed", + outputs: [ + { + components: [ + { internalType: "address", name: "token", type: "address" }, + { internalType: "uint256", name: "owed", type: "uint256" } + ], + internalType: "struct CometRewards.RewardOwed", + name: "", + type: "tuple" + } + ], + stateMutability: "nonpayable", + type: "function" + }, + { + inputs: [{ internalType: "address", name: "", type: "address" }], + name: "rewardConfig", + outputs: [ + { internalType: "address", name: "token", type: "address" }, + { internalType: "uint64", name: "rescaleFactor", type: "uint64" }, + { internalType: "bool", name: "shouldUpscale", type: "bool" } + ], + stateMutability: "view", + type: "function" + }, + { + inputs: [ + { internalType: "address", name: "", type: "address" }, + { internalType: "address", name: "", type: "address" } + ], + name: "rewardsClaimed", + outputs: [{ internalType: "uint256", name: "", type: "uint256" }], + stateMutability: "view", + type: "function" + } + ]; + + const marketABI = [ + { + inputs: [{ internalType: "address", name: "account", type: "address" }], + name: "balanceOf", + outputs: [{ internalType: "uint256", name: "", type: "uint256" }], + stateMutability: "view", + type: "function" + }, + { + inputs: [{ internalType: "address", name: "account", type: "address" }], + name: "borrowBalanceOf", + outputs: [{ internalType: "uint256", name: "", type: "uint256" }], + stateMutability: "view", + type: "function" + }, + { + inputs: [], + name: "baseBorrowMin", + outputs: [{ internalType: "uint256", name: "", type: "uint256" }], + stateMutability: "view", + type: "function" + }, + { + inputs: [], + name: "baseMinForRewards", + outputs: [{ internalType: "uint256", name: "", type: "uint256" }], + stateMutability: "view", + type: "function" + }, + { + inputs: [], + name: "baseToken", + outputs: [{ internalType: "address", name: "", type: "address" }], + stateMutability: "view", + type: "function" + }, + { + inputs: [], + name: "decimals", + outputs: [{ internalType: "uint8", name: "", type: "uint8" }], + stateMutability: "view", + type: "function" + }, + { + inputs: [{ internalType: "address", name: "priceFeed", type: "address" }], + name: "getPrice", + outputs: [{ internalType: "uint256", name: "", type: "uint256" }], + stateMutability: "view", + type: "function" + }, + { + inputs: [ + { internalType: "address", name: "owner", type: "address" }, + { internalType: "address", name: "manager", type: "address" } + ], + name: "hasPermission", + outputs: [{ internalType: "bool", name: "", type: "bool" }], + stateMutability: "view", + type: "function" + }, + { + inputs: [], + name: "numAssets", + outputs: [{ internalType: "uint8", name: "", type: "uint8" }], + stateMutability: "view", + type: "function" + }, + { + inputs: [ + { internalType: "address", name: "asset", type: "address" }, + { internalType: "uint256", name: "baseAmount", type: "uint256" } + ], + name: "quoteCollateral", + outputs: [{ internalType: "uint256", name: "", type: "uint256" }], + stateMutability: "view", + type: "function" + }, + { + inputs: [{ internalType: "address", name: "", type: "address" }], + name: "userBasic", + outputs: [ + { internalType: "int104", name: "principal", type: "int104" }, + { internalType: "uint64", name: "baseTrackingIndex", type: "uint64" }, + { internalType: "uint64", name: "baseTrackingAccrued", type: "uint64" }, + { internalType: "uint16", name: "assetsIn", type: "uint16" }, + { internalType: "uint8", name: "_reserved", type: "uint8" } + ], + stateMutability: "view", + type: "function" + }, + { + inputs: [ + { internalType: "address", name: "", type: "address" }, + { internalType: "address", name: "", type: "address" } + ], + name: "userCollateral", + outputs: [ + { internalType: "uint128", name: "balance", type: "uint128" }, + { internalType: "uint128", name: "_reserved", type: "uint128" } + ], + stateMutability: "view", + type: "function" + } + ]; + + let dsaWallet0: any; + let dsaWallet1: any; + let wallet: any; + let dsa0Signer: any; + let masterSigner: Signer; + let instaConnectorsV2: Contract; + let connector: any; + let connectorMain: any; + let signer: any; + let wethSigner: any; + let usdcSigner: any; + + const cometReward = new ethers.Contract(rewards, cometABI); + const comet = new ethers.Contract(market, marketABI); + + const wallets = provider.getWallets(); + const [wallet0, wallet1, wallet2, wallet3] = wallets; + + before(async () => { + await hre.network.provider.request({ + method: "hardhat_reset", + params: [ + { + forking: { + //@ts-ignore + jsonRpcUrl: hre.config.networks.hardhat.forking.url, + // blockNumber: 15444500 + } + } + ] + }); + masterSigner = await getMasterSigner(); + instaConnectorsV2 = await ethers.getContractAt(abis.core.connectorsV2, addresses.core.connectorsV2); + connector = await deployAndEnableConnector({ + connectorName, + contractArtifact: ConnectV2CompoundV3ArbitrumRewards__factory, + signer: masterSigner, + connectors: instaConnectorsV2 + }); + console.log("Connector address", connector.address); + + await hre.network.provider.send("hardhat_setBalance", [account, ethers.utils.parseEther("10").toHexString()]); + + await hre.network.provider.request({ + method: "hardhat_impersonateAccount", + params: [account] + }); + + signer = await ethers.getSigner(account); + + await hre.network.provider.request({ + method: "hardhat_impersonateAccount", + params: [wethWhale] + }); + wethSigner = await ethers.getSigner(wethWhale); + + await hre.network.provider.request({ + method: "hardhat_impersonateAccount", + params: [baseWhale] + }); + usdcSigner = await ethers.getSigner(baseWhale); + await hre.network.provider.send("hardhat_setBalance", [ + usdcSigner.address, + ethers.utils.parseEther("10").toHexString() + ]); + }); + + it("Should have contracts deployed.", async function () { + expect(!!instaConnectorsV2.address).to.be.true; + expect(!!connector.address).to.be.true; + expect(!!(await masterSigner.getAddress())).to.be.true; + }); + + describe("DSA wallet setup", function () { + it("Should build DSA v2", async function () { + dsaWallet0 = await buildDSAv2(wallet0.address); + expect(!!dsaWallet0.address).to.be.true; + dsaWallet1 = await buildDSAv2(wallet0.address); + expect(!!dsaWallet1.address).to.be.true; + wallet = await ethers.getSigner(dsaWallet0.address); + expect(!!dsaWallet1.address).to.be.true; + }); + + it("Deposit ETH into DSA wallet", async function () { + await hre.network.provider.request({ + method: "hardhat_impersonateAccount", + params: [wallet.address] + }); + + dsa0Signer = await ethers.getSigner(wallet.address); + await wallet0.sendTransaction({ + to: dsaWallet0.address, + value: ethers.utils.parseEther("10") + }); + expect(await ethers.provider.getBalance(dsaWallet0.address)).to.be.gte(ethers.utils.parseEther("10")); + await wallet0.sendTransaction({ + to: dsaWallet1.address, + value: ethers.utils.parseEther("10") + }); + expect(await ethers.provider.getBalance(dsaWallet1.address)).to.be.gte(ethers.utils.parseEther("10")); + }); + + it("should deposit USDC in dsa wallet", async function () { + await baseContract.connect(usdcSigner).transfer(dsaWallet0.address, ethers.utils.parseUnits("500", 6)); + + expect(await baseContract.connect(usdcSigner).balanceOf(dsaWallet0.address)).to.be.gte( + ethers.utils.parseUnits("500", 6) + ); + }); + }); + + describe("Main", function () { + //deposit asset + it("Should supply USDC in Compound V3", async function () { + connectorName = "COMPOUND-V3-TEST-A"; + connectorMain = await deployAndEnableConnector({ + connectorName, + contractArtifact: ConnectV2CompoundV3Arbitrum__factory, + signer: masterSigner, + connectors: instaConnectorsV2 + }); + const amount = ethers.utils.parseUnits("400", 6); + const spells = [ + { + connector: "COMPOUND-V3-TEST-A", + method: "deposit", + args: [market, base, amount, 0, 0] + } + ]; + + const tx = await dsaWallet0.connect(wallet0).cast(...encodeSpells(spells), wallet1.address); + const receipt = await tx.wait(); + expect(new BigNumber(await baseContract.connect(signer).balanceOf(dsaWallet0.address)).toFixed(0)).to.be.lte( + ethers.utils.parseUnits("100", 6) + ); + expect(new BigNumber(await comet.connect(signer).balanceOf(dsaWallet0.address)).toFixed(0)).to.be.gte( + ethers.utils.parseUnits("399", 6) + ); + }); + + let connector_ = "COMPOUND-V3-REWARDS-TEST-A"; + it("Should claim rewards", async function () { + let reward = (await cometReward.connect(signer).rewardConfig(market)).token; + let rewardInterface = new ethers.Contract(reward, ABI); + let owed_ = await cometReward.connect(signer).callStatic.getRewardOwed(market, dsaWallet0.address); + let amt: number = owed_.owed; + console.log(new BigNumber(amt).toFixed(0)); + const spells = [ + { + connector: connector_, + method: "claimRewards", + args: [market, 0] + } + ]; + + const tx = await dsaWallet0.connect(wallet0).cast(...encodeSpells(spells), wallet1.address); + const receipt = await tx.wait(); + expect(new BigNumber(await rewardInterface.connect(signer).balanceOf(dsaWallet0.address)).toFixed(0)).to.be.gte( + amt + ); + }); + + it("Should supply USDC in Compound V3 through dsaWallet0", async function () { + const amount = ethers.utils.parseUnits("100", 6); // 1 ETH + const spells = [ + { + connector: "COMPOUND-V3-TEST-A", + method: "deposit", + args: [market, base, amount, 0, 0] + } + ]; + + const tx = await dsaWallet0.connect(wallet0).cast(...encodeSpells(spells), wallet1.address); + const receipt = await tx.wait(); + expect(new BigNumber(await baseContract.connect(signer).balanceOf(dsaWallet0.address)).toFixed(0)).to.be.lte( + ethers.utils.parseUnits("0", 6) + ); + expect(new BigNumber(await comet.connect(signer).balanceOf(dsaWallet0.address)).toFixed(0)).to.be.gte( + ethers.utils.parseUnits("499", 6) + ); + }); + + it("Should claim rewards to dsa1", async function () { + let reward = (await cometReward.connect(signer).rewardConfig(market)).token; + let rewardInterface = new ethers.Contract(reward, ABI); + let owed_ = await cometReward.connect(signer).callStatic.getRewardOwed(market, dsaWallet0.address); + let amt: number = owed_.owed; + + const spells = [ + { + connector: connector_, + method: "claimRewardsOnBehalfOf", + args: [market, dsaWallet0.address, dsaWallet1.address, 0] + } + ]; + + const tx = await dsaWallet0.connect(wallet0).cast(...encodeSpells(spells), wallet1.address); + const receipt = await tx.wait(); + expect(new BigNumber(await rewardInterface.connect(signer).balanceOf(dsaWallet1.address)).toFixed(0)).to.be.gte( + amt + ); + }); + + it("should allow manager for dsaWallet0's collateral and base", async function () { + const spells = [ + { + connector: connectorName, + method: "toggleAccountManager", + args: [market, dsaWallet1.address, true] + } + ]; + + const tx = await dsaWallet0.connect(wallet0).cast(...encodeSpells(spells), wallet1.address); + const receipt = await tx.wait(); + }); + + it("Should claim rewards to dsa1 using manager", async function () { + let reward = (await cometReward.connect(signer).rewardConfig(market)).token; + let rewardInterface = new ethers.Contract(reward, ABI); + let owed_ = await cometReward.connect(signer).callStatic.getRewardOwed(market, dsaWallet0.address); + let amt: number = owed_.owed; + + const spells = [ + { + connector: connector_, + method: "claimRewardsOnBehalfOf", + args: [market, dsaWallet0.address, dsaWallet1.address, 0] + } + ]; + + const tx = await dsaWallet1.connect(wallet0).cast(...encodeSpells(spells), wallet1.address); + const receipt = await tx.wait(); + expect(new BigNumber(await rewardInterface.connect(signer).balanceOf(dsaWallet1.address)).toFixed(0)).to.be.gte( + amt + ); + }); + }); +}); diff --git a/test/arbitrum/compound/compound.iii.test.ts b/test/arbitrum/compound/compound.iii.test.ts new file mode 100644 index 0000000..4ad3baf --- /dev/null +++ b/test/arbitrum/compound/compound.iii.test.ts @@ -0,0 +1,651 @@ +import { expect } from "chai"; +import hre from "hardhat"; +const { waffle, ethers } = hre; +const { provider, deployContract } = waffle; + +import { Signer, Contract } from "ethers"; +import { BigNumber } from "bignumber.js"; + +import { deployAndEnableConnector } from "../../../scripts/tests/deployAndEnableConnector"; +import { buildDSAv2 } from "../../../scripts/tests/buildDSAv2"; +import { encodeSpells } from "../../../scripts/tests/encodeSpells"; +import { getMasterSigner } from "../../../scripts/tests/getMasterSigner"; +import { addresses } from "../../../scripts/tests/arbitrum/addresses"; +import { tokens, tokenMapping } from "../../../scripts/tests/arbitrum/tokens"; +import { abis } from "../../../scripts/constant/abis"; +import { ConnectV2CompoundV3Arbitrum__factory } from "../../../typechain"; + +describe("Compound III Arbitrum", function () { + const connectorName = "COMPOUND-V3-TEST-A"; + const market = "0xA5EDBDD9646f8dFF606d7448e414884C7d905dCA"; + const base = "0xFF970A61A04b1cA14834A43f5dE4533eBDDB5CC8"; + const account = "0x62383739d68dd0f844103db8dfb05a7eded5bbe6"; + const wethWhale = "0xe50fA9b3c56FfB159cB0FCA61F5c9D750e8128c8"; + + const ABI = [ + "function balanceOf(address account) public view returns (uint256)", + "function approve(address spender, uint256 amount) external returns(bool)", + "function transfer(address recipient, uint256 amount) external returns (bool)" + ]; + const wethContract = new ethers.Contract(tokens.weth.address, ABI); + const baseContract = new ethers.Contract(base, ABI); + + const cometABI = [ + { + inputs: [{ internalType: "address", name: "account", type: "address" }], + name: "balanceOf", + outputs: [{ internalType: "uint256", name: "", type: "uint256" }], + stateMutability: "view", + type: "function" + }, + { + inputs: [{ internalType: "address", name: "account", type: "address" }], + name: "borrowBalanceOf", + outputs: [{ internalType: "uint256", name: "", type: "uint256" }], + stateMutability: "view", + type: "function" + }, + { + inputs: [], + name: "baseBorrowMin", + outputs: [{ internalType: "uint256", name: "", type: "uint256" }], + stateMutability: "view", + type: "function" + }, + { + inputs: [], + name: "baseMinForRewards", + outputs: [{ internalType: "uint256", name: "", type: "uint256" }], + stateMutability: "view", + type: "function" + }, + { + inputs: [], + name: "baseToken", + outputs: [{ internalType: "address", name: "", type: "address" }], + stateMutability: "view", + type: "function" + }, + { + inputs: [], + name: "decimals", + outputs: [{ internalType: "uint8", name: "", type: "uint8" }], + stateMutability: "view", + type: "function" + }, + { + inputs: [{ internalType: "address", name: "priceFeed", type: "address" }], + name: "getPrice", + outputs: [{ internalType: "uint256", name: "", type: "uint256" }], + stateMutability: "view", + type: "function" + }, + { + inputs: [ + { internalType: "address", name: "owner", type: "address" }, + { internalType: "address", name: "manager", type: "address" } + ], + name: "hasPermission", + outputs: [{ internalType: "bool", name: "", type: "bool" }], + stateMutability: "view", + type: "function" + }, + { + inputs: [], + name: "numAssets", + outputs: [{ internalType: "uint8", name: "", type: "uint8" }], + stateMutability: "view", + type: "function" + }, + { + inputs: [ + { internalType: "address", name: "asset", type: "address" }, + { internalType: "uint256", name: "baseAmount", type: "uint256" } + ], + name: "quoteCollateral", + outputs: [{ internalType: "uint256", name: "", type: "uint256" }], + stateMutability: "view", + type: "function" + }, + { + inputs: [{ internalType: "address", name: "", type: "address" }], + name: "userBasic", + outputs: [ + { internalType: "int104", name: "principal", type: "int104" }, + { internalType: "uint64", name: "baseTrackingIndex", type: "uint64" }, + { internalType: "uint64", name: "baseTrackingAccrued", type: "uint64" }, + { internalType: "uint16", name: "assetsIn", type: "uint16" }, + { internalType: "uint8", name: "_reserved", type: "uint8" } + ], + stateMutability: "view", + type: "function" + }, + { + inputs: [ + { internalType: "address", name: "", type: "address" }, + { internalType: "address", name: "", type: "address" } + ], + name: "userCollateral", + outputs: [ + { internalType: "uint128", name: "balance", type: "uint128" }, + { internalType: "uint128", name: "_reserved", type: "uint128" } + ], + stateMutability: "view", + type: "function" + } + ]; + + let dsaWallet0: any; + let dsaWallet1: any; + let dsaWallet2: any; + let dsaWallet3: any; + let wallet: any; + let dsa0Signer: any; + let masterSigner: Signer; + let instaConnectorsV2: Contract; + let connector: any; + let signer: any; + let wethSigner: any; + + const comet = new ethers.Contract(market, cometABI); + + const wallets = provider.getWallets(); + const [wallet0, wallet1, wallet2, wallet3] = wallets; + + before(async () => { + await hre.network.provider.request({ + method: "hardhat_reset", + params: [ + { + forking: { + //@ts-ignore + jsonRpcUrl: hre.config.networks.hardhat.forking.url, + ignoreUnknownTxType: true, + // blockNumber: 15444500 + } + } + ] + }); + masterSigner = await getMasterSigner(); + instaConnectorsV2 = await ethers.getContractAt(abis.core.connectorsV2, addresses.core.connectorsV2); + connector = await deployAndEnableConnector({ + connectorName, + contractArtifact: ConnectV2CompoundV3Arbitrum__factory, + signer: masterSigner, + connectors: instaConnectorsV2 + }); + console.log("Connector address", connector.address); + + await hre.network.provider.send("hardhat_setBalance", [account, '0x56bc75e2d63100000']); // set balance as 100 eth + + await hre.network.provider.request({ + method: "hardhat_impersonateAccount", + params: [account] + }); + + signer = await ethers.getSigner(account); + + await hre.network.provider.send("hardhat_setBalance", [wethWhale, '0x56bc75e2d63100000']); // set balance as 100 eth + await hre.network.provider.request({ + method: "hardhat_impersonateAccount", + params: [wethWhale] + }); + wethSigner = await ethers.getSigner(wethWhale); + + }); + + it("Should have contracts deployed.", async function () { + expect(!!instaConnectorsV2.address).to.be.true; + expect(!!connector.address).to.be.true; + expect(!!(await masterSigner.getAddress())).to.be.true; + }); + + describe("DSA wallet setup", function () { + it("Should build DSA v2", async function () { + dsaWallet0 = await buildDSAv2(wallet0.address); + expect(!!dsaWallet0.address).to.be.true; + dsaWallet1 = await buildDSAv2(wallet0.address); + expect(!!dsaWallet1.address).to.be.true; + dsaWallet2 = await buildDSAv2(wallet0.address); + expect(!!dsaWallet2.address).to.be.true; + dsaWallet3 = await buildDSAv2(wallet0.address); + expect(!!dsaWallet3.address).to.be.true; + wallet = await ethers.getSigner(dsaWallet0.address); + expect(!!dsaWallet1.address).to.be.true; + }); + + it("Deposit ETH into DSA wallet", async function () { + await hre.network.provider.request({ + method: "hardhat_impersonateAccount", + params: [wallet.address] + }); + + dsa0Signer = await ethers.getSigner(wallet.address); + await wallet0.sendTransaction({ + to: dsaWallet0.address, + value: ethers.utils.parseEther("10") + }); + expect(await ethers.provider.getBalance(dsaWallet0.address)).to.be.gte(ethers.utils.parseEther("10")); + await wallet0.sendTransaction({ + to: dsaWallet1.address, + value: ethers.utils.parseEther("10") + }); + expect(await ethers.provider.getBalance(dsaWallet0.address)).to.be.gte(ethers.utils.parseEther("10")); + await wallet0.sendTransaction({ + to: dsaWallet3.address, + value: ethers.utils.parseEther("10") + }); + expect(await ethers.provider.getBalance(dsaWallet0.address)).to.be.gte(ethers.utils.parseEther("10")); + }); + }); + + describe("Main", function () { + //deposit asset + it("Should supply ETH collateral in Compound V3", async function () { + const amount = ethers.utils.parseEther("5"); // 1 ETH + const spells = [ + { + connector: connectorName, + method: "deposit", + args: [market, tokens.eth.address, amount, 0, 0] + } + ]; + + const tx = await dsaWallet0.connect(wallet0).cast(...encodeSpells(spells), wallet1.address); + const receipt = await tx.wait(); + expect(await ethers.provider.getBalance(dsaWallet0.address)).to.be.lte(ethers.utils.parseEther("5")); + expect((await comet.connect(signer).userCollateral(dsaWallet0.address, tokens.weth.address)).balance).to.be.gte( + ethers.utils.parseEther("5") + ); + }); + + //deposit asset on behalf of + it("Should supply ETH collateral on behalf of dsaWallet0 in Compound V3", async function () { + const amount = ethers.utils.parseEther("1"); // 1 ETH + const spells = [ + { + connector: connectorName, + method: "depositOnBehalf", + args: [market, tokens.eth.address, dsaWallet0.address, amount, 0, 0] + } + ]; + + const tx = await dsaWallet1.connect(wallet0).cast(...encodeSpells(spells), wallet1.address); + const receipt = await tx.wait(); + expect(await ethers.provider.getBalance(dsaWallet1.address)).to.be.lte(ethers.utils.parseEther("9")); + expect((await comet.connect(wallet0).userCollateral(dsaWallet0.address, tokens.weth.address)).balance).to.be.gte( + ethers.utils.parseEther("6") + ); + }); + + it("Should borrow and payback base token from Compound", async function () { + const amount = ethers.utils.parseUnits("150", 6); + const spells = [ + { + connector: connectorName, + method: "borrow", + args: [market, base, amount, 0, 0] + }, + { + connector: connectorName, + method: "payback", + args: [market, base, ethers.utils.parseUnits("50", 6), 0, 0] + } + ]; + + const tx = await dsaWallet0.connect(wallet0).cast(...encodeSpells(spells), wallet1.address); + const receipt = await tx.wait(); + expect(await comet.connect(wallet0).borrowBalanceOf(dsaWallet0.address)).to.be.equal( + ethers.utils.parseUnits("100", 6) + ); + expect(await baseContract.connect(wallet0).balanceOf(dsaWallet0.address)).to.be.equal( + ethers.utils.parseUnits("100", 6) + ); + }); + + it("should allow manager for dsaWallet0's collateral and base", async function () { + const spells = [ + { + connector: connectorName, + method: "toggleAccountManager", + args: [market, dsaWallet2.address, true] + } + ]; + + const tx = await dsaWallet0.connect(wallet0).cast(...encodeSpells(spells), wallet1.address); + const receipt = await tx.wait(); + }); + + it("should payback base token on Compound using manager", async function () { + await baseContract.connect(signer).transfer(dsaWallet0.address, ethers.utils.parseUnits("5", 6)); + const amount = ethers.utils.parseUnits("102", 6); + await baseContract.connect(dsa0Signer).approve(market, amount); + + const spells = [ + { + connector: connectorName, + method: "paybackFromUsingManager", + args: [market, base, dsaWallet0.address, dsaWallet0.address, ethers.constants.MaxUint256, 0, 0] + } + ]; + + const tx = await dsaWallet2.connect(wallet0).cast(...encodeSpells(spells), wallet1.address); + const receipt = await tx.wait(); + expect(await comet.connect(signer).borrowBalanceOf(dsaWallet0.address)).to.be.equal( + ethers.utils.parseUnits("0", 6) + ); + }); + + it("Should borrow to another dsa from Compound", async function () { + const amount = ethers.utils.parseUnits("100", 6); + const spells = [ + { + connector: connectorName, + method: "borrowTo", + args: [market, base, dsaWallet1.address, amount, 0, 0] + } + ]; + + const tx = await dsaWallet0.connect(wallet0).cast(...encodeSpells(spells), wallet1.address); + const receipt = await tx.wait(); + expect(new BigNumber(await comet.connect(signer).borrowBalanceOf(dsaWallet0.address)).toFixed()).to.be.equal( + ethers.utils.parseUnits("100", 6) + ); + }); + + it("Should payback on behalf of from Compound", async function () { + const spells = [ + { + connector: connectorName, + method: "paybackOnBehalf", + args: [market, base, dsaWallet0.address, ethers.constants.MaxUint256, 0, 0] + } + ]; + + const tx = await dsaWallet1.connect(wallet0).cast(...encodeSpells(spells), wallet1.address); + const receipt = await tx.wait(); + expect(await comet.connect(signer).borrowBalanceOf(dsaWallet0.address)).to.be.equal( + ethers.utils.parseUnits("0", 6) + ); + }); + + it("should withdraw some ETH collateral", async function () { + let initialBal = await ethers.provider.getBalance(dsaWallet0.address); + const amount_ = ethers.utils.parseEther("2"); + const spells = [ + { + connector: connectorName, + method: "withdraw", + args: [market, tokens.eth.address, amount_, 0, 0] + } + ]; + + const tx = await dsaWallet0.connect(wallet0).cast(...encodeSpells(spells), wallet1.address); + const receipt = await tx.wait(); + expect((await comet.connect(signer).userCollateral(dsaWallet0.address, tokens.weth.address)).balance).to.be.gte( + ethers.utils.parseEther("4") + ); + expect(await ethers.provider.getBalance(dsaWallet0.address)).to.be.gte(initialBal.add(amount_).toString()); + }); + + it("manager should be able to withdraw collateral from the position and transfer", async function () { + await wallet1.sendTransaction({ + to: tokens.weth.address, + value: ethers.utils.parseEther("10") + }); + const amount = ethers.constants.MaxUint256; + const spells = [ + { + connector: connectorName, + method: "withdrawOnBehalfAndTransfer", + args: [market, tokens.eth.address, dsaWallet0.address, dsaWallet1.address, amount, 0, 0] + } + ]; + + const tx = await dsaWallet2.connect(wallet0).cast(...encodeSpells(spells), wallet1.address); + const receipt = await tx.wait(); + expect((await comet.connect(signer).userCollateral(dsaWallet0.address, tokens.weth.address)).balance).to.be.gte( + ethers.utils.parseEther("0") + ); + expect(await wethContract.connect(wallet0).balanceOf(dsaWallet1.address)).to.be.gte(ethers.utils.parseEther("4")); + }); + + it("Should withdraw collateral to another DSA", async function () { + const spells1 = [ + { + connector: connectorName, + method: "deposit", + args: [market, tokens.eth.address, ethers.utils.parseEther("5"), 0, 0] + } + ]; + + const tx1 = await dsaWallet1.connect(wallet0).cast(...encodeSpells(spells1), wallet1.address); + let initialBal = await ethers.provider.getBalance(dsaWallet0.address); + + const amount = ethers.utils.parseEther("2"); + const spells = [ + { + connector: connectorName, + method: "withdrawTo", + args: [market, tokens.eth.address, dsaWallet0.address, amount, 0, 0] + } + ]; + + const tx = await dsaWallet1.connect(wallet0).cast(...encodeSpells(spells), wallet1.address); + const receipt = await tx.wait(); + expect(await wethContract.connect(wallet0).balanceOf(dsaWallet0.address)).to.be.gte(amount); + + expect((await comet.connect(signer).userCollateral(dsaWallet1.address, tokens.weth.address)).balance).to.be.gte( + ethers.utils.parseEther("3") + ); + }); + + it("Should withdraw collateral to another DSA", async function () { + const spells1 = [ + { + connector: connectorName, + method: "deposit", + args: [market, tokens.eth.address, ethers.utils.parseEther("3"), 0, 0] + } + ]; + + const tx1 = await dsaWallet0.connect(wallet0).cast(...encodeSpells(spells1), wallet1.address); + let initialBal = await ethers.provider.getBalance(dsaWallet0.address); + + const amount = ethers.utils.parseEther("2"); + const spells = [ + { + connector: connectorName, + method: "withdrawTo", + args: [market, tokens.eth.address, dsaWallet0.address, amount, 0, 0] + } + ]; + + const tx = await dsaWallet0.connect(wallet0).cast(...encodeSpells(spells), wallet1.address); + const receipt = await tx.wait(); + expect(await ethers.provider.getBalance(dsaWallet0.address)).to.be.gte(initialBal.add(amount)); + + expect((await comet.connect(signer).userCollateral(dsaWallet1.address, tokens.weth.address)).balance).to.be.gte( + ethers.utils.parseEther("1") + ); + }); + + it("should transfer eth from dsaWallet1 to dsaWallet0 position", async function () { + const spells = [ + { + connector: connectorName, + method: "transferAsset", + args: [market, tokens.eth.address, dsaWallet0.address, ethers.utils.parseEther("3"), 0, 0] + } + ]; + + const tx = await dsaWallet1.connect(wallet0).cast(...encodeSpells(spells), wallet1.address); + const receipt = await tx.wait(); + expect((await comet.connect(signer).userCollateral(dsaWallet1.address, tokens.weth.address)).balance).to.be.gte( + ethers.utils.parseEther("0") + ); + expect((await comet.connect(signer).userCollateral(dsaWallet0.address, tokens.weth.address)).balance).to.be.gte( + ethers.utils.parseEther("3") + ); + }); + + it("should transfer base token from dsaWallet1 to dsaWallet0 position", async function () { + await baseContract.connect(signer).transfer(dsaWallet1.address, ethers.utils.parseUnits("10", 6)); + + const spells = [ + { + connector: connectorName, + method: "deposit", + args: [market, base, ethers.constants.MaxUint256, 0, 0] + } + ]; + const tx = await dsaWallet1.connect(wallet0).cast(...encodeSpells(spells), wallet1.address); + const receipt = await tx.wait(); + let initialBal = await baseContract.connect(signer).balanceOf(dsaWallet1.address); + let spells1 = [ + { + connector: connectorName, + method: "transferAsset", + args: [market, base, dsaWallet0.address, ethers.constants.MaxUint256, 0, 0] + } + ]; + + const tx1 = await dsaWallet1.connect(wallet0).cast(...encodeSpells(spells1), wallet1.address); + const receipt1 = await tx.wait(); + expect(await comet.connect(signer).balanceOf(dsaWallet1.address)).to.be.lte(ethers.utils.parseUnits("0", 6)); + expect(await comet.connect(signer).balanceOf(dsaWallet0.address)).to.be.gte(initialBal); + }); + + it("should transfer base token using manager from dsaWallet0 to dsaWallet1 position", async function () { + const spells = [ + { + connector: connectorName, + method: "transferAssetOnBehalf", + args: [market, base, dsaWallet0.address, dsaWallet1.address, ethers.constants.MaxUint256, 0, 0] + } + ]; + let initialBal = await baseContract.connect(signer).balanceOf(dsaWallet0.address); + + const tx = await dsaWallet2.connect(wallet0).cast(...encodeSpells(spells), wallet1.address); + const receipt = await tx.wait(); + expect(await comet.connect(signer).balanceOf(dsaWallet0.address)).to.be.lte(ethers.utils.parseUnits("0", 6)); + expect(await comet.connect(signer).balanceOf(dsaWallet1.address)).to.be.gte(initialBal); + }); + + it("should deposit weth using manager", async function () { + await wethContract.connect(wethSigner).transfer(dsaWallet0.address, ethers.utils.parseEther("10")); + let initialBal = await wethContract.connect(wallet0).balanceOf(dsaWallet0.address); + + const amount = ethers.utils.parseEther("1"); + await wethContract.connect(dsa0Signer).approve(market, amount); + + const spells = [ + { + connector: connectorName, + method: "depositFromUsingManager", + args: [market, tokens.eth.address, dsaWallet0.address, dsaWallet1.address, amount, 0, 0] + } + ]; + const tx = await dsaWallet2.connect(wallet0).cast(...encodeSpells(spells), wallet1.address); + const receipt = await tx.wait(); + + expect((await comet.connect(signer).userCollateral(dsaWallet1.address, tokens.weth.address)).balance).to.be.gte( + ethers.utils.parseEther("1") + ); + expect(await wethContract.connect(wallet0).balanceOf(dsaWallet0.address)).to.be.lte(initialBal.sub(amount)); + }); + + it("should allow manager for dsaWallet0's collateral", async function () { + const spells = [ + { + connector: connectorName, + method: "toggleAccountManager", + args: [market, dsaWallet2.address, true] + } + ]; + + const tx = await dsaWallet3.connect(wallet0).cast(...encodeSpells(spells), wallet1.address); + const receipt = await tx.wait(); + }); + it("should borrow on behalf using manager", async function () { + let initialBal = await baseContract.connect(wallet0).balanceOf(dsaWallet0.address); + await wallet0.sendTransaction({ + to: dsaWallet3.address, + value: ethers.utils.parseEther("15") + }); + const spells1 = [ + { + connector: connectorName, + method: "deposit", + args: [market, tokens.eth.address, ethers.utils.parseEther("15"), 0, 0] + } + ]; + const tx1 = await dsaWallet3.connect(wallet0).cast(...encodeSpells(spells1), wallet1.address); + const amount = ethers.utils.parseUnits("500", 6); + const spells = [ + { + connector: connectorName, + method: "borrowOnBehalfAndTransfer", + args: [market, base, dsaWallet3.address, dsaWallet0.address, amount, 0, 0] + } + ]; + + const tx = await dsaWallet2.connect(wallet0).cast(...encodeSpells(spells), wallet1.address); + const receipt = await tx.wait(); + expect(new BigNumber(await comet.connect(signer).borrowBalanceOf(dsaWallet3.address)).toFixed()).to.be.equal( + ethers.utils.parseUnits("500", 6) + ); + expect(await baseContract.connect(wallet0).balanceOf(dsaWallet0.address)).to.be.equal(initialBal.add(amount)); + }); + + it("should transferAsset collateral using manager", async function () { + let bal1 = (await comet.connect(signer).userCollateral(dsaWallet1.address, tokens.weth.address)).balance; + let bal0 = (await comet.connect(signer).userCollateral(dsaWallet0.address, tokens.weth.address)).balance; + const spells = [ + { + connector: connectorName, + method: "transferAssetOnBehalf", + args: [market, tokens.eth.address, dsaWallet0.address, dsaWallet1.address, ethers.utils.parseEther("1"), 0, 0] + } + ]; + + const tx = await dsaWallet2.connect(wallet0).cast(...encodeSpells(spells), wallet1.address); + const receipt = await tx.wait(); + expect((await comet.connect(signer).userCollateral(dsaWallet1.address, tokens.weth.address)).balance).to.be.gte( + bal1.add(ethers.utils.parseEther("1")).toString() + ); + expect((await comet.connect(signer).userCollateral(dsaWallet0.address, tokens.weth.address)).balance).to.be.gte( + bal0.sub(ethers.utils.parseEther("1")).toString() + ); + }); + + //can buy only when target reserves not reached. + + // it("should buy collateral", async function () { + // //deposit 10 usdc(base token) to dsa + // await baseContract.connect(signer).transfer(dsaWallet0.address, ethers.utils.parseUnits("10", 6)); + // console.log(await baseContract.connect(signer).balanceOf(dsaWallet0.address)); + + // //dsawallet0 --> collateral 0eth, balance 9eth 10usdc + // //dsaWallet1 --> balance 2eth coll: 3eth + // const amount = ethers.utils.parseUnits("1",6); + // const bal = await baseContract.connect(signer).balanceOf(dsaWallet0.address); + // const spells = [ + // { + // connector: connectorName, + // method: "buyCollateral", + // args: [market, tokens.link.address, dsaWallet0.address, amount, bal, 0, 0] + // } + // ]; + + // const tx = await dsaWallet0.connect(wallet0).cast(...encodeSpells(spells), wallet1.address); + // const receipt = await tx.wait(); + // expect(new BigNumber(await linkContract.connect(signer).balanceOf(dsaWallet0.address)).toFixed()).to.be.gte( + // ethers.utils.parseEther("1") + // ); + + // //dsawallet0 --> collateral 0eth, balance 9eth >1link + // //dsaWallet1 --> balance 2eth coll: 3eth + // }); + }); +}); diff --git a/test/arbitrum/connext/connext.test.ts b/test/arbitrum/connext/connext.test.ts new file mode 100644 index 0000000..74e98d6 --- /dev/null +++ b/test/arbitrum/connext/connext.test.ts @@ -0,0 +1,154 @@ +import { expect } from "chai"; +import hre from "hardhat"; +const { ethers, waffle } = hre; +const { provider } = waffle; + +import { deployAndEnableConnector } from "../../../scripts/tests/deployAndEnableConnector"; +import { buildDSAv2 } from "../../../scripts/tests/buildDSAv2"; +import { encodeSpells } from "../../../scripts/tests/encodeSpells"; +import { getMasterSigner } from "../../../scripts/tests/getMasterSigner"; +import { addresses } from "../../../scripts/tests/arbitrum/addresses"; +import { abis } from "../../../scripts/constant/abis"; +import { ConnectV2ConnextArbitrum__factory } from "../../../typechain"; +import { Signer, Contract } from "ethers"; + + +describe("Connext Connector [Arbitrum]", () => { + const connectorName = "CONNEXT-TEST-A"; + + let dsaWallet0: Contract; + let masterSigner: Signer; + let instaConnectorsV2: Contract; + let connector: Contract; + let usdcContract: Contract; + let signer: any; + + const usdcAddr = "0xFF970A61A04b1cA14834A43f5dE4533eBDDB5CC8"; + const ethAddr = "0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE"; + const account = "0x62383739d68dd0f844103db8dfb05a7eded5bbe6"; + + const wallets = provider.getWallets(); + const [wallet0, wallet1] = wallets; + + before(async () => { + await hre.network.provider.request({ + method: "hardhat_reset", + params: [ + { + forking: { + // @ts-ignore + jsonRpcUrl: hre.config.networks.hardhat.forking.url, + blockNumber: 82686991 + } + } + ] + }); + + masterSigner = await getMasterSigner(); + instaConnectorsV2 = await ethers.getContractAt(abis.core.connectorsV2, addresses.core.connectorsV2); + connector = await deployAndEnableConnector({ + connectorName, + contractArtifact: ConnectV2ConnextArbitrum__factory, + signer: masterSigner, + connectors: instaConnectorsV2 + }); + usdcContract = await ethers.getContractAt(abis.basic.erc20, usdcAddr); + signer = await ethers.getSigner(account); + + await hre.network.provider.send("hardhat_setBalance", [account, ethers.utils.parseEther("10").toHexString()]); + + await hre.network.provider.request({ + method: "hardhat_impersonateAccount", + params: [account] + }); + + await usdcContract.connect(signer).transfer(wallet0.address, ethers.utils.parseUnits("10000", 6)); + console.log("deployed connector: ", connector.address); + }); + + it("Should have contracts deployed.", async () => { + expect(!!instaConnectorsV2.address).to.be.true; + expect(!!connector.address).to.be.true; + expect(!!(await masterSigner.getAddress())).to.be.true; + }); + + describe("DSA wallet setup", () => { + it("Should build DSA v2", async () => { + dsaWallet0 = await buildDSAv2(wallet0.getAddress()); + expect(!!dsaWallet0.address).to.be.true; + }); + + it("Deposit ETH & USDC into DSA wallet", async () => { + await wallet0.sendTransaction({ + to: dsaWallet0.address, + value: ethers.utils.parseEther("10") + }); + expect(await ethers.provider.getBalance(dsaWallet0.address)).to.be.gte(ethers.utils.parseEther("10")); + + await usdcContract.connect(wallet0).transfer(dsaWallet0.address, ethers.utils.parseUnits("10", 6)); + expect(await usdcContract.balanceOf(dsaWallet0.address)).to.be.gte(ethers.utils.parseUnits("10", 6)); + }); + }); + + describe("Main", () => { + it("should xcall with eth", async () => { + const amount = ethers.utils.parseEther("5"); + const domainId = 6648936; + const slippage = 10000; + const relayerFee = ethers.utils.parseEther("1"); + const callData = "0x"; + + const xcallParams: any = [ + domainId, + wallet1.address, + ethAddr, + wallet1.address, + amount, + slippage, + relayerFee, + callData + ]; + + const spells = [ + { + connector: connectorName, + method: "xcall", + args: [xcallParams, 0, 0] + } + ]; + + const tx = await dsaWallet0.connect(wallet0).cast(...encodeSpells(spells), wallet1.address); + const receipt = await tx.wait(); + }); + + it("should xcall with usdc", async () => { + const amount = ethers.utils.parseUnits("5", 6); + const domainId = 6648936; + const slippage = 10000; + const relayerFee = ethers.utils.parseEther("1"); + const callData = "0x"; + + const xcallParams: any = [ + domainId, + wallet1.address, + usdcAddr, + wallet1.address, + amount, + slippage, + relayerFee, + callData + ]; + + const spells = [ + { + connector: connectorName, + method: "xcall", + args: [xcallParams, 0, 0] + } + ]; + + const tx = await dsaWallet0.connect(wallet0).cast(...encodeSpells(spells), wallet1.address); + const receipt = await tx.wait(); + }); + }); +}); diff --git a/test/arbitrum/dsa-spell/dsa-spell.test.ts b/test/arbitrum/dsa-spell/dsa-spell.test.ts new file mode 100644 index 0000000..6ae968c --- /dev/null +++ b/test/arbitrum/dsa-spell/dsa-spell.test.ts @@ -0,0 +1,180 @@ +import hre from "hardhat"; +import axios from "axios"; +import { expect } from "chai"; +const { ethers } = hre; //check +import { deployAndEnableConnector } from "../../../scripts/tests/deployAndEnableConnector"; +import { buildDSAv2 } from "../../../scripts/tests/buildDSAv2"; +import { encodeSpells } from "../../../scripts/tests/encodeSpells"; +import { getMasterSigner } from "../../../scripts/tests/getMasterSigner"; +import { addresses } from "../../../scripts/tests/arbitrum/addresses"; +import { abis } from "../../../scripts/constant/abis"; +import { ConnectV2DSASpellArbitrum__factory } from "../../../typechain"; +import { Signer, Contract } from "ethers"; +import { BigNumber } from "bignumber.js"; + +describe("DSA Spell", function () { + const connectorName = "dsa-spell-test"; + + let dsaWallet0: any; + let dsaWallet1: any; + let dsaWallet2: any; + let walletB: any; + let wallet0: any; + let masterSigner: Signer; + let instaConnectorsV2: Contract; + let connector: any; + + before(async () => { + await hre.network.provider.request({ + method: "hardhat_reset", + params: [ + { + forking: { + // @ts-ignore + jsonRpcUrl: hre.config.networks.hardhat.forking.url + } + } + ] + }); + [wallet0] = await ethers.getSigners(); + + masterSigner = await getMasterSigner(); + instaConnectorsV2 = await ethers.getContractAt(abis.core.connectorsV2, addresses.core.connectorsV2); + connector = await deployAndEnableConnector({ + connectorName, + contractArtifact: ConnectV2DSASpellArbitrum__factory, + signer: masterSigner, + connectors: instaConnectorsV2 + }); + console.log("\tConnector address", connector.address); + }); + + it("Should have contracts deployed.", async function () { + expect(!!instaConnectorsV2.address).to.be.true; + expect(!!connector.address).to.be.true; + expect(!!(await masterSigner.getAddress())).to.be.true; + }); + + describe("DSA wallet setup", function () { + it("Should build DSA v2", async function () { + dsaWallet0 = await buildDSAv2(wallet0.address); + expect(!!dsaWallet0.address).to.be.true; + walletB = await ethers.getSigner(dsaWallet0.address); + dsaWallet1 = await buildDSAv2(dsaWallet0.address); + expect(!!dsaWallet1.address).to.be.true; + console.log(`\t${dsaWallet1.address}`); + }); + + it("Deposit eth into DSA wallet 0", async function () { + await wallet0.sendTransaction({ + to: dsaWallet0.address, + value: ethers.utils.parseEther("10") + }); + + expect(await ethers.provider.getBalance(dsaWallet0.address)).to.be.gte(ethers.utils.parseEther("10")); + }); + + it("Deposit eth into DSA wallet 1", async function () { + await wallet0.sendTransaction({ + to: dsaWallet1.address, + value: ethers.utils.parseEther("10") + }); + + expect(await ethers.provider.getBalance(dsaWallet1.address)).to.be.gte(ethers.utils.parseEther("10")); + }); + }); + + describe("Main", function () { + let ETH = "0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE"; + let USDC = "0xFF970A61A04b1cA14834A43f5dE4533eBDDB5CC8"; + let usdc = new ethers.Contract(USDC, abis.basic.erc20); + let aETH = "0xe50fA9b3c56FfB159cB0FCA61F5c9D750e8128c8"; + let aEth = new ethers.Contract(aETH, abis.basic.aToken); + var abi = [ + "function withdraw(address,uint256,address,uint256,uint256)", + "function deposit(address,uint256,uint256,uint256)", + "function borrow(address,uint256,uint256,uint256,uint256)" + ]; + function getCallData(spell: string, params: any) { + var iface = new ethers.utils.Interface(abi); + let data = iface.encodeFunctionData(spell, params); + return ethers.utils.hexlify(data); + } + + it("should cast spells", async function () { + async function getArg(connectors: any, spells: any, params: any) { + let datas = []; + for (let i = 0; i < connectors.length; i++) { + datas.push(getCallData(spells[i], params[i])); + } + return [dsaWallet1.address, connectors, datas]; + } + + let connectors = ["BASIC-A", "AAVE-V3-A", "AAVE-V3-A"]; + let methods = ["withdraw", "deposit", "borrow"]; + let params = [ + [ETH, ethers.utils.parseEther("2"), dsaWallet0.address, 0, 0], + [ETH, ethers.constants.MaxUint256, 0, 0], + [USDC, ethers.utils.parseUnits("1", 6), 2, 0, 0] + ]; + let arg = await getArg(connectors, methods, params); + + const spells = [ + { + connector: connectorName, + method: "castOnDSA", + args: arg + } + ]; + const tx = await dsaWallet0.connect(wallet0).cast(...encodeSpells(spells), await wallet0.getAddress()); + const receipt = await tx.wait(); + }); + + it("should check balances after cast on DSA", async function () { + expect(await ethers.provider.getBalance(dsaWallet1.address)).to.be.lte(0); + expect(await usdc.connect(wallet0).balanceOf(dsaWallet1.address)).to.be.gte( + new BigNumber(1).multipliedBy(1e6).toString() + ); + expect(await ethers.provider.getBalance(dsaWallet0.address)).to.be.gte( + new BigNumber(12).multipliedBy(1e18).toString() + ); + }); + + it("should cast spell on the first successful", async function () { + async function getArg(connectors: any, spells: any, params: any) { + let datas = []; + for (let i = 0; i < connectors.length; i++) { + datas.push(getCallData(spells[i], params[i])); + } + return [connectors, datas]; + } + + let connectors = ["AAVE-V3-A"]; + let methods = ["deposit"]; + let params = [ + [ETH, ethers.utils.parseEther("10"), 0, 0] + ]; + let arg = await getArg(connectors, methods, params); + const spells = [ + { + connector: connectorName, + method: "castAny", + args: arg + } + ]; + const tx = await dsaWallet0 + .connect(wallet0) + .cast(...encodeSpells(spells), await wallet0.getAddress()); + const receipt = await tx.wait(); + }); + + it("should check balances after spells on DSA", async function () { + expect(await ethers.provider.getBalance(dsaWallet0.address)).to.be.lte( + new BigNumber(2).multipliedBy(1e18).toString() + ); + expect(await aEth.connect(wallet0).balanceOf(dsaWallet0.address)).to.be.gte( + new BigNumber(10).multipliedBy(1e18).toString() + ); + }); + }); +}); diff --git a/test/arbitrum/hop/hop.test.ts b/test/arbitrum/hop/hop.test.ts new file mode 100644 index 0000000..5ccbddb --- /dev/null +++ b/test/arbitrum/hop/hop.test.ts @@ -0,0 +1,142 @@ +import { expect } from "chai"; +import hre, { ethers } from "hardhat"; +import type { Signer, Contract } from "ethers"; +import { BigNumber } from "bignumber.js"; +import { buildDSAv2 } from "../../../scripts/tests/buildDSAv2"; +import { addresses } from "../../../scripts/tests/arbitrum/addresses"; +import { deployAndEnableConnector } from "../../../scripts/tests/deployAndEnableConnector"; +import { abis } from "../../../scripts/constant/abis"; +import { getMasterSigner } from "../../../scripts/tests/getMasterSigner"; +import { parseEther } from "ethers/lib/utils"; +import { encodeSpells } from "../../../scripts/tests/encodeSpells"; +import { ConnectV2HopArbitrum__factory, IERC20__factory } from "../../../typechain"; + +let account = "0xa067668661c84476afcdc6fa5d758c4c01c34352"; +const mnemonic = "test test test test test test test test test test test junk"; +const WETH = "0x82aF49447D8a07e3bd95BD0d56f35241523fBab1"; +const connectorName = "HOP-X"; +let signer: any, wallet0: any; + +describe("Hop connector", function () { + let dsaWallet0: any; + let masterSigner: Signer; + let instaConnectorsV2: Contract; + let connector: any; + + const wallet = ethers.Wallet.fromMnemonic(mnemonic); + const token = new ethers.Contract(WETH, IERC20__factory.abi); + + before(async () => { + await hre.network.provider.request({ + method: "hardhat_reset", + params: [ + { + forking: { + //@ts-ignore + jsonRpcUrl: hre.config.networks.hardhat.forking.url + // blockNumber: 9333600 + } + } + ] + }); + masterSigner = await getMasterSigner(); + [wallet0] = await ethers.getSigners(); + + await hre.network.provider.send("hardhat_setBalance", [account, ethers.utils.parseEther("10").toHexString()]); + + await hre.network.provider.request({ + method: "hardhat_impersonateAccount", + params: [account] + }); + + signer = await ethers.getSigner(account); + + instaConnectorsV2 = await ethers.getContractAt(abis.core.connectorsV2, addresses.core.connectorsV2); + connector = await deployAndEnableConnector({ + connectorName, + contractArtifact: ConnectV2HopArbitrum__factory, + signer: masterSigner, + connectors: instaConnectorsV2 + }); + }); + + describe("Deployment", async () => { + it("Should set correct name", async () => { + expect(await connector.name()).to.eq("Hop-v1.0"); + }); + }); + + describe("DSA wallet setup", async () => { + it("Should build DSA v2", async () => { + dsaWallet0 = await buildDSAv2(wallet.address); + expect(!!dsaWallet0.address).to.be.true; + }); + + it("Deposit ETH into DSA wallet", async function () { + await wallet0.sendTransaction({ + to: dsaWallet0.address, + value: ethers.utils.parseEther("5") + }); + + expect(await ethers.provider.getBalance(dsaWallet0.address)).to.be.gte(ethers.utils.parseEther("5")); + }); + }); + + describe("Main", async () => { + it("should send ETH successfully", async () => { + const deadline = new BigNumber(Date.now()).dividedBy(1000).plus(604800).toFixed(0); + const bridgeParams = [ + "0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE", + "0x33ceb27b39d2Bb7D2e61F7564d3Df29344020417", + dsaWallet0.address, + "137", + parseEther("1"), + parseEther("0.01"), + parseEther("0.8"), + deadline, + parseEther("0.8"), + deadline + ]; + + const spells = [ + { + connector: connectorName, + method: "bridge", + args: [bridgeParams, "0"] + } + ]; + + const tx = await dsaWallet0.connect(wallet0).cast(...encodeSpells(spells), wallet.getAddress()); + await tx.wait(); + }); + + it("should send WETH successfully", async () => { + const deadline = new BigNumber(Date.now()).dividedBy(1000).plus(604800).toFixed(0); + await token.connect(signer).transfer(dsaWallet0.address, ethers.utils.parseEther("10")); + + const bridgeParams = [ + "0x82aF49447D8a07e3bd95BD0d56f35241523fBab1", + "0x33ceb27b39d2Bb7D2e61F7564d3Df29344020417", + dsaWallet0.address, + "137", + parseEther("1"), + parseEther("0.01"), + parseEther("0.8"), + deadline, + parseEther("0.8"), + deadline + ]; + + const spells = [ + { + connector: connectorName, + method: "bridge", + args: [bridgeParams, "0"] + } + ]; + + const tx = await dsaWallet0.connect(wallet0).cast(...encodeSpells(spells), wallet.getAddress()); + await tx.wait(); + }); + }); +}); diff --git a/test/arbitrum/sushiswap/sushiswap.test.ts b/test/arbitrum/sushiswap/sushiswap.test.ts new file mode 100644 index 0000000..68da8d6 --- /dev/null +++ b/test/arbitrum/sushiswap/sushiswap.test.ts @@ -0,0 +1,148 @@ +import { expect } from "chai"; +import hre from "hardhat"; +const { waffle, ethers } = hre; +const { provider } = waffle; + +import { deployAndEnableConnector } from "../../../scripts/tests/deployAndEnableConnector"; +import { buildDSAv2 } from "../../../scripts/tests/buildDSAv2"; +import { encodeSpells } from "../../../scripts/tests/encodeSpells"; +import { getMasterSigner } from "../../../scripts/tests/getMasterSigner"; +import { addLiquidity } from "../../../scripts/tests/addLiquidity"; + +import { constants } from "../../../scripts/constant/constant"; +import { addresses } from "../../../scripts/tests/arbitrum/addresses"; +import { abis } from "../../../scripts/constant/abis"; +import { ConnectV2SushiswapArbitrum__factory } from "../../../typechain"; +import type { Signer, Contract } from "ethers"; + +const DAI_ADDR = "0xda10009cbd5d07dd0cecc66161fc93d7c9000da1"; + +describe("Sushiswap", function () { + const connectorName = "Sushiswap-v1"; + + let dsaWallet0: Contract; + let masterSigner: Signer; + let instaConnectorsV2: Contract; + let connector: Contract; + + const wallets = provider.getWallets(); + const [wallet0, wallet1, wallet2, wallet3] = wallets; + before(async () => { + await hre.network.provider.request({ + method: "hardhat_reset", + params: [ + { + forking: { + // @ts-ignore + jsonRpcUrl: hre.config.networks.hardhat.forking.url, + blockNumber: 13005785 + } + } + ] + }); + + masterSigner = await getMasterSigner(); + instaConnectorsV2 = await ethers.getContractAt(abis.core.connectorsV2, addresses.core.connectorsV2); + connector = await deployAndEnableConnector({ + connectorName, + contractArtifact: ConnectV2SushiswapArbitrum__factory, + signer: masterSigner, + connectors: instaConnectorsV2 + }); + console.log("Connector address", connector.address); + }); + + it("Should have contracts deployed.", async function () { + expect(!!instaConnectorsV2.address).to.be.true; + expect(!!connector.address).to.be.true; + expect(!!(await masterSigner.getAddress())).to.be.true; + }); + + describe("DSA wallet setup", function () { + it("Should build DSA v2", async function () { + dsaWallet0 = await buildDSAv2(wallet0.address); + expect(!!dsaWallet0.address).to.be.true; + }); + + it("Deposit aeth & DAI into DSA wallet", async function () { + await wallet0.sendTransaction({ + to: dsaWallet0.address, + value: ethers.utils.parseEther("10") + }); + expect(await ethers.provider.getBalance(dsaWallet0.address)).to.be.gte(ethers.utils.parseEther("10")); + + await addLiquidity("dai", dsaWallet0.address, ethers.utils.parseEther("10000")); + }); + + it("Deposit aeth & USDT into DSA wallet", async function () { + await wallet0.sendTransaction({ + to: dsaWallet0.address, + value: ethers.utils.parseEther("10") + }); + expect(await ethers.provider.getBalance(dsaWallet0.address)).to.be.gte(ethers.utils.parseEther("10")); + + await addLiquidity("usdt", dsaWallet0.address, ethers.utils.parseEther("10000")); + }); + }); + + describe("Main", function () { + it("Should deposit successfully", async function () { + const aethAmount = ethers.utils.parseEther("0.1"); + const daiUnitAmount = ethers.utils.parseEther("4000"); + const aethAddress = constants.native_address; + + const getId = "0"; + const setId = "0"; + + const spells = [ + { + connector: connectorName, + method: "deposit", + args: [aethAddress, DAI_ADDR, aethAmount, daiUnitAmount, "500000000000000000", getId, setId] + } + ]; + + const tx = await dsaWallet0.connect(wallet0).cast(...encodeSpells(spells), wallet1.address); + let receipt = await tx.wait(); + }).timeout(10000000000); + + it("Should withdraw successfully", async function () { + const aethAmount = ethers.utils.parseEther("0.1"); + const aethAddress = constants.native_address; + + const getId = "0"; + const setIds = ["0", "0"]; + + const spells = [ + { + connector: connectorName, + method: "withdraw", + args: [aethAddress, DAI_ADDR, aethAmount, 0, 0, getId, setIds] + } + ]; + + const tx = await dsaWallet0.connect(wallet0).cast(...encodeSpells(spells), wallet1.address); + let receipt = await tx.wait(); + }); + + it("Should buy successfully", async function () { + const aethAmount = ethers.utils.parseEther("0.1"); + const daiUnitAmount = ethers.utils.parseEther("4000"); + const aethAddress = constants.native_address; + + const getId = "0"; + const setId = "0"; + + const spells = [ + { + connector: connectorName, + method: "buy", + args: [aethAddress, DAI_ADDR, aethAmount, daiUnitAmount, getId, setId] + } + ]; + + const tx = await dsaWallet0.connect(wallet0).cast(...encodeSpells(spells), wallet1.address); + let receipt = await tx.wait(); + }); + }); +}); diff --git a/test/arbitrum/swap/swap-test.ts b/test/arbitrum/swap/swap-test.ts new file mode 100644 index 0000000..7ea8416 --- /dev/null +++ b/test/arbitrum/swap/swap-test.ts @@ -0,0 +1,151 @@ +import hre from "hardhat"; +import axios from "axios"; +import { expect } from "chai"; +const { ethers } = hre; //check +import { BigNumber } from "bignumber.js"; +import { deployAndEnableConnector } from "../../../scripts/tests/deployAndEnableConnector"; +import { buildDSAv2 } from "../../../scripts/tests/buildDSAv2"; +import { encodeSpells } from "../../../scripts/tests/encodeSpells"; +import { getMasterSigner } from "../../../scripts/tests/getMasterSigner"; +import { addresses } from "../../../scripts/tests/arbitrum/addresses"; +import { abis } from "../../../scripts/constant/abis"; +import { ConnectV2SwapAggregatorArbitrum__factory } from "../../../typechain"; +import er20abi from "../../../scripts/constant/abi/basics/erc20.json"; +import type { Signer, Contract } from "ethers"; + +describe("Swap | Arbitrum", function () { + const connectorName = "swap-test"; + + let dsaWallet0: Contract; + let wallet0: Signer, wallet1: Signer; + let masterSigner: Signer; + let instaConnectorsV2: Contract; + let connector: Contract; + + before(async () => { + await hre.network.provider.request({ + method: "hardhat_reset", + params: [ + { + forking: { + // @ts-ignore + jsonRpcUrl: hre.config.networks.hardhat.forking.url + } + } + ] + }); + [wallet0, wallet1] = await ethers.getSigners(); + + masterSigner = await getMasterSigner(); + instaConnectorsV2 = await ethers.getContractAt(abis.core.connectorsV2, addresses.core.connectorsV2); + connector = await deployAndEnableConnector({ + connectorName, + contractArtifact: ConnectV2SwapAggregatorArbitrum__factory, + signer: masterSigner, + connectors: instaConnectorsV2 + }); + console.log("Connector address", connector.address); + }); + + it("Should have contracts deployed.", async function () { + expect(!!instaConnectorsV2.address).to.be.true; + expect(!!connector.address).to.be.true; + expect(!!(await masterSigner.getAddress())).to.be.true; + }); + + describe("DSA wallet setup", function () { + it("Should build DSA v2", async function () { + dsaWallet0 = await buildDSAv2(await wallet0.getAddress()); + console.log(dsaWallet0.address); + expect(!!dsaWallet0.address).to.be.true; + }); + + it("Deposit matic into DSA wallet", async function () { + await wallet0.sendTransaction({ + to: dsaWallet0.address, + value: ethers.utils.parseEther("10") + }); + + expect(await ethers.provider.getBalance(dsaWallet0.address)).to.be.gte(ethers.utils.parseEther("10")); + }); + }); + + describe("Main", function () { + it("should swap the tokens", async function () { + let buyTokenAmount1Inch: any; + let buyTokenAmountParaswap: any; + + async function getArg() { + const slippage = 0.5; + /* eth -> dai */ + const sellTokenAddress = "0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE"; // eth, decimals 18 + const sellTokenDecimals = 18; + const buyTokenAddress = "0xDA10009cBd5D07dd0CeCc66161FC93D7c9000da1"; // DAI, decimals 18 + const buyTokenDecimals = 18; + const amount = 1; + + const srcAmount = new BigNumber(amount).times(new BigNumber(10).pow(sellTokenDecimals)).toFixed(0); + + //1inch + const paramDaiUsdc = { + buyToken: buyTokenAddress, + sellToken: sellTokenAddress, + sellAmount: "1000000000000000000", + dsaAddress: dsaWallet0.address + }; + const response1 = await axios.get("https://api.instadapp.io/defi/arbitrum/1inch/swap", { + params: paramDaiUsdc + }); + + const data1 = response1.data; + // console.log(data1); + let unitAmt1Inch = data1.unitAmt; + const calldata1Inch = data1.calldata; + buyTokenAmount1Inch = data1.buyTokenAmount; + console.log(buyTokenAmount1Inch); + + function getCallData(connector: string, unitAmt: any, callData: any) { + var abi = [ + "function swap(address,address,uint256,uint256,bytes,uint256)", + "function sell(address,address,uint256,uint256,bytes,uint256)" + ]; + var iface = new ethers.utils.Interface(abi); + const spell = connector === "1INCH-A" ? "sell" : "swap"; + let data = iface.encodeFunctionData(spell, [ + buyTokenAddress, + sellTokenAddress, + srcAmount, + unitAmt, + callData, + 0 + ]); + return data; + } + let data1Inch = ethers.utils.hexlify(await getCallData("1INCH-A", unitAmt1Inch, calldata1Inch)); + let datas = [data1Inch]; + + let connectors = ["1INCH-A"]; + return [connectors, datas]; + } + + let arg = await getArg(); + const spells = [ + { + connector: connectorName, + method: "swap", + args: arg + } + ]; + const tx = await dsaWallet0.connect(wallet0).cast(...encodeSpells(spells), await wallet1.getAddress()); + const receipt = await tx.wait(); + + const daiToken = await ethers.getContractAt( + er20abi, + "0xDA10009cBd5D07dd0CeCc66161FC93D7c9000da1" // dai address + ); + + expect(await daiToken.balanceOf(dsaWallet0.address)).to.be.gte(buyTokenAmount1Inch); + expect(await ethers.provider.getBalance(dsaWallet0.address)).to.be.lte(ethers.utils.parseEther("9")); + }); + }); +}); diff --git a/test/arbitrum/uniswap-sell-beta/uniswap-sell-beta.ts b/test/arbitrum/uniswap-sell-beta/uniswap-sell-beta.ts new file mode 100644 index 0000000..c4cf12a --- /dev/null +++ b/test/arbitrum/uniswap-sell-beta/uniswap-sell-beta.ts @@ -0,0 +1,129 @@ +import { BigNumberish } from "@ethersproject/bignumber"; +import { Contract } from "@ethersproject/contracts"; +import { expect } from "chai"; +import hre, { artifacts } from "hardhat"; +const { ethers } = hre; + +const USDC_ADDR = "0xff970a61a04b1ca14834a43f5de4533ebddb5cc8"; +const WETH_ADDR = "0x82af49447d8a07e3bd95bd0d56f35241523fbab1"; + +describe("Uniswap-sell-beta", function () { + let UniswapSellBeta, uniswapSellBeta: Contract; + + async function setBalance(address: string) { + await hre.network.provider.send("hardhat_setBalance", [ + address, + ethers.utils.parseEther("10.0").toHexString(), + ]); + } + + async function impersonate(owner: string, account: any, token0: string, decimals: BigNumberish | undefined) { + const tokenArtifact = await artifacts.readArtifact( + "@openzeppelin/contracts/token/ERC20/IERC20.sol:IERC20" + ); + + setBalance(owner); + setBalance(account); + + await hre.network.provider.request({ + method: "hardhat_impersonateAccount", + params: [account], + }); + + const signer = await ethers.getSigner(account); + + const token = new ethers.Contract( + token0, + tokenArtifact.abi, + ethers.provider + ); + + // console.log((await token.balanceOf(account)).toString()); + + await token + .connect(signer) + .transfer(owner, ethers.utils.parseUnits("10", decimals)); + + await hre.network.provider.request({ + method: "hardhat_stopImpersonatingAccount", + params: [account], + }); + } + + beforeEach(async () => { + const account0 = "0xa067668661c84476afcdc6fa5d758c4c01c34352"; + const account1 = "0x0db3fe3b770c95a0b99d1ed6f2627933466c0dd8"; + + const [owner, add1, add2] = await ethers.getSigners(); + await impersonate(owner.address, account1, USDC_ADDR, 6); + await impersonate(owner.address, account0, WETH_ADDR, 18); + + UniswapSellBeta = await ethers.getContractFactory( + "ConnectV2UniswapSellBeta" + ); + uniswapSellBeta = await UniswapSellBeta.deploy(); + await uniswapSellBeta.deployed(); + }); + + it("Should have contracts deployed.", async function () { + expect(uniswapSellBeta.address).to.exist; + }); + + it("Should swap WETH with USDC", async () => { + const [owner, add1, add2] = await ethers.getSigners(); + + const tokenArtifact = await artifacts.readArtifact( + "@openzeppelin/contracts/token/ERC20/IERC20.sol:IERC20" + ); + + const token = new ethers.Contract( + WETH_ADDR, + tokenArtifact.abi, + ethers.provider + ); + + const signer = await ethers.getSigner(owner.address); + + await token + .connect(signer) + .transfer(uniswapSellBeta.address, ethers.utils.parseUnits("10.0", 18)); + + const tx = await uniswapSellBeta.sell( + WETH_ADDR, + USDC_ADDR, + 3000, + ethers.utils.parseUnits("10.0", 18), + 0 + ); + // console.log(tx); + }); + + it("Should swap USDC with WETH", async () => { + const [owner, add1, add2] = await ethers.getSigners(); + + const tokenArtifact = await artifacts.readArtifact( + "@openzeppelin/contracts/token/ERC20/IERC20.sol:IERC20" + ); + + const token = new ethers.Contract( + USDC_ADDR, + tokenArtifact.abi, + ethers.provider + ); + + const signer = await ethers.getSigner(owner.address); + + await token + .connect(signer) + .transfer(uniswapSellBeta.address, ethers.utils.parseUnits("10.0", 6)); + + const tx = await uniswapSellBeta.sell( + USDC_ADDR, + WETH_ADDR, + 3000, + ethers.utils.parseUnits("10.0", 6), + 0 + ); + // console.log(tx); + }); +}); diff --git a/test/arbitrum/uniswap/uniswap-swap.test.ts b/test/arbitrum/uniswap/uniswap-swap.test.ts new file mode 100644 index 0000000..a915ea7 --- /dev/null +++ b/test/arbitrum/uniswap/uniswap-swap.test.ts @@ -0,0 +1,138 @@ +import { expect } from "chai"; +import hre from "hardhat"; +const { web3, deployments, waffle, ethers } = hre; +const { provider, deployContract } = waffle; + +import { deployAndEnableConnector } from "../../../scripts/tests/deployAndEnableConnector"; +import { buildDSAv2 } from "../../../scripts/tests/buildDSAv2"; +import { encodeSpells } from "../../../scripts/tests/encodeSpells"; +import { getMasterSigner } from "../../../scripts/tests/getMasterSigner"; +import { addLiquidity } from "../../../scripts/tests/addLiquidity"; +import { addresses } from "../../../scripts/tests/arbitrum/addresses"; +import { abis } from "../../../scripts/constant/abis"; +import type { Signer, Contract } from "ethers"; + +import { abi } from "@uniswap/v3-periphery/artifacts/contracts/NonfungiblePositionManager.sol/NonfungiblePositionManager.json"; +import { ConnectV2UniswapV3SwapArbitrum__factory } from "../../../typechain"; + +const FeeAmount = { + LOW: 500, + MEDIUM: 3000, + HIGH: 10000 +}; + +const TICK_SPACINGS: Record = { + 500: 10, + 3000: 60, + 10000: 200 +}; + +const DAI_ADDR = "0xDA10009cBd5D07dd0CeCc66161FC93D7c9000da1"; + +let tokenIds: any[] = []; +let liquidities: any[] = []; +const abiCoder = ethers.utils.defaultAbiCoder; + +describe("UniswapV3 [Arbitrum]", function () { + const connectorName = "UniswapV3-Swap-v1"; + + let dsaWallet0: any; + let masterSigner: Signer; + let instaConnectorsV2: Contract; + let connector: Contract; + let nftManager: Contract; + + const wallets = provider.getWallets(); + const [wallet0, wallet1, wallet2, wallet3] = wallets; + before(async () => { + await hre.network.provider.request({ + method: "hardhat_reset", + params: [ + { + forking: { + // @ts-ignore + jsonRpcUrl: hre.config.networks.hardhat.forking.url, + blockNumber: 11201500 + } + } + ] + }); + masterSigner = await getMasterSigner(); + instaConnectorsV2 = await ethers.getContractAt(abis.core.connectorsV2, addresses.core.connectorsV2); + connector = await deployAndEnableConnector({ + connectorName, + contractArtifact: ConnectV2UniswapV3SwapArbitrum__factory, + signer: masterSigner, + connectors: instaConnectorsV2 + }); + console.log("Connector address", connector.address); + }); + + it("Should have contracts deployed.", async function () { + expect(!!instaConnectorsV2.address).to.be.true; + expect(!!connector.address).to.be.true; + expect(!!(await masterSigner.getAddress())).to.be.true; + }); + + describe("DSA wallet setup", function () { + it("Should build DSA v2", async function () { + dsaWallet0 = await buildDSAv2(wallet0.address); + expect(!!dsaWallet0.address).to.be.true; + }); + + it("Deposit ETH & DAI into DSA wallet", async function () { + await wallet0.sendTransaction({ + to: dsaWallet0.address, + value: ethers.utils.parseEther("10") + }); + expect(await ethers.provider.getBalance(dsaWallet0.address)).to.be.gte(ethers.utils.parseEther("10")); + + await addLiquidity("dai", dsaWallet0.address, ethers.utils.parseEther("100000")); + }); + }); + + describe("Main", function () { + it("Should buy successfully", async function () { + const ethAmount = ethers.utils.parseEther("0.1"); + const unitAmt = ethers.utils.parseEther("0.000359232717483266"); + const ethAddress = "0xeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee"; + const getId = "0"; + const setId = "0"; + + const spells = [ + { + connector: connectorName, + method: "buy", + args: [DAI_ADDR, ethAddress, FeeAmount.MEDIUM, unitAmt, ethAmount, getId, setId] + } + ]; + + const tx = await dsaWallet0.connect(wallet0).cast(...encodeSpells(spells), wallet1.address); + const receipt = await tx.wait(); + // console.log(receipt); + }); + + it("Should sell successfully", async function () { + const ethAmount = ethers.utils.parseEther("0.1"); + const unitAmt = ethers.utils.parseEther("2770.23"); + const ethAddress = "0xeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee"; + const getId = "0"; + const setId = "0"; + + const spells = [ + { + connector: connectorName, + method: "sell", + args: [DAI_ADDR, ethAddress, FeeAmount.MEDIUM, unitAmt, ethAmount, getId, setId] + } + ]; + + const tx = await dsaWallet0.connect(wallet0).cast(...encodeSpells(spells), wallet1.address); + const receipt = await tx.wait(); + // console.log(receipt); + }); + }); +}); + +const getMinTick = (tickSpacing: number) => Math.ceil(-887272 / tickSpacing) * tickSpacing; +const getMaxTick = (tickSpacing: number) => Math.floor(887272 / tickSpacing) * tickSpacing; diff --git a/test/avalanche/0x/zeroEx.test.ts b/test/avalanche/0x/zeroEx.test.ts new file mode 100644 index 0000000..dfb1c01 --- /dev/null +++ b/test/avalanche/0x/zeroEx.test.ts @@ -0,0 +1,159 @@ +import hre from "hardhat"; +import axios from "axios"; +import { expect } from "chai"; +const { ethers } = hre; //check +import { BigNumber } from "bignumber.js"; +import { deployAndEnableConnector } from "../../../scripts/tests/deployAndEnableConnector"; +import { buildDSAv2 } from "../../../scripts/tests/buildDSAv2"; +import { encodeSpells } from "../../../scripts/tests/encodeSpells"; +import { getMasterSigner } from "../../../scripts/tests/getMasterSigner"; +import { addresses } from "../../../scripts/tests/avalanche/addresses"; +import { abis } from "../../../scripts/constant/abis"; +import {ConnectV2ZeroExAvalanche__factory } from "../../../typechain"; +import er20abi from "../../../scripts/constant/abi/basics/erc20.json"; +import type { Signer, Contract } from "ethers"; + +describe("ZeroEx", function() { + const connectorName = "zeroEx-test"; + + let dsaWallet0: Contract; + let wallet0: Signer, wallet1: Signer; + let masterSigner: Signer; + let instaConnectorsV2: Contract; + let connector: Contract; + + before(async () => { + await hre.network.provider.request({ + method: "hardhat_reset", + params: [ + { + forking: { + // @ts-ignore + jsonRpcUrl: hre.config.networks.hardhat.forking.url, + }, + }, + ], + }); + [wallet0, wallet1] = await ethers.getSigners(); + + masterSigner = await getMasterSigner(); + instaConnectorsV2 = await ethers.getContractAt( + abis.core.connectorsV2, + addresses.core.connectorsV2 + ); + connector = await deployAndEnableConnector({ + connectorName, + contractArtifact: ConnectV2ZeroExAvalanche__factory, + signer: masterSigner, + connectors: instaConnectorsV2, + }); + console.log("Connector address", connector.address); + }); + + it("Should have contracts deployed.", async function() { + expect(!!instaConnectorsV2.address).to.be.true; + expect(!!connector.address).to.be.true; + expect(!!(await masterSigner.getAddress())).to.be.true; + }); + + describe("DSA wallet setup", function() { + it("Should build DSA v2", async function() { + dsaWallet0 = await buildDSAv2(await wallet0.getAddress()); + expect(!!dsaWallet0.address).to.be.true; + }); + + it("Deposit matic into DSA wallet", async function() { + await wallet0.sendTransaction({ + to: dsaWallet0.address, + value: ethers.utils.parseEther("10"), + }); + + expect(await ethers.provider.getBalance(dsaWallet0.address)).to.be.gte( + ethers.utils.parseEther("10") + ); + }); + }); + + describe("Main", function() { + it("should swap the tokens", async function() { + let buyTokenAmount: any; + async function getArg() { + // const slippage = 0.5; + /* matic -> dai */ + const sellTokenAddress = "0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE"; // matic, decimals 18 + const sellTokenDecimals = 18; + const buyTokenAddress = "0x8f3Cf7ad23Cd3CaDbD9735AFf958023239c6A063"; // dai, decimals 18 + const buyTokenDecimals = 18; + const amount = 1; + + const srcAmount = new BigNumber(amount) + .times(new BigNumber(10).pow(sellTokenDecimals)) + .toFixed(0); + + let url = `https://avalanche.api.0x.org/swap/v1/quote`; + + const params = { + buyToken: "DAI", + sellToken: "AVAX", + sellAmount: "1000000000000000000", // Always denominated in wei + }; + const response = await axios + .get(url, { params: params }) + .then((data: any) => data); + +console.log(response); + buyTokenAmount = response.data.buyAmount; + const calldata = response.data.data; + + let caculateUnitAmt = () => { + const buyTokenAmountRes = new BigNumber(buyTokenAmount) + .dividedBy(new BigNumber(10).pow(buyTokenDecimals)) + .toFixed(8); + + let unitAmt: any = new BigNumber(buyTokenAmountRes).dividedBy( + new BigNumber(amount) + ); + + unitAmt = unitAmt.multipliedBy((100 - 0.3) / 100); + unitAmt = unitAmt.multipliedBy(1e18).toFixed(0); + return unitAmt; + }; + let unitAmt = caculateUnitAmt(); + + return [ + buyTokenAddress, + sellTokenAddress, + srcAmount, + unitAmt, + calldata, + 0, + ]; + } + + let arg = await getArg(); + const spells = [ + { + connector: connectorName, + method: "swap", + args: arg, + }, + ]; + const tx = await dsaWallet0 + .connect(wallet0) + .cast(...encodeSpells(spells), await wallet1.getAddress()); + const receipt = await tx.wait(); + + const daiToken = await ethers.getContractAt( + er20abi, + "0x8f3Cf7ad23Cd3CaDbD9735AFf958023239c6A063" // dai address + ); + + expect(await daiToken.balanceOf(dsaWallet0.address)).to.be.gte( + buyTokenAmount + ); + expect(await ethers.provider.getBalance(dsaWallet0.address)).to.be.lte( + ethers.utils.parseEther("9") + ); + }); + }); +}); diff --git a/test/avalanche/1inch/oneInch.test.ts b/test/avalanche/1inch/oneInch.test.ts new file mode 100644 index 0000000..9ac0ba4 --- /dev/null +++ b/test/avalanche/1inch/oneInch.test.ts @@ -0,0 +1,160 @@ +import hre from "hardhat"; +import axios from "axios"; +import { expect } from "chai"; +const { ethers } = hre; //check +import { BigNumber } from "bignumber.js"; +import { deployAndEnableConnector } from "../../../scripts/tests/deployAndEnableConnector"; +import { buildDSAv2 } from "../../../scripts/tests/buildDSAv2"; +import { encodeSpells } from "../../../scripts/tests/encodeSpells"; +import { getMasterSigner } from "../../../scripts/tests/getMasterSigner"; +import { addresses } from "../../../scripts/tests/avalanche/addresses"; +import { abis } from "../../../scripts/constant/abis"; +import {ConnectV2OneInchV4Avalanche__factory } from "../../../typechain"; +import er20abi from "../../../scripts/constant/abi/basics/erc20.json"; +import type { Signer, Contract } from "ethers"; + +describe("1Inch", function() { + const connectorName = "1inch-connector-test"; + + let dsaWallet0: Contract; + let wallet0: Signer, wallet1: Signer; + let masterSigner: Signer; + let instaConnectorsV2: Contract; + let connector: Contract; + + before(async () => { + await hre.network.provider.request({ + method: "hardhat_reset", + params: [ + { + forking: { + // @ts-ignore + jsonRpcUrl: hre.config.networks.hardhat.forking.url, + }, + }, + ], + }); + [wallet0, wallet1] = await ethers.getSigners(); + + masterSigner = await getMasterSigner(); + instaConnectorsV2 = await ethers.getContractAt( + abis.core.connectorsV2, + addresses.core.connectorsV2 + ); + connector = await deployAndEnableConnector({ + connectorName, + contractArtifact: ConnectV2OneInchV4Avalanche__factory, + signer: masterSigner, + connectors: instaConnectorsV2, + }); + console.log("Connector address", connector.address); + }); + + it("Should have contracts deployed.", async function() { + expect(!!instaConnectorsV2.address).to.be.true; + expect(!!connector.address).to.be.true; + expect(!!(await masterSigner.getAddress())).to.be.true; + }); + + describe("DSA wallet setup", function() { + it("Should build DSA v2", async function() { + dsaWallet0 = await buildDSAv2(await wallet0.getAddress()); + expect(!!dsaWallet0.address).to.be.true; + }); + + it("Deposit avax into DSA wallet", async function() { + await wallet0.sendTransaction({ + to: dsaWallet0.address, + value: ethers.utils.parseEther("10"), + }); + + expect(await ethers.provider.getBalance(dsaWallet0.address)).to.be.gte( + ethers.utils.parseEther("10") + ); + }); + }); + + describe("Main", function() { + it("should swap the tokens", async function() { + let buyTokenAmount: any; + async function getArg() { + // const slippage = 0.5; + /* avax -> dai */ + const sellTokenAddress = "0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE"; // avax, decimals 18 + const sellTokenDecimals = 18; + const buyTokenAddress = "0xd586e7f844cea2f87f50152665bcbc2c279d8d70"; // dai, decimals 18 + const buyTokenDecimals = 18; + const amount = 1; + + const srcAmount = new BigNumber(amount) + .times(new BigNumber(10).pow(sellTokenDecimals)) + .toFixed(0); + + let url = `https://api.1inch.exchange/v4.0/43114/swap`; + + const params = { + toTokenAddress: buyTokenAddress, + fromTokenAddress: sellTokenAddress, + amount: "1000000000000000000", // Always denominated in wei + fromAddress: dsaWallet0.address, + slippage:1 + }; + const response = await axios + .get(url, { params: params }) + .then((data: any) => data); + + buyTokenAmount = response.data.toTokenAmount; + const calldata = response.data.tx.data; + + let caculateUnitAmt = () => { + const buyTokenAmountRes = new BigNumber(buyTokenAmount) + .dividedBy(new BigNumber(10).pow(buyTokenDecimals)) + .toFixed(8); + + let unitAmt: any = new BigNumber(buyTokenAmountRes).dividedBy( + new BigNumber(amount) + ); + + unitAmt = unitAmt.multipliedBy((100 - 0.3) / 100); + unitAmt = unitAmt.multipliedBy(1e18).toFixed(0); + return unitAmt; + }; + let unitAmt = caculateUnitAmt(); + + return [ + buyTokenAddress, + sellTokenAddress, + srcAmount, + unitAmt, + calldata, + 0, + ]; + } + + let arg = await getArg(); + const spells = [ + { + connector: connectorName, + method: "sell", + args: arg, + }, + ]; + const tx = await dsaWallet0 + .connect(wallet0) + .cast(...encodeSpells(spells), await wallet1.getAddress()); + const receipt = await tx.wait(); + + const daiToken = await ethers.getContractAt( + er20abi, + "0x8f3Cf7ad23Cd3CaDbD9735AFf958023239c6A063" // dai address + ); + + expect(await daiToken.balanceOf(dsaWallet0.address)).to.be.gte( + buyTokenAmount + ); + expect(await ethers.provider.getBalance(dsaWallet0.address)).to.be.lte( + ethers.utils.parseEther("9") + ); + }); + }); +}); diff --git a/test/avalanche/aave/aaveV3-import-test.ts b/test/avalanche/aave/aaveV3-import-test.ts new file mode 100644 index 0000000..563ccec --- /dev/null +++ b/test/avalanche/aave/aaveV3-import-test.ts @@ -0,0 +1,353 @@ +import { expect, should } from "chai"; +import hre, { ethers, waffle } from "hardhat"; +import type { Signer, Contract } from "ethers"; +import { ecsign, ecrecover, pubToAddress } from "ethereumjs-util"; +import { keccak256 } from "@ethersproject/keccak256"; +import { defaultAbiCoder } from "@ethersproject/abi"; +import { BigNumber } from "bignumber.js"; +import { buildDSAv2 } from "../../../scripts/tests/buildDSAv2"; +import { addresses } from "../../../scripts/tests/avalanche/addresses"; +import { deployAndEnableConnector } from "../../../scripts/tests/deployAndEnableConnector"; +import { abis } from "../../../scripts/constant/abis"; +import { getMasterSigner } from "../../../scripts/tests/getMasterSigner"; +import { parseEther, parseUnits } from "ethers/lib/utils"; +import { encodeSpells } from "../../../scripts/tests/encodeSpells"; +import encodeFlashcastData from "../../../scripts/tests/encodeFlashcastData"; +import { ConnectV2AaveV3ImportPermitAvalanche__factory, IERC20__factory } from "../../../typechain"; + +const ABI = [ + "function DOMAIN_SEPARATOR() public view returns (bytes32)", + "function balanceOf(address account) public view returns (uint256)", + "function nonces(address owner) public view returns (uint256)" +]; + +const aDaiAddress = "0x82E64f49Ed5EC1bC6e43DAD4FC8Af9bb3A2312EE"; +const aaveAddress = "0x794a61358d6845594f94dc1db02a252b5b4814ad"; +// const account = "0xf04adbf75cdfc5ed26eea4bbbb991db002036bdd"; +let account = "0x95eEA1Bdd19A8C40E9575048Dd0d6577D11a84e5"; +const DAI = "0xd586E7F844cEa2F87f50152665BCbc2C279D8d70"; +const ETH = "0x49D5c2BdFfac6CE2BFdB6640F4F80f226bc10bAB"; +const mnemonic = "test test test test test test test test test test test junk"; +const connectorName = "AAVE-V3-IMPORT-PERMIT-X"; +let signer: any, wallet0: any; + +const aaveAbi =[ + { + inputs: [ + { + internalType: "address", + name: "asset", + type: "address" + }, + { + internalType: "uint256", + name: "amount", + type: "uint256" + }, + { + internalType: "uint256", + name: "interestRateMode", + type: "uint256" + }, + { + internalType: "uint16", + name: "referralCode", + type: "uint16" + }, + { + internalType: "address", + name: "onBehalfOf", + type: "address" + } + ], + name: "borrow", + outputs: [], + stateMutability: "nonpayable", + type: "function" + }, + { + inputs: [ + { + internalType: "address", + name: "asset", + type: "address" + }, + { + internalType: "uint256", + name: "amount", + type: "uint256" + }, + { + internalType: "address", + name: "onBehalfOf", + type: "address" + }, + { + internalType: "uint16", + name: "referralCode", + type: "uint16" + } + ], + name: "deposit", + outputs: [], + stateMutability: "nonpayable", + type: "function" + }, + { + inputs: [ + { + internalType: "address", + name: "asset", + type: "address" + }, + { + internalType: "uint256", + name: "amount", + type: "uint256" + }, + { + internalType: "address", + name: "onBehalfOf", + type: "address" + }, + { + internalType: "uint16", + name: "referralCode", + type: "uint16" + } + ], + name: "supply", + outputs: [], + stateMutability: "nonpayable", + type: "function" + } +]; + +const erc20Abi = [ + { + constant: false, + inputs: [ + { + name: "_spender", + type: "address" + }, + { + name: "_value", + type: "uint256" + } + ], + name: "approve", + outputs: [ + { + name: "", + type: "bool" + } + ], + payable: false, + stateMutability: "nonpayable", + type: "function" + }, + { + constant: true, + inputs: [ + { + name: "_owner", + type: "address" + } + ], + name: "balanceOf", + outputs: [ + { + name: "balance", + type: "uint256" + } + ], + payable: false, + stateMutability: "view", + type: "function" + }, + { + constant: false, + inputs: [ + { + name: "_to", + type: "address" + }, + { + name: "_value", + type: "uint256" + } + ], + name: "transfer", + outputs: [ + { + name: "", + type: "bool" + } + ], + payable: false, + stateMutability: "nonpayable", + type: "function" + } +]; + +const token = new ethers.Contract(DAI, erc20Abi); +const aDai = new ethers.Contract(aDaiAddress, ABI); +const ethToken = new ethers.Contract(ETH, erc20Abi); +const aave = new ethers.Contract(aaveAddress, aaveAbi); + +describe("Import Aave v3 Position for Avalanche", function () { + let dsaWallet0: any; + let masterSigner: Signer; + let instaConnectorsV2: Contract; + let connector: any; + + const wallet = ethers.Wallet.fromMnemonic(mnemonic); + + before(async () => { + await hre.network.provider.request({ + method: "hardhat_reset", + params: [ + { + forking: { + //@ts-ignore + jsonRpcUrl: hre.config.networks.hardhat.forking.url, + blockNumber: 13024200 + } + } + ] + }); + masterSigner = await getMasterSigner(); + [wallet0] = await ethers.getSigners(); + await hre.network.provider.send("hardhat_setBalance", [account, ethers.utils.parseEther("10").toHexString()]); + + await hre.network.provider.request({ + method: "hardhat_impersonateAccount", + params: [account] + }); + + signer = await ethers.getSigner(account); + + await token.connect(signer).transfer(wallet0.address, ethers.utils.parseEther("8")); + instaConnectorsV2 = await ethers.getContractAt(abis.core.connectorsV2, addresses.core.connectorsV2); + connector = await deployAndEnableConnector({ + connectorName, + contractArtifact: ConnectV2AaveV3ImportPermitAvalanche__factory, + signer: masterSigner, + connectors: instaConnectorsV2 + }); + }); + + describe("check user AAVE position", async () => { + it("Should create Aave v3 position of DAI(collateral) and ETH(debt)", async () => { + // approve DAI to aavePool + await token.connect(wallet0).approve(aaveAddress, parseEther("8")); + + //deposit DAI in aave + await aave.connect(wallet0).supply(DAI, parseEther("8"), wallet.address, 3228); + console.log("Supplied DAI on aave"); + + //borrow ETH from aave + await aave.connect(wallet0).borrow(ETH, parseUnits("3", 6), 2, 3228, wallet.address); + console.log("Borrowed ETH from aave"); + }); + + it("Should check position of user", async () => { + expect(await aDai.connect(wallet0).balanceOf(wallet.address)).to.be.gte( + new BigNumber(8).multipliedBy(1e18).toString() + ); + + expect(await ethToken.connect(wallet0).balanceOf(wallet.address)).to.be.gte( + new BigNumber(3).multipliedBy(1e6).toString() + ); + }); + }); + + describe("Deployment", async () => { + it("Should set correct name", async () => { + expect(await connector.name()).to.eq("Aave-v3-import-permit-v1"); + }); + }); + + describe("DSA wallet setup", async () => { + it("Should build DSA v2", async () => { + dsaWallet0 = await buildDSAv2(wallet.address); + expect(!!dsaWallet0.address).to.be.true; + }); + + it("Deposit ETH into DSA wallet", async function () { + await wallet0.sendTransaction({ + to: dsaWallet0.address, + value: ethers.utils.parseEther("3") + }); + + expect(await ethers.provider.getBalance(dsaWallet0.address)).to.be.gte(ethers.utils.parseEther("3")); + }); + }); + + describe("Aave position migration", async () => { + it("Should migrate Aave position", async () => { + const DOMAIN_SEPARATOR = await aDai.connect(wallet0).DOMAIN_SEPARATOR(); + const PERMIT_TYPEHASH = "0x6e71edae12b1b97f4d1f60370fef10105fa2faae0126114a169c64845d6126c9"; + + let nonce = (await aDai.connect(wallet0).nonces(wallet.address)).toNumber(); + //Approving max amount + const amount = ethers.constants.MaxUint256; + const expiry = Date.now() + 20 * 60; + + const digest = keccak256( + ethers.utils.solidityPack( + ["bytes1", "bytes1", "bytes32", "bytes32"], + [ + "0x19", + "0x01", + DOMAIN_SEPARATOR, + keccak256( + defaultAbiCoder.encode( + ["bytes32", "address", "address", "uint256", "uint256", "uint256"], + [PERMIT_TYPEHASH, wallet.address, dsaWallet0.address, amount, nonce, expiry] + ) + ) + ] + ) + ); + const { v, r, s } = ecsign(Buffer.from(digest.slice(2), "hex"), Buffer.from(wallet.privateKey.slice(2), "hex")); + const amount0 = new BigNumber(await ethToken.connect(wallet0).balanceOf(wallet.address)); + const amountB = new BigNumber(amount0.toString()).multipliedBy(9).dividedBy(1e4); + const amountWithFee = amount0.plus(amountB); + + const flashSpells = [ + { + connector: "AAVE-V3-IMPORT-PERMIT-X", + method: "importAave", + args: [ + wallet.address, + [[DAI], [ETH], false, [amountB.toFixed(0)]], + [[v], [ethers.utils.hexlify(r)], [ethers.utils.hexlify(s)], [expiry]] + ] + }, + { + connector: "INSTAPOOL-C", + method: "flashPayback", + args: [ETH, amountWithFee.toFixed(0), 0, 0] + } + ]; + + const spells = [ + { + connector: "INSTAPOOL-C", + method: "flashBorrowAndCast", + args: [ETH, amount0.toString(), 1, encodeFlashcastData(flashSpells), "0x"] + } + ]; + const tx = await dsaWallet0.connect(wallet0).cast(...encodeSpells(spells), wallet.address); + const receipt = await tx.wait(); + }); + + it("Should check DSA AAVE position", async () => { + expect(await aDai.connect(wallet0).balanceOf(dsaWallet0.address)).to.be.gte( + new BigNumber(3).multipliedBy(1e18).toString() + ); + }); + }); +}); \ No newline at end of file diff --git a/test/avalanche/aave/v3-test.ts b/test/avalanche/aave/v3-test.ts new file mode 100644 index 0000000..3e9e23a --- /dev/null +++ b/test/avalanche/aave/v3-test.ts @@ -0,0 +1,273 @@ +import { expect, should } from "chai"; +import hre, { ethers, waffle } from "hardhat"; +import type { Signer, Contract } from "ethers"; +import { BigNumber } from "bignumber.js"; +import { buildDSAv2 } from "../../../scripts/tests/buildDSAv2"; +import { addresses } from "../../../scripts/tests/avalanche/addresses"; +import { deployAndEnableConnector } from "../../../scripts/tests/deployAndEnableConnector"; +import { abis } from "../../../scripts/constant/abis"; +import { getMasterSigner } from "../../../scripts/tests/getMasterSigner"; +import { parseEther, parseUnits } from "ethers/lib/utils"; +import { encodeSpells } from "../../../scripts/tests/encodeSpells"; +import { ConnectV2AaveV3Avalanche__factory, IERC20__factory } from "../../../typechain"; + +const ABI = ["function balanceOf(address account) public view returns (uint256)"]; + +const aDaiAddress = "0x82E64f49Ed5EC1bC6e43DAD4FC8Af9bb3A2312EE"; +const aaveAddress = "0x69FA688f1Dc47d4B5d8029D5a35FB7a548310654"; +const ETH = "0xB97EF9Ef8734C71904D8002F8b6Bc66Dd9c48a6E"; +let account = "0xC4Aa5b4d4049324C09376D586482c7F8fB57542a"; +const DAI = "0xd586E7F844cEa2F87f50152665BCbc2C279D8d70"; +const mnemonic = "test test test test test test test test test test test junk"; +const connectorName = "AAVE-V3-X"; +let signer: any, wallet0: any; + +const aaveAbi = [ + { + inputs: [ + { internalType: "address", name: "asset", type: "address" }, + { internalType: "address", name: "user", type: "address" } + ], + name: "getUserReserveData", + outputs: [ + { internalType: "uint256", name: "currentATokenBalance", type: "uint256" }, + { internalType: "uint256", name: "currentStableDebt", type: "uint256" }, + { internalType: "uint256", name: "currentVariableDebt", type: "uint256" }, + { internalType: "uint256", name: "principalStableDebt", type: "uint256" }, + { internalType: "uint256", name: "scaledVariableDebt", type: "uint256" }, + { internalType: "uint256", name: "stableBorrowRate", type: "uint256" }, + { internalType: "uint256", name: "liquidityRate", type: "uint256" }, + { internalType: "uint40", name: "stableRateLastUpdated", type: "uint40" }, + { internalType: "bool", name: "usageAsCollateralEnabled", type: "bool" } + ], + stateMutability: "view", + type: "function" + } +]; + +const erc20Abi = [ + { + constant: false, + inputs: [ + { + name: "_spender", + type: "address" + }, + { + name: "_value", + type: "uint256" + } + ], + name: "approve", + outputs: [ + { + name: "", + type: "bool" + } + ], + payable: false, + stateMutability: "nonpayable", + type: "function" + }, + { + constant: true, + inputs: [], + name: "totalSupply", + outputs: [ + { + name: "", + type: "uint256" + } + ], + payable: false, + stateMutability: "view", + type: "function" + }, + { + constant: true, + inputs: [ + { + name: "_owner", + type: "address" + } + ], + name: "balanceOf", + outputs: [ + { + name: "balance", + type: "uint256" + } + ], + payable: false, + stateMutability: "view", + type: "function" + }, + { + constant: false, + inputs: [ + { + name: "_to", + type: "address" + }, + { + name: "_value", + type: "uint256" + } + ], + name: "transfer", + outputs: [ + { + name: "", + type: "bool" + } + ], + payable: false, + stateMutability: "nonpayable", + type: "function" + } +]; + +const token = new ethers.Contract(DAI, erc20Abi); +const aDai = new ethers.Contract(aDaiAddress, ABI); +const ethToken = new ethers.Contract(ETH, erc20Abi); +const aave = new ethers.Contract(aaveAddress, aaveAbi); + +describe("Aave v3 Position for Avalanche", function () { + let dsaWallet0: any; + let masterSigner: Signer; + let instaConnectorsV2: Contract; + let connector: any; + + const wallet = ethers.Wallet.fromMnemonic(mnemonic); + + before(async () => { + await hre.network.provider.request({ + method: "hardhat_reset", + params: [ + { + forking: { + //@ts-ignore + jsonRpcUrl: hre.config.networks.hardhat.forking.url, + blockNumber: 16201000 + } + } + ] + }); + masterSigner = await getMasterSigner(); + [wallet0] = await ethers.getSigners(); + await hre.network.provider.send("hardhat_setBalance", [account, ethers.utils.parseEther("10").toHexString()]); + + await hre.network.provider.request({ + method: "hardhat_impersonateAccount", + params: [account] + }); + + signer = await ethers.getSigner(account); + + await token.connect(signer).transfer(wallet0.address, ethers.utils.parseEther("8")); + + instaConnectorsV2 = await ethers.getContractAt(abis.core.connectorsV2, addresses.core.connectorsV2); + connector = await deployAndEnableConnector({ + connectorName, + contractArtifact: ConnectV2AaveV3Avalanche__factory, + signer: masterSigner, + connectors: instaConnectorsV2 + }); + }); + + describe("Deployment", async () => { + it("Should set correct name", async () => { + expect(await connector.name()).to.eq("AaveV3-v1.2"); + }); + }); + + describe("DSA wallet setup", async () => { + it("Should build DSA v2", async () => { + dsaWallet0 = await buildDSAv2(wallet0.address); + expect(!!dsaWallet0.address).to.be.true; + }); + + it("Deposit ETH into DSA wallet", async function () { + await wallet0.sendTransaction({ + to: dsaWallet0.address, + value: ethers.utils.parseEther("5") + }); + + expect(await ethers.provider.getBalance(dsaWallet0.address)).to.be.gte(ethers.utils.parseEther("5")); + }); + }); + + describe("check user AAVE position", async () => { + it("Should create DSA Aave v3 position of DAI(collateral) and USDC(debt)", async () => { + await token.connect(signer).transfer(dsaWallet0.address, ethers.utils.parseEther("8")); + + const spells = [ + //deposit DAI in aave + { + connector: connectorName, + method: "deposit", + args: [DAI, parseEther("8"), 0, 0] + }, + //borrow USDC from aave + { + connector: connectorName, + method: "borrow", + args: [ETH, parseUnits("1", 6), 2, 0, 0] + } + ]; + const tx = await dsaWallet0.connect(wallet0).cast(...encodeSpells(spells), wallet0.address); + const receipt = await tx.wait(); + }); + + it("Should check position of dsa", async () => { + expect(await aDai.connect(wallet0).balanceOf(dsaWallet0.address)).to.be.gte( + new BigNumber(8).multipliedBy(1e18).toString() + ); + + expect(await ethToken.connect(wallet0).balanceOf(dsaWallet0.address)).to.be.gte( + new BigNumber(1).multipliedBy(1e6).toString() + ); + + expect((await aave.connect(wallet0).getUserReserveData(ETH, dsaWallet0.address)).currentStableDebt).to.be.equal( + 0 + ); + expect((await aave.connect(wallet0).getUserReserveData(ETH, dsaWallet0.address)).currentVariableDebt).to.be.gte( + new BigNumber(1).multipliedBy(1e6).toString() + ); + console.log(`\tstable borrow before: ${(await aave.connect(wallet0).getUserReserveData(ETH, dsaWallet0.address)).currentStableDebt}`); + console.log(`\tvariable borrow before: ${(await aave.connect(wallet0).getUserReserveData(ETH, dsaWallet0.address)).currentVariableDebt}`); + }); + + it("Should swap borrowRateMode", async () => { + const spells = [ + //deposit DAI in aave + { + connector: connectorName, + method: "swapBorrowRateMode", + args: [ETH, 2] + } + ]; + const tx = await dsaWallet0.connect(wallet0).cast(...encodeSpells(spells), wallet0.address); + const receipt = await tx.wait(); + }); + + it("Should check position of dsa", async () => { + expect(await aDai.connect(wallet0).balanceOf(dsaWallet0.address)).to.be.gte( + new BigNumber(8).multipliedBy(1e18).toString() + ); + + expect(await ethToken.connect(wallet0).balanceOf(dsaWallet0.address)).to.be.gte( + new BigNumber(1).multipliedBy(1e6).toString() + ); + expect( + (await aave.connect(wallet0).getUserReserveData(ETH, dsaWallet0.address)).currentVariableDebt + ).to.be.equal(0); + expect((await aave.connect(wallet0).getUserReserveData(ETH, dsaWallet0.address)).currentStableDebt).to.be.gte( + new BigNumber(1).multipliedBy(1e6).toString() + ); + + console.log(`\tstable borrow after: ${(await aave.connect(wallet0).getUserReserveData(ETH, dsaWallet0.address)).currentStableDebt}`); + console.log(`\tvariable borrow after: ${(await aave.connect(wallet0).getUserReserveData(ETH, dsaWallet0.address)).currentVariableDebt}`) + }); + }); +}); diff --git a/test/avalanche/dsa-spell/dsa-spell.test.ts b/test/avalanche/dsa-spell/dsa-spell.test.ts new file mode 100644 index 0000000..e5430cd --- /dev/null +++ b/test/avalanche/dsa-spell/dsa-spell.test.ts @@ -0,0 +1,185 @@ +import hre from "hardhat"; +import axios from "axios"; +import { expect } from "chai"; +const { ethers } = hre; //check +import { BigNumber } from "bignumber.js"; +import { deployAndEnableConnector } from "../../../scripts/tests/deployAndEnableConnector"; +import { buildDSAv2 } from "../../../scripts/tests/buildDSAv2"; +import { encodeSpells } from "../../../scripts/tests/encodeSpells"; +import { getMasterSigner } from "../../../scripts/tests/getMasterSigner"; +import { addresses } from "../../../scripts/tests/avalanche/addresses"; +import { abis } from "../../../scripts/constant/abis"; +import { ConnectV2DSASpellAvalanche__factory } from "../../../typechain"; +import er20abi from "../../../scripts/constant/abi/basics/erc20.json"; +import type { Signer, Contract } from "ethers"; + +describe("DSA Spell", function () { + const connectorName = "dsa-spell-test"; + + let dsaWallet0: any; + let dsaWallet1: any; + let dsaWallet2: any; + let walletB: any; + let wallet0: any; + let walletBsigner: any; + let masterSigner: Signer; + let instaConnectorsV2: Contract; + let connector: any; + + before(async () => { + await hre.network.provider.request({ + method: "hardhat_reset", + params: [ + { + forking: { + // @ts-ignore + jsonRpcUrl: hre.config.networks.hardhat.forking.url + } + } + ] + }); + [wallet0] = await ethers.getSigners(); + + masterSigner = await getMasterSigner(); + instaConnectorsV2 = await ethers.getContractAt(abis.core.connectorsV2, addresses.core.connectorsV2); + connector = await deployAndEnableConnector({ + connectorName, + contractArtifact: ConnectV2DSASpellAvalanche__factory, + signer: masterSigner, + connectors: instaConnectorsV2 + }); + console.log("\tConnector address", connector.address); + }); + + it("Should have contracts deployed.", async function () { + expect(!!instaConnectorsV2.address).to.be.true; + expect(!!connector.address).to.be.true; + expect(!!(await masterSigner.getAddress())).to.be.true; + }); + + describe("DSA wallet setup", function () { + it("Should build DSA v2", async function () { + dsaWallet0 = await buildDSAv2(wallet0.address); + expect(!!dsaWallet0.address).to.be.true; + walletB = await ethers.getSigner(dsaWallet0.address); + dsaWallet1 = await buildDSAv2(dsaWallet0.address); + expect(!!dsaWallet1.address).to.be.true; + console.log(`\t${dsaWallet1.address}`); + dsaWallet2 = await buildDSAv2(wallet0.address); + expect(!!dsaWallet2.address).to.be.true; + }); + + it("Deposit avax into DSA wallet 0", async function () { + await wallet0.sendTransaction({ + to: dsaWallet0.address, + value: ethers.utils.parseEther("10") + }); + + expect(await ethers.provider.getBalance(dsaWallet0.address)).to.be.gte(ethers.utils.parseEther("10")); + }); + + it("Deposit avax into DSA wallet 1", async function () { + await wallet0.sendTransaction({ + to: dsaWallet1.address, + value: ethers.utils.parseEther("10") + }); + + expect(await ethers.provider.getBalance(dsaWallet1.address)).to.be.gte(ethers.utils.parseEther("10")); + }); + }); + + describe("Main", function () { + let AVAX = "0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE"; + let USDC = "0xB97EF9Ef8734C71904D8002F8b6Bc66Dd9c48a6E"; + let usdc = new ethers.Contract(USDC, abis.basic.erc20); + let aAVAX = "0x6d80113e533a2C0fe82EaBD35f1875DcEA89Ea97"; + let aAvax = new ethers.Contract(aAVAX, abis.basic.aToken); + var abi = [ + "function withdraw(address,uint256,address,uint256,uint256)", + "function deposit(address,uint256,uint256,uint256)", + "function borrow(address,uint256,uint256,uint256,uint256)" + ]; + function getCallData(spell: string, params: any) { + var iface = new ethers.utils.Interface(abi); + let data = iface.encodeFunctionData(spell, params); + return ethers.utils.hexlify(data); + } + + it("should cast spells", async function () { + async function getArg(connectors: any, spells: any, params: any) { + let datas = []; + for (let i = 0; i < connectors.length; i++) { + datas.push(getCallData(spells[i], params[i])); + } + return [dsaWallet1.address, connectors, datas]; + } + + let connectors = ["BASIC-A", "AAVE-V3-A", "AAVE-V3-A"]; + let methods = ["withdraw", "deposit", "borrow"]; + let params = [ + [AVAX, ethers.utils.parseEther("2"), dsaWallet0.address, 0, 0], + [AVAX, ethers.constants.MaxUint256, 0, 0], + [USDC, ethers.utils.parseUnits("1", 6), 2, 0, 0] + ]; + let arg = await getArg(connectors, methods, params); + + const spells = [ + { + connector: connectorName, + method: "castOnDSA", + args: arg + } + ]; + const tx = await dsaWallet0.connect(wallet0).cast(...encodeSpells(spells), await wallet0.getAddress()); + const receipt = await tx.wait(); + }); + + it("should check balances after cast on DSA", async function () { + expect(await ethers.provider.getBalance(dsaWallet1.address)).to.be.lte(0); + expect(await usdc.connect(wallet0).balanceOf(dsaWallet1.address)).to.be.gte( + new BigNumber(1).multipliedBy(1e6).toString() + ); + expect(await ethers.provider.getBalance(dsaWallet0.address)).to.be.gte( + new BigNumber(12).multipliedBy(1e18).toString() + ); + }); + + it("should cast spell on the first successful", async function () { + async function getArg(connectors: any, spells: any, params: any) { + let datas = []; + for (let i = 0; i < connectors.length; i++) { + datas.push(getCallData(spells[i], params[i])); + } + return [connectors, datas]; + } + + let connectors = ["AAVE-V3-A", "AAVE-V2-A"]; + let methods = ["deposit","deposit"]; + let params = [ + [AVAX, ethers.utils.parseEther("10"), 0, 0], + [AVAX, ethers.utils.parseEther("10"), 0, 0] + ]; + let arg = await getArg(connectors, methods, params); + const spells = [ + { + connector: connectorName, + method: "castAny", + args: arg + } + ]; + const tx = await dsaWallet0 + .connect(wallet0) + .cast(...encodeSpells(spells), await wallet0.getAddress()); + const receipt = await tx.wait(); + }); + + it("should check balances after spells on DSA", async function () { + expect(await ethers.provider.getBalance(dsaWallet0.address)).to.be.lte( + new BigNumber(2).multipliedBy(1e18).toString() + ); + expect(await aAvax.connect(wallet0).balanceOf(dsaWallet0.address)).to.be.gte( + new BigNumber(10).multipliedBy(1e18).toString() + ); + }); + }); +}); diff --git a/test/avalanche/pangolin/pangolin_exchange.test.ts b/test/avalanche/pangolin/pangolin_exchange.test.ts new file mode 100644 index 0000000..d4bd37f --- /dev/null +++ b/test/avalanche/pangolin/pangolin_exchange.test.ts @@ -0,0 +1,282 @@ +import { expect } from "chai"; +import hre from "hardhat"; + +const { waffle, ethers } = hre; +const { provider } = waffle; + +import { deployAndEnableConnector } from "../../../scripts/tests/deployAndEnableConnector"; +import { buildDSAv2 } from "../../../scripts/tests/buildDSAv2"; +import { encodeSpells } from "../../../scripts/tests/encodeSpells"; +import { getMasterSigner } from "../../../scripts/tests/getMasterSigner"; +import { addresses } from "../../../scripts/tests/avalanche/addresses"; +import { abis } from "../../../scripts/constant/abis"; +import { Signer, Contract } from "ethers"; + +import { ConnectV2PngAvalanche__factory } from "../../../typechain"; + +const PNG_ADDRESS = "0x60781C2586D68229fde47564546784ab3fACA982"; +const WAVAX_ADDRESS = "0xB31f66AA3C1e785363F0875A1B74E27b85FD66c7"; +const PNG_AVAX_LP_ADDRESS = "0xd7538cABBf8605BdE1f4901B47B8D42c61DE0367"; + +describe("Pangolin DEX - Avalanche", function () { + const pangolinConnectorName = "PANGOLIN-TEST-A" + + let dsaWallet0: Contract; + let masterSigner: Signer; + let instaConnectorsV2: Contract; + let pangolinConnector: Contract; + + const wallets = provider.getWallets() + const [wallet0, wallet1] = wallets + before(async () => { + await hre.network.provider.request({ + method: "hardhat_reset", + params: [ + { + forking: { + jsonRpcUrl: `https://api.avax.network/ext/bc/C/rpc`, + blockNumber: 8197390 + }, + }, + ], + }); + + masterSigner = await getMasterSigner(); + instaConnectorsV2 = await ethers.getContractAt( + abis.core.connectorsV2, + addresses.core.connectorsV2 + ); + + // Deploy and enable Pangolin Connector + pangolinConnector = await deployAndEnableConnector({ + connectorName: pangolinConnectorName, + contractArtifact: ConnectV2PngAvalanche__factory, + signer: masterSigner, + connectors: instaConnectorsV2 + }); + console.log("Pangolin Connector address: "+ pangolinConnector.address); + }) + + it("Should have contracts deployed.", async function () { + expect(!!instaConnectorsV2.address).to.be.true; + expect(!!pangolinConnector.address).to.be.true; + expect(!!(await masterSigner.getAddress())).to.be.true; + }); + + describe("DSA wallet setup", function () { + it("Should build DSA v2", async function () { + dsaWallet0 = await buildDSAv2(wallet0.getAddress()) + expect(!!dsaWallet0.address).to.be.true; + }); + + it("Deposit 10 AVAX into DSA wallet", async function () { + await wallet0.sendTransaction({ + to: dsaWallet0.address, + value: ethers.utils.parseEther("10") + }); + expect(await ethers.provider.getBalance(dsaWallet0.address)).to.be.gte(ethers.utils.parseEther("10")); + }); + }); + + describe("Main - PANGOLIN PNG/AVAX Liquidity Test", function () { + + it("Should use pangolin to swap AVAX for PNG, and deposit to PNG/AVAX LP", async function () { + const amount = ethers.utils.parseEther("100"); // 100 PNG + const int_slippage = 0.03 + const slippage = ethers.utils.parseEther(int_slippage.toString()); + const setId = "83528353"; + + const PangolinRouterABI = [ + "function getAmountsOut(uint amountIn, address[] calldata path) external view returns (uint[] memory amounts)" + ]; + + // Get amount of AVAX for 100 POOL from Pangolin + const PangolinRouter = await ethers.getContractAt( + PangolinRouterABI, + "0xE54Ca86531e17Ef3616d22Ca28b0D458b6C89106" + ); + const amounts = await PangolinRouter.getAmountsOut( + amount, + [ + PNG_ADDRESS, + WAVAX_ADDRESS + ] + ); + + const amtA = amounts[0]; + const amtB = amounts[1]; + const unitAmt = (amtB * (1 + int_slippage)) / amtA; + const unitAmount = ethers.utils.parseEther(unitAmt.toString()); + + const spells = [ + { + connector: pangolinConnectorName, + method: "buy", + args: [ + PNG_ADDRESS, + "0xeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee", + amount, + unitAmount, + 0, + setId + ] + }, + { + connector: pangolinConnectorName, + method: "deposit", + args: [ + PNG_ADDRESS, + "0xeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee", + amount, + unitAmount, + slippage, + 0, + setId + ] + }, + ]; + + // Before Spell + let avaxBalance = await ethers.provider.getBalance(dsaWallet0.address); + expect(avaxBalance, `AVAX Balance equals 10`).to.be.eq(ethers.utils.parseEther("10")); + + let pngToken = await ethers.getContractAt(abis.basic.erc20, PNG_ADDRESS); + const pngBalance = await pngToken.balanceOf(dsaWallet0.address); + expect(pngBalance, `PNG Token greater than 0`).to.be.eq(0); + + let pangolinLPToken = await ethers.getContractAt( + abis.basic.erc20, + PNG_AVAX_LP_ADDRESS + ); + const pangolinPoolAVAXBalance = await pangolinLPToken.balanceOf(dsaWallet0.address); + expect(pangolinPoolAVAXBalance, `Pangolin PNG/AVAX LP equals 0`).to.be.eq(0); + + // Run spell transaction + const tx = await dsaWallet0.connect(wallet0).cast( + ...encodeSpells(spells), wallet1.address + ); + const receipt = await tx.wait(); + + // After spell + avaxBalance = await ethers.provider.getBalance(dsaWallet0.address); + expect(avaxBalance, `AVAX Balance less than 10`).to.be.lt(ethers.utils.parseEther("10")); + + const pngBalanceAfter = await pngToken.balanceOf(dsaWallet0.address) + expect(pngBalanceAfter, `PNG Token to be same after spell`).to.be.eq(pngBalance); + + const pangolinPoolAVAXBalanceAfter = await pangolinLPToken.balanceOf(dsaWallet0.address); + expect( + pangolinPoolAVAXBalanceAfter, + `Pangolin PNG/AVAX LP greater than 0` + ).to.be.gt(0); + }); + + it("Should use pangolin to withdraw to PNG/AVAX LP, and swap PNG for AVAX", async function () { + const amount = ethers.utils.parseEther("100"); // 100 PNG + const int_slippage = 0.03 + + // Before Spell + let avaxBalance = await ethers.provider.getBalance(dsaWallet0.address); + let pngToken = await ethers.getContractAt(abis.basic.erc20, PNG_ADDRESS); + let pangolinLPToken = await ethers.getContractAt( + abis.basic.erc20, + PNG_AVAX_LP_ADDRESS + ); + + const pngBalance = await pngToken.balanceOf(dsaWallet0.address) + expect(pngBalance, `PNG Token balance equal to 0`).to.be.eq(0); + + const pangolinPoolAVAXBalance = await pangolinLPToken.balanceOf(dsaWallet0.address); + expect(pangolinPoolAVAXBalance, `Pangolin PNG/AVAX LP greater than 0`).to.be.gt(0); + + const PangolinRouterABI = [ + "function getAmountsOut(uint amountIn, address[] calldata path) external view returns (uint[] memory amounts)" + ]; + + // Get amount of avax for 100 PNG from Pangolin + const PangolinRouter= await ethers.getContractAt( + PangolinRouterABI, + "0xE54Ca86531e17Ef3616d22Ca28b0D458b6C89106" + ); + const amounts = await PangolinRouter.getAmountsOut( + amount, + [ + PNG_ADDRESS, + WAVAX_ADDRESS + ] + ); + const amtA = amounts[0]; + const amtB = amounts[1]; + const unitAmtA = ethers.utils.parseEther( + (amtA * (1 - int_slippage) / pangolinPoolAVAXBalance).toString() + ); + const unitAmtB = ethers.utils.parseEther( + (amtB * (1 - int_slippage) / pangolinPoolAVAXBalance).toString() + ); + + let spells = [ + { + connector: pangolinConnectorName, + method: "withdraw", + args: [ + PNG_ADDRESS, + "0xeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee", + pangolinPoolAVAXBalance, + unitAmtA, + unitAmtB, + 0, + [ + 0, + 0 + ] + ] + }, + ]; + + // Run spell transaction (withdraw token of pool) + const tx = await dsaWallet0.connect(wallet0).cast( + ...encodeSpells(spells), + wallet1.address + ); + const receipt = await tx.wait(); + + // After spell + const pangolinPoolAVAXBalanceAfter = await pangolinLPToken.balanceOf( + dsaWallet0.address + ); + expect(pangolinPoolAVAXBalanceAfter, `Pangolin PNG/AVAX LP equal 0`).to.be.eq(0); + + let pngBalanceAfter = await pngToken.balanceOf(dsaWallet0.address); + expect(pngBalanceAfter, `PNG Token balance greater than`).to.be.gt(0); + const unitAmt = amount.div(pngBalanceAfter); + + spells = [ + { + connector: pangolinConnectorName, + method: "sell", + args: [ + "0xeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee", + PNG_ADDRESS, + pngBalanceAfter, + unitAmt, + 0, + 0 + ] + }, + ]; + + // Run spell transaction (withdraw token of pool) + const tx2 = await dsaWallet0.connect(wallet0).cast(...encodeSpells(spells), wallet1.address); + const receipt2 = await tx2.wait(); + + let avaxBalanceAfter = await ethers.provider.getBalance(dsaWallet0.address); + expect( + avaxBalanceAfter, + `AVAX Balance After greater than AVAX Balance Before` + ).to.be.gt(avaxBalance); + + pngBalanceAfter = await pngToken.balanceOf(dsaWallet0.address); + expect(pngBalanceAfter, `PNG Token balance equal 0`).to.be.eq(0); + }); + }) +}); diff --git a/test/avalanche/pangolin/pangolin_stake.test.ts b/test/avalanche/pangolin/pangolin_stake.test.ts new file mode 100644 index 0000000..7297b57 --- /dev/null +++ b/test/avalanche/pangolin/pangolin_stake.test.ts @@ -0,0 +1,821 @@ +import { expect } from "chai"; +import hre from "hardhat"; + +const { waffle, ethers } = hre; +const { provider } = waffle; + +import { deployAndEnableConnector } from "../../../scripts/tests/deployAndEnableConnector"; +import { buildDSAv2 } from "../../../scripts/tests/buildDSAv2"; +import { encodeSpells } from "../../../scripts/tests/encodeSpells"; +import { getMasterSigner } from "../../../scripts/tests/getMasterSigner"; +import { addresses } from "../../../scripts/tests/avalanche/addresses"; +import { abis } from "../../../scripts/constant/abis"; +import { Signer, Contract, BigNumber } from "ethers"; + +import { ConnectV2PngAvalanche__factory, ConnectV2PngStakeAvalanche__factory } from "../../../typechain"; + +const PNG_ADDRESS = "0x60781C2586D68229fde47564546784ab3fACA982"; +const WAVAX_ADDRESS = "0xB31f66AA3C1e785363F0875A1B74E27b85FD66c7"; +const PNG_AVAX_LP_ADDRESS = "0xd7538cABBf8605BdE1f4901B47B8D42c61DE0367"; +const PNG_STAKING_ADDRESS = "0x88afdaE1a9F58Da3E68584421937E5F564A0135b"; + +describe("Pangolin Stake - Avalanche", function () { + const pangolinConnectorName = "PANGOLIN-TEST-A" + const pangolinStakeConnectorName = "PANGOLIN-STAKE-TEST-A" + + let dsaWallet0: Contract; + let masterSigner: Signer; + let instaConnectorsV2: Contract; + let pangolinConnector: Contract; + let pangolinStakeConnector: Contract; + + let PNG: Contract; + + const wallets = provider.getWallets() + const [wallet0, wallet1] = wallets + before(async () => { + await hre.network.provider.request({ + method: "hardhat_reset", + params: [ + { + forking: { + jsonRpcUrl: `https://api.avax.network/ext/bc/C/rpc`, + blockNumber: 8197390 + }, + }, + ], + }); + + PNG = await ethers.getContractAt( + abis.basic.erc20, + PNG_ADDRESS + ); + + masterSigner = await getMasterSigner(); + instaConnectorsV2 = await ethers.getContractAt( + abis.core.connectorsV2, + addresses.core.connectorsV2 + ); + + // Deploy and enable Pangolin Connector + pangolinConnector = await deployAndEnableConnector({ + connectorName: pangolinConnectorName, + contractArtifact: ConnectV2PngAvalanche__factory, + signer: masterSigner, + connectors: instaConnectorsV2 + }); + console.log("Pangolin Connector address: "+ pangolinConnector.address); + + // Deploy and enable Pangolin Stake Connector + pangolinStakeConnector = await deployAndEnableConnector({ + connectorName: pangolinStakeConnectorName, + contractArtifact: ConnectV2PngStakeAvalanche__factory, + signer: masterSigner, + connectors: instaConnectorsV2 + }); + console.log("Pangolin Stake Connector address: "+ pangolinStakeConnector.address); + }) + + it("Should have contracts deployed.", async function () { + expect(!!instaConnectorsV2.address).to.be.true; + expect(!!pangolinConnector.address).to.be.true; + expect(!!pangolinStakeConnector.address).to.be.true; + expect(!!(await masterSigner.getAddress())).to.be.true; + }); + + describe("DSA wallet setup", function () { + it("Should build DSA v2", async function () { + dsaWallet0 = await buildDSAv2(wallet0.getAddress()) + expect(!!dsaWallet0.address).to.be.true; + }); + + it("Deposit 10 AVAX into DSA wallet", async function () { + await wallet0.sendTransaction({ + to: dsaWallet0.address, + value: ethers.utils.parseEther("10") + }); + expect(await ethers.provider.getBalance(dsaWallet0.address)).to.be.gte(ethers.utils.parseEther("10")); + }); + }); + + describe("Pangolin Staking - LP Stake Test", function () { + let lpAmount: BigNumber; + let pangolinLPToken: Contract; + // Buy 100 PNG and deposity in PNG/AVAX LP + before(async () => { + const amount = ethers.utils.parseEther("100"); // 100 PNG + const int_slippage = 0.03 + const slippage = ethers.utils.parseEther(int_slippage.toString()); + const setId = "0"; + + const PangolinRouterABI = [ + "function getAmountsOut(uint amountIn, address[] calldata path) external view returns (uint[] memory amounts)" + ]; + + // Get amount of AVAX for 200 PNG from Pangolin + const PangolinRouter = await ethers.getContractAt( + PangolinRouterABI, + "0xE54Ca86531e17Ef3616d22Ca28b0D458b6C89106" + ); + const amounts = await PangolinRouter.getAmountsOut( + amount, + [ + PNG_ADDRESS, + WAVAX_ADDRESS + ] + ); + + const amtA = amounts[0]; + const amtB = amounts[1]; + const unitAmt = (amtB * (1 + int_slippage)) / amtA; + const unitAmount = ethers.utils.parseEther(unitAmt.toString()); + + const spells = [ + { + connector: pangolinConnectorName, + method: "buy", + args: [ + PNG_ADDRESS, + "0xeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee", + amount, + unitAmount, + 0, + 0 + ] + }, + { + connector: pangolinConnectorName, + method: "deposit", + args: [ + PNG_ADDRESS, + "0xeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee", + amount, + unitAmount, + slippage, + 0, + setId + ] + }, + ]; + // Run spell transaction + const tx = await dsaWallet0.connect(wallet0).cast( + ...encodeSpells(spells), wallet1.address + ); + const receipt = await tx.wait(); + pangolinLPToken = await ethers.getContractAt( + abis.basic.erc20, + PNG_AVAX_LP_ADDRESS + ); + }); + + it("Check if has PNG/AVAX LP", async function () { + const pangolinPoolAVAXBalance = await pangolinLPToken.balanceOf(dsaWallet0.address); + expect(pangolinPoolAVAXBalance, `Pangolin PNG/AVAX LP greater than 0`).to.be.gt(0); + console.log("PNG/AVAX LP: ", ethers.utils.formatUnits(pangolinPoolAVAXBalance, "ether").toString()) + lpAmount = pangolinPoolAVAXBalance; + }); + + it("Check if all functions reverts by: Invalid pid!", async function () { + const pid = BigNumber.from("999999999999"); + const amount = ethers.utils.parseEther("1"); + const getId = 0; + const setId = 0; + + let spells = [ + { + connector: pangolinStakeConnectorName, + method: "depositLpStake", + args: [ + pid, + amount, + getId, + setId + ] + } + ]; + await expect( + dsaWallet0.connect(wallet0).cast( + ...encodeSpells(spells), + wallet1.address + ) + ).to.be.revertedWith("Invalid pid!"); + + spells[0].method = "withdrawLpStake" + await expect( + dsaWallet0.connect(wallet0).cast( + ...encodeSpells(spells), + wallet1.address + ) + ).to.be.revertedWith("Invalid pid!"); + + spells[0].method = "withdrawAndClaimLpRewards" + await expect( + dsaWallet0.connect(wallet0).cast( + ...encodeSpells(spells), + wallet1.address + ) + ).to.be.revertedWith("Invalid pid!"); + + spells = [ + { + connector: pangolinStakeConnectorName, + method: "claimLpRewards", + args: [ + pid + ] + } + ]; + await expect( + dsaWallet0.connect(wallet0).cast( + ...encodeSpells(spells), + wallet1.address + ) + ).to.be.revertedWith("Invalid pid!"); + + spells[0].method = "emergencyWithdrawLpStake" + await expect( + dsaWallet0.connect(wallet0).cast( + ...encodeSpells(spells), + wallet1.address + ) + ).to.be.revertedWith("Invalid pid!"); + }); + + it("Check if all functions reverts by: 'Invalid amount, amount cannot be 0'", async function () { + let spells = [ + { + connector: pangolinStakeConnectorName, + method: "depositLpStake", + args: [ + 0, + 0, + 0, + 0 + ] + } + ]; + await expect( + dsaWallet0.connect(wallet0).cast( + ...encodeSpells(spells), + wallet1.address + ) + ).to.be.revertedWith("Invalid amount, amount cannot be 0"); + + spells[0].method = "withdrawLpStake" + await expect( + dsaWallet0.connect(wallet0).cast( + ...encodeSpells(spells), + wallet1.address + ) + ).to.be.revertedWith("Invalid amount, amount cannot be 0"); + + spells[0].method = "withdrawLpStake" + await expect( + dsaWallet0.connect(wallet0).cast( + ...encodeSpells(spells), + wallet1.address + ) + ).to.be.revertedWith("Invalid amount, amount cannot be 0"); + + spells[0].method = "withdrawAndClaimLpRewards" + await expect( + dsaWallet0.connect(wallet0).cast( + ...encodeSpells(spells), + wallet1.address + ) + ).to.be.revertedWith("Invalid amount, amount cannot be 0"); + }); + + describe("depositLpStake function", function () { + it("Check if depositLpStake function reverts by: Invalid amount, amount greater than balance of LP token", async function () { + const amount = lpAmount.mul(2); + const spells = [ + { + connector: pangolinStakeConnectorName, + method: "depositLpStake", + args: [ + 0, + amount, + 0, + 0 + ] + } + ]; + await expect( + dsaWallet0.connect(wallet0).cast( + ...encodeSpells(spells), + wallet1.address + ) + ).to.be.revertedWith("Invalid amount, amount greater than balance of LP token"); + }); + + it("Check if success in depositLpStake", async function () { + const spells = [ + { + connector: pangolinStakeConnectorName, + method: "depositLpStake", + args: [ + 0, + lpAmount, + 0, + 0 + ] + } + ]; + await expect( + dsaWallet0.connect(wallet0).cast( + ...encodeSpells(spells), + wallet1.address + ) + ).to.be.not.reverted; + // Check if PNG/AVAX LP is equal 0 + const balance = await pangolinLPToken.balanceOf(dsaWallet0.address); + expect(balance).to.be.eq(0); + }); + + it("Check if depositLpStake function reverts by: Invalid LP token balance", async function () { + const spells = [ + { + connector: pangolinStakeConnectorName, + method: "depositLpStake", + args: [ + 0, + lpAmount, + 0, + 0 + ] + } + ]; + await expect( + dsaWallet0.connect(wallet0).cast( + ...encodeSpells(spells), + wallet1.address + ) + ).to.be.revertedWith("Invalid LP token balance"); + }); + }); + + describe("claimLpRewards function", function () { + it("Check if success in claimLpRewards", async function () { + // Increase Time in 20 seconds + await hre.network.provider.send("evm_increaseTime", [20]); + // Mine new block + await hre.network.provider.send("evm_mine"); + const spells = [ + { + connector: pangolinStakeConnectorName, + method: "claimLpRewards", + args: [0] + } + ]; + await expect( + dsaWallet0.connect(wallet0).cast( + ...encodeSpells(spells), + wallet1.address + ) + ).to.be.not.reverted; + // Checks if the wallet has more than 100 PNG + const balance = await PNG.balanceOf(dsaWallet0.address); + expect(balance).to.be.gt(0); + }); + }); + + describe("withdrawLpStake function", function () { + it("Check if withdrawLpStake function reverts by: Invalid amount, amount greater than balance of staking", async function () { + const amount = lpAmount.mul(2); + const spells = [ + { + connector: pangolinStakeConnectorName, + method: "withdrawLpStake", + args: [ + 0, + amount, + 0, + 0 + ] + } + ]; + await expect( + dsaWallet0.connect(wallet0).cast( + ...encodeSpells(spells), + wallet1.address + ) + ).to.be.revertedWith("Invalid amount, amount greater than balance of staking"); + }); + + it("Check if success in withdrawLpStake", async function () { + const spells = [ + { + connector: pangolinStakeConnectorName, + method: "withdrawLpStake", + args: [ + 0, + lpAmount.div(2), + 0, + 0 + ] + } + ]; + await expect( + dsaWallet0.connect(wallet0).cast( + ...encodeSpells(spells), + wallet1.address + ) + ).to.be.not.reverted; + // Check if PNG/AVAX LP is equal 0 + const balance = await pangolinLPToken.balanceOf(dsaWallet0.address); + expect(balance).to.be.eq(lpAmount.div(2)); + }); + }); + + describe("withdrawAndClaimLpRewards function", function () { + it("Check if withdrawAndClaimLpRewards function reverts by: Invalid amount, amount greater than balance of staking", async function () { + const amount = lpAmount.mul(2); + const spells = [ + { + connector: pangolinStakeConnectorName, + method: "withdrawAndClaimLpRewards", + args: [ + 0, + amount, + 0, + 0 + ] + } + ]; + await expect( + dsaWallet0.connect(wallet0).cast( + ...encodeSpells(spells), + wallet1.address + ) + ).to.be.revertedWith("Invalid amount, amount greater than balance of staking"); + }); + + it("Check if success in withdrawAndClaimLpRewards", async function () { + let balance = await pangolinLPToken.balanceOf(dsaWallet0.address); + const png_balance = await PNG.balanceOf(dsaWallet0.address); + const amount = lpAmount.sub(balance) + const spells = [ + { + connector: pangolinStakeConnectorName, + method: "withdrawAndClaimLpRewards", + args: [ + 0, + amount, + 0, + 0 + ] + } + ]; + await expect( + dsaWallet0.connect(wallet0).cast( + ...encodeSpells(spells), + wallet1.address + ) + ).to.be.not.reverted; + // Check if PNG/AVAX LP is equal 0 + balance = await pangolinLPToken.balanceOf(dsaWallet0.address); + expect(balance).to.be.eq(lpAmount); + const new_png_balance = await PNG.balanceOf(dsaWallet0.address); + expect(new_png_balance).to.be.gt(png_balance); + }); + }); + + describe("emergencyWithdrawLpStake function", function () { + // Deposit LP again + before(async () => { + const spells = [ + { + connector: pangolinStakeConnectorName, + method: "depositLpStake", + args: [ + 0, + lpAmount, + 0, + 0 + ] + } + ]; + await dsaWallet0.connect(wallet0).cast( + ...encodeSpells(spells), + wallet1.address + ) + }); + + it("Check if success in emergencyWithdrawLpStake", async function () { + let balance = await pangolinLPToken.balanceOf(dsaWallet0.address); + const amount = lpAmount.sub(balance) + const spells = [ + { + connector: pangolinStakeConnectorName, + method: "emergencyWithdrawLpStake", + args: [0] + } + ]; + await expect( + dsaWallet0.connect(wallet0).cast( + ...encodeSpells(spells), + wallet1.address + ) + ).to.be.not.reverted; + // Check if PNG/AVAX LP is equal 0 + balance = await pangolinLPToken.balanceOf(dsaWallet0.address); + expect(balance).to.be.eq(lpAmount); + }); + }); + }); + + describe("Pangolin Staking - Single Stake Test (PNG)", function () { + let pngToken: Contract; + let stakingContract: Contract; + let stakingBalance: BigNumber; + before(async () => { + const amount = ethers.utils.parseEther("100"); // 100 PNG + const int_slippage = 0.03 + + const PangolinRouterABI = [ + "function getAmountsOut(uint amountIn, address[] calldata path) external view returns (uint[] memory amounts)" + ]; + + // Get amount of AVAX for 200 PNG from Pangolin + const PangolinRouter = await ethers.getContractAt( + PangolinRouterABI, + "0xE54Ca86531e17Ef3616d22Ca28b0D458b6C89106" + ); + const amounts = await PangolinRouter.getAmountsOut( + amount, + [ + PNG_ADDRESS, + WAVAX_ADDRESS + ] + ); + + const amtA = amounts[0]; + const amtB = amounts[1]; + const unitAmt = (amtB * (1 + int_slippage)) / amtA; + const unitAmount = ethers.utils.parseEther(unitAmt.toString()); + + const spells = [ + { + connector: pangolinConnectorName, + method: "buy", + args: [ + PNG_ADDRESS, + "0xeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee", + amount, + unitAmount, + 0, + 0 + ] + } + ]; + // Run spell transaction + const tx = await dsaWallet0.connect(wallet0).cast( + ...encodeSpells(spells), wallet1.address + ); + const receipt = await tx.wait(); + + pngToken = await ethers.getContractAt(abis.basic.erc20, PNG_ADDRESS); + stakingContract = await ethers.getContractAt(abis.basic.erc20, PNG_STAKING_ADDRESS); + }); + + it("Check if has 100 PNG", async function () { + const amount = ethers.utils.parseEther("100"); + const pngBalance = await pngToken.balanceOf(dsaWallet0.address); + expect(pngBalance, `PNG Token is equal 100`).to.be.gt(amount.toString()); + }); + + it("Check if some functions reverts by: Invalid amount, amount cannot be 0", async function () { + const amount = 0; + const getId = 0; + const setId = 0; + let spells = [ + { + connector: pangolinStakeConnectorName, + method: "depositPNGStake", + args: [ + PNG_STAKING_ADDRESS, + amount, + getId, + setId + ] + } + ]; + await expect( + dsaWallet0.connect(wallet0).cast( + ...encodeSpells(spells), + wallet1.address + ) + ).to.be.revertedWith("Invalid amount, amount cannot be 0"); + + spells[0].method = "withdrawPNGStake" + await expect( + dsaWallet0.connect(wallet0).cast( + ...encodeSpells(spells), + wallet1.address + ) + ).to.be.revertedWith("Invalid amount, amount cannot be 0"); + }); + + describe("depositPNGStake function", function () { + it("Check if reverts by: Invalid amount, amount greater than balance of PNG", async function () { + const amount = ethers.utils.parseEther("200") + let spells = [ + { + connector: pangolinStakeConnectorName, + method: "depositPNGStake", + args: [ + PNG_STAKING_ADDRESS, + amount, + 0, + 0 + ] + } + ]; + await expect( + dsaWallet0.connect(wallet0).cast( + ...encodeSpells(spells), + wallet1.address + ) + ).to.be.revertedWith("Invalid amount, amount greater than balance of PNG"); + }); + + it("Check if success in depositPNGStake", async function () { + const amount = await pngToken.balanceOf(dsaWallet0.address); + let spells = [ + { + connector: pangolinStakeConnectorName, + method: "depositPNGStake", + args: [ + PNG_STAKING_ADDRESS, + amount, + 0, + 0 + ] + } + ]; + await expect( + dsaWallet0.connect(wallet0).cast( + ...encodeSpells(spells), + wallet1.address + ) + ).to.be.not.reverted; + const new_png_balance = await pngToken.balanceOf(dsaWallet0.address); + expect(new_png_balance).to.be.eq(0); + const staking_balance = await stakingContract.balanceOf(dsaWallet0.address); + expect(staking_balance).to.be.gt(0); + stakingBalance = staking_balance + }); + + it("Check if reverts by: Invalid PNG balance", async function () { + const amount = ethers.utils.parseEther("100") + let spells = [ + { + connector: pangolinStakeConnectorName, + method: "depositPNGStake", + args: [ + PNG_STAKING_ADDRESS, + amount, + 0, + 0 + ] + } + ]; + await expect( + dsaWallet0.connect(wallet0).cast( + ...encodeSpells(spells), + wallet1.address + ) + ).to.be.revertedWith("Invalid PNG balance"); + }); + }); + + describe("withdrawPNGStake function", function () { + it("Check if reverts by: Invalid amount, amount greater than balance of staking", async function () { + const amount = ethers.utils.parseEther("200") + let spells = [ + { + connector: pangolinStakeConnectorName, + method: "withdrawPNGStake", + args: [ + PNG_STAKING_ADDRESS, + amount, + 0, + 0 + ] + } + ]; + await expect( + dsaWallet0.connect(wallet0).cast( + ...encodeSpells(spells), + wallet1.address + ) + ).to.be.revertedWith("Invalid amount, amount greater than balance of staking"); + }); + + it("Check if success in withdrawPNGStake", async function () { + const amount = ethers.utils.parseEther("50"); + let spells = [ + { + connector: pangolinStakeConnectorName, + method: "withdrawPNGStake", + args: [ + PNG_STAKING_ADDRESS, + amount, + 0, + 0 + ] + } + ]; + await expect( + dsaWallet0.connect(wallet0).cast( + ...encodeSpells(spells), + wallet1.address + ) + ).to.be.not.reverted; + + const balance = await pngToken.balanceOf(dsaWallet0.address); + expect(balance).to.be.eq(amount); + }); + }); + + describe("claimPNGStakeReward function", function () { + it("Check if success in claimPNGStakeReward", async function () { + // Increase Time in 20 seconds + await hre.network.provider.send("evm_increaseTime", [20]); + // Mine new block + await hre.network.provider.send("evm_mine"); + const amount = ethers.utils.parseEther("50"); + let spells = [ + { + connector: pangolinStakeConnectorName, + method: "claimPNGStakeReward", + args: [PNG_STAKING_ADDRESS] + } + ]; + await expect( + dsaWallet0.connect(wallet0).cast( + ...encodeSpells(spells), + wallet1.address + ) + ).to.be.not.reverted; + + const balance = await pngToken.balanceOf(dsaWallet0.address); + expect(balance).to.be.gt(amount); + }); + + it("Check if reverts by: No rewards to claim", async function () { + let spells = [ + { + connector: pangolinStakeConnectorName, + method: "claimPNGStakeReward", + args: [PNG_STAKING_ADDRESS] + } + ]; + await expect( + dsaWallet0.connect(wallet0).cast( + ...encodeSpells(spells), + wallet1.address + ) + ).to.be.revertedWith("No rewards to claim"); + }); + }); + + describe("exitPNGStake function", function () { + it("Check if success in exitPNGStake", async function () { + let spells = [ + { + connector: pangolinStakeConnectorName, + method: "exitPNGStake", + args: [PNG_STAKING_ADDRESS] + } + ]; + await expect( + dsaWallet0.connect(wallet0).cast( + ...encodeSpells(spells), + wallet1.address + ) + ).to.be.not.reverted; + + const balance = await stakingContract.balanceOf(dsaWallet0.address); + expect(balance).to.be.eq(0); + }); + + it("Check if reverts by: No balance to exit", async function () { + let spells = [ + { + connector: pangolinStakeConnectorName, + method: "exitPNGStake", + args: [PNG_STAKING_ADDRESS] + } + ]; + await expect( + dsaWallet0.connect(wallet0).cast( + ...encodeSpells(spells), + wallet1.address + ) + ).to.be.revertedWith("No balance to exit"); + }); + }); + }); +}); diff --git a/test/avalanche/sushiswap/sushiswap.test.ts b/test/avalanche/sushiswap/sushiswap.test.ts new file mode 100644 index 0000000..c2ef92f --- /dev/null +++ b/test/avalanche/sushiswap/sushiswap.test.ts @@ -0,0 +1,148 @@ +import { expect } from "chai"; +import hre from "hardhat"; +const { waffle, ethers } = hre; +const { provider } = waffle; + +import { deployAndEnableConnector } from "../../../scripts/tests/deployAndEnableConnector"; +import { buildDSAv2 } from "../../../scripts/tests/buildDSAv2"; +import { encodeSpells } from "../../../scripts/tests/encodeSpells"; +import { getMasterSigner } from "../../../scripts/tests/getMasterSigner"; +import { addLiquidity } from "../../../scripts/tests/addLiquidity"; + +import { constants } from "../../../scripts/constant/constant"; +import { addresses } from "../../../scripts/tests/avalanche/addresses"; +import { abis } from "../../../scripts/constant/abis"; +import { ConnectV2SushiswapAvalanche__factory, ConnectV2SushiswapAvalanche } from "../../../typechain"; +import type { Signer, Contract } from "ethers"; + +const DAI_ADDR = "0xd586e7f844cea2f87f50152665bcbc2c279d8d70"; + +describe("Sushiswap", function () { + const connectorName = "Sushiswap-v1"; + + let dsaWallet0: Contract; + let masterSigner: Signer; + let instaConnectorsV2: Contract; + let connector: Contract; + + const wallets = provider.getWallets(); + const [wallet0, wallet1, wallet2, wallet3] = wallets; + before(async () => { + await hre.network.provider.request({ + method: "hardhat_reset", + params: [ + { + forking: { + // @ts-ignore + jsonRpcUrl: hre.config.networks.hardhat.forking.url, + blockNumber: 13005785 + } + } + ] + }); + + masterSigner = await getMasterSigner(); + instaConnectorsV2 = await ethers.getContractAt(abis.core.connectorsV2, addresses.core.connectorsV2); + connector = await deployAndEnableConnector({ + connectorName, + contractArtifact: ConnectV2SushiswapAvalanche__factory, + signer: masterSigner, + connectors: instaConnectorsV2 + }); + console.log("Connector address", connector.address); + }); + + it("Should have contracts deployed.", async function () { + expect(!!instaConnectorsV2.address).to.be.true; + expect(!!connector.address).to.be.true; + expect(!!(await masterSigner.getAddress())).to.be.true; + }); + + describe("DSA wallet setup", function () { + it("Should build DSA v2", async function () { + dsaWallet0 = await buildDSAv2(wallet0.address); + expect(!!dsaWallet0.address).to.be.true; + }); + + it("Deposit avax & DAI into DSA wallet", async function () { + await wallet0.sendTransaction({ + to: dsaWallet0.address, + value: ethers.utils.parseEther("10") + }); + expect(await ethers.provider.getBalance(dsaWallet0.address)).to.be.gte(ethers.utils.parseEther("10")); + + await addLiquidity("dai", dsaWallet0.address, ethers.utils.parseEther("10000")); + }); + + it("Deposit avax & USDT into DSA wallet", async function () { + await wallet0.sendTransaction({ + to: dsaWallet0.address, + value: ethers.utils.parseEther("10") + }); + expect(await ethers.provider.getBalance(dsaWallet0.address)).to.be.gte(ethers.utils.parseEther("10")); + + await addLiquidity("usdt", dsaWallet0.address, ethers.utils.parseEther("10000")); + }); + }); + + describe("Main", function () { + it("Should deposit successfully", async function () { + const avaxAmount = ethers.utils.parseEther("0.1"); + const daiUnitAmount = ethers.utils.parseEther("4000"); + const avaxAddress = constants.native_address; + + const getId = "0"; + const setId = "0"; + + const spells = [ + { + connector: connectorName, + method: "deposit", + args: [avaxAddress, DAI_ADDR, avaxAmount, daiUnitAmount, "500000000000000000", getId, setId] + } + ]; + + const tx = await dsaWallet0.connect(wallet0).cast(...encodeSpells(spells), wallet1.address); + let receipt = await tx.wait(); + }).timeout(10000000000); + + it("Should withdraw successfully", async function () { + const avaxAmount = ethers.utils.parseEther("0.1"); + const avaxAddress = constants.native_address; + + const getId = "0"; + const setIds = ["0", "0"]; + + const spells = [ + { + connector: connectorName, + method: "withdraw", + args: [avaxAddress, DAI_ADDR, avaxAmount, 0, 0, getId, setIds] + } + ]; + + const tx = await dsaWallet0.connect(wallet0).cast(...encodeSpells(spells), wallet1.address); + let receipt = await tx.wait(); + }); + + it("Should buy successfully", async function () { + const avaxAmount = ethers.utils.parseEther("0.1"); + const daiUnitAmount = ethers.utils.parseEther("4000"); + const avaxAddress = constants.native_address; + + const getId = "0"; + const setId = "0"; + + const spells = [ + { + connector: connectorName, + method: "buy", + args: [avaxAddress, DAI_ADDR, avaxAmount, daiUnitAmount, getId, setId] + } + ]; + + const tx = await dsaWallet0.connect(wallet0).cast(...encodeSpells(spells), wallet1.address); + let receipt = await tx.wait(); + }); + }); +}); diff --git a/test/avalanche/swap/swap-test.ts b/test/avalanche/swap/swap-test.ts new file mode 100644 index 0000000..e1aee93 --- /dev/null +++ b/test/avalanche/swap/swap-test.ts @@ -0,0 +1,188 @@ +import hre from "hardhat"; +import axios from "axios"; +import { expect } from "chai"; +const { ethers } = hre; //check +import { BigNumber } from "bignumber.js"; +import { deployAndEnableConnector } from "../../../scripts/tests/deployAndEnableConnector"; +import { buildDSAv2 } from "../../../scripts/tests/buildDSAv2"; +import { encodeSpells } from "../../../scripts/tests/encodeSpells"; +import { getMasterSigner } from "../../../scripts/tests/getMasterSigner"; +import { addresses } from "../../../scripts/tests/avalanche/addresses"; +import { abis } from "../../../scripts/constant/abis"; +import { ConnectV2SwapAggregatorAvalanche__factory } from "../../../typechain"; +import er20abi from "../../../scripts/constant/abi/basics/erc20.json"; +import type { Signer, Contract } from "ethers"; + +describe("Swap | Avalanche", function () { + const connectorName = "swap-test"; + + let dsaWallet0: Contract; + let wallet0: Signer, wallet1: Signer; + let masterSigner: Signer; + let instaConnectorsV2: Contract; + let connector: Contract; + + before(async () => { + await hre.network.provider.request({ + method: "hardhat_reset", + params: [ + { + forking: { + // @ts-ignore + jsonRpcUrl: hre.config.networks.hardhat.forking.url + } + } + ] + }); + [wallet0, wallet1] = await ethers.getSigners(); + + masterSigner = await getMasterSigner(); + instaConnectorsV2 = await ethers.getContractAt(abis.core.connectorsV2, addresses.core.connectorsV2); + connector = await deployAndEnableConnector({ + connectorName, + contractArtifact: ConnectV2SwapAggregatorAvalanche__factory, + signer: masterSigner, + connectors: instaConnectorsV2 + }); + console.log("Connector address", connector.address); + }); + + it("Should have contracts deployed.", async function () { + expect(!!instaConnectorsV2.address).to.be.true; + expect(!!connector.address).to.be.true; + expect(!!(await masterSigner.getAddress())).to.be.true; + }); + + describe("DSA wallet setup", function () { + it("Should build DSA v2", async function () { + dsaWallet0 = await buildDSAv2(await wallet0.getAddress()); + console.log(dsaWallet0.address); + expect(!!dsaWallet0.address).to.be.true; + }); + + it("Deposit matic into DSA wallet", async function () { + await wallet0.sendTransaction({ + to: dsaWallet0.address, + value: ethers.utils.parseEther("10") + }); + + expect(await ethers.provider.getBalance(dsaWallet0.address)).to.be.gte(ethers.utils.parseEther("10")); + }); + }); + + describe("Main", function () { + it("should swap the tokens", async function () { + let buyTokenAmountZeroX: any; + let unitAmount1Inch: any; + let calldata1Inch: any; + // let buyTokenAmount1Inch: any; + let buyTokenAmountParaswap: any; + + async function getArg() { + // const slippage = 0.5; + /* avax -> usdt */ + const sellTokenAddress = "0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE"; // matic, decimals 18 + const sellTokenDecimals = 18; + const buyTokenAddress = "0xd586E7F844cEa2F87f50152665BCbc2C279D8d70"; // USDT, decimals 6 + const buyTokenDecimals = 18; + const amount = 1; + + const srcAmount = new BigNumber(amount).times(new BigNumber(10).pow(sellTokenDecimals)).toFixed(0); + + let zeroXUrl = `https://avalanche.api.0x.org/swap/v1/quote`; + let paraswapUrl1 = `https://apiv5.paraswap.io/prices/`; + let paraswapUrl2 = `https://apiv5.paraswap.io/transactions/43114?ignoreChecks=true`; + + //paraswap + let paramsPara = { + srcToken: sellTokenAddress, + destToken: buyTokenAddress, + srcDecimals: sellTokenDecimals, + destDecimals: buyTokenDecimals, + amount: srcAmount, + side: "SELL", + network: 43114 + }; + + const priceRoute = (await axios.get(paraswapUrl1, { params: paramsPara })).data.priceRoute; + buyTokenAmountParaswap = priceRoute.destAmount; + let minAmount = new BigNumber(priceRoute.destAmount).times((100 - 1) / 100).toFixed(0); + + let txConfig = { + priceRoute: priceRoute, + srcToken: sellTokenAddress, + destToken: buyTokenAddress, + srcDecimals: sellTokenDecimals, + destDecimals: buyTokenDecimals, + srcAmount: srcAmount, + destAmount: minAmount, + userAddress: dsaWallet0.address + }; + const calldataPara = (await axios.post(paraswapUrl2, txConfig)).data.data; + + // zeroX + const paramsZeroX = { + buyToken: buyTokenAddress, + sellToken: sellTokenAddress, + sellAmount: "1000000000000000000" // Always denominated in wei + }; + + const responseZeroX = await axios.get(zeroXUrl, { params: paramsZeroX }).then((data: any) => data); + buyTokenAmountZeroX = responseZeroX.data.buyAmount; + const calldataZeroX = responseZeroX.data.data; + + let calculateUnitAmt = (buyAmount: any) => { + const buyTokenAmountRes = new BigNumber(buyAmount) + .dividedBy(new BigNumber(10).pow(buyTokenDecimals)) + .toFixed(8); + + let unitAmt: any = new BigNumber(buyTokenAmountRes).dividedBy(new BigNumber(amount)); + + unitAmt = unitAmt.multipliedBy((100 - 1) / 100); + unitAmt = unitAmt.multipliedBy(1e18).toFixed(0); + return unitAmt; + }; + + let unitAmt0x = calculateUnitAmt(buyTokenAmountZeroX); + let unitAmtParaswap = calculateUnitAmt(buyTokenAmountParaswap); + + function getCallData(connector: string, unitAmt: any, callData: any) { + var abi = [ + "function swap(address,address,uint256,uint256,bytes,uint256)", + "function sell(address,address,uint256,uint256,bytes,uint256)" + ]; + var iface = new ethers.utils.Interface(abi); + const spell = connector === "1INCH-A" ? "sell" : "swap"; + let data = iface.encodeFunctionData(spell, [buyTokenAddress, sellTokenAddress, srcAmount, unitAmt, callData, 0]); + return data; + } + let dataPara = ethers.utils.hexlify(await getCallData("PARASWAP-A", unitAmtParaswap, calldataPara)); + let dataZeroX = ethers.utils.hexlify(await getCallData("ZEROX-A", unitAmt0x, calldataZeroX)); + let datas = [dataPara, dataZeroX]; + + let connectors = ["PARASWAP-A", "ZEROX-A"]; + + return [connectors, datas]; + } + + let arg = await getArg(); + const spells = [ + { + connector: connectorName, + method: "swap", + args: arg + } + ]; + const tx = await dsaWallet0.connect(wallet0).cast(...encodeSpells(spells), await wallet1.getAddress()); + const receipt = await tx.wait(); + + const usdtToken = await ethers.getContractAt( + er20abi, + "0xd586E7F844cEa2F87f50152665BCbc2C279D8d70" // usdt address + ); + + expect(await usdtToken.balanceOf(dsaWallet0.address)).to.be.gte(buyTokenAmountParaswap); + expect(await ethers.provider.getBalance(dsaWallet0.address)).to.be.lte(ethers.utils.parseEther("9")); + }); + }); +}); diff --git a/test/fantom/aave/v3-test.ts b/test/fantom/aave/v3-test.ts new file mode 100644 index 0000000..292d36b --- /dev/null +++ b/test/fantom/aave/v3-test.ts @@ -0,0 +1,264 @@ +import { expect, should } from "chai"; +import hre, { ethers, waffle } from "hardhat"; +import type { Signer, Contract } from "ethers"; +import { BigNumber } from "bignumber.js"; +import { buildDSAv2 } from "../../../scripts/tests/buildDSAv2"; +import { addresses } from "../../../scripts/tests/fantom/addresses"; +import { deployAndEnableConnector } from "../../../scripts/tests/deployAndEnableConnector"; +import { abis } from "../../../scripts/constant/abis"; +import { getMasterSigner } from "../../../scripts/tests/getMasterSigner"; +import { parseEther, parseUnits } from "ethers/lib/utils"; +import { encodeSpells } from "../../../scripts/tests/encodeSpells"; +import { ConnectV2AaveV3Fantom__factory, IERC20__factory } from "../../../typechain"; + +const ABI = ["function balanceOf(address account) public view returns (uint256)"]; + +const aDaiAddress = "0x82E64f49Ed5EC1bC6e43DAD4FC8Af9bb3A2312EE"; +const aaveAddress = "0x69FA688f1Dc47d4B5d8029D5a35FB7a548310654"; +let account = "0x1c664Bafc646510684Ba1588798c67fe22a8c7cf"; +const DAI = "0x8D11eC38a3EB5E956B052f67Da8Bdc9bef8Abf3E"; +const USDC = "0x04068DA6C83AFCFA0e13ba15A6696662335D5B75"; +const mnemonic = "test test test test test test test test test test test junk"; +const connectorName = "AAVE-V3-X"; +let signer: any, wallet0: any; + +const aaveAbi = [ + { + inputs: [ + { internalType: "address", name: "asset", type: "address" }, + { internalType: "address", name: "user", type: "address" } + ], + name: "getUserReserveData", + outputs: [ + { internalType: "uint256", name: "currentATokenBalance", type: "uint256" }, + { internalType: "uint256", name: "currentStableDebt", type: "uint256" }, + { internalType: "uint256", name: "currentVariableDebt", type: "uint256" }, + { internalType: "uint256", name: "principalStableDebt", type: "uint256" }, + { internalType: "uint256", name: "scaledVariableDebt", type: "uint256" }, + { internalType: "uint256", name: "stableBorrowRate", type: "uint256" }, + { internalType: "uint256", name: "liquidityRate", type: "uint256" }, + { internalType: "uint40", name: "stableRateLastUpdated", type: "uint40" }, + { internalType: "bool", name: "usageAsCollateralEnabled", type: "bool" } + ], + stateMutability: "view", + type: "function" + } +]; + +const erc20Abi = [ + { + constant: false, + inputs: [ + { + name: "_spender", + type: "address" + }, + { + name: "_value", + type: "uint256" + } + ], + name: "approve", + outputs: [ + { + name: "", + type: "bool" + } + ], + payable: false, + stateMutability: "nonpayable", + type: "function" + }, + { + constant: true, + inputs: [], + name: "totalSupply", + outputs: [ + { + name: "", + type: "uint256" + } + ], + payable: false, + stateMutability: "view", + type: "function" + }, + { + constant: true, + inputs: [ + { + name: "_owner", + type: "address" + } + ], + name: "balanceOf", + outputs: [ + { + name: "balance", + type: "uint256" + } + ], + payable: false, + stateMutability: "view", + type: "function" + }, + { + constant: false, + inputs: [ + { + name: "_to", + type: "address" + }, + { + name: "_value", + type: "uint256" + } + ], + name: "transfer", + outputs: [ + { + name: "", + type: "bool" + } + ], + payable: false, + stateMutability: "nonpayable", + type: "function" + } +]; + +const token = new ethers.Contract(DAI, erc20Abi); +const aDai = new ethers.Contract(aDaiAddress, ABI); +const usdcToken = new ethers.Contract(USDC, erc20Abi); +const aave = new ethers.Contract(aaveAddress, aaveAbi); + +describe("Aave v3 Position for Fantom", function () { + let dsaWallet0: any; + let masterSigner: Signer; + let instaConnectorsV2: Contract; + let connector: any; + + const wallet = ethers.Wallet.fromMnemonic(mnemonic); + + before(async () => { + await hre.network.provider.request({ + method: "hardhat_reset", + params: [ + { + forking: { + //@ts-ignore + jsonRpcUrl: hre.config.networks.hardhat.forking.url, + blockNumber: 40790000 + } + } + ] + }); + masterSigner = await getMasterSigner(); + [wallet0] = await ethers.getSigners(); + await hre.network.provider.send("hardhat_setBalance", [account, ethers.utils.parseEther("10").toHexString()]); + + await hre.network.provider.request({ + method: "hardhat_impersonateAccount", + params: [account] + }); + + signer = await ethers.getSigner(account); + + await token.connect(signer).transfer(wallet0.address, ethers.utils.parseEther("10")); + + instaConnectorsV2 = await ethers.getContractAt(abis.core.connectorsV2, addresses.core.connectorsV2); + connector = await deployAndEnableConnector({ + connectorName, + contractArtifact: ConnectV2AaveV3Fantom__factory, + signer: masterSigner, + connectors: instaConnectorsV2 + }); + }); + + describe("Deployment", async () => { + it("Should set correct name", async () => { + expect(await connector.name()).to.eq("AaveV3-v1.2"); + }); + }); + + describe("DSA wallet setup", async () => { + it("Should build DSA v2", async () => { + dsaWallet0 = await buildDSAv2(wallet0.address); + expect(!!dsaWallet0.address).to.be.true; + }); + + it("Deposit ETH into DSA wallet", async function () { + await wallet0.sendTransaction({ + to: dsaWallet0.address, + value: ethers.utils.parseEther("5") + }); + + expect(await ethers.provider.getBalance(dsaWallet0.address)).to.be.gte(ethers.utils.parseEther("5")); + }); + }); + + describe("check user AAVE position", async () => { + it("Should create DSA Aave v3 position of DAI(collateral) and USDC(debt)", async () => { + await token.connect(signer).transfer(dsaWallet0.address, ethers.utils.parseEther("10")); + + const spells = [ + //deposit DAI in aave + { + connector: connectorName, + method: "deposit", + args: [DAI, parseEther("10"), 0, 0] + }, + //borrow USDC from aave + { + connector: connectorName, + method: "borrow", + args: [USDC, parseUnits("3", 6), 2, 0, 0] + } + ]; + const tx = await dsaWallet0.connect(wallet0).cast(...encodeSpells(spells), wallet0.address); + const receipt = await tx.wait(); + }); + + it("Should check position of dsa", async () => { + expect(await aDai.connect(wallet0).balanceOf(dsaWallet0.address)).to.be.gte( + new BigNumber(10).multipliedBy(1e18).toString() + ); + + expect(await usdcToken.connect(wallet0).balanceOf(dsaWallet0.address)).to.be.gte( + new BigNumber(3).multipliedBy(1e6).toString() + ); + + expect((await aave.connect(wallet0).getUserReserveData(USDC, dsaWallet0.address)).currentStableDebt).to.be.equal(0); + expect((await aave.connect(wallet0).getUserReserveData(USDC, dsaWallet0.address)).currentVariableDebt).to.be.gte( + new BigNumber(3).multipliedBy(1e6).toString() + ); + }); + + it("Should swap borrowRateMode", async () => { + const spells = [ + //deposit DAI in aave + { + connector: connectorName, + method: "swapBorrowRateMode", + args: [USDC, 2] + } + ]; + const tx = await dsaWallet0.connect(wallet0).cast(...encodeSpells(spells), wallet0.address); + const receipt = await tx.wait(); + }); + + it("Should check position of dsa", async () => { + expect(await aDai.connect(wallet0).balanceOf(dsaWallet0.address)).to.be.gte( + new BigNumber(10).multipliedBy(1e18).toString() + ); + + expect(await usdcToken.connect(wallet0).balanceOf(dsaWallet0.address)).to.be.gte( + new BigNumber(3).multipliedBy(1e6).toString() + ); + expect((await aave.connect(wallet0).getUserReserveData(USDC, dsaWallet0.address)).currentVariableDebt).to.be.equal(0); + expect((await aave.connect(wallet0).getUserReserveData(USDC, dsaWallet0.address)).currentStableDebt).to.be.gte( + new BigNumber(3).multipliedBy(1e6).toString() + ); + }); + }); +}); diff --git a/test/fantom/dsa-spell/dsa-spell.test.ts b/test/fantom/dsa-spell/dsa-spell.test.ts new file mode 100644 index 0000000..5a1f0c8 --- /dev/null +++ b/test/fantom/dsa-spell/dsa-spell.test.ts @@ -0,0 +1,182 @@ +import hre from "hardhat"; +import axios from "axios"; +import { expect } from "chai"; +const { ethers } = hre; //check +import { deployAndEnableConnector } from "../../../scripts/tests/deployAndEnableConnector"; +import { buildDSAv2 } from "../../../scripts/tests/buildDSAv2"; +import { encodeSpells } from "../../../scripts/tests/encodeSpells"; +import { getMasterSigner } from "../../../scripts/tests/getMasterSigner"; +import { addresses } from "../../../scripts/tests/fantom/addresses"; +import { abis } from "../../../scripts/constant/abis"; +import { ConnectV2DSASpellFantom__factory } from "../../../typechain"; +import type { Signer, Contract } from "ethers"; +import BigNumber from "bignumber.js"; + +describe("DSA Spell", function () { + const connectorName = "dsa-spell-test"; + + let dsaWallet0: any; + let dsaWallet1: any; + let dsaWallet2: any; + let walletB: any; + let wallet0: any; + let masterSigner: Signer; + let instaConnectorsV2: Contract; + let connector: any; + + before(async () => { + await hre.network.provider.request({ + method: "hardhat_reset", + params: [ + { + forking: { + // @ts-ignore + jsonRpcUrl: hre.config.networks.hardhat.forking.url + } + } + ] + }); + [wallet0] = await ethers.getSigners(); + + masterSigner = await getMasterSigner(); + instaConnectorsV2 = await ethers.getContractAt(abis.core.connectorsV2, addresses.core.connectorsV2); + connector = await deployAndEnableConnector({ + connectorName, + contractArtifact: ConnectV2DSASpellFantom__factory, + signer: masterSigner, + connectors: instaConnectorsV2 + }); + console.log("\tConnector address", connector.address); + }); + + it("Should have contracts deployed.", async function () { + expect(!!instaConnectorsV2.address).to.be.true; + expect(!!connector.address).to.be.true; + expect(!!(await masterSigner.getAddress())).to.be.true; + }); + + describe("DSA wallet setup", function () { + it("Should build DSA v2", async function () { + dsaWallet0 = await buildDSAv2(wallet0.address); + expect(!!dsaWallet0.address).to.be.true; + walletB = await ethers.getSigner(dsaWallet0.address); + dsaWallet1 = await buildDSAv2(dsaWallet0.address); + expect(!!dsaWallet1.address).to.be.true; + console.log(`\t${dsaWallet1.address}`); + dsaWallet2 = await buildDSAv2(wallet0.address); + expect(!!dsaWallet2.address).to.be.true; + }); + + it("Deposit ftm into DSA wallet 0", async function () { + await wallet0.sendTransaction({ + to: dsaWallet0.address, + value: ethers.utils.parseEther("10") + }); + + expect(await ethers.provider.getBalance(dsaWallet0.address)).to.be.gte(ethers.utils.parseEther("10")); + }); + + it("Deposit ftm into DSA wallet 1", async function () { + await wallet0.sendTransaction({ + to: dsaWallet1.address, + value: ethers.utils.parseEther("10") + }); + + expect(await ethers.provider.getBalance(dsaWallet1.address)).to.be.gte(ethers.utils.parseEther("10")); + }); + }); + + describe("Main", function () { + let FTM = "0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE"; + let aFTM = "0x6d80113e533a2C0fe82EaBD35f1875DcEA89Ea97"; + let USDC = "0x04068DA6C83AFCFA0e13ba15A6696662335D5B75"; + let usdc = new ethers.Contract(USDC, abis.basic.erc20); + let aFtm = new ethers.Contract(aFTM, abis.basic.aToken); + var abi = [ + "function withdraw(address,uint256,address,uint256,uint256)", + "function deposit(address,uint256,uint256,uint256)", + "function borrow(address,uint256,uint256,uint256,uint256)" + ]; + function getCallData(spell: string, params: any) { + var iface = new ethers.utils.Interface(abi); + let data = iface.encodeFunctionData(spell, params); + return ethers.utils.hexlify(data); + } + + it("should cast spells", async function () { + async function getArg(connectors: any, spells: any, params: any) { + let datas = []; + for (let i = 0; i < connectors.length; i++) { + datas.push(getCallData(spells[i], params[i])); + } + return [dsaWallet1.address, connectors, datas]; + } + + let connectors = ["BASIC-A", "AAVE-V3-A", "AAVE-V3-A"]; + let methods = ["withdraw", "deposit", "borrow"]; + let params = [ + [FTM, ethers.utils.parseEther("2"), dsaWallet0.address, 0, 0], + [FTM, ethers.constants.MaxUint256, 0, 0], + [USDC, ethers.utils.parseUnits("1", 5), 2, 0, 0] + ]; + let arg = await getArg(connectors, methods, params); + + const spells = [ + { + connector: connectorName, + method: "castOnDSA", + args: arg + } + ]; + const tx = await dsaWallet0.connect(wallet0).cast(...encodeSpells(spells), await wallet0.getAddress()); + const receipt = await tx.wait(); + }); + + it("should check balances after cast on DSA", async function () { + expect(await ethers.provider.getBalance(dsaWallet1.address)).to.be.lte(0); + expect(await usdc.connect(wallet0).balanceOf(dsaWallet1.address)).to.be.gte( + new BigNumber(1).multipliedBy(1e5).toString() + ); + expect(await ethers.provider.getBalance(dsaWallet0.address)).to.be.gte( + new BigNumber(12).multipliedBy(1e18).toString() + ); + }); + + it("should cast spell on the first successful", async function () { + async function getArg(connectors: any, spells: any, params: any) { + let datas = []; + for (let i = 0; i < connectors.length; i++) { + datas.push(getCallData(spells[i], params[i])); + } + return [connectors, datas]; + } + + let connectors = ["AAVE-V3-A"]; + let methods = ["deposit"]; + let params = [ + [FTM, ethers.utils.parseEther("10"), 0, 0] + ]; + let arg = await getArg(connectors, methods, params); + const spells = [ + { + connector: connectorName, + method: "castAny", + args: arg + } + ]; + const tx = await dsaWallet0 + .connect(wallet0) + .cast(...encodeSpells(spells), await wallet0.getAddress()); + const receipt = await tx.wait(); + }); + + it("should check balances after spells on DSA", async function () { + expect(await ethers.provider.getBalance(dsaWallet0.address)).to.be.lte( + new BigNumber(2).multipliedBy(1e18).toString() + ); + expect(await aFtm.connect(wallet0).balanceOf(dsaWallet0.address)).to.be.gte( + new BigNumber(10).multipliedBy(1e18).toString() + ); + }); + }); +}); diff --git a/test/fantom/swap/swap-test.ts b/test/fantom/swap/swap-test.ts new file mode 100644 index 0000000..dacfdd0 --- /dev/null +++ b/test/fantom/swap/swap-test.ts @@ -0,0 +1,175 @@ +import hre from "hardhat"; +import axios from "axios"; +import { expect } from "chai"; +const { ethers } = hre; //check +import { BigNumber } from "bignumber.js"; +import { deployAndEnableConnector } from "../../../scripts/tests/deployAndEnableConnector"; +import { buildDSAv2 } from "../../../scripts/tests/buildDSAv2"; +import { encodeSpells } from "../../../scripts/tests/encodeSpells"; +import { getMasterSigner } from "../../../scripts/tests/getMasterSigner"; +import { addresses } from "../../../scripts/tests/fantom/addresses"; +import { abis } from "../../../scripts/constant/abis"; +import { ConnectV2SwapAggregatorFantom__factory } from "../../../typechain"; +import er20abi from "../../../scripts/constant/abi/basics/erc20.json"; +import type { Signer, Contract } from "ethers"; + +describe("Swap | Fantom", function () { + const connectorName = "swap-test"; + + let dsaWallet0: Contract; + let wallet0: Signer, wallet1: Signer; + let masterSigner: Signer; + let instaConnectorsV2: Contract; + let connector: Contract; + + before(async () => { + await hre.network.provider.request({ + method: "hardhat_reset", + params: [ + { + forking: { + // @ts-ignore + jsonRpcUrl: hre.config.networks.hardhat.forking.url + } + } + ] + }); + [wallet0, wallet1] = await ethers.getSigners(); + + masterSigner = await getMasterSigner(); + instaConnectorsV2 = await ethers.getContractAt(abis.core.connectorsV2, addresses.core.connectorsV2); + connector = await deployAndEnableConnector({ + connectorName, + contractArtifact: ConnectV2SwapAggregatorFantom__factory, + signer: masterSigner, + connectors: instaConnectorsV2 + }); + console.log("Connector address", connector.address); + }); + + it("Should have contracts deployed.", async function () { + expect(!!instaConnectorsV2.address).to.be.true; + expect(!!connector.address).to.be.true; + expect(!!(await masterSigner.getAddress())).to.be.true; + }); + + describe("DSA wallet setup", function () { + it("Should build DSA v2", async function () { + dsaWallet0 = await buildDSAv2(await wallet0.getAddress()); + console.log(dsaWallet0.address); + expect(!!dsaWallet0.address).to.be.true; + }); + + it("Deposit fantom into DSA wallet", async function () { + await wallet0.sendTransaction({ + to: dsaWallet0.address, + value: ethers.utils.parseEther("10") + }); + + expect(await ethers.provider.getBalance(dsaWallet0.address)).to.be.gte(ethers.utils.parseEther("10")); + }); + }); + + describe("Main", function () { + it("should swap the tokens", async function () { + let buyTokenAmountParaswap: any; + + async function getArg() { + const slippage = 0.5; + /* eth -> dai */ + const sellTokenAddress = "0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE"; // FTM, decimals 18 + const sellTokenDecimals = 18; + const buyTokenAddress = "0x8D11eC38a3EB5E956B052f67Da8Bdc9bef8Abf3E"; // DAI, decimals 18 + const buyTokenDecimals = 18; + const amount = 1; + + const srcAmount = new BigNumber(amount).times(new BigNumber(10).pow(sellTokenDecimals)).toFixed(0); + + let paraswapUrl1 = `https://apiv5.paraswap.io/prices/`; + let paraswapUrl2 = `https://apiv5.paraswap.io/transactions/250?ignoreChecks=true`; + + //paraswap + let paramsPara = { + srcToken: sellTokenAddress, + destToken: buyTokenAddress, + srcDecimals: sellTokenDecimals, + destDecimals: buyTokenDecimals, + amount: srcAmount, + side: "SELL", + network: 250 + }; + + const priceRoute = (await axios.get(paraswapUrl1, { params: paramsPara })).data.priceRoute; + buyTokenAmountParaswap = priceRoute.destAmount; + let minAmount = new BigNumber(priceRoute.destAmount).times((100 - 1) / 100).toFixed(0); + + let txConfig = { + priceRoute: priceRoute, + srcToken: sellTokenAddress, + destToken: buyTokenAddress, + srcDecimals: sellTokenDecimals, + destDecimals: buyTokenDecimals, + srcAmount: srcAmount, + destAmount: minAmount, + userAddress: dsaWallet0.address + }; + const calldataPara = (await axios.post(paraswapUrl2, txConfig)).data.data; + + let calculateUnitAmt = (buyAmount: any) => { + const buyTokenAmountRes = new BigNumber(buyAmount) + .dividedBy(new BigNumber(10).pow(buyTokenDecimals)) + .toFixed(8); + + let unitAmt: any = new BigNumber(buyTokenAmountRes).dividedBy(new BigNumber(amount)); + + unitAmt = unitAmt.multipliedBy((100 - 1) / 100); + unitAmt = unitAmt.multipliedBy(1e18).toFixed(0); + return unitAmt; + }; + let unitAmtParaswap = calculateUnitAmt(buyTokenAmountParaswap); + + function getCallData(connector: string, unitAmt: any, callData: any) { + var abi = [ + "function swap(address,address,uint256,uint256,bytes,uint256)", + "function sell(address,address,uint256,uint256,bytes,uint256)" + ]; + var iface = new ethers.utils.Interface(abi); + const spell = connector === "1INCH-A" ? "sell" : "swap"; + let data = iface.encodeFunctionData(spell, [ + buyTokenAddress, + sellTokenAddress, + srcAmount, + unitAmt, + callData, + 0 + ]); + return data; + } + let dataPara = ethers.utils.hexlify(await getCallData("PARASWAP-A", unitAmtParaswap, calldataPara)); + let datas = [dataPara]; + + let connectors = ["PARASWAP-A"]; + return [connectors, datas]; + } + + let arg = await getArg(); + const spells = [ + { + connector: connectorName, + method: "swap", + args: arg + } + ]; + const tx = await dsaWallet0.connect(wallet0).cast(...encodeSpells(spells), await wallet1.getAddress()); + const receipt = await tx.wait(); + + const daiToken = await ethers.getContractAt( + er20abi, + "0x8D11eC38a3EB5E956B052f67Da8Bdc9bef8Abf3E" // dai address + ); + + expect(await daiToken.balanceOf(dsaWallet0.address)).to.be.gte(buyTokenAmountParaswap); + expect(await ethers.provider.getBalance(dsaWallet0.address)).to.be.lte(ethers.utils.parseEther("9")); + }); + }); +}); diff --git a/test/mainnet/0x/zeroEx.test.ts b/test/mainnet/0x/zeroEx.test.ts new file mode 100644 index 0000000..2cddb01 --- /dev/null +++ b/test/mainnet/0x/zeroEx.test.ts @@ -0,0 +1,161 @@ +import hre from "hardhat"; +import axios from "axios"; +import { expect } from "chai"; +const { ethers } = hre; //check +import { BigNumber } from "bignumber.js"; +import { deployAndEnableConnector } from "../../../scripts/tests/deployAndEnableConnector"; +import { buildDSAv2 } from "../../../scripts/tests/buildDSAv2"; +import { encodeSpells } from "../../../scripts/tests/encodeSpells"; +import { getMasterSigner } from "../../../scripts/tests/getMasterSigner"; +import { addresses } from "../../../scripts/tests/mainnet/addresses"; +import { abis } from "../../../scripts/constant/abis"; +import { ConnectV2ZeroEx, ConnectV2ZeroEx__factory } from "../../../typechain"; +import er20abi from "../../../scripts/constant/abi/basics/erc20.json"; +import type { Signer, Contract } from "ethers"; + +describe("ZeroEx", function() { + const connectorName = "zeroEx-test"; + + let dsaWallet0: Contract; + let wallet0: Signer, wallet1: Signer; + let masterSigner: Signer; + let instaConnectorsV2: Contract; + let connector: Contract; + + before(async () => { + await hre.network.provider.request({ + method: "hardhat_reset", + params: [ + { + forking: { + // @ts-ignore + jsonRpcUrl: hre.config.networks.hardhat.forking.url, + }, + }, + ], + }); + [wallet0, wallet1] = await ethers.getSigners(); + + masterSigner = await getMasterSigner(); + instaConnectorsV2 = await ethers.getContractAt( + abis.core.connectorsV2, + addresses.core.connectorsV2 + ); + connector = await deployAndEnableConnector({ + connectorName, + contractArtifact: ConnectV2ZeroEx__factory, + signer: masterSigner, + connectors: instaConnectorsV2, + }); + console.log("Connector address", connector.address); + }); + + it("Should have contracts deployed.", async function() { + expect(!!instaConnectorsV2.address).to.be.true; + expect(!!connector.address).to.be.true; + expect(!!(await masterSigner.getAddress())).to.be.true; + }); + + describe("DSA wallet setup", function() { + it("Should build DSA v2", async function() { + dsaWallet0 = await buildDSAv2(await wallet0.getAddress()); + expect(!!dsaWallet0.address).to.be.true; + }); + + it("Deposit ETH into DSA wallet", async function() { + await wallet0.sendTransaction({ + to: dsaWallet0.address, + value: ethers.utils.parseEther("10"), + }); + // console.log(dsaWallet0.address); + expect(await ethers.provider.getBalance(dsaWallet0.address)).to.be.gte( + ethers.utils.parseEther("10") + ); + }); + }); + + describe("Main", function() { + it("should swap the tokens", async function() { + let buyTokenAmount: any; + async function getArg() { + // const slippage = 0.5; + /* Eth -> dai */ + const sellTokenAddress = "0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE"; // eth, decimals 18 + const sellTokenDecimals = 18; + const buyTokenAddress = "0x6b175474e89094c44da98b954eedeac495271d0f"; // dai, decimals 18 + const buyTokenDecimals = 18; + const amount = 1; + + const srcAmount = new BigNumber(amount) + .times(new BigNumber(10).pow(sellTokenDecimals)) + .toFixed(0); + + const fromAddress = dsaWallet0.address; + + let url = `https://api.0x.org/swap/v1/quote`; + + const params = { + buyToken: "DAI", + sellToken: "ETH", + sellAmount: "1000000000000000000", // Always denominated in wei + }; + + const response = await axios + .get(url, { params: params }) + .then((data: any) => data); + + buyTokenAmount = response.data.buyAmount; + const calldata = response.data.data; + + let caculateUnitAmt = () => { + const buyTokenAmountRes = new BigNumber(buyTokenAmount) + .dividedBy(new BigNumber(10).pow(buyTokenDecimals)) + .toFixed(8); + + let unitAmt: any = new BigNumber(buyTokenAmountRes).dividedBy( + new BigNumber(amount) + ); + + unitAmt = unitAmt.multipliedBy((100 - 0.3) / 100); + unitAmt = unitAmt.multipliedBy(1e18).toFixed(0); + return unitAmt; + }; + let unitAmt = caculateUnitAmt(); + + return [ + buyTokenAddress, + sellTokenAddress, + srcAmount, + unitAmt, + calldata, + 0, + ]; + } + + let arg = await getArg(); + const spells = [ + { + connector: connectorName, + method: "swap", + args: arg, + }, + ]; + const tx = await dsaWallet0 + .connect(wallet0) + .cast(...encodeSpells(spells), await wallet1.getAddress()); + const receipt = await tx.wait(); + + const daiToken = await ethers.getContractAt( + er20abi, + "0x6b175474e89094c44da98b954eedeac495271d0f" // dai address + ); + + expect(await daiToken.balanceOf(dsaWallet0.address)).to.be.gte( + buyTokenAmount + ); + expect(await ethers.provider.getBalance(dsaWallet0.address)).to.be.lte( + ethers.utils.parseEther("9") + ); + }); + }); +}); diff --git a/test/mainnet/aave/v1.test.ts b/test/mainnet/aave/v1.test.ts new file mode 100644 index 0000000..a65c3a2 --- /dev/null +++ b/test/mainnet/aave/v1.test.ts @@ -0,0 +1,168 @@ +import hre from "hardhat"; +import { expect } from "chai"; +import { abis } from "../../../scripts/constant/abis"; +import { addresses } from "../../../scripts/tests/mainnet/addresses"; +import { deployAndEnableConnector } from "../../../scripts/tests/deployAndEnableConnector"; +import { getMasterSigner } from "../../../scripts/tests/getMasterSigner"; +import { buildDSAv2 } from "../../../scripts/tests/buildDSAv2"; +import { ConnectV2AaveV1, ConnectV2AaveV1__factory } from "../../../typechain"; +import { parseEther } from "@ethersproject/units"; +import { encodeSpells } from "../../../scripts/tests/encodeSpells"; +import { tokens } from "../../../scripts/tests/mainnet/tokens"; +import { constants } from "../../../scripts/constant/constant"; +import { addLiquidity } from "../../../scripts/tests/addLiquidity"; +const { ethers } = hre; +import type { Signer, Contract } from "ethers"; + +describe("Aave V1", function () { + const connectorName = "AAVEV1-TEST-A"; + + let wallet0: Signer, wallet1: Signer; + let dsaWallet0: Contract; + let instaConnectorsV2: Contract; + let connector: any; + let masterSigner: Signer; + + before(async () => { + try { + await hre.network.provider.request({ + method: "hardhat_reset", + params: [ + { + forking: { + // @ts-ignore + jsonRpcUrl: hre.config.networks.hardhat.forking.url, + blockNumber: 12796965, + }, + }, + ], + }); + [wallet0, wallet1] = await ethers.getSigners(); + masterSigner = await getMasterSigner(); + instaConnectorsV2 = await ethers.getContractAt( + abis.core.connectorsV2, + addresses.core.connectorsV2 + ); + connector = await deployAndEnableConnector({ + connectorName, + contractArtifact: ConnectV2AaveV1__factory, + signer: masterSigner, + connectors: instaConnectorsV2, + }); + console.log("Connector address", connector.address); + } catch (err) { + console.log("error", err); + } + }); + + it("should have contracts deployed", async () => { + expect(!!instaConnectorsV2.address).to.be.true; + expect(!!connector.address).to.be.true; + expect(!!(await masterSigner.getAddress())).to.be.true; + }); + + describe("DSA wallet setup", function () { + it("Should build DSA v2", async function () { + dsaWallet0 = await buildDSAv2(wallet0.getAddress()); + expect(!!dsaWallet0.address).to.be.true; + }); + + it("Deposit ETH into DSA wallet", async function () { + await wallet0.sendTransaction({ + to: dsaWallet0.address, + value: parseEther("10"), + }); + expect(await ethers.provider.getBalance(dsaWallet0.address)).to.be.gte( + parseEther("10") + ); + }); + }); + + describe("Main", function () { + it("should deposit ETH in Aave V1", async function () { + const amt = parseEther("1"); + const spells = [ + { + connector: connectorName, + method: "deposit", + args: [tokens.eth.address, amt, 0, 0], + }, + ]; + + const tx = await dsaWallet0 + .connect(wallet0) + .cast(...encodeSpells(spells), wallet1.getAddress()); + + await tx.wait(); + + expect(await ethers.provider.getBalance(dsaWallet0.address)).to.eq( + parseEther("9") + ); + }); + + it("Should borrow and payback DAI from Aave V1", async function () { + const amt = parseEther("100"); // 100 DAI + + // add a little amount of dai to cover any shortfalls + await addLiquidity("dai", dsaWallet0.address, parseEther("1")); + + const spells = [ + { + connector: connectorName, + method: "borrow", + args: [tokens.dai.address, amt, 0, 0], + }, + { + connector: connectorName, + method: "payback", + // FIXME: we need to pass max_value because of roundoff/shortfall errors + args: [tokens.dai.address, constants.max_value, 0, 0], + }, + ]; + + const tx = await dsaWallet0 + .connect(wallet0) + .cast(...encodeSpells(spells), wallet1.getAddress()); + await tx.wait(); + expect(await ethers.provider.getBalance(dsaWallet0.address)).to.be.lte( + ethers.utils.parseEther("9") + ); + }); + + it("Should deposit all ETH in Aave V1", async function () { + const spells = [ + { + connector: connectorName, + method: "deposit", + args: [tokens.eth.address, constants.max_value, 0, 0], + }, + ]; + + const tx = await dsaWallet0 + .connect(wallet0) + .cast(...encodeSpells(spells), wallet1.getAddress()); + await tx.wait(); + expect(await ethers.provider.getBalance(dsaWallet0.address)).to.be.lte( + ethers.utils.parseEther("0") + ); + }); + + it("Should withdraw all ETH from Aave V1", async function () { + const spells = [ + { + connector: connectorName, + method: "withdraw", + args: [tokens.eth.address, constants.max_value, 0, 0], + }, + ]; + + const tx = await dsaWallet0 + .connect(wallet0) + .cast(...encodeSpells(spells), wallet1.getAddress()); + await tx.wait(); + expect(await ethers.provider.getBalance(dsaWallet0.address)).to.be.gte( + ethers.utils.parseEther("10") + ); + }); + }); +}); diff --git a/test/mainnet/aave/v2.test.ts b/test/mainnet/aave/v2.test.ts new file mode 100644 index 0000000..6752e6a --- /dev/null +++ b/test/mainnet/aave/v2.test.ts @@ -0,0 +1,226 @@ +import { expect } from "chai"; +import hre from "hardhat"; +import { abis } from "../../../scripts/constant/abis"; +import { addresses } from "../../../scripts/tests/mainnet/addresses"; +import { deployAndEnableConnector } from "../../../scripts/tests/deployAndEnableConnector"; +import { getMasterSigner } from "../../../scripts/tests/getMasterSigner"; +import { buildDSAv2 } from "../../../scripts/tests/buildDSAv2"; +import { ConnectV2AaveV2, ConnectV2AaveV2__factory } from "../../../typechain"; +import { parseEther } from "@ethersproject/units"; +import { encodeSpells } from "../../../scripts/tests/encodeSpells"; +import { tokens } from "../../../scripts/tests/mainnet/tokens"; +import { constants } from "../../../scripts/constant/constant"; +import { addLiquidity } from "../../../scripts/tests/addLiquidity"; +const { ethers } = hre; +import type { Signer, Contract } from "ethers"; + +describe("Aave V2", function () { + const connectorName = "AAVEV2-TEST-A"; + let connector: any; + + let wallet0: Signer, wallet1:Signer; + let dsaWallet0: any; + let instaConnectorsV2: Contract; + let masterSigner: Signer; + before(async () => { + await hre.network.provider.request({ + method: "hardhat_reset", + params: [ + { + forking: { + // @ts-ignore + jsonRpcUrl: hre.config.networks.hardhat.forking.url, + blockNumber: 12796965, + }, + }, + ], + }); + [wallet0, wallet1] = await ethers.getSigners(); + masterSigner = await getMasterSigner(); + instaConnectorsV2 = await ethers.getContractAt( + abis.core.connectorsV2, + addresses.core.connectorsV2 + ); + connector = await deployAndEnableConnector({ + connectorName, + contractArtifact: ConnectV2AaveV2__factory, + signer: masterSigner, + connectors: instaConnectorsV2, + }); + console.log("Connector address", connector.address); + }); + + it("should have contracts deployed", async () => { + expect(!!instaConnectorsV2.address).to.be.true; + expect(!!connector.address).to.be.true; + expect(!!(await masterSigner.getAddress())).to.be.true; + }); + + describe("DSA wallet setup", function () { + it("Should build DSA v2", async function () { + dsaWallet0 = await buildDSAv2(wallet0.getAddress()); + expect(!!dsaWallet0.address).to.be.true; + }); + + it("Deposit ETH into DSA wallet", async function () { + await wallet0.sendTransaction({ + to: dsaWallet0.address, + value: parseEther("10"), + }); + expect(await ethers.provider.getBalance(dsaWallet0.address)).to.be.gte( + parseEther("10") + ); + }); + }); + + describe("Main", function () { + it("should deposit ETH in Aave V2", async function () { + const amt = parseEther("1"); + const spells = [ + { + connector: connectorName, + method: "deposit", + args: [tokens.eth.address, amt, 0, 0], + }, + ]; + + const tx = await dsaWallet0 + .connect(wallet0) + .cast(...encodeSpells(spells), wallet1.getAddress()); + + await tx.wait(); + + expect(await ethers.provider.getBalance(dsaWallet0.address)).to.eq( + parseEther("9") + ); + }); + + it("Should borrow and payback DAI from Aave V2", async function () { + const amt = parseEther("100"); // 100 DAI + const setId = "83478237"; + const spells = [ + { + connector: connectorName, + method: "borrow", + args: [tokens.dai.address, amt, 2, 0, setId], + }, + { + connector: connectorName, + method: "payback", + args: [tokens.dai.address, amt, 2, setId, 0], + }, + ]; + + const tx = await dsaWallet0 + .connect(wallet0) + .cast(...encodeSpells(spells), wallet1.getAddress()); + await tx.wait(); + expect(await ethers.provider.getBalance(dsaWallet0.address)).to.be.lte( + ethers.utils.parseEther("9") + ); + }); + + it("Should borrow and payback half DAI from Aave V2", async function () { + const amt = parseEther("100"); // 100 DAI + // const setId = "83478237"; + await addLiquidity("dai", dsaWallet0.address, parseEther("1")); + let spells = [ + { + connector: connectorName, + method: "borrow", + args: [tokens.dai.address, amt, 2, 0, 0], + }, + { + connector: connectorName, + method: "payback", + args: [tokens.dai.address, amt.div(2), 2, 0, 0], + }, + ]; + + let tx = await dsaWallet0 + .connect(wallet0) + .cast(...encodeSpells(spells), wallet1.getAddress()); + await tx.wait(); + expect(await ethers.provider.getBalance(dsaWallet0.address)).to.be.lte( + ethers.utils.parseEther("9") + ); + + spells = [ + { + connector: connectorName, + method: "payback", + args: [tokens.dai.address, constants.max_value, 2, 0, 0], + }, + ]; + + tx = await dsaWallet0 + .connect(wallet0) + .cast(...encodeSpells(spells), wallet1.getAddress()); + await tx.wait(); + expect(await ethers.provider.getBalance(dsaWallet0.address)).to.be.lte( + ethers.utils.parseEther("9") + ); + }); + + it("Should deposit all ETH in Aave V2", async function () { + const spells = [ + { + connector: connectorName, + method: "deposit", + args: [tokens.eth.address, constants.max_value, 0, 0], + }, + ]; + + const tx = await dsaWallet0 + .connect(wallet0) + .cast(...encodeSpells(spells), wallet1.getAddress()); + await tx.wait(); + expect(await ethers.provider.getBalance(dsaWallet0.address)).to.be.lte( + ethers.utils.parseEther("0") + ); + }); + + it("Should withdraw all ETH from Aave V2", async function () { + const spells = [ + { + connector: connectorName, + method: "withdraw", + args: [tokens.eth.address, constants.max_value, 0, 0], + }, + ]; + + const tx = await dsaWallet0 + .connect(wallet0) + .cast(...encodeSpells(spells), wallet1.getAddress()); + await tx.wait(); + expect(await ethers.provider.getBalance(dsaWallet0.address)).to.be.gte( + ethers.utils.parseEther("10") + ); + }); + + it("should deposit and withdraw", async () => { + const amt = parseEther("1"); // 1 eth + const setId = "834782373"; + const spells = [ + { + connector: connectorName, + method: "deposit", + args: [tokens.eth.address, amt, 0, setId], + }, + { + connector: connectorName, + method: "withdraw", + args: [tokens.eth.address, amt, setId, 0], + }, + ]; + + const tx = await dsaWallet0 + .connect(wallet0) + .cast(...encodeSpells(spells), wallet1.getAddress()); + await tx.wait(); + expect(await ethers.provider.getBalance(dsaWallet0.address)).to.be.gte( + ethers.utils.parseEther("10") + ); + }); + }); +}); diff --git a/test/mainnet/b.protocol/b.compound.test.ts b/test/mainnet/b.protocol/b.compound.test.ts new file mode 100644 index 0000000..3402321 --- /dev/null +++ b/test/mainnet/b.protocol/b.compound.test.ts @@ -0,0 +1,138 @@ +import { expect } from "chai"; +import hre from "hardhat"; +const { web3, deployments, waffle, ethers } = hre; //check +const { provider, deployContract } = waffle + +import { deployAndEnableConnector } from "../../../scripts/tests/deployAndEnableConnector" +import { buildDSAv2 } from "../../../scripts/tests/buildDSAv2" +import { encodeSpells } from "../../../scripts/tests/encodeSpells" +import { getMasterSigner } from "../../../scripts/tests/getMasterSigner" +import { addresses } from "../../../scripts/tests/mainnet/addresses"; +import { abis } from "../../../scripts/constant/abis"; +import { constants } from "../../../scripts/constant/constant"; +import { ConnectV2BCompound__factory } from "../../../typechain"; +import type { Signer, Contract } from "ethers"; + +describe("B.Compound", function () { + const connectorName = "B.COMPOUND-TEST-A" + + let dsaWallet0: any; + let masterSigner: Signer; + let instaConnectorsV2: Contract; + let connector: Contract; + + const wallets = provider.getWallets() + const [wallet0, wallet1, wallet2, wallet3] = wallets + before(async () => { + await hre.network.provider.request({ + method: "hardhat_reset", + params: [ + { + forking: { + // @ts-ignore + jsonRpcUrl: hre.config.networks.hardhat.forking.url, + blockNumber: 13300000, + }, + }, + ], + }); + masterSigner = await getMasterSigner() + instaConnectorsV2 = await ethers.getContractAt(abis.core.connectorsV2, addresses.core.connectorsV2); + connector = await deployAndEnableConnector({ + connectorName, + contractArtifact: ConnectV2BCompound__factory, + signer: masterSigner, + connectors: instaConnectorsV2 + }) + console.log("Connector address", connector.address) + }) + + it("Should have contracts deployed.", async function () { + expect(!!instaConnectorsV2.address).to.be.true; + expect(!!connector.address).to.be.true; + expect(!!(await masterSigner.getAddress())).to.be.true; + expect(await connector.name()).to.be.equal("B.Compound-v1.0"); + }); + + describe("DSA wallet setup", function () { + it("Should build DSA v2", async function () { + dsaWallet0 = await buildDSAv2(wallet0.address) + expect(!!dsaWallet0.address).to.be.true; + }); + + it("Deposit ETH into DSA wallet", async function () { + await wallet0.sendTransaction({ + to: dsaWallet0.address, + value: ethers.utils.parseEther("10") + }); + expect(await ethers.provider.getBalance(dsaWallet0.address)).to.be.gte(ethers.utils.parseEther("10")); + }); + }); + + describe("Main", function () { + + it("Should deposit ETH in Compound", async function () { + const amount = ethers.utils.parseEther("1") // 1 ETH + const spells = [ + { + connector: connectorName, + method: "deposit", + args: ["ETH-A", amount, 0, 0] + } + ] + + const tx = await dsaWallet0.connect(wallet0).cast(...encodeSpells(spells), wallet1.address) + const receipt = await tx.wait() + expect(await ethers.provider.getBalance(dsaWallet0.address)).to.be.lte(ethers.utils.parseEther("9")); + }); + + it("Should borrow and payback DAI from Compound", async function () { + const amount = ethers.utils.parseEther("100") // 100 DAI + const setId = "83478237" + const spells = [ + { + connector: connectorName, + method: "borrow", + args: ["DAI-A", amount, 0, setId] + }, + { + connector: connectorName, + method: "payback", + args: ["DAI-A", 0, setId, 0] + } + ] + + const tx = await dsaWallet0.connect(wallet0).cast(...encodeSpells(spells), wallet1.address) + const receipt = await tx.wait() + expect(await ethers.provider.getBalance(dsaWallet0.address)).to.be.lte(ethers.utils.parseEther("9")); + }); + + it("Should deposit all ETH in Compound", async function () { + const spells = [ + { + connector: connectorName, + method: "deposit", + args: ["ETH-A", constants.max_value, 0, 0] + } + ] + + const tx = await dsaWallet0.connect(wallet0).cast(...encodeSpells(spells), wallet1.address) + const receipt = await tx.wait() + expect(await ethers.provider.getBalance(dsaWallet0.address)).to.be.lte(ethers.utils.parseEther("0")); + }); + + it("Should withdraw all ETH from Compound", async function () { + const spells = [ + { + connector: connectorName, + method: "withdraw", + args: ["ETH-A", constants.max_value, 0, 0] + } + ] + + const tx = await dsaWallet0.connect(wallet0).cast(...encodeSpells(spells), wallet1.address) + const receipt = await tx.wait() + expect(await ethers.provider.getBalance(dsaWallet0.address)).to.be.gte(ethers.utils.parseEther("10")); + }); + }) +}) diff --git a/test/mainnet/b.protocol/b.liquity.test.ts b/test/mainnet/b.protocol/b.liquity.test.ts new file mode 100644 index 0000000..88e5f7c --- /dev/null +++ b/test/mainnet/b.protocol/b.liquity.test.ts @@ -0,0 +1,189 @@ +import { expect } from "chai"; +import hre from "hardhat"; +const { web3, deployments, waffle, ethers } = hre; //check +const { provider, deployContract } = waffle + +import { deployAndEnableConnector } from "../../../scripts/tests/deployAndEnableConnector" +import { buildDSAv2 } from "../../../scripts/tests/buildDSAv2" +import { encodeSpells } from "../../../scripts/tests/encodeSpells" +import { getMasterSigner } from "../../../scripts/tests/getMasterSigner" +import { abis } from "../../../scripts/constant/abis"; +import { ConnectV2BLiquity__factory } from "../../../typechain"; +import type { Signer, Contract } from "ethers"; +import { addresses } from "../../../scripts/tests/mainnet/addresses"; + +const LUSD_WHALE = "0x66017D22b0f8556afDd19FC67041899Eb65a21bb" // stability pool +const BAMM_ADDRESS = "0x0d3AbAA7E088C2c82f54B2f47613DA438ea8C598" + +describe("B.Liquity", function () { + const connectorName = "B.LIQUITY-TEST-A" + + let dsaWallet0: any; + let dsaWallet1: any; + let masterSigner: Signer; + let instaConnectorsV2: Contract; + let connector: Contract; + let manager: Contract; + let vat: Contract; + let lusd: Contract; + let bammToken: Contract; + let stabilityPool: Contract; + + const wallets = provider.getWallets() + const [wallet0, wallet1, wallet2, wallet3] = wallets + before(async () => { + await hre.network.provider.request({ + method: "hardhat_reset", + params: [ + { + forking: { + // @ts-ignore + jsonRpcUrl: hre.config.networks.hardhat.forking.url, + blockNumber: 12996875, + }, + }, + ], + }); + masterSigner = await getMasterSigner() + instaConnectorsV2 = await ethers.getContractAt(abis.core.connectorsV2, addresses.core.connectorsV2); + connector = await deployAndEnableConnector({ + connectorName, + contractArtifact: ConnectV2BLiquity__factory, + signer: masterSigner, + connectors: instaConnectorsV2 + }) + + lusd = await ethers.getContractAt("../artifacts/contracts/mainnet/common/interfaces.sol:TokenInterface", "0x5f98805A4E8be255a32880FDeC7F6728C6568bA0") + bammToken = await ethers.getContractAt("../artifacts/contracts/mainnet/connectors/b.protocol/liquity/interface.sol:BAMMLike", BAMM_ADDRESS) + stabilityPool = await ethers.getContractAt("../artifacts/contracts/mainnet/connectors/b.protocol/liquity/interface.sol:StabilityPoolLike", "0x66017D22b0f8556afDd19FC67041899Eb65a21bb") + + console.log("Connector address", connector.address) + }) + + it("test veryClose.", async function () { + expect(veryClose(1000001, 1000000)).to.be.true + expect(veryClose(1000000, 1000001)).to.be.true + expect(veryClose(1003000, 1000001)).to.be.false + expect(veryClose(1000001, 1000300)).to.be.false + }); + + it("Should have contracts deployed.", async function () { + expect(!!instaConnectorsV2.address).to.be.true; + expect(!!connector.address).to.be.true; + expect(!!(await masterSigner.getAddress())).to.be.true; + expect(await connector.name()).to.be.equal("B.Liquity-v1"); + }); + + describe("DSA wallet setup", function () { + it("Should build DSA v2", async function () { + dsaWallet0 = await buildDSAv2(wallet0.address) + expect(!!dsaWallet0.address).to.be.true; + + dsaWallet1 = await buildDSAv2(wallet1.address) + expect(!!dsaWallet1.address).to.be.true; + }); + + it("Deposit LUSD into DSA wallet", async function () { + await hre.network.provider.request({ + method: "hardhat_impersonateAccount", + params: [LUSD_WHALE], + }); + + const signer = await hre.ethers.provider.getSigner(LUSD_WHALE); + await lusd.connect(signer).transfer(dsaWallet0.address, ethers.utils.parseEther("100000")) + + expect(await lusd.balanceOf(dsaWallet0.address)).to.equal(ethers.utils.parseEther("100000")); + }); + }); + + describe("Main", function () { + it("should deposit 10k LUSD", async function () { + const totalSupplyBefore = await bammToken.totalSupply(); + const lusdBalanceBefore = await stabilityPool.getCompoundedLUSDDeposit(BAMM_ADDRESS); + const amount = ethers.utils.parseEther("10000"); + const spells = [ + { + connector: connectorName, + method: "deposit", + args: [amount, 0, 0, 0] + } + ] + + const tx = await dsaWallet0.connect(wallet0).cast(...encodeSpells(spells), wallet1.address) + const receipt = await tx.wait() + + const expectedBalance = totalSupplyBefore.mul(amount).div(lusdBalanceBefore) + expect(veryClose(expectedBalance, await bammToken.balanceOf(dsaWallet0.address))).to.be.true + }); + + it("should deposit all LUSD", async function () { + const totalSupplyBefore = await bammToken.totalSupply(); + const lusdBalanceBefore = await stabilityPool.getCompoundedLUSDDeposit(BAMM_ADDRESS); + const amount = web3.utils.toBN("2").pow(web3.utils.toBN("256")).sub(web3.utils.toBN("1")); + const balanceBefore = await bammToken.balanceOf(dsaWallet0.address) + + const spells = [ + { + connector: connectorName, + method: "deposit", + args: [amount, 0, 0, 0] + } + ] + + const tx = await dsaWallet0.connect(wallet0).cast(...encodeSpells(spells), wallet1.address) + const receipt = await tx.wait() + + const expectedBalance = (totalSupplyBefore.mul(ethers.utils.parseEther("90000")).div(lusdBalanceBefore)).add(balanceBefore) + expect(veryClose(expectedBalance, await bammToken.balanceOf(dsaWallet0.address))).to.be.true + }); + + it("should withdraw half of the shares", async function () { + const balanceBefore = await bammToken.balanceOf(dsaWallet0.address) + const halfBalance = balanceBefore.div("2") + + const spells = [ + { + connector: connectorName, + method: "withdraw", + args: [halfBalance, 0, 0, 0] + } + ] + + const tx = await dsaWallet0.connect(wallet0).cast(...encodeSpells(spells), wallet1.address) + const receipt = await tx.wait() + + expect(veryClose(halfBalance, await bammToken.balanceOf(dsaWallet0.address))).to.be.true + expect(veryClose(ethers.utils.parseEther("50000"), await lusd.balanceOf(dsaWallet0.address))).to.be.true + }); + + it("should withdraw all the shares", async function () { + const amount = web3.utils.toBN("2").pow(web3.utils.toBN("256")).sub(web3.utils.toBN("1")); + + const spells = [ + { + connector: connectorName, + method: "withdraw", + args: [amount, 0, 0, 0] + } + ] + + const tx = await dsaWallet0.connect(wallet0).cast(...encodeSpells(spells), wallet1.address) + const receipt = await tx.wait() + + expect(veryClose(ethers.utils.parseEther("100000"), await lusd.balanceOf(dsaWallet0.address))).to.be.true + }); + }) +}) + +function veryClose(n1: any, n2: any) { + n1 = web3.utils.toBN(n1) + n2 = web3.utils.toBN(n2) + + let _10000 = web3.utils.toBN(10000) + let _9999 = web3.utils.toBN(9999) + + if (n1.mul(_10000).lt(n2.mul(_9999))) return false + if (n2.mul(_10000).lt(n1.mul(_9999))) return false + + return true +} diff --git a/test/mainnet/b.protocol/b.maker.test.ts b/test/mainnet/b.protocol/b.maker.test.ts new file mode 100644 index 0000000..53bcd8b --- /dev/null +++ b/test/mainnet/b.protocol/b.maker.test.ts @@ -0,0 +1,336 @@ +import { expect } from "chai"; +import hre from "hardhat"; +const { web3, deployments, waffle, ethers } = hre; +const { provider, deployContract } = waffle + +import { deployAndEnableConnector } from "../../../scripts/tests/deployAndEnableConnector" +import { buildDSAv2 } from "../../../scripts/tests/buildDSAv2" +import { encodeSpells } from "../../../scripts/tests/encodeSpells" +import { getMasterSigner } from "../../../scripts/tests/getMasterSigner" +import { addresses } from "../../../scripts/tests/mainnet/addresses"; +import { abis } from "../../../scripts/constant/abis"; +import { tokens } from "../../../scripts/tests/mainnet/tokens"; +import { ConnectV2BMakerDAO__factory } from "../../../typechain"; +import type { Signer, Contract } from "ethers"; + +describe("B.Maker", function () { + const connectorName = "B.MAKER-TEST-A" + + let dsaWallet0: any; + let dsaWallet1: any; + let masterSigner: Signer; + let instaConnectorsV2: Contract; + let connector: any; + let manager: any; + let vat: any; + let dai: any; + + const wallets = provider.getWallets() + const [wallet0, wallet1, wallet2, wallet3] = wallets + before(async () => { + await hre.network.provider.request({ + method: "hardhat_reset", + params: [ + { + forking: { + // @ts-ignore + jsonRpcUrl: hre.config.networks.hardhat.forking.url, + blockNumber: 12696000, + }, + }, + ], + }); + masterSigner = await getMasterSigner() + instaConnectorsV2 = await ethers.getContractAt(abis.core.connectorsV2, addresses.core.connectorsV2); + connector = await deployAndEnableConnector({ + connectorName, + contractArtifact: ConnectV2BMakerDAO__factory, + signer: masterSigner, + connectors: instaConnectorsV2 + }) + + manager = await ethers.getContractAt("BManagerLike", "0x3f30c2381CD8B917Dd96EB2f1A4F96D91324BBed") + vat = await ethers.getContractAt("../artifacts/contracts/mainnet/connectors/b.protocol/makerdao/interface.sol:VatLike", await manager.vat()) + dai = await ethers.getContractAt("../artifacts/contracts/mainnet/common/interfaces.sol:TokenInterface", tokens.dai.address) + + console.log("Connector address", connector.address) + }) + + it("test veryClose.", async function () { + expect(veryClose(1000001, 1000000)).to.be.true + expect(veryClose(1000000, 1000001)).to.be.true + expect(veryClose(1003000, 1000001)).to.be.false + expect(veryClose(1000001, 1000300)).to.be.false + }); + + it("Should have contracts deployed.", async function () { + expect(!!instaConnectorsV2.address).to.be.true; + expect(!!connector.address).to.be.true; + expect(!!(await masterSigner.getAddress())).to.be.true; + expect(await connector.name()).to.be.equal("B.MakerDAO-v1.0"); + }); + + describe("DSA wallet setup", function () { + it("Should build DSA v2", async function () { + dsaWallet0 = await buildDSAv2(wallet0.address) + expect(!!dsaWallet0.address).to.be.true; + + dsaWallet1 = await buildDSAv2(wallet1.address) + expect(!!dsaWallet1.address).to.be.true; + }); + + it("Deposit ETH into DSA wallet", async function () { + await wallet0.sendTransaction({ + to: dsaWallet0.address, + value: ethers.utils.parseEther("10") + }); + expect(await ethers.provider.getBalance(dsaWallet0.address)).to.be.gte(ethers.utils.parseEther("10")); + + await wallet1.sendTransaction({ + to: dsaWallet1.address, + value: ethers.utils.parseEther("10") + }); + expect(await ethers.provider.getBalance(dsaWallet1.address)).to.be.gte(ethers.utils.parseEther("10")); + }); + }); + + describe("Main", function () { + let vault: any; + let ilk: any; + let urn: any; + + it("Should open ETH-A vault Maker", async function () { + vault = Number(await manager.cdpi()) + 1 + const spells = [ + { + connector: connectorName, + method: "open", + args: ["ETH-A"] + } + ] + + const tx = await dsaWallet0.connect(wallet0).cast(...encodeSpells(spells), wallet1.address) + const receipt = await tx.wait() + + expect(await manager.owns(vault)).to.be.equal(dsaWallet0.address) + + ilk = await manager.ilks(vault) + expect(ilk).to.be.equal("0x4554482d41000000000000000000000000000000000000000000000000000000") + + urn = await manager.urns(vault) + }); + + it("Should deposit", async function () { + const amount = ethers.utils.parseEther("7") // 7 ETH + const setId = "83478237" + + const spells = [ + { + connector: connectorName, + method: "deposit", + args: [vault, amount, 0, setId] + } + ] + + const tx = await dsaWallet0.connect(wallet0).cast(...encodeSpells(spells), wallet1.address) + const receipt = await tx.wait() + + expect(await ethers.provider.getBalance(dsaWallet0.address)).to.be.gte(ethers.utils.parseEther("3")) + + const urnData = await vat.urns(ilk, urn) + expect(urnData[0]).to.be.equal(amount) // ink + expect(urnData[1]).to.be.equal("0") // art + + }); + + it("Should withdraw", async function () { + const amount = ethers.utils.parseEther("1") // 1 ETH + const setId = "83478237" + + const spells = [ + { + connector: connectorName, + method: "withdraw", + args: [vault, amount, 0, setId] + } + ] + + const tx = await dsaWallet0.connect(wallet0).cast(...encodeSpells(spells), wallet1.address) + const receipt = await tx.wait() + + expect(await ethers.provider.getBalance(dsaWallet0.address)).to.be.gte(ethers.utils.parseEther("4")) + + const urnData = await vat.urns(ilk, urn) + expect(urnData[0]).to.be.equal(ethers.utils.parseEther("6")) // ink + expect(urnData[1]).to.be.equal("0") // art + + }); + + it("Should borrow", async function () { + const amount = ethers.utils.parseEther("6000") // 6000 dai + const setId = "83478237" + + const spells = [ + { + connector: connectorName, + method: "borrow", + args: [vault, amount, 0, setId] + } + ] + + const tx = await dsaWallet0.connect(wallet0).cast(...encodeSpells(spells), wallet1.address) + const receipt = await tx.wait() + + const urnData = await vat.urns(ilk, urn) + expect(urnData[0]).to.be.equal(ethers.utils.parseEther("6")) // ink + expect(urnData[1]).to.be.equal(await daiToArt(vat, ilk, amount)) // art + + expect(await dai.balanceOf(dsaWallet0.address)).to.be.equal(amount) + }); + + it("Should repay", async function () { + const amount = ethers.utils.parseEther("500") // 500 dai + const setId = "83478237" + + const spells = [ + { + connector: connectorName, + method: "payback", + args: [vault, amount, 0, setId] + } + ] + + const tx = await dsaWallet0.connect(wallet0).cast(...encodeSpells(spells), wallet1.address) + const receipt = await tx.wait() + + const urnData = await vat.urns(ilk, urn) + expect(urnData[0]).to.be.equal(ethers.utils.parseEther("6")) // ink + expect(urnData[1]).to.be.equal(await daiToArt(vat, ilk, ethers.utils.parseEther("5500"))) // art + expect(await dai.balanceOf(dsaWallet0.address)).to.be.equal(ethers.utils.parseEther("5500")) + }); + + it("Should depositAndBorrow", async function () { + const borrowAmount = ethers.utils.parseEther("1000") // 1000 dai + const depositAmount = ethers.utils.parseEther("1") // 1 dai + + const setId = "83478237" + + const spells = [ + { + connector: connectorName, + method: "depositAndBorrow", + args: [vault, depositAmount, borrowAmount, 0, 0, 0, 0] + } + ] + + const tx = await dsaWallet0.connect(wallet0).cast(...encodeSpells(spells), wallet1.address) + const receipt = await tx.wait() + + const urnData = await vat.urns(ilk, urn) + expect(urnData[0]).to.be.equal(ethers.utils.parseEther("7")) // ink + expect(await dai.balanceOf(dsaWallet0.address)).to.be.equal(ethers.utils.parseEther("6500")) + // calculation is not precise as the jug was dripped + expect(veryClose(urnData[1], await daiToArt(vat, ilk, ethers.utils.parseEther("6500")))).to.be.true + expect(await ethers.provider.getBalance(dsaWallet0.address)).to.be.gte(ethers.utils.parseEther("1")) + }); + + it("Should close", async function () { + // open a new vault + const newVault = vault + 1 + let spells = [ + { + connector: connectorName, + method: "open", + args: ["ETH-A"] + } + ] + + let tx = await dsaWallet1.connect(wallet1).cast(...encodeSpells(spells), wallet1.address) + let receipt = await tx.wait() + + expect(await manager.owns(newVault)).to.be.equal(dsaWallet1.address) + + ilk = await manager.ilks(newVault) + expect(ilk).to.be.equal("0x4554482d41000000000000000000000000000000000000000000000000000000") + + urn = await manager.urns(newVault) + + // deposit and borrow + const borrowAmount = ethers.utils.parseEther("6000") // 6000 dai + const depositAmount = ethers.utils.parseEther("5") // 5 ETH + + spells = [ + { + connector: connectorName, + method: "depositAndBorrow", + args: [newVault, depositAmount, borrowAmount, 0, 0, 0, 0] + } + ] + + tx = await dsaWallet1.connect(wallet1).cast(...encodeSpells(spells), wallet1.address) + receipt = await tx.wait() + + const setId = 0 + + // repay borrow + spells = [ + { + connector: connectorName, + method: "payback", + args: [newVault, borrowAmount, 0, setId] + } + ] + + tx = await dsaWallet1.connect(wallet1).cast(...encodeSpells(spells), wallet1.address) + receipt = await tx.wait() + + // withdraw deposit + spells = [ + { + connector: connectorName, + method: "withdraw", + args: [newVault, depositAmount, 0, setId] + } + ] + + tx = await dsaWallet1.connect(wallet1).cast(...encodeSpells(spells), wallet1.address) + receipt = await tx.wait() + + // close + spells = [ + { + connector: connectorName, + method: "close", + args: [newVault] + } + ] + + tx = await dsaWallet1.connect(wallet1).cast(...encodeSpells(spells), wallet1.address) + receipt = await tx.wait() + + expect(await manager.owns(newVault)).not.to.be.equal(dsaWallet1.address) + }); + }) +}) + +async function daiToArt(vat: any, ilk: any, dai: any) { + const ilks = await vat.ilks(ilk) + const rate = ilks[1] // second parameter + const _1e27 = ethers.utils.parseEther("1000000000") // 1e9 * 1e18 + const art = dai.mul(_1e27).div(rate) + + return art.add(1) +} + +function veryClose(n1: any, n2: any) { + n1 = web3.utils.toBN(n1) + n2 = web3.utils.toBN(n2) + + let _10000 = web3.utils.toBN(10000) + let _9999 = web3.utils.toBN(9999) + + if (n1.mul(_10000).lt(n2.mul(_9999))) return false + if (n2.mul(_10000).lt(n1.mul(_9999))) return false + + return true +} diff --git a/test/mainnet/basic-ERC1155/ERC1155-transfer.ts b/test/mainnet/basic-ERC1155/ERC1155-transfer.ts new file mode 100644 index 0000000..ceffc1c --- /dev/null +++ b/test/mainnet/basic-ERC1155/ERC1155-transfer.ts @@ -0,0 +1,135 @@ +import { expect } from "chai" +import hre from "hardhat" +const { web3, deployments, waffle, ethers } = hre; +const { provider, deployContract } = waffle +import { abi } from "../../../scripts/constant/abi/core/InstaImplementations.json" + +import { deployAndEnableConnector } from "../../../scripts/tests/deployAndEnableConnector" +import { buildDSAv2 } from "../../../scripts/tests/buildDSAv2" +import { encodeSpells } from "../../../scripts/tests/encodeSpells" +import { getMasterSigner } from "../../../scripts/tests/getMasterSigner" +import { addresses } from "../../../scripts/tests/mainnet/addresses" +import { abis } from "../../../scripts/constant/abis" +import type { Signer, Contract } from "ethers"; + +import { ConnectV2BasicERC1155__factory, IERC1155__factory } from "../../../typechain"; + +const TOKEN_CONTRACT_ADDR = "0x1ca3262009b21F944e6b92a2a88D039D06F1acFa"; +const TOKEN_OWNER_ADDR = "0x1ca3262009b21F944e6b92a2a88D039D06F1acFa"; +const TOKEN_ID = "1"; + +const implementationsMappingAddr = "0xCBA828153d3a85b30B5b912e1f2daCac5816aE9D" + +describe("BASIC-ERC1155", function () { + const connectorName = "BASIC-ERC1155-A" + + let dsaWallet0: any; + let masterSigner: Signer; + let instaConnectorsV2: Contract; + let connector: Contract; + let nftContract: Contract; + let tokenOwner: any; + let instaImplementationsMapping: any; + let InstaAccountV2DefaultImpl: any; + let instaAccountV2DefaultImpl: any; + + + const wallets = provider.getWallets() + const [wallet0, wallet1, wallet2, wallet3] = wallets + before(async () => { + await hre.network.provider.request({ + method: "hardhat_reset", + params: [ + { + forking: { + // @ts-ignore + jsonRpcUrl: hre.config.networks.hardhat.forking.url, + blockNumber: 13300000, + }, + }, + ], + }); + await hre.network.provider.request({ + method: "hardhat_impersonateAccount", + params: [TOKEN_OWNER_ADDR], + }); + + await hre.network.provider.send("hardhat_setBalance", [ + TOKEN_OWNER_ADDR, + "0x10000000000000000", + ]); + // get tokenOwner + tokenOwner = await ethers.getSigner( + TOKEN_OWNER_ADDR + ); + nftContract = await ethers.getContractAt(IERC1155__factory.abi, TOKEN_CONTRACT_ADDR) + masterSigner = await getMasterSigner() + instaConnectorsV2 = await ethers.getContractAt(abis.core.connectorsV2, addresses.core.connectorsV2); + + instaImplementationsMapping = await ethers.getContractAt(abi, implementationsMappingAddr); + InstaAccountV2DefaultImpl = await ethers.getContractFactory("InstaDefaultImplementation") + instaAccountV2DefaultImpl = await InstaAccountV2DefaultImpl.deploy(addresses.core.instaIndex); + await instaAccountV2DefaultImpl.deployed() + connector = await deployAndEnableConnector({ + connectorName, + contractArtifact: ConnectV2BasicERC1155__factory, + signer: masterSigner, + connectors: instaConnectorsV2 + }) + console.log("Connector address", connector.address) + }) + + it("Should have contracts deployed.", async function () { + expect(!!instaConnectorsV2.address).to.be.true; + expect(!!connector.address).to.be.true; + expect(!!(await masterSigner.getAddress())).to.be.true; + }); + + describe("Implementations", function () { + it("Should add default implementation to mapping.", async function () { + const tx = await instaImplementationsMapping.connect(masterSigner).setDefaultImplementation(instaAccountV2DefaultImpl.address); + await tx.wait() + expect(await instaImplementationsMapping.defaultImplementation()).to.be.equal(instaAccountV2DefaultImpl.address); + }); + }); + + describe("DSA wallet setup", function () { + it("Should build DSA v2", async function () { + dsaWallet0 = await buildDSAv2(tokenOwner.address) + expect(!!dsaWallet0.address).to.be.true; + }); + + it("Deposit ETH into DSA wallet", async function () { + await wallet0.sendTransaction({ + to: dsaWallet0.address, + value: ethers.utils.parseEther("10") + }); + expect(await ethers.provider.getBalance(dsaWallet0.address)).to.be.gte(ethers.utils.parseEther("10")); + }); + }); + + describe("Main", function () { + it("should deposit successfully", async () => { + console.log("DSA wallet address", dsaWallet0.address) + await nftContract.connect(tokenOwner).setApprovalForAll(dsaWallet0.address, true); + const spells = [ + { + connector: connectorName, + method: "depositERC1155", + args: [ + TOKEN_CONTRACT_ADDR, + TOKEN_ID, + 1, + "0", + "0" + ] + } + ]; + + const tx = await dsaWallet0 + .connect(tokenOwner) + .cast(...encodeSpells(spells), tokenOwner.address); + const receipt = await tx.wait(); + }); + }) +}) diff --git a/test/mainnet/basic-ERC721/ERC721-transfer.ts b/test/mainnet/basic-ERC721/ERC721-transfer.ts new file mode 100644 index 0000000..1ed72c0 --- /dev/null +++ b/test/mainnet/basic-ERC721/ERC721-transfer.ts @@ -0,0 +1,135 @@ +import { expect } from "chai"; +import hre, { network } from "hardhat"; +const { web3, deployments, waffle, ethers } = hre; +const { provider, deployContract } = waffle +import { abi } from "../../../scripts/constant/abi/core/InstaImplementations.json" + +import { deployAndEnableConnector } from "../../../scripts/tests/deployAndEnableConnector" +import { buildDSAv2 } from "../../../scripts/tests/buildDSAv2" +import { encodeSpells } from "../../../scripts/tests/encodeSpells" +import { getMasterSigner } from "../../../scripts/tests/getMasterSigner" +import { addresses } from "../../../scripts/tests/mainnet/addresses" +import { abis } from "../../../scripts/constant/abis" +import type { Signer, Contract } from "ethers"; + +import { ConnectV2BasicERC721__factory, IERC721__factory } from "../../../typechain"; + +const TOKEN_CONTRACT_ADDR = "0x4d695c615a7aacf2d7b9c481b66045bb2457dfde"; +const TOKEN_OWNER_ADDR = "0x8c6b10d42ff08e56133fca0dac75e1931b1fcc23"; +const TOKEN_ID = "38"; + +const implementationsMappingAddr = "0xCBA828153d3a85b30B5b912e1f2daCac5816aE9D" + +describe("BASIC-ERC721", function () { + const connectorName = "BASIC-ERC721-A" + + let dsaWallet0: any; + let masterSigner: Signer; + let instaConnectorsV2: Contract; + let connector: any; + let nftContract: any; + let tokenOwner: any; + let instaImplementationsMapping: any; + let InstaAccountV2DefaultImpl: any; + let instaAccountV2DefaultImpl: any; + + const wallets = provider.getWallets() + const [wallet0, wallet1, wallet2, wallet3] = wallets + before(async () => { + await hre.network.provider.request({ + method: "hardhat_reset", + params: [ + { + forking: { + // @ts-ignore + jsonRpcUrl: hre.config.networks.hardhat.forking.url, + blockNumber: 13300000, + }, + }, + ], + }); + await hre.network.provider.request({ + method: "hardhat_impersonateAccount", + params: [TOKEN_OWNER_ADDR], + }); + + await network.provider.send("hardhat_setBalance", [ + TOKEN_OWNER_ADDR, + "0x10000000000000000", + ]); + + // get tokenOwner + tokenOwner = await ethers.getSigner( + TOKEN_OWNER_ADDR + ); + nftContract = await ethers.getContractAt(IERC721__factory.abi, TOKEN_CONTRACT_ADDR) + masterSigner = await getMasterSigner() + instaConnectorsV2 = await ethers.getContractAt(abis.core.connectorsV2, addresses.core.connectorsV2); + + instaImplementationsMapping = await ethers.getContractAt(abi, implementationsMappingAddr); + InstaAccountV2DefaultImpl = await ethers.getContractFactory("InstaDefaultImplementation") + instaAccountV2DefaultImpl = await InstaAccountV2DefaultImpl.deploy(addresses.core.instaIndex); + await instaAccountV2DefaultImpl.deployed() + connector = await deployAndEnableConnector({ + connectorName, + contractArtifact: ConnectV2BasicERC721__factory, + signer: masterSigner, + connectors: instaConnectorsV2 + }) + console.log("Connector address", connector.address) + }) + + it("Should have contracts deployed.", async function () { + expect(!!instaConnectorsV2.address).to.be.true; + expect(!!connector.address).to.be.true; + expect(!!(await masterSigner.getAddress())).to.be.true; + }); + + describe("Implementations", function () { + it("Should add default implementation to mapping.", async function () { + const tx = await instaImplementationsMapping.connect(masterSigner).setDefaultImplementation(instaAccountV2DefaultImpl.address); + await tx.wait() + expect(await instaImplementationsMapping.defaultImplementation()).to.be.equal(instaAccountV2DefaultImpl.address); + }); + + }); + + describe("DSA wallet setup", function () { + it("Should build DSA v2", async function () { + dsaWallet0 = await buildDSAv2(tokenOwner.address) + expect(!!dsaWallet0.address).to.be.true; + }); + + it("Deposit ETH into DSA wallet", async function () { + await wallet0.sendTransaction({ + to: dsaWallet0.address, + value: ethers.utils.parseEther("10") + }); + expect(await ethers.provider.getBalance(dsaWallet0.address)).to.be.gte(ethers.utils.parseEther("10")); + }); + }); + + describe("Main", function () { + it("should deposit successfully", async () => { + console.log("DSA wallet address", dsaWallet0.address) + await nftContract.connect(tokenOwner).setApprovalForAll(dsaWallet0.address, true); + const spells = [ + { + connector: connectorName, + method: "depositERC721", + args: [ + TOKEN_CONTRACT_ADDR, + TOKEN_ID, + "0", + "0" + ] + } + ]; + + const tx = await dsaWallet0 + .connect(tokenOwner) + .cast(...encodeSpells(spells), tokenOwner.address); + const receipt = await tx.wait(); + }); + }) +}) diff --git a/test/mainnet/basic_ERC4626/ERC4626.test.ts b/test/mainnet/basic_ERC4626/ERC4626.test.ts new file mode 100644 index 0000000..032b01b --- /dev/null +++ b/test/mainnet/basic_ERC4626/ERC4626.test.ts @@ -0,0 +1,318 @@ +import { expect } from "chai"; +import hre, { network } from "hardhat"; +const { web3, deployments, waffle, ethers } = hre; +const { provider, deployContract } = waffle; + +import type { Signer, Contract } from "ethers"; +import { parseEther, parseUnits } from "ethers/lib/utils"; +import { BigNumber } from "bignumber.js"; + +import { deployAndEnableConnector } from "../../../scripts/tests/deployAndEnableConnector"; +import { buildDSAv2 } from "../../../scripts/tests/buildDSAv2"; +import { encodeSpells } from "../../../scripts/tests/encodeSpells"; +import { getMasterSigner } from "../../../scripts/tests/getMasterSigner"; +import { addresses } from "../../../scripts/tests/mainnet/addresses"; +import { abis } from "../../../scripts/constant/abis"; +import { tokens } from "../../../scripts/tests/mainnet/tokens"; +import { ConnectV2BasicERC4626__factory, IERC4626__factory, IERC20Minimal__factory } from "../../../typechain"; + +describe("BASIC-D", function () { + const connectorName = "BASIC-D"; + + let dsaWallet0: any; + let masterSigner: Signer; + let instaConnectorsV2: Contract; + let connector: any; + let wallet: any; + + const account = "0x075e72a5edf65f0a5f44699c7654c1a76941ddc8"; + const sDAIaddress = "0x83f20f44975d03b1b09e64809b757c47f942beea"; + let signer: any; + + const daiContract = new ethers.Contract(tokens.dai.address, IERC20Minimal__factory.abi, ethers.provider); + const erc4626Contract = new ethers.Contract(sDAIaddress, IERC4626__factory.abi, ethers.provider); + + const wallets = provider.getWallets(); + const [wallet0] = wallets; + + before(async () => { + await hre.network.provider.request({ + method: "hardhat_reset", + params: [ + { + forking: { + // @ts-ignore + jsonRpcUrl: hre.config.networks.hardhat.forking?.url, + blockNumber: 17907926 + } + } + ] + }); + + masterSigner = await getMasterSigner(); + instaConnectorsV2 = await ethers.getContractAt(abis.core.connectorsV2, addresses.core.connectorsV2); + connector = await deployAndEnableConnector({ + connectorName, + contractArtifact: ConnectV2BasicERC4626__factory, + signer: masterSigner, + connectors: instaConnectorsV2 + }); + + console.log("Connector address", connector.address); + + await hre.network.provider.send("hardhat_setBalance", [account, ethers.utils.parseEther("10").toHexString()]); + await hre.network.provider.request({ + method: "hardhat_impersonateAccount", + params: [account] + }); + + signer = await ethers.getSigner(account); + }); + + it("Should have contracts deployed.", async function () { + expect(!!instaConnectorsV2.address).to.be.true; + expect(!!connector.address).to.be.true; + expect(!!(await masterSigner.getAddress())).to.be.true; + }); + + describe("DSA wallet setup", function () { + it("Should build DSA v2", async function () { + dsaWallet0 = await buildDSAv2(wallet0.address); + expect(!!dsaWallet0.address).to.be.true; + wallet = await ethers.getSigner(dsaWallet0.address); + }); + + it("Deposit ETH into DSA wallet", async function () { + await hre.network.provider.request({ + method: "hardhat_impersonateAccount", + params: [wallet.address] + }); + + await wallet0.sendTransaction({ + to: dsaWallet0.address, + value: ethers.utils.parseEther("10") + }); + + let txRes = await daiContract.connect(signer).transfer(dsaWallet0.address, ethers.utils.parseEther("1000")); + await txRes.wait(); + // expect(await daiContract.balanceOf(dsaWallet0.address)).to.be.eq(ethers.utils.parseEther("10000")); + }); + }); + + describe("Main", function () { + // it("Calculate Total Asset and Total Supply", async () => { + // const totalAsset = await erc4626Contract.totalAssets(); + // const totalSupply = await erc4626Contract.totalSupply(); + // console.log("totalAsset :>> ", totalAsset); + // console.log("totalSupply :>> ", totalSupply); + // }); + it("should deposit asset to ERC4626", async () => { + const assets = ethers.utils.parseEther("1"); + + // Returns the amount of shares for assets + const previewDeposit = await erc4626Contract.previewDeposit(assets); + console.log("previewDeposit :>> ", previewDeposit.toString()); + + const maxDeposit = await erc4626Contract.maxDeposit(dsaWallet0.address); + + let minSharesPerToken = ethers.utils.parseUnits("0.95"); + + const beforebalance = await erc4626Contract.balanceOf(dsaWallet0.address); + console.log("Share before balance :>> ", beforebalance.toString()); + + let spells = [ + { + connector: connectorName, + method: "deposit", + args: [sDAIaddress, assets, minSharesPerToken, 0, 0] + } + ]; + + let tx = await dsaWallet0.connect(wallet0).cast(...encodeSpells(spells), wallet0.address); + let receipt = await tx.wait(); + + const afterbalance = await erc4626Contract.balanceOf(dsaWallet0.address); + console.log("Share after balance :>> ", afterbalance.toString()); + + expect(afterbalance.sub(beforebalance)).to.be.lte(previewDeposit) + + // In case of not satisfying min rate + minSharesPerToken = ethers.utils.parseUnits("1"); + spells = [ + { + connector: connectorName, + method: "deposit", + args: [sDAIaddress, assets, minSharesPerToken, 0, 0] + } + ]; + + await expect(dsaWallet0.connect(wallet0).cast(...encodeSpells(spells), wallet0.address)).to.be.reverted; + + }); + it("should mint asset to ERC4626", async () => { + const beforeBalance = await daiContract.balanceOf(dsaWallet0.address); + console.log("token balance before :>> ", beforeBalance.toString()); + const beforeSharebalance = await erc4626Contract.balanceOf(dsaWallet0.address); + console.log("share balance before :>> ", beforeSharebalance.toString()); + + const shares = ethers.utils.parseEther("1"); + // Returns token amount for shares + const previewMint = await erc4626Contract.previewMint(shares); + console.log("Token amount preview Mint :>> ", previewMint.toString()); + + let maxTokenPerShares = ethers.utils.parseUnits("1.1"); + + let spells = [ + { + connector: connectorName, + method: "mint", + args: [sDAIaddress, shares, maxTokenPerShares, 0, 0] + } + ]; + + let tx = await dsaWallet0.connect(wallet0).cast(...encodeSpells(spells), wallet0.address); + let receipt = await tx.wait(); + + const afterbalance = await daiContract.balanceOf(dsaWallet0.address); + console.log("token balance after :>> ", afterbalance.toString()); + const afterSharebalance = await erc4626Contract.balanceOf(dsaWallet0.address); + console.log("share balance after :>> ", afterSharebalance.toString()); + + // In case of not satisfying max rate + maxTokenPerShares = ethers.utils.parseUnits("1"); + + spells = [ + { + connector: connectorName, + method: "mint", + args: [sDAIaddress, shares, maxTokenPerShares, 0, 0] + } + ]; + + await expect(dsaWallet0.connect(wallet0).cast(...encodeSpells(spells), wallet0.address)).to.be.reverted; + + }); + it("should Max redeem", async () => { + const balance = await erc4626Contract.balanceOf(dsaWallet0.address); + console.log("Share balance :>> ", balance.toString()); + + // Returns max Shares + const maxRedeem: BigNumber = await erc4626Contract.maxRedeem(dsaWallet0.address); + console.log("maxRedeem :>> ", maxRedeem.toString()); + + const beforeUnderbalance = await daiContract.balanceOf(dsaWallet0.address); + console.log("beforeUnderbalance :>> ", beforeUnderbalance.toString()); + + const beforeVaultbalance = await erc4626Contract.balanceOf(dsaWallet0.address); + console.log("beforeVaultbalance :>> ", beforeVaultbalance.toString()); + + let minTokenPerShares = ethers.utils.parseUnits("1.01"); + + const setId = "83478237"; + let spells = [ + { + connector: connectorName, + method: "redeem", + args: [sDAIaddress, ethers.constants.MaxUint256, minTokenPerShares, dsaWallet0.address, 0, setId] + } + ]; + + let tx = await dsaWallet0.connect(wallet0).cast(...encodeSpells(spells), wallet0.address); + let receipt = await tx.wait(); + + const afterUnderbalance = await daiContract.balanceOf(dsaWallet0.address); + console.log("afterUnderbalance :>> ", afterUnderbalance.toString()); + + const afterVaultbalance = await erc4626Contract.balanceOf(dsaWallet0.address); + console.log("afterVaultbalance :>> ", afterVaultbalance.toString()); + }); + + it("should Revert for not satisfying min redeem rate", async () => { + const balance = await erc4626Contract.balanceOf(dsaWallet0.address); + console.log("Share balance :>> ", balance.toString()); + + let spells = [ + { + connector: connectorName, + method: "deposit", + args: [sDAIaddress, ethers.utils.parseEther("1"), ethers.utils.parseUnits("0.95"), 0, 0] + } + ]; + + let tx = await dsaWallet0.connect(wallet0).cast(...encodeSpells(spells), wallet0.address); + let receipt = await tx.wait(); + + // Returns max Shares + const maxRedeem: BigNumber = await erc4626Contract.maxRedeem(dsaWallet0.address); + console.log("maxRedeem :>> ", maxRedeem.toString()); + + const beforeUnderbalance = await daiContract.balanceOf(dsaWallet0.address); + console.log("beforeUnderbalance :>> ", beforeUnderbalance.toString()); + + const beforeVaultbalance = await erc4626Contract.balanceOf(dsaWallet0.address); + console.log("beforeVaultbalance :>> ", beforeVaultbalance.toString()); + + expect(beforeVaultbalance).to.be.gte("950000000000000000") + + // In case of not satisfying min rate + let minTokenPerShares = ethers.utils.parseUnits("1.5"); + + spells = [ + { + connector: connectorName, + method: "redeem", + args: [sDAIaddress, ethers.constants.MaxUint256, minTokenPerShares, dsaWallet0.address, 0, 0] + } + ]; + + await expect(dsaWallet0.connect(wallet0).cast(...encodeSpells(spells), wallet0.address)).to.be.reverted; + + }); + + it("should withdraw asset to ERC4626", async () => { + const maxWithdraw: BigNumber = await erc4626Contract.maxWithdraw(dsaWallet0.address); + console.log("maxWithdraw :>> ", maxWithdraw.toString()); + + const beforeUnderbalance = await daiContract.balanceOf(dsaWallet0.address); + console.log("beforeUnderbalance :>> ", beforeUnderbalance.toString()); + + const beforeVaultbalance = await erc4626Contract.balanceOf(dsaWallet0.address); + console.log("beforeVaultbalance :>> ", beforeVaultbalance.toString()); + + let maxSharesPerToken = ethers.utils.parseUnits("0.975"); + + const setId = "83478237"; + let spells = [ + { + connector: connectorName, + method: "withdraw", + args: [sDAIaddress, maxWithdraw, maxSharesPerToken, dsaWallet0.address, 0, setId] + } + ]; + + let tx = await dsaWallet0.connect(wallet0).cast(...encodeSpells(spells), wallet0.address); + let receipt = await tx.wait(); + + const afterUnderbalance = await daiContract.balanceOf(dsaWallet0.address); + console.log("afterUnderbalance :>> ", afterUnderbalance.toString()); + + const afterVaultbalance = await erc4626Contract.balanceOf(dsaWallet0.address); + console.log("afterVaultbalance :>> ", afterVaultbalance.toString()); + + // In case of not satisfying min rate + + maxSharesPerToken = ethers.utils.parseUnits("0.95"); + + spells = [ + { + connector: connectorName, + method: "withdraw", + args: [sDAIaddress, maxWithdraw, maxSharesPerToken, dsaWallet0.address, 0, setId] + } + ]; + + await expect(dsaWallet0.connect(wallet0).cast(...encodeSpells(spells), wallet0.address)).to.be.reverted; + + }); + }); +}); diff --git a/test/mainnet/compound-import/compound-import.test.ts b/test/mainnet/compound-import/compound-import.test.ts new file mode 100644 index 0000000..fb4585f --- /dev/null +++ b/test/mainnet/compound-import/compound-import.test.ts @@ -0,0 +1,254 @@ +import { expect, should } from "chai"; +import hre, { ethers, waffle } from "hardhat"; +import type { Signer, Contract } from "ethers"; +import { BigNumber } from "bignumber.js"; +import { buildDSAv2 } from "../../../scripts/tests/buildDSAv2"; +import { addresses } from "../../../scripts/tests/mainnet/addresses"; +import { deployAndEnableConnector } from "../../../scripts/tests/deployAndEnableConnector"; +import { abis } from "../../../scripts/constant/abis"; +import { getMasterSigner } from "../../../scripts/tests/getMasterSigner"; +import { parseEther, parseUnits } from "ethers/lib/utils"; +import { encodeSpells } from "../../../scripts/tests/encodeSpells"; +import encodeFlashcastData from "../../../scripts/tests/encodeFlashcastData"; +import { ConnectV2CompoundImport__factory } from "../../../typechain"; +const { provider } = waffle; + +const cEthAddress = "0x4Ddc2D193948926D02f9B1fE9e1daa0718270ED5"; +const cDaiAddress = "0x5d3a536E4D6DbD6114cc1Ead35777bAB948E3643"; +const daiAddress = "0x6B175474E89094C44Da98b954EedeAC495271d0F"; +const comptrollerAddress = "0x3d9819210A31b4961b30EF54bE2aeD79B9c9Cd3B"; + +describe("Import Compound", function () { + const connectorName = "COMPOUND-IMPORT-X"; + + const cEthAbi = [ + { + constant: false, + inputs: [], + name: "mint", + outputs: [], + payable: true, + stateMutability: "payable", + type: "function", + signature: "0x1249c58b" + }, + { + constant: true, + inputs: [{ internalType: "address", name: "owner", type: "address" }], + name: "balanceOf", + outputs: [{ internalType: "uint256", name: "", type: "uint256" }], + payable: false, + stateMutability: "view", + type: "function" + }, + { + constant: false, + inputs: [], + name: "exchangeRateCurrent", + outputs: [{ name: "", type: "uint256" }], + payable: false, + stateMutability: "nonpayable", + type: "function" + }, + { + constant: false, + inputs: [ + { internalType: "address", name: "usr", type: "address" }, + { internalType: "uint256", name: "wad", type: "uint256" } + ], + name: "approve", + outputs: [{ internalType: "bool", name: "", type: "bool" }], + payable: false, + stateMutability: "nonpayable", + type: "function" + } + ]; + + const cDaiAbi = [ + { + constant: false, + inputs: [ + { + internalType: "uint256", + name: "borrowAmount", + type: "uint256" + } + ], + name: "borrow", + outputs: [ + { + internalType: "uint256", + name: "", + type: "uint256" + } + ], + payable: false, + stateMutability: "nonpayable", + type: "function", + signature: "0xc5ebeaec" + }, + { + constant: false, + inputs: [{ internalType: "address", name: "account", type: "address" }], + name: "borrowBalanceCurrent", + outputs: [{ internalType: "uint256", name: "", type: "uint256" }], + payable: false, + stateMutability: "nonpayable", + type: "function" + } + ]; + + const comptrollerAbi = [ + { + constant: false, + inputs: [ + { + internalType: "address[]", + name: "cTokens", + type: "address[]" + } + ], + name: "enterMarkets", + outputs: [ + { + internalType: "uint256[]", + name: "", + type: "uint256[]" + } + ], + payable: false, + stateMutability: "nonpayable", + type: "function", + signature: "0xc2998238" + } + ]; + + let cEth: Contract, cDai: Contract, comptroller, Dai: any; + + let dsaWallet0: any; + let masterSigner: Signer; + let instaConnectorsV2: Contract; + let connector: any; + + const wallets = provider.getWallets(); + const [wallet0, wallet1, wallet2, wallet3] = wallets; + + before(async () => { + await hre.network.provider.request({ + method: "hardhat_reset", + params: [ + { + forking: { + // @ts-ignore + jsonRpcUrl: hre.config.networks.hardhat.forking.url, + blockNumber: 14441991 + } + } + ] + }); + + masterSigner = await getMasterSigner(); + instaConnectorsV2 = await ethers.getContractAt(abis.core.connectorsV2, addresses.core.connectorsV2); + + connector = await deployAndEnableConnector({ + connectorName, + contractArtifact: ConnectV2CompoundImport__factory, + signer: masterSigner, + connectors: instaConnectorsV2 + }); + console.log("Connector address", connector.address); + + cEth = new ethers.Contract(cEthAddress, cEthAbi); + cDai = new ethers.Contract(cDaiAddress, cDaiAbi); + Dai = new ethers.Contract(daiAddress, abis.basic.erc20); + comptroller = new ethers.Contract(comptrollerAddress, comptrollerAbi); + + // deposit ether to Compound: ETH-A + await cEth.connect(wallet0).mint({ + value: parseEther("9") + }); + + // enter markets with deposits + const cTokens = [cEth.address]; + await comptroller.connect(wallet0).enterMarkets(cTokens); + + // borrow dai from Compound: DAI-A + await cDai.connect(wallet0).borrow(parseUnits("100")); + }); + + describe("Deployment", async () => { + it("Should set correct name", async () => { + expect(await connector.name()).to.eq("Compound-Import-v2"); + }); + }); + + describe("checks", async () => { + it("Should check user COMPOUND position", async () => { + const ethExchangeRate = (await cEth.connect(wallet0).callStatic.exchangeRateCurrent()) / 1e28; + expect(new BigNumber(await cEth.connect(wallet0).balanceOf(wallet0.address)).dividedBy(1e8).toFixed(0)).to.eq( + new BigNumber(9).dividedBy(ethExchangeRate).toFixed(0) + ); + expect(await Dai.connect(wallet0).balanceOf(wallet0.address)).to.eq("100000000000000000000"); + }); + }); + + describe("DSA wallet setup", async () => { + it("Should build DSA v2", async () => { + dsaWallet0 = await buildDSAv2(wallet0.address); + expect(!!dsaWallet0.address).to.be.true; + }); + + it("Deposit ETH into DSA wallet", async function () { + await wallet0.sendTransaction({ + to: dsaWallet0.address, + value: ethers.utils.parseEther("10") + }); + expect(await ethers.provider.getBalance(dsaWallet0.address)).to.be.gte(ethers.utils.parseEther("10")); + }); + }); + + describe("Compound position migration", async () => { + it("Should migrate Compound position", async () => { + const tx0 = await cEth + .connect(wallet0) + .approve(dsaWallet0.address, await cEth.connect(wallet0).balanceOf(wallet0.address)); + + await tx0.wait(); + + // const amount0 = await cDai.connect(wallet0).callStatic.borrowBalanceCurrent(wallet0.address); + const amount0 = new BigNumber("100000007061117456728"); + const amount = new BigNumber(amount0.toString()).multipliedBy(5).dividedBy(1e4); + + const amountWithFee = amount0.plus(amount); + const flashSpells = [ + { + connector: "COMPOUND-IMPORT-X", + method: "importCompound", + args: [wallet0.address, ["ETH-A"], ["DAI-A"], [amount.toFixed(0)]] + }, + { + connector: "INSTAPOOL-C", + method: "flashPayback", + args: [daiAddress, amountWithFee.toFixed(0), 0, 0] + } + ]; + + const spells = [ + { + connector: "INSTAPOOL-C", + method: "flashBorrowAndCast", + args: [daiAddress, amount0.toString(), 5, encodeFlashcastData(flashSpells), "0x"] + } + ]; + const tx = await dsaWallet0.connect(wallet0).cast(...encodeSpells(spells), wallet0.address); + const receipt = await tx.wait(); + }); + + it("Should check DSA COMPOUND position", async () => { + const ethExchangeRate = (await cEth.connect(wallet0).callStatic.exchangeRateCurrent()) / 1e28; + expect(new BigNumber(await cEth.connect(wallet0).balanceOf(dsaWallet0.address)).dividedBy(1e8).toFixed(0)).to.eq( + new BigNumber(9).dividedBy(ethExchangeRate).toFixed(0) + ); + }); + }); +}); diff --git a/test/mainnet/compound-import/compound-v3-import.test.ts b/test/mainnet/compound-import/compound-v3-import.test.ts new file mode 100644 index 0000000..59f32ef --- /dev/null +++ b/test/mainnet/compound-import/compound-v3-import.test.ts @@ -0,0 +1,401 @@ +import { expect, should } from "chai"; +import hre, { ethers, waffle } from "hardhat"; +import type { Signer, Contract } from "ethers"; +import { ecsign, ecrecover, pubToAddress } from "ethereumjs-util"; +import { keccak256 } from "@ethersproject/keccak256"; +import { defaultAbiCoder } from "@ethersproject/abi"; +import { BigNumber } from "bignumber.js"; +import { buildDSAv2 } from "../../../scripts/tests/buildDSAv2"; +import { addresses } from "../../../scripts/tests/mainnet/addresses"; +import { deployAndEnableConnector } from "../../../scripts/tests/deployAndEnableConnector"; +import { abis } from "../../../scripts/constant/abis"; +import { getMasterSigner } from "../../../scripts/tests/getMasterSigner"; +import { parseEther, parseUnits } from "ethers/lib/utils"; +import { encodeSpells } from "../../../scripts/tests/encodeSpells"; +import encodeFlashcastData from "../../../scripts/tests/encodeFlashcastData"; +import { ConnectV2CompoundV3__factory, IERC20__factory } from "../../../typechain"; +import { tokens } from "../../../scripts/tests/mainnet/tokens"; +const { provider } = waffle; +import { getChainId } from "hardhat"; + +const ABI = [ + "function balanceOf(address account) public view returns (uint256)", + "function approve(address spender, uint256 amount) external returns(bool)", + "function transfer(address recipient, uint256 amount) external returns (bool)" +]; + +const market = "0xc3d688B66703497DAA19211EEdff47f25384cdc3"; +const user = "0x0a904e5e342d853952ad8159502dc1a29f9b084e"; +const wethWhale = "0xf04a5cc80b1e94c69b48f5ee68a08cd2f09a7c3e"; +const account = "0x72a53cdbbcc1b9efa39c834a540550e23463aacb"; +const mnemonic = "test test test test test test test test test test test junk"; +const connectorName = "COMPOUND-V3-X"; + +const cometABI = [ + { + inputs: [{ internalType: "address", name: "account", type: "address" }], + name: "balanceOf", + outputs: [{ internalType: "uint256", name: "", type: "uint256" }], + stateMutability: "view", + type: "function" + }, + { + inputs: [{ internalType: "address", name: "account", type: "address" }], + name: "borrowBalanceOf", + outputs: [{ internalType: "uint256", name: "", type: "uint256" }], + stateMutability: "view", + type: "function" + }, + { + inputs: [ + { internalType: "address", name: "asset", type: "address" }, + { internalType: "uint256", name: "amount", type: "uint256" } + ], + name: "supply", + outputs: [], + stateMutability: "nonpayable", + type: "function" + }, + { + inputs: [ + { internalType: "address", name: "dst", type: "address" }, + { internalType: "address", name: "asset", type: "address" }, + { internalType: "uint256", name: "amount", type: "uint256" } + ], + name: "supplyTo", + outputs: [], + stateMutability: "nonpayable", + type: "function" + }, + { + inputs: [ + { internalType: "address", name: "dst", type: "address" }, + { internalType: "uint256", name: "amount", type: "uint256" } + ], + name: "transfer", + outputs: [{ internalType: "bool", name: "", type: "bool" }], + stateMutability: "nonpayable", + type: "function" + }, + { + inputs: [ + { internalType: "address", name: "dst", type: "address" }, + { internalType: "address", name: "asset", type: "address" }, + { internalType: "uint256", name: "amount", type: "uint256" } + ], + name: "transferAsset", + outputs: [], + stateMutability: "nonpayable", + type: "function" + }, + { + inputs: [ + { internalType: "address", name: "src", type: "address" }, + { internalType: "address", name: "dst", type: "address" }, + { internalType: "address", name: "asset", type: "address" }, + { internalType: "uint256", name: "amount", type: "uint256" } + ], + name: "transferAssetFrom", + outputs: [], + stateMutability: "nonpayable", + type: "function" + }, + { + inputs: [ + { internalType: "address", name: "", type: "address" }, + { internalType: "address", name: "", type: "address" } + ], + name: "userCollateral", + outputs: [ + { internalType: "uint128", name: "balance", type: "uint128" }, + { internalType: "uint128", name: "_reserved", type: "uint128" } + ], + stateMutability: "view", + type: "function" + }, + { + inputs: [{ internalType: "address", name: "", type: "address" }], + name: "userNonce", + outputs: [{ internalType: "uint256", name: "", type: "uint256" }], + stateMutability: "view", + type: "function" + }, + { + inputs: [ + { internalType: "address", name: "asset", type: "address" }, + { internalType: "uint256", name: "amount", type: "uint256" } + ], + name: "withdraw", + outputs: [], + stateMutability: "nonpayable", + type: "function" + }, + { + inputs: [ + { internalType: "address", name: "owner", type: "address" }, + { internalType: "address", name: "manager", type: "address" }, + { internalType: "bool", name: "isAllowed_", type: "bool" }, + { internalType: "uint256", name: "nonce", type: "uint256" }, + { internalType: "uint256", name: "expiry", type: "uint256" }, + { internalType: "uint8", name: "v", type: "uint8" }, + { internalType: "bytes32", name: "r", type: "bytes32" }, + { internalType: "bytes32", name: "s", type: "bytes32" } + ], + name: "allowBySig", + outputs: [], + stateMutability: "nonpayable", + type: "function" + }, + { + inputs: [], + name: "version", + outputs: [{ internalType: "string", name: "", type: "string" }], + stateMutability: "view", + type: "function" + }, + { + inputs: [ + { internalType: "address", name: "manager", type: "address" }, + { internalType: "bool", name: "isAllowed_", type: "bool" } + ], + name: "allow", + outputs: [], + stateMutability: "nonpayable", + type: "function" + } +]; + +const comet = new ethers.Contract(market, cometABI); +const wethContract = new ethers.Contract(tokens.weth.address, ABI); + +describe("Import Compound v3 Position", function () { + let dsaWallet0: any; + let masterSigner: Signer; + let signer: any; + let wallet0: any; + let walletSigner: any; + let instaConnectorsV2: Contract; + let connector: any; + + const wallets = provider.getWallets(); + const [wallet1, wallet2, wallet3] = wallets; + + const wallet = ethers.Wallet.fromMnemonic(mnemonic); + + before(async () => { + await hre.network.provider.request({ + method: "hardhat_reset", + params: [ + { + forking: { + //@ts-ignore + jsonRpcUrl: hre.config.networks.hardhat.forking.url, + blockNumber: 15469858 + } + } + ] + }); + masterSigner = await getMasterSigner(); + + await hre.network.provider.send("hardhat_setBalance", [account, ethers.utils.parseEther("10").toHexString()]); + await hre.network.provider.send("hardhat_setBalance", [wethWhale, ethers.utils.parseEther("10").toHexString()]); + + await hre.network.provider.request({ + method: "hardhat_impersonateAccount", + params: [wethWhale] + }); + signer = await ethers.getSigner(wethWhale); + [wallet0] = await ethers.getSigners(); + + await hre.network.provider.request({ + method: "hardhat_impersonateAccount", + params: [wallet.address] + }); + walletSigner = await ethers.getSigner(wallet.address); + instaConnectorsV2 = await ethers.getContractAt(abis.core.connectorsV2, addresses.core.connectorsV2); + connector = await deployAndEnableConnector({ + connectorName, + contractArtifact: ConnectV2CompoundV3__factory, + signer: masterSigner, + connectors: instaConnectorsV2 + }); + }); + + describe("check user Compound position", async () => { + it("Should create Compound v3 position of WETH(collateral) and USDC(debt)", async () => { + await wethContract.connect(signer).transfer(wallet.address, parseEther("100")); + // approve WETH to market + + await wethContract.connect(walletSigner).approve(market, parseEther("100")); + + //deposit WETH in Compound + await comet.connect(walletSigner).supply(tokens.weth.address, parseEther("100")); + console.log("Supplied WETH on compound"); + + //borrow Base from compound + await comet.connect(walletSigner).withdraw(tokens.usdc.address, parseUnits("100", 6)); + console.log("Borrowed USDC from compound"); + }); + + it("Should check position of user", async () => { + expect((await comet.connect(signer).userCollateral(wallet.address, tokens.weth.address)).balance).to.be.gte( + new BigNumber(100).multipliedBy(1e18).toString() + ); + + expect(await comet.connect(signer).borrowBalanceOf(wallet.address)).to.be.gte( + new BigNumber(100).multipliedBy(1e6).toString() + ); + }); + }); + + describe("Deployment", async () => { + it("Should set correct name", async () => { + expect(await connector.name()).to.eq("CompoundV3-v1.0"); + }); + }); + + describe("DSA wallet setup", async () => { + it("Should build DSA v2", async () => { + dsaWallet0 = await buildDSAv2(wallet.address); + expect(!!dsaWallet0.address).to.be.true; + }); + + it("Deposit ETH into DSA wallet", async function () { + await wallet0.sendTransaction({ + to: dsaWallet0.address, + value: ethers.utils.parseEther("10") + }); + + expect(await ethers.provider.getBalance(dsaWallet0.address)).to.be.gte(ethers.utils.parseEther("10")); + }); + }); + + describe("Compound position migration - Using `toggleManagerUsingPermit` spell by Manager DSA", async () => { + let initialbal: any; + let initialborrow: any; + + it("Should migrate Compound position", async () => { + initialbal = new BigNumber( + (await comet.connect(wallet0).userCollateral(dsaWallet0.address, tokens.weth.address)).balance + ); + initialborrow = new BigNumber(await comet.connect(wallet0).borrowBalanceOf(dsaWallet0.address)); + + const DOMAIN_TYPEHASH = keccak256( + ethers.utils.toUtf8Bytes("EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)") + ); + const PERMIT_TYPEHASH = keccak256( + ethers.utils.toUtf8Bytes( + "Authorization(address owner,address manager,bool isAllowed,uint256 nonce,uint256 expiry)" + ) + ); + const name = keccak256(ethers.utils.toUtf8Bytes("Compound USDC")); + const version = keccak256(ethers.utils.toUtf8Bytes("0")); + //hardhat network chainID + const chainId = new BigNumber(await getChainId()).toFixed(0); + const DOMAIN_SEPARATOR = keccak256( + defaultAbiCoder.encode( + ["bytes32", "bytes32", "bytes32", "uint256", "address"], + [DOMAIN_TYPEHASH, name, version, chainId, market] + ) + ); + + let nonce = new BigNumber(await comet.connect(walletSigner).userNonce(wallet.address)).toFixed(0); + //Approving max amount + const amount = ethers.constants.MaxUint256; + const expiry = Date.now() + 100 * 60; + const structHash = keccak256( + defaultAbiCoder.encode( + ["bytes32", "address", "address", "bool", "uint256", "uint256"], + [PERMIT_TYPEHASH, wallet.address, dsaWallet0.address, true, nonce, expiry] + ) + ); + const digest = keccak256( + ethers.utils.solidityPack( + ["bytes1", "bytes1", "bytes32", "bytes32"], + ["0x19", "0x01", DOMAIN_SEPARATOR, structHash] + ) + ); + const { v, r, s } = ecsign(Buffer.from(digest.slice(2), "hex"), Buffer.from(wallet.privateKey.slice(2), "hex")); + let buffer = ethers.utils.parseUnits("100", 3).toNumber(); + let amount0 = new BigNumber(await comet.connect(wallet0).borrowBalanceOf(wallet.address)).plus(buffer); + let amountB = new BigNumber(amount0.toString()).multipliedBy(5).dividedBy(1e4); + let amountWithFee = amount0.plus(amountB); + + console.log(`\n\tOwner: ${wallet.address}`); + console.log(`\tManager: ${dsaWallet0.address}`); + console.log(`\tdomain speparator: ${DOMAIN_SEPARATOR}`); + console.log(`\tdomain typehash: ${DOMAIN_TYPEHASH}`); + console.log(`\tpermit typehash: ${PERMIT_TYPEHASH}`); + console.log(`\tnonce: ${nonce}`); + console.log(`\texpiry: ${expiry}`); + console.log(`\tv: ${v}`); + console.log(`\tr: ${ethers.utils.hexlify(r)}`); + console.log(`\ts: ${ethers.utils.hexlify(s)}`); + console.log(`\tDigest: ${digest}`); + console.log(`\tstructHash: ${structHash}`); + console.log(`\tblock timestamp: ${(await provider.getBlock(15469858)).timestamp}`); + + const flashSpells = [ + { + connector: "COMPOUND-V3-X", + method: "paybackOnBehalf", + args: [market, tokens.usdc.address, wallet.address, ethers.constants.MaxUint256, 0, 0] + }, + { + connector: "COMPOUND-V3-X", + method: "transferAssetOnBehalf", + args: [market, tokens.weth.address, wallet.address, dsaWallet0.address, ethers.constants.MaxUint256, 0, 0] + }, + { + connector: "COMPOUND-V3-X", + method: "borrow", + args: [market, tokens.usdc.address, amountWithFee.toFixed(0), 0, 0] + }, + { + connector: "INSTAPOOL-C", + method: "flashPayback", + args: [tokens.usdc.address, amountWithFee.toFixed(0), 0, 0] + } + ]; + const spells = [ + { + connector: "COMPOUND-V3-X", + method: "toggleAccountManagerWithPermit", + args: [ + market, + wallet.address, + dsaWallet0.address, + true, + nonce, + expiry, + v, + ethers.utils.hexlify(r), + ethers.utils.hexlify(s) + ] + }, + { + connector: "INSTAPOOL-C", + method: "flashBorrowAndCast", + args: [tokens.usdc.address, amount0.toFixed(), 5, encodeFlashcastData(flashSpells), "0x"] + } + ]; + + let tx = await dsaWallet0.connect(walletSigner).cast(...encodeSpells(spells), wallet0.address); + await tx.wait(); + }); + + it("Should check DSA COMPOUND position", async () => { + expect((await comet.connect(wallet0).userCollateral(dsaWallet0.address, tokens.weth.address)).balance).to.be.gte( + initialbal.plus(100 * 1e18).toFixed(0) + ); + expect(await comet.connect(wallet0).borrowBalanceOf(dsaWallet0.address)).to.be.gte( + initialborrow.plus(100 * 1e6).toFixed(0) + ); + + expect((await comet.connect(wallet0).userCollateral(wallet.address, tokens.weth.address)).balance).to.be.lte( + ethers.utils.parseEther("0") + ); + expect(await comet.connect(wallet0).borrowBalanceOf(wallet.address)).to.be.lte(ethers.utils.parseUnits("0", 6)); + }); + }); +}); diff --git a/test/mainnet/compound/compound.iii.rewards.test.ts b/test/mainnet/compound/compound.iii.rewards.test.ts new file mode 100644 index 0000000..401b50b --- /dev/null +++ b/test/mainnet/compound/compound.iii.rewards.test.ts @@ -0,0 +1,444 @@ +import { expect } from "chai"; +import hre from "hardhat"; +const { waffle, ethers } = hre; +const { provider, deployContract } = waffle; + +import { Signer, Contract } from "ethers"; +import { BigNumber } from "bignumber.js"; + +import { deployAndEnableConnector } from "../../../scripts/tests/deployAndEnableConnector"; +import { buildDSAv2 } from "../../../scripts/tests/buildDSAv2"; +import { encodeSpells } from "../../../scripts/tests/encodeSpells"; +import { getMasterSigner } from "../../../scripts/tests/getMasterSigner"; +import { addresses } from "../../../scripts/tests/mainnet/addresses"; +import { tokens, tokenMapping } from "../../../scripts/tests/mainnet/tokens"; +import { abis } from "../../../scripts/constant/abis"; +import { ConnectV2CompoundV3Rewards__factory, ConnectV2CompoundV3__factory } from "../../../typechain"; + +describe("Compound III Rewards", function () { + let connectorName = "COMPOUND-V3-REWARDS-TEST-A"; + const market = "0xc3d688B66703497DAA19211EEdff47f25384cdc3"; + const rewards = "0x1B0e765F6224C21223AeA2af16c1C46E38885a40"; + const base = "0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48"; + const account = "0x72a53cdbbcc1b9efa39c834a540550e23463aacb"; + const wethWhale = "0x1c11ba15939e1c16ec7ca1678df6160ea2063bc5"; + const baseWhale = "0x72a53cdbbcc1b9efa39c834a540550e23463aacb"; + + const ABI = [ + "function balanceOf(address account) public view returns (uint256)", + "function approve(address spender, uint256 amount) external returns(bool)", + "function transfer(address recipient, uint256 amount) external returns (bool)" + ]; + const wethContract = new ethers.Contract(tokens.weth.address, ABI); + const baseContract = new ethers.Contract(base, ABI); + + const cometABI = [ + { + inputs: [ + { internalType: "address", name: "comet", type: "address" }, + { internalType: "address", name: "src", type: "address" }, + { internalType: "bool", name: "shouldAccrue", type: "bool" } + ], + name: "claim", + outputs: [], + stateMutability: "nonpayable", + type: "function" + }, + { + inputs: [ + { internalType: "address", name: "comet", type: "address" }, + { internalType: "address", name: "src", type: "address" }, + { internalType: "address", name: "to", type: "address" }, + { internalType: "bool", name: "shouldAccrue", type: "bool" } + ], + name: "claimTo", + outputs: [], + stateMutability: "nonpayable", + type: "function" + }, + { + inputs: [ + { internalType: "address", name: "comet", type: "address" }, + { internalType: "address", name: "account", type: "address" } + ], + name: "getRewardOwed", + outputs: [ + { + components: [ + { internalType: "address", name: "token", type: "address" }, + { internalType: "uint256", name: "owed", type: "uint256" } + ], + internalType: "struct CometRewards.RewardOwed", + name: "", + type: "tuple" + } + ], + stateMutability: "nonpayable", + type: "function" + }, + { + inputs: [{ internalType: "address", name: "", type: "address" }], + name: "rewardConfig", + outputs: [ + { internalType: "address", name: "token", type: "address" }, + { internalType: "uint64", name: "rescaleFactor", type: "uint64" }, + { internalType: "bool", name: "shouldUpscale", type: "bool" } + ], + stateMutability: "view", + type: "function" + }, + { + inputs: [ + { internalType: "address", name: "", type: "address" }, + { internalType: "address", name: "", type: "address" } + ], + name: "rewardsClaimed", + outputs: [{ internalType: "uint256", name: "", type: "uint256" }], + stateMutability: "view", + type: "function" + } + ]; + + const marketABI = [ + { + inputs: [{ internalType: "address", name: "account", type: "address" }], + name: "balanceOf", + outputs: [{ internalType: "uint256", name: "", type: "uint256" }], + stateMutability: "view", + type: "function" + }, + { + inputs: [{ internalType: "address", name: "account", type: "address" }], + name: "borrowBalanceOf", + outputs: [{ internalType: "uint256", name: "", type: "uint256" }], + stateMutability: "view", + type: "function" + }, + { + inputs: [], + name: "baseBorrowMin", + outputs: [{ internalType: "uint256", name: "", type: "uint256" }], + stateMutability: "view", + type: "function" + }, + { + inputs: [], + name: "baseMinForRewards", + outputs: [{ internalType: "uint256", name: "", type: "uint256" }], + stateMutability: "view", + type: "function" + }, + { + inputs: [], + name: "baseToken", + outputs: [{ internalType: "address", name: "", type: "address" }], + stateMutability: "view", + type: "function" + }, + { + inputs: [], + name: "decimals", + outputs: [{ internalType: "uint8", name: "", type: "uint8" }], + stateMutability: "view", + type: "function" + }, + { + inputs: [{ internalType: "address", name: "priceFeed", type: "address" }], + name: "getPrice", + outputs: [{ internalType: "uint256", name: "", type: "uint256" }], + stateMutability: "view", + type: "function" + }, + { + inputs: [ + { internalType: "address", name: "owner", type: "address" }, + { internalType: "address", name: "manager", type: "address" } + ], + name: "hasPermission", + outputs: [{ internalType: "bool", name: "", type: "bool" }], + stateMutability: "view", + type: "function" + }, + { + inputs: [], + name: "numAssets", + outputs: [{ internalType: "uint8", name: "", type: "uint8" }], + stateMutability: "view", + type: "function" + }, + { + inputs: [ + { internalType: "address", name: "asset", type: "address" }, + { internalType: "uint256", name: "baseAmount", type: "uint256" } + ], + name: "quoteCollateral", + outputs: [{ internalType: "uint256", name: "", type: "uint256" }], + stateMutability: "view", + type: "function" + }, + { + inputs: [{ internalType: "address", name: "", type: "address" }], + name: "userBasic", + outputs: [ + { internalType: "int104", name: "principal", type: "int104" }, + { internalType: "uint64", name: "baseTrackingIndex", type: "uint64" }, + { internalType: "uint64", name: "baseTrackingAccrued", type: "uint64" }, + { internalType: "uint16", name: "assetsIn", type: "uint16" }, + { internalType: "uint8", name: "_reserved", type: "uint8" } + ], + stateMutability: "view", + type: "function" + }, + { + inputs: [ + { internalType: "address", name: "", type: "address" }, + { internalType: "address", name: "", type: "address" } + ], + name: "userCollateral", + outputs: [ + { internalType: "uint128", name: "balance", type: "uint128" }, + { internalType: "uint128", name: "_reserved", type: "uint128" } + ], + stateMutability: "view", + type: "function" + } + ]; + + let dsaWallet0: any; + let dsaWallet1: any; + let wallet: any; + let dsa0Signer: any; + let masterSigner: Signer; + let instaConnectorsV2: Contract; + let connector: any; + let connectorMain: any; + let signer: any; + let wethSigner: any; + let usdcSigner: any; + + const cometReward = new ethers.Contract(rewards, cometABI); + const comet = new ethers.Contract(market, marketABI); + + const wallets = provider.getWallets(); + const [wallet0, wallet1, wallet2, wallet3] = wallets; + + before(async () => { + await hre.network.provider.request({ + method: "hardhat_reset", + params: [ + { + forking: { + //@ts-ignore + jsonRpcUrl: hre.config.networks.hardhat.forking.url, + blockNumber: 15444500 + } + } + ] + }); + masterSigner = await getMasterSigner(); + instaConnectorsV2 = await ethers.getContractAt(abis.core.connectorsV2, addresses.core.connectorsV2); + connector = await deployAndEnableConnector({ + connectorName, + contractArtifact: ConnectV2CompoundV3Rewards__factory, + signer: masterSigner, + connectors: instaConnectorsV2 + }); + console.log("Connector address", connector.address); + + await hre.network.provider.send("hardhat_setBalance", [account, ethers.utils.parseEther("10").toHexString()]); + + await hre.network.provider.request({ + method: "hardhat_impersonateAccount", + params: [account] + }); + + signer = await ethers.getSigner(account); + + await hre.network.provider.request({ + method: "hardhat_impersonateAccount", + params: [wethWhale] + }); + wethSigner = await ethers.getSigner(wethWhale); + + await hre.network.provider.request({ + method: "hardhat_impersonateAccount", + params: [baseWhale] + }); + usdcSigner = await ethers.getSigner(baseWhale); + await hre.network.provider.send("hardhat_setBalance", [ + usdcSigner.address, + ethers.utils.parseEther("10").toHexString() + ]); + }); + + it("Should have contracts deployed.", async function () { + expect(!!instaConnectorsV2.address).to.be.true; + expect(!!connector.address).to.be.true; + expect(!!(await masterSigner.getAddress())).to.be.true; + }); + + describe("DSA wallet setup", function () { + it("Should build DSA v2", async function () { + dsaWallet0 = await buildDSAv2(wallet0.address); + expect(!!dsaWallet0.address).to.be.true; + dsaWallet1 = await buildDSAv2(wallet0.address); + expect(!!dsaWallet1.address).to.be.true; + wallet = await ethers.getSigner(dsaWallet0.address); + expect(!!dsaWallet1.address).to.be.true; + }); + + it("Deposit ETH into DSA wallet", async function () { + await hre.network.provider.request({ + method: "hardhat_impersonateAccount", + params: [wallet.address] + }); + + dsa0Signer = await ethers.getSigner(wallet.address); + await wallet0.sendTransaction({ + to: dsaWallet0.address, + value: ethers.utils.parseEther("10") + }); + expect(await ethers.provider.getBalance(dsaWallet0.address)).to.be.gte(ethers.utils.parseEther("10")); + await wallet0.sendTransaction({ + to: dsaWallet1.address, + value: ethers.utils.parseEther("10") + }); + expect(await ethers.provider.getBalance(dsaWallet1.address)).to.be.gte(ethers.utils.parseEther("10")); + }); + + it("should deposit USDC in dsa wallet", async function () { + await baseContract.connect(usdcSigner).transfer(dsaWallet0.address, ethers.utils.parseUnits("500", 6)); + + expect(await baseContract.connect(usdcSigner).balanceOf(dsaWallet0.address)).to.be.gte( + ethers.utils.parseUnits("500", 6) + ); + }); + }); + + describe("Main", function () { + //deposit asset + it("Should supply USDC in Compound V3", async function () { + connectorName = "COMPOUND-V3-TEST-A"; + connectorMain = await deployAndEnableConnector({ + connectorName, + contractArtifact: ConnectV2CompoundV3__factory, + signer: masterSigner, + connectors: instaConnectorsV2 + }); + const amount = ethers.utils.parseUnits("400", 6); + const spells = [ + { + connector: "COMPOUND-V3-TEST-A", + method: "deposit", + args: [market, base, amount, 0, 0] + } + ]; + + const tx = await dsaWallet0.connect(wallet0).cast(...encodeSpells(spells), wallet1.address); + const receipt = await tx.wait(); + expect(new BigNumber(await baseContract.connect(signer).balanceOf(dsaWallet0.address)).toFixed(0)).to.be.lte( + ethers.utils.parseUnits("100", 6) + ); + expect(new BigNumber(await comet.connect(signer).balanceOf(dsaWallet0.address)).toFixed(0)).to.be.gte( + ethers.utils.parseUnits("399", 6) + ); + }); + + let connector_ = "COMPOUND-V3-REWARDS-TEST-A"; + it("Should claim rewards", async function () { + let reward = (await cometReward.connect(signer).rewardConfig(market)).token; + let rewardInterface = new ethers.Contract(reward, ABI); + let owed_ = await cometReward.connect(signer).callStatic.getRewardOwed(market, dsaWallet0.address); + let amt: number = owed_.owed; + console.log(new BigNumber(amt).toFixed(0)); + const spells = [ + { + connector: connector_, + method: "claimRewards", + args: [market, 0] + } + ]; + + const tx = await dsaWallet0.connect(wallet0).cast(...encodeSpells(spells), wallet1.address); + const receipt = await tx.wait(); + expect(new BigNumber(await rewardInterface.connect(signer).balanceOf(dsaWallet0.address)).toFixed(0)).to.be.gte( + amt + ); + }); + + it("Should supply USDC in Compound V3 through dsaWallet0", async function () { + const amount = ethers.utils.parseUnits("100", 6); // 1 ETH + const spells = [ + { + connector: "COMPOUND-V3-TEST-A", + method: "deposit", + args: [market, base, amount, 0, 0] + } + ]; + + const tx = await dsaWallet0.connect(wallet0).cast(...encodeSpells(spells), wallet1.address); + const receipt = await tx.wait(); + expect(new BigNumber(await baseContract.connect(signer).balanceOf(dsaWallet0.address)).toFixed(0)).to.be.lte( + ethers.utils.parseUnits("0", 6) + ); + expect(new BigNumber(await comet.connect(signer).balanceOf(dsaWallet0.address)).toFixed(0)).to.be.gte( + ethers.utils.parseUnits("499", 6) + ); + }); + + it("Should claim rewards to dsa1", async function () { + let reward = (await cometReward.connect(signer).rewardConfig(market)).token; + let rewardInterface = new ethers.Contract(reward, ABI); + let owed_ = await cometReward.connect(signer).callStatic.getRewardOwed(market, dsaWallet0.address); + let amt: number = owed_.owed; + + const spells = [ + { + connector: connector_, + method: "claimRewardsOnBehalfOf", + args: [market, dsaWallet0.address, dsaWallet1.address, 0] + } + ]; + + const tx = await dsaWallet0.connect(wallet0).cast(...encodeSpells(spells), wallet1.address); + const receipt = await tx.wait(); + expect(new BigNumber(await rewardInterface.connect(signer).balanceOf(dsaWallet1.address)).toFixed(0)).to.be.gte( + amt + ); + }); + + it("should allow manager for dsaWallet0's collateral and base", async function () { + const spells = [ + { + connector: connectorName, + method: "toggleAccountManager", + args: [market, dsaWallet1.address, true] + } + ]; + + const tx = await dsaWallet0.connect(wallet0).cast(...encodeSpells(spells), wallet1.address); + const receipt = await tx.wait(); + }); + + it("Should claim rewards to dsa1 using manager", async function () { + let reward = (await cometReward.connect(signer).rewardConfig(market)).token; + let rewardInterface = new ethers.Contract(reward, ABI); + let owed_ = await cometReward.connect(signer).callStatic.getRewardOwed(market, dsaWallet0.address); + let amt: number = owed_.owed; + + const spells = [ + { + connector: connector_, + method: "claimRewardsOnBehalfOf", + args: [market, dsaWallet0.address, dsaWallet1.address, 0] + } + ]; + + const tx = await dsaWallet1.connect(wallet0).cast(...encodeSpells(spells), wallet1.address); + const receipt = await tx.wait(); + expect(new BigNumber(await rewardInterface.connect(signer).balanceOf(dsaWallet1.address)).toFixed(0)).to.be.gte( + amt + ); + }); + }); +}); diff --git a/test/mainnet/compound/compound.iii.test.ts b/test/mainnet/compound/compound.iii.test.ts new file mode 100644 index 0000000..3d60c91 --- /dev/null +++ b/test/mainnet/compound/compound.iii.test.ts @@ -0,0 +1,653 @@ +import { expect } from "chai"; +import hre from "hardhat"; +const { waffle, ethers } = hre; +const { provider, deployContract } = waffle; + +import { Signer, Contract } from "ethers"; +import { BigNumber } from "bignumber.js"; + +import { deployAndEnableConnector } from "../../../scripts/tests/deployAndEnableConnector"; +import { buildDSAv2 } from "../../../scripts/tests/buildDSAv2"; +import { encodeSpells } from "../../../scripts/tests/encodeSpells"; +import { getMasterSigner } from "../../../scripts/tests/getMasterSigner"; +import { addresses } from "../../../scripts/tests/mainnet/addresses"; +import { tokens, tokenMapping } from "../../../scripts/tests/mainnet/tokens"; +import { abis } from "../../../scripts/constant/abis"; +import { constants } from "../../../scripts/constant/constant"; +import { ConnectV2CompoundV3__factory } from "../../../typechain"; +import { MaxUint256 } from "@uniswap/sdk-core"; +import { USDC_OPTIMISTIC_KOVAN } from "@uniswap/smart-order-router"; + +describe("Compound III", function () { + const connectorName = "COMPOUND-V3-TEST-A"; + const market = "0xc3d688B66703497DAA19211EEdff47f25384cdc3"; + const base = "0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48"; + const account = "0x72a53cdbbcc1b9efa39c834a540550e23463aacb"; + const wethWhale = "0x1c11ba15939e1c16ec7ca1678df6160ea2063bc5"; + + const ABI = [ + "function balanceOf(address account) public view returns (uint256)", + "function approve(address spender, uint256 amount) external returns(bool)", + "function transfer(address recipient, uint256 amount) external returns (bool)" + ]; + const wethContract = new ethers.Contract(tokens.weth.address, ABI); + const baseContract = new ethers.Contract(base, ABI); + const linkContract = new ethers.Contract(tokens.link.address, ABI); + + const cometABI = [ + { + inputs: [{ internalType: "address", name: "account", type: "address" }], + name: "balanceOf", + outputs: [{ internalType: "uint256", name: "", type: "uint256" }], + stateMutability: "view", + type: "function" + }, + { + inputs: [{ internalType: "address", name: "account", type: "address" }], + name: "borrowBalanceOf", + outputs: [{ internalType: "uint256", name: "", type: "uint256" }], + stateMutability: "view", + type: "function" + }, + { + inputs: [], + name: "baseBorrowMin", + outputs: [{ internalType: "uint256", name: "", type: "uint256" }], + stateMutability: "view", + type: "function" + }, + { + inputs: [], + name: "baseMinForRewards", + outputs: [{ internalType: "uint256", name: "", type: "uint256" }], + stateMutability: "view", + type: "function" + }, + { + inputs: [], + name: "baseToken", + outputs: [{ internalType: "address", name: "", type: "address" }], + stateMutability: "view", + type: "function" + }, + { + inputs: [], + name: "decimals", + outputs: [{ internalType: "uint8", name: "", type: "uint8" }], + stateMutability: "view", + type: "function" + }, + { + inputs: [{ internalType: "address", name: "priceFeed", type: "address" }], + name: "getPrice", + outputs: [{ internalType: "uint256", name: "", type: "uint256" }], + stateMutability: "view", + type: "function" + }, + { + inputs: [ + { internalType: "address", name: "owner", type: "address" }, + { internalType: "address", name: "manager", type: "address" } + ], + name: "hasPermission", + outputs: [{ internalType: "bool", name: "", type: "bool" }], + stateMutability: "view", + type: "function" + }, + { + inputs: [], + name: "numAssets", + outputs: [{ internalType: "uint8", name: "", type: "uint8" }], + stateMutability: "view", + type: "function" + }, + { + inputs: [ + { internalType: "address", name: "asset", type: "address" }, + { internalType: "uint256", name: "baseAmount", type: "uint256" } + ], + name: "quoteCollateral", + outputs: [{ internalType: "uint256", name: "", type: "uint256" }], + stateMutability: "view", + type: "function" + }, + { + inputs: [{ internalType: "address", name: "", type: "address" }], + name: "userBasic", + outputs: [ + { internalType: "int104", name: "principal", type: "int104" }, + { internalType: "uint64", name: "baseTrackingIndex", type: "uint64" }, + { internalType: "uint64", name: "baseTrackingAccrued", type: "uint64" }, + { internalType: "uint16", name: "assetsIn", type: "uint16" }, + { internalType: "uint8", name: "_reserved", type: "uint8" } + ], + stateMutability: "view", + type: "function" + }, + { + inputs: [ + { internalType: "address", name: "", type: "address" }, + { internalType: "address", name: "", type: "address" } + ], + name: "userCollateral", + outputs: [ + { internalType: "uint128", name: "balance", type: "uint128" }, + { internalType: "uint128", name: "_reserved", type: "uint128" } + ], + stateMutability: "view", + type: "function" + } + ]; + + let dsaWallet0: any; + let dsaWallet1: any; + let dsaWallet2: any; + let dsaWallet3: any; + let wallet: any; + let dsa0Signer: any; + let masterSigner: Signer; + let instaConnectorsV2: Contract; + let connector: any; + let signer: any; + let wethSigner: any; + + const comet = new ethers.Contract(market, cometABI); + + const wallets = provider.getWallets(); + const [wallet0, wallet1, wallet2, wallet3] = wallets; + + before(async () => { + await hre.network.provider.request({ + method: "hardhat_reset", + params: [ + { + forking: { + //@ts-ignore + jsonRpcUrl: hre.config.networks.hardhat.forking.url, + blockNumber: 15444500 + } + } + ] + }); + masterSigner = await getMasterSigner(); + instaConnectorsV2 = await ethers.getContractAt(abis.core.connectorsV2, addresses.core.connectorsV2); + connector = await deployAndEnableConnector({ + connectorName, + contractArtifact: ConnectV2CompoundV3__factory, + signer: masterSigner, + connectors: instaConnectorsV2 + }); + console.log("Connector address", connector.address); + + await hre.network.provider.send("hardhat_setBalance", [account, ethers.utils.parseEther("10").toHexString()]); + + await hre.network.provider.request({ + method: "hardhat_impersonateAccount", + params: [account] + }); + + signer = await ethers.getSigner(account); + + await hre.network.provider.request({ + method: "hardhat_impersonateAccount", + params: [wethWhale] + }); + wethSigner = await ethers.getSigner(wethWhale); + }); + + it("Should have contracts deployed.", async function () { + expect(!!instaConnectorsV2.address).to.be.true; + expect(!!connector.address).to.be.true; + expect(!!(await masterSigner.getAddress())).to.be.true; + }); + + describe("DSA wallet setup", function () { + it("Should build DSA v2", async function () { + dsaWallet0 = await buildDSAv2(wallet0.address); + expect(!!dsaWallet0.address).to.be.true; + dsaWallet1 = await buildDSAv2(wallet0.address); + expect(!!dsaWallet1.address).to.be.true; + dsaWallet2 = await buildDSAv2(wallet0.address); + expect(!!dsaWallet2.address).to.be.true; + dsaWallet3 = await buildDSAv2(wallet0.address); + expect(!!dsaWallet3.address).to.be.true; + wallet = await ethers.getSigner(dsaWallet0.address); + expect(!!dsaWallet1.address).to.be.true; + }); + + it("Deposit ETH into DSA wallet", async function () { + await hre.network.provider.request({ + method: "hardhat_impersonateAccount", + params: [wallet.address] + }); + + dsa0Signer = await ethers.getSigner(wallet.address); + await wallet0.sendTransaction({ + to: dsaWallet0.address, + value: ethers.utils.parseEther("10") + }); + expect(await ethers.provider.getBalance(dsaWallet0.address)).to.be.gte(ethers.utils.parseEther("10")); + await wallet0.sendTransaction({ + to: dsaWallet1.address, + value: ethers.utils.parseEther("10") + }); + expect(await ethers.provider.getBalance(dsaWallet0.address)).to.be.gte(ethers.utils.parseEther("10")); + await wallet0.sendTransaction({ + to: dsaWallet3.address, + value: ethers.utils.parseEther("10") + }); + expect(await ethers.provider.getBalance(dsaWallet0.address)).to.be.gte(ethers.utils.parseEther("10")); + }); + }); + + describe("Main", function () { + //deposit asset + it("Should supply ETH collateral in Compound V3", async function () { + const amount = ethers.utils.parseEther("5"); // 1 ETH + const spells = [ + { + connector: connectorName, + method: "deposit", + args: [market, tokens.eth.address, amount, 0, 0] + } + ]; + + const tx = await dsaWallet0.connect(wallet0).cast(...encodeSpells(spells), wallet1.address); + const receipt = await tx.wait(); + expect(await ethers.provider.getBalance(dsaWallet0.address)).to.be.lte(ethers.utils.parseEther("5")); + expect((await comet.connect(signer).userCollateral(dsaWallet0.address, tokens.weth.address)).balance).to.be.gte( + ethers.utils.parseEther("5") + ); + }); + + //deposit asset on behalf of + it("Should supply ETH collateral on behalf of dsaWallet0 in Compound V3", async function () { + const amount = ethers.utils.parseEther("1"); // 1 ETH + const spells = [ + { + connector: connectorName, + method: "depositOnBehalf", + args: [market, tokens.eth.address, dsaWallet0.address, amount, 0, 0] + } + ]; + + const tx = await dsaWallet1.connect(wallet0).cast(...encodeSpells(spells), wallet1.address); + const receipt = await tx.wait(); + expect(await ethers.provider.getBalance(dsaWallet1.address)).to.be.lte(ethers.utils.parseEther("9")); + expect((await comet.connect(wallet0).userCollateral(dsaWallet0.address, tokens.weth.address)).balance).to.be.gte( + ethers.utils.parseEther("6") + ); + }); + + it("Should borrow and payback base token from Compound", async function () { + const amount = ethers.utils.parseUnits("150", 6); + const spells = [ + { + connector: connectorName, + method: "borrow", + args: [market, base, amount, 0, 0] + }, + { + connector: connectorName, + method: "payback", + args: [market, base, ethers.utils.parseUnits("50", 6), 0, 0] + } + ]; + + const tx = await dsaWallet0.connect(wallet0).cast(...encodeSpells(spells), wallet1.address); + const receipt = await tx.wait(); + expect(await comet.connect(wallet0).borrowBalanceOf(dsaWallet0.address)).to.be.equal( + ethers.utils.parseUnits("100", 6) + ); + expect(await baseContract.connect(wallet0).balanceOf(dsaWallet0.address)).to.be.equal( + ethers.utils.parseUnits("100", 6) + ); + }); + + it("should allow manager for dsaWallet0's collateral and base", async function () { + const spells = [ + { + connector: connectorName, + method: "toggleAccountManager", + args: [market, dsaWallet2.address, true] + } + ]; + + const tx = await dsaWallet0.connect(wallet0).cast(...encodeSpells(spells), wallet1.address); + const receipt = await tx.wait(); + }); + + it("should payback base token on Compound using manager", async function () { + await baseContract.connect(signer).transfer(dsaWallet0.address, ethers.utils.parseUnits("5", 6)); + + const amount = ethers.utils.parseUnits("102", 6); + await baseContract.connect(dsa0Signer).approve(market, amount); + + const spells = [ + { + connector: connectorName, + method: "paybackFromUsingManager", + args: [market, base, dsaWallet0.address, dsaWallet0.address, ethers.constants.MaxUint256, 0, 0] + } + ]; + + const tx = await dsaWallet2.connect(wallet0).cast(...encodeSpells(spells), wallet1.address); + const receipt = await tx.wait(); + expect(await comet.connect(signer).borrowBalanceOf(dsaWallet0.address)).to.be.equal( + ethers.utils.parseUnits("0", 6) + ); + }); + + it("Should borrow to another dsa from Compound", async function () { + const amount = ethers.utils.parseUnits("100", 6); + const spells = [ + { + connector: connectorName, + method: "borrowTo", + args: [market, base, dsaWallet1.address, amount, 0, 0] + } + ]; + + const tx = await dsaWallet0.connect(wallet0).cast(...encodeSpells(spells), wallet1.address); + const receipt = await tx.wait(); + expect(new BigNumber(await comet.connect(signer).borrowBalanceOf(dsaWallet0.address)).toFixed()).to.be.equal( + ethers.utils.parseUnits("100", 6) + ); + }); + + it("Should payback on behalf of from Compound", async function () { + const spells = [ + { + connector: connectorName, + method: "paybackOnBehalf", + args: [market, base, dsaWallet0.address, ethers.constants.MaxUint256, 0, 0] + } + ]; + + const tx = await dsaWallet1.connect(wallet0).cast(...encodeSpells(spells), wallet1.address); + const receipt = await tx.wait(); + expect(await comet.connect(signer).borrowBalanceOf(dsaWallet0.address)).to.be.equal( + ethers.utils.parseUnits("0", 6) + ); + }); + + it("should withdraw some ETH collateral", async function () { + let initialBal = await ethers.provider.getBalance(dsaWallet0.address); + const amount_ = ethers.utils.parseEther("2"); + const spells = [ + { + connector: connectorName, + method: "withdraw", + args: [market, tokens.eth.address, amount_, 0, 0] + } + ]; + + const tx = await dsaWallet0.connect(wallet0).cast(...encodeSpells(spells), wallet1.address); + const receipt = await tx.wait(); + expect((await comet.connect(signer).userCollateral(dsaWallet0.address, tokens.weth.address)).balance).to.be.gte( + ethers.utils.parseEther("4") + ); + expect(await ethers.provider.getBalance(dsaWallet0.address)).to.be.gte(initialBal.add(amount_).toString()); + }); + + it("manager should be able to withdraw collateral from the position and transfer", async function () { + await wallet1.sendTransaction({ + to: tokens.weth.address, + value: ethers.utils.parseEther("10") + }); + const amount = ethers.constants.MaxUint256; + const spells = [ + { + connector: connectorName, + method: "withdrawOnBehalfAndTransfer", + args: [market, tokens.eth.address, dsaWallet0.address, dsaWallet1.address, amount, 0, 0] + } + ]; + + const tx = await dsaWallet2.connect(wallet0).cast(...encodeSpells(spells), wallet1.address); + const receipt = await tx.wait(); + expect((await comet.connect(signer).userCollateral(dsaWallet0.address, tokens.weth.address)).balance).to.be.gte( + ethers.utils.parseEther("0") + ); + expect(await wethContract.connect(wallet0).balanceOf(dsaWallet1.address)).to.be.gte(ethers.utils.parseEther("4")); + }); + + it("Should withdraw collateral to another DSA", async function () { + const spells1 = [ + { + connector: connectorName, + method: "deposit", + args: [market, tokens.eth.address, ethers.utils.parseEther("5"), 0, 0] + } + ]; + + const tx1 = await dsaWallet1.connect(wallet0).cast(...encodeSpells(spells1), wallet1.address); + let initialBal = await ethers.provider.getBalance(dsaWallet0.address); + + const amount = ethers.utils.parseEther("2"); + const spells = [ + { + connector: connectorName, + method: "withdrawTo", + args: [market, tokens.eth.address, dsaWallet0.address, amount, 0, 0] + } + ]; + + const tx = await dsaWallet1.connect(wallet0).cast(...encodeSpells(spells), wallet1.address); + const receipt = await tx.wait(); + expect(await wethContract.connect(wallet0).balanceOf(dsaWallet0.address)).to.be.gte(amount); + + expect((await comet.connect(signer).userCollateral(dsaWallet1.address, tokens.weth.address)).balance).to.be.gte( + ethers.utils.parseEther("3") + ); + }); + + it("Should withdraw collateral to another DSA", async function () { + const spells1 = [ + { + connector: connectorName, + method: "deposit", + args: [market, tokens.eth.address, ethers.utils.parseEther("3"), 0, 0] + } + ]; + + const tx1 = await dsaWallet0.connect(wallet0).cast(...encodeSpells(spells1), wallet1.address); + let initialBal = await ethers.provider.getBalance(dsaWallet0.address); + + const amount = ethers.utils.parseEther("2"); + const spells = [ + { + connector: connectorName, + method: "withdrawTo", + args: [market, tokens.eth.address, dsaWallet0.address, amount, 0, 0] + } + ]; + + const tx = await dsaWallet0.connect(wallet0).cast(...encodeSpells(spells), wallet1.address); + const receipt = await tx.wait(); + expect(await ethers.provider.getBalance(dsaWallet0.address)).to.be.gte(initialBal.add(amount)); + + expect((await comet.connect(signer).userCollateral(dsaWallet1.address, tokens.weth.address)).balance).to.be.gte( + ethers.utils.parseEther("1") + ); + }); + + it("should transfer eth from dsaWallet1 to dsaWallet0 position", async function () { + const spells = [ + { + connector: connectorName, + method: "transferAsset", + args: [market, tokens.eth.address, dsaWallet0.address, ethers.utils.parseEther("3"), 0, 0] + } + ]; + + const tx = await dsaWallet1.connect(wallet0).cast(...encodeSpells(spells), wallet1.address); + const receipt = await tx.wait(); + expect((await comet.connect(signer).userCollateral(dsaWallet1.address, tokens.weth.address)).balance).to.be.gte( + ethers.utils.parseEther("0") + ); + expect((await comet.connect(signer).userCollateral(dsaWallet0.address, tokens.weth.address)).balance).to.be.gte( + ethers.utils.parseEther("3") + ); + }); + + it("should transfer base token from dsaWallet1 to dsaWallet0 position", async function () { + await baseContract.connect(signer).transfer(dsaWallet1.address, ethers.utils.parseUnits("10", 6)); + + const spells = [ + { + connector: connectorName, + method: "deposit", + args: [market, base, ethers.constants.MaxUint256, 0, 0] + } + ]; + const tx = await dsaWallet1.connect(wallet0).cast(...encodeSpells(spells), wallet1.address); + const receipt = await tx.wait(); + let initialBal = await baseContract.connect(signer).balanceOf(dsaWallet1.address); + let spells1 = [ + { + connector: connectorName, + method: "transferAsset", + args: [market, base, dsaWallet0.address, ethers.constants.MaxUint256, 0, 0] + } + ]; + + const tx1 = await dsaWallet1.connect(wallet0).cast(...encodeSpells(spells1), wallet1.address); + const receipt1 = await tx.wait(); + expect(await comet.connect(signer).balanceOf(dsaWallet1.address)).to.be.lte(ethers.utils.parseUnits("0", 6)); + expect(await comet.connect(signer).balanceOf(dsaWallet0.address)).to.be.gte(initialBal); + }); + + it("should transfer base token using manager from dsaWallet0 to dsaWallet1 position", async function () { + const spells = [ + { + connector: connectorName, + method: "transferAssetOnBehalf", + args: [market, base, dsaWallet0.address, dsaWallet1.address, ethers.constants.MaxUint256, 0, 0] + } + ]; + let initialBal = await baseContract.connect(signer).balanceOf(dsaWallet0.address); + + const tx = await dsaWallet2.connect(wallet0).cast(...encodeSpells(spells), wallet1.address); + const receipt = await tx.wait(); + expect(await comet.connect(signer).balanceOf(dsaWallet0.address)).to.be.lte(ethers.utils.parseUnits("0", 6)); + expect(await comet.connect(signer).balanceOf(dsaWallet1.address)).to.be.gte(initialBal); + }); + + it("should deposit weth using manager", async function () { + await wethContract.connect(wethSigner).transfer(dsaWallet0.address, ethers.utils.parseEther("10")); + let initialBal = await wethContract.connect(wallet0).balanceOf(dsaWallet0.address); + + const amount = ethers.utils.parseEther("1"); + await wethContract.connect(dsa0Signer).approve(market, amount); + + const spells = [ + { + connector: connectorName, + method: "depositFromUsingManager", + args: [market, tokens.eth.address, dsaWallet0.address, dsaWallet1.address, amount, 0, 0] + } + ]; + + const tx = await dsaWallet2.connect(wallet0).cast(...encodeSpells(spells), wallet1.address); + const receipt = await tx.wait(); + expect((await comet.connect(signer).userCollateral(dsaWallet1.address, tokens.weth.address)).balance).to.be.gte( + ethers.utils.parseEther("1") + ); + expect(await wethContract.connect(wallet0).balanceOf(dsaWallet0.address)).to.be.lte(initialBal.sub(amount)); + }); + + it("should allow manager for dsaWallet0's collateral", async function () { + const spells = [ + { + connector: connectorName, + method: "toggleAccountManager", + args: [market, dsaWallet2.address, true] + } + ]; + + const tx = await dsaWallet3.connect(wallet0).cast(...encodeSpells(spells), wallet1.address); + const receipt = await tx.wait(); + }); + it("should borrow on behalf using manager", async function () { + let initialBal = await baseContract.connect(wallet0).balanceOf(dsaWallet0.address); + await wallet0.sendTransaction({ + to: dsaWallet3.address, + value: ethers.utils.parseEther("15") + }); + const spells1 = [ + { + connector: connectorName, + method: "deposit", + args: [market, tokens.eth.address, ethers.utils.parseEther("15"), 0, 0] + } + ]; + const tx1 = await dsaWallet3.connect(wallet0).cast(...encodeSpells(spells1), wallet1.address); + const amount = ethers.utils.parseUnits("500", 6); + const spells = [ + { + connector: connectorName, + method: "borrowOnBehalfAndTransfer", + args: [market, base, dsaWallet3.address, dsaWallet0.address, amount, 0, 0] + } + ]; + + const tx = await dsaWallet2.connect(wallet0).cast(...encodeSpells(spells), wallet1.address); + const receipt = await tx.wait(); + expect(new BigNumber(await comet.connect(signer).borrowBalanceOf(dsaWallet3.address)).toFixed()).to.be.equal( + ethers.utils.parseUnits("500", 6) + ); + expect(await baseContract.connect(wallet0).balanceOf(dsaWallet0.address)).to.be.equal(initialBal.add(amount)); + }); + + it("should transferAsset collateral using manager", async function () { + let bal1 = (await comet.connect(signer).userCollateral(dsaWallet1.address, tokens.weth.address)).balance; + let bal0 = (await comet.connect(signer).userCollateral(dsaWallet0.address, tokens.weth.address)).balance; + const spells = [ + { + connector: connectorName, + method: "transferAssetOnBehalf", + args: [market, tokens.eth.address, dsaWallet0.address, dsaWallet1.address, ethers.utils.parseEther("1"), 0, 0] + } + ]; + + const tx = await dsaWallet2.connect(wallet0).cast(...encodeSpells(spells), wallet1.address); + const receipt = await tx.wait(); + expect((await comet.connect(signer).userCollateral(dsaWallet1.address, tokens.weth.address)).balance).to.be.gte( + bal1.add(ethers.utils.parseEther("1")).toString() + ); + expect((await comet.connect(signer).userCollateral(dsaWallet0.address, tokens.weth.address)).balance).to.be.gte( + bal0.sub(ethers.utils.parseEther("1")).toString() + ); + }); + + //can buy only when target reserves not reached. + + // it("should buy collateral", async function () { + // //deposit 10 usdc(base token) to dsa + // await baseContract.connect(signer).transfer(dsaWallet0.address, ethers.utils.parseUnits("10", 6)); + // console.log(await baseContract.connect(signer).balanceOf(dsaWallet0.address)); + + // //dsawallet0 --> collateral 0eth, balance 9eth 10usdc + // //dsaWallet1 --> balance 2eth coll: 3eth + // const amount = ethers.utils.parseUnits("1",6); + // const bal = await baseContract.connect(signer).balanceOf(dsaWallet0.address); + // const spells = [ + // { + // connector: connectorName, + // method: "buyCollateral", + // args: [market, tokens.link.address, dsaWallet0.address, amount, bal, 0, 0] + // } + // ]; + + // const tx = await dsaWallet0.connect(wallet0).cast(...encodeSpells(spells), wallet1.address); + // const receipt = await tx.wait(); + // expect(new BigNumber(await linkContract.connect(signer).balanceOf(dsaWallet0.address)).toFixed()).to.be.gte( + // ethers.utils.parseEther("1") + // ); + + // //dsawallet0 --> collateral 0eth, balance 9eth >1link + // //dsaWallet1 --> balance 2eth coll: 3eth + // }); + }); +}); diff --git a/test/mainnet/compound/compound.test.ts b/test/mainnet/compound/compound.test.ts new file mode 100644 index 0000000..89c582f --- /dev/null +++ b/test/mainnet/compound/compound.test.ts @@ -0,0 +1,138 @@ +import { expect } from "chai"; +import hre from "hardhat"; +const { waffle, ethers } = hre; +const { provider, deployContract} = waffle + +import type { Signer, Contract } from "ethers"; + +import { deployAndEnableConnector } from "../../../scripts/tests/deployAndEnableConnector"; +import { buildDSAv2 } from "../../../scripts/tests/buildDSAv2" +import { encodeSpells } from "../../../scripts/tests/encodeSpells"; +import { getMasterSigner } from "../../../scripts/tests/getMasterSigner" +import { addresses } from "../../../scripts/tests/mainnet/addresses"; +import { abis } from "../../../scripts/constant/abis"; +import { constants } from "../../../scripts/constant/constant"; +import { ConnectV2Compound__factory } from "../../../typechain"; + +describe("Compound", function () { + const connectorName = "COMPOUND-TEST-A" + + let dsaWallet0: any; + let masterSigner: Signer; + let instaConnectorsV2: Contract; + let connector: any; + + const wallets = provider.getWallets() + const [wallet0, wallet1, wallet2, wallet3] = wallets + before(async () => { + await hre.network.provider.request({ + method: "hardhat_reset", + params: [ + { + forking: { + //@ts-ignore + jsonRpcUrl: hre.config.networks.hardhat.forking.url, + blockNumber: 13300000, + }, + }, + ], + }); + masterSigner = await getMasterSigner() + instaConnectorsV2 = await ethers.getContractAt(abis.core.connectorsV2, addresses.core.connectorsV2); + connector = await deployAndEnableConnector({ + connectorName, + contractArtifact: ConnectV2Compound__factory, + signer: masterSigner, + connectors: instaConnectorsV2 + }) + console.log("Connector address", connector.address) + }) + + it("Should have contracts deployed.", async function () { + expect(!!instaConnectorsV2.address).to.be.true; + expect(!!connector.address).to.be.true; + expect(!!(await masterSigner.getAddress())).to.be.true; + }); + + describe("DSA wallet setup", function () { + it("Should build DSA v2", async function () { + dsaWallet0 = await buildDSAv2(wallet0.address) + expect(!!dsaWallet0.address).to.be.true; + }); + + it("Deposit ETH into DSA wallet", async function () { + await wallet0.sendTransaction({ + to: dsaWallet0.address, + value: ethers.utils.parseEther("10") + }); + expect(await ethers.provider.getBalance(dsaWallet0.address)).to.be.gte(ethers.utils.parseEther("10")); + }); + }); + + describe("Main", function () { + + it("Should deposit ETH in Compound", async function () { + const amount = ethers.utils.parseEther("1") // 1 ETH + const spells = [ + { + connector: connectorName, + method: "deposit", + args: ["ETH-A", amount, 0, 0] + } + ] + + const tx = await dsaWallet0.connect(wallet0).cast(...encodeSpells(spells), wallet1.address) + const receipt = await tx.wait() + expect(await ethers.provider.getBalance(dsaWallet0.address)).to.be.lte(ethers.utils.parseEther("9")); + }); + + it("Should borrow and payback DAI from Compound", async function () { + const amount = ethers.utils.parseEther("100") // 100 DAI + const setId = "83478237" + const spells = [ + { + connector: connectorName, + method: "borrow", + args: ["DAI-A", amount, 0, setId] + }, + { + connector: connectorName, + method: "payback", + args: ["DAI-A", 0, setId, 0] + } + ] + + const tx = await dsaWallet0.connect(wallet0).cast(...encodeSpells(spells), wallet1.address) + const receipt = await tx.wait() + expect(await ethers.provider.getBalance(dsaWallet0.address)).to.be.lte(ethers.utils.parseEther("9")); + }); + + it("Should deposit all ETH in Compound", async function () { + const spells = [ + { + connector: connectorName, + method: "deposit", + args: ["ETH-A", constants.max_value, 0, 0] + } + ] + + const tx = await dsaWallet0.connect(wallet0).cast(...encodeSpells(spells), wallet1.address) + const receipt = await tx.wait() + expect(await ethers.provider.getBalance(dsaWallet0.address)).to.be.lte(ethers.utils.parseEther("0")); + }); + + it("Should withdraw all ETH from Compound", async function () { + const spells = [ + { + connector: connectorName, + method: "withdraw", + args: ["ETH-A", constants.max_value, 0, 0] + } + ] + + const tx = await dsaWallet0.connect(wallet0).cast(...encodeSpells(spells), wallet1.address) + const receipt = await tx.wait() + expect(await ethers.provider.getBalance(dsaWallet0.address)).to.be.gte(ethers.utils.parseEther("10")); + }); + }) +}) diff --git a/test/mainnet/connext/connext.test.ts b/test/mainnet/connext/connext.test.ts new file mode 100644 index 0000000..7e4c0d2 --- /dev/null +++ b/test/mainnet/connext/connext.test.ts @@ -0,0 +1,154 @@ +import { expect } from "chai"; +import hre from "hardhat"; +const { ethers, waffle } = hre; +const { provider } = waffle; + +import { deployAndEnableConnector } from "../../../scripts/tests/deployAndEnableConnector"; +import { buildDSAv2 } from "../../../scripts/tests/buildDSAv2"; +import { encodeSpells } from "../../../scripts/tests/encodeSpells"; +import { getMasterSigner } from "../../../scripts/tests/getMasterSigner"; +import { addresses } from "../../../scripts/tests/mainnet/addresses"; +import { abis } from "../../../scripts/constant/abis"; +import { ConnectV2ConnextMainnet__factory } from "../../../typechain"; +import { Signer, Contract } from "ethers"; + + +describe("Connext Connector [Mainnet]", () => { + const connectorName = "CONNEXT-TEST-A"; + + let dsaWallet0: Contract; + let masterSigner: Signer; + let instaConnectorsV2: Contract; + let connector: Contract; + let usdcContract: Contract; + let signer: any; + + const usdcAddr = "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48"; + const ethAddr = "0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE"; + const account = "0x756D64Dc5eDb56740fC617628dC832DDBCfd373c"; + + const wallets = provider.getWallets(); + const [wallet0, wallet1] = wallets; + + before(async () => { + await hre.network.provider.request({ + method: "hardhat_reset", + params: [ + { + forking: { + // @ts-ignore + jsonRpcUrl: hre.config.networks.hardhat.forking.url, + blockNumber: 82686991 + } + } + ] + }); + + masterSigner = await getMasterSigner(); + instaConnectorsV2 = await ethers.getContractAt(abis.core.connectorsV2, addresses.core.connectorsV2); + connector = await deployAndEnableConnector({ + connectorName, + contractArtifact: ConnectV2ConnextMainnet__factory, + signer: masterSigner, + connectors: instaConnectorsV2 + }); + usdcContract = await ethers.getContractAt(abis.basic.erc20, usdcAddr); + signer = await ethers.getSigner(account); + + await hre.network.provider.send("hardhat_setBalance", [account, ethers.utils.parseEther("10").toHexString()]); + + await hre.network.provider.request({ + method: "hardhat_impersonateAccount", + params: [account] + }); + + await usdcContract.connect(signer).transfer(wallet0.address, ethers.utils.parseUnits("10000", 6)); + console.log("deployed connector: ", connector.address); + }); + + it("Should have contracts deployed.", async () => { + expect(!!instaConnectorsV2.address).to.be.true; + expect(!!connector.address).to.be.true; + expect(!!(await masterSigner.getAddress())).to.be.true; + }); + + describe("DSA wallet setup", () => { + it("Should build DSA v2", async () => { + dsaWallet0 = await buildDSAv2(wallet0.getAddress()); + expect(!!dsaWallet0.address).to.be.true; + }); + + it("Deposit ETH & USDC into DSA wallet", async () => { + await wallet0.sendTransaction({ + to: dsaWallet0.address, + value: ethers.utils.parseEther("10") + }); + expect(await ethers.provider.getBalance(dsaWallet0.address)).to.be.gte(ethers.utils.parseEther("10")); + + await usdcContract.connect(wallet0).transfer(dsaWallet0.address, ethers.utils.parseUnits("10", 6)); + expect(await usdcContract.balanceOf(dsaWallet0.address)).to.be.gte(ethers.utils.parseUnits("10", 6)); + }); + }); + + describe("Main", () => { + it("should xcall with eth", async () => { + const amount = ethers.utils.parseEther("5"); + const domainId = 6648936; + const slippage = 10000; + const relayerFee = ethers.utils.parseEther("1"); + const callData = "0x"; + + const xcallParams: any = [ + domainId, + wallet1.address, + ethAddr, + wallet1.address, + amount, + slippage, + relayerFee, + callData + ]; + + const spells = [ + { + connector: connectorName, + method: "xcall", + args: [xcallParams, 0, 0] + } + ]; + + const tx = await dsaWallet0.connect(wallet0).cast(...encodeSpells(spells), wallet1.address); + const receipt = await tx.wait(); + }); + + it("should xcall with usdc", async () => { + const amount = ethers.utils.parseUnits("5", 6); + const domainId = 6648936; + const slippage = 10000; + const relayerFee = ethers.utils.parseEther("1"); + const callData = "0x"; + + const xcallParams: any = [ + domainId, + wallet1.address, + usdcAddr, + wallet1.address, + amount, + slippage, + relayerFee, + callData + ]; + + const spells = [ + { + connector: connectorName, + method: "xcall", + args: [xcallParams, 0, 0] + } + ]; + + const tx = await dsaWallet0.connect(wallet0).cast(...encodeSpells(spells), wallet1.address); + const receipt = await tx.wait(); + }); + }); +}); diff --git a/test/mainnet/crv_usd/crv_usd.test.ts b/test/mainnet/crv_usd/crv_usd.test.ts new file mode 100644 index 0000000..039217c --- /dev/null +++ b/test/mainnet/crv_usd/crv_usd.test.ts @@ -0,0 +1,445 @@ +import { expect } from "chai"; +import hre from "hardhat"; +const { waffle, ethers } = hre; +const { provider, deployContract } = waffle; + +import { Signer, Contract } from "ethers"; +import { BigNumber } from "bignumber.js"; + +import { deployAndEnableConnector } from "../../../scripts/tests/deployAndEnableConnector"; +import { buildDSAv2 } from "../../../scripts/tests/buildDSAv2"; +import { encodeSpells } from "../../../scripts/tests/encodeSpells"; +import { getMasterSigner } from "../../../scripts/tests/getMasterSigner"; +import { addresses } from "../../../scripts/tests/mainnet/addresses"; +import { tokens, dsaMaxValue } from "../../../scripts/tests/mainnet/tokens"; +import { abis } from "../../../scripts/constant/abis"; +import { constants } from "../../../scripts/constant/constant"; +import { ConnectV2CurveUSD__factory, IERC20Minimal__factory } from "../../../typechain"; + +// import ABI_Ctr from "./ABI.json" + +describe("CRV USD", function () { + const connectorName = "CRV_USD-TEST-A"; + const market = "0xc3d688B66703497DAA19211EEdff47f25384cdc3"; + const base = "0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48"; + const wst_whale = "0x78bB3aEC3d855431bd9289fD98dA13F9ebB7ef15"; + const wethWhale = "0x78bB3aEC3d855431bd9289fD98dA13F9ebB7ef15"; + + const wethContract = new ethers.Contract( + tokens.weth.address, + IERC20Minimal__factory.abi, + ethers.provider + ); + const baseContract = new ethers.Contract( + base, + IERC20Minimal__factory.abi, + ethers.provider + ); + const linkContract = new ethers.Contract( + tokens.wbtc.address, + IERC20Minimal__factory.abi, + ethers.provider + ); + const crvUSD = new ethers.Contract( + tokens.crvusd.address, + IERC20Minimal__factory.abi, + ethers.provider + ); + const sfrxEth = new ethers.Contract( + tokens.sfrxeth.address, + IERC20Minimal__factory.abi, + ethers.provider + ); + + let dsaWallet0: any; + let dsaWallet1: any; + let dsaWallet2: any; + let dsaWallet3: any; + let wallet: any; + let dsa0Signer: any; + let masterSigner: Signer; + let instaConnectorsV2: Contract; + let connector: any; + let signer: any; + let sfrxSigner: any; + + const wallets = provider.getWallets(); + const [wallet0, wallet1, wallet2, wallet3] = wallets; + + before(async () => { + await hre.network.provider.request({ + method: "hardhat_reset", + params: [ + { + forking: { + //@ts-ignore + jsonRpcUrl: hre.config.networks.hardhat.forking.url, + // blockNumber: 17811076 + } + } + ] + }); + masterSigner = await getMasterSigner(); + instaConnectorsV2 = await ethers.getContractAt(abis.core.connectorsV2, addresses.core.connectorsV2); + connector = await deployAndEnableConnector({ + connectorName, + contractArtifact: ConnectV2CurveUSD__factory, + signer: masterSigner, + connectors: instaConnectorsV2 + }); + console.log("Connector address", connector.address); + + await hre.network.provider.send("hardhat_setBalance", [wst_whale, ethers.utils.parseEther("10").toHexString()]); + + await hre.network.provider.request({ + method: "hardhat_impersonateAccount", + params: [wst_whale] + }); + + signer = await ethers.getSigner(wst_whale); + }); + + it("Should have contracts deployed.", async function () { + expect(!!instaConnectorsV2.address).to.be.true; + expect(!!connector.address).to.be.true; + expect(!!(await masterSigner.getAddress())).to.be.true; + }); + + describe("DSA wallet setup", function () { + it("Should build DSA v2", async function () { + dsaWallet0 = await buildDSAv2(wallet0.address); + expect(!!dsaWallet0.address).to.be.true; + dsaWallet1 = await buildDSAv2(wallet0.address); + expect(!!dsaWallet1.address).to.be.true; + dsaWallet2 = await buildDSAv2(wallet0.address); + expect(!!dsaWallet2.address).to.be.true; + dsaWallet3 = await buildDSAv2(wallet0.address); + expect(!!dsaWallet3.address).to.be.true; + wallet = await ethers.getSigner(dsaWallet0.address); + expect(!!dsaWallet1.address).to.be.true; + }); + + it("Deposit ETH into DSA wallet", async function () { + await hre.network.provider.request({ + method: "hardhat_impersonateAccount", + params: [wallet.address] + }); + + dsa0Signer = await ethers.getSigner(wallet.address); + await wallet0.sendTransaction({ + to: dsaWallet0.address, + value: ethers.utils.parseEther("10") + }); + expect(await ethers.provider.getBalance(dsaWallet0.address)).to.be.gte(ethers.utils.parseEther("10")); + await wallet0.sendTransaction({ + to: dsaWallet1.address, + value: ethers.utils.parseEther("10") + }); + expect(await ethers.provider.getBalance(dsaWallet0.address)).to.be.gte(ethers.utils.parseEther("10")); + await wallet0.sendTransaction({ + to: dsaWallet3.address, + value: ethers.utils.parseEther("10") + }); + expect(await ethers.provider.getBalance(dsaWallet0.address)).to.be.gte(ethers.utils.parseEther("10")); + + let txRes = await sfrxEth.connect(signer).transfer(dsaWallet0.address, ethers.utils.parseEther("10000")); + await txRes.wait(); + txRes = await sfrxEth.connect(signer).transfer(dsaWallet1.address, ethers.utils.parseEther("1000")); + await txRes.wait(); + txRes = await sfrxEth.connect(signer).transfer(dsaWallet2.address, ethers.utils.parseEther("1000")); + await txRes.wait(); + }); + }); + + describe("Main", function () { + //deposit asset + it("Create Loan", async function () { + const spells = [ + { + connector: connectorName, + method: "createLoan", + args: [tokens.sfrxeth.address, ethers.utils.parseEther('1').toString(), ethers.utils.parseEther('1000'), "10", "1", "0", "0"] + } + ]; + + const tx = await dsaWallet0.connect(wallet0).cast(...encodeSpells(spells), wallet1.address); + await tx.wait(); + + expect(await crvUSD.balanceOf(dsaWallet0.address)).to.be.eq( + ethers.utils.parseEther("1000") + ); + }); + + it("add Collateral", async function () { + const balanceBefore = await sfrxEth.balanceOf(dsaWallet0.address) + const spells = [ + { + connector: connectorName, + method: "addCollateral", + args: [tokens.sfrxeth.address, ethers.utils.parseEther('1').toString(), "1", 0, 0] + } + ]; + + const tx = await dsaWallet0.connect(wallet0).cast(...encodeSpells(spells), wallet1.address); + await tx.wait(); + + expect(await sfrxEth.balanceOf(dsaWallet0.address)).to.be.eq( + ethers.BigNumber.from(balanceBefore).sub(ethers.utils.parseEther('1')) + ); + }); + + it("remove Collateral", async function () { + const balance = await sfrxEth.balanceOf(dsaWallet0.address) + const spells = [ + { + connector: connectorName, + method: "removeCollateral", + args: [tokens.sfrxeth.address, ethers.utils.parseEther('1').toString(), "1", 0, 0] + } + ]; + + const tx = await dsaWallet0.connect(wallet0).cast(...encodeSpells(spells), wallet1.address); + await tx.wait(); + + expect(await sfrxEth.balanceOf(dsaWallet0.address)).to.be.eq( + ethers.BigNumber.from(balance).add(ethers.utils.parseEther('1')) + ); + }); + + it("borrow more", async function () { + const balance = await crvUSD.balanceOf(dsaWallet0.address) + const spells = [ + { + connector: connectorName, + method: "borrowMore", + args: [tokens.sfrxeth.address, ethers.utils.parseEther('50'), "1", 0, 0] + } + ]; + + const tx = await dsaWallet0.connect(wallet0).cast(...encodeSpells(spells), wallet1.address); + await tx.wait(); + + expect(await crvUSD.balanceOf(dsaWallet0.address)).to.be.eq( + ethers.BigNumber.from(balance).add(ethers.utils.parseEther('50')) + ); + }); + + it("addCollateralAndBorrowMore with maximum value", async function () { + const balance = await crvUSD.balanceOf(dsaWallet0.address) + const spells = [ + { + connector: connectorName, + method: "addCollateralAndBorrowMore", + args: [tokens.sfrxeth.address, ethers.utils.parseEther('2'), dsaMaxValue, 1, 0, 0] + } + ]; + + const tx = await dsaWallet0.connect(wallet0).cast(...encodeSpells(spells), wallet1.address); + await tx.wait(); + + expect(await crvUSD.balanceOf(dsaWallet0.address)).to.be.gt( + ethers.BigNumber.from(balance).add(ethers.utils.parseEther('100')) + ); + }); + + it("Revert when loan exists", async function () { + const spells = [ + { + connector: connectorName, + method: "createLoan", + args: [tokens.sfrxeth.address, ethers.utils.parseEther('1').toString(), dsaMaxValue, 10, "1", "0", "0"] + } + ]; + await expect(dsaWallet0.connect(wallet0).cast(...encodeSpells(spells), wallet1.address)).to.be.revertedWith('Loan already created'); + }); + + + it("create loan with maximum debt", async function () { + const spells = [ + { + connector: connectorName, + method: "createLoan", + args: [tokens.sfrxeth.address, ethers.utils.parseEther('1').toString(), dsaMaxValue, 10, "1", "0", "0"] + } + ]; + + const tx = await dsaWallet1.connect(wallet0).cast(...encodeSpells(spells), wallet1.address); + await tx.wait(); + + expect(await crvUSD.balanceOf(dsaWallet1.address)).to.be.gt( + ethers.utils.parseEther("1000") + ); + + console.log("maximum debt amount: ", (await crvUSD.balanceOf(dsaWallet1.address)).toString() ) + }); + + it("Repay loans", async function () { + const balance = await crvUSD.balanceOf(dsaWallet1.address) + const spells = [ + { + connector: connectorName, + method: "repay", + args: [tokens.sfrxeth.address, ethers.utils.parseEther('100').toString(), "1", "0", "0"] + } + ]; + const tx = await dsaWallet1.connect(wallet0).cast(...encodeSpells(spells), wallet1.address); + expect(await crvUSD.balanceOf(dsaWallet1.address)).to.be.eq( + ethers.BigNumber.from(balance).sub(ethers.utils.parseEther('100')) + ); + }); + + it("Repay loans with max value", async function () { + const balance = await crvUSD.balanceOf(dsaWallet1.address) + const spells = [ + { + connector: connectorName, + method: "repay", + args: [tokens.sfrxeth.address, dsaMaxValue, "1", "0", "0"] + } + ]; + await dsaWallet1.connect(wallet0).cast(...encodeSpells(spells), wallet1.address); + expect(await crvUSD.balanceOf(dsaWallet1.address)).to.be.eq(0); + }); + + it("Create Loan with maximum collateral and maximum debt", async function () { + const spells = [ + { + connector: connectorName, + method: "createLoan", + args: [tokens.sfrxeth.address, dsaMaxValue, dsaMaxValue, 10, "1", "0", "0"] + } + ]; + + const tx = await dsaWallet2.connect(wallet0).cast(...encodeSpells(spells), wallet1.address); + await tx.wait(); + + expect(await crvUSD.balanceOf(dsaWallet2.address)).to.be.gt( + ethers.utils.parseEther("1000").toString() + ); + expect(await sfrxEth.balanceOf(dsaWallet2.address)).to.be.eq( + '0' + ); + console.log("maximum debt amount after maximum collateral: ", (await crvUSD.balanceOf(dsaWallet2.address)).toString() ) + }); + + it("Create Loan with eth", async function () { + const balance = await ethers.provider.getBalance(dsaWallet0.address) + const spells = [ + { + connector: connectorName, + method: "createLoan", + args: [tokens.eth.address, ethers.utils.parseEther('2').toString(), dsaMaxValue, 10, "0", "0", "0"] + } + ]; + + const tx = await dsaWallet0.connect(wallet0).cast(...encodeSpells(spells), wallet1.address); + await tx.wait(); + + expect(await ethers.provider.getBalance(dsaWallet0.address)).to.be.eq( + ethers.BigNumber.from(balance).sub(ethers.utils.parseEther('2')) + ); + console.log("maximum debt amount after create loan with 2 eth: ", (await crvUSD.balanceOf(dsaWallet0.address)).toString() ) + }); + + it("add Collateral eth", async function () { + const balance = await ethers.provider.getBalance(dsaWallet0.address) + const spells = [ + { + connector: connectorName, + method: "addCollateral", + args: [tokens.eth.address, ethers.utils.parseEther('3').toString(), 0, 0, 0] + } + ]; + + const tx = await dsaWallet0.connect(wallet0).cast(...encodeSpells(spells), wallet1.address); + await tx.wait(); + + expect(await ethers.provider.getBalance(dsaWallet0.address)).to.be.eq( + ethers.BigNumber.from(balance).sub(ethers.utils.parseEther('3')) + ); + }); + + it("remove Collateral eth", async function () { + const balance = await ethers.provider.getBalance(dsaWallet0.address) + const spells = [ + { + connector: connectorName, + method: "removeCollateral", + args: [tokens.eth.address, ethers.utils.parseEther('1').toString(), 0, 0, 0] + } + ]; + + const tx = await dsaWallet0.connect(wallet0).cast(...encodeSpells(spells), wallet1.address); + await tx.wait(); + + expect(await ethers.provider.getBalance(dsaWallet0.address)).to.be.eq( + ethers.BigNumber.from(balance).add(ethers.utils.parseEther('1')) + ); + }); + + it("borrow more", async function () { + const balance = await crvUSD.balanceOf(dsaWallet0.address) + const spells = [ + { + connector: connectorName, + method: "borrowMore", + args: [tokens.eth.address, ethers.utils.parseEther('10'), 0, 0, 0] + } + ]; + + const tx = await dsaWallet0.connect(wallet0).cast(...encodeSpells(spells), wallet1.address); + await tx.wait(); + + expect(await crvUSD.balanceOf(dsaWallet0.address)).to.be.eq( + ethers.BigNumber.from(balance).add(ethers.utils.parseEther('10')) + ); + }); + + it("borrow more", async function () { + const balance = await crvUSD.balanceOf(dsaWallet0.address) + const spells = [ + { + connector: connectorName, + method: "borrowMore", + args: [tokens.eth.address, dsaMaxValue, 0, 0, 0] + } + ]; + + const tx = await dsaWallet0.connect(wallet0).cast(...encodeSpells(spells), wallet1.address); + await tx.wait(); + expect(await crvUSD.balanceOf(dsaWallet0.address)).to.be.gt( + ethers.BigNumber.from(balance).add(ethers.utils.parseEther('1000')) + ); + }); + + + it("Repay loans", async function () { + const balance = await crvUSD.balanceOf(dsaWallet0.address) + const spells = [ + { + connector: connectorName, + method: "repay", + args: [tokens.eth.address, ethers.utils.parseEther('100').toString(), "0", "0", "0"] + } + ]; + const tx = await dsaWallet0.connect(wallet0).cast(...encodeSpells(spells), wallet1.address); + expect(await crvUSD.balanceOf(dsaWallet0.address)).to.be.eq( + ethers.BigNumber.from(balance).sub(ethers.utils.parseEther('100')) + ); + }); + + it("Repay loans with max value", async function () { + const balance = await crvUSD.balanceOf(dsaWallet0.address) + const spells = [ + { + connector: connectorName, + method: "repay", + args: [tokens.eth.address, dsaMaxValue, "0", "0", "0"] + } + ]; + await dsaWallet0.connect(wallet0).cast(...encodeSpells(spells), wallet1.address); + console.log("crv balance after repay with max value: ",await crvUSD.balanceOf(dsaWallet0.address)) + }); + + }); +}); diff --git a/test/mainnet/dsa-spell/dsa-spell.test.ts b/test/mainnet/dsa-spell/dsa-spell.test.ts new file mode 100644 index 0000000..a717ec3 --- /dev/null +++ b/test/mainnet/dsa-spell/dsa-spell.test.ts @@ -0,0 +1,176 @@ +import hre from "hardhat"; +import axios from "axios"; +import { expect } from "chai"; +const { ethers } = hre; //check +import { deployAndEnableConnector } from "../../../scripts/tests/deployAndEnableConnector"; +import { buildDSAv2 } from "../../../scripts/tests/buildDSAv2"; +import { encodeSpells } from "../../../scripts/tests/encodeSpells"; +import { getMasterSigner } from "../../../scripts/tests/getMasterSigner"; +import { addresses } from "../../../scripts/tests/mainnet/addresses"; +import { abis } from "../../../scripts/constant/abis"; +import { ConnectV2DSASpell__factory } from "../../../typechain"; +import type { Signer, Contract } from "ethers"; +import BigNumber from "bignumber.js"; + +describe("DSA Spell", function () { + const connectorName = "dsa-spell-test"; + + let dsaWallet0: any; + let dsaWallet1: any; + let dsaWallet2: any; + let walletB: any; + let wallet0: any; + let masterSigner: Signer; + let instaConnectorsV2: Contract; + let connector: any; + + before(async () => { + await hre.network.provider.request({ + method: "hardhat_reset", + params: [ + { + forking: { + // @ts-ignore + jsonRpcUrl: hre.config.networks.hardhat.forking.url + } + } + ] + }); + [wallet0] = await ethers.getSigners(); + + masterSigner = await getMasterSigner(); + instaConnectorsV2 = await ethers.getContractAt(abis.core.connectorsV2, addresses.core.connectorsV2); + connector = await deployAndEnableConnector({ + connectorName, + contractArtifact: ConnectV2DSASpell__factory, + signer: masterSigner, + connectors: instaConnectorsV2 + }); + console.log("\tConnector address", connector.address); + }); + + it("Should have contracts deployed.", async function () { + expect(!!instaConnectorsV2.address).to.be.true; + expect(!!connector.address).to.be.true; + expect(!!(await masterSigner.getAddress())).to.be.true; + }); + + describe("DSA wallet setup", function () { + it("Should build DSA v2", async function () { + dsaWallet0 = await buildDSAv2(wallet0.address); + expect(!!dsaWallet0.address).to.be.true; + walletB = await ethers.getSigner(dsaWallet0.address); + dsaWallet1 = await buildDSAv2(dsaWallet0.address); + expect(!!dsaWallet1.address).to.be.true; + console.log(`\t${dsaWallet1.address}`); + }); + + it("Deposit eth into DSA wallet 0", async function () { + await wallet0.sendTransaction({ + to: dsaWallet0.address, + value: ethers.utils.parseEther("10") + }); + + expect(await ethers.provider.getBalance(dsaWallet0.address)).to.be.gte(ethers.utils.parseEther("10")); + }); + + it("Deposit eth into DSA wallet 1", async function () { + await wallet0.sendTransaction({ + to: dsaWallet1.address, + value: ethers.utils.parseEther("10") + }); + + expect(await ethers.provider.getBalance(dsaWallet1.address)).to.be.gte(ethers.utils.parseEther("10")); + }); + }); + + describe("Main", function () { + let ETH = "0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE"; + let USDC = "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48"; + let usdc = new ethers.Contract(USDC, abis.basic.erc20); + let aETH = "0x030bA81f1c18d280636F32af80b9AAd02Cf0854e"; + let aEth = new ethers.Contract(aETH, abis.basic.aToken); + var abi = [ + "function withdraw(address,uint256,address,uint256,uint256)", + "function deposit(address,uint256,uint256,uint256)", + "function borrow(address,uint256,uint256,uint256,uint256)" + ]; + function getCallData(spell: string, params: any) { + var iface = new ethers.utils.Interface(abi); + let data = iface.encodeFunctionData(spell, params); + return ethers.utils.hexlify(data); + } + + it("should cast spells", async function () { + async function getArg(connectors: any, spells: any, params: any) { + let datas = []; + for (let i = 0; i < connectors.length; i++) { + datas.push(getCallData(spells[i], params[i])); + } + return [dsaWallet1.address, connectors, datas]; + } + + let connectors = ["BASIC-A"]; + let methods = ["withdraw"]; + let params = [ + [ETH, ethers.utils.parseEther("2"), dsaWallet0.address, 0, 0], + ]; + let arg = await getArg(connectors, methods, params); + + const spells = [ + { + connector: connectorName, + method: "castOnDSA", + args: arg + } + ]; + const tx = await dsaWallet0.connect(wallet0).cast(...encodeSpells(spells), await wallet0.getAddress()); + const receipt = await tx.wait(); + }); + + it("should check balances after cast on DSA", async function () { + expect(await ethers.provider.getBalance(dsaWallet1.address)).to.be.lte(new BigNumber(8).multipliedBy(1e18).toString()); + expect(await ethers.provider.getBalance(dsaWallet0.address)).to.be.gte( + new BigNumber(12).multipliedBy(1e18).toString() + ); + }); + + it("should cast spell on the first successful", async function () { + async function getArg(connectors: any, spells: any, params: any) { + let datas = []; + for (let i = 0; i < connectors.length; i++) { + datas.push(getCallData(spells[i], params[i])); + } + return [connectors, datas]; + } + + let connectors = ["AAVE-V2-A", "AAVE-V1-A"]; + let methods = ["deposit", "deposit"]; + let params = [ + [ETH, ethers.utils.parseEther("10"), 0, 0], + [ETH, ethers.utils.parseEther("10"), 0, 0] + ]; + let arg = await getArg(connectors, methods, params); + const spells = [ + { + connector: connectorName, + method: "castAny", + args: arg + } + ]; + const tx = await dsaWallet0 + .connect(wallet0) + .cast(...encodeSpells(spells), await wallet0.getAddress()); + const receipt = await tx.wait(); + }); + + it("should check balances after spells on DSA", async function () { + expect(await ethers.provider.getBalance(dsaWallet0.address)).to.be.lte( + new BigNumber(2).multipliedBy(1e18).toString() + ); + expect(await aEth.connect(wallet0).balanceOf(dsaWallet0.address)).to.be.gte( + new BigNumber(10).multipliedBy(1e18).toString() + ); + }); + }); +}); diff --git a/test/mainnet/euler-import/euler-import.test.ts b/test/mainnet/euler-import/euler-import.test.ts new file mode 100644 index 0000000..b50500f --- /dev/null +++ b/test/mainnet/euler-import/euler-import.test.ts @@ -0,0 +1,268 @@ +import { expect } from "chai"; +import hre from "hardhat"; +import { abis } from "../../../scripts/constant/abis"; +import { addresses } from "../../../scripts/tests/mainnet/addresses"; +import { deployAndEnableConnector } from "../../../scripts/tests/deployAndEnableConnector"; +import { getMasterSigner } from "../../../scripts/tests/getMasterSigner"; +import { buildDSAv2 } from "../../../scripts/tests/buildDSAv2"; +import { ConnectV2EulerImport__factory, IERC20__factory } from "../../../typechain"; +import { parseEther, parseUnits } from "@ethersproject/units"; +import { encodeSpells } from "../../../scripts/tests/encodeSpells"; +const { ethers } = hre; +import type { Signer, Contract } from "ethers"; +import { BigNumber } from "bignumber.js"; +import { Address } from "@project-serum/anchor"; + +const DAI = '0x6b175474e89094c44da98b954eedeac495271d0f' +const Dai = parseUnits('50', 18) + +const WETH = '0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2' +const ACC_WETH = '0x05547D4e1A2191B91510Ea7fA8555a2788C70030' +const Weth = parseUnits('50', 18) + +const ETH = '0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE' + +const token_weth = new ethers.Contract( + WETH, + [{"constant":true,"inputs":[],"name":"name","outputs":[{"name":"","type":"string"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"guy","type":"address"},{"name":"wad","type":"uint256"}],"name":"approve","outputs":[{"name":"","type":"bool"}],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[],"name":"totalSupply","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"src","type":"address"},{"name":"dst","type":"address"},{"name":"wad","type":"uint256"}],"name":"transferFrom","outputs":[{"name":"","type":"bool"}],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[{"name":"wad","type":"uint256"}],"name":"withdraw","outputs":[],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":true,"inputs":[],"name":"decimals","outputs":[{"name":"","type":"uint8"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[{"name":"","type":"address"}],"name":"balanceOf","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":true,"inputs":[],"name":"symbol","outputs":[{"name":"","type":"string"}],"payable":false,"stateMutability":"view","type":"function"},{"constant":false,"inputs":[{"name":"dst","type":"address"},{"name":"wad","type":"uint256"}],"name":"transfer","outputs":[{"name":"","type":"bool"}],"payable":false,"stateMutability":"nonpayable","type":"function"},{"constant":false,"inputs":[],"name":"deposit","outputs":[],"payable":true,"stateMutability":"payable","type":"function"},{"constant":true,"inputs":[{"name":"","type":"address"},{"name":"","type":"address"}],"name":"allowance","outputs":[{"name":"","type":"uint256"}],"payable":false,"stateMutability":"view","type":"function"},{"payable":true,"stateMutability":"payable","type":"fallback"},{"anonymous":false,"inputs":[{"indexed":true,"name":"src","type":"address"},{"indexed":true,"name":"guy","type":"address"},{"indexed":false,"name":"wad","type":"uint256"}],"name":"Approval","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"name":"src","type":"address"},{"indexed":true,"name":"dst","type":"address"},{"indexed":false,"name":"wad","type":"uint256"}],"name":"Transfer","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"name":"dst","type":"address"},{"indexed":false,"name":"wad","type":"uint256"}],"name":"Deposit","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"name":"src","type":"address"},{"indexed":false,"name":"wad","type":"uint256"}],"name":"Withdrawal","type":"event"}], + // IERC20__factory.abi, + ethers.provider, +) + +const token_dai = new ethers.Contract( + DAI, + IERC20__factory.abi, + ethers.provider, +) + +const eTokensABI = [ + "function approve(address, uint256) public", + "function balanceOf(address account) public view returns (uint256)", + "function allowance(address, address) public returns (uint256)", + "function deposit(uint256,uint256) public", + "function balanceOfUnderlying(address) public view returns (uint256)", + "function mint(uint256,uint256) public", + "function approveSubAccount(uint256, address, uint256) public" +]; + +const dTokensABI = [ + "function balanceOf(address account) public view returns (uint256)", + "function borrow(uint256,uint256) public" +]; + +const marketsABI = [ + "function enterMarket(uint256,address) public", + "function underlyingToEToken(address) public view returns (address)", + "function underlyingToDToken(address) public view returns (address)" +] + +const eWethAddress = '0x1b808F49ADD4b8C6b5117d9681cF7312Fcf0dC1D'; +const eWethContract = new ethers.Contract(eWethAddress, eTokensABI); + +const dWethAddress = '0x62e28f054efc24b26A794F5C1249B6349454352C' +const dWethContract = new ethers.Contract(dWethAddress, dTokensABI); + +const dDaiAddress = '0x6085Bc95F506c326DCBCD7A6dd6c79FBc18d4686'; +const dDaiContract = new ethers.Contract(dDaiAddress, dTokensABI); + +const euler_mainnet = '0x27182842E098f60e3D576794A5bFFb0777E025d3' +const euler_markets = '0x3520d5a913427E6F0D6A83E07ccD4A4da316e4d3' +const marketsContract = new ethers.Contract(euler_markets, marketsABI); + + +describe("Euler", function () { + const connectorName = "EULER-IMPORT-TEST-A"; + let connector: any; + + let wallet0: Signer, wallet1:Signer; + let dsaWallet0: any; + let instaConnectorsV2: Contract; + let masterSigner: Signer; + let walletAddr: Address; + let subAcc1: Address; + let subAcc2DSA: Address; + + before(async () => { + await hre.network.provider.request({ + method: "hardhat_reset", + params: [ + { + forking: { + // @ts-ignore + jsonRpcUrl: hre.config.networks.hardhat.forking.url, + blockNumber: 15379000, + }, + }, + ], + }); + [wallet0, wallet1] = await ethers.getSigners(); + + await hre.network.provider.send("hardhat_setBalance", [ACC_WETH, ethers.utils.parseEther("10").toHexString()]); + + await hre.network.provider.request({ + method: "hardhat_impersonateAccount", + params: [ACC_WETH] + }); + + const signer_weth = await ethers.getSigner(ACC_WETH) + await token_weth.connect(signer_weth).transfer(wallet0.getAddress(), ethers.utils.parseEther("8")); + console.log("WETH transferred to wallet0"); + + await hre.network.provider.request({ + method: 'hardhat_stopImpersonatingAccount', + params: [ACC_WETH], + }) + + masterSigner = await getMasterSigner(); + instaConnectorsV2 = await ethers.getContractAt( + abis.core.connectorsV2, + addresses.core.connectorsV2 + ); + connector = await deployAndEnableConnector({ + connectorName, + contractArtifact: ConnectV2EulerImport__factory, + signer: masterSigner, + connectors: instaConnectorsV2, + }); + console.log("Connector address", connector.address); + walletAddr = (await wallet0.getAddress()).toString() + console.log("walletAddr: ", walletAddr) + subAcc1 = ethers.BigNumber.from(walletAddr).xor(1).toHexString() + console.log("subAcc1: ", subAcc1) + }); + + it("should have contracts deployed", async () => { + expect(!!instaConnectorsV2.address).to.be.true; + expect(!!connector.address).to.be.true; + expect(!!(await masterSigner.getAddress())).to.be.true; + }); + + describe("DSA wallet setup", function () { + it("Should build DSA v2", async function () { + dsaWallet0 = await buildDSAv2(wallet0.getAddress()); + expect(!!dsaWallet0.address).to.be.true; + + subAcc2DSA = ethers.BigNumber.from(dsaWallet0.address).xor(2).toHexString() + console.log("subAcc2DSA: ", subAcc2DSA) + }); + + it("Deposit ETH into DSA wallet", async function () { + await wallet0.sendTransaction({ + to: dsaWallet0.address, + value: parseEther("10"), + }); + expect(await ethers.provider.getBalance(dsaWallet0.address)).to.be.gte( + parseEther("10") + ); + }); + + describe("Create Euler position in SUBACCOUNT 0", async () => { + it("Should create Euler position of WETH(collateral) and DAI(debt)", async () => { + // approve WETH to euler + await token_weth.connect(wallet0).approve(euler_mainnet, Weth); + console.log("Approved WETH"); + + // deposit WETH in euler + await eWethContract.connect(wallet0).deposit("0", parseEther("2")); + expect(await eWethContract.connect(wallet0).balanceOfUnderlying(walletAddr)).to.be.gte(parseEther("1.9")); + console.log("Supplied WETH on Euler"); + + // enter WETH market + await marketsContract.connect(wallet0).enterMarket("0", WETH); + console.log("Entered market for WETH"); + + // borrow DAI from Euler + await dDaiContract.connect(wallet0).borrow("0", Dai); + console.log("Borrowed DAI from Euler"); + }); + + it("Should check created position of user", async () => { + expect(await token_dai.connect(wallet0).balanceOf(walletAddr)).to.be.gte( + parseUnits('50', 18) + ); + }); + }); + + describe("Create Euler self-position in SUBACCOUNT 1", async () => { + it("Should create Euler self-position of WETH(collateral) and WETH(debt)", async () => { + // approve WETH to euler + await token_weth.connect(wallet0).approve(euler_mainnet, Weth); + console.log("Approved WETH"); + + // deposit WETH in euler + await eWethContract.connect(wallet0).deposit("1", parseEther("2")); + expect(await eWethContract.connect(wallet0).balanceOfUnderlying(subAcc1)).to.be.gte(parseEther("1.9")); + console.log("Supplied WETH on Euler"); + + // enter WETH market + await marketsContract.connect(wallet0).enterMarket("1", WETH); + console.log("Entered market for WETH"); + + // mint WETH from Euler + await eWethContract.connect(wallet0).mint("1", parseEther("1")); + expect(await eWethContract.connect(wallet0).balanceOfUnderlying(subAcc1)).to.be.gte(parseEther("2.9")); + console.log("Minted WETH from Euler"); + }); + + it("Should check created position of user", async () => { + expect(await eWethContract.connect(wallet0).balanceOfUnderlying(subAcc1)).to.be.gte(parseEther("2.9")); + }); + }); + + describe("Euler position migration", async () => { + it("Approve sub-account0 eTokens for import to DSA sub-account 0", async () => { + let balance = await eWethContract.connect(wallet0).balanceOf(walletAddr) + await eWethContract.connect(wallet0).approve(dsaWallet0.address, balance); + }); + + it("Approve sub-account1 eTokens for import to DSA sub-account 2", async () => { + let balance = await eWethContract.connect(wallet0).balanceOf(subAcc1) + await eWethContract.connect(wallet0).approveSubAccount("1", dsaWallet0.address, balance); + }); + + it("Should migrate euler position of sub-account 0 to DSA sub-account 0", async () => { + const spells = [ + { + connector: "EULER-IMPORT-TEST-A", + method: "importEuler", + args: [ + walletAddr, + "0", + "0", + [[ETH],[DAI],["true"]] + ] + }, + ]; + const tx = await dsaWallet0.connect(wallet0).cast(...encodeSpells(spells), wallet0.getAddress()); + const receipt = await tx.wait(); + }); + + it("Should check migration", async () => { + expect(await eWethContract.connect(wallet0).balanceOfUnderlying(dsaWallet0.address)).to.be.gte(parseEther("2")); + expect(await dDaiContract.connect(wallet0).balanceOf(dsaWallet0.address)).to.be.gte(parseEther("50")); + }); + + it("Should migrate euler position of sub-account 1 to DSA sub-account 2", async () => { + const spells = [ + { + connector: "EULER-IMPORT-TEST-A", + method: "importEuler", + args: [ + walletAddr, + "1", + "2", + [[ETH],[ETH],["true"]] + ] + }, + ]; + const tx = await dsaWallet0.connect(wallet0).cast(...encodeSpells(spells), wallet0.getAddress()); + const receipt = await tx.wait(); + }); + + it("Should check migration", async () => { + expect(await eWethContract.connect(wallet0).balanceOfUnderlying(subAcc2DSA)).to.be.gte(parseEther("3")); + expect(await dWethContract.connect(wallet0).balanceOf(subAcc2DSA)).to.be.gte(parseEther("1")); + }); + }) +}); +}) diff --git a/test/mainnet/euler-rewards/euler-rewards.test.ts b/test/mainnet/euler-rewards/euler-rewards.test.ts new file mode 100644 index 0000000..e563419 --- /dev/null +++ b/test/mainnet/euler-rewards/euler-rewards.test.ts @@ -0,0 +1,72 @@ +import { SignerWithAddress } from "@nomiclabs/hardhat-ethers/signers"; +import { ethers } from "hardhat"; +import { ConnectV2EulerIncentives, ConnectV2EulerIncentives__factory } from "../../../typechain"; +import hre from "hardhat"; + +describe("Euler Rewards Claim Test", () => { + let signer: SignerWithAddress; + let signer_user: any; + const user = "0x9F60699cE23f1Ab86Ec3e095b477Ff79d4f409AD"; + const EUL = "0xd9Fcd98c322942075A5C3860693e9f4f03AAE07b"; + + before(async () => { + [signer] = await ethers.getSigners(); + }); + + describe("Euler Functions", () => { + let contract: ConnectV2EulerIncentives; + + before(async () => { + await hre.network.provider.request({ + method: "hardhat_reset", + params: [ + { + forking: { + // eslint-disable-next-line @typescript-eslint/ban-ts-comment + // @ts-ignore + jsonRpcUrl: hre.config.networks.hardhat.forking.url, + blockNumber: 15247044, + }, + }, + ], + }); + + const deployer = new ConnectV2EulerIncentives__factory(signer); + contract = await deployer.deploy(); + await contract.deployed(); + console.log("Contract deployed at: ", contract.address); + + await hre.network.provider.request({ + method: 'hardhat_impersonateAccount', + params: [user], + }) + + signer_user = await ethers.getSigner(user) + }); + + it("Returns the positions on Euler", async () => { + let response = await contract + .connect(signer_user) + .claim( + user, + EUL, + "459635735326895", + ["0x7289f1c53c25e201538b91f7f6582388677bd6ff2fca03082267dbb1d96258ac", + '0xc85ccc67cd68d38b0da6eec50c3c897757832450e22ca103543589fb610b79ee', + '0x7e57faa32389a498f27187bc1c8ebb993a2065f019e3840cf36d599866ea5504', + '0x38aae0a5be3e76eef383cc55c48d029acb1c6c4ed91c8385a501c1cc8c2031ca', + '0x8bfd0363720bf2e5671b2689adbf667b74e47be7a5e91d36c3bdb24e4abad4f4', + '0xc23081eb1f3001535376d454a070abd67a27de6f3db68089568cf981adeb480f', + '0x8356b1a7033ff341b75749de37aab0d3a6a9420f20c03b9e07d0f7d3d1840753', + '0x4300ab0ed87f84baa140f31113e2eee8d20dea7d7ff7848cf6806ecd8067b1c9', + '0x83248015245f51e6d42021617415097cde157288d1c1de17f4de5fcbbd97b70e', + '0x785649546b18cad99c2aea8d889689b5b2d4fabc15e6a9ea1ff2b9310beebc44', + '0x310fdf199874bf9f992c02125278b4592bec5cd5ad1ee30fac130fa2b9ffc0ae', + '0x6d0a8568a994d5b8a356cf1a933ff8f5c195ea72f0f213f38a08ff3d87b3c4da' + ], + "0" + ) + console.log(response); + }); + }); +}); diff --git a/test/mainnet/euler/euler.test.ts b/test/mainnet/euler/euler.test.ts new file mode 100644 index 0000000..7322299 --- /dev/null +++ b/test/mainnet/euler/euler.test.ts @@ -0,0 +1,538 @@ +import { expect } from "chai"; +import hre from "hardhat"; +import { abis } from "../../../scripts/constant/abis"; +import { addresses } from "../../../scripts/tests/mainnet/addresses"; +import { deployAndEnableConnector } from "../../../scripts/tests/deployAndEnableConnector"; +import { getMasterSigner } from "../../../scripts/tests/getMasterSigner"; +import { buildDSAv2 } from "../../../scripts/tests/buildDSAv2"; +import { ConnectV2Euler__factory, IERC20__factory } from "../../../typechain"; +import { parseEther, parseUnits } from "@ethersproject/units"; +import { encodeSpells } from "../../../scripts/tests/encodeSpells"; +import { tokens } from "../../../scripts/tests/mainnet/tokens"; +const { ethers } = hre; +import type { Signer, Contract } from "ethers"; + +const USDC = '0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48' +const ACC_USDC = '0xe78388b4ce79068e89bf8aa7f218ef6b9ab0e9d0' +const Usdc = parseUnits('5000', 6) + +const DAI = '0x6b175474e89094c44da98b954eedeac495271d0f' +const ACC_DAI = '0xcd6Eb888e76450eF584E8B51bB73c76ffBa21FF2' +const Dai = parseUnits('5000', 18) + +const WETH = '0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2' +const ACC_WETH = '0x05547D4e1A2191B91510Ea7fA8555a2788C70030' +const Weth = parseUnits('50', 18) + +const token_usdc = new ethers.Contract( + USDC, + IERC20__factory.abi, + ethers.provider, +) + +describe("Euler", function () { + const connectorName = "EULER-TEST-A"; + let connector: any; + + let wallet0: Signer, wallet1:Signer; + let dsaWallet0: any; + let instaConnectorsV2: Contract; + let masterSigner: Signer; + + before(async () => { + await hre.network.provider.request({ + method: "hardhat_reset", + params: [ + { + forking: { + // @ts-ignore + jsonRpcUrl: hre.config.networks.hardhat.forking.url, + blockNumber: 15078000, + }, + }, + ], + }); + [wallet0, wallet1] = await ethers.getSigners(); + masterSigner = await getMasterSigner(); + instaConnectorsV2 = await ethers.getContractAt( + abis.core.connectorsV2, + addresses.core.connectorsV2 + ); + connector = await deployAndEnableConnector({ + connectorName, + contractArtifact: ConnectV2Euler__factory, + signer: masterSigner, + connectors: instaConnectorsV2, + }); + console.log("Connector address", connector.address); + }); + + it("should have contracts deployed", async () => { + expect(!!instaConnectorsV2.address).to.be.true; + expect(!!connector.address).to.be.true; + expect(!!(await masterSigner.getAddress())).to.be.true; + }); + + describe("DSA wallet setup", function () { + it("Should build DSA v2", async function () { + dsaWallet0 = await buildDSAv2(wallet0.getAddress()); + expect(!!dsaWallet0.address).to.be.true; + }); + + it("Deposit ETH into DSA wallet", async function () { + await wallet0.sendTransaction({ + to: dsaWallet0.address, + value: parseEther("10"), + }); + expect(await ethers.provider.getBalance(dsaWallet0.address)).to.be.gte( + parseEther("10") + ); + }); + + it("Deposit USDC into DSA wallet", async function () { + + await hre.network.provider.request({ + method: 'hardhat_impersonateAccount', + params: [ACC_USDC], + }) + + const signer_usdc = await ethers.getSigner(ACC_USDC) + await token_usdc.connect(signer_usdc).transfer(wallet0.getAddress(), Usdc) + + await hre.network.provider.request({ + method: 'hardhat_stopImpersonatingAccount', + params: [ACC_USDC], + }) + + await token_usdc.connect(wallet0).transfer(dsaWallet0.address, Usdc); + + expect(await token_usdc.connect(masterSigner).balanceOf(dsaWallet0.address)).to.be.gte( + parseUnits('5000', 6) + ); + }); + + it("Deposit DAI into DSA wallet", async function () { + const token_dai = new ethers.Contract( + DAI, + IERC20__factory.abi, + ethers.provider, + ) + + await hre.network.provider.request({ + method: 'hardhat_impersonateAccount', + params: [ACC_DAI], + }) + + const signer_dai = await ethers.getSigner(ACC_DAI) + await token_dai.connect(signer_dai).transfer(wallet0.getAddress(), Dai) + + await hre.network.provider.request({ + method: 'hardhat_stopImpersonatingAccount', + params: [ACC_DAI], + }) + + await token_dai.connect(wallet0).transfer(dsaWallet0.address, Dai); + + expect(await token_dai.connect(masterSigner).balanceOf(dsaWallet0.address)).to.be.gte( + parseUnits('5000', 18) + ); + }); + + it("Deposit WETH into DSA wallet", async function () { + const token_weth = new ethers.Contract( + WETH, + IERC20__factory.abi, + ethers.provider, + ) + + await hre.network.provider.request({ + method: 'hardhat_impersonateAccount', + params: [ACC_WETH], + }) + + const signer_weth = await ethers.getSigner(ACC_WETH) + await token_weth.connect(signer_weth).transfer(wallet0.getAddress(), Weth) + + await hre.network.provider.request({ + method: 'hardhat_stopImpersonatingAccount', + params: [ACC_WETH], + }) + + await token_weth.connect(wallet0).transfer(dsaWallet0.address, Weth); + + expect(await token_weth.connect(masterSigner).balanceOf(dsaWallet0.address)).to.be.gte( + parseUnits('50', 18) + ); + }); + }); + + describe("Main", function () { + beforeEach(async function () { + const spells = [ + { + connector: connectorName, + method: "deposit", + args: ["0", tokens.usdc.address, "10000000", "true", "0", "0"], // 10 USDC + }, + ]; + + const tx = await dsaWallet0 + .connect(wallet0) + .cast(...encodeSpells(spells), wallet1.getAddress()); + + await tx.wait(); + }); + + it("Should deposit in sub-account 1", async function () { + const spells = [ + { + connector: connectorName, + method: "deposit", + args: ["1", tokens.usdc.address, "10000000", "true", "0", "0"], // 10 USDC + }, + ]; + + const tx = await dsaWallet0 + .connect(wallet0) + .cast(...encodeSpells(spells), wallet1.getAddress()); + + await tx.wait(); + expect(await ethers.provider.getBalance(dsaWallet0.address)).to.be.gte( + parseUnits('1', 18) + ); + }) + it("Should borrow DAI into DSA wallet sub-account 0", async function () { + const spells = [ + { + connector: connectorName, + method: "borrow", + args: ["0", tokens.dai.address, "1000000000000000000", "0", "0"], // 1 DAI + }, + ]; + + const tx = await dsaWallet0 + .connect(wallet0) + .cast(...encodeSpells(spells), wallet1.getAddress()); + + await tx.wait(); + expect(await ethers.provider.getBalance(dsaWallet0.address)).to.be.gte( + parseUnits('1', 18) + ); + }) + it("Should repay DAI sub-account 0", async function () { + const spells = [ + { + connector: connectorName, + method: "repay", + args: ["0", tokens.dai.address, "500000000000000000", "0", "0"], // 0.5 DAI + }, + ]; + + const tx = await dsaWallet0 + .connect(wallet0) + .cast(...encodeSpells(spells), wallet1.getAddress()); + + await tx.wait(); + expect(await ethers.provider.getBalance(dsaWallet0.address)).to.be.lte( + parseUnits('5000', 18) + ); + }) + + it("Should withdraw USDC into DSA wallet from sub-account 0", async function () { + const spells = [ + { + connector: connectorName, + method: "withdraw", + args: ["0", tokens.usdc.address, "2000000", "0", "0"], // 2 USDC + }, + ]; + + const tx = await dsaWallet0 + .connect(wallet0) + .cast(...encodeSpells(spells), wallet1.getAddress()); + + await tx.wait(); + expect(await token_usdc.connect(masterSigner).balanceOf(dsaWallet0.address)).to.be.gte( + parseUnits('2', 6) + ); + }) + + it("Should borrow ENS into DSA wallet sub-account 1", async function () { + const spells = [ + { + connector: connectorName, + method: "borrow", + args: ["1", tokens.ens.address, "100000000000000", "0", "0"], + }, + ]; + + const tx = await dsaWallet0 + .connect(wallet0) + .cast(...encodeSpells(spells), wallet1.getAddress()); + + await tx.wait(); + expect(await ethers.provider.getBalance(dsaWallet0.address)).to.be.gte( + parseUnits('1', 18) + ); + }) + it("Should withdraw USDC from sub-account 1", async function () { + const spells = [ + { + connector: connectorName, + method: "withdraw", + args: ["1", tokens.usdc.address, "20000", "0", "0"], + }, + ]; + + const tx = await dsaWallet0 + .connect(wallet0) + .cast(...encodeSpells(spells), wallet1.getAddress()); + + await tx.wait(); + }) + + it("Should eTransfer from subAccount 0 to sub account 2", async function () { + const spells = [ + { + connector: connectorName, + method: "eTransfer", + args: ["0", "2", tokens.usdc.address, "2000000", "0", "0"], + }, + ]; + + const tx = await dsaWallet0 + .connect(wallet0) + .cast(...encodeSpells(spells), wallet1.getAddress()); + + await tx.wait(); + }) + + it("Should eTransfer from subAccount 1 to sub account 2", async function () { + const spells = [ + { + connector: connectorName, + method: "eTransfer", + args: ["1", "2", tokens.usdc.address, "10000", "0", "0"], + }, + ]; + + const tx = await dsaWallet0 + .connect(wallet0) + .cast(...encodeSpells(spells), wallet1.getAddress()); + + await tx.wait(); + }) + + it("Should eTransfer from subAccount 2 to sub account 0", async function () { + const spells = [ + { + connector: connectorName, + method: "eTransfer", + args: ["2", "0", tokens.usdc.address, "10000", "0", "0"], + }, + ]; + + const tx = await dsaWallet0 + .connect(wallet0) + .cast(...encodeSpells(spells), wallet1.getAddress()); + + await tx.wait(); + }) + + it("Should deposit in sub-account 2", async function () { + const spells = [ + { + connector: connectorName, + method: "deposit", + args: ["2", tokens.usdc.address, "10000000", "true", "0", "0"], + }, + ]; + + const tx = await dsaWallet0 + .connect(wallet0) + .cast(...encodeSpells(spells), wallet1.getAddress()); + + await tx.wait(); + expect(await ethers.provider.getBalance(dsaWallet0.address)).to.be.gte( + parseUnits('1', 18) + ); + }) + + it("Should dTransfer from subAccount 0 to sub account 2", async function () { + const spells = [ + { + connector: connectorName, + method: "dTransfer", + args: ["0", "2", tokens.dai.address, "50000000000000", "0", "0"], + }, + ]; + + const tx = await dsaWallet0 + .connect(wallet0) + .cast(...encodeSpells(spells), wallet1.getAddress()); + + await tx.wait(); + }) + + it("Should dTransfer from subAccount 1 to sub account 2", async function () { + + const spells = [ + { + connector: connectorName, + method: "dTransfer", + args: ["1", "2", tokens.ens.address, "100000", "0", "0"], + }, + ]; + + const tx = await dsaWallet0 + .connect(wallet0) + .cast(...encodeSpells(spells), wallet1.getAddress()); + + await tx.wait(); + }) + + it("Should dTransfer from sub account 2 to subAccount 0", async function () { + + const spells = [ + { + connector: connectorName, + method: "dTransfer", + args: ["2", "0", tokens.dai.address, "5000000000000", "0", "0"], + }, + ]; + + const tx = await dsaWallet0 + .connect(wallet0) + .cast(...encodeSpells(spells), wallet1.getAddress()); + + await tx.wait(); + }) + + it("Should give debt transfer allowance", async function () { + const spell = [ + { + connector: connectorName, + method: "approveSpenderDebt", + args: ["0", "0x85c2ac24a8BD9Ff6E2Ef6cf76C198E36550f41D7", tokens.dai.address, "10000000", "0"], + }, + ]; + + const txn = await dsaWallet0 + .connect(wallet0) + .cast(...encodeSpells(spell), wallet1.getAddress()); + + await txn.wait(); + }) + + it("Should enter the market", async function () { + const spell = [ + { + connector: connectorName, + method: "enterMarket", + args: ["0", [tokens.weth.address]], + }, + ]; + + const txn = await dsaWallet0 + .connect(wallet0) + .cast(...encodeSpells(spell), wallet1.getAddress()); + + await txn.wait(); + }); + + it("Should exit the market", async function () { + const spell = [ + { + connector: connectorName, + method: "exitMarket", + args: ["0", tokens.weth.address], + }, + ]; + + const txn = await dsaWallet0 + .connect(wallet0) + .cast(...encodeSpells(spell), wallet1.getAddress()); + + await txn.wait(); + }); + + it("Should mint in sub-account 3", async function () { + + const spells = [ + { + connector: connectorName, + method: "deposit", + args: ["3", tokens.dai.address, "1000000000000000000", "true", "0", "0"], + }, + ]; + + const tx = await dsaWallet0 + .connect(wallet0) + .cast(...encodeSpells(spells), wallet1.getAddress()); + + await tx.wait(); + + const spell = [ + { + connector: connectorName, + method: "mint", + args: ["3", tokens.weth.address, "100000000", "0", "0"], + }, + ]; + + const txn = await dsaWallet0 + .connect(wallet0) + .cast(...encodeSpells(spell), wallet1.getAddress()); + + await txn.wait(); + }) + + it("Should burn in sub account 3", async function () { + const spell = [ + { + connector: connectorName, + method: "burn", + args: ["3", tokens.weth.address, "115792089237316195423570985008687907853269984665640564039457584007913129639935", "0", "0"], + }, + ]; + + const txn = await dsaWallet0 + .connect(wallet0) + .cast(...encodeSpells(spell), wallet1.getAddress()); + + await txn.wait(); + }) + + it("Should deposit in sub account 4", async function () { + + const spells = [ + { + connector: connectorName, + method: "deposit", + args: ["4", tokens.weth.address, "1000000000000000000", "true", "0", "0"], + }, + ]; + + const tx = await dsaWallet0 + .connect(wallet0) + .cast(...encodeSpells(spells), wallet1.getAddress()); + + await tx.wait(); + }) + it("Should withdraw from sub account 4", async function () { + const spell = [ + { + connector: connectorName, + method: "withdraw", + args: ["4", tokens.weth.address, "115792089237316195423570985008687907853269984665640564039457584007913129639935", "0", "0"], + }, + ]; + + const txn = await dsaWallet0 + .connect(wallet0) + .cast(...encodeSpells(spell), wallet1.getAddress()); + + await txn.wait(); + }) + }); +}); diff --git a/test/mainnet/hop/hop.test.ts b/test/mainnet/hop/hop.test.ts new file mode 100644 index 0000000..415d382 --- /dev/null +++ b/test/mainnet/hop/hop.test.ts @@ -0,0 +1,127 @@ +import { expect } from "chai"; +import hre from "hardhat"; +const { waffle, ethers } = hre; +const { provider } = waffle; + +import { deployAndEnableConnector } from "../../../scripts/tests/deployAndEnableConnector"; +import { buildDSAv2 } from "../../../scripts/tests/buildDSAv2"; +import { encodeSpells } from "../../../scripts/tests/encodeSpells"; +import { getMasterSigner } from "../../../scripts/tests/getMasterSigner"; +import { addLiquidity } from "../../../scripts/tests/addLiquidity"; +import { addresses } from "../../../scripts/tests/mainnet/addresses"; +import { abis } from "../../../scripts/constant/abis"; +import { ConnectV2Hop__factory } from "../../../typechain"; +import { Signer, Contract } from "ethers"; + +describe("Hop Connector", function () { + const connectorName = "HOP-MAINNET-X"; + + let dsaWallet0: Contract; + let masterSigner: Signer; + let instaConnectorsV2: Contract; + let connector: Contract; + + const wallets = provider.getWallets(); + const [wallet0, wallet1, wallet2, wallet3] = wallets; + + const DAI_ADDR = "0x6B175474E89094C44Da98b954EedeAC495271d0F"; + const l2AmmWrapper = "0x3d4Cc8A61c7528Fd86C55cfe061a78dCBA48EDd1"; + + const token = new ethers.Contract(DAI_ADDR, abis.basic.erc20); + + before(async () => { + await hre.network.provider.request({ + method: "hardhat_reset", + params: [ + { + forking: { + // @ts-ignore + jsonRpcUrl: hre.config.networks.hardhat.forking.url, + blockNumber: 14854895 + } + } + ] + }); + + masterSigner = await getMasterSigner(); + instaConnectorsV2 = await ethers.getContractAt(abis.core.connectorsV2, addresses.core.connectorsV2); + connector = await deployAndEnableConnector({ + connectorName, + contractArtifact: ConnectV2Hop__factory, + signer: masterSigner, + connectors: instaConnectorsV2 + }); + console.log("Connector address", connector.address); + }); + + it("Should have contracts deployed.", async function () { + expect(!!instaConnectorsV2.address).to.be.true; + expect(!!connector.address).to.be.true; + expect(!!(await masterSigner.getAddress())).to.be.true; + }); + + describe("DSA wallet setup", function () { + it("Should build DSA v2", async function () { + dsaWallet0 = await buildDSAv2(wallet0.address); + expect(!!dsaWallet0.address).to.be.true; + }); + + it("Deposit ETH & DAI into DSA wallet", async function () { + await wallet0.sendTransaction({ + to: dsaWallet0.address, + value: ethers.utils.parseEther("10") + }); + expect(await ethers.provider.getBalance(dsaWallet0.address)).to.be.gte(ethers.utils.parseEther("10")); + + await addLiquidity("dai", dsaWallet0.address, ethers.utils.parseEther("10000")); + }); + }); + + describe("Main", function () { + it("should migrate DAI from L1 to L2", async function () { + const amount = ethers.utils.parseEther("10"); + const deadline = Date.now() + 604800; + const getId = "0"; + + const params: any = [DAI_ADDR, wallet0.address, l2AmmWrapper, 137, amount.toString(), "0", deadline]; + + const spells = [ + { + connector: connectorName, + method: "bridge", + args: [params, getId] + } + ]; + + const tx = await dsaWallet0.connect(wallet0).cast(...encodeSpells(spells), wallet1.address); + let receipt = await tx.wait(); + }); + + it("should migrate ETH from L1 to L2", async function () { + const amount = ethers.utils.parseEther("10"); + const deadline = Date.now() + 604800; + const getId = "0"; + + const params: any = [ + "0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE", + wallet0.address, + "0xb8901acB165ed027E32754E0FFe830802919727f", + 137, + amount.toString(), + "0", + deadline + ]; + + const spells = [ + { + connector: connectorName, + method: "bridge", + args: [params, getId] + } + ]; + + const tx = await dsaWallet0.connect(wallet0).cast(...encodeSpells(spells), wallet1.address); + let receipt = await tx.wait(); + }); + }); +}); diff --git a/test/mainnet/instaLite/instaLite.test.ts b/test/mainnet/instaLite/instaLite.test.ts new file mode 100644 index 0000000..a08847e --- /dev/null +++ b/test/mainnet/instaLite/instaLite.test.ts @@ -0,0 +1,101 @@ +import hre from "hardhat"; +import { expect } from "chai"; +const { ethers } = hre; //check +import { BigNumber } from "bignumber.js"; +import { deployAndEnableConnector } from "../../../scripts/tests/deployAndEnableConnector"; +import { buildDSAv2 } from "../../../scripts/tests/buildDSAv2"; +import { encodeSpells } from "../../../scripts/tests/encodeSpells"; +import { getMasterSigner } from "../../../scripts/tests/getMasterSigner"; +import { addresses } from "../../../scripts/tests/mainnet/addresses"; +import { addLiquidity } from "../../../scripts/tests/addLiquidity"; +import { abis } from "../../../scripts/constant/abis"; +import { ConnectV2InstaLiteVault1__factory } from "../../../typechain"; +// import lido_abi from "./abi.json"; +import type { Signer, Contract } from "ethers"; +import { parseEther } from "ethers/lib/utils"; + +describe("instaLite", function () { + const connectorName = "instaLite-test"; + + let dsaWallet0: Contract; + let wallet0: Signer, wallet1: Signer; + let masterSigner: Signer; + let instaConnectorsV2: Contract; + let connector: Contract; + + before(async () => { + // await hre.network.provider.request({ + // method: "hardhat_reset", + // params: [ + // { + // forking: { + // // @ts-ignore + // jsonRpcUrl: hre.config.networks.hardhat.forking.url, + // blockNumber: 14334859 + // }, + // }, + // ], + // }); + [wallet0, wallet1] = await ethers.getSigners(); + masterSigner = await getMasterSigner(); + instaConnectorsV2 = await ethers.getContractAt(abis.core.connectorsV2, addresses.core.connectorsV2); + connector = await deployAndEnableConnector({ + connectorName, + contractArtifact: ConnectV2InstaLiteVault1__factory, + signer: masterSigner, + connectors: instaConnectorsV2 + }); + console.log("Connector address", connector.address); + }); + + it("Should have contracts deployed.", async function () { + expect(!!instaConnectorsV2.address).to.be.true; + expect(!!connector.address).to.be.true; + expect(!!(await masterSigner.getAddress())).to.be.true; + }); + + describe("DSA wallet setup", function () { + it("Should build DSA v2", async function () { + dsaWallet0 = await buildDSAv2(await wallet0.getAddress()); + expect(!!dsaWallet0.address).to.be.true; + }); + + it("Deposit ETH into DSA wallet", async function () { + await wallet0.sendTransaction({ + to: dsaWallet0.address, + value: ethers.utils.parseEther("10") + }); + expect(await ethers.provider.getBalance(dsaWallet0.address)).to.be.gte(ethers.utils.parseEther("10")); + }); + }); + + describe("Main", function () { + it("should deposit the eth", async function () { + const _amt = ethers.utils.parseEther("5"); + const ethAddr = "0xeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee"; + const spells = [ + { + connector: connectorName, + method: "supply", + args: ["0xc383a3833a87009fd9597f8184979af5edfad019", ethAddr, _amt, 0, [0, 0]] + } + ]; + const tx = await dsaWallet0.connect(wallet0).cast(...encodeSpells(spells), await wallet1.getAddress()); + const receipt = await tx.wait(); + expect(await ethers.provider.getBalance(dsaWallet0.address)).to.eq(parseEther("5")); + }); + + it("should withdraw", async function () { + const _amt = ethers.utils.parseEther("1"); + const spells = [ + { + connector: connectorName, + method: "withdraw", + args: ["0xc383a3833a87009fd9597f8184979af5edfad019", _amt, 0, [0, 0]] + } + ]; + const tx = await dsaWallet0.connect(wallet0).cast(...encodeSpells(spells), await wallet1.getAddress()); + const receipt = await tx.wait(); + }); + }); +}); diff --git a/test/mainnet/instapool/instapool.test.ts b/test/mainnet/instapool/instapool.test.ts new file mode 100644 index 0000000..59f8d13 --- /dev/null +++ b/test/mainnet/instapool/instapool.test.ts @@ -0,0 +1,119 @@ +import { expect } from "chai"; +import hre from "hardhat"; +const { web3, deployments, waffle, ethers } = hre; +const { provider, deployContract } = waffle + +import { deployAndEnableConnector } from "../../../scripts/tests/deployAndEnableConnector" +import { buildDSAv2 } from "../../../scripts/tests/buildDSAv2" +import { encodeSpells } from "../../../scripts/tests/encodeSpells" +import encodeFlashcastData from "../../../scripts/tests/encodeFlashcastData" +import { getMasterSigner } from "../../../scripts/tests/getMasterSigner" + +import { addresses } from "../../../scripts/tests/mainnet/addresses"; +import { abis } from "../../../scripts/constant/abis"; +import type { Signer, Contract } from "ethers"; +import { ConnectV2Compound__factory } from "../../../typechain"; + +describe("Instapool", function () { + const connectorName = "COMPOUND-TEST-A" + + let dsaWallet0: any; + let masterSigner: Signer; + let instaConnectorsV2: Contract; + let connector: any; + + const wallets = provider.getWallets() + const [wallet0, wallet1, wallet2, wallet3] = wallets + before(async () => { + await hre.network.provider.request({ + method: "hardhat_reset", + params: [ + { + forking: { + // @ts-ignore + jsonRpcUrl: hre.config.networks.hardhat.forking.url, + blockNumber: 13300000, + }, + }, + ], + }); + masterSigner = await getMasterSigner() + instaConnectorsV2 = await ethers.getContractAt(abis.core.connectorsV2, addresses.core.connectorsV2); + connector = await deployAndEnableConnector({ + connectorName, + contractArtifact: ConnectV2Compound__factory, + signer: masterSigner, + connectors: instaConnectorsV2 + }) + console.log("Connector address", connector.address) + }) + + it("Should have contracts deployed.", async function () { + expect(!!instaConnectorsV2.address).to.be.true; + expect(!!connector.address).to.be.true; + expect(!!(await masterSigner.getAddress())).to.be.true; + }); + + describe("DSA wallet setup", function () { + it("Should build DSA v2", async function () { + dsaWallet0 = await buildDSAv2(wallet0.address) + expect(!!dsaWallet0.address).to.be.true; + }); + + it("Deposit ETH into DSA wallet", async function () { + await wallet0.sendTransaction({ + to: dsaWallet0.address, + value: ethers.utils.parseEther("10") + }); + expect(await ethers.provider.getBalance(dsaWallet0.address)).to.be.gte(ethers.utils.parseEther("10")); + }); + }); + + describe("Main", function () { + + it("Should take 100 ETH flashloan from Instapool", async function () { + const amount = ethers.utils.parseEther("1") // 1 ETH + const flashloanAmount = ethers.utils.parseEther("100") // 100 ETH + const ethAddress = "0xeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee" + + const IdOne = "2878734423" + const IdTwo = "783243246" + + const spells = [ + { + connector: connectorName, + method: "deposit", + args: ["ETH-A", flashloanAmount, 0, IdOne] + }, + { + connector: connectorName, + method: "withdraw", + args: ["ETH-A", amount, IdOne, IdTwo] + }, + { + connector: "INSTAPOOL-A", + method: "flashPayback", + args: [ethAddress, flashloanAmount, IdTwo, 0], + } + ] + + const calldata = encodeFlashcastData(spells); + + const spells2 = [ + { + connector: "INSTAPOOL-A", + method: "flashBorrowAndCast", + args: [ + "0xeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee", + flashloanAmount, + 0, // route + calldata, + ], + } + ] + + const tx = await dsaWallet0.connect(wallet0).cast(...encodeSpells(spells2), wallet1.address) + const receipt = await tx.wait() + }); + }) +}) diff --git a/test/mainnet/kyberswap/kyberswap.test.ts b/test/mainnet/kyberswap/kyberswap.test.ts new file mode 100644 index 0000000..62e1bb0 --- /dev/null +++ b/test/mainnet/kyberswap/kyberswap.test.ts @@ -0,0 +1,167 @@ +import hre from "hardhat"; +import { expect } from "chai"; +import { abis } from "../../../scripts/constant/abis"; +import { addresses } from "../../../scripts/tests/mainnet/addresses"; +import { deployAndEnableConnector } from "../../../scripts/tests/deployAndEnableConnector"; +import { getMasterSigner } from "../../../scripts/tests/getMasterSigner"; +import { buildDSAv2 } from "../../../scripts/tests/buildDSAv2"; +import { + ConnectV2KyberAggregator__factory, +} from "../../../typechain" +import { parseEther } from "@ethersproject/units"; +import { encodeSpells } from "../../../scripts/tests/encodeSpells"; +import { tokens } from "../../../scripts/tests/mainnet/tokens"; +import { constants } from "../../../scripts/constant/constant"; +import { addLiquidity } from "../../../scripts/tests/addLiquidity"; +import BigNumber from "bignumber.js"; +import axios from "axios"; +const { web3, deployments, waffle, ethers } = hre; +const { provider, deployContract } = waffle; +describe("Kyberswap", function() { + const connectorName = "kyberswap-test"; + let dsaWallet0: any; + let masterSigner: any; + let instaConnectorsV2: any; + let connector: any; + const wallets = provider.getWallets(); + const [wallet0, wallet1, wallet2, wallet3] = wallets; + before(async () => { + await hre.network.provider.request({ + method: "hardhat_reset", + params: [ + { + forking: { + // @ts-ignore + jsonRpcUrl: hre.config.networks.hardhat.forking.url, + // blockNumber: 13300000, + }, + }, + ], + }); + masterSigner = await getMasterSigner(); + const erc20 = abis.basic.erc20; + instaConnectorsV2 = await ethers.getContractAt( + abis.core.connectorsV2, + addresses.core.connectorsV2 + ); + + connector = await deployAndEnableConnector({ + connectorName, + contractArtifact: ConnectV2KyberAggregator__factory, + signer: masterSigner, + connectors: instaConnectorsV2, + }); + // console.log("Connector address", connector.address); + }); + it("Should have contracts deployed.", async function() { + expect(!!instaConnectorsV2.address).to.be.true; + expect(!!connector.address).to.be.true; + expect(!!masterSigner.address).to.be.true; + }); + describe("DSA wallet setup", function() { + it("Should build DSA v2", async function() { + dsaWallet0 = await buildDSAv2(wallet0.address); + expect(!!dsaWallet0.address).to.be.true; + }); + it("Deposit ETH into DSA wallet", async function() { + await wallet0.sendTransaction({ + to: dsaWallet0.address, + value: ethers.utils.parseEther("10"), + }); + + expect(await ethers.provider.getBalance(dsaWallet0.address)).to.be.gte( + ethers.utils.parseEther("10") + ); + }); + }); + describe("Main", async function() { + it("should swap successfully", async function() { + const latestBlock = await hre.ethers.provider.getBlock("latest"); + console.log('latestBlock: ', latestBlock.number) + async function getArg() { + const slippage = 1; + /* eth -> USDT */ + const sellTokenAddress = "0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE"; // eth, decimals 18 + const sellTokenDecimals = 18; + const buyTokenAddress = "0xdac17f958d2ee523a2206206994597c13d831ec7"; // USDT, decimals 6 + const buyTokenDecimals = 6; + const amount = 2; + const srcAmount = new BigNumber(amount) + .times(new BigNumber(10).pow(sellTokenDecimals)) + .toFixed(0); + const fromAddress = dsaWallet0.address; + let url = `https://aggregator-api.kyberswap.com/ethereum/api/v1/routes`; + let params = { + tokenIn: sellTokenAddress, + tokenOut: buyTokenAddress, + amountIn: srcAmount, + }; + + const routeSummary = await axios + .get(url, { params: params }) + .then((response) => response.data.data.routeSummary); + + let txConfig = { + routeSummary: routeSummary, + sender: fromAddress, + recipient: fromAddress, + }; + + let url2 = "https://aggregator-api.kyberswap.com/ethereum/api/v1/route/build"; + const calldata = await axios + .post(url2, txConfig) + .then((response) => response.data.data); + + function caculateUnitAmt( + buyAmount: any, + sellAmount: any, + buyDecimal: any, + sellDecimal: any, + maxSlippage: any + ) { + let unitAmt: any; + unitAmt = new BigNumber(buyAmount) + .dividedBy(10 ** buyDecimal) + .dividedBy(new BigNumber(sellAmount).dividedBy(10 ** sellDecimal)); + unitAmt = unitAmt.multipliedBy((100 - maxSlippage) / 100); + unitAmt = unitAmt.multipliedBy(1e18).toFixed(0); + return unitAmt; + } + + let unitAmt = caculateUnitAmt( + routeSummary.amountOut, + srcAmount, + buyTokenDecimals, + sellTokenDecimals, + 1 + ); + console.log('unitAmt: ', unitAmt.toString()) + return [ + buyTokenAddress, + sellTokenAddress, + srcAmount, + unitAmt, + calldata.data, + 0, + ]; + } + + let arg = await getArg(); + + const spells = [ + { + connector: connectorName, + method: "swap", + args: arg, + }, + ]; + const tx = await dsaWallet0 + .connect(wallet0) + .cast(...encodeSpells(spells), wallet1.address); + const receipt = await tx.wait(); + expect(await ethers.provider.getBalance(dsaWallet0.address)).to.be.lte( + ethers.utils.parseEther("8") + ); + }); + }); +}); diff --git a/test/mainnet/lido_stETH/abi.json b/test/mainnet/lido_stETH/abi.json new file mode 100644 index 0000000..0db9440 --- /dev/null +++ b/test/mainnet/lido_stETH/abi.json @@ -0,0 +1,731 @@ +[ + { + "constant": false, + "inputs": [], + "name": "resume", + "outputs": [], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": true, + "inputs": [], + "name": "name", + "outputs": [{ "name": "", "type": "string" }], + "payable": false, + "stateMutability": "pure", + "type": "function" + }, + { + "constant": false, + "inputs": [], + "name": "stop", + "outputs": [], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": true, + "inputs": [], + "name": "hasInitialized", + "outputs": [{ "name": "", "type": "bool" }], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { "name": "_spender", "type": "address" }, + { "name": "_amount", "type": "uint256" } + ], + "name": "approve", + "outputs": [{ "name": "", "type": "bool" }], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { "name": "depositContract", "type": "address" }, + { "name": "_oracle", "type": "address" }, + { "name": "_operators", "type": "address" }, + { "name": "_treasury", "type": "address" }, + { "name": "_insuranceFund", "type": "address" } + ], + "name": "initialize", + "outputs": [], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": true, + "inputs": [], + "name": "getInsuranceFund", + "outputs": [{ "name": "", "type": "address" }], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [], + "name": "totalSupply", + "outputs": [{ "name": "", "type": "uint256" }], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [{ "name": "_ethAmount", "type": "uint256" }], + "name": "getSharesByPooledEth", + "outputs": [{ "name": "", "type": "uint256" }], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { "name": "_sender", "type": "address" }, + { "name": "_recipient", "type": "address" }, + { "name": "_amount", "type": "uint256" } + ], + "name": "transferFrom", + "outputs": [{ "name": "", "type": "bool" }], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": true, + "inputs": [], + "name": "getOperators", + "outputs": [{ "name": "", "type": "address" }], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [{ "name": "_script", "type": "bytes" }], + "name": "getEVMScriptExecutor", + "outputs": [{ "name": "", "type": "address" }], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [], + "name": "decimals", + "outputs": [{ "name": "", "type": "uint8" }], + "payable": false, + "stateMutability": "pure", + "type": "function" + }, + { + "constant": true, + "inputs": [], + "name": "getRecoveryVault", + "outputs": [{ "name": "", "type": "address" }], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [], + "name": "DEPOSIT_ROLE", + "outputs": [{ "name": "", "type": "bytes32" }], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [], + "name": "DEPOSIT_SIZE", + "outputs": [{ "name": "", "type": "uint256" }], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [], + "name": "getTotalPooledEther", + "outputs": [{ "name": "", "type": "uint256" }], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [], + "name": "PAUSE_ROLE", + "outputs": [{ "name": "", "type": "bytes32" }], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { "name": "_spender", "type": "address" }, + { "name": "_addedValue", "type": "uint256" } + ], + "name": "increaseAllowance", + "outputs": [{ "name": "", "type": "bool" }], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": true, + "inputs": [], + "name": "getTreasury", + "outputs": [{ "name": "", "type": "address" }], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [], + "name": "SET_ORACLE", + "outputs": [{ "name": "", "type": "bytes32" }], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [], + "name": "isStopped", + "outputs": [{ "name": "", "type": "bool" }], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [], + "name": "MANAGE_WITHDRAWAL_KEY", + "outputs": [{ "name": "", "type": "bytes32" }], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [], + "name": "getBufferedEther", + "outputs": [{ "name": "", "type": "uint256" }], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [], + "name": "SIGNATURE_LENGTH", + "outputs": [{ "name": "", "type": "uint256" }], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [], + "name": "getWithdrawalCredentials", + "outputs": [{ "name": "", "type": "bytes32" }], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [{ "name": "_account", "type": "address" }], + "name": "balanceOf", + "outputs": [{ "name": "", "type": "uint256" }], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [], + "name": "getFeeDistribution", + "outputs": [ + { "name": "treasuryFeeBasisPoints", "type": "uint16" }, + { "name": "insuranceFeeBasisPoints", "type": "uint16" }, + { "name": "operatorsFeeBasisPoints", "type": "uint16" } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [{ "name": "_sharesAmount", "type": "uint256" }], + "name": "getPooledEthByShares", + "outputs": [{ "name": "", "type": "uint256" }], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": false, + "inputs": [{ "name": "_oracle", "type": "address" }], + "name": "setOracle", + "outputs": [], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": true, + "inputs": [{ "name": "token", "type": "address" }], + "name": "allowRecoverability", + "outputs": [{ "name": "", "type": "bool" }], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [], + "name": "appId", + "outputs": [{ "name": "", "type": "bytes32" }], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [], + "name": "getOracle", + "outputs": [{ "name": "", "type": "address" }], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [], + "name": "getInitializationBlock", + "outputs": [{ "name": "", "type": "uint256" }], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { "name": "_treasuryFeeBasisPoints", "type": "uint16" }, + { "name": "_insuranceFeeBasisPoints", "type": "uint16" }, + { "name": "_operatorsFeeBasisPoints", "type": "uint16" } + ], + "name": "setFeeDistribution", + "outputs": [], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": false, + "inputs": [{ "name": "_feeBasisPoints", "type": "uint16" }], + "name": "setFee", + "outputs": [], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": false, + "inputs": [{ "name": "_maxDeposits", "type": "uint256" }], + "name": "depositBufferedEther", + "outputs": [], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": true, + "inputs": [], + "name": "symbol", + "outputs": [{ "name": "", "type": "string" }], + "payable": false, + "stateMutability": "pure", + "type": "function" + }, + { + "constant": true, + "inputs": [], + "name": "MANAGE_FEE", + "outputs": [{ "name": "", "type": "bytes32" }], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": false, + "inputs": [{ "name": "_token", "type": "address" }], + "name": "transferToVault", + "outputs": [], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": true, + "inputs": [], + "name": "SET_TREASURY", + "outputs": [{ "name": "", "type": "bytes32" }], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [ + { "name": "_sender", "type": "address" }, + { "name": "_role", "type": "bytes32" }, + { "name": "_params", "type": "uint256[]" } + ], + "name": "canPerform", + "outputs": [{ "name": "", "type": "bool" }], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": false, + "inputs": [{ "name": "_referral", "type": "address" }], + "name": "submit", + "outputs": [{ "name": "", "type": "uint256" }], + "payable": true, + "stateMutability": "payable", + "type": "function" + }, + { + "constant": true, + "inputs": [], + "name": "WITHDRAWAL_CREDENTIALS_LENGTH", + "outputs": [{ "name": "", "type": "uint256" }], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { "name": "_spender", "type": "address" }, + { "name": "_subtractedValue", "type": "uint256" } + ], + "name": "decreaseAllowance", + "outputs": [{ "name": "", "type": "bool" }], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": true, + "inputs": [], + "name": "getEVMScriptRegistry", + "outputs": [{ "name": "", "type": "address" }], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [], + "name": "PUBKEY_LENGTH", + "outputs": [{ "name": "", "type": "uint256" }], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { "name": "_amount", "type": "uint256" }, + { "name": "_pubkeyHash", "type": "bytes32" } + ], + "name": "withdraw", + "outputs": [], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { "name": "_recipient", "type": "address" }, + { "name": "_amount", "type": "uint256" } + ], + "name": "transfer", + "outputs": [{ "name": "", "type": "bool" }], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": true, + "inputs": [], + "name": "getDepositContract", + "outputs": [{ "name": "", "type": "address" }], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [], + "name": "getBeaconStat", + "outputs": [ + { "name": "depositedValidators", "type": "uint256" }, + { "name": "beaconValidators", "type": "uint256" }, + { "name": "beaconBalance", "type": "uint256" } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [], + "name": "BURN_ROLE", + "outputs": [{ "name": "", "type": "bytes32" }], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": false, + "inputs": [{ "name": "_insuranceFund", "type": "address" }], + "name": "setInsuranceFund", + "outputs": [], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": true, + "inputs": [], + "name": "getFee", + "outputs": [{ "name": "feeBasisPoints", "type": "uint16" }], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [], + "name": "SET_INSURANCE_FUND", + "outputs": [{ "name": "", "type": "bytes32" }], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [], + "name": "kernel", + "outputs": [{ "name": "", "type": "address" }], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [], + "name": "getTotalShares", + "outputs": [{ "name": "", "type": "uint256" }], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [ + { "name": "_owner", "type": "address" }, + { "name": "_spender", "type": "address" } + ], + "name": "allowance", + "outputs": [{ "name": "", "type": "uint256" }], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": true, + "inputs": [], + "name": "isPetrified", + "outputs": [{ "name": "", "type": "bool" }], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { + "constant": false, + "inputs": [{ "name": "_withdrawalCredentials", "type": "bytes32" }], + "name": "setWithdrawalCredentials", + "outputs": [], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": false, + "inputs": [], + "name": "depositBufferedEther", + "outputs": [], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { "name": "_account", "type": "address" }, + { "name": "_sharesAmount", "type": "uint256" } + ], + "name": "burnShares", + "outputs": [{ "name": "newTotalShares", "type": "uint256" }], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": false, + "inputs": [{ "name": "_treasury", "type": "address" }], + "name": "setTreasury", + "outputs": [], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { "name": "_beaconValidators", "type": "uint256" }, + { "name": "_beaconBalance", "type": "uint256" } + ], + "name": "pushBeacon", + "outputs": [], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": true, + "inputs": [{ "name": "_account", "type": "address" }], + "name": "sharesOf", + "outputs": [{ "name": "", "type": "uint256" }], + "payable": false, + "stateMutability": "view", + "type": "function" + }, + { "payable": true, "stateMutability": "payable", "type": "fallback" }, + { + "anonymous": false, + "inputs": [ + { "indexed": true, "name": "executor", "type": "address" }, + { "indexed": false, "name": "script", "type": "bytes" }, + { "indexed": false, "name": "input", "type": "bytes" }, + { "indexed": false, "name": "returnData", "type": "bytes" } + ], + "name": "ScriptResult", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { "indexed": true, "name": "vault", "type": "address" }, + { "indexed": true, "name": "token", "type": "address" }, + { "indexed": false, "name": "amount", "type": "uint256" } + ], + "name": "RecoverToVault", + "type": "event" + }, + { "anonymous": false, "inputs": [], "name": "Stopped", "type": "event" }, + { "anonymous": false, "inputs": [], "name": "Resumed", "type": "event" }, + { + "anonymous": false, + "inputs": [ + { "indexed": true, "name": "from", "type": "address" }, + { "indexed": true, "name": "to", "type": "address" }, + { "indexed": false, "name": "value", "type": "uint256" } + ], + "name": "Transfer", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { "indexed": true, "name": "owner", "type": "address" }, + { "indexed": true, "name": "spender", "type": "address" }, + { "indexed": false, "name": "value", "type": "uint256" } + ], + "name": "Approval", + "type": "event" + }, + { + "anonymous": false, + "inputs": [{ "indexed": false, "name": "feeBasisPoints", "type": "uint16" }], + "name": "FeeSet", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { "indexed": false, "name": "treasuryFeeBasisPoints", "type": "uint16" }, + { "indexed": false, "name": "insuranceFeeBasisPoints", "type": "uint16" }, + { "indexed": false, "name": "operatorsFeeBasisPoints", "type": "uint16" } + ], + "name": "FeeDistributionSet", + "type": "event" + }, + { + "anonymous": false, + "inputs": [{ "indexed": false, "name": "withdrawalCredentials", "type": "bytes32" }], + "name": "WithdrawalCredentialsSet", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { "indexed": true, "name": "sender", "type": "address" }, + { "indexed": false, "name": "amount", "type": "uint256" }, + { "indexed": false, "name": "referral", "type": "address" } + ], + "name": "Submitted", + "type": "event" + }, + { + "anonymous": false, + "inputs": [{ "indexed": false, "name": "amount", "type": "uint256" }], + "name": "Unbuffered", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { "indexed": true, "name": "sender", "type": "address" }, + { "indexed": false, "name": "tokenAmount", "type": "uint256" }, + { "indexed": false, "name": "sentFromBuffer", "type": "uint256" }, + { "indexed": true, "name": "pubkeyHash", "type": "bytes32" }, + { "indexed": false, "name": "etherAmount", "type": "uint256" } + ], + "name": "Withdrawal", + "type": "event" + } +] diff --git a/test/mainnet/lido_stETH/lidoStEth.test.ts b/test/mainnet/lido_stETH/lidoStEth.test.ts new file mode 100644 index 0000000..9cd318c --- /dev/null +++ b/test/mainnet/lido_stETH/lidoStEth.test.ts @@ -0,0 +1,103 @@ +import hre from "hardhat"; +import axios from "axios"; +import { expect } from "chai"; +const { ethers } = hre; //check +import { BigNumber } from "bignumber.js"; +import { deployAndEnableConnector } from "../../../scripts/tests/deployAndEnableConnector"; +import { buildDSAv2 } from "../../../scripts/tests/buildDSAv2"; +import { encodeSpells } from "../../../scripts/tests/encodeSpells"; +import { getMasterSigner } from "../../../scripts/tests/getMasterSigner"; +import { addresses } from "../../../scripts/tests/mainnet/addresses"; +import { abis } from "../../../scripts/constant/abis"; +import { ConnectV2LidoStEth__factory } from "../../../typechain"; +import lido_abi from "./abi.json"; +import type { Signer, Contract } from "ethers"; + +describe("LidoStEth", function() { + const connectorName = "LidoStEth-test"; + + let dsaWallet0: Contract; + let wallet0: Signer, wallet1: Signer; + let masterSigner: Signer; + let instaConnectorsV2: Contract; + let connector: Contract; + + before(async () => { + // await hre.network.provider.request({ + // method: "hardhat_reset", + // params: [ + // { + // forking: { + // // @ts-ignore + // jsonRpcUrl: hre.config.networks.hardhat.forking.url, + // blockNumber: 14334859 + // }, + // }, + // ], + // }); + [wallet0, wallet1] = await ethers.getSigners(); + masterSigner = await getMasterSigner(); + instaConnectorsV2 = await ethers.getContractAt( + abis.core.connectorsV2, + addresses.core.connectorsV2 + ); + connector = await deployAndEnableConnector({ + connectorName, + contractArtifact: ConnectV2LidoStEth__factory, + signer: masterSigner, + connectors: instaConnectorsV2, + }); + console.log("Connector address", connector.address); + }); + + it("Should have contracts deployed.", async function() { + expect(!!instaConnectorsV2.address).to.be.true; + expect(!!connector.address).to.be.true; + expect(!!(await masterSigner.getAddress())).to.be.true; + }); + + describe("DSA wallet setup", function() { + it("Should build DSA v2", async function() { + dsaWallet0 = await buildDSAv2(await wallet0.getAddress()); + expect(!!dsaWallet0.address).to.be.true; + }); + + it("Deposit ETH into DSA wallet", async function() { + await wallet0.sendTransaction({ + to: dsaWallet0.address, + value: ethers.utils.parseEther("10"), + }); + expect(await ethers.provider.getBalance(dsaWallet0.address)).to.be.gte( + ethers.utils.parseEther("10") + ); + }); + }); + + describe("Main", function() { + it("should stake the eth", async function() { + const _amt = ethers.utils.parseEther("1"); + const stETHToken = await ethers.getContractAt( + lido_abi, + "0xae7ab96520DE3A18E5e111B5EaAb095312D7fE84" + ); + const initialstEthBalance = await stETHToken.balanceOf(dsaWallet0.address) + const spells = [ + { + connector: connectorName, + method: "deposit", + args: [_amt,0,0] + }, + ]; + const tx = await dsaWallet0 + .connect(wallet0) + .cast(...encodeSpells(spells), await wallet1.getAddress()); + const receipt = await tx.wait(); + + const finalstEthBalance = await stETHToken.balanceOf(dsaWallet0.address) + expect(finalstEthBalance).to.be.gt(initialstEthBalance); + expect(await ethers.provider.getBalance(dsaWallet0.address)).to.be.lte( + ethers.utils.parseEther("9") + ); + }); + }); +}); diff --git a/test/mainnet/liquity/liquity.contracts.ts b/test/mainnet/liquity/liquity.contracts.ts new file mode 100644 index 0000000..7cee4a1 --- /dev/null +++ b/test/mainnet/liquity/liquity.contracts.ts @@ -0,0 +1,95 @@ +const TROVE_MANAGER_ADDRESS = "0xA39739EF8b0231DbFA0DcdA07d7e29faAbCf4bb2"; +const TROVE_MANAGER_ABI = [ + "function getTroveColl(address _borrower) external view returns (uint)", + "function getTroveDebt(address _borrower) external view returns (uint)", + "function getTroveStatus(address _borrower) external view returns (uint)", + "function redeemCollateral(uint _LUSDAmount, address _firstRedemptionHint, address _upperPartialRedemptionHint, address _lowerPartialRedemptionHint, uint _partialRedemptionHintNICR, uint _maxIterations, uint _maxFee) external returns (uint)", + "function getNominalICR(address _borrower) external view returns (uint)", + "function liquidate(address _borrower) external", + "function liquidateTroves(uint _n) external", +]; + +const BORROWER_OPERATIONS_ADDRESS = + "0x24179CD81c9e782A4096035f7eC97fB8B783e007"; +const BORROWER_OPERATIONS_ABI = [ + "function openTrove(uint256 _maxFee, uint256 _LUSDAmount, address _upperHint, address _lowerHint) external payable", + "function closeTrove() external", +]; + +const LUSD_TOKEN_ADDRESS = "0x5f98805A4E8be255a32880FDeC7F6728C6568bA0"; +const LUSD_TOKEN_ABI = [ + "function transfer(address _to, uint256 _value) public returns (bool success)", + "function balanceOf(address account) external view returns (uint256)", + "function approve(address spender, uint256 amount) external returns (bool)", +]; + +const ACTIVE_POOL_ADDRESS = "0xDf9Eb223bAFBE5c5271415C75aeCD68C21fE3D7F"; +const ACTIVE_POOL_ABI = ["function getLUSDDebt() external view returns (uint)"]; + +const PRICE_FEED_ADDRESS = "0x4c517D4e2C851CA76d7eC94B805269Df0f2201De"; +const PRICE_FEED_ABI = ["function fetchPrice() external returns (uint)"]; + +const HINT_HELPERS_ADDRESS = "0xE84251b93D9524E0d2e621Ba7dc7cb3579F997C0"; +const HINT_HELPERS_ABI = [ + "function getRedemptionHints(uint _LUSDamount, uint _price, uint _maxIterations) external view returns (address firstRedemptionHint, uint partialRedemptionHintNICR, uint truncatedLUSDamount)", + "function getApproxHint(uint _CR, uint _numTrials, uint _inputRandomSeed) view returns (address hintAddress, uint diff, uint latestRandomSeed)", + "function computeNominalCR(uint _coll, uint _debt) external pure returns (uint)", +]; + +const SORTED_TROVES_ADDRESS = "0x8FdD3fbFEb32b28fb73555518f8b361bCeA741A6"; +const SORTED_TROVES_ABI = [ + "function findInsertPosition(uint256 _ICR, address _prevId, address _nextId) external view returns (address, address)", + "function getLast() external view returns (address)", +]; + +const STABILITY_POOL_ADDRESS = "0x66017D22b0f8556afDd19FC67041899Eb65a21bb"; +const STABILITY_POOL_ABI = [ + "function getCompoundedLUSDDeposit(address _depositor) external view returns (uint)", + "function getDepositorETHGain(address _depositor) external view returns (uint)", + "function getDepositorLQTYGain(address _depositor) external view returns (uint)", +]; + +const STAKING_ADDRESS = "0x4f9Fbb3f1E99B56e0Fe2892e623Ed36A76Fc605d"; +const STAKING_ABI = [ + "function stake(uint _LQTYamount) external", + "function unstake(uint _LQTYamount) external", + "function getPendingETHGain(address _user) external view returns (uint)", + "function getPendingLUSDGain(address _user) external view returns (uint)", +]; + +const LQTY_TOKEN_ADDRESS = "0x6DEA81C8171D0bA574754EF6F8b412F2Ed88c54D"; +const LQTY_TOKEN_ABI = [ + "function balanceOf(address account) external view returns (uint256)", + "function transfer(address _to, uint256 _value) public returns (bool success)", + "function approve(address spender, uint256 amount) external returns (bool)", +]; + +const COLL_SURPLUS_ADDRESS = "0x3D32e8b97Ed5881324241Cf03b2DA5E2EBcE5521"; +const COLL_SURPLUS_ABI = [ + "function getCollateral(address _account) external view returns (uint)", +]; + +export default { + TROVE_MANAGER_ADDRESS, + TROVE_MANAGER_ABI, + BORROWER_OPERATIONS_ADDRESS, + BORROWER_OPERATIONS_ABI, + LUSD_TOKEN_ADDRESS, + LUSD_TOKEN_ABI, + STABILITY_POOL_ADDRESS, + STABILITY_POOL_ABI, + ACTIVE_POOL_ADDRESS, + ACTIVE_POOL_ABI, + PRICE_FEED_ADDRESS, + PRICE_FEED_ABI, + HINT_HELPERS_ADDRESS, + HINT_HELPERS_ABI, + SORTED_TROVES_ADDRESS, + SORTED_TROVES_ABI, + STAKING_ADDRESS, + STAKING_ABI, + LQTY_TOKEN_ADDRESS, + LQTY_TOKEN_ABI, + COLL_SURPLUS_ADDRESS, + COLL_SURPLUS_ABI, +}; diff --git a/test/mainnet/liquity/liquity.helpers.ts b/test/mainnet/liquity/liquity.helpers.ts new file mode 100644 index 0000000..97814e7 --- /dev/null +++ b/test/mainnet/liquity/liquity.helpers.ts @@ -0,0 +1,343 @@ +import hre from "hardhat"; +import { ethers } from "hardhat"; +import hardhatConfig from "../../../hardhat.config"; + +// Instadapp deployment and testing helpers +import { deployAndEnableConnector } from "../../../scripts/tests/deployAndEnableConnector" +import { buildDSAv2 } from "../../../scripts/tests/buildDSAv2" +import { encodeSpells } from "../../../scripts/tests/encodeSpells" +import { getMasterSigner } from "../../../scripts/tests/getMasterSigner" + +// Instadapp addresses/ABIs +import { addresses } from "../../../scripts/tests/mainnet/addresses"; +import { abis } from "../../../scripts/constant/abis"; + +// Instadapp Liquity Connector artifacts +import { ConnectV2Liquity__factory, ConnectV2Basic__factory } from "../../../typechain"; + +// Instadapp uses a fake address to represent native ETH +import { constants } from "../../../scripts/constant/constant"; +import type { Signer, Contract, BigNumber } from "ethers"; +const LIQUITY_CONNECTOR = "LIQUITY-v1-TEST"; +const LUSD_GAS_COMPENSATION = hre.ethers.utils.parseUnits("200", 18); // 200 LUSD gas compensation repaid after loan repayment +const LIQUIDATABLE_TROVES_BLOCK_NUMBER = 12478159; // Deterministic block number for tests to run against, if you change this, tests will break. +const JUSTIN_SUN_ADDRESS = "0x903d12bf2c57a29f32365917c706ce0e1a84cce3"; // LQTY whale address +const LIQUIDATABLE_TROVE_ADDRESS = "0xafbeb4cb97f3b08ec2fe07ef0dac15d37013a347"; // Trove which is liquidatable at blockNumber: LIQUIDATABLE_TROVES_BLOCK_NUMBER +// @ts-ignore +const MAX_GAS = hardhatConfig.networks.hardhat.blockGasLimit ?? 12000000; // Maximum gas limit (12000000) +const INSTADAPP_BASIC_V1_CONNECTOR = "Basic-v1"; +const ETH = constants.native_address + +const openTroveSpell = async ( + dsa: any, + signer: Signer, + depositAmount: any, + borrowAmount: any, + upperHint: any, + lowerHint: any, + maxFeePercentage: any +) => { + let address = await signer.getAddress(); + + const openTroveSpell = { + connector: LIQUITY_CONNECTOR, + method: "open", + args: [ + depositAmount, + maxFeePercentage, + borrowAmount, + upperHint, + lowerHint, + [0, 0], + [0, 0], + ], + }; + + return await dsa + .connect(signer) + .cast(...encodeSpells([openTroveSpell]), address, { + value: depositAmount, + }); +}; + +const createDsaTrove = async ( + dsa: any, + signer: any, + liquity: any, + depositAmount = hre.ethers.utils.parseEther("5"), + borrowAmount = hre.ethers.utils.parseUnits("2000", 18) +) => { + const maxFeePercentage = hre.ethers.utils.parseUnits("0.5", 18); // 0.5% max fee + const { upperHint, lowerHint } = await getTroveInsertionHints( + depositAmount, + borrowAmount, + liquity + ); + return await openTroveSpell( + dsa, + signer, + depositAmount, + borrowAmount, + upperHint, + lowerHint, + maxFeePercentage + ); +}; + +const sendToken = async (token: any, amount: any, from: any, to: any) => { + await hre.network.provider.request({ + method: "hardhat_impersonateAccount", + params: [from], + }); + const signer = hre.ethers.provider.getSigner(from); + + return await token.connect(signer).transfer(to, amount); +}; + +const resetInitialState = async (walletAddress: any, contracts: any, isDebug = false) => { + const liquity = await deployAndConnect(contracts, isDebug); + const dsa = await buildDSAv2(walletAddress); + + return [liquity, dsa]; +}; + +const resetHardhatBlockNumber = async (blockNumber: number) => { + return await hre.network.provider.request({ + method: "hardhat_reset", + params: [ + { + forking: { + // @ts-ignore + jsonRpcUrl: hre.config.networks.hardhat.forking.url, + blockNumber, + }, + }, + ], + }); +}; + +const deployAndConnect = async (contracts: any, isDebug = false) => { + // Pin Liquity tests to a particular block number to create deterministic state (Ether price etc.) + await resetHardhatBlockNumber(LIQUIDATABLE_TROVES_BLOCK_NUMBER); + type Liquidity = { + troveManager: Contract, + borrowerOperations: Contract, + stabilityPool: Contract, + lusdToken: Contract, + lqtyToken: Contract, + activePool: Contract, + priceFeed: Contract, + hintHelpers: Contract, + sortedTroves: Contract, + staking: Contract, + collSurplus: Contract, + }; + + const liquity = {} as Liquidity + + const masterSigner = await getMasterSigner(); + const instaConnectorsV2 = await ethers.getContractAt( + abis.core.connectorsV2, + addresses.core.connectorsV2 + ); + const connector = await deployAndEnableConnector({ + connectorName: LIQUITY_CONNECTOR, + contractArtifact: ConnectV2Liquity__factory, + signer: masterSigner, + connectors: instaConnectorsV2, + }); + isDebug && + console.log(`${LIQUITY_CONNECTOR} Connector address`, connector.address); + + const basicConnector = await deployAndEnableConnector({ + connectorName: "Basic-v1", + contractArtifact: ConnectV2Basic__factory, + signer: masterSigner, + connectors: instaConnectorsV2, + }); + isDebug && console.log("Basic-v1 Connector address", basicConnector.address); + + liquity.troveManager = new ethers.Contract( + contracts.TROVE_MANAGER_ADDRESS, + contracts.TROVE_MANAGER_ABI, + ethers.provider + ); + + liquity.borrowerOperations = new ethers.Contract( + contracts.BORROWER_OPERATIONS_ADDRESS, + contracts.BORROWER_OPERATIONS_ABI, + ethers.provider + ); + + liquity.stabilityPool = new ethers.Contract( + contracts.STABILITY_POOL_ADDRESS, + contracts.STABILITY_POOL_ABI, + ethers.provider + ); + + liquity.lusdToken = new ethers.Contract( + contracts.LUSD_TOKEN_ADDRESS, + contracts.LUSD_TOKEN_ABI, + ethers.provider + ); + + liquity.lqtyToken = new ethers.Contract( + contracts.LQTY_TOKEN_ADDRESS, + contracts.LQTY_TOKEN_ABI, + ethers.provider + ); + + liquity.activePool = new ethers.Contract( + contracts.ACTIVE_POOL_ADDRESS, + contracts.ACTIVE_POOL_ABI, + ethers.provider + ); + + liquity.priceFeed = new ethers.Contract( + contracts.PRICE_FEED_ADDRESS, + contracts.PRICE_FEED_ABI, + ethers.provider + ); + + liquity.hintHelpers = new ethers.Contract( + contracts.HINT_HELPERS_ADDRESS, + contracts.HINT_HELPERS_ABI, + ethers.provider + ); + + liquity.sortedTroves = new ethers.Contract( + contracts.SORTED_TROVES_ADDRESS, + contracts.SORTED_TROVES_ABI, + ethers.provider + ); + + liquity.staking = new ethers.Contract( + contracts.STAKING_ADDRESS, + contracts.STAKING_ABI, + ethers.provider + ); + liquity.collSurplus = new ethers.Contract( + contracts.COLL_SURPLUS_ADDRESS, + contracts.COLL_SURPLUS_ABI, + ethers.provider + ); + + return liquity; +}; + +const getTroveInsertionHints = async (depositAmount: BigNumber, borrowAmount: BigNumber, liquity: any) => { + const nominalCR = await liquity.hintHelpers.computeNominalCR( + depositAmount, + borrowAmount + ); + + const { + hintAddress, + latestRandomSeed, + } = await liquity.hintHelpers.getApproxHint(nominalCR, 50, 1298379, { + gasLimit: MAX_GAS, + }); + randomSeed = latestRandomSeed; + + const { + 0: upperHint, + 1: lowerHint, + } = await liquity.sortedTroves.findInsertPosition( + nominalCR, + hintAddress, + hintAddress, + { + gasLimit: MAX_GAS, + } + ); + + return { + upperHint, + lowerHint, + }; +}; + +let randomSeed = 4223; + +const getRedemptionHints = async (amount: any, liquity: any) => { + const ethPrice = await liquity.priceFeed.callStatic.fetchPrice(); + const [ + firstRedemptionHint, + partialRedemptionHintNicr, + ] = await liquity.hintHelpers.getRedemptionHints(amount, ethPrice, 0); + + const { + hintAddress, + latestRandomSeed, + } = await liquity.hintHelpers.getApproxHint( + partialRedemptionHintNicr, + 50, + randomSeed, + { + gasLimit: MAX_GAS, + } + ); + randomSeed = latestRandomSeed; + + const { + 0: upperHint, + 1: lowerHint, + } = await liquity.sortedTroves.findInsertPosition( + partialRedemptionHintNicr, + hintAddress, + hintAddress, + { + gasLimit: MAX_GAS, + } + ); + + return { + partialRedemptionHintNicr, + firstRedemptionHint, + upperHint, + lowerHint, + }; +}; + +const redeem = async (amount: any, from: any, wallet: { address: any; }, liquity: any) => { + await sendToken(liquity.lusdToken, amount, from, wallet.address); + const { + partialRedemptionHintNicr, + firstRedemptionHint, + upperHint, + lowerHint, + } = await getRedemptionHints(amount, liquity); + const maxFeePercentage = ethers.utils.parseUnits("0.5", 18); // 0.5% max fee + + return await liquity.troveManager + .connect(wallet) + .redeemCollateral( + amount, + firstRedemptionHint, + upperHint, + lowerHint, + partialRedemptionHintNicr, + 0, + maxFeePercentage, + { + gasLimit: MAX_GAS, // permit max gas + } + ); +}; + +export default { + deployAndConnect, + resetInitialState, + createDsaTrove, + sendToken, + getTroveInsertionHints, + getRedemptionHints, + redeem, + LIQUITY_CONNECTOR, + LUSD_GAS_COMPENSATION, + JUSTIN_SUN_ADDRESS, + LIQUIDATABLE_TROVE_ADDRESS, + MAX_GAS, + INSTADAPP_BASIC_V1_CONNECTOR, + ETH, +}; diff --git a/test/mainnet/liquity/liquity.test.ts b/test/mainnet/liquity/liquity.test.ts new file mode 100644 index 0000000..10cfe29 --- /dev/null +++ b/test/mainnet/liquity/liquity.test.ts @@ -0,0 +1,2752 @@ +import hre from "hardhat"; +import { expect } from "chai"; + +// Instadapp deployment and testing helpers +import { buildDSAv2 } from "../../../scripts/tests/buildDSAv2"; +import { encodeSpells } from "../../../scripts/tests/encodeSpells"; + +// Liquity smart contracts +import contracts from "./liquity.contracts"; + +// Liquity helpers +import helpers from "./liquity.helpers"; + +describe("Liquity", () => { + const { waffle, ethers } = hre; + const { provider } = waffle; + let userWallet: any; + // Waffle test account 0xf39fd6e51aad88f6f4ce6ab8827279cfffb92266 (holds 1000 ETH) + let dsaWallet0: any; + let liquity: any; + + before(async () => { + await hre.network.provider.request({ + method: "hardhat_reset", + params: [ + { + forking: { + // @ts-ignore + jsonRpcUrl: hre.config.networks.hardhat.forking.url, + blockNumber: 13300000, + }, + }, + ], + }); + liquity = await helpers.deployAndConnect(contracts, true); + expect(liquity.troveManager.address).to.exist; + expect(liquity.borrowerOperations.address).to.exist; + expect(liquity.stabilityPool.address).to.exist; + expect(liquity.lusdToken.address).to.exist; + expect(liquity.lqtyToken.address).to.exist; + expect(liquity.activePool.address).to.exist; + expect(liquity.priceFeed.address).to.exist; + expect(liquity.hintHelpers.address).to.exist; + expect(liquity.sortedTroves.address).to.exist; + expect(liquity.staking.address).to.exist; + }); + + beforeEach(async () => { + userWallet = provider.getWallets()[0]; + // Build a new DSA before each test so we start each test from the same default state + dsaWallet0 = await buildDSAv2(userWallet.address); + expect(dsaWallet0.address).to.exist; + }); + + describe("Main (Connector)", () => { + describe("Trove", () => { + describe("open()", () => { + it("opens a Trove", async () => { + const depositAmount = ethers.utils.parseEther("5"); // 5 ETH + const borrowAmount = ethers.utils.parseUnits("2000", 18); // 2000 LUSD + const maxFeePercentage = ethers.utils.parseUnits("0.5", 18); // 0.5% max fee + const upperHint = ethers.constants.AddressZero; + const lowerHint = ethers.constants.AddressZero; + const originalUserBalance = await ethers.provider.getBalance( + userWallet.address + ); + const originalDsaBalance = await ethers.provider.getBalance( + dsaWallet0.address + ); + + const openTroveSpell = [ + { + connector: helpers.LIQUITY_CONNECTOR, + method: "open", + args: [ + depositAmount, + maxFeePercentage, + borrowAmount, + upperHint, + lowerHint, + [0, 0], + [0, 0], + ], + }, + ]; + + await dsaWallet0 + .connect(userWallet) + .cast(...encodeSpells(openTroveSpell), userWallet.address, { + value: depositAmount, + }); + + const userBalance = await ethers.provider.getBalance( + userWallet.address + ); + const dsaEthBalance = await ethers.provider.getBalance( + dsaWallet0.address + ); + const dsaLusdBalance = await liquity.lusdToken.balanceOf( + dsaWallet0.address + ); + const troveDebt = await liquity.troveManager.getTroveDebt( + dsaWallet0.address + ); + const troveCollateral = await liquity.troveManager.getTroveColl( + dsaWallet0.address + ); + + expect(userBalance).lt( + originalUserBalance.sub(depositAmount), + "User's Ether balance should decrease" + ); + + expect(dsaEthBalance).to.eq( + originalDsaBalance, + "User's DSA account Ether should not change after borrowing" + ); + + expect( + dsaLusdBalance, + "DSA account should now hold the amount the user borrowed" + ).to.eq(borrowAmount); + + expect(troveDebt).to.gt( + borrowAmount, + "Trove debt should equal the borrowed amount plus fee" + ); + + expect(troveCollateral).to.eq( + depositAmount, + "Trove collateral should equal the deposited amount" + ); + }); + + it("opens a Trove using ETH collected from a previous spell", async () => { + const depositAmount = ethers.utils.parseEther("5"); // 5 ETH + const borrowAmount = ethers.utils.parseUnits("2000", 18); // 2000 LUSD + const maxFeePercentage = ethers.utils.parseUnits("0.5", 18); // 0.5% max fee + const upperHint = ethers.constants.AddressZero; + const lowerHint = ethers.constants.AddressZero; + const originalUserBalance = await ethers.provider.getBalance( + userWallet.address + ); + const originalDsaBalance = await ethers.provider.getBalance( + dsaWallet0.address + ); + const depositId = 1; // Choose an ID to store and retrieve the deposited ETH + + const depositEthSpell = { + connector: helpers.INSTADAPP_BASIC_V1_CONNECTOR, + method: "deposit", + args: [helpers.ETH, depositAmount, 0, depositId], + }; + + const openTroveSpell = { + connector: helpers.LIQUITY_CONNECTOR, + method: "open", + args: [ + 0, // When pulling ETH from a previous spell it doesn't matter what deposit value we put in this param + maxFeePercentage, + borrowAmount, + upperHint, + lowerHint, + [depositId, 0], + [0, 0], + ], + }; + + const spells = [depositEthSpell, openTroveSpell]; + await dsaWallet0 + .connect(userWallet) + .cast(...encodeSpells(spells), userWallet.address, { + value: depositAmount, + }); + + const userBalance = await ethers.provider.getBalance( + userWallet.address + ); + const dsaEthBalance = await ethers.provider.getBalance( + dsaWallet0.address + ); + const dsaLusdBalance = await liquity.lusdToken.balanceOf( + dsaWallet0.address + ); + const troveDebt = await liquity.troveManager.getTroveDebt( + dsaWallet0.address + ); + const troveCollateral = await liquity.troveManager.getTroveColl( + dsaWallet0.address + ); + + expect(userBalance).lt( + originalUserBalance.sub(depositAmount), + "User's Ether balance should decrease by the amount they deposited" + ); + + expect(dsaEthBalance).to.eq( + originalDsaBalance, + "DSA balance should not change" + ); + + expect( + dsaLusdBalance, + "DSA account should now hold the amount the user borrowed" + ).to.eq(borrowAmount); + + expect(troveDebt).to.gt( + borrowAmount, + "Trove debt should equal the borrowed amount plus fee" + ); + + expect(troveCollateral).to.eq( + depositAmount, + "Trove collateral should equal the deposited amount" + ); + }); + + it("opens a Trove and stores the debt for other spells to use", async () => { + const depositAmount = ethers.utils.parseEther("5"); // 5 ETH + const borrowAmount = ethers.utils.parseUnits("2000", 18); // 2000 LUSD + const maxFeePercentage = ethers.utils.parseUnits("0.5", 18); // 0.5% max fee + const upperHint = ethers.constants.AddressZero; + const lowerHint = ethers.constants.AddressZero; + const originalUserBalance = await ethers.provider.getBalance( + userWallet.address + ); + const originalDsaBalance = await ethers.provider.getBalance( + dsaWallet0.address + ); + const borrowId = 1; + + const openTroveSpell = { + connector: helpers.LIQUITY_CONNECTOR, + method: "open", + args: [ + depositAmount, + maxFeePercentage, + borrowAmount, + upperHint, + lowerHint, + [0, 0], + [borrowId, 0], + ], + }; + + const withdrawLusdSpell = { + connector: helpers.INSTADAPP_BASIC_V1_CONNECTOR, + method: "withdraw", + args: [ + contracts.LUSD_TOKEN_ADDRESS, + 0, // Amount comes from the previous spell's setId + dsaWallet0.address, + borrowId, + 0, + ], + }; + + const spells = [openTroveSpell, withdrawLusdSpell]; + await dsaWallet0 + .connect(userWallet) + .cast(...encodeSpells(spells), userWallet.address, { + value: depositAmount, + }); + + const userBalance = await ethers.provider.getBalance( + userWallet.address + ); + const dsaEthBalance = await ethers.provider.getBalance( + dsaWallet0.address + ); + const dsaLusdBalance = await liquity.lusdToken.balanceOf( + dsaWallet0.address + ); + const troveDebt = await liquity.troveManager.getTroveDebt( + dsaWallet0.address + ); + const troveCollateral = await liquity.troveManager.getTroveColl( + dsaWallet0.address + ); + + expect(userBalance).lt( + originalUserBalance.sub(depositAmount), + "User's Ether balance should decrease by the amount they deposited" + ); + + expect(dsaEthBalance).to.eq( + originalDsaBalance, + "User's DSA account Ether should not change after borrowing" + ); + + expect( + dsaLusdBalance, + "DSA account should now hold the amount the user borrowed" + ).to.eq(borrowAmount); + + expect(troveDebt).to.gt( + borrowAmount, + "Trove debt should equal the borrowed amount plus fee" + ); + + expect(troveCollateral).to.eq( + depositAmount, + "Trove collateral should equal the deposited amount" + ); + }); + + it("returns Instadapp event name and data", async () => { + const depositAmount = ethers.utils.parseEther("5"); + const borrowAmount = ethers.utils.parseUnits("2000", 18); + const maxFeePercentage = ethers.utils.parseUnits("0.5", 18); + const upperHint = ethers.constants.AddressZero; + const lowerHint = ethers.constants.AddressZero; + + const openTroveSpell = { + connector: helpers.LIQUITY_CONNECTOR, + method: "open", + args: [ + depositAmount, + maxFeePercentage, + borrowAmount, + upperHint, + lowerHint, + [0, 0], + [0, 0], + ], + }; + + const openTx = await dsaWallet0.cast( + ...encodeSpells([openTroveSpell]), + userWallet.address, + { + value: depositAmount, + } + ); + const receipt = await openTx.wait(); + const castLogEvent = receipt.events.find( + (e: { event: string }) => e.event === "LogCast" + ).args; + expect(castLogEvent.eventNames[0]).eq( + "LogOpen(address,uint256,uint256,uint256,uint256[],uint256[])" + ); + const expectedEventParams = ethers.utils.defaultAbiCoder.encode( + [ + "address", + "uint256", + "uint256", + "uint256", + "uint256[]", + "uint256[]", + ], + [ + dsaWallet0.address, + maxFeePercentage, + depositAmount, + borrowAmount, + [0, 0], + [0, 0], + ] + ); + expect(castLogEvent.eventParams[0]).eq(expectedEventParams); + }); + }); + + describe("close()", () => { + it("closes a Trove", async () => { + const depositAmount = ethers.utils.parseEther("5"); + const borrowAmount = ethers.utils.parseUnits("2000", 18); + // Create a dummy Trove + await helpers.createDsaTrove( + dsaWallet0, + userWallet, + liquity, + depositAmount, + borrowAmount + ); + + const troveDebtBefore = await liquity.troveManager.getTroveDebt( + dsaWallet0.address + ); + + const troveCollateralBefore = await liquity.troveManager.getTroveColl( + dsaWallet0.address + ); + + // Send DSA account enough LUSD (from Stability Pool) to close their Trove + const extraLusdRequiredToCloseTrove = troveDebtBefore.sub( + borrowAmount + ); + + await helpers.sendToken( + liquity.lusdToken, + extraLusdRequiredToCloseTrove, + contracts.STABILITY_POOL_ADDRESS, + dsaWallet0.address + ); + + const originalDsaLusdBalance = await liquity.lusdToken.balanceOf( + dsaWallet0.address + ); + + expect( + originalDsaLusdBalance, + "DSA account should now hold the LUSD amount required to pay off the Trove debt" + ).to.eq(troveDebtBefore); + + const closeTroveSpell = { + connector: helpers.LIQUITY_CONNECTOR, + method: "close", + args: [0], + }; + + await dsaWallet0 + .connect(userWallet) + .cast(...encodeSpells([closeTroveSpell]), userWallet.address); + + const dsaEthBalance = await ethers.provider.getBalance( + dsaWallet0.address + ); + const dsaLusdBalance = await liquity.lusdToken.balanceOf( + dsaWallet0.address + ); + const troveDebt = await liquity.troveManager.getTroveDebt( + dsaWallet0.address + ); + const troveCollateral = await liquity.troveManager.getTroveColl( + dsaWallet0.address + ); + + expect(troveDebt, "Trove debt should equal 0 after close").to.eq(0); + + expect( + troveCollateral, + "Trove collateral should equal 0 after close" + ).to.eq(0); + + expect( + dsaEthBalance, + "DSA account should now hold the Trove's ETH collateral" + ).to.eq(troveCollateralBefore); + + expect( + dsaLusdBalance, + "DSA account should now hold the gas compensation amount of LUSD as it paid off the Trove debt" + ).to.eq(helpers.LUSD_GAS_COMPENSATION); + }); + + it("closes a Trove using LUSD obtained from a previous spell", async () => { + // Create a dummy Trove + await helpers.createDsaTrove(dsaWallet0, userWallet, liquity); + + const troveDebtBefore = await liquity.troveManager.getTroveDebt( + dsaWallet0.address + ); + const troveCollateralBefore = await liquity.troveManager.getTroveColl( + dsaWallet0.address + ); + + // Send user enough LUSD to repay the loan, we'll use a deposit and withdraw spell to obtain it + await helpers.sendToken( + liquity.lusdToken, + troveDebtBefore, + contracts.STABILITY_POOL_ADDRESS, + userWallet.address + ); + + // Allow DSA to spend user's LUSD + await liquity.lusdToken + .connect(userWallet) + .approve(dsaWallet0.address, troveDebtBefore); + + // Simulate a spell which would have pulled LUSD from somewhere (e.g. Uniswap) into InstaMemory + // In this case we're simply running a deposit spell from the user's EOA + const depositLusdSpell = { + connector: helpers.INSTADAPP_BASIC_V1_CONNECTOR, + method: "deposit", + args: [contracts.LUSD_TOKEN_ADDRESS, troveDebtBefore, 0, 0], + }; + + const closeTroveSpell = { + connector: helpers.LIQUITY_CONNECTOR, + method: "close", + args: [0], + }; + const spells = [depositLusdSpell, closeTroveSpell]; + + await dsaWallet0 + .connect(userWallet) + .cast(...encodeSpells(spells), userWallet.address); + + const dsaEthBalance = await ethers.provider.getBalance( + dsaWallet0.address + ); + const troveDebt = await liquity.troveManager.getTroveDebt( + dsaWallet0.address + ); + const troveCollateral = await liquity.troveManager.getTroveColl( + dsaWallet0.address + ); + + expect(troveDebt, "Trove debt should equal 0 after close").to.eq(0); + + expect( + troveCollateral, + "Trove collateral should equal 0 after close" + ).to.eq(0); + + expect( + dsaEthBalance, + "DSA account should now hold the Trove's ETH collateral" + ).to.eq(troveCollateralBefore); + }); + + it("closes a Trove and stores the released collateral for other spells to use", async () => { + const depositAmount = ethers.utils.parseEther("5"); + const borrowAmount = ethers.utils.parseUnits("2000", 18); + // Create a dummy Trove + await helpers.createDsaTrove( + dsaWallet0, + userWallet, + liquity, + depositAmount, + borrowAmount + ); + + const troveDebtBefore = await liquity.troveManager.getTroveDebt( + dsaWallet0.address + ); + const troveCollateralBefore = await liquity.troveManager.getTroveColl( + dsaWallet0.address + ); + + // Send DSA account enough LUSD (from Stability Pool) to close their Trove + const extraLusdRequiredToCloseTrove = troveDebtBefore.sub( + borrowAmount + ); + await helpers.sendToken( + liquity.lusdToken, + extraLusdRequiredToCloseTrove, + contracts.STABILITY_POOL_ADDRESS, + dsaWallet0.address + ); + const originalDsaLusdBalance = await liquity.lusdToken.balanceOf( + dsaWallet0.address + ); + + expect( + originalDsaLusdBalance, + "DSA account should now hold the LUSD amount required to pay off the Trove debt" + ).to.eq(troveDebtBefore); + + const collateralWithdrawId = 1; + + const closeTroveSpell = { + connector: helpers.LIQUITY_CONNECTOR, + method: "close", + args: [collateralWithdrawId], + }; + + const withdrawEthSpell = { + connector: helpers.INSTADAPP_BASIC_V1_CONNECTOR, + method: "withdraw", + args: [ + helpers.ETH, + 0, // amount comes from the previous spell's setId + dsaWallet0.address, + collateralWithdrawId, + 0, + ], + }; + + await dsaWallet0 + .connect(userWallet) + .cast( + ...encodeSpells([closeTroveSpell, withdrawEthSpell]), + userWallet.address + ); + + const dsaEthBalance = await ethers.provider.getBalance( + dsaWallet0.address + ); + const dsaLusdBalance = await liquity.lusdToken.balanceOf( + dsaWallet0.address + ); + const troveDebt = await liquity.troveManager.getTroveDebt( + dsaWallet0.address + ); + const troveCollateral = await liquity.troveManager.getTroveColl( + dsaWallet0.address + ); + + expect(troveDebt, "Trove debt should equal 0 after close").to.eq(0); + + expect( + troveCollateral, + "Trove collateral should equal 0 after close" + ).to.eq(0); + + expect( + dsaEthBalance, + "DSA account should now hold the Trove's ETH collateral" + ).to.eq(troveCollateralBefore); + + expect( + dsaLusdBalance, + "DSA account should now hold the gas compensation amount of LUSD as it paid off the Trove debt" + ).to.eq(helpers.LUSD_GAS_COMPENSATION); + }); + + it("returns Instadapp event name and data", async () => { + const depositAmount = ethers.utils.parseEther("5"); + const borrowAmount = ethers.utils.parseUnits("2000", 18); + // Create a dummy Trove + await helpers.createDsaTrove( + dsaWallet0, + userWallet, + liquity, + depositAmount, + borrowAmount + ); + await helpers.sendToken( + liquity.lusdToken, + ethers.utils.parseUnits("2500", 18), + contracts.STABILITY_POOL_ADDRESS, + dsaWallet0.address + ); + + const closeTroveSpell = { + connector: helpers.LIQUITY_CONNECTOR, + method: "close", + args: [0], + }; + + const closeTx = await dsaWallet0 + .connect(userWallet) + .cast(...encodeSpells([closeTroveSpell]), userWallet.address); + + const receipt = await closeTx.wait(); + const castLogEvent = receipt.events.find( + (e: { event: string }) => e.event === "LogCast" + ).args; + const expectedEventParams = ethers.utils.defaultAbiCoder.encode( + ["address", "uint256"], + [dsaWallet0.address, 0] + ); + expect(castLogEvent.eventNames[0]).eq("LogClose(address,uint256)"); + expect(castLogEvent.eventParams[0]).eq(expectedEventParams); + }); + }); + + describe("deposit()", () => { + it("deposits ETH into a Trove", async () => { + await helpers.createDsaTrove(dsaWallet0, userWallet, liquity); + + const troveCollateralBefore = await liquity.troveManager.getTroveColl( + dsaWallet0.address + ); + + const topupAmount = ethers.utils.parseEther("1"); + const upperHint = ethers.constants.AddressZero; + const lowerHint = ethers.constants.AddressZero; + const depositEthSpell = { + connector: helpers.LIQUITY_CONNECTOR, + method: "deposit", + args: [topupAmount, upperHint, lowerHint, 0, 0], + }; + + await dsaWallet0 + .connect(userWallet) + .cast(...encodeSpells([depositEthSpell]), userWallet.address, { + value: topupAmount, + }); + + const troveCollateral = await liquity.troveManager.getTroveColl( + dsaWallet0.address + ); + + const expectedTroveCollateral = troveCollateralBefore.add( + topupAmount + ); + + expect( + troveCollateral, + `Trove collateral should have increased by ${topupAmount} ETH` + ).to.eq(expectedTroveCollateral); + }); + + it("deposits using ETH gained from a previous spell", async () => { + await helpers.createDsaTrove(dsaWallet0, userWallet, liquity); + const troveCollateralBefore = await liquity.troveManager.getTroveColl( + dsaWallet0.address + ); + + const topupAmount = ethers.utils.parseEther("1"); + const depositId = 1; + const depositEthSpell = { + connector: helpers.INSTADAPP_BASIC_V1_CONNECTOR, + method: "deposit", + args: [helpers.ETH, topupAmount, 0, depositId], + }; + + const upperHint = ethers.constants.AddressZero; + const lowerHint = ethers.constants.AddressZero; + const depositEthToTroveSpell = { + connector: helpers.LIQUITY_CONNECTOR, + method: "deposit", + args: [0, upperHint, lowerHint, depositId, 0], + }; + const spells = [depositEthSpell, depositEthToTroveSpell]; + + await dsaWallet0 + .connect(userWallet) + .cast(...encodeSpells(spells), userWallet.address, { + value: topupAmount, + }); + + const troveCollateral = await liquity.troveManager.getTroveColl( + dsaWallet0.address + ); + + const expectedTroveCollateral = troveCollateralBefore.add( + topupAmount + ); + + expect( + troveCollateral, + `Trove collateral should have increased by ${topupAmount} ETH` + ).to.eq(expectedTroveCollateral); + }); + + it("returns Instadapp event name and data", async () => { + await helpers.createDsaTrove(dsaWallet0, userWallet, liquity); + + const topupAmount = ethers.utils.parseEther("1"); + const upperHint = ethers.constants.AddressZero; + const lowerHint = ethers.constants.AddressZero; + const depositEthSpell = { + connector: helpers.LIQUITY_CONNECTOR, + method: "deposit", + args: [topupAmount, upperHint, lowerHint, 0, 0], + }; + + const depositTx = await dsaWallet0 + .connect(userWallet) + .cast(...encodeSpells([depositEthSpell]), userWallet.address, { + value: topupAmount, + }); + + const receipt = await depositTx.wait(); + const castLogEvent = receipt.events.find( + (e: { event: string }) => e.event === "LogCast" + ).args; + const expectedEventParams = ethers.utils.defaultAbiCoder.encode( + ["address", "uint256", "uint256", "uint256"], + [dsaWallet0.address, topupAmount, 0, 0] + ); + expect(castLogEvent.eventNames[0]).eq( + "LogDeposit(address,uint256,uint256,uint256)" + ); + expect(castLogEvent.eventParams[0]).eq(expectedEventParams); + }); + }); + + describe("withdraw()", () => { + it("withdraws ETH from a Trove", async () => { + await helpers.createDsaTrove(dsaWallet0, userWallet, liquity); + + const troveCollateralBefore = await liquity.troveManager.getTroveColl( + dsaWallet0.address + ); + const withdrawAmount = ethers.utils.parseEther("1"); + const upperHint = ethers.constants.AddressZero; + const lowerHint = ethers.constants.AddressZero; + const withdrawEthSpell = { + connector: helpers.LIQUITY_CONNECTOR, + method: "withdraw", + args: [withdrawAmount, upperHint, lowerHint, 0, 0], + }; + + await dsaWallet0 + .connect(userWallet) + .cast(...encodeSpells([withdrawEthSpell]), userWallet.address); + + const troveCollateral = await liquity.troveManager.getTroveColl( + dsaWallet0.address + ); + const expectedTroveCollateral = troveCollateralBefore.sub( + withdrawAmount + ); + + expect( + troveCollateral, + `Trove collateral should have decreased by ${withdrawAmount} ETH` + ).to.eq(expectedTroveCollateral); + }); + + it("withdraws ETH from a Trove and stores the ETH for other spells to use", async () => { + await helpers.createDsaTrove(dsaWallet0, userWallet, liquity); + + const troveCollateralBefore = await liquity.troveManager.getTroveColl( + dsaWallet0.address + ); + const originalUserEthBalance = await ethers.provider.getBalance( + userWallet.address + ); + + const withdrawAmount = ethers.utils.parseEther("1"); + const upperHint = ethers.constants.AddressZero; + const lowerHint = ethers.constants.AddressZero; + const withdrawId = 1; + const withdrawEthFromTroveSpell = { + connector: helpers.LIQUITY_CONNECTOR, + method: "withdraw", + args: [withdrawAmount, upperHint, lowerHint, 0, withdrawId], + }; + + const withdrawEthSpell = { + connector: helpers.INSTADAPP_BASIC_V1_CONNECTOR, + method: "withdraw", + args: [helpers.ETH, 0, userWallet.address, withdrawId, 0], + }; + const spells = [withdrawEthFromTroveSpell, withdrawEthSpell]; + await dsaWallet0 + .connect(userWallet) + .cast(...encodeSpells(spells), userWallet.address); + + const troveCollateral = await liquity.troveManager.getTroveColl( + dsaWallet0.address + ); + const expectedTroveCollateral = troveCollateralBefore.sub( + withdrawAmount + ); + const userEthBalance = await ethers.provider.getBalance( + userWallet.address + ); + + expect( + troveCollateral, + `Trove collateral should have decreased by ${withdrawAmount} ETH` + ).to.eq(expectedTroveCollateral); + + expect( + userEthBalance, + `User ETH balance should have increased by ${withdrawAmount} ETH` + ).to.lt(originalUserEthBalance.add(withdrawAmount)); + }); + + it("returns Instadapp event name and data", async () => { + await helpers.createDsaTrove(dsaWallet0, userWallet, liquity); + + const withdrawAmount = ethers.utils.parseEther("1"); + const upperHint = ethers.constants.AddressZero; + const lowerHint = ethers.constants.AddressZero; + const withdrawEthSpell = { + connector: helpers.LIQUITY_CONNECTOR, + method: "withdraw", + args: [withdrawAmount, upperHint, lowerHint, 0, 0], + }; + + const withdrawTx = await dsaWallet0 + .connect(userWallet) + .cast(...encodeSpells([withdrawEthSpell]), userWallet.address); + + const receipt = await withdrawTx.wait(); + const castLogEvent = receipt.events.find( + (e: { event: string }) => e.event === "LogCast" + ).args; + const expectedEventParams = ethers.utils.defaultAbiCoder.encode( + ["address", "uint256", "uint256", "uint256"], + [dsaWallet0.address, withdrawAmount, 0, 0] + ); + expect(castLogEvent.eventNames[0]).eq( + "LogWithdraw(address,uint256,uint256,uint256)" + ); + expect(castLogEvent.eventParams[0]).eq(expectedEventParams); + }); + }); + + describe("borrow()", () => { + it("borrows LUSD from a Trove", async () => { + // Create a dummy Trove + await helpers.createDsaTrove(dsaWallet0, userWallet, liquity); + + const troveDebtBefore = await liquity.troveManager.getTroveDebt( + dsaWallet0.address + ); + + const borrowAmount = ethers.utils.parseUnits("1000", 18); // 1000 LUSD + const upperHint = ethers.constants.AddressZero; + const lowerHint = ethers.constants.AddressZero; + const maxFeePercentage = ethers.utils.parseUnits("0.5", 18); // 0.5% max fee + const borrowSpell = { + connector: helpers.LIQUITY_CONNECTOR, + method: "borrow", + args: [maxFeePercentage, borrowAmount, upperHint, lowerHint, 0, 0], + }; + + // Borrow more LUSD from the Trove + await dsaWallet0 + .connect(userWallet) + .cast(...encodeSpells([borrowSpell]), userWallet.address); + + const troveDebt = await liquity.troveManager.getTroveDebt( + dsaWallet0.address + ); + const expectedTroveDebt = troveDebtBefore.add(borrowAmount); + + expect( + troveDebt, + `Trove debt should have increased by at least ${borrowAmount} ETH` + ).to.gte(expectedTroveDebt); + }); + + it("borrows LUSD from a Trove and stores the LUSD for other spells to use", async () => { + // Create a dummy Trove + await helpers.createDsaTrove(dsaWallet0, userWallet, liquity); + + const troveDebtBefore = await liquity.troveManager.getTroveDebt( + dsaWallet0.address + ); + + const borrowAmount = ethers.utils.parseUnits("1000", 18); // 1000 LUSD + const upperHint = ethers.constants.AddressZero; + const lowerHint = ethers.constants.AddressZero; + const maxFeePercentage = ethers.utils.parseUnits("0.5", 18); // 0.5% max fee + const borrowId = 1; + const borrowSpell = { + connector: helpers.LIQUITY_CONNECTOR, + method: "borrow", + args: [ + maxFeePercentage, + borrowAmount, + upperHint, + lowerHint, + 0, + borrowId, + ], + }; + const withdrawSpell = { + connector: helpers.INSTADAPP_BASIC_V1_CONNECTOR, + method: "withdraw", + args: [ + liquity.lusdToken.address, + 0, + userWallet.address, + borrowId, + 0, + ], + }; + const spells = [borrowSpell, withdrawSpell]; + + // Borrow more LUSD from the Trove + await dsaWallet0 + .connect(userWallet) + .cast(...encodeSpells(spells), userWallet.address); + + const troveDebt = await liquity.troveManager.getTroveDebt( + dsaWallet0.address + ); + const expectedTroveDebt = troveDebtBefore.add(borrowAmount); + const userLusdBalance = await liquity.lusdToken.balanceOf( + userWallet.address + ); + + expect( + troveDebt, + `Trove debt should have increased by at least ${borrowAmount} ETH` + ).to.gte(expectedTroveDebt); + + expect( + userLusdBalance, + `User LUSD balance should equal the borrowed LUSD due to the second withdraw spell` + ).eq(borrowAmount); + }); + + it("returns Instadapp event name and data", async () => { + // Create a dummy Trove + await helpers.createDsaTrove(dsaWallet0, userWallet, liquity); + + const borrowAmount = ethers.utils.parseUnits("1000", 18); // 1000 LUSD + const upperHint = ethers.constants.AddressZero; + const lowerHint = ethers.constants.AddressZero; + const maxFeePercentage = ethers.utils.parseUnits("0.5", 18); // 0.5% max fee + const borrowSpell = { + connector: helpers.LIQUITY_CONNECTOR, + method: "borrow", + args: [maxFeePercentage, borrowAmount, upperHint, lowerHint, 0, 0], + }; + + const borrowTx = await dsaWallet0 + .connect(userWallet) + .cast(...encodeSpells([borrowSpell]), userWallet.address); + + const receipt = await borrowTx.wait(); + const castLogEvent = receipt.events.find( + (e: { event: string }) => e.event === "LogCast" + ).args; + const expectedEventParams = ethers.utils.defaultAbiCoder.encode( + ["address", "uint256", "uint256", "uint256"], + [dsaWallet0.address, borrowAmount, 0, 0] + ); + expect(castLogEvent.eventNames[0]).eq( + "LogBorrow(address,uint256,uint256,uint256)" + ); + expect(castLogEvent.eventParams[0]).eq(expectedEventParams); + }); + }); + + describe("repay()", () => { + it("repays LUSD to a Trove", async () => { + const depositAmount = ethers.utils.parseEther("5"); + const borrowAmount = ethers.utils.parseUnits("2500", 18); + + // Create a dummy Trove + await helpers.createDsaTrove( + dsaWallet0, + userWallet, + liquity, + depositAmount, + borrowAmount + ); + + const troveDebtBefore = await liquity.troveManager.getTroveDebt( + dsaWallet0.address + ); + // DSA account is holding 2500 LUSD from opening a Trove, so we use some of that to repay + const repayAmount = ethers.utils.parseUnits("100", 18); // 100 LUSD + + const { upperHint, lowerHint } = await helpers.getTroveInsertionHints( + depositAmount, + borrowAmount, + liquity + ); + const repaySpell = { + connector: helpers.LIQUITY_CONNECTOR, + method: "repay", + args: [repayAmount, upperHint, lowerHint, 0, 0], + }; + + await dsaWallet0 + .connect(userWallet) + .cast(...encodeSpells([repaySpell]), userWallet.address); + + const troveDebt = await liquity.troveManager.getTroveDebt( + dsaWallet0.address + ); + const expectedTroveDebt = troveDebtBefore.sub(repayAmount); + + expect( + troveDebt, + `Trove debt should have decreased by ${repayAmount} ETH` + ).to.eq(expectedTroveDebt); + }); + + it("repays LUSD to a Trove using LUSD collected from a previous spell", async () => { + const depositAmount = ethers.utils.parseEther("5"); + const borrowAmount = ethers.utils.parseUnits("2500", 18); + + // Create a dummy Trove + await helpers.createDsaTrove( + dsaWallet0, + userWallet, + liquity, + depositAmount, + borrowAmount + ); + + const troveDebtBefore = await liquity.troveManager.getTroveDebt( + dsaWallet0.address + ); + + const repayAmount = ethers.utils.parseUnits("100", 18); // 100 LUSD + const { upperHint, lowerHint } = await helpers.getTroveInsertionHints( + depositAmount, + borrowAmount, + liquity + ); + + // Drain the DSA's LUSD balance so that we ensure we are repaying using LUSD from a previous spell + await helpers.sendToken( + liquity.lusdToken, + borrowAmount, + dsaWallet0.address, + userWallet.address + ); + + // Allow DSA to spend user's LUSD + await liquity.lusdToken + .connect(userWallet) + .approve(dsaWallet0.address, repayAmount); + + const lusdDepositId = 1; + const depositSpell = { + connector: helpers.INSTADAPP_BASIC_V1_CONNECTOR, + method: "deposit", + args: [liquity.lusdToken.address, repayAmount, 0, lusdDepositId], + }; + const borrowSpell = { + connector: helpers.LIQUITY_CONNECTOR, + method: "repay", + args: [0, upperHint, lowerHint, lusdDepositId, 0], + }; + + const spells = [depositSpell, borrowSpell]; + + await dsaWallet0 + .connect(userWallet) + .cast(...encodeSpells(spells), userWallet.address); + + const troveDebt = await liquity.troveManager.getTroveDebt( + dsaWallet0.address + ); + const expectedTroveDebt = troveDebtBefore.sub(repayAmount); + + expect( + troveDebt, + `Trove debt should have decreased by ${repayAmount} ETH` + ).to.eq(expectedTroveDebt); + }); + + it("returns Instadapp event name and data", async () => { + // Create a dummy Trove + const depositAmount = ethers.utils.parseEther("5"); + const borrowAmount = ethers.utils.parseUnits("2500", 18); + await helpers.createDsaTrove( + dsaWallet0, + userWallet, + liquity, + depositAmount, + borrowAmount + ); + + const repayAmount = ethers.utils.parseUnits("100", 18); // 100 LUSD + const { upperHint, lowerHint } = await helpers.getTroveInsertionHints( + depositAmount, + borrowAmount, + liquity + ); + + const borrowSpell = { + connector: helpers.LIQUITY_CONNECTOR, + method: "repay", + args: [repayAmount, upperHint, lowerHint, 0, 0], + }; + + const repayTx = await dsaWallet0 + .connect(userWallet) + .cast(...encodeSpells([borrowSpell]), userWallet.address, { + value: repayAmount, + }); + + const receipt = await repayTx.wait(); + const castLogEvent = receipt.events.find( + (e: { event: string }) => e.event === "LogCast" + ).args; + const expectedEventParams = ethers.utils.defaultAbiCoder.encode( + ["address", "uint256", "uint256", "uint256"], + [dsaWallet0.address, repayAmount, 0, 0] + ); + expect(castLogEvent.eventNames[0]).eq( + "LogRepay(address,uint256,uint256,uint256)" + ); + expect(castLogEvent.eventParams[0]).eq(expectedEventParams); + }); + }); + + describe("adjust()", () => { + it("adjusts a Trove: deposit ETH and borrow LUSD", async () => { + // Create a dummy Trove + await helpers.createDsaTrove(dsaWallet0, userWallet, liquity); + + const troveCollateralBefore = await liquity.troveManager.getTroveColl( + dsaWallet0.address + ); + const troveDebtBefore = await liquity.troveManager.getTroveDebt( + dsaWallet0.address + ); + const depositAmount = ethers.utils.parseEther("1"); // 1 ETH + const borrowAmount = ethers.utils.parseUnits("500", 18); // 500 LUSD + const withdrawAmount = 0; + const repayAmount = 0; + const upperHint = ethers.constants.AddressZero; + const lowerHint = ethers.constants.AddressZero; + const maxFeePercentage = ethers.utils.parseUnits("0.5", 18); // 0.5% max fee + + const adjustSpell = { + connector: helpers.LIQUITY_CONNECTOR, + method: "adjust", + args: [ + maxFeePercentage, + depositAmount, + withdrawAmount, + borrowAmount, + repayAmount, + upperHint, + lowerHint, + [0, 0, 0, 0], + [0, 0, 0, 0], + ], + }; + + // Adjust Trove by depositing ETH and borrowing LUSD + await dsaWallet0 + .connect(userWallet) + .cast(...encodeSpells([adjustSpell]), userWallet.address, { + value: depositAmount, + gasLimit: helpers.MAX_GAS, + }); + + const troveCollateral = await liquity.troveManager.getTroveColl( + dsaWallet0.address + ); + const troveDebt = await liquity.troveManager.getTroveDebt( + dsaWallet0.address + ); + const expectedTroveColl = troveCollateralBefore.add(depositAmount); + const expectedTroveDebt = troveDebtBefore.add(borrowAmount); + + expect( + troveCollateral, + `Trove collateral should have increased by ${depositAmount} ETH` + ).to.eq(expectedTroveColl); + + expect( + troveDebt, + `Trove debt should have increased by at least ${borrowAmount} ETH` + ).to.gte(expectedTroveDebt); + }); + + it("adjusts a Trove: withdraw ETH and repay LUSD", async () => { + // Create a dummy Trove + await helpers.createDsaTrove(dsaWallet0, userWallet, liquity); + + const troveCollateralBefore = await liquity.troveManager.getTroveColl( + dsaWallet0.address + ); + const troveDebtBefore = await liquity.troveManager.getTroveDebt( + dsaWallet0.address + ); + const depositAmount = 0; + const borrowAmount = 0; + const withdrawAmount = ethers.utils.parseEther("1"); // 1 ETH; + const repayAmount = ethers.utils.parseUnits("10", 18); // 10 LUSD; + const { upperHint, lowerHint } = await helpers.getTroveInsertionHints( + troveCollateralBefore.sub(withdrawAmount), + troveDebtBefore.sub(repayAmount), + liquity + ); + const maxFeePercentage = ethers.utils.parseUnits("0.5", 18); // 0.5% max fee + + const adjustSpell = { + connector: helpers.LIQUITY_CONNECTOR, + method: "adjust", + args: [ + maxFeePercentage, + depositAmount, + withdrawAmount, + borrowAmount, + repayAmount, + upperHint, + lowerHint, + [0, 0, 0, 0], + [0, 0, 0, 0], + ], + }; + + // Adjust Trove by withdrawing ETH and repaying LUSD + await dsaWallet0 + .connect(userWallet) + .cast(...encodeSpells([adjustSpell]), userWallet.address, { + value: depositAmount, + gasLimit: helpers.MAX_GAS, + }); + + const troveCollateral = await liquity.troveManager.getTroveColl( + dsaWallet0.address + ); + const troveDebt = await liquity.troveManager.getTroveDebt( + dsaWallet0.address + ); + const expectedTroveColl = troveCollateralBefore.sub(withdrawAmount); + const expectedTroveDebt = troveDebtBefore.sub(repayAmount); + + expect( + troveCollateral, + `Trove collateral should have increased by ${depositAmount} ETH` + ).to.eq(expectedTroveColl); + + expect( + troveDebt, + `Trove debt should have decreased by at least ${repayAmount} LUSD` + ).to.gte(expectedTroveDebt); + }); + + it("adjusts a Trove: deposit ETH and repay LUSD using previous spells", async () => { + // Create a dummy Trove + await helpers.createDsaTrove(dsaWallet0, userWallet, liquity); + + const troveCollateralBefore = await liquity.troveManager.getTroveColl( + dsaWallet0.address + ); + const troveDebtBefore = await liquity.troveManager.getTroveDebt( + dsaWallet0.address + ); + const depositAmount = ethers.utils.parseEther("1"); // 1 ETH + const borrowAmount = 0; + const withdrawAmount = 0; + const repayAmount = ethers.utils.parseUnits("10", 18); // 10 lUSD + const upperHint = ethers.constants.AddressZero; + const lowerHint = ethers.constants.AddressZero; + const maxFeePercentage = ethers.utils.parseUnits("0.5", 18); // 0.5% max fee + + const ethDepositId = 1; + const lusdRepayId = 2; + + const depositEthSpell = { + connector: helpers.INSTADAPP_BASIC_V1_CONNECTOR, + method: "deposit", + args: [helpers.ETH, depositAmount, 0, ethDepositId], + }; + + const depositLusdSpell = { + connector: helpers.INSTADAPP_BASIC_V1_CONNECTOR, + method: "deposit", + args: [liquity.lusdToken.address, repayAmount, 0, lusdRepayId], + }; + + const adjustSpell = { + connector: helpers.LIQUITY_CONNECTOR, + method: "adjust", + args: [ + maxFeePercentage, + 0, // Deposit amount comes from a previous spell's storage slot + withdrawAmount, + borrowAmount, + 0, // Repay amount comes from a previous spell's storage slot + upperHint, + lowerHint, + [ethDepositId, 0, 0, lusdRepayId], + [0, 0, 0, 0], + ], + }; + const spells = [depositEthSpell, depositLusdSpell, adjustSpell]; + + // Send user some LUSD so they can repay + await helpers.sendToken( + liquity.lusdToken, + repayAmount, + helpers.JUSTIN_SUN_ADDRESS, + userWallet.address + ); + + // Allow DSA to spend user's LUSD + await liquity.lusdToken + .connect(userWallet) + .approve(dsaWallet0.address, repayAmount); + + // Adjust Trove by depositing ETH and repaying LUSD + await dsaWallet0 + .connect(userWallet) + .cast(...encodeSpells(spells), userWallet.address, { + value: depositAmount, + gasLimit: helpers.MAX_GAS, + }); + + const troveCollateral = await liquity.troveManager.getTroveColl( + dsaWallet0.address + ); + const troveDebt = await liquity.troveManager.getTroveDebt( + dsaWallet0.address + ); + const expectedTroveColl = troveCollateralBefore.add(depositAmount); + const expectedTroveDebt = troveDebtBefore.sub(repayAmount); + + expect( + troveCollateral, + `Trove collateral should have increased by ${depositAmount} ETH` + ).to.eq(expectedTroveColl); + + expect( + troveDebt, + `Trove debt (${troveDebtBefore}) should have decreased by at least ${repayAmount} LUSD` + ).to.eq(expectedTroveDebt); + }); + + it("adjusts a Trove: withdraw ETH, borrow LUSD, and store the amounts for other spells", async () => { + // Create a dummy Trove + await helpers.createDsaTrove(dsaWallet0, userWallet, liquity); + + const userEthBalanceBefore = await ethers.provider.getBalance( + userWallet.address + ); + const userLusdBalanceBefore = await liquity.lusdToken.balanceOf( + userWallet.address + ); + + const depositAmount = 0; + const borrowAmount = ethers.utils.parseUnits("100", 18); // 100 LUSD + const withdrawAmount = ethers.utils.parseEther("1"); // 1 ETH + const repayAmount = 0; + const upperHint = ethers.constants.AddressZero; + const lowerHint = ethers.constants.AddressZero; + const maxFeePercentage = ethers.utils.parseUnits("0.5", 18); // 0.5% max fee + + const ethWithdrawId = 1; + const lusdBorrowId = 2; + + const adjustSpell = { + connector: helpers.LIQUITY_CONNECTOR, + method: "adjust", + args: [ + maxFeePercentage, + depositAmount, + withdrawAmount, + borrowAmount, + repayAmount, + upperHint, + lowerHint, + [0, 0, 0, 0], + [0, ethWithdrawId, lusdBorrowId, 0], + ], + }; + + const withdrawEthSpell = { + connector: helpers.INSTADAPP_BASIC_V1_CONNECTOR, + method: "withdraw", + args: [helpers.ETH, 0, userWallet.address, ethWithdrawId, 0], + }; + + const withdrawLusdSpell = { + connector: helpers.INSTADAPP_BASIC_V1_CONNECTOR, + method: "withdraw", + args: [ + liquity.lusdToken.address, + 0, + userWallet.address, + lusdBorrowId, + 0, + ], + }; + + const spells = [adjustSpell, withdrawEthSpell, withdrawLusdSpell]; + + // Adjust Trove by withdrawing ETH and borrowing LUSD + await dsaWallet0 + .connect(userWallet) + .cast(...encodeSpells(spells), userWallet.address, { + gasLimit: helpers.MAX_GAS, + }); + + const userEthBalanceAfter = await ethers.provider.getBalance( + userWallet.address + ); + const userLusdBalanceAfter = await liquity.lusdToken.balanceOf( + userWallet.address + ); + expect(userEthBalanceAfter).lt( + userEthBalanceBefore.add(withdrawAmount) + ); + expect(userLusdBalanceAfter).eq( + userLusdBalanceBefore.add(borrowAmount) + ); + }); + + it("returns Instadapp event name and data", async () => { + // Create a dummy Trove + await helpers.createDsaTrove(dsaWallet0, userWallet, liquity); + + const depositAmount = ethers.utils.parseEther("1"); // 1 ETH + const borrowAmount = ethers.utils.parseUnits("500", 18); // 500 LUSD + const withdrawAmount = 0; + const repayAmount = 0; + const upperHint = ethers.constants.AddressZero; + const lowerHint = ethers.constants.AddressZero; + const maxFeePercentage = ethers.utils.parseUnits("0.5", 18); // 0.5% max fee + + const adjustSpell = { + connector: helpers.LIQUITY_CONNECTOR, + method: "adjust", + args: [ + maxFeePercentage, + depositAmount, + withdrawAmount, + borrowAmount, + repayAmount, + upperHint, + lowerHint, + [0, 0, 0, 0], + [0, 0, 0, 0], + ], + }; + + const adjustTx = await dsaWallet0 + .connect(userWallet) + .cast(...encodeSpells([adjustSpell]), userWallet.address, { + value: depositAmount, + gasLimit: helpers.MAX_GAS, + }); + + const receipt = await adjustTx.wait(); + const castLogEvent = receipt.events.find( + (e: { event: string }) => e.event === "LogCast" + ).args; + const expectedEventParams = ethers.utils.defaultAbiCoder.encode( + [ + "address", + "uint256", + "uint256", + "uint256", + "uint256", + "uint256", + "uint256[]", + "uint256[]", + ], + [ + dsaWallet0.address, + maxFeePercentage, + depositAmount, + withdrawAmount, + borrowAmount, + repayAmount, + [0, 0, 0, 0], + [0, 0, 0, 0], + ] + ); + expect(castLogEvent.eventNames[0]).eq( + "LogAdjust(address,uint256,uint256,uint256,uint256,uint256,uint256[],uint256[])" + ); + expect(castLogEvent.eventParams[0]).eq(expectedEventParams); + }); + }); + + describe("claimCollateralFromRedemption()", () => { + it("claims collateral from a redeemed Trove", async () => { + // Create a low collateralized Trove + const depositAmount = ethers.utils.parseEther("1.5"); + const borrowAmount = ethers.utils.parseUnits("2500", 18); + + await helpers.createDsaTrove( + dsaWallet0, + userWallet, + liquity, + depositAmount, + borrowAmount + ); + + // Redeem lots of LUSD to cause the Trove to become redeemed + const redeemAmount = ethers.utils.parseUnits("10000000", 18); + await helpers.sendToken( + liquity.lusdToken, + redeemAmount, + contracts.STABILITY_POOL_ADDRESS, + userWallet.address + ); + const { + partialRedemptionHintNicr, + firstRedemptionHint, + upperHint, + lowerHint, + } = await helpers.getRedemptionHints(redeemAmount, liquity); + const maxFeePercentage = ethers.utils.parseUnits("0.5", 18); // 0.5% max fee + + await liquity.troveManager + .connect(userWallet) + .redeemCollateral( + redeemAmount, + firstRedemptionHint, + upperHint, + lowerHint, + partialRedemptionHintNicr, + 0, + maxFeePercentage, + { + gasLimit: helpers.MAX_GAS, // permit max gas + } + ); + + const remainingEthCollateral = await liquity.collSurplus.getCollateral( + dsaWallet0.address + ); + + // Claim the remaining collateral from the redeemed Trove + const claimCollateralFromRedemptionSpell = { + connector: helpers.LIQUITY_CONNECTOR, + method: "claimCollateralFromRedemption", + args: [0], + }; + + await dsaWallet0 + .connect(userWallet) + .cast( + ...encodeSpells([claimCollateralFromRedemptionSpell]), + userWallet.address + ); + + const ethBalance = await ethers.provider.getBalance( + dsaWallet0.address + ); + + expect(ethBalance).to.eq(remainingEthCollateral); + }); + + it("returns Instadapp event name and data", async () => { + // Create a low collateralized Trove + const depositAmount = ethers.utils.parseEther("1.5"); + const borrowAmount = ethers.utils.parseUnits("2500", 18); + + await helpers.createDsaTrove( + dsaWallet0, + userWallet, + liquity, + depositAmount, + borrowAmount + ); + + // Redeem lots of LUSD to cause the Trove to become redeemed + const redeemAmount = ethers.utils.parseUnits("10000000", 18); + const setId = 0; + await helpers.sendToken( + liquity.lusdToken, + redeemAmount, + contracts.STABILITY_POOL_ADDRESS, + userWallet.address + ); + const { + partialRedemptionHintNicr, + firstRedemptionHint, + upperHint, + lowerHint, + } = await helpers.getRedemptionHints(redeemAmount, liquity); + const maxFeePercentage = ethers.utils.parseUnits("0.5", 18); // 0.5% max fee + + await liquity.troveManager + .connect(userWallet) + .redeemCollateral( + redeemAmount, + firstRedemptionHint, + upperHint, + lowerHint, + partialRedemptionHintNicr, + 0, + maxFeePercentage, + { + gasLimit: helpers.MAX_GAS, // permit max gas + } + ); + const claimAmount = await liquity.collSurplus.getCollateral( + dsaWallet0.address + ); + + const claimCollateralFromRedemptionSpell = { + connector: helpers.LIQUITY_CONNECTOR, + method: "claimCollateralFromRedemption", + args: [setId], + }; + + const claimTx = await dsaWallet0 + .connect(userWallet) + .cast( + ...encodeSpells([claimCollateralFromRedemptionSpell]), + userWallet.address + ); + + const receipt = await claimTx.wait(); + const castLogEvent = receipt.events.find( + (e: { event: string }) => e.event === "LogCast" + ).args; + const expectedEventParams = ethers.utils.defaultAbiCoder.encode( + ["address", "uint256", "uint256"], + [dsaWallet0.address, claimAmount, setId] + ); + expect(castLogEvent.eventNames[0]).eq( + "LogClaimCollateralFromRedemption(address,uint256,uint256)" + ); + expect(castLogEvent.eventParams[0]).eq(expectedEventParams); + }); + }); + }); + + describe("Stability Pool", () => { + describe("stabilityDeposit()", () => { + it("deposits into Stability Pool", async () => { + const amount = ethers.utils.parseUnits("100", 18); + const frontendTag = ethers.constants.AddressZero; + + await helpers.sendToken( + liquity.lusdToken, + amount, + contracts.STABILITY_POOL_ADDRESS, + dsaWallet0.address + ); + + const stabilityDepositSpell = { + connector: helpers.LIQUITY_CONNECTOR, + method: "stabilityDeposit", + args: [amount, frontendTag, 0, 0, 0, 0], + }; + + await dsaWallet0 + .connect(userWallet) + .cast(...encodeSpells([stabilityDepositSpell]), userWallet.address); + + const depositedAmount = await liquity.stabilityPool.getCompoundedLUSDDeposit( + dsaWallet0.address + ); + expect(depositedAmount).to.eq(amount); + }); + + it("deposits into Stability Pool using LUSD collected from a previous spell", async () => { + const amount = ethers.utils.parseUnits("100", 18); + const frontendTag = ethers.constants.AddressZero; + + await helpers.sendToken( + liquity.lusdToken, + amount, + contracts.STABILITY_POOL_ADDRESS, + userWallet.address + ); + const lusdDepositId = 1; + + const depositLusdSpell = { + connector: helpers.INSTADAPP_BASIC_V1_CONNECTOR, + method: "deposit", + args: [liquity.lusdToken.address, amount, 0, lusdDepositId], + }; + const stabilityDepositSpell = { + connector: helpers.LIQUITY_CONNECTOR, + method: "stabilityDeposit", + args: [0, frontendTag, lusdDepositId, 0, 0, 0], + }; + const spells = [depositLusdSpell, stabilityDepositSpell]; + + // Allow DSA to spend user's LUSD + await liquity.lusdToken + .connect(userWallet) + .approve(dsaWallet0.address, amount); + + await dsaWallet0 + .connect(userWallet) + .cast(...encodeSpells(spells), userWallet.address); + + const depositedAmount = await liquity.stabilityPool.getCompoundedLUSDDeposit( + dsaWallet0.address + ); + expect(depositedAmount).to.eq(amount); + }); + + it("returns Instadapp event name and data", async () => { + const amount = ethers.utils.parseUnits("100", 18); + const halfAmount = amount.div(2); + const frontendTag = ethers.constants.AddressZero; + const getDepositId = 0; + const setDepositId = 0; + const setEthGainId = 0; + const setLqtyGainId = 0; + + await helpers.sendToken( + liquity.lusdToken, + amount, + contracts.STABILITY_POOL_ADDRESS, + dsaWallet0.address + ); + + const stabilityDepositSpell = { + connector: helpers.LIQUITY_CONNECTOR, + method: "stabilityDeposit", + args: [ + halfAmount, + frontendTag, + getDepositId, + setDepositId, + setEthGainId, + setLqtyGainId, + ], + }; + + // Create a Stability deposit for this DSA + await dsaWallet0 + .connect(userWallet) + .cast(...encodeSpells([stabilityDepositSpell]), userWallet.address); + + // Liquidate a Trove to cause an ETH gain + await liquity.troveManager.connect(userWallet).liquidateTroves(1, { + gasLimit: helpers.MAX_GAS, + }); + + // Fast forward in time so we have an LQTY gain + await provider.send("evm_increaseTime", [600]); + await provider.send("evm_mine", []); + + // Create a Stability Pool deposit with a differen DSA so that LQTY gains can be calculated + // See: https://github.com/liquity/dev/#lqty-reward-events-and-payouts + const tempDsa = await buildDSAv2(userWallet.address); + await helpers.sendToken( + liquity.lusdToken, + amount, + contracts.STABILITY_POOL_ADDRESS, + tempDsa.address + ); + await tempDsa + .connect(userWallet) + .cast(...encodeSpells([stabilityDepositSpell]), userWallet.address); + + const ethGain = await liquity.stabilityPool.getDepositorETHGain( + dsaWallet0.address + ); + const lqtyGain = await liquity.stabilityPool.getDepositorLQTYGain( + dsaWallet0.address + ); + + // Top up the user's deposit so that we can track their ETH and LQTY gain + const depositAgainTx = await dsaWallet0 + .connect(userWallet) + .cast(...encodeSpells([stabilityDepositSpell]), userWallet.address); + + const receipt = await depositAgainTx.wait(); + const castLogEvent = receipt.events.find( + (e: { event: string }) => e.event === "LogCast" + ).args; + const expectedEventParams = ethers.utils.defaultAbiCoder.encode( + [ + "address", + "uint256", + "uint256", + "uint256", + "address", + "uint256", + "uint256", + "uint256", + "uint256", + ], + [ + dsaWallet0.address, + halfAmount, + ethGain, + lqtyGain, + frontendTag, + getDepositId, + setDepositId, + setEthGainId, + setLqtyGainId, + ] + ); + expect(castLogEvent.eventNames[0]).eq( + "LogStabilityDeposit(address,uint256,uint256,uint256,address,uint256,uint256,uint256,uint256)" + ); + expect(castLogEvent.eventParams[0]).eq(expectedEventParams); + }); + }); + + describe("stabilityWithdraw()", () => { + it("withdraws from Stability Pool", async () => { + // Start this test from scratch since we need to remove any liquidatable Troves withdrawing from Stability Pool + [liquity, dsaWallet0] = await helpers.resetInitialState( + userWallet.address, + contracts + ); + + // The current block number has liquidatable Troves. + // Remove them otherwise Stability Pool withdrawals are disabled + await liquity.troveManager.connect(userWallet).liquidateTroves(90, { + gasLimit: helpers.MAX_GAS, + }); + + const amount = ethers.utils.parseUnits("100", 18); + const frontendTag = ethers.constants.AddressZero; + + await helpers.sendToken( + liquity.lusdToken, + amount, + contracts.STABILITY_POOL_ADDRESS, + dsaWallet0.address + ); + + const stabilityDepositSpell = { + connector: helpers.LIQUITY_CONNECTOR, + method: "stabilityDeposit", + args: [amount, frontendTag, 0, 0, 0, 0], + }; + + // Withdraw half of the deposit + const stabilityWithdrawSpell = { + connector: helpers.LIQUITY_CONNECTOR, + method: "stabilityWithdraw", + args: [amount.div(2), 0, 0, 0, 0], + }; + const spells = [stabilityDepositSpell, stabilityWithdrawSpell]; + + await dsaWallet0 + .connect(userWallet) + .cast(...encodeSpells(spells), userWallet.address); + + const depositedAmount = await liquity.stabilityPool.getCompoundedLUSDDeposit( + dsaWallet0.address + ); + const dsaLusdBalance = await liquity.lusdToken.balanceOf( + dsaWallet0.address + ); + + expect(depositedAmount).to.eq(amount.div(2)); + expect(dsaLusdBalance).to.eq(amount.div(2)); + }); + + it("withdraws from Stability Pool and stores the LUSD for other spells", async () => { + // Start this test from scratch since we need to remove any liquidatable Troves withdrawing from Stability Pool + [liquity, dsaWallet0] = await helpers.resetInitialState( + userWallet.address, + contracts + ); + + // The current block number has liquidatable Troves. + // Remove them otherwise Stability Pool withdrawals are disabled + await liquity.troveManager.connect(userWallet).liquidateTroves(90, { + gasLimit: helpers.MAX_GAS, + }); + const amount = ethers.utils.parseUnits("100", 18); + const frontendTag = ethers.constants.AddressZero; + const withdrawId = 1; + + await helpers.sendToken( + liquity.lusdToken, + amount, + contracts.STABILITY_POOL_ADDRESS, + dsaWallet0.address + ); + + const stabilityDepositSpell = { + connector: helpers.LIQUITY_CONNECTOR, + method: "stabilityDeposit", + args: [amount, frontendTag, 0, 0, 0, 0], + }; + + // Withdraw half of the deposit + const stabilityWithdrawSpell = { + connector: helpers.LIQUITY_CONNECTOR, + method: "stabilityWithdraw", + args: [amount.div(2), 0, 0, 0, withdrawId], + }; + + const withdrawLusdSpell = { + connector: helpers.INSTADAPP_BASIC_V1_CONNECTOR, + method: "withdraw", + args: [ + liquity.lusdToken.address, + 0, + userWallet.address, + withdrawId, + 0, + ], + }; + + const spells = [ + stabilityDepositSpell, + stabilityWithdrawSpell, + withdrawLusdSpell, + ]; + + await dsaWallet0 + .connect(userWallet) + .cast(...encodeSpells(spells), userWallet.address); + + const depositedAmount = await liquity.stabilityPool.getCompoundedLUSDDeposit( + dsaWallet0.address + ); + const walletLusdBalance = await liquity.lusdToken.balanceOf( + dsaWallet0.address + ); + + expect(depositedAmount).to.eq(amount.div(2)); + expect(walletLusdBalance).to.eq(amount.div(2)); + }); + + it("returns Instadapp event name and data", async () => { + // Start this test from scratch since we need to remove any liquidatable Troves withdrawing from Stability Pool + [liquity, dsaWallet0] = await helpers.resetInitialState( + userWallet.address, + contracts + ); + + const amount = ethers.utils.parseUnits("100", 18); + const frontendTag = ethers.constants.AddressZero; + + await helpers.sendToken( + liquity.lusdToken, + amount, + contracts.STABILITY_POOL_ADDRESS, + dsaWallet0.address + ); + + const stabilityDepositSpell = { + connector: helpers.LIQUITY_CONNECTOR, + method: "stabilityDeposit", + args: [amount, frontendTag, 0, 0, 0, 0], + }; + + // Withdraw half of the deposit + const withdrawAmount = amount.div(2); + const getWithdrawId = 0; + const setWithdrawId = 0; + const setEthGainId = 0; + const setLqtyGainId = 0; + + // Create a Stability Pool deposit + await dsaWallet0 + .connect(userWallet) + .cast(...encodeSpells([stabilityDepositSpell]), userWallet.address); + + // The current block number has liquidatable Troves. + // Remove them otherwise Stability Pool withdrawals are disabled + await liquity.troveManager.connect(userWallet).liquidateTroves(90, { + gasLimit: helpers.MAX_GAS, + }); + + // Fast forward in time so we have an LQTY gain + await provider.send("evm_increaseTime", [600]); + await provider.send("evm_mine", []); + + // Create another Stability Pool deposit so that LQTY gains are realized + // See: https://github.com/liquity/dev/#lqty-reward-events-and-payouts + const tempDsa = await buildDSAv2(userWallet.address); + await helpers.sendToken( + liquity.lusdToken, + amount, + contracts.STABILITY_POOL_ADDRESS, + tempDsa.address + ); + await tempDsa + .connect(userWallet) + .cast(...encodeSpells([stabilityDepositSpell]), userWallet.address); + + const ethGain = await liquity.stabilityPool.getDepositorETHGain( + dsaWallet0.address + ); + const lqtyGain = await liquity.stabilityPool.getDepositorLQTYGain( + dsaWallet0.address + ); + + const stabilityWithdrawSpell = { + connector: helpers.LIQUITY_CONNECTOR, + method: "stabilityWithdraw", + args: [ + withdrawAmount, + getWithdrawId, + setWithdrawId, + setEthGainId, + setLqtyGainId, + ], + }; + + const withdrawTx = await dsaWallet0 + .connect(userWallet) + .cast( + ...encodeSpells([stabilityWithdrawSpell]), + userWallet.address + ); + + const receipt = await withdrawTx.wait(); + const castLogEvent = receipt.events.find( + (e: { event: string }) => e.event === "LogCast" + ).args; + const expectedEventParams = ethers.utils.defaultAbiCoder.encode( + [ + "address", + "uint256", + "uint256", + "uint256", + "uint256", + "uint256", + "uint256", + "uint256", + ], + [ + dsaWallet0.address, + withdrawAmount, + ethGain, + lqtyGain, + getWithdrawId, + setWithdrawId, + setEthGainId, + setLqtyGainId, + ] + ); + expect(castLogEvent.eventNames[0]).eq( + "LogStabilityWithdraw(address,uint256,uint256,uint256,uint256,uint256,uint256,uint256)" + ); + expect(castLogEvent.eventParams[0]).eq(expectedEventParams); + }); + }); + + describe("stabilityMoveEthGainToTrove()", () => { + beforeEach(async () => { + // Start these test from fresh so that we definitely have a liquidatable Trove within this block + [liquity, dsaWallet0] = await helpers.resetInitialState( + userWallet.address, + contracts + ); + }); + + it("moves ETH gain from Stability Pool to Trove", async () => { + // Create a DSA owned Trove to capture ETH liquidation gains + // Create a dummy Trove + await helpers.createDsaTrove(dsaWallet0, userWallet, liquity); + const troveCollateralBefore = await liquity.troveManager.getTroveColl( + dsaWallet0.address + ); + + // Create a Stability Deposit using the Trove's borrowed LUSD + const amount = ethers.utils.parseUnits("100", 18); + const frontendTag = ethers.constants.AddressZero; + const stabilityDepositSpell = { + connector: helpers.LIQUITY_CONNECTOR, + method: "stabilityDeposit", + args: [amount, frontendTag, 0, 0, 0, 0], + }; + + await dsaWallet0 + .connect(userWallet) + .cast(...encodeSpells([stabilityDepositSpell]), userWallet.address); + + // Liquidate a Trove to create an ETH gain for the new DSA Trove + await liquity.troveManager + .connect(userWallet) + .liquidate(helpers.LIQUIDATABLE_TROVE_ADDRESS, { + gasLimit: helpers.MAX_GAS, // permit max gas + }); + + const ethGainFromLiquidation = await liquity.stabilityPool.getDepositorETHGain( + dsaWallet0.address + ); + + // Move ETH gain to Trove + const moveEthGainSpell = { + connector: helpers.LIQUITY_CONNECTOR, + method: "stabilityMoveEthGainToTrove", + args: [ethers.constants.AddressZero, ethers.constants.AddressZero], + }; + + await dsaWallet0 + .connect(userWallet) + .cast(...encodeSpells([moveEthGainSpell]), userWallet.address); + + const ethGainAfterMove = await liquity.stabilityPool.getDepositorETHGain( + dsaWallet0.address + ); + const troveCollateral = await liquity.troveManager.getTroveColl( + dsaWallet0.address + ); + const expectedTroveCollateral = troveCollateralBefore.add( + ethGainFromLiquidation + ); + expect(ethGainAfterMove).to.eq(0); + expect(troveCollateral).to.eq(expectedTroveCollateral); + }); + + it("returns Instadapp event name and data", async () => { + // Create a DSA owned Trove to capture ETH liquidation gains + // Create a dummy Trove + await helpers.createDsaTrove(dsaWallet0, userWallet, liquity); + + // Create a Stability Deposit using the Trove's borrowed LUSD + const amount = ethers.utils.parseUnits("100", 18); + const frontendTag = ethers.constants.AddressZero; + const stabilityDepositSpell = { + connector: helpers.LIQUITY_CONNECTOR, + method: "stabilityDeposit", + args: [amount, frontendTag, 0, 0, 0, 0], + }; + + await dsaWallet0 + .connect(userWallet) + .cast(...encodeSpells([stabilityDepositSpell]), userWallet.address); + + // Liquidate a Trove to create an ETH gain for the new DSA Trove + await liquity.troveManager + .connect(userWallet) + .liquidate(helpers.LIQUIDATABLE_TROVE_ADDRESS, { + gasLimit: helpers.MAX_GAS, // permit max gas + }); + + const ethGainFromLiquidation = await liquity.stabilityPool.getDepositorETHGain( + dsaWallet0.address + ); + + // Move ETH gain to Trove + const moveEthGainSpell = { + connector: helpers.LIQUITY_CONNECTOR, + method: "stabilityMoveEthGainToTrove", + args: [ethers.constants.AddressZero, ethers.constants.AddressZero], + }; + + const moveEthGainTx = await dsaWallet0 + .connect(userWallet) + .cast(...encodeSpells([moveEthGainSpell]), userWallet.address); + + const receipt = await moveEthGainTx.wait(); + + const castLogEvent = receipt.events.find( + (e: { event: string }) => e.event === "LogCast" + ).args; + const expectedEventParams = ethers.utils.defaultAbiCoder.encode( + ["address", "uint256"], + [dsaWallet0.address, ethGainFromLiquidation] + ); + expect(castLogEvent.eventNames[0]).eq( + "LogStabilityMoveEthGainToTrove(address,uint256)" + ); + expect(castLogEvent.eventParams[0]).eq(expectedEventParams); + }); + }); + }); + + describe("Staking", () => { + describe("stake()", () => { + it("stakes LQTY", async () => { + const totalStakingBalanceBefore = await liquity.lqtyToken.balanceOf( + contracts.STAKING_ADDRESS + ); + + const amount = ethers.utils.parseUnits("1", 18); + await helpers.sendToken( + liquity.lqtyToken, + amount, + helpers.JUSTIN_SUN_ADDRESS, + dsaWallet0.address + ); + + const stakeSpell = { + connector: helpers.LIQUITY_CONNECTOR, + method: "stake", + args: [amount, 0, 0, 0, 0], + }; + + await dsaWallet0 + .connect(userWallet) + .cast(...encodeSpells([stakeSpell]), userWallet.address); + + const lqtyBalance = await liquity.lqtyToken.balanceOf( + dsaWallet0.address + ); + expect(lqtyBalance).to.eq(0); + + const totalStakingBalance = await liquity.lqtyToken.balanceOf( + contracts.STAKING_ADDRESS + ); + expect(totalStakingBalance).to.eq( + totalStakingBalanceBefore.add(amount) + ); + }); + + it("stakes LQTY using LQTY obtained from a previous spell", async () => { + const totalStakingBalanceBefore = await liquity.lqtyToken.balanceOf( + contracts.STAKING_ADDRESS + ); + + const amount = ethers.utils.parseUnits("1", 18); + await helpers.sendToken( + liquity.lqtyToken, + amount, + helpers.JUSTIN_SUN_ADDRESS, + userWallet.address + ); + + const lqtyDepositId = 1; + const depositSpell = { + connector: helpers.INSTADAPP_BASIC_V1_CONNECTOR, + method: "deposit", + args: [liquity.lqtyToken.address, amount, 0, lqtyDepositId], + }; + const stakeSpell = { + connector: helpers.LIQUITY_CONNECTOR, + method: "stake", + args: [0, lqtyDepositId, 0, 0, 0], + }; + const spells = [depositSpell, stakeSpell]; + + // Allow DSA to spend user's LQTY + await liquity.lqtyToken + .connect(userWallet) + .approve(dsaWallet0.address, amount); + + await dsaWallet0 + .connect(userWallet) + .cast(...encodeSpells(spells), userWallet.address); + + const lqtyBalance = await liquity.lqtyToken.balanceOf( + dsaWallet0.address + ); + expect(lqtyBalance).to.eq(0); + + const totalStakingBalance = await liquity.lqtyToken.balanceOf( + contracts.STAKING_ADDRESS + ); + expect(totalStakingBalance).to.eq( + totalStakingBalanceBefore.add(amount) + ); + }); + + it("returns Instadapp event name and data", async () => { + const amount = ethers.utils.parseUnits("1", 18); + await helpers.sendToken( + liquity.lqtyToken, + amount, + helpers.JUSTIN_SUN_ADDRESS, + dsaWallet0.address + ); + + const getStakeId = 0; + const setStakeId = 0; + const setEthGainId = 0; + const setLusdGainId = 0; + const stakeSpell = { + connector: helpers.LIQUITY_CONNECTOR, + method: "stake", + args: [amount, getStakeId, setStakeId, setEthGainId, setLusdGainId], + }; + + const stakeTx = await dsaWallet0 + .connect(userWallet) + .cast(...encodeSpells([stakeSpell]), userWallet.address); + + const receipt = await stakeTx.wait(); + + const castLogEvent = receipt.events.find( + (e: { event: string }) => e.event === "LogCast" + ).args; + const expectedEventParams = ethers.utils.defaultAbiCoder.encode( + ["address", "uint256", "uint256", "uint256", "uint256", "uint256"], + [ + dsaWallet0.address, + amount, + getStakeId, + setStakeId, + setEthGainId, + setLusdGainId, + ] + ); + expect(castLogEvent.eventNames[0]).eq( + "LogStake(address,uint256,uint256,uint256,uint256,uint256)" + ); + expect(castLogEvent.eventParams[0]).eq(expectedEventParams); + }); + }); + + describe("unstake()", () => { + it("unstakes LQTY", async () => { + const amount = ethers.utils.parseUnits("1", 18); + await helpers.sendToken( + liquity.lqtyToken, + amount, + helpers.JUSTIN_SUN_ADDRESS, + dsaWallet0.address + ); + + const stakeSpell = { + connector: helpers.LIQUITY_CONNECTOR, + method: "stake", + args: [amount, 0, 0, 0, 0], + }; + + await dsaWallet0 + .connect(userWallet) + .cast(...encodeSpells([stakeSpell]), userWallet.address); + + const totalStakingBalanceBefore = await liquity.lqtyToken.balanceOf( + contracts.STAKING_ADDRESS + ); + const unstakeSpell = { + connector: helpers.LIQUITY_CONNECTOR, + method: "unstake", + args: [amount, 0, 0, 0, 0], + }; + + await dsaWallet0 + .connect(userWallet) + .cast(...encodeSpells([unstakeSpell]), userWallet.address); + + const lqtyBalance = await liquity.lqtyToken.balanceOf( + dsaWallet0.address + ); + expect(lqtyBalance).to.eq(amount); + + const totalStakingBalance = await liquity.lqtyToken.balanceOf( + contracts.STAKING_ADDRESS + ); + expect(totalStakingBalance).to.eq( + totalStakingBalanceBefore.sub(amount) + ); + }); + + it("unstakes LQTY and stores the LQTY for other spells", async () => { + const amount = ethers.utils.parseUnits("1", 18); + await helpers.sendToken( + liquity.lqtyToken, + amount, + helpers.JUSTIN_SUN_ADDRESS, + dsaWallet0.address + ); + + const stakeSpell = { + connector: helpers.LIQUITY_CONNECTOR, + method: "stake", + args: [amount, 0, 0, 0, 0], + }; + + await dsaWallet0 + .connect(userWallet) + .cast(...encodeSpells([stakeSpell]), userWallet.address); + + const totalStakingBalanceBefore = await liquity.lqtyToken.balanceOf( + contracts.STAKING_ADDRESS + ); + const withdrawId = 1; + const unstakeSpell = { + connector: helpers.LIQUITY_CONNECTOR, + method: "unstake", + args: [amount, 0, withdrawId, 0, 0], + }; + + const withdrawLqtySpell = { + connector: helpers.INSTADAPP_BASIC_V1_CONNECTOR, + method: "withdraw", + args: [ + liquity.lqtyToken.address, + 0, + userWallet.address, + withdrawId, + 0, + ], + }; + const spells = [unstakeSpell, withdrawLqtySpell]; + await dsaWallet0 + .connect(userWallet) + .cast(...encodeSpells(spells), userWallet.address); + + const lqtyBalance = await liquity.lqtyToken.balanceOf( + dsaWallet0.address + ); + const totalStakingBalance = await liquity.lqtyToken.balanceOf( + contracts.STAKING_ADDRESS + ); + const userLqtyBalance = await liquity.lqtyToken.balanceOf( + userWallet.address + ); + expect(lqtyBalance).to.eq(0); + expect(totalStakingBalance).to.eq( + totalStakingBalanceBefore.sub(amount) + ); + expect(userLqtyBalance).to.eq(amount); + }); + + it("returns Instadapp event name and data", async () => { + const amount = ethers.utils.parseUnits("1", 18); + await helpers.sendToken( + liquity.lqtyToken, + amount, + helpers.JUSTIN_SUN_ADDRESS, + dsaWallet0.address + ); + + const stakeSpell = { + connector: helpers.LIQUITY_CONNECTOR, + method: "stake", + args: [amount, 0, 0, 0, 0], + }; + + await dsaWallet0 + .connect(userWallet) + .cast(...encodeSpells([stakeSpell]), userWallet.address); + + const getUnstakeId = 0; + const setUnstakeId = 0; + const setEthGainId = 0; + const setLusdGainId = 0; + const unstakeSpell = { + connector: helpers.LIQUITY_CONNECTOR, + method: "unstake", + args: [ + amount, + getUnstakeId, + setUnstakeId, + setEthGainId, + setLusdGainId, + ], + }; + + const unstakeTx = await dsaWallet0 + .connect(userWallet) + .cast(...encodeSpells([unstakeSpell]), userWallet.address); + + const receipt = await unstakeTx.wait(); + + const castLogEvent = receipt.events.find( + (e: { event: string }) => e.event === "LogCast" + ).args; + const expectedEventParams = ethers.utils.defaultAbiCoder.encode( + ["address", "uint256", "uint256", "uint256", "uint256", "uint256"], + [ + dsaWallet0.address, + amount, + getUnstakeId, + setUnstakeId, + setEthGainId, + setLusdGainId, + ] + ); + expect(castLogEvent.eventNames[0]).eq( + "LogUnstake(address,uint256,uint256,uint256,uint256,uint256)" + ); + expect(castLogEvent.eventParams[0]).eq(expectedEventParams); + }); + }); + + describe("claimStakingGains()", () => { + it("claims gains from staking", async () => { + const stakerDsa = await buildDSAv2(userWallet.address); + const amount = ethers.utils.parseUnits("1000", 18); // 1000 LQTY + + // Stake lots of LQTY + await helpers.sendToken( + liquity.lqtyToken, + amount, + helpers.JUSTIN_SUN_ADDRESS, + stakerDsa.address + ); + const stakeSpell = { + connector: helpers.LIQUITY_CONNECTOR, + method: "stake", + args: [amount, 0, 0, 0, 0], + }; + await stakerDsa + .connect(userWallet) + .cast(...encodeSpells([stakeSpell]), userWallet.address); + + // Open a Trove to cause an ETH issuance gain for stakers + await helpers.createDsaTrove(dsaWallet0, userWallet, liquity); + + // Redeem some ETH to cause an LUSD redemption gain for stakers + await helpers.redeem( + ethers.utils.parseUnits("1000", 18), + contracts.STABILITY_POOL_ADDRESS, + userWallet, + liquity + ); + + const setEthGainId = 0; + const setLusdGainId = 0; + const ethGain = await liquity.staking.getPendingETHGain( + stakerDsa.address + ); + const lusdGain = await liquity.staking.getPendingLUSDGain( + stakerDsa.address + ); + + const claimStakingGainsSpell = { + connector: helpers.LIQUITY_CONNECTOR, + method: "claimStakingGains", + args: [setEthGainId, setLusdGainId], + }; + + const ethBalanceBefore = await ethers.provider.getBalance( + stakerDsa.address + ); + + // Claim gains + await stakerDsa + .connect(userWallet) + .cast( + ...encodeSpells([claimStakingGainsSpell]), + userWallet.address + ); + + const ethBalanceAfter = await ethers.provider.getBalance( + stakerDsa.address + ); + const lusdBalanceAfter = await liquity.lusdToken.balanceOf( + stakerDsa.address + ); + expect(ethBalanceAfter).to.eq(ethBalanceBefore.add(ethGain)); + expect(lusdBalanceAfter).to.eq(lusdGain); + }); + + it("claims gains from staking and stores them for other spells", async () => { + const stakerDsa = await buildDSAv2(userWallet.address); + const amount = ethers.utils.parseUnits("1000", 18); // 1000 LQTY + + // Stake lots of LQTY + await helpers.sendToken( + liquity.lqtyToken, + amount, + helpers.JUSTIN_SUN_ADDRESS, + stakerDsa.address + ); + const stakeSpell = { + connector: helpers.LIQUITY_CONNECTOR, + method: "stake", + args: [amount, 0, 0, 0, 0], + }; + await stakerDsa + .connect(userWallet) + .cast(...encodeSpells([stakeSpell]), userWallet.address); + + // Open a Trove to cause an ETH issuance gain for stakers + await helpers.createDsaTrove(dsaWallet0, userWallet, liquity); + + // Redeem some ETH to cause an LUSD redemption gain for stakers + await helpers.redeem( + ethers.utils.parseUnits("1000", 18), + contracts.STABILITY_POOL_ADDRESS, + userWallet, + liquity + ); + + const ethGain = await liquity.staking.getPendingETHGain( + stakerDsa.address + ); + const lusdGain = await liquity.staking.getPendingLUSDGain( + stakerDsa.address + ); + const lusdBalanceBefore = await liquity.lusdToken.balanceOf( + userWallet.address + ); + const ethBalanceBefore = await ethers.provider.getBalance( + userWallet.address + ); + const ethGainId = 111; + const lusdGainId = 222; + + const claimStakingGainsSpell = { + connector: helpers.LIQUITY_CONNECTOR, + method: "claimStakingGains", + args: [ethGainId, lusdGainId], + }; + + const withdrawEthSpell = { + connector: helpers.INSTADAPP_BASIC_V1_CONNECTOR, + method: "withdraw", + args: [helpers.ETH, 0, userWallet.address, ethGainId, 0], + }; + + const withdrawLusdSpell = { + connector: helpers.INSTADAPP_BASIC_V1_CONNECTOR, + method: "withdraw", + args: [ + liquity.lusdToken.address, + 0, + userWallet.address, + lusdGainId, + 0, + ], + }; + + const spells = [ + claimStakingGainsSpell, + withdrawEthSpell, + withdrawLusdSpell, + ]; + + // Claim gains + await stakerDsa + .connect(userWallet) + .cast(...encodeSpells(spells), userWallet.address, { + ce: 0, + }); + + const ethBalanceAfter = await ethers.provider.getBalance( + userWallet.address + ); + const lusdBalanceAfter = await liquity.lusdToken.balanceOf( + userWallet.address + ); + + expect( + ethBalanceAfter, + "User's ETH balance should have increased by the issuance gain from staking" + ).to.eq(ethBalanceBefore.add(ethGain)); + expect( + lusdBalanceAfter, + "User's LUSD balance should have increased by the redemption gain from staking" + ).to.eq(lusdBalanceBefore.add(lusdGain)); + }); + + it("returns Instadapp event name and data", async () => { + const stakerDsa = await buildDSAv2(userWallet.address); + const amount = ethers.utils.parseUnits("1000", 18); // 1000 LQTY + + // Stake lots of LQTY + await helpers.sendToken( + liquity.lqtyToken, + amount, + helpers.JUSTIN_SUN_ADDRESS, + stakerDsa.address + ); + const stakeSpell = { + connector: helpers.LIQUITY_CONNECTOR, + method: "stake", + args: [amount, 0, 0, 0, 0], + }; + await stakerDsa + .connect(userWallet) + .cast(...encodeSpells([stakeSpell]), userWallet.address); + + // Open a Trove to cause an ETH issuance gain for stakers + await helpers.createDsaTrove(dsaWallet0, userWallet, liquity); + + // Redeem some ETH to cause an LUSD redemption gain for stakers + await helpers.redeem( + ethers.utils.parseUnits("1000", 18), + contracts.STABILITY_POOL_ADDRESS, + userWallet, + liquity + ); + + const setEthGainId = 0; + const setLusdGainId = 0; + const ethGain = await liquity.staking.getPendingETHGain( + stakerDsa.address + ); + const lusdGain = await liquity.staking.getPendingLUSDGain( + stakerDsa.address + ); + + const claimStakingGainsSpell = { + connector: helpers.LIQUITY_CONNECTOR, + method: "claimStakingGains", + args: [setEthGainId, setLusdGainId], + }; + + // Claim gains + const claimGainsTx = await stakerDsa + .connect(userWallet) + .cast( + ...encodeSpells([claimStakingGainsSpell]), + userWallet.address + ); + + const receipt = await claimGainsTx.wait(); + + const castLogEvent = receipt.events.find( + (e: { event: string }) => e.event === "LogCast" + ).args; + const expectedEventParams = ethers.utils.defaultAbiCoder.encode( + ["address", "uint256", "uint256", "uint256", "uint256"], + [stakerDsa.address, ethGain, lusdGain, setEthGainId, setLusdGainId] + ); + expect(castLogEvent.eventNames[0]).eq( + "LogClaimStakingGains(address,uint256,uint256,uint256,uint256)" + ); + expect(castLogEvent.eventParams[0]).eq(expectedEventParams); + }); + }); + }); + }); +}); diff --git a/test/mainnet/mappings/mappings.test.ts b/test/mainnet/mappings/mappings.test.ts new file mode 100644 index 0000000..dfc6064 --- /dev/null +++ b/test/mainnet/mappings/mappings.test.ts @@ -0,0 +1,182 @@ +import hre, { ethers, network } from "hardhat"; +import chai from "chai"; +import chaiPromise from "chai-as-promised"; +import { solidity } from "ethereum-waffle"; +import type { Signer, Contract } from "ethers"; + +chai.use(chaiPromise); +chai.use(solidity); + +const { expect } = chai; + +const getMapping = (address: string, signer: Signer) => { + return ethers.getContractAt("InstaMappingController", address, signer); +}; + +describe("Test InstaMapping contract", () => { + let account: any, instaMaster: any; + let mappingAddress: any; + let masterMapping: any; + const indexInterfaceAddress = "0x2971AdFa57b20E5a416aE5a708A8655A9c74f723"; + const testRoleAddress = "0x2971AdFa57b20E5a416aE5a708A8655A9c74f723"; + + before("get signers", async () => { + await hre.network.provider.request({ + method: "hardhat_reset", + params: [ + { + forking: { + // @ts-ignore + jsonRpcUrl: hre.config.networks.hardhat.forking.url, + blockNumber: 12796965, + }, + }, + ], + }); + [account] = await ethers.getSigners(); + + const IndexContract = await ethers.getContractAt( + "contracts/mapping/InstaMappingController.sol:IndexInterface", + indexInterfaceAddress + ); + const masterAddress = await IndexContract.master(); + + await network.provider.request({ + method: "hardhat_impersonateAccount", + params: [masterAddress], + }); + + await network.provider.send("hardhat_setBalance", [ + masterAddress, + "0x1000000000000000000000000", + ]); + + instaMaster = await ethers.getSigner(masterAddress); + }); + + after(async () => { + await network.provider.request({ + method: "hardhat_stopImpersonatingAccount", + params: [instaMaster.address], + }); + }); + + beforeEach("deploy contract", async () => { + const mappingFactory = await ethers.getContractFactory( + "InstaMappingController" + ); + const mapping = await mappingFactory.deploy(); + + await mapping.deployed(); + mappingAddress = mapping.address; + + masterMapping = await getMapping(mappingAddress, instaMaster); + }); + + it("grant,revoke role should fail with non master signer", async () => { + const selfMapping = await getMapping(mappingAddress, account); + + await expect( + selfMapping.grantRole(testRoleAddress, account.address) + ).to.rejectedWith(/MappingController: sender must be master/); + + await expect( + selfMapping.revokeRole(testRoleAddress, account.address) + ).to.rejectedWith(/MappingController: sender must be master/); + }); + + it("hasRole should return false for roles not assigned to users", async () => { + expect(await masterMapping.hasRole(testRoleAddress, account.address)).to.eq( + false + ); + }); + + it("should grant roles", async () => { + await expect(masterMapping.grantRole(testRoleAddress, account.address)) + .to.emit(masterMapping, "RoleGranted") + .withArgs(testRoleAddress, account.address); + + expect(await masterMapping.hasRole(testRoleAddress, account.address)).to.eq( + true + ); + }); + + it("should revoke role", async () => { + // add a role first + await masterMapping.grantRole(testRoleAddress, account.address); + expect(await masterMapping.hasRole(testRoleAddress, account.address)).to.eq( + true + ); + + // then remove the role + await expect(masterMapping.revokeRole(testRoleAddress, account.address)) + .to.emit(masterMapping, "RoleRevoked") + .withArgs(testRoleAddress, account.address, instaMaster.address); + + expect(await masterMapping.hasRole(testRoleAddress, account.address)).to.eq( + false + ); + }); + + it("should renounce role only with the account not master", async () => { + // add a role first + await masterMapping.grantRole(testRoleAddress, account.address); + expect(await masterMapping.hasRole(testRoleAddress, account.address)).to.eq( + true + ); + + // then renounce the the role + await expect( + masterMapping.renounceRole(testRoleAddress, account.address) + ).to.rejectedWith(/MappingController: can only renounce roles for self/); + + const selfMapping = await getMapping(mappingAddress, account); + expect(await selfMapping.renounceRole(testRoleAddress, account.address)) + .to.emit(masterMapping, "RoleRevoked") + .withArgs(testRoleAddress, account.address, account.address); + + expect(await masterMapping.hasRole(testRoleAddress, account.address)).to.eq( + false + ); + }); + + it("should do role count properly", async () => { + expect(await masterMapping.getRoleMemberCount(testRoleAddress)).to.eq(0); + + await masterMapping.grantRole(testRoleAddress, account.address); + + expect(await masterMapping.getRoleMemberCount(testRoleAddress)).to.eq(1); + + await masterMapping.grantRole(testRoleAddress, instaMaster.address); + + expect(await masterMapping.getRoleMemberCount(testRoleAddress)).to.eq(2); + + await masterMapping.revokeRole(testRoleAddress, instaMaster.address); + + expect(await masterMapping.getRoleMemberCount(testRoleAddress)).to.eq(1); + }); + + it("should get member correctly by index", async () => { + await expect( + masterMapping.getRoleMember(testRoleAddress, 0) + ).to.rejectedWith(/EnumerableSet: index out of bounds/); + + await masterMapping.grantRole(testRoleAddress, account.address); + + expect(await masterMapping.getRoleMember(testRoleAddress, 0)).to.eq( + account.address + ); + + await masterMapping.grantRole(testRoleAddress, instaMaster.address); + + expect(await masterMapping.getRoleMember(testRoleAddress, 1)).to.eq( + instaMaster.address + ); + + await masterMapping.revokeRole(testRoleAddress, instaMaster.address); + + await expect( + masterMapping.getRoleMember(testRoleAddress, 1) + ).to.rejectedWith(/EnumerableSet: index out of bounds/); + }); +}); diff --git a/test/mainnet/morpho/morpho-aave-v3.test.ts b/test/mainnet/morpho/morpho-aave-v3.test.ts new file mode 100644 index 0000000..eb9bd94 --- /dev/null +++ b/test/mainnet/morpho/morpho-aave-v3.test.ts @@ -0,0 +1,484 @@ +import { expect } from "chai"; +import hre from "hardhat"; +import { abis } from "../../../scripts/constant/abis"; +import { addresses } from "../../../scripts/tests/mainnet/addresses"; +import { deployAndEnableConnector } from "../../../scripts/tests/deployAndEnableConnector"; +import { getMasterSigner } from "../../../scripts/tests/getMasterSigner"; +import { buildDSAv2 } from "../../../scripts/tests/buildDSAv2"; +import { ConnectV2MorphoAaveV3__factory, IERC20Minimal__factory } from "../../../typechain"; +import { parseEther, parseUnits } from "@ethersproject/units"; +import { encodeSpells } from "../../../scripts/tests/encodeSpells"; +import { dsaMaxValue, tokens } from "../../../scripts/tests/mainnet/tokens"; + +const { ethers } = hre; +import type { Signer, Contract } from "ethers"; + +const USDC = "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48"; +const ACC_USDC = "0xe78388b4ce79068e89bf8aa7f218ef6b9ab0e9d0"; +const Usdc = parseUnits("5000", 6); + +const DAI = "0x6b175474e89094c44da98b954eedeac495271d0f"; +const ACC_DAI = "0xcd6Eb888e76450eF584E8B51bB73c76ffBa21FF2"; +const Dai = parseUnits("1", 18); + +const WETH = "0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2"; + +const user = "0x41bc7d0687e6cea57fa26da78379dfdc5627c56d"; + +const token_usdc = new ethers.Contract(USDC, IERC20Minimal__factory.abi, ethers.provider); + +const token_dai = new ethers.Contract(DAI, IERC20Minimal__factory.abi, ethers.provider); + +const token_weth = new ethers.Contract(WETH, IERC20Minimal__factory.abi, ethers.provider); + +describe("Morpho-Aave-v3", function () { + const connectorName = "MORPHO-AAVE-V3-TEST-A"; + let connector: any; + + let wallet0: Signer, wallet1: Signer; + let dsaWallet0: any; + let dsaWallet1: any; + let instaConnectorsV2: Contract; + let masterSigner: Signer; + + before(async () => { + await hre.network.provider.request({ + method: "hardhat_reset", + params: [ + { + forking: { + // @ts-ignore + jsonRpcUrl: hre.config.networks.hardhat.forking.url, + blockNumber: 17544460 + } + } + ] + }); + [wallet0, wallet1] = await ethers.getSigners(); + masterSigner = await getMasterSigner(); + instaConnectorsV2 = await ethers.getContractAt(abis.core.connectorsV2, addresses.core.connectorsV2); + connector = await deployAndEnableConnector({ + connectorName, + contractArtifact: ConnectV2MorphoAaveV3__factory, + signer: masterSigner, + connectors: instaConnectorsV2 + }); + console.log("Connector address", connector.address); + }); + + it("should have contracts deployed", async () => { + expect(!!instaConnectorsV2.address).to.be.true; + expect(!!connector.address).to.be.true; + expect(!!(await masterSigner.getAddress())).to.be.true; + }); + + describe("DSA wallet setup", function () { + it("Should build DSA v2", async function () { + dsaWallet0 = await buildDSAv2(wallet0.getAddress()); + expect(!!dsaWallet0.address).to.be.true; + dsaWallet1 = await buildDSAv2(wallet0.getAddress()); + expect(!!dsaWallet1.address).to.be.true; + }); + + it("Deposit 1000 ETH into DSA wallet", async function () { + await wallet0.sendTransaction({ + to: dsaWallet0.address, + value: parseEther("1000") + }); + expect(await ethers.provider.getBalance(dsaWallet0.address)).to.be.gte(parseEther("1000")); + await wallet0.sendTransaction({ + to: dsaWallet1.address, + value: parseEther("1000") + }); + expect(await ethers.provider.getBalance(dsaWallet1.address)).to.be.gte(parseEther("1000")); + }); + + it("Deposit 5000 USDC into DSA wallet", async function () { + await hre.network.provider.request({ + method: "hardhat_impersonateAccount", + params: [ACC_USDC] + }); + + const signer_usdc = await ethers.getSigner(ACC_USDC); + await token_usdc.connect(signer_usdc).transfer(wallet0.getAddress(), Usdc); + + await hre.network.provider.request({ + method: "hardhat_stopImpersonatingAccount", + params: [ACC_USDC] + }); + + await token_usdc.connect(wallet0).transfer(dsaWallet0.address, Usdc); + + expect(await token_usdc.connect(masterSigner).balanceOf(dsaWallet0.address)).to.be.gte(parseUnits("5000", 6)); + }); + }); + + describe("Main", function () { + it("Should deposit 10 ETH", async function () { + const spells = [ + { + connector: connectorName, + method: "deposit", + args: [tokens.eth.address, "10000000000000000000", "0", "0"], // 10 ETH + }, + ]; + + const tx = await dsaWallet0 + .connect(wallet0) + .cast(...encodeSpells(spells), wallet1.getAddress()); + + await tx.wait(); + expect(expect(await ethers.provider.getBalance(dsaWallet0.address)).to.be.lte( + parseUnits('990', 18)) + ); + }) + + it("Should deposit 1 ETH with MaxIteration", async function () { + const spells = [ + { + connector: connectorName, + method: "depositWithMaxIterations", + args: [tokens.eth.address, "1000000000000000000", 5, "0", "0"], // 1 ETH + }, + ]; + + const tx = await dsaWallet0 + .connect(wallet0) + .cast(...encodeSpells(spells), wallet1.getAddress()); + + await tx.wait(); + expect(expect(await ethers.provider.getBalance(dsaWallet0.address)).to.be.lte( + parseUnits('989', 18)) + ); + }) + + it("Should deposit 10 ETH on behalf", async function () { + const spells = [ + { + connector: connectorName, + method: "depositOnBehalfWithMaxIterations", + args: [tokens.eth.address, "10000000000000000000", user, 4, "0", "0"], // 1 ETH + }, + ]; + + const tx = await dsaWallet0 + .connect(wallet0) + .cast(...encodeSpells(spells), wallet1.getAddress()); + + await tx.wait(); + expect(expect(await ethers.provider.getBalance(dsaWallet0.address)).to.be.lte( + parseUnits('979', 18)) + ); + }) + + it("Should deposit 1 ETH on behalf with MaxIteration", async function () { + const spells = [ + { + connector: connectorName, + method: "depositOnBehalfWithMaxIterations", + args: [tokens.eth.address, "1000000000000000000", user, 5, "0", "0"], // 1 ETH + }, + ]; + + const tx = await dsaWallet0 + .connect(wallet0) + .cast(...encodeSpells(spells), wallet1.getAddress()); + + await tx.wait(); + expect(expect(await ethers.provider.getBalance(dsaWallet0.address)).to.be.lte( + parseUnits('978', 18)) + ); + }) + + it("Should deposit collateral 2000 USDC", async function () { + const spells = [ + { + connector: connectorName, + method: "depositCollateral", + args: [tokens.usdc.address, "2000000000", "0", "0"], // 50 USDC + }, + ]; + + const tx = await dsaWallet0 + .connect(wallet0) + .cast(...encodeSpells(spells), wallet1.getAddress()); + + await tx.wait(); + expect(await token_usdc.connect(wallet0).balanceOf(dsaWallet0.address)).to.be.lte( + parseUnits('3000', 6) + ); + }) + + it("Should deposit collateral 2000 USDC on behalf with maxValue", async function () { + const spells = [ + { + connector: connectorName, + method: "depositCollateralOnBehalf", + args: [tokens.usdc.address, dsaMaxValue, user, "0", "0"], // ~3000 USDC + }, + ]; + + const tx = await dsaWallet0 + .connect(wallet0) + .cast(...encodeSpells(spells), wallet1.getAddress()); + + await tx.wait(); + expect(await token_usdc.connect(wallet0).balanceOf(dsaWallet0.address)).to.be.lte( + parseUnits('1', 6) + ); + }) + + it("Should withdraw 10 ETH", async function () { + const spells = [ + { + connector: connectorName, + method: "withdraw", + args: [tokens.eth.address, "10000000000000000000", "0", "0"], // 10 ETH + }, + ]; + + const tx = await dsaWallet0 + .connect(wallet0) + .cast(...encodeSpells(spells), wallet1.getAddress()); + + await tx.wait(); + expect(expect(await ethers.provider.getBalance(dsaWallet0.address)).to.be.gte( + parseUnits('978', 18)) + ); + }) + + it("Should withdraw on behalf of user with maxValue", async function () { + let ethBala = await ethers.provider.getBalance(user) + let wethBala = await token_weth.balanceOf(user) + + const spells = [ + { + connector: connectorName, + method: "withdrawOnBehalfWithMaxIterations", + args: [tokens.eth.address, dsaMaxValue, dsaWallet0.address, user, 4, "0", "0"], // Max ETH + }, + ]; + + const tx = await dsaWallet0 + .connect(wallet0) + .cast(...encodeSpells(spells), wallet1.getAddress()); + + await tx.wait(); + ethBala = await ethers.provider.getBalance(user) + wethBala = await token_weth.balanceOf(user) + + }) + + it("Should borrow ETH into DSA", async function () { + const balanceBefore = await ethers.provider.getBalance(dsaWallet0.address); + const spells = [ + { + connector: connectorName, + method: "borrow", + args: [tokens.eth.address, "500000000000000000", "0", "0"], // 0.5 WETH + }, + ]; + + const tx = await dsaWallet0 + .connect(wallet0) + .cast(...encodeSpells(spells), wallet1.getAddress()); + + await tx.wait(); + const balanceAfter = await ethers.provider.getBalance(dsaWallet0.address); + expect((balanceAfter).sub(balanceBefore)).to.be.gte(parseUnits('4.9', 17)); + }) + + it("Should borrow ETH into user", async function () { + const balance = await token_weth.balanceOf(user); + const spells = [ + { + connector: connectorName, + method: "borrowOnBehalfWithMaxIterations", + args: [tokens.eth.address, "200000000000000000", dsaWallet0.address, user, 4, "0", "0"], // 0.7 WETH + }, + ]; + + const tx = await dsaWallet0 + .connect(wallet0) + .cast(...encodeSpells(spells), wallet1.getAddress()); + + await tx.wait(); + expect((await token_weth.balanceOf(user)).sub(balance)) + .to.be.eq(parseUnits('2', 17)); + }) + + it("Should borrow WETH into wallet1 using iteration", async function () { + const balance = await token_weth.balanceOf(dsaWallet0.address); + const spells = [ + { + connector: connectorName, + method: "borrowWithMaxIterations", + args: [tokens.weth.address, "20000000000000000", 10, "0", "0"], // 0.02 WETH + }, + ]; + + const tx = await dsaWallet0 + .connect(wallet0) + .cast(...encodeSpells(spells), wallet1.getAddress()); + + await tx.wait(); + expect((await token_weth.balanceOf(dsaWallet0.address)).sub(balance)) + .to.be.eq(parseUnits('2', 16)); + }) + + it("Test withdrawCollateral ", async function () { + await hre.network.provider.request({ + method: 'hardhat_impersonateAccount', + params: [ACC_USDC], + }) + + const signer_usdc = await ethers.getSigner(ACC_USDC) + await token_usdc.connect(signer_usdc).transfer(dsaWallet0.address, parseUnits('500', 6)) + + await hre.network.provider.request({ + method: 'hardhat_stopImpersonatingAccount', + params: [ACC_USDC], + }) + + expect(await token_usdc.connect(masterSigner).balanceOf(dsaWallet0.address)).to.be.gte( + parseUnits('500', 6) + ); + + const balance = await token_usdc.balanceOf(dsaWallet0.address); + + const spells = [ + { + connector: connectorName, + method: "depositCollateral", + args: [tokens.usdc.address, "20000000", "0", "0"], // 20 USDC + }, + { + connector: connectorName, + method: "withdrawCollateral", + args: [tokens.usdc.address, "19000000", "0", "0"], // 19 USDC + }, + ]; + + const tx = await dsaWallet0 + .connect(wallet0) + .cast(...encodeSpells(spells), wallet1.getAddress()); + + await tx.wait(); + + }) + + it("Test withdrawCollateralOnBehalf with maxValue", async function () { + await hre.network.provider.request({ + method: "hardhat_impersonateAccount", + params: [ACC_USDC] + }); + + const signer_usdc = await ethers.getSigner(ACC_USDC); + await token_usdc.connect(signer_usdc).transfer(dsaWallet0.address, parseUnits("500", 6)); + + await hre.network.provider.request({ + method: "hardhat_stopImpersonatingAccount", + params: [ACC_USDC] + }); + + expect(await token_usdc.connect(masterSigner).balanceOf(dsaWallet0.address)).to.be.gte(parseUnits("500", 6)); + + const balance = await token_usdc.balanceOf(dsaWallet0.address); + + let spells = [ + { + connector: connectorName, + method: "approveManager", + args: [dsaWallet0.address, true] // 20 USDC + }, + ]; + + let tx = await dsaWallet1.connect(wallet0).cast(...encodeSpells(spells), wallet1.getAddress()); + await tx.wait(); + + spells = [ + { + connector: connectorName, + method: "depositCollateralOnBehalf", + args: [tokens.usdc.address, "20000000", dsaWallet1.address, "0", "0"] // 20 USDC + }, + { + connector: connectorName, + method: "withdrawCollateralOnBehalf", + args: [tokens.usdc.address, dsaMaxValue, dsaWallet1.address, user, "0", "0"] // 20 USDC + } + ]; + + tx = await dsaWallet0.connect(wallet0).cast(...encodeSpells(spells), wallet1.getAddress()); + + await tx.wait(); + + expect(await token_usdc.connect(masterSigner).balanceOf(dsaWallet0.address)).to.be.gte(parseUnits("499", 6)); + + }); + + it("Test payback with maxValue", async function () { + await hre.network.provider.request({ + method: 'hardhat_impersonateAccount', + params: [ACC_USDC], + }) + + const signer_usdc = await ethers.getSigner(ACC_USDC) + await token_usdc.connect(signer_usdc).transfer(dsaWallet0.address, parseUnits('500', 6)) + + await hre.network.provider.request({ + method: 'hardhat_stopImpersonatingAccount', + params: [ACC_USDC], + }) + + expect(await token_usdc.connect(masterSigner).balanceOf(dsaWallet0.address)).to.be.gte( + parseUnits('500', 6) + ); + + const balance = await token_usdc.balanceOf(dsaWallet0.address); + + const spells = [ + { + connector: connectorName, + method: "depositCollateral", + args: [tokens.usdc.address, "200000000", "0", "0"], // 2 ETH + }, + { + connector: connectorName, + method: "borrow", + args: [tokens.eth.address, "1000000000000000", "0", "0"], // 20 USDC + }, + { + connector: connectorName, + method: "payback", + args: [tokens.eth.address, dsaMaxValue, "0", "0"], // 20 USDC + }, + ]; + + const tx = await dsaWallet0 + .connect(wallet0) + .cast(...encodeSpells(spells), wallet1.getAddress()); + + await tx.wait(); + + expect(await token_usdc.connect(masterSigner).balanceOf(dsaWallet0.address)).to.be.gte( + parseUnits('499', 6) + ); + }) + + it("approve manger", async () => { + const spells = [ + { + connector: connectorName, + method: "approveManager", + args: [user, true], + }, + ] + const tx = await dsaWallet0 + .connect(wallet0) + .cast(...encodeSpells(spells), wallet1.getAddress()); + + await tx.wait(); + }) + }); +}); diff --git a/test/mainnet/morpho/morpho-aave.test.ts b/test/mainnet/morpho/morpho-aave.test.ts new file mode 100644 index 0000000..4d8dd22 --- /dev/null +++ b/test/mainnet/morpho/morpho-aave.test.ts @@ -0,0 +1,294 @@ +import { expect } from "chai"; +import hre from "hardhat"; +import { abis } from "../../../scripts/constant/abis"; +import { addresses } from "../../../scripts/tests/mainnet/addresses"; +import { deployAndEnableConnector } from "../../../scripts/tests/deployAndEnableConnector"; +import { getMasterSigner } from "../../../scripts/tests/getMasterSigner"; +import { buildDSAv2 } from "../../../scripts/tests/buildDSAv2"; +import { ConnectV2MorphoAaveV2__factory, IERC20Minimal__factory } from "../../../typechain"; +import { parseEther, parseUnits } from "@ethersproject/units"; +import { encodeSpells } from "../../../scripts/tests/encodeSpells"; +import { dsaMaxValue, tokens } from "../../../scripts/tests/mainnet/tokens"; +const { ethers } = hre; +import type { Signer, Contract } from "ethers"; + +const USDC = '0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48' +const ACC_USDC = '0xe78388b4ce79068e89bf8aa7f218ef6b9ab0e9d0' +const Usdc = parseUnits('500', 6) + +const DAI = '0x6b175474e89094c44da98b954eedeac495271d0f' +const ACC_DAI = '0xcd6Eb888e76450eF584E8B51bB73c76ffBa21FF2' +const Dai = parseUnits('1', 18) + +const user = "0x41bc7d0687e6cea57fa26da78379dfdc5627c56d" + +const token_usdc = new ethers.Contract( + USDC, + IERC20Minimal__factory.abi, + ethers.provider, +) + +const token_dai = new ethers.Contract( + DAI, + IERC20Minimal__factory.abi, + ethers.provider, +) + +describe("Morpho-Aave", function () { + const connectorName = "MORPHO-AAVE-TEST-A"; + let connector: any; + + let wallet0: Signer, wallet1:Signer; + let dsaWallet0: any; + let instaConnectorsV2: Contract; + let masterSigner: Signer; + + before(async () => { + await hre.network.provider.request({ + method: "hardhat_reset", + params: [ + { + forking: { + // @ts-ignore + jsonRpcUrl: hre.config.networks.hardhat.forking.url, + blockNumber: 15714501, + }, + }, + ], + }); + [wallet0, wallet1] = await ethers.getSigners(); + masterSigner = await getMasterSigner(); + instaConnectorsV2 = await ethers.getContractAt( + abis.core.connectorsV2, + addresses.core.connectorsV2 + ); + connector = await deployAndEnableConnector({ + connectorName, + contractArtifact: ConnectV2MorphoAaveV2__factory, + signer: masterSigner, + connectors: instaConnectorsV2, + }); + console.log("Connector address", connector.address); + }); + + it("should have contracts deployed", async () => { + expect(!!instaConnectorsV2.address).to.be.true; + expect(!!connector.address).to.be.true; + expect(!!(await masterSigner.getAddress())).to.be.true; + }); + + describe("DSA wallet setup", function () { + it("Should build DSA v2", async function () { + dsaWallet0 = await buildDSAv2(wallet0.getAddress()); + expect(!!dsaWallet0.address).to.be.true; + }); + + it("Deposit 1000 ETH into DSA wallet", async function () { + await wallet0.sendTransaction({ + to: dsaWallet0.address, + value: parseEther("1000"), + }); + expect(await ethers.provider.getBalance(dsaWallet0.address)).to.be.gte( + parseEther("1000") + ); + }); + + it("Deposit 500 USDC into DSA wallet", async function () { + + await hre.network.provider.request({ + method: 'hardhat_impersonateAccount', + params: [ACC_USDC], + }) + + const signer_usdc = await ethers.getSigner(ACC_USDC) + await token_usdc.connect(signer_usdc).transfer(wallet0.getAddress(), Usdc) + + await hre.network.provider.request({ + method: 'hardhat_stopImpersonatingAccount', + params: [ACC_USDC], + }) + + await token_usdc.connect(wallet0).transfer(dsaWallet0.address, Usdc); + + expect(await token_usdc.connect(masterSigner).balanceOf(dsaWallet0.address)).to.be.gte( + parseUnits('500', 6) + ); + }); + + it("Deposit 1 DAI into DSA wallet", async function () { + + await hre.network.provider.request({ + method: 'hardhat_impersonateAccount', + params: [ACC_DAI], + }) + + const signer_dai = await ethers.getSigner(ACC_DAI) + await token_dai.connect(signer_dai).transfer(wallet0.getAddress(), Dai) + + await hre.network.provider.request({ + method: 'hardhat_stopImpersonatingAccount', + params: [ACC_DAI], + }) + + await token_dai.connect(wallet0).transfer(dsaWallet0.address, Dai); + + expect(await token_dai.connect(masterSigner).balanceOf(dsaWallet0.address)).to.be.gte( + parseUnits('1', 18) + ); + }); + }); + + describe("Main", function () { + + it("Should deposit 10 ETH", async function () { + const spells = [ + { + connector: connectorName, + method: "deposit", + args: [tokens.eth.address, tokens.eth.aTokenAddress, "10000000000000000000", "0", "0"], // 10 ETH + }, + ]; + + const tx = await dsaWallet0 + .connect(wallet0) + .cast(...encodeSpells(spells), wallet1.getAddress()); + + await tx.wait(); + expect(expect(await ethers.provider.getBalance(dsaWallet0.address)).to.be.lte( + parseUnits('990', 18)) + ); + }) + + it("Should deposit 10 USDC", async function () { + const spells = [ + { + connector: connectorName, + method: "deposit", + args: [tokens.usdc.address, tokens.usdc.aTokenAddress, "10000000", "0", "0"], // 10 USDC + }, + ]; + + const tx = await dsaWallet0 + .connect(wallet0) + .cast(...encodeSpells(spells), wallet1.getAddress()); + + await tx.wait(); + expect(await token_usdc.connect(wallet0).balanceOf(dsaWallet0.address)).to.be.lte( + parseUnits('490', 18) + ); + }) + + it("Should deposit 100 USDC on behalf", async function () { + const spells = [ + { + connector: connectorName, + method: "depositOnBehalf", + args: [tokens.usdc.address, tokens.usdc.aTokenAddress, user, "100000000", "0", "0"], // 100 USDC + }, + ]; + + const tx = await dsaWallet0 + .connect(wallet0) + .cast(...encodeSpells(spells), wallet1.getAddress()); + + await tx.wait(); + expect(await token_usdc.connect(wallet0).balanceOf(dsaWallet0.address)).to.be.lte( + parseUnits('390', 18) + ); + }) + + it("Should borrow DAI into DSA", async function () { + const spells = [ + { + connector: connectorName, + method: "borrow", + args: [tokens.dai.address, tokens.dai.aTokenAddress, "10000000000000000000", "0", "0"], // 10 DAI + }, + ]; + + const tx = await dsaWallet0 + .connect(wallet0) + .cast(...encodeSpells(spells), wallet1.getAddress()); + + await tx.wait(); + expect(await token_dai.connect(masterSigner).balanceOf(dsaWallet0.address)) + .to.be.gte(parseUnits('11', 18)); + }) + + it("Should payback DAI MAX", async function () { + const spells = [ + { + connector: connectorName, + method: "payback", + args: [tokens.dai.address, tokens.dai.aTokenAddress, dsaMaxValue, "0", "0"], // Max DAI + }, + ]; + + const tx = await dsaWallet0 + .connect(wallet0) + .cast(...encodeSpells(spells), wallet1.getAddress()); + + await tx.wait(); + expect(await token_dai.connect(masterSigner).balanceOf(dsaWallet0.address)).to.be.lte( + parseUnits('1', 18) + ); + }) + + it("Should payback ETH on behalf", async function () { + const spells = [ + { + connector: connectorName, + method: "paybackOnBehalf", + args: [tokens.eth.address, tokens.eth.aTokenAddress, user, dsaMaxValue, "0", "0"], // Max ETH + }, + ]; + + const tx = await dsaWallet0 + .connect(wallet0) + .cast(...encodeSpells(spells), wallet1.getAddress()); + + await tx.wait(); + expect(await ethers.provider.getBalance(dsaWallet0.address)).to.be.lte( + parseUnits('125', 18) + ); + }) + + it("Should withdraw ETH max", async function () { + const spells = [ + { + connector: connectorName, + method: "withdraw", + args: [tokens.eth.address, tokens.eth.aTokenAddress, dsaMaxValue, "0", "0"], // Max ETH + }, + ]; + + const tx = await dsaWallet0 + .connect(wallet0) + .cast(...encodeSpells(spells), wallet1.getAddress()); + + await tx.wait(); + expect(expect(await ethers.provider.getBalance(dsaWallet0.address)).to.be.gte( + parseUnits('134', 18)) + ); + }) + + it("Should withdraw 8 USDC", async function () { + const spells = [ + { + connector: connectorName, + method: "withdraw", + args: [tokens.usdc.address, tokens.usdc.aTokenAddress, "8000000", "0", "0"], // 8 USDC + }, + ]; + + const tx = await dsaWallet0 + .connect(wallet0) + .cast(...encodeSpells(spells), wallet1.getAddress()); + + await tx.wait(); + expect(expect(await token_usdc.connect(wallet0).balanceOf(dsaWallet0.address)).to.be.gte( + parseUnits('398', 6)) + ); + }) + }); +}); diff --git a/test/mainnet/morpho/morpho-compound.test.ts b/test/mainnet/morpho/morpho-compound.test.ts new file mode 100644 index 0000000..461f59b --- /dev/null +++ b/test/mainnet/morpho/morpho-compound.test.ts @@ -0,0 +1,272 @@ +import { expect } from "chai"; +import hre from "hardhat"; +import { abis } from "../../../scripts/constant/abis"; +import { addresses } from "../../../scripts/tests/mainnet/addresses"; +import { deployAndEnableConnector } from "../../../scripts/tests/deployAndEnableConnector"; +import { getMasterSigner } from "../../../scripts/tests/getMasterSigner"; +import { buildDSAv2 } from "../../../scripts/tests/buildDSAv2"; +import { ConnectV2MorphoCompound__factory, IERC20Minimal__factory } from "../../../typechain"; +import { parseEther, parseUnits } from "@ethersproject/units"; +import { encodeSpells } from "../../../scripts/tests/encodeSpells"; +import { dsaMaxValue, tokens } from "../../../scripts/tests/mainnet/tokens"; +const { ethers } = hre; +import type { Signer, Contract } from "ethers"; + +const USDC = '0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48' +const ACC_USDC = '0xe78388b4ce79068e89bf8aa7f218ef6b9ab0e9d0' +const Usdc = parseUnits('500', 6) + +const DAI = '0x6b175474e89094c44da98b954eedeac495271d0f' +const ACC_DAI = '0xcd6Eb888e76450eF584E8B51bB73c76ffBa21FF2' +const Dai = parseUnits('400000', 18) + +const user = '0x5dd596c901987a2b28c38a9c1dfbf86fffc15d77' + +const token_usdc = new ethers.Contract( + USDC, + IERC20Minimal__factory.abi, + ethers.provider, +) + +const token_dai = new ethers.Contract( + DAI, + IERC20Minimal__factory.abi, + ethers.provider, +) + +describe("Morpho-Compound", function () { + const connectorName = "MORPHO-COMPOUND-TEST-A"; + let connector: any; + + let wallet0: Signer, wallet1:Signer; + let dsaWallet0: any; + let instaConnectorsV2: Contract; + let masterSigner: Signer; + + before(async () => { + await hre.network.provider.request({ + method: "hardhat_reset", + params: [ + { + forking: { + // @ts-ignore + jsonRpcUrl: hre.config.networks.hardhat.forking.url, + blockNumber: 15714501, + }, + }, + ], + }); + [wallet0, wallet1] = await ethers.getSigners(); + masterSigner = await getMasterSigner(); + instaConnectorsV2 = await ethers.getContractAt( + abis.core.connectorsV2, + addresses.core.connectorsV2 + ); + connector = await deployAndEnableConnector({ + connectorName, + contractArtifact: ConnectV2MorphoCompound__factory, + signer: masterSigner, + connectors: instaConnectorsV2, + }); + console.log("Connector address", connector.address); + }); + + it("should have contracts deployed", async () => { + expect(!!instaConnectorsV2.address).to.be.true; + expect(!!connector.address).to.be.true; + expect(!!(await masterSigner.getAddress())).to.be.true; + }); + + describe("DSA wallet setup", function () { + it("Should build DSA v2", async function () { + dsaWallet0 = await buildDSAv2(wallet0.getAddress()); + expect(!!dsaWallet0.address).to.be.true; + }); + + it("Deposit 10 ETH into DSA wallet", async function () { + await wallet0.sendTransaction({ + to: dsaWallet0.address, + value: parseEther("10"), + }); + expect(await ethers.provider.getBalance(dsaWallet0.address)).to.be.gte( + parseEther("10") + ); + }); + + it("Deposit 500 USDC into DSA wallet", async function () { + + await hre.network.provider.request({ + method: 'hardhat_impersonateAccount', + params: [ACC_USDC], + }) + + const signer_usdc = await ethers.getSigner(ACC_USDC) + await token_usdc.connect(signer_usdc).transfer(wallet0.getAddress(), Usdc) + + await hre.network.provider.request({ + method: 'hardhat_stopImpersonatingAccount', + params: [ACC_USDC], + }) + + await token_usdc.connect(wallet0).transfer(dsaWallet0.address, Usdc); + + expect(await token_usdc.connect(masterSigner).balanceOf(dsaWallet0.address)).to.be.gte( + parseUnits('500', 6) + ); + }); + + it("Deposit 400000 DAI into DSA wallet", async function () { + + await hre.network.provider.request({ + method: 'hardhat_impersonateAccount', + params: [ACC_DAI], + }) + + const signer_dai = await ethers.getSigner(ACC_DAI) + await token_dai.connect(signer_dai).transfer(wallet0.getAddress(), Dai) + + await hre.network.provider.request({ + method: 'hardhat_stopImpersonatingAccount', + params: [ACC_DAI], + }) + + await token_dai.connect(wallet0).transfer(dsaWallet0.address, Dai); + + expect(await token_dai.connect(masterSigner).balanceOf(dsaWallet0.address)).to.be.gte( + parseUnits('400000', 18) + ); + }); + }); + + describe("Main Morpho Compound", function () { + + it("Should deposit ETH max", async function () { + const spells = [ + { + connector: connectorName, + method: "deposit", + args: [tokens.eth.address, tokens.eth.cTokenAddress, dsaMaxValue, "0", "0"], // 10 ETH + }, + ]; + + const tx = await dsaWallet0 + .connect(wallet0) + .cast(...encodeSpells(spells), wallet1.getAddress()); + + await tx.wait(); + expect(expect(await ethers.provider.getBalance(dsaWallet0.address)).to.be.lte( + parseUnits('0', 18)) + ); + }) + + it("Should deposit 10 USDC", async function () { + const spells = [ + { + connector: connectorName, + method: "deposit", + args: [tokens.usdc.address, tokens.usdc.cTokenAddress, "10000000", "0", "0"], // 10 USDC + }, + ]; + + const tx = await dsaWallet0 + .connect(wallet0) + .cast(...encodeSpells(spells), wallet1.getAddress()); + + await tx.wait(); + expect(await token_usdc.connect(wallet0).balanceOf(dsaWallet0.address)).to.be.lte( + parseUnits('490', 18) + ); + }) + + it("Should deposit 100 USDC on behalf", async function () { + const spells = [ + { + connector: connectorName, + method: "depositOnBehalf", + args: [tokens.usdc.address, tokens.usdc.cTokenAddress, user, "100000000", "0", "0"], // 100 USDC + }, + ]; + + const tx = await dsaWallet0 + .connect(wallet0) + .cast(...encodeSpells(spells), wallet1.getAddress()); + + await tx.wait(); + expect(await token_usdc.connect(wallet0).balanceOf(dsaWallet0.address)).to.be.lte( + parseUnits('390', 18) + ); + }) + + it("Should borrow DAI into DSA", async function () { + const spells = [ + { + connector: connectorName, + method: "borrow", + args: [tokens.dai.address, tokens.dai.cTokenAddress, "10000000000000000000", "0", "0"], // 10 DAI + }, + ]; + + const tx = await dsaWallet0 + .connect(wallet0) + .cast(...encodeSpells(spells), wallet1.getAddress()); + + await tx.wait(); + expect(await token_dai.connect(masterSigner).balanceOf(dsaWallet0.address)) + .to.be.gte(parseUnits('400010', 18)); + }) + + it("Should payback DAI max", async function () { + const spells = [ + { + connector: connectorName, + method: "payback", + args: [tokens.dai.address, tokens.dai.cTokenAddress, dsaMaxValue, "0", "0"], // Max DAI + }, + ]; + + const tx = await dsaWallet0 + .connect(wallet0) + .cast(...encodeSpells(spells), wallet1.getAddress()); + + await tx.wait(); + expect(await token_dai.connect(masterSigner).balanceOf(dsaWallet0.address)).to.be.lte( + parseUnits('400000', 18) + ); + }) + + it("Should payback DAI on behalf", async function () { + const spells = [ + { + connector: connectorName, + method: "paybackOnBehalf", + args: [tokens.dai.address, tokens.dai.cTokenAddress, user, dsaMaxValue, "0", "0"], + }, + ]; + + const tx = await dsaWallet0 + .connect(wallet0) + .cast(...encodeSpells(spells), wallet1.getAddress()); + + await tx.wait(); + }) + + it("Should withdraw ETH max", async function () { + const spells = [ + { + connector: connectorName, + method: "withdraw", + args: [tokens.eth.address, tokens.eth.cTokenAddress, dsaMaxValue, "0", "0"], // Max ETH + }, + ]; + + const tx = await dsaWallet0 + .connect(wallet0) + .cast(...encodeSpells(spells), wallet1.getAddress()); + + await tx.wait(); + expect(expect(await ethers.provider.getBalance(dsaWallet0.address)).to.be.gte( + parseUnits('10', 18)) + ); + }) + }); +}); diff --git a/test/mainnet/mstable/mstable.helpers.ts b/test/mainnet/mstable/mstable.helpers.ts new file mode 100644 index 0000000..1dde163 --- /dev/null +++ b/test/mainnet/mstable/mstable.helpers.ts @@ -0,0 +1,160 @@ +import hre, { ethers } from "hardhat"; +import { IERC20Minimal__factory } from "../../../typechain"; +import { BigNumber as BN } from "ethers"; + +export const DEAD_ADDRESS = "0x0000000000000000000000000000000000000001"; +export const ZERO_ADDRESS = ethers.constants.AddressZero; + +export const DEFAULT_DECIMALS = 18; + +export const ZERO = BN.from(0); +export const ONE_MIN = BN.from(60); +export const TEN_MINS = BN.from(60 * 10); +export const ONE_HOUR = BN.from(60 * 60); +export const ONE_DAY = BN.from(60 * 60 * 24); +export const FIVE_DAYS = BN.from(60 * 60 * 24 * 5); +export const TEN_DAYS = BN.from(60 * 60 * 24 * 10); +export const ONE_WEEK = BN.from(60 * 60 * 24 * 7); +export const ONE_YEAR = BN.from(60 * 60 * 24 * 365); + +export const connectorName = "MStable"; + +interface TokenData { + tokenAddress: string; + tokenWhaleAddress?: string; + feederPool?: string; +} + +export const toEther = (amount: BN) => ethers.utils.formatEther(amount); + +export const getToken = (tokenSymbol: string): TokenData => { + switch (tokenSymbol) { + case "MTA": + return { + tokenAddress: "0xa3BeD4E1c75D00fa6f4E5E6922DB7261B5E9AcD2" + }; + case "mUSD": + return { + tokenAddress: "0xe2f2a5c287993345a840db3b0845fbc70f5935a5", + tokenWhaleAddress: "0x503828976D22510aad0201ac7EC88293211D23Da" + }; + + case "DAI": + return { + tokenAddress: "0x6b175474e89094c44da98b954eedeac495271d0f", + tokenWhaleAddress: "0xF977814e90dA44bFA03b6295A0616a897441aceC" + }; + case "USDC": + return { + tokenAddress: "0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48" + }; + case "imUSD": + return { + tokenAddress: "0x30647a72dc82d7fbb1123ea74716ab8a317eac19" + }; + + case "imUSDVault": + return { + tokenAddress: "0x78BefCa7de27d07DC6e71da295Cc2946681A6c7B" + }; + + // Feeder Asset + case "alUSD": + return { + tokenAddress: "0xbc6da0fe9ad5f3b0d58160288917aa56653660e9", + tokenWhaleAddress: "0x115f95c00e8cf2f5C57250caA555A6B4e50B446b", + feederPool: "0x4eaa01974B6594C0Ee62fFd7FEE56CF11E6af936" + }; + + default: + throw new Error(`Token ${tokenSymbol} not supported`); + } +}; + +export const sendToken = async (token: string, amount: any, from: string, to: string): Promise => { + await hre.network.provider.request({ + method: "hardhat_impersonateAccount", + params: [from] + }); + const [signer] = await ethers.getSigners(); + const sender = hre.ethers.provider.getSigner(from); + + await signer.sendTransaction({ + to: from, + value: ethers.utils.parseEther("1") + }); + + return await IERC20Minimal__factory.connect(token, sender).transfer(to, amount); +}; + +export const fundWallet = async (token: string, amount: any, to: string) => { + const { tokenAddress, tokenWhaleAddress } = getToken(token); + await sendToken(tokenAddress, amount, tokenWhaleAddress!, to); +}; + +export const calcMinOut = (amount: BN, slippage: number): BN => { + const value = simpleToExactAmount(1 - slippage); + const minOut = amount.mul(value).div(ethers.BigNumber.from(10).pow(DEFAULT_DECIMALS)); + return minOut; +}; + +export const simpleToExactAmount = (amount: number | string | BN, decimals: number | BN = DEFAULT_DECIMALS): BN => { + let amountString = amount.toString(); + const decimalsBN = BN.from(decimals); + + if (decimalsBN.gt(100)) { + throw new Error(`Invalid decimals amount`); + } + + const scale = BN.from(10).pow(decimals); + const scaleString = scale.toString(); + + // Is it negative? + const negative = amountString.substring(0, 1) === "-"; + if (negative) { + amountString = amountString.substring(1); + } + + if (amountString === ".") { + throw new Error(`Error converting number ${amountString} to precise unit, invalid value`); + } + + // Split it into a whole and fractional part + // eslint-disable-next-line prefer-const + let [whole, fraction, ...rest] = amountString.split("."); + if (rest.length > 0) { + throw new Error(`Error converting number ${amountString} to precise unit, too many decimal points`); + } + + if (!whole) { + whole = "0"; + } + if (!fraction) { + fraction = "0"; + } + + if (fraction.length > scaleString.length - 1) { + throw new Error(`Error converting number ${amountString} to precise unit, too many decimal places`); + } + + while (fraction.length < scaleString.length - 1) { + fraction += "0"; + } + + const wholeBN = BN.from(whole); + const fractionBN = BN.from(fraction); + let result = wholeBN.mul(scale).add(fractionBN); + + if (negative) { + result = result.mul("-1"); + } + + return result; +}; + +export const advanceBlock = async (): Promise => ethers.provider.send("evm_mine", []); + +export const increaseTime = async (length: BN | number): Promise => { + await ethers.provider.send("evm_increaseTime", [BN.from(length).toNumber()]); + await advanceBlock(); +}; diff --git a/test/mainnet/mstable/mstable.test.ts b/test/mainnet/mstable/mstable.test.ts new file mode 100644 index 0000000..8ca09a8 --- /dev/null +++ b/test/mainnet/mstable/mstable.test.ts @@ -0,0 +1,234 @@ +import { expect } from "chai"; +import hre from "hardhat"; +const { waffle, ethers } = hre; +const { provider } = waffle; + +import { deployAndEnableConnector } from "../../../scripts/tests/deployAndEnableConnector"; +import { buildDSAv2 } from "../../../scripts/tests/buildDSAv2"; +import { encodeSpells } from "../../../scripts/tests/encodeSpells"; +import { getMasterSigner } from "../../../scripts/tests/getMasterSigner"; + +import { addresses } from "../../../scripts/tests/mainnet/addresses"; +import { abis } from "../../../scripts/constant/abis"; +import type { Signer, Contract } from "ethers"; + +import { ConnectV2mStable__factory, IERC20Minimal__factory, IERC20Minimal } from "../../../typechain"; + +import { executeAndAssertDeposit, executeAndAssertSwap, executeAndAssertWithdraw } from "./mstable.utils"; + +import { + fundWallet, + getToken, + simpleToExactAmount, + DEAD_ADDRESS, + calcMinOut, + ONE_DAY, + increaseTime, + connectorName, + toEther +} from "./mstable.helpers"; + +describe("MStable", async () => { + let dsaWallet0: Contract; + let masterSigner: Signer; + let instaConnectorsV2: Contract; + let connector: Contract; + + let mtaToken: IERC20Minimal = IERC20Minimal__factory.connect(getToken("MTA").tokenAddress, provider); + let mUsdToken: IERC20Minimal = IERC20Minimal__factory.connect(getToken("mUSD").tokenAddress, provider); + let imUsdToken: IERC20Minimal = IERC20Minimal__factory.connect(getToken("imUSD").tokenAddress, provider); + let imUsdVault: IERC20Minimal = IERC20Minimal__factory.connect(getToken("imUSDVault").tokenAddress, provider); + + let daiToken: IERC20Minimal = IERC20Minimal__factory.connect(getToken("DAI").tokenAddress, provider); + let usdcToken: IERC20Minimal = IERC20Minimal__factory.connect(getToken("USDC").tokenAddress, provider); + let alusdToken: IERC20Minimal = IERC20Minimal__factory.connect(getToken("alUSD").tokenAddress, provider); + + const wallets = provider.getWallets(); + const [wallet0, wallet1, wallet2, wallet3] = wallets; + + describe("DSA wallet", async () => { + const fundAmount = simpleToExactAmount(10000); + + const setup = async () => { + await hre.network.provider.request({ + method: "hardhat_reset", + params: [ + { + forking: { + // @ts-ignore + jsonRpcUrl: hre.config.networks.hardhat.forking.url, + blockNumber: 13905885 + } + } + ] + }); + + masterSigner = await getMasterSigner(); + instaConnectorsV2 = await ethers.getContractAt(abis.core.connectorsV2, addresses.core.connectorsV2); + connector = await deployAndEnableConnector({ + connectorName, + contractArtifact: ConnectV2mStable__factory, + signer: masterSigner, + connectors: instaConnectorsV2 + }); + + console.log("Connector address", connector.address); + + dsaWallet0 = await buildDSAv2(wallet0.address); + + await wallet0.sendTransaction({ + to: dsaWallet0.address, + value: simpleToExactAmount(10) + }); + + await fundWallet("mUSD", fundAmount, dsaWallet0.address); + await fundWallet("DAI", fundAmount, dsaWallet0.address); + await fundWallet("alUSD", fundAmount, dsaWallet0.address); + }; + + describe("Deploy", async () => { + before(async () => { + await setup(); + }); + + it("Should deploy properly", async () => { + expect(instaConnectorsV2.address).to.be.properAddress; + expect(connector.address).to.be.properAddress; + expect(await masterSigner.getAddress()).to.be.properAddress; + + expect(dsaWallet0.address).to.be.properAddress; + }); + it("Should fund the wallet", async () => { + expect(await ethers.provider.getBalance(dsaWallet0.address)).to.be.gte(ethers.utils.parseEther("10")); + + expect(await mUsdToken.balanceOf(dsaWallet0.address)).to.be.gte(fundAmount); + expect(await daiToken.balanceOf(dsaWallet0.address)).to.be.gte(fundAmount); + expect(await alusdToken.balanceOf(dsaWallet0.address)).to.be.gte(fundAmount); + }); + it("Should not have vault tokens prior", async () => { + // No deposits prior + expect(await imUsdToken.balanceOf(dsaWallet0.address)).to.be.eq(0); + expect(await imUsdVault.balanceOf(dsaWallet0.address)).to.be.eq(0); + }); + }); + + describe("Main SAVE", async () => { + before(async () => { + await setup(); + }); + it("Should deposit mUSD to Vault successfully", async () => { + const depositAmount = simpleToExactAmount(100); + const minOut = depositAmount; + + await executeAndAssertDeposit("deposit", mUsdToken, depositAmount, dsaWallet0, wallet0, [minOut, true]); + }); + it("Should deposit DAI to Vault successfully (mUSD bAsset)", async () => { + const depositAmount = simpleToExactAmount(100); + const minOut = calcMinOut(depositAmount, 0.02); + + await executeAndAssertDeposit("deposit", daiToken, depositAmount, dsaWallet0, wallet0, [minOut, true]); + }); + it("Should deposit alUSD to Vault successfully (via Feeder Pool)", async () => { + const depositAmount = simpleToExactAmount(100); + const minOut = calcMinOut(depositAmount, 0.02); + const path = getToken("alUSD").feederPool; + + await executeAndAssertDeposit("depositViaSwap", alusdToken, depositAmount, dsaWallet0, wallet0, [ + minOut, + path, + true + ]); + }); + it("Should withdraw from Vault to mUSD", async () => { + const withdrawAmount = simpleToExactAmount(100); + const minOut = simpleToExactAmount(1); + + await executeAndAssertWithdraw("withdraw", mUsdToken, withdrawAmount, dsaWallet0, wallet0, [minOut, true]); + }); + it("Should withdraw from Vault to DAI (mUSD bAsset)", async () => { + const withdrawAmount = simpleToExactAmount(100); + const minOut = simpleToExactAmount(1); + + await executeAndAssertWithdraw("withdraw", mUsdToken, withdrawAmount, dsaWallet0, wallet0, [minOut, true]); + }); + it("Should withdraw from Vault to alUSD (via Feeder Pool)", async () => { + const withdrawAmount = simpleToExactAmount(100); + const minOut = simpleToExactAmount(1); + const path = getToken("alUSD").feederPool; + + await executeAndAssertWithdraw("withdrawViaSwap", alusdToken, withdrawAmount, dsaWallet0, wallet0, [ + minOut, + path, + true + ]); + }); + it("Should claim Rewards", async () => { + const mtaBalanceBefore = await mtaToken.balanceOf(dsaWallet0.address); + console.log("MTA balance before: ", toEther(mtaBalanceBefore)); + + // Wait a bit and let the rewards accumulate + await increaseTime(ONE_DAY); + + const spells = [ + { + connector: connectorName, + method: "claimRewards", + args: [0, 0] + } + ]; + + const tx = await dsaWallet0.connect(wallet0).cast(...encodeSpells(spells), DEAD_ADDRESS); + + const mtaBalanceAfter = await mtaToken.balanceOf(dsaWallet0.address); + console.log("MTA balance after: ", toEther(mtaBalanceAfter)); + + expect(mtaBalanceAfter).to.be.gt(mtaBalanceBefore); + }); + }); + describe("Main SWAP", async () => { + before(async () => { + await setup(); + }); + it("Should swap mUSD to bAsset (redeem)", async () => { + const swapAmount = simpleToExactAmount(100); + await executeAndAssertSwap("swap", mUsdToken, 18, daiToken, 18, swapAmount, dsaWallet0, wallet0); + }); + it("Should swap mUSD to fAsset (via feeder pool)", async () => { + const swapAmount = simpleToExactAmount(100); + const path = getToken("alUSD").feederPool; + await executeAndAssertSwap("swapViaFeeder", mUsdToken, 18, alusdToken, 18, swapAmount, dsaWallet0, wallet0, [ + path + ]); + }); + it("Should swap bAsset to mUSD (mint)", async () => { + const swapAmount = simpleToExactAmount(100); + await executeAndAssertSwap("swap", daiToken, 18, mUsdToken, 18, swapAmount, dsaWallet0, wallet0); + }); + it("Should swap bAsset to bAsset (swap)", async () => { + const swapAmount = simpleToExactAmount(100); + await executeAndAssertSwap("swap", daiToken, 18, usdcToken, 6, swapAmount, dsaWallet0, wallet0); + }); + it("Should swap bAsset to fAsset (via feeder)", async () => { + const swapAmount = simpleToExactAmount(100); + const path = getToken("alUSD").feederPool; + await executeAndAssertSwap("swapViaFeeder", daiToken, 18, alusdToken, 18, swapAmount, dsaWallet0, wallet0, [ + path + ]); + }); + it("Should swap fAsset to bAsset (via feeder)", async () => { + const swapAmount = simpleToExactAmount(100); + const path = getToken("alUSD").feederPool; + await executeAndAssertSwap("swapViaFeeder", alusdToken, 18, daiToken, 18, swapAmount, dsaWallet0, wallet0, [ + path + ]); + }); + it("Should swap fAsset to mUSD (via feeder)", async () => { + const swapAmount = simpleToExactAmount(100); + const path = getToken("alUSD").feederPool; + await executeAndAssertSwap("swapViaFeeder", alusdToken, 18, mUsdToken, 18, swapAmount, dsaWallet0, wallet0, [ + path + ]); + }); + }); + }); +}); diff --git a/test/mainnet/mstable/mstable.utils.ts b/test/mainnet/mstable/mstable.utils.ts new file mode 100644 index 0000000..88bb21e --- /dev/null +++ b/test/mainnet/mstable/mstable.utils.ts @@ -0,0 +1,137 @@ +import hre from "hardhat"; +import { ethers } from "hardhat"; +import { assert, expect } from "chai"; + +import { + DEFAULT_DECIMALS, + DEAD_ADDRESS, + toEther, + connectorName, + simpleToExactAmount, + getToken +} from "./mstable.helpers"; + +import { IERC20Minimal, IERC20Minimal__factory } from "../../../typechain"; +import { BigNumber, Contract, Wallet } from "ethers"; + +import { encodeSpells } from "../../../scripts/tests/encodeSpells"; + +const provider = hre.waffle.provider; + +let imUsdToken: IERC20Minimal = IERC20Minimal__factory.connect(getToken("imUSD").tokenAddress, provider); +let imUsdVault: IERC20Minimal = IERC20Minimal__factory.connect(getToken("imUSDVault").tokenAddress, provider); + +export const executeAndAssertSwap = async ( + method: string, + tokenFrom: IERC20Minimal, + tokenFromDecimals: number, + tokenTo: IERC20Minimal, + tokenToDecimals: number, + swapAmount: BigNumber, + dsaWallet0: Contract, + wallet0: Wallet, + args?: any[] +) => { + const diffFrom = ethers.BigNumber.from(10).pow(DEFAULT_DECIMALS - tokenFromDecimals); + const diffTo = ethers.BigNumber.from(10).pow(DEFAULT_DECIMALS - tokenToDecimals); + + const tokenFromBalanceBefore = (await tokenFrom.balanceOf(dsaWallet0.address)).mul(diffFrom); + console.log("Token From balance before: ", toEther(tokenFromBalanceBefore)); + + const tokenToBalanceBefore = (await tokenTo.balanceOf(dsaWallet0.address)).mul(diffTo); + console.log("Token To balance before: ", toEther(tokenToBalanceBefore)); + + const spells = [ + { + connector: connectorName, + method, + args: [tokenFrom.address, tokenTo.address, swapAmount, 1, ...(args ? args : []), 0, 0] + } + ]; + + console.log("Swapping...", toEther(swapAmount)); + + const tx = await dsaWallet0.connect(wallet0).cast(...encodeSpells(spells), DEAD_ADDRESS); + + const tokenFromBalanceAfter = (await tokenFrom.balanceOf(dsaWallet0.address)).mul(diffFrom); + console.log("Token From balance after: ", toEther(tokenFromBalanceAfter)); + + const tokenToBalanceAfter = (await tokenTo.balanceOf(dsaWallet0.address)).mul(diffTo); + console.log("Token To balance after: ", toEther(tokenToBalanceAfter)); + + expect(tokenFromBalanceAfter).to.be.eq(tokenFromBalanceBefore.sub(swapAmount)); + expect(tokenToBalanceAfter).to.be.gt(tokenToBalanceBefore); +}; + +export const executeAndAssertDeposit = async ( + method: string, + tokenFrom: IERC20Minimal, + depositAmount: BigNumber, + dsaWallet0: Contract, + wallet0: Wallet, + args?: any[] +) => { + const FromBalanceBefore = await tokenFrom.balanceOf(dsaWallet0.address); + console.log("Balance before: ", toEther(FromBalanceBefore)); + + const imUsdVaultBalanceBefore = await imUsdVault.balanceOf(dsaWallet0.address); + console.log("imUSD Vault balance before: ", toEther(imUsdVaultBalanceBefore)); + + const spells = [ + { + connector: connectorName, + method, + args: [tokenFrom.address, depositAmount, ...(args ? args : []), 0, 0] + } + ]; + + const tx = await dsaWallet0.connect(wallet0).cast(...encodeSpells(spells), DEAD_ADDRESS); + + const FromBalanceAfter = await tokenFrom.balanceOf(dsaWallet0.address); + console.log("Balance after: ", toEther(FromBalanceAfter)); + + const imUsdBalance = await imUsdToken.balanceOf(dsaWallet0.address); + console.log("imUSD balance: ", toEther(imUsdBalance)); + + const imUsdVaultBalance = await imUsdVault.balanceOf(dsaWallet0.address); + console.log("imUSD Vault balance: ", toEther(imUsdVaultBalance)); + + // Should have something in the vault but no imUSD + expect(await imUsdToken.balanceOf(dsaWallet0.address)).to.be.eq(0); + expect(await imUsdVault.balanceOf(dsaWallet0.address)).to.be.gt(imUsdVaultBalanceBefore); + expect(FromBalanceAfter).to.eq(FromBalanceBefore.sub(depositAmount)); +}; + +export const executeAndAssertWithdraw = async ( + method: string, + tokenTo: IERC20Minimal, + withdrawAmount: BigNumber, + dsaWallet0: Contract, + wallet0: Wallet, + args: any[] +) => { + const tokenToBalanceBefore = await tokenTo.balanceOf(dsaWallet0.address); + console.log("Balance before: ", toEther(tokenToBalanceBefore)); + + const imUsdVaultBalanceBefore = await imUsdVault.balanceOf(dsaWallet0.address); + console.log("imUSD Vault balance before: ", toEther(imUsdVaultBalanceBefore)); + + const spells = [ + { + connector: connectorName, + method, + args: [tokenTo.address, withdrawAmount, ...(args ? args : []), 0, 0] + } + ]; + + const tx = await dsaWallet0.connect(wallet0).cast(...encodeSpells(spells), DEAD_ADDRESS); + + const imUsdVaultBalanceAfter = await imUsdVault.balanceOf(dsaWallet0.address); + console.log("imUSD Vault balance after: ", toEther(imUsdVaultBalanceAfter)); + + const tokenToBalanceAfter = await tokenTo.balanceOf(dsaWallet0.address); + console.log("Balance after: ", toEther(tokenToBalanceAfter)); + + expect(imUsdVaultBalanceAfter).to.be.eq(imUsdVaultBalanceBefore.sub(withdrawAmount)); + expect(tokenToBalanceAfter).to.gt(tokenToBalanceBefore); +}; diff --git a/test/mainnet/notional/notional.contracts.ts b/test/mainnet/notional/notional.contracts.ts new file mode 100644 index 0000000..54809b4 --- /dev/null +++ b/test/mainnet/notional/notional.contracts.ts @@ -0,0 +1,116 @@ + +const NOTIONAL_CONTRACT_ADDRESS = '0x1344A36A1B56144C3Bc62E7757377D288fDE0369'; +const NOTIONAL_CONTRACT_ABI = [ + { + "inputs": [ + { + "internalType": "uint16", + "name": "currencyId", + "type": "uint16" + }, + { + "internalType": "address", + "name": "account", + "type": "address" + } + ], + "name": "getAccountBalance", + "outputs": [ + { + "internalType": "int256", + "name": "cashBalance", + "type": "int256" + }, + { + "internalType": "int256", + "name": "nTokenBalance", + "type": "int256" + }, + { + "internalType": "uint256", + "name": "lastClaimTime", + "type": "uint256" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "account", + "type": "address" + } + ], + "name": "getAccountPortfolio", + "outputs": [ + { + "components": [ + { + "internalType": "uint256", + "name": "currencyId", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "maturity", + "type": "uint256" + }, + { + "internalType": "uint256", + "name": "assetType", + "type": "uint256" + }, + { + "internalType": "int256", + "name": "notional", + "type": "int256" + }, + { + "internalType": "uint256", + "name": "storageSlot", + "type": "uint256" + }, + { + "internalType": "enum AssetStorageState", + "name": "storageState", + "type": "uint8" + } + ], + "internalType": "struct PortfolioAsset[]", + "name": "", + "type": "tuple[]" + } + ], + "stateMutability": "view", + "type": "function" + } +]; + +const SNOTE_CONTRACT_ADDRESS = '0x38de42f4ba8a35056b33a746a6b45be9b1c3b9d2'; + +const WETH_TOKEN_ADDRESS = "0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2"; +const DAI_TOKEN_ADDRESS = "0x6B175474E89094C44Da98b954EedeAC495271d0F"; +const CDAI_TOKEN_ADDRESS = "0x5d3a536E4D6DbD6114cc1Ead35777bAB948E3643"; +const CETH_TOKEN_ADDRESS = "0x4ddc2d193948926d02f9b1fe9e1daa0718270ed5"; +const BPT_TOKEN_ADDRESS = "0x5122E01D819E58BB2E22528c0D68D310f0AA6FD7"; +const NOTE_TOKEN_ADDRESS = "0xCFEAead4947f0705A14ec42aC3D44129E1Ef3eD5"; +const ERC20_TOKEN_ABI = [ + "function transfer(address _to, uint256 _value) public returns (bool success)", + "function balanceOf(address account) external view returns (uint256)", + "function approve(address spender, uint256 amount) external returns (bool)", +]; + +export default { + NOTIONAL_CONTRACT_ADDRESS, + NOTIONAL_CONTRACT_ABI, + SNOTE_CONTRACT_ADDRESS, + WETH_TOKEN_ADDRESS, + BPT_TOKEN_ADDRESS, + DAI_TOKEN_ADDRESS, + CDAI_TOKEN_ADDRESS, + CETH_TOKEN_ADDRESS, + NOTE_TOKEN_ADDRESS, + ERC20_TOKEN_ABI +}; diff --git a/test/mainnet/notional/notional.helpers.ts b/test/mainnet/notional/notional.helpers.ts new file mode 100644 index 0000000..d5d04bf --- /dev/null +++ b/test/mainnet/notional/notional.helpers.ts @@ -0,0 +1,332 @@ +import { BigNumber } from "ethers"; +import { encodeSpells } from "../../../scripts/tests/encodeSpells" + +const depositCollteral = async ( + dsa: any, + authority: any, + referrer: any, + currencyId: number, + amount: BigNumber, + underlying: boolean +) => { + const spells = [ + { + connector: "NOTIONAL-TEST-A", + method: "depositCollateral", + args: [currencyId, underlying, amount, 0, 0] + } + ]; + + const tx = await dsa.connect(authority).cast(...encodeSpells(spells), referrer.address); + await tx.wait() +}; + +const depositAndMintNToken = async ( + dsa: any, + authority: any, + referrer: any, + currencyId: number, + amount: BigNumber, + underlying: boolean +) => { + const spells = [ + { + connector: "NOTIONAL-TEST-A", + method: "depositAndMintNToken", + args: [currencyId, amount, underlying, 0, 0] + } + ]; + + const tx = await dsa.connect(authority).cast(...encodeSpells(spells), referrer.address); + await tx.wait() +} + +const depositAndLend = async ( + dsa: any, + authority: any, + referrer: any, + currencyId: number, + underlying: boolean, + amount: BigNumber, + market: number, + fcash: BigNumber +) => { + const spells = [ + { + connector: "NOTIONAL-TEST-A", + method: "depositAndLend", + args: [currencyId, amount, underlying, market, fcash, 0, 0] + } + ]; + + const tx = await dsa.connect(authority).cast(...encodeSpells(spells), referrer.address); + await tx.wait() +}; + +const withdrawCollateral = async ( + dsa: any, + authority: any, + referrer: any, + currencyId: number, + amount: BigNumber, + underlying: boolean +) => { + const spells = [ + { + connector: "NOTIONAL-TEST-A", + method: "withdrawCollateral", + args: [currencyId, underlying, amount, 0, 0] + } + ]; + + const tx = await dsa.connect(authority).cast(...encodeSpells(spells), referrer.address); + await tx.wait() +}; + +const redeemNTokenRaw = async ( + dsa: any, + authority: any, + referrer: any, + currencyId: number, + sellTokenAssets: boolean, + tokensToRedeem: BigNumber +) => { + const spells = [ + { + connector: "NOTIONAL-TEST-A", + method: "redeemNTokenRaw", + args: [currencyId, sellTokenAssets, tokensToRedeem, false, 0, 0] + } + ]; + + const tx = await dsa.connect(authority).cast(...encodeSpells(spells), referrer.address); + await tx.wait() +}; + +const redeemNTokenAndWithdraw = async ( + dsa: any, + authority: any, + referrer: any, + currencyId: number, + tokensToRedeem: BigNumber, + amountToWithdraw: BigNumber, + redeemToUnderlying: boolean +) => { + const spells = [ + { + connector: "NOTIONAL-TEST-A", + method: "redeemNTokenAndWithdraw", + args: [currencyId, tokensToRedeem, amountToWithdraw, redeemToUnderlying, 0, 0] + } + ]; + + const tx = await dsa.connect(authority).cast(...encodeSpells(spells), referrer.address); + await tx.wait() +}; + +const redeemNTokenAndDeleverage = async ( + dsa: any, + authority: any, + referrer: any, + currencyId: number, + tokensToRedeem: BigNumber, + marketIndex: number, + fCashAmount: BigNumber +) => { + const spells = [ + { + connector: "NOTIONAL-TEST-A", + method: "redeemNTokenAndDeleverage", + args: [currencyId, tokensToRedeem, marketIndex, fCashAmount, 0, 0] + } + ]; + + const tx = await dsa.connect(authority).cast(...encodeSpells(spells), referrer.address); + await tx.wait() +}; + +const depositCollateralBorrowAndWithdraw = async ( + dsa: any, + authority: any, + referrer: any, + depositCurrencyId: number, + depositType: number, + depositAmount: BigNumber, + borrowCurrencyId: number, + marketIndex: number, + fCashAmount: BigNumber, + redeedmUnderlying: boolean +) => { + const spells = [ + { + connector: "NOTIONAL-TEST-A", + method: "depositCollateralBorrowAndWithdraw", + args: [ + depositCurrencyId, + depositType, + depositAmount, + borrowCurrencyId, + marketIndex, + fCashAmount, + 0, + redeedmUnderlying, + 0, + 0 + ] + } + ]; + + const tx = await dsa.connect(authority).cast(...encodeSpells(spells), referrer.address); + await tx.wait() +}; + +const withdrawLend = async ( + dsa: any, + authority: any, + referrer: any, + currencyId: number, + marketIndex: number, + fCashAmount: BigNumber +) => { + const spells = [ + { + connector: "NOTIONAL-TEST-A", + method: "withdrawLend", + args: [currencyId, marketIndex, fCashAmount, 0, 0] + } + ]; + + const tx = await dsa.connect(authority).cast(...encodeSpells(spells), referrer.address); + await tx.wait() +}; + +const mintSNoteFromETH = async ( + dsa: any, + authority: any, + referrer: any, + noteAmount: BigNumber, + ethAmount: BigNumber, + minBPT: BigNumber +) => { + const spells = [ + { + connector: "NOTIONAL-TEST-A", + method: "mintSNoteFromETH", + args: [noteAmount, ethAmount, minBPT, 0] + } + ] + + const tx = await dsa.connect(authority).cast(...encodeSpells(spells), referrer.address); + await tx.wait() +} + +const mintSNoteFromWETH = async ( + dsa: any, + authority: any, + referrer: any, + noteAmount: BigNumber, + wethAmount: BigNumber, + minBPT: BigNumber +) => { + const spells = [ + { + connector: "NOTIONAL-TEST-A", + method: "mintSNoteFromWETH", + args: [noteAmount, wethAmount, minBPT, 0] + } + ] + + const tx = await dsa.connect(authority).cast(...encodeSpells(spells), referrer.address); + await tx.wait() +} + +const mintSNoteFromBPT = async ( + dsa: any, + authority: any, + referrer: any, + bptAmount: BigNumber +) => { + const spells = [ + { + connector: "NOTIONAL-TEST-A", + method: "mintSNoteFromBPT", + args: [bptAmount] + } + ] + + const tx = await dsa.connect(authority).cast(...encodeSpells(spells), referrer.address); + await tx.wait() +} + +const startCoolDown = async ( + dsa: any, + authority: any, + referrer: any +) => { + const spells = [ + { + connector: "NOTIONAL-TEST-A", + method: "startCoolDown", + args: [] + } + ] + + const tx = await dsa.connect(authority).cast(...encodeSpells(spells), referrer.address); + await tx.wait() +} + +const stopCoolDown = async ( + dsa: any, + authority: any, + referrer: any +) => { + const spells = [ + { + connector: "NOTIONAL-TEST-A", + method: "stopCoolDown", + args: [] + } + ] + + const tx = await dsa.connect(authority).cast(...encodeSpells(spells), referrer.address); + await tx.wait() +} + +const redeemSNote = async ( + dsa: any, + authority: any, + referrer: any, + sNOTEAmount: BigNumber, + minWETH: BigNumber, + minNOTE: BigNumber, + redeemWETH: boolean +) => { + const spells = [ + { + connector: "NOTIONAL-TEST-A", + method: "redeemSNote", + args: [sNOTEAmount, minWETH, minNOTE, redeemWETH] + } + ] + + const tx = await dsa.connect(authority).cast(...encodeSpells(spells), referrer.address); + await tx.wait() +} + +export default { + depositCollteral, + depositAndMintNToken, + depositAndLend, + withdrawCollateral, + withdrawLend, + redeemNTokenRaw, + redeemNTokenAndWithdraw, + redeemNTokenAndDeleverage, + depositCollateralBorrowAndWithdraw, + mintSNoteFromETH, + mintSNoteFromWETH, + mintSNoteFromBPT, + startCoolDown, + stopCoolDown, + redeemSNote +}; diff --git a/test/mainnet/notional/notional.test.ts b/test/mainnet/notional/notional.test.ts new file mode 100644 index 0000000..304ee25 --- /dev/null +++ b/test/mainnet/notional/notional.test.ts @@ -0,0 +1,513 @@ +import { expect } from "chai"; +import hre from "hardhat"; +const { waffle, ethers } = hre; +const { provider, deployContract } = waffle + +import { deployAndEnableConnector } from "../../../scripts/tests/deployAndEnableConnector"; +import { buildDSAv2 } from "../../../scripts/tests/buildDSAv2" +import { getMasterSigner } from "../../../scripts/tests/getMasterSigner" +import { addresses } from "../../../scripts/tests/mainnet/addresses"; +import { abis } from "../../../scripts/constant/abis"; +import contracts from "./notional.contracts"; +import helpers from "./notional.helpers"; + +import { ConnectV2Notional__factory } from "../../../typechain"; +import { BigNumber } from "ethers"; + +const DAI_WHALE = "0x6dfaf865a93d3b0b5cfd1b4db192d1505676645b"; +const CDAI_WHALE = "0x33b890d6574172e93e58528cd99123a88c0756e9"; +const ETH_WHALE = "0x7D24796f7dDB17d73e8B1d0A3bbD103FBA2cb2FE"; +const CETH_WHALE = "0x1a1cd9c606727a7400bb2da6e4d5c70db5b4cade"; +const WETH_WHALE = "0x6555e1cc97d3cba6eaddebbcd7ca51d75771e0b8"; +const BPT_WHALE = "0x38de42f4ba8a35056b33a746a6b45be9b1c3b9d2"; +const MaxUint96 = BigNumber.from("0xffffffffffffffffffffffff"); +const DEPOSIT_ASSET = 1; +const DEPOSIT_UNDERLYING = 2; +const DEPOSIT_ASSET_MINT_NTOKEN = 3; +const DEPOSIT_UNDERLYING_MINT_NTOKEN = 4; +const ETH_ID = 1; +const DAI_ID = 2; +const MARKET_3M = 1; + +describe("Notional", function () { + const connectorName = "NOTIONAL-TEST-A" + + let dsaWallet0: any; + let masterSigner: any; + let instaConnectorsV2: any; + let connector: any; + let notional: any; + let snote: any; + let daiToken: any; + let cdaiToken: any; + let cethToken: any; + let wethToken: any; + let bptToken: any; + let noteToken: any; + let daiWhale: any; + let cdaiWhale: any; + let cethWhale: any; + let wethWhale: any; + let bptWhale: any; + + const wallets = provider.getWallets() + const [wallet0, wallet1, wallet2, wallet3] = wallets + beforeEach(async () => { + await hre.network.provider.request({ + method: "hardhat_reset", + params: [ + { + forking: { + //@ts-ignore + jsonRpcUrl: hre.config.networks.hardhat.forking.url, + blockNumber: 14483893, + }, + }, + ], + }); + await hre.network.provider.request({ + method: "hardhat_impersonateAccount", + params: [DAI_WHALE] + }) + await hre.network.provider.request({ + method: "hardhat_impersonateAccount", + params: [CDAI_WHALE] + }) + await hre.network.provider.request({ + method: "hardhat_impersonateAccount", + params: [ETH_WHALE] + }) + await hre.network.provider.request({ + method: "hardhat_impersonateAccount", + params: [CETH_WHALE] + }) + await hre.network.provider.request({ + method: "hardhat_impersonateAccount", + params: [WETH_WHALE] + }) + await hre.network.provider.request({ + method: "hardhat_impersonateAccount", + params: [BPT_WHALE] + }) + + masterSigner = await getMasterSigner() + instaConnectorsV2 = await ethers.getContractAt(abis.core.connectorsV2, addresses.core.connectorsV2); + connector = await deployAndEnableConnector({ + connectorName, + contractArtifact: ConnectV2Notional__factory, + signer: masterSigner, + connectors: instaConnectorsV2 + }) + notional = new ethers.Contract( + contracts.NOTIONAL_CONTRACT_ADDRESS, + contracts.NOTIONAL_CONTRACT_ABI, + ethers.provider + ); + snote = new ethers.Contract( + contracts.SNOTE_CONTRACT_ADDRESS, + contracts.ERC20_TOKEN_ABI, + ethers.provider + ) + daiToken = new ethers.Contract( + contracts.DAI_TOKEN_ADDRESS, + contracts.ERC20_TOKEN_ABI, + ethers.provider + ); + daiWhale = await ethers.getSigner(DAI_WHALE); + cdaiToken = new ethers.Contract( + contracts.CDAI_TOKEN_ADDRESS, + contracts.ERC20_TOKEN_ABI, + ethers.provider + ); + cdaiWhale = await ethers.getSigner(CDAI_WHALE); + cethToken = new ethers.Contract( + contracts.CETH_TOKEN_ADDRESS, + contracts.ERC20_TOKEN_ABI, + ethers.provider + ); + cethWhale = await ethers.getSigner(CETH_WHALE); + wethToken = new ethers.Contract( + contracts.WETH_TOKEN_ADDRESS, + contracts.ERC20_TOKEN_ABI, + ethers.provider + ); + wethWhale = await ethers.getSigner(WETH_WHALE); + bptToken = new ethers.Contract( + contracts.BPT_TOKEN_ADDRESS, + contracts.ERC20_TOKEN_ABI, + ethers.provider + ); + bptWhale = await ethers.getSigner(BPT_WHALE); + noteToken = new ethers.Contract( + contracts.NOTE_TOKEN_ADDRESS, + contracts.ERC20_TOKEN_ABI, + ethers.provider + ) + dsaWallet0 = await buildDSAv2(wallet0.address); + }); + + describe("Deposit Tests", function () { + it("test_deposit_ETH_underlying", async function () { + await wallet0.sendTransaction({ + to: dsaWallet0.address, + value: ethers.utils.parseEther("10") + }); + const depositAmount = ethers.utils.parseEther("1"); + await helpers.depositCollteral(dsaWallet0, wallet0, wallet1, ETH_ID, depositAmount, true); + const bal = await notional.callStatic.getAccountBalance(ETH_ID, dsaWallet0.address); + // balance in internal asset precision + expect(bal[0], "expect at least 49 cETH").to.be.gte(ethers.utils.parseUnits("4900000000", 0)); + expect(bal[1], "expect 0 nETH").to.be.equal(ethers.utils.parseUnits("0", 0)); + }); + + it("test_deposit_ETH_asset", async function () { + const depositAmount = ethers.utils.parseUnits("1", 8); + await cethToken.connect(cethWhale).transfer(dsaWallet0.address, depositAmount); + await helpers.depositCollteral(dsaWallet0, wallet0, wallet1, ETH_ID, depositAmount, false); + const bal = await notional.callStatic.getAccountBalance(ETH_ID, dsaWallet0.address); + // balance in internal asset precision + expect(bal[0], "expect at least 1 cETH").to.be.gte(ethers.utils.parseUnits("100000000", 0)); + expect(bal[1], "expect 0 nETH").to.be.equal(ethers.utils.parseUnits("0", 0)); + }); + + it("test_deposit_DAI_underlying", async function () { + const depositAmount = ethers.utils.parseUnits("1000", 18); + await daiToken.connect(daiWhale).transfer(dsaWallet0.address, depositAmount); + await helpers.depositCollteral(dsaWallet0, wallet0, wallet1, DAI_ID, depositAmount, true); + const bal = await notional.callStatic.getAccountBalance(DAI_ID, dsaWallet0.address); + // balance in internal asset precision + expect(bal[0], "expect at least 45000 cDAI").to.be.gte(ethers.utils.parseUnits("4500000000000", 0)); + expect(bal[1], "expect 0 nDAI").to.be.equal(ethers.utils.parseUnits("0", 0)); + }); + + it("test_deposit_DAI_asset", async function () { + const depositAmount = ethers.utils.parseUnits("1000", 8); + await cdaiToken.connect(cdaiWhale).transfer(dsaWallet0.address, depositAmount); + await helpers.depositCollteral(dsaWallet0, wallet0, wallet1, DAI_ID, depositAmount, false); + const bal = await notional.callStatic.getAccountBalance(DAI_ID, dsaWallet0.address); + // balance in internal asset precision + expect(bal[0], "expect at least 1000 cDAI").to.be.gte(ethers.utils.parseUnits("100000000000", 0)); + expect(bal[1], "expect 0 nDAI").to.be.equal(ethers.utils.parseUnits("0", 0)); + }); + + it("test_deposit_ETH_underlying_and_mint_ntoken", async function () { + await wallet0.sendTransaction({ + to: dsaWallet0.address, + value: ethers.utils.parseEther("10") + }); + const depositAmount = ethers.utils.parseEther("1"); + await helpers.depositAndMintNToken(dsaWallet0, wallet0, wallet1, ETH_ID, depositAmount, true); + const bal = await notional.callStatic.getAccountBalance(ETH_ID, dsaWallet0.address); + expect(bal[0], "expect 0 balance").to.be.equal(ethers.utils.parseUnits("0", 0)); + expect(bal[1], "expect at least 49 nETH").to.be.gte(ethers.utils.parseUnits("4900000000", 0)); + }); + }); + + describe("Lend Tests", function () { + it("test_deposit_ETH_underlying_and_lend", async function () { + await wallet0.sendTransaction({ + to: dsaWallet0.address, + value: ethers.utils.parseEther("10") + }); + const depositAmount = ethers.utils.parseEther("10"); + const fcash = ethers.utils.parseUnits("9", 8); + await helpers.depositAndLend(dsaWallet0, wallet0, wallet1, ETH_ID, true, depositAmount, MARKET_3M, fcash); + const portfolio = await notional.getAccountPortfolio(dsaWallet0.address); + expect(portfolio.length, "expect 1 lending position").to.be.equal(1); + expect(portfolio[0][3], "expect 9 fETH").to.be.gte(ethers.utils.parseUnits("900000000", 0)); + }); + + it("test_deposit_ETH_asset_and_lend", async function () { + const depositAmount = ethers.utils.parseUnits("1", 8); + await cethToken.connect(cethWhale).transfer(dsaWallet0.address, depositAmount); + const fcash = ethers.utils.parseUnits("0.01", 8); + await helpers.depositAndLend(dsaWallet0, wallet0, wallet1, ETH_ID, false, depositAmount, MARKET_3M, fcash); + const portfolio = await notional.getAccountPortfolio(dsaWallet0.address); + expect(portfolio.length, "expect 1 lending position").to.be.equal(1); + expect(portfolio[0][3], "expect 0.01 fETH").to.be.gte(ethers.utils.parseUnits("1000000", 0)); + }); + + it("test_deposit_DAI_underlying_and_lend", async function () { + const depositAmount = ethers.utils.parseUnits("1000", 18); + await daiToken.connect(daiWhale).transfer(dsaWallet0.address, depositAmount); + const fcash = ethers.utils.parseUnits("100", 8); + await helpers.depositAndLend(dsaWallet0, wallet0, wallet1, DAI_ID, true, depositAmount, MARKET_3M, fcash); + const portfolio = await notional.getAccountPortfolio(dsaWallet0.address); + expect(portfolio.length, "expect 1 lending position").to.be.equal(1); + expect(portfolio[0][3], "expect 100 fDAI").to.be.gte(ethers.utils.parseUnits("10000000000", 0)); + }); + + it("test_deposit_DAI_asset_and_lend", async function () { + const depositAmount = ethers.utils.parseUnits("1000", 8); + await cdaiToken.connect(cdaiWhale).transfer(dsaWallet0.address, depositAmount); + const fcash = ethers.utils.parseUnits("10", 8); + await helpers.depositAndLend(dsaWallet0, wallet0, wallet1, DAI_ID, false, depositAmount, MARKET_3M, fcash); + const portfolio = await notional.getAccountPortfolio(dsaWallet0.address); + expect(portfolio.length, "expect 1 lending position").to.be.equal(1); + expect(portfolio[0][3], "expect 10 fDAI").to.be.gte(ethers.utils.parseUnits("1000000000", 0)); + }); + + it("test_withdraw_lend_ETH", async function () { + await wallet0.sendTransaction({ + to: dsaWallet0.address, + value: ethers.utils.parseEther("10") + }); + const depositAmount = ethers.utils.parseEther("10"); + const fcash = ethers.utils.parseUnits("9", 8); + await helpers.depositAndLend(dsaWallet0, wallet0, wallet1, ETH_ID, true, depositAmount, MARKET_3M, fcash); + const before = await notional.getAccountPortfolio(dsaWallet0.address); + expect(before.length, "expect 1 lending position").to.be.equal(1); + expect(before[0][3], "expect 9 fETH").to.be.gte(ethers.utils.parseUnits("900000000", 0)); + await helpers.withdrawLend(dsaWallet0, wallet0, wallet1, ETH_ID, MARKET_3M, fcash); + const after = await notional.getAccountPortfolio(dsaWallet0.address); + expect(after.length, "expect lending position to be closed out").to.be.equal(0); + }); + }); + + describe("Borrow Tests", function () { + it("test_deposit_ETH_and_borrow_DAI_underlying", async function () { + await wallet0.sendTransaction({ + to: dsaWallet0.address, + value: ethers.utils.parseEther("10") + }); + const depositAmount = ethers.utils.parseEther("10"); + const fcash = ethers.utils.parseUnits("1000", 8); + await helpers.depositCollateralBorrowAndWithdraw( + dsaWallet0, wallet0, wallet1, ETH_ID, DEPOSIT_UNDERLYING, depositAmount, DAI_ID, MARKET_3M, fcash, true + ); + expect( + await daiToken.balanceOf(dsaWallet0.address), + "expect DSA wallet to contain borrowed balance minus fees" + ).to.be.gte(ethers.utils.parseEther("985")); + }); + + it("test_deposit_ETH_and_borrow_DAI_asset", async function () { + await wallet0.sendTransaction({ + to: dsaWallet0.address, + value: ethers.utils.parseEther("10") + }); + const depositAmount = ethers.utils.parseEther("10"); + const fcash = ethers.utils.parseUnits("1000", 8); + await helpers.depositCollateralBorrowAndWithdraw( + dsaWallet0, wallet0, wallet1, ETH_ID, DEPOSIT_UNDERLYING, depositAmount, DAI_ID, MARKET_3M, fcash, false + ); + expect( + await cdaiToken.balanceOf(dsaWallet0.address), + "expect DSA wallet to contain borrowed balance minus fees" + ).to.be.gte(ethers.utils.parseUnits("4490000000000", 0)); + }); + + it("test_deposit_DAI_underlying_and_borrow_ETH", async function () { + const depositAmount = ethers.utils.parseUnits("20000", 18); + await daiToken.connect(daiWhale).transfer(dsaWallet0.address, depositAmount); + const fcash = ethers.utils.parseUnits("1", 8); + await helpers.depositCollateralBorrowAndWithdraw( + dsaWallet0, wallet0, wallet1, DAI_ID, DEPOSIT_UNDERLYING, depositAmount, ETH_ID, MARKET_3M, fcash, true + ); + expect( + await ethers.provider.getBalance(dsaWallet0.address), + "expect DSA wallet to contain borrowed balance minus fees" + ).to.be.gte(ethers.utils.parseEther("0.99")); + }); + + it("test_deposit_DAI_asset_and_borrow_ETH", async function () { + const depositAmount = ethers.utils.parseUnits("1000000", 8); + await cdaiToken.connect(cdaiWhale).transfer(dsaWallet0.address, depositAmount); + const fcash = ethers.utils.parseUnits("1", 8); + await helpers.depositCollateralBorrowAndWithdraw( + dsaWallet0, wallet0, wallet1, DAI_ID, DEPOSIT_ASSET, depositAmount, ETH_ID, MARKET_3M, fcash, true + ); + expect( + await ethers.provider.getBalance(dsaWallet0.address), + "expect DSA wallet to contain borrowed balance minus fees" + ).to.be.gte(ethers.utils.parseEther("0.99")); + }); + + it("test_mint_nDAI_underlying_and_borrow_ETH", async function () { + const depositAmount = ethers.utils.parseUnits("20000", 18); + await daiToken.connect(daiWhale).transfer(dsaWallet0.address, depositAmount); + const fcash = ethers.utils.parseUnits("1", 8); + await helpers.depositCollateralBorrowAndWithdraw( + dsaWallet0, wallet0, wallet1, DAI_ID, DEPOSIT_UNDERLYING_MINT_NTOKEN, depositAmount, ETH_ID, MARKET_3M, fcash, true + ); + expect( + await ethers.provider.getBalance(dsaWallet0.address), + "expect DSA wallet to contain borrowed balance minus fees" + ).to.be.gte(ethers.utils.parseEther("0.99")); + }); + + it("test_mint_nDAI_asset_and_borrow_ETH", async function () { + const depositAmount = ethers.utils.parseUnits("1000000", 8); + await cdaiToken.connect(cdaiWhale).transfer(dsaWallet0.address, depositAmount); + const fcash = ethers.utils.parseUnits("1", 8); + await helpers.depositCollateralBorrowAndWithdraw( + dsaWallet0, wallet0, wallet1, DAI_ID, DEPOSIT_ASSET_MINT_NTOKEN, depositAmount, ETH_ID, MARKET_3M, fcash, true + ); + expect( + await ethers.provider.getBalance(dsaWallet0.address), + "expect DSA wallet to contain borrowed balance minus fees" + ).to.be.gte(ethers.utils.parseEther("0.99")); + }); + }); + + describe("Withdraw Tests", function () { + it("test_withdraw_ETH_underlying", async function () { + await wallet0.sendTransaction({ + to: dsaWallet0.address, + value: ethers.utils.parseEther("10") + }); + const depositAmount = ethers.utils.parseEther("1"); + await helpers.depositCollteral(dsaWallet0, wallet0, wallet1, 1, depositAmount, true); + await helpers.withdrawCollateral(dsaWallet0, wallet0, wallet1, 1, ethers.constants.MaxUint256, true); + expect( + await ethers.provider.getBalance(dsaWallet0.address), + "expect DSA wallet to contain underlying funds" + ).to.be.gte(ethers.utils.parseEther("10")); + }); + + it("test_withdraw_ETH_asset", async function () { + await wallet0.sendTransaction({ + to: dsaWallet0.address, + value: ethers.utils.parseEther("10") + }); + const depositAmount = ethers.utils.parseEther("1"); + await helpers.depositCollteral(dsaWallet0, wallet0, wallet1, ETH_ID, depositAmount, true); + await helpers.withdrawCollateral(dsaWallet0, wallet0, wallet1, ETH_ID, ethers.constants.MaxUint256, false); + expect( + await cethToken.balanceOf(dsaWallet0.address), + "expect DSA wallet to contain cToken funds" + ).to.be.gte(ethers.utils.parseUnits("4900000000", 0)); + }); + + it("test_redeem_DAI_raw", async function () { + const depositAmount = ethers.utils.parseUnits("1000", 8); + await cdaiToken.connect(cdaiWhale).transfer(dsaWallet0.address, depositAmount); + await helpers.depositAndMintNToken(dsaWallet0, wallet0, wallet1, DAI_ID, depositAmount, false); + await helpers.redeemNTokenRaw(dsaWallet0, wallet0, wallet1, DAI_ID, true, MaxUint96) + const bal = await notional.callStatic.getAccountBalance(DAI_ID, dsaWallet0.address); + expect(bal[0], "expect cDAI balance after redemption").to.be.gte(ethers.utils.parseUnits("99000000000", 0)); + expect(bal[1], "expect 0 nDAI").to.be.equal(ethers.utils.parseEther("0")); + }); + + it("test_redeem_DAI_and_withdraw_redeem", async function () { + const depositAmount = ethers.utils.parseUnits("1000", 8); + await cdaiToken.connect(cdaiWhale).transfer(dsaWallet0.address, depositAmount); + await helpers.depositAndMintNToken(dsaWallet0, wallet0, wallet1, DAI_ID, depositAmount, false); + await helpers.redeemNTokenAndWithdraw(dsaWallet0, wallet0, wallet1, DAI_ID, MaxUint96, ethers.constants.MaxUint256, true); + const bal = await notional.callStatic.getAccountBalance(DAI_ID, dsaWallet0.address); + expect(bal[0], "expect 0 cDAI balance").to.be.equal(ethers.utils.parseEther("0")); + expect(bal[1], "expect 0 nDAI balance").to.be.equal(ethers.utils.parseEther("0")); + }); + + it("test_redeem_DAI_and_withdraw_no_redeem", async function () { + const depositAmount = ethers.utils.parseUnits("1000", 8); + await cdaiToken.connect(cdaiWhale).transfer(dsaWallet0.address, depositAmount); + await helpers.depositAndMintNToken(dsaWallet0, wallet0, wallet1, DAI_ID, depositAmount, false); + expect(await cdaiToken.balanceOf(dsaWallet0.address)).to.be.equal(ethers.utils.parseEther("0")); + await helpers.redeemNTokenAndWithdraw(dsaWallet0, wallet0, wallet1, DAI_ID, MaxUint96, ethers.constants.MaxUint256, false); + const bal = await notional.callStatic.getAccountBalance(DAI_ID, dsaWallet0.address); + expect(bal[0], "expect 0 cDAI balance").to.be.equal(ethers.utils.parseEther("0")); + expect(bal[1], "expect 0 nDAI balance").to.be.equal(ethers.utils.parseEther("0")); + expect( + await cdaiToken.balanceOf(dsaWallet0.address), + "expect DSA wallet to contain cToken funds" + ).to.be.gte(ethers.utils.parseUnits("99000000000", 0)); + }); + + it("test_redeem_DAI_and_deleverage", async function () { + const depositAmount = ethers.utils.parseUnits("20000", 18); + await daiToken.connect(daiWhale).transfer(dsaWallet0.address, depositAmount); + const fcash = ethers.utils.parseUnits("1", 8); + await helpers.depositCollateralBorrowAndWithdraw( + dsaWallet0, wallet0, wallet1, DAI_ID, DEPOSIT_UNDERLYING, depositAmount, ETH_ID, MARKET_3M, fcash, true + ); + const bal = await ethers.provider.getBalance(dsaWallet0.address); + await helpers.depositAndMintNToken(dsaWallet0, wallet0, wallet1, ETH_ID, bal, true); + const before = await notional.getAccountPortfolio(dsaWallet0.address); + expect(before.length, "expect 1 fDAI debt position").to.be.equal(1); + expect(before[0][3], "expect fDAI debt position to equal borrow amount").to.be.lte(ethers.utils.parseUnits("-100000000", 0)); + const fcash2 = ethers.utils.parseUnits("0.98", 8); + await helpers.redeemNTokenAndDeleverage(dsaWallet0, wallet0, wallet1, ETH_ID, MaxUint96, MARKET_3M, fcash2); + const after = await notional.getAccountPortfolio(dsaWallet0.address); + expect(after.length, "expect 1 fDAI debt position after deleverage").to.be.equal(1); + expect(after[0][3], "expect fDAI debt balance to go down after deleverage").to.be.lte(ethers.utils.parseUnits("-2000000", 0)); + }); + }); + + describe("Staking Tests", function () { + it("test_stake_ETH", async function () { + const depositAmount = ethers.utils.parseEther("1"); + await wallet0.sendTransaction({ + to: dsaWallet0.address, + value: depositAmount + }); + expect(await snote.balanceOf(dsaWallet0.address), "expect 0 initial sNOTE balance").to.be.equal(0); + await helpers.mintSNoteFromETH(dsaWallet0, wallet0, wallet1, BigNumber.from(0), depositAmount, BigNumber.from(0)); + expect(await snote.balanceOf(dsaWallet0.address), "expect sNOTE balance to increase").to.be.gte(ethers.utils.parseEther("297")) + }); + + it("test_stake_WETH", async function () { + const depositAmount = ethers.utils.parseEther("1"); + await wethToken.connect(wethWhale).transfer(dsaWallet0.address, depositAmount); + expect(await snote.balanceOf(dsaWallet0.address), "expect 0 initial sNOTE balance").to.be.equal(0); + await helpers.mintSNoteFromWETH(dsaWallet0, wallet0, wallet1, BigNumber.from(0), depositAmount, BigNumber.from(0)); + expect(await snote.balanceOf(dsaWallet0.address), "expect sNOTE balance to increase").to.be.gte(ethers.utils.parseEther("297")) + }); + + it("test_stake_BPT", async function () { + const depositAmount = ethers.utils.parseEther("1"); + await wallet0.sendTransaction({ + to: bptWhale.address, + value: depositAmount + }); + await bptToken.connect(bptWhale).transfer(dsaWallet0.address, depositAmount); + expect(await snote.balanceOf(dsaWallet0.address), "expect 0 initial sNOTE balance").to.be.equal(0); + await helpers.mintSNoteFromBPT(dsaWallet0, wallet0, wallet1, depositAmount); + expect(await snote.balanceOf(dsaWallet0.address), "expect sNOTE balance to increase").to.be.eq(depositAmount) + }); + + it("test_unstake_success", async function () { + const depositAmount = ethers.utils.parseEther("1"); + await wallet0.sendTransaction({ + to: bptWhale.address, + value: depositAmount + }); + await bptToken.connect(bptWhale).transfer(dsaWallet0.address, depositAmount); + await helpers.mintSNoteFromBPT(dsaWallet0, wallet0, wallet1, depositAmount); + await helpers.startCoolDown(dsaWallet0, wallet0, wallet1); + // Skip ahead 16 days + await hre.network.provider.send("evm_increaseTime", [1382400]) + await hre.network.provider.send("evm_mine") + await helpers.redeemSNote( + dsaWallet0, + wallet0, + wallet1, + ethers.constants.MaxUint256, + BigNumber.from(0), + BigNumber.from(0), + true + ); + expect(await noteToken.balanceOf(dsaWallet0.address)).to.be.gte(ethers.utils.parseUnits("50000000000", 0)); + expect(await provider.getBalance(dsaWallet0.address)).to.be.gte(ethers.utils.parseUnits("32500000000000000", 0)) + }); + + it("test_unstable_failure", async function () { + const depositAmount = ethers.utils.parseEther("1"); + await wallet0.sendTransaction({ + to: bptWhale.address, + value: depositAmount + }); + await bptToken.connect(bptWhale).transfer(dsaWallet0.address, depositAmount); + await helpers.mintSNoteFromBPT(dsaWallet0, wallet0, wallet1, depositAmount); + await expect(helpers.redeemSNote( + dsaWallet0, + wallet0, + wallet1, + ethers.constants.MaxUint256, + BigNumber.from(0), + BigNumber.from(0), + true + )).to.be.revertedWith("Not in Redemption Window"); + }); + }); +}); diff --git a/test/mainnet/paraswap/paraswap.test.ts b/test/mainnet/paraswap/paraswap.test.ts new file mode 100644 index 0000000..d225285 --- /dev/null +++ b/test/mainnet/paraswap/paraswap.test.ts @@ -0,0 +1,177 @@ +import hre from "hardhat"; +import { expect } from "chai"; +import { abis } from "../../../scripts/constant/abis"; +import { addresses } from "../../../scripts/tests/mainnet/addresses"; +import { deployAndEnableConnector } from "../../../scripts/tests/deployAndEnableConnector"; +import { getMasterSigner } from "../../../scripts/tests/getMasterSigner"; +import { buildDSAv2 } from "../../../scripts/tests/buildDSAv2"; +import { + ConnectV2ParaswapV5__factory, +} from "../../../typechain" +import { parseEther } from "@ethersproject/units"; +import { encodeSpells } from "../../../scripts/tests/encodeSpells"; +import { tokens } from "../../../scripts/tests/mainnet/tokens"; +import { constants } from "../../../scripts/constant/constant"; +import { addLiquidity } from "../../../scripts/tests/addLiquidity"; +import BigNumber from "bignumber.js"; +import axios from "axios"; +const { web3, deployments, waffle, ethers } = hre; +const { provider, deployContract } = waffle; +describe("Paraswap", function() { + const connectorName = "paraswap-test"; + let dsaWallet0: any; + let masterSigner: any; + let instaConnectorsV2: any; + let connector: any; + const wallets = provider.getWallets(); + const [wallet0, wallet1, wallet2, wallet3] = wallets; + before(async () => { + await hre.network.provider.request({ + method: "hardhat_reset", + params: [ + { + forking: { + // @ts-ignore + jsonRpcUrl: hre.config.networks.hardhat.forking.url, + // blockNumber: 13300000, + }, + }, + ], + }); + masterSigner = await getMasterSigner(); + const erc20 = abis.basic.erc20; + instaConnectorsV2 = await ethers.getContractAt( + abis.core.connectorsV2, + addresses.core.connectorsV2 + ); + + connector = await deployAndEnableConnector({ + connectorName, + contractArtifact: ConnectV2ParaswapV5__factory, + signer: masterSigner, + connectors: instaConnectorsV2, + }); + // console.log("Connector address", connector.address); + }); + it("Should have contracts deployed.", async function() { + expect(!!instaConnectorsV2.address).to.be.true; + expect(!!connector.address).to.be.true; + expect(!!masterSigner.address).to.be.true; + }); + describe("DSA wallet setup", function() { + it("Should build DSA v2", async function() { + dsaWallet0 = await buildDSAv2(wallet0.address); + expect(!!dsaWallet0.address).to.be.true; + }); + it("Deposit ETH into DSA wallet", async function() { + await wallet0.sendTransaction({ + to: dsaWallet0.address, + value: ethers.utils.parseEther("10"), + }); + + expect(await ethers.provider.getBalance(dsaWallet0.address)).to.be.gte( + ethers.utils.parseEther("10") + ); + }); + }); + describe("Main", function() { + it("should swap successfully", async function() { + async function getArg() { + const slippage = 1; + /* eth -> USDT */ + const sellTokenAddress = "0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE"; // eth, decimals 18 + const sellTokenDecimals = 18; + const buyTokenAddress = "0xdac17f958d2ee523a2206206994597c13d831ec7"; // USDT, decimals 6 + const buyTokenDecimals = 6; + const amount = 2; + const srcAmount = new BigNumber(amount) + .times(new BigNumber(10).pow(sellTokenDecimals)) + .toFixed(0); + const fromAddress = dsaWallet0.address; + let url = `https://apiv5.paraswap.io/prices/`; + let params = { + srcToken: sellTokenAddress, + destToken: buyTokenAddress, + srcDecimals: sellTokenDecimals, + destDecimals: buyTokenDecimals, + amount: srcAmount, + side: "SELL", + network: 1, + }; + + const priceRoute = await axios + .get(url, { params: params }) + .then((data) => data.data.priceRoute); + + let buyTokenAmount = priceRoute.destAmount; + let minAmount = new BigNumber(priceRoute.destAmount) + .times(1 - slippage / 100) + .toFixed(0); + + let txConfig = { + priceRoute: priceRoute, + srcToken: sellTokenAddress, + destToken: buyTokenAddress, + srcDecimals: sellTokenDecimals, + destDecimals: buyTokenDecimals, + srcAmount: srcAmount, + destAmount: minAmount, + userAddress: fromAddress, + }; + let url2 = "https://apiv5.paraswap.io/transactions/1?ignoreChecks=true"; + const calldata = await axios + .post(url2, txConfig) + .then((data) => data.data.data); + + function caculateUnitAmt( + buyAmount: any, + sellAmount: any, + buyDecimal: any, + sellDecimal: any, + maxSlippage: any + ) { + let unitAmt: any; + unitAmt = new BigNumber(buyAmount) + .dividedBy(10 ** buyDecimal) + .dividedBy(new BigNumber(sellAmount).dividedBy(10 ** sellDecimal)); + unitAmt = unitAmt.multipliedBy((100 - maxSlippage) / 100); + unitAmt = unitAmt.multipliedBy(1e18).toFixed(0); + return unitAmt; + } + + let unitAmt = caculateUnitAmt( + buyTokenAmount, + srcAmount, + buyTokenDecimals, + sellTokenDecimals, + 1 + ); + + return [ + buyTokenAddress, + sellTokenAddress, + srcAmount, + unitAmt, + calldata, + 0, + ]; + } + let arg = await getArg(); + const spells = [ + { + connector: connectorName, + method: "swap", + args: arg, + }, + ]; + const tx = await dsaWallet0 + .connect(wallet0) + .cast(...encodeSpells(spells), wallet1.address); + const receipt = await tx.wait(); + + expect(await ethers.provider.getBalance(dsaWallet0.address)).to.be.lte( + ethers.utils.parseEther("8") + ); + }); + }); +}); diff --git a/test/mainnet/pooltogether/pooltogether.test.ts b/test/mainnet/pooltogether/pooltogether.test.ts new file mode 100644 index 0000000..e300ec2 --- /dev/null +++ b/test/mainnet/pooltogether/pooltogether.test.ts @@ -0,0 +1,1238 @@ +import { expect } from "chai"; +import hre from "hardhat"; +const { web3, deployments, waffle, ethers } = hre; +const { provider, deployContract } = waffle; + +import { deployAndEnableConnector } from "../../../scripts/tests/deployAndEnableConnector"; +import { buildDSAv2 } from "../../../scripts/tests/buildDSAv2"; +import { encodeSpells } from "../../../scripts/tests/encodeSpells"; +import { getMasterSigner } from "../../../scripts/tests/getMasterSigner"; + +import { addresses } from "../../../scripts/tests/mainnet/addresses"; +import { abis } from "../../../scripts/constant/abis"; +import { constants } from "../../../scripts/constant/constant"; +import { tokens } from "../../../scripts/tests/mainnet/tokens"; +import type { Signer, Contract } from "ethers"; + +import { + ConnectV2Compound__factory, + ConnectV2PoolTogether__factory, + ConnectV2UniswapV2__factory, +} from "../../../typechain"; + +const DAI_TOKEN_ADDR = tokens.dai.address; // DAI Token + +// PoolTogether Address: https://docs.pooltogether.com/resources/networks/ethereum +const DAI_PRIZE_POOL_ADDR = "0xEBfb47A7ad0FD6e57323C8A42B2E5A6a4F68fc1a"; // DAI Prize Pool +const PT_DAI_TICKET_ADDR = "0x334cBb5858417Aee161B53Ee0D5349cCF54514CF"; // PT DAI Ticket +const DAI_POOL_FAUCET_ADDR = "0xF362ce295F2A4eaE4348fFC8cDBCe8d729ccb8Eb"; // DAI POOL Faucet +const POOL_TOKEN_ADDRESS = "0x0cEC1A9154Ff802e7934Fc916Ed7Ca50bDE6844e"; // POOL Tocken +const TOKEN_FAUCET_PROXY_FACTORY_ADDR = + "0xE4E9cDB3E139D7E8a41172C20b6Ed17b6750f117"; // TokenFaucetProxyFactory for claimAll +const DAI_POD_ADDR = "0x2f994e2E4F3395649eeE8A89092e63Ca526dA829"; // DAI Pod +const UNISWAP_POOLETHLP_PRIZE_POOL_ADDR = + "0x3AF7072D29Adde20FC7e173a7CB9e45307d2FB0A"; // Uniswap Pool/ETH LP PrizePool +const UNISWAP_POOLETHLP_FAUCET_ADDR = + "0x9A29401EF1856b669f55Ae5b24505b3B6fAEb370"; // Uniswap Pool/ETH LP Faucet +const UNISWAP_POOLETHLP_TOKEN_ADDR = + "0x85cb0bab616fe88a89a35080516a8928f38b518b"; // Uniswap Pool/ETH Token +const PT_UNISWAP_POOLETHLP_TICKET_ADDR = + "0xeb8928ee92efb06c44d072a24c2bcb993b61e543"; // Pool Together Uniswap Pool/ETH LP Ticket +const POOL_PRIZE_POOL_ADDR = "0x396b4489da692788e327e2e4b2b0459a5ef26791"; // POOL Prize Pool +const PT_POOL_TICKET_ADDR = "0x27d22a7648e955e510a40bdb058333e9190d12d4"; // Pool Together POOL Ticket +const WETH_ADDR = "0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2"; // WETH +const DAI_POD_TOKEN_DROP = "0xc5209623E3dFdf9C0cCbe497c8012883C4147731"; + +// Community WETH Prize Pool (Rari): https://reference-app.pooltogether.com/pools/mainnet/0xa88ca010b32a54d446fc38091ddbca55750cbfc3/manage#stats +const WETH_PRIZE_POOL_ADDR = "0xa88ca010b32a54d446fc38091ddbca55750cbfc3"; // Community WETH Prize Pool (Rari) +const WETH_POOL_TICKET_ADDR = "0x9b5c30aeb9ce2a6a121cea9a85bc0d662f6d9b40"; // Community WETH Prize Pool Ticket (Rari) + +const prizePoolABI = [ + "function calculateEarlyExitFee( address from, address controlledToken, uint256 amount) external returns ( uint256 exitFee, uint256 burnedCredit)", +]; + +const podABI = [ + "function getEarlyExitFee(uint256 amount) external returns (uint256)", + "function balanceOfUnderlying(address user) external view returns (uint256 amount)", + "function drop() public returns (uint256)", + "function balanceOf(address account) external view returns (uint256)", +]; + +const POD_FACTORY_ADDRESS = "0x4e3a9f9fbafb2ec49727cffa2a411f7a0c1c4ce1"; +const podFactoryABI = [ + "function create( address _prizePool, address _ticket, address _faucet, address _manager, uint8 _decimals) external returns (address pod)", +]; + +const tokenDropABI = [ + "function claim(address user) external returns (uint256)", +]; + +describe("PoolTogether", function() { + const connectorName = "COMPOUND-TEST-A"; + const uniswapConnectorName = "UNISWAP-TEST-A"; + const ptConnectorName = "POOLTOGETHER-TEST-A"; + + let dsaWallet0: any; + let masterSigner: Signer; + let instaConnectorsV2: Contract; + let connector: any; + let ptConnector: any; + let uniswapConnector: any; + + const wallets = provider.getWallets(); + const [wallet0, wallet1, wallet2, wallet3] = wallets; + before(async () => { + await hre.network.provider.request({ + method: "hardhat_reset", + params: [ + { + forking: { + // @ts-ignore + jsonRpcUrl: hre.config.networks.hardhat.forking.url, + blockNumber: 12696000, + }, + }, + ], + }); + + masterSigner = await getMasterSigner(); + instaConnectorsV2 = await ethers.getContractAt( + abis.core.connectorsV2, + addresses.core.connectorsV2 + ); + + // Deploy and enable Compound Connector + connector = await deployAndEnableConnector({ + connectorName, + contractArtifact: ConnectV2Compound__factory, + signer: masterSigner, + connectors: instaConnectorsV2, + }); + + // Deploy and enable Pool Together Connector + ptConnector = await deployAndEnableConnector({ + connectorName: ptConnectorName, + contractArtifact: ConnectV2PoolTogether__factory, + signer: masterSigner, + connectors: instaConnectorsV2, + }); + + // Deploy and enable Uniswap Connector + uniswapConnector = await deployAndEnableConnector({ + connectorName: uniswapConnectorName, + contractArtifact: ConnectV2UniswapV2__factory, + signer: masterSigner, + connectors: instaConnectorsV2, + }); + }); + + it("Should have contracts deployed.", async function() { + expect(!!instaConnectorsV2.address).to.be.true; + expect(!!connector.address).to.be.true; + expect(!!ptConnector.address).to.be.true; + expect(!!uniswapConnector.address).to.be.true; + expect(!!(await masterSigner.getAddress())).to.be.true; + }); + + describe("DSA wallet setup", function() { + it("Should build DSA v2", async function() { + dsaWallet0 = await buildDSAv2(wallet0.address); + expect(!!dsaWallet0.address).to.be.true; + }); + + it("Deposit 10 ETH into DSA wallet", async function() { + await wallet0.sendTransaction({ + to: dsaWallet0.address, + value: ethers.utils.parseEther("10"), + }); + expect(await ethers.provider.getBalance(dsaWallet0.address)).to.be.gte( + ethers.utils.parseEther("10") + ); + }); + }); + + describe("Main - DAI Prize Pool Test", function() { + it("Should deposit 1 ETH in Compound", async function() { + const amount = ethers.utils.parseEther("1"); // 1 ETH + const spells = [ + { + connector: connectorName, + method: "deposit", + args: ["ETH-A", amount, 0, 0], + }, + ]; + + const tx = await dsaWallet0 + .connect(wallet0) + .cast(...encodeSpells(spells), wallet1.address); + const receipt = await tx.wait(); + expect(await ethers.provider.getBalance(dsaWallet0.address)).to.be.lte( + ethers.utils.parseEther("9") + ); + }); + + it("Should borrow 100 DAI from Compound and deposit DAI into DAI Prize Pool", async function() { + const amount = ethers.utils.parseEther("100"); // 100 DAI + const setId = "83478237"; + const spells = [ + { + connector: connectorName, + method: "borrow", + args: ["DAI-A", amount, 0, setId], + }, + { + connector: ptConnectorName, + method: "depositTo", + args: [DAI_PRIZE_POOL_ADDR, amount, PT_DAI_TICKET_ADDR, setId, 0], + }, + ]; + // Before Spell + let daiToken = await ethers.getContractAt( + abis.basic.erc20, + DAI_TOKEN_ADDR + ); + let daiBalance = await daiToken.balanceOf(dsaWallet0.address); + expect(daiBalance, `DAI balance is 0`).to.be.eq( + ethers.utils.parseEther("0") + ); + + let cToken = await ethers.getContractAt( + abis.basic.erc20, + PT_DAI_TICKET_ADDR + ); + const balance = await cToken.balanceOf(dsaWallet0.address); + expect(balance, `PoolTogether DAI Ticket balance is 0`).to.be.eq(0); + + // Run spell transaction + const tx = await dsaWallet0 + .connect(wallet0) + .cast(...encodeSpells(spells), wallet1.address); + const receipt = await tx.wait(); + + // After spell + daiBalance = await daiToken.balanceOf(dsaWallet0.address); + expect( + daiBalance, + `Expect DAI balance to still equal 0 since it was deposited into Prize Pool` + ).to.be.eq(0); + + const balanceAfter = await cToken.balanceOf(dsaWallet0.address); + expect( + balanceAfter, + `PoolTogether DAI Ticket balance equals 100` + ).to.be.eq(ethers.utils.parseEther("100")); + + // ETH used for transaction + expect(await ethers.provider.getBalance(dsaWallet0.address)).to.be.lte( + ethers.utils.parseEther("9") + ); + }); + + it("Should wait 11 days, withdraw all PrizePool, get back 100 DAI, and claim POOL", async function() { + const amount = ethers.utils.parseEther("100"); // 100 DAI + + let prizePoolContract = new ethers.Contract( + DAI_PRIZE_POOL_ADDR, + prizePoolABI, + ethers.provider + ); + let earlyExitFee = await prizePoolContract.callStatic[ + "calculateEarlyExitFee" + ](dsaWallet0.address, PT_DAI_TICKET_ADDR, amount); + expect( + earlyExitFee.exitFee, + "Exit Fee equal to 1 DAI because starts at 10%" + ).to.be.eq(ethers.utils.parseEther("1")); + + const spells = [ + { + connector: ptConnectorName, + method: "withdrawInstantlyFrom", + args: [ + DAI_PRIZE_POOL_ADDR, + amount, + PT_DAI_TICKET_ADDR, + earlyExitFee.exitFee, + 0, + 0, + ], + }, + { + connector: ptConnectorName, + method: "claim", + args: [DAI_POOL_FAUCET_ADDR, 0], + }, + ]; + + // Before spell + let daiToken = await ethers.getContractAt( + abis.basic.erc20, + DAI_TOKEN_ADDR + ); + let daiBalance = await daiToken.balanceOf(dsaWallet0.address); + expect(daiBalance, `DAI balance equals 0`).to.be.eq( + ethers.utils.parseEther("0") + ); + + let cToken = await ethers.getContractAt( + abis.basic.erc20, + PT_DAI_TICKET_ADDR + ); + const balance = await cToken.balanceOf(dsaWallet0.address); + expect(balance, `PoolTogether Dai Ticket is 100`).to.be.eq( + ethers.utils.parseEther("100") + ); + + let poolToken = await ethers.getContractAt( + abis.basic.erc20, + POOL_TOKEN_ADDRESS + ); + const poolBalance = await poolToken.balanceOf(dsaWallet0.address); + expect(poolBalance, `POOL Token equals 0`).to.be.eq( + ethers.utils.parseEther("0") + ); + + // Increase time by 11 days so we get back all DAI without early withdrawal fee + await ethers.provider.send("evm_increaseTime", [11 * 24 * 60 * 60]); + + earlyExitFee = await prizePoolContract.callStatic[ + "calculateEarlyExitFee" + ](dsaWallet0.address, PT_DAI_TICKET_ADDR, amount); + expect( + earlyExitFee.exitFee, + "Exit Fee equal to 0 DAI because past 10 days" + ).to.be.eq(0); + + // Run spell transaction + const tx = await dsaWallet0 + .connect(wallet0) + .cast(...encodeSpells(spells), wallet1.address); + const receipt = await tx.wait(); + + // After spell + daiBalance = await daiToken.balanceOf(dsaWallet0.address); + expect( + daiBalance, + `DAI balance to be equal to 100, because of no early withdrawal fee` + ).to.be.eq(ethers.utils.parseEther("100")); + + const balanceAfter = await cToken.balanceOf(dsaWallet0.address); + expect(balanceAfter, `PoolTogether Dai Ticket to equal 0`).to.be.eq(0); + + const poolBalanceAfter = await poolToken.balanceOf(dsaWallet0.address); + expect( + poolBalanceAfter, + `POOL Token Balance to be greater than 0` + ).to.be.gt(ethers.utils.parseEther("0")); + }); + + it("Should deposit and withdraw all PrizePool, get back less than 100 DAI", async function() { + const amount = ethers.utils.parseEther("100"); // 100 DAI + const exitFee = ethers.utils.parseEther("1"); // 1 DAI is 10% of 100 DAI + const spells = [ + { + connector: ptConnectorName, + method: "depositTo", + args: [DAI_PRIZE_POOL_ADDR, amount, PT_DAI_TICKET_ADDR, 0, 0], + }, + { + connector: ptConnectorName, + method: "withdrawInstantlyFrom", + args: [ + DAI_PRIZE_POOL_ADDR, + amount, + PT_DAI_TICKET_ADDR, + exitFee, + 0, + 0, + ], + }, + ]; + + // Before spell + let daiToken = await ethers.getContractAt( + abis.basic.erc20, + DAI_TOKEN_ADDR + ); + let daiBalance = await daiToken.balanceOf(dsaWallet0.address); + expect(daiBalance, `DAI Balance equals 0`).to.be.eq( + ethers.utils.parseEther("100") + ); + + let cToken = await ethers.getContractAt( + abis.basic.erc20, + PT_DAI_TICKET_ADDR + ); + const balance = await cToken.balanceOf(dsaWallet0.address); + expect(balance, `PoolTogether DAI Ticket equals 0`).to.be.eq(0); + + let poolToken = await ethers.getContractAt( + abis.basic.erc20, + POOL_TOKEN_ADDRESS + ); + const poolBalance = await poolToken.balanceOf(dsaWallet0.address); + expect(poolBalance, `PoolTogether Token greater than 0`).to.be.gt(0); + + // Run spell transaction + const tx = await dsaWallet0 + .connect(wallet0) + .cast(...encodeSpells(spells), wallet1.address); + const receipt = await tx.wait(); + + // After spell + daiBalance = await daiToken.balanceOf(dsaWallet0.address); + expect( + daiBalance, + `DAI balance to be less than 100, because of early withdrawal fee` + ).to.be.lt(ethers.utils.parseEther("100")); + + const balanceAfter = await cToken.balanceOf(dsaWallet0.address); + expect(balanceAfter, `PoolTogether Dai Ticket to equal 0`).to.be.eq(0); + + const poolBalanceAfter = await poolToken.balanceOf(dsaWallet0.address); + expect(poolBalanceAfter, `POOL Token Balance to greater than 0`).to.be.gt( + ethers.utils.parseEther("0") + ); + }); + + it("Should deposit, wait 11 days, and withdraw all PrizePool, get 99 DAI, and claim all POOL using claimAll", async function() { + const amount = ethers.utils.parseEther("99"); // 99 DAI + const depositSpells = [ + { + connector: ptConnectorName, + method: "depositTo", + args: [DAI_PRIZE_POOL_ADDR, amount, PT_DAI_TICKET_ADDR, 0, 0], + }, + ]; + + // Before spell + let daiToken = await ethers.getContractAt( + abis.basic.erc20, + DAI_TOKEN_ADDR + ); + let daiBalance = await daiToken.balanceOf(dsaWallet0.address); + expect(daiBalance, `DAI balance less than 100`).to.be.lt( + ethers.utils.parseEther("100") + ); + + let cToken = await ethers.getContractAt( + abis.basic.erc20, + PT_DAI_TICKET_ADDR + ); + const balance = await cToken.balanceOf(dsaWallet0.address); + expect(balance, `PoolTogether DAI Ticket equal 0`).to.be.eq(0); + + let poolToken = await ethers.getContractAt( + abis.basic.erc20, + POOL_TOKEN_ADDRESS + ); + const poolBalance = await poolToken.balanceOf(dsaWallet0.address); + expect(poolBalance, `POOL Token is greater than 0`).to.be.gt( + ethers.utils.parseEther("0") + ); + + // Run spell transaction + const tx = await dsaWallet0 + .connect(wallet0) + .cast(...encodeSpells(depositSpells), wallet1.address); + const receipt = await tx.wait(); + + const prizePoolContract = new ethers.Contract( + DAI_PRIZE_POOL_ADDR, + prizePoolABI, + ethers.provider + ); + let earlyExitFee = await prizePoolContract.callStatic[ + "calculateEarlyExitFee" + ](dsaWallet0.address, PT_DAI_TICKET_ADDR, amount); + expect( + earlyExitFee.exitFee, + "Exit Fee equal to .99 DAI because starts at 10%" + ).to.be.eq(ethers.utils.parseEther(".99")); + + // Increase time by 11 days so we get back all DAI without early withdrawal fee + await ethers.provider.send("evm_increaseTime", [11 * 24 * 60 * 60]); + + earlyExitFee = await prizePoolContract.callStatic[ + "calculateEarlyExitFee" + ](dsaWallet0.address, PT_DAI_TICKET_ADDR, amount); + expect( + earlyExitFee.exitFee, + "Exit Fee equal to 0 DAI because past 10 days" + ).to.be.eq(0); + + const withdrawSpells = [ + { + connector: ptConnectorName, + method: "withdrawInstantlyFrom", + args: [ + DAI_PRIZE_POOL_ADDR, + amount, + PT_DAI_TICKET_ADDR, + earlyExitFee.exitFee, + 0, + 0, + ], + }, + { + connector: ptConnectorName, + method: "claimAll", + args: [TOKEN_FAUCET_PROXY_FACTORY_ADDR, [DAI_POOL_FAUCET_ADDR]], + }, + ]; + + // Run spell transaction + const tx2 = await dsaWallet0 + .connect(wallet0) + .cast(...encodeSpells(withdrawSpells), wallet1.address); + const receipt2 = await tx2.wait(); + + // After spell + daiBalance = await daiToken.balanceOf(dsaWallet0.address); + expect(daiBalance, `DAI balance equals 99`).to.be.eq( + ethers.utils.parseEther("99") + ); + + const balanceAfter = await cToken.balanceOf(dsaWallet0.address); + expect(balanceAfter, `PoolTogether DAI Ticket equal 0`).to.be.eq(0); + + // Expect + const poolBalanceAfter = await poolToken.balanceOf(dsaWallet0.address); + expect(poolBalanceAfter, `Pool Token to be greateir than 0`).to.be.gt( + ethers.utils.parseEther("0") + ); + }); + }); + + describe("Main - DAI Pod Test", function() { + it("Should deposit 99 DAI in DAI Pod", async function() { + const amount = ethers.utils.parseEther("99"); // 99 DAI + const spells = [ + { + connector: ptConnectorName, + method: "depositToPod", + args: [DAI_TOKEN_ADDR, DAI_POD_ADDR, amount, 0, 0], + }, + ]; + + // Before spell + let daiToken = await ethers.getContractAt( + abis.basic.erc20, + DAI_TOKEN_ADDR + ); + let daiBalance = await daiToken.balanceOf(dsaWallet0.address); + expect(daiBalance, `DAI balance equals 99`).to.be.eq( + ethers.utils.parseEther("99") + ); + + let poolToken = await ethers.getContractAt( + abis.basic.erc20, + POOL_TOKEN_ADDRESS + ); + const poolBalance = await poolToken.balanceOf(dsaWallet0.address); + expect(poolBalance, `POOL Token greater than 0`).to.be.gte(0); + + let podToken = await ethers.getContractAt(abis.basic.erc20, DAI_POD_ADDR); + const podBalance = await podToken.balanceOf(dsaWallet0.address); + expect(podBalance, `Pod DAI Token equals 0`).to.be.eq(0); + + // Run spell transaction + const tx = await dsaWallet0 + .connect(wallet0) + .cast(...encodeSpells(spells), wallet1.address); + const receipt = await tx.wait(); + + // After spell + daiBalance = await daiToken.balanceOf(dsaWallet0.address); + expect(daiBalance, `DAI equals 0`).to.be.eq(0); + + const poolBalanceAfter = await poolToken.balanceOf(dsaWallet0.address); + expect(poolBalanceAfter, `POOL Token greater than 0`).to.be.gt(0); + + const podBalanceAfter = await podToken.balanceOf(dsaWallet0.address); + expect(podBalanceAfter, `Pod DAI token greater than 0`).to.be.eq( + ethers.utils.parseEther("99") + ); + }); + + it("Should claim rewards from pod token drop", async function() { + const spells = [ + { + connector: ptConnectorName, + method: "claimPodTokenDrop", + args: [DAI_POD_TOKEN_DROP, 0], + }, + ]; + + const tokenDropContract = new ethers.Contract( + DAI_POD_TOKEN_DROP, + tokenDropABI, + ethers.provider + ); + const podContract = new ethers.Contract( + DAI_POD_ADDR, + podABI, + masterSigner + ); + + // drop(): Claim TokenDrop asset for PrizePool Pod and transfers token(s) to external Pod TokenDrop + // dropt() also calls batch which, Deposit Pod float into PrizePool. Deposits the current float + // amount into the PrizePool and claims current POOL rewards. + const dropTx = await podContract.drop(); + await dropTx.wait(); + + // POOL Rewards able to claim from Pod Token Drop + let claimAmount = await tokenDropContract.callStatic["claim"]( + dsaWallet0.address + ); + + // Before spell + let poolToken = await ethers.getContractAt( + abis.basic.erc20, + POOL_TOKEN_ADDRESS + ); + const poolBalance = await poolToken.balanceOf(dsaWallet0.address); + expect(poolBalance, `POOL Token greater than 0`).to.be.gt(0); + + // Run spell transaction + const tx = await dsaWallet0 + .connect(wallet0) + .cast(...encodeSpells(spells), wallet1.address); + const receipt = await tx.wait(); + + // After spell + const poolBalanceAfter = await poolToken.balanceOf(dsaWallet0.address); + const total = claimAmount.add(poolBalance); + expect(poolBalanceAfter, `POOL Token same as before spell`).to.be.eq( + total + ); + }); + + it("Should wait 11 days, withdraw all podTokens, get back 99 DAI", async function() { + const amount = ethers.utils.parseEther("99"); // 99 DAI + + const podContract = new ethers.Contract( + DAI_POD_ADDR, + podABI, + ethers.provider + ); + let maxFee = await podContract.callStatic["getEarlyExitFee"](amount); + // maxFee depends on if token has been deposited to PrizePool yet + // since we called drop in previous test case, the tokens were deposited to PrizePool + expect( + maxFee, + "Exit Fee equal to .99 DAI because token still in float" + ).to.be.eq(ethers.utils.parseEther(".99")); + + const spells = [ + { + connector: ptConnectorName, + method: "withdrawFromPod", + args: [DAI_POD_ADDR, amount, maxFee, 0, 0], + }, + ]; + + // Before spell + let daiToken = await ethers.getContractAt( + abis.basic.erc20, + DAI_TOKEN_ADDR + ); + let daiBalance = await daiToken.balanceOf(dsaWallet0.address); + expect(daiBalance, `DAI Balance equals 0`).to.be.eq(0); + + let poolToken = await ethers.getContractAt( + abis.basic.erc20, + POOL_TOKEN_ADDRESS + ); + const poolBalance = await poolToken.balanceOf(dsaWallet0.address); + expect(poolBalance, `POOL Token balance greater than 0`).to.be.gt(0); + + let podToken = await ethers.getContractAt(abis.basic.erc20, DAI_POD_ADDR); + const podBalance = await podToken.balanceOf(dsaWallet0.address); + expect(podBalance, `Pod DAI Token equals 99`).to.be.eq( + ethers.utils.parseEther("99") + ); + + // Increase time by 11 days so we get back all DAI without early withdrawal fee + await ethers.provider.send("evm_increaseTime", [11 * 24 * 60 * 60]); + + // Run spell transaction + const tx = await dsaWallet0 + .connect(wallet0) + .cast(...encodeSpells(spells), wallet1.address); + const receipt = await tx.wait(); + + // After spell + daiBalance = await daiToken.balanceOf(dsaWallet0.address); + expect( + daiBalance, + `DAI balance equals 99, because of no early withdrawal fee` + ).to.be.eq(ethers.utils.parseEther("99")); + + const poolBalanceAfter = await poolToken.balanceOf(dsaWallet0.address); + expect(poolBalanceAfter, `POOL Token to be greater than 0`).to.be.gt(0); + + const podBalanceAfter = await podToken.balanceOf(dsaWallet0.address); + expect(podBalanceAfter, `Pod DAI Token equals 0`).to.be.eq(0); + }); + + it("Should deposit and withdraw from pod, get back same amount of 99 DAI", async function() { + const amount = ethers.utils.parseEther("99"); + const maxFee = 0; // maxFee 0 since it doesn't give chance for Pod to actually deposit into PrizePool + + const spells = [ + { + connector: ptConnectorName, + method: "depositToPod", + args: [DAI_TOKEN_ADDR, DAI_POD_ADDR, amount, 0, 0], + }, + { + connector: ptConnectorName, + method: "withdrawFromPod", + args: [DAI_POD_ADDR, amount, maxFee, 0, 0], + }, + ]; + + // Before spell + let daiToken = await ethers.getContractAt( + abis.basic.erc20, + DAI_TOKEN_ADDR + ); + let daiBalance = await daiToken.balanceOf(dsaWallet0.address); + expect(daiBalance, `DAI equals 99`).to.be.eq( + ethers.utils.parseEther("99") + ); + + let poolToken = await ethers.getContractAt( + abis.basic.erc20, + POOL_TOKEN_ADDRESS + ); + const poolBalance = await poolToken.balanceOf(dsaWallet0.address); + expect(poolBalance, `POOL Token greater than 0`).to.be.gt(0); + + // PodToken is 0 + let podToken = await ethers.getContractAt(abis.basic.erc20, DAI_POD_ADDR); + const podBalance = await podToken.balanceOf(dsaWallet0.address); + expect(podBalance, `Pod DAI Token equals 0`).to.be.eq(0); + + // Run spell transaction + const tx = await dsaWallet0 + .connect(wallet0) + .cast(...encodeSpells(spells), wallet1.address); + const receipt = await tx.wait(); + + // After spell + daiBalance = await daiToken.balanceOf(dsaWallet0.address); + expect( + daiBalance, + `DAI balance to be equal to 99, because funds still in 'float` + ).to.be.eq(ethers.utils.parseEther("99")); + + const poolBalanceAfter = await poolToken.balanceOf(dsaWallet0.address); + expect(poolBalanceAfter, `POOL Token same as before spell`).to.be.eq( + poolBalance + ); + + // Expect Pod Token Balance to equal 0 + const podBalanceAfter = await podToken.balanceOf(dsaWallet0.address); + expect(podBalanceAfter, `Pod DAI Token equals 0`).to.be.eq( + ethers.utils.parseEther("0") + ); + }); + }); + + describe("Main - UNISWAP POOL/ETH Prize Pool Test", function() { + it("Should use uniswap to swap ETH for POOL, deposit to POOL/ETH LP, deposit POOL/ETH LP to PrizePool", async function() { + const amount = ethers.utils.parseEther("100"); // 100 POOL + const slippage = ethers.utils.parseEther("0.03"); + const setId = "83478237"; + + const UniswapV2Router02ABI = [ + "function getAmountsOut(uint amountIn, address[] calldata path) external view returns (uint[] memory amounts)", + ]; + + // Get amount of ETH for 100 POOL from Uniswap + const UniswapV2Router02 = await ethers.getContractAt( + UniswapV2Router02ABI, + "0x7a250d5630B4cF539739dF2C5dAcb4c659F2488D" + ); + const amounts = await UniswapV2Router02.getAmountsOut(amount, [ + POOL_TOKEN_ADDRESS, + WETH_ADDR, + ]); + const unitAmount = ethers.utils.parseEther( + ((amounts[1] * 1.03) / amounts[0]).toString() + ); + + const spells = [ + { + connector: uniswapConnectorName, + method: "buy", + args: [ + POOL_TOKEN_ADDRESS, + tokens.eth.address, + amount, + unitAmount, + 0, + setId, + ], + }, + { + connector: uniswapConnectorName, + method: "deposit", + args: [ + POOL_TOKEN_ADDRESS, + tokens.eth.address, + amount, + unitAmount, + slippage, + 0, + setId, + ], + }, + { + connector: ptConnectorName, + method: "depositTo", + args: [ + UNISWAP_POOLETHLP_PRIZE_POOL_ADDR, + 0, + PT_UNISWAP_POOLETHLP_TICKET_ADDR, + setId, + 0, + ], + }, + ]; + + // Before Spell + let ethBalance = await ethers.provider.getBalance(dsaWallet0.address); + expect(ethBalance, `ETH Balance equals 9`).to.be.eq( + ethers.utils.parseEther("9") + ); + + let poolToken = await ethers.getContractAt( + abis.basic.erc20, + POOL_TOKEN_ADDRESS + ); + const poolBalance = await poolToken.balanceOf(dsaWallet0.address); + expect(poolBalance, `POOL Token greater than 0`).to.be.gte(0); + + let uniswapLPToken = await ethers.getContractAt( + abis.basic.erc20, + UNISWAP_POOLETHLP_TOKEN_ADDR + ); + const uniswapPoolEthBalance = await uniswapLPToken.balanceOf( + dsaWallet0.address + ); + expect(uniswapPoolEthBalance, `Uniswap POOL/ETH LP equals 0`).to.be.eq(0); + + let ptUniswapPoolEthToken = await ethers.getContractAt( + abis.basic.erc20, + PT_UNISWAP_POOLETHLP_TICKET_ADDR + ); + const ptUniswapPoolEthBalance = await ptUniswapPoolEthToken.balanceOf( + dsaWallet0.address + ); + expect( + ptUniswapPoolEthBalance, + `PoolTogether Uniswap POOL?ETH LP equals 0` + ).to.be.eq(0); + + // Run spell transaction + const tx = await dsaWallet0 + .connect(wallet0) + .cast(...encodeSpells(spells), wallet1.address); + const receipt = await tx.wait(); + + // After spell + ethBalance = await ethers.provider.getBalance(dsaWallet0.address); + expect(ethBalance, `ETH Balance less than 9`).to.be.lt( + ethers.utils.parseEther("9") + ); + + const poolBalanceAfter = await poolToken.balanceOf(dsaWallet0.address); + expect(poolBalanceAfter, `POOL Token to be same after spell`).to.be.eq( + poolBalance + ); + + const uniswapPoolEthBalanceAfter = await uniswapLPToken.balanceOf( + dsaWallet0.address + ); + expect( + uniswapPoolEthBalanceAfter, + `Uniswap POOL/ETH LP equals 0` + ).to.be.eq(0); + + const ptUniswapPoolEthBalanceAfter = await ptUniswapPoolEthToken.balanceOf( + dsaWallet0.address + ); + expect( + ptUniswapPoolEthBalanceAfter, + `PT Uniswap POOL/ETH LP to greater than 0` + ).to.be.gt(0); + }); + + it("Should withdraw all PrizePool, get back Uniswap LP, claim POOL, deposit claimed POOL into Pool PrizePool", async function() { + let ptUniswapPoolEthToken = await ethers.getContractAt( + abis.basic.erc20, + PT_UNISWAP_POOLETHLP_TICKET_ADDR + ); + const ptUniswapPoolEthBalance = await ptUniswapPoolEthToken.balanceOf( + dsaWallet0.address + ); + const setId = "83478237"; + + let uniswapPrizePoolContract = new ethers.Contract( + UNISWAP_POOLETHLP_PRIZE_POOL_ADDR, + prizePoolABI, + ethers.provider + ); + let earlyExitFee = await uniswapPrizePoolContract.callStatic[ + "calculateEarlyExitFee" + ]( + dsaWallet0.address, + PT_UNISWAP_POOLETHLP_TICKET_ADDR, + ptUniswapPoolEthBalance + ); + expect( + earlyExitFee.exitFee, + "Exit Fee equals 0 because no early exit fee for this prize pool" + ).to.be.eq(0); + + const spells = [ + { + connector: ptConnectorName, + method: "withdrawInstantlyFrom", + args: [ + UNISWAP_POOLETHLP_PRIZE_POOL_ADDR, + ptUniswapPoolEthBalance, + PT_UNISWAP_POOLETHLP_TICKET_ADDR, + earlyExitFee.exitFee, + 0, + 0, + ], + }, + { + connector: ptConnectorName, + method: "claim", + args: [UNISWAP_POOLETHLP_FAUCET_ADDR, setId], + }, + { + connector: ptConnectorName, + method: "depositTo", + args: [POOL_PRIZE_POOL_ADDR, 0, PT_POOL_TICKET_ADDR, setId, 0], + }, + ]; + + // Before spell + let poolToken = await ethers.getContractAt( + abis.basic.erc20, + POOL_TOKEN_ADDRESS + ); + const poolBalance = await poolToken.balanceOf(dsaWallet0.address); + expect(poolBalance, `POOL Token greater than 0`).to.be.gt(0); + + // Uniswap POOL/ETH LP is 0 + let uniswapLPToken = await ethers.getContractAt( + abis.basic.erc20, + UNISWAP_POOLETHLP_TOKEN_ADDR + ); + const uniswapPoolEthBalance = await uniswapLPToken.balanceOf( + dsaWallet0.address + ); + expect(uniswapPoolEthBalance, `Uniswap POOL/ETH LP equals 0`).to.be.eq(0); + + expect( + ptUniswapPoolEthBalance, + `PT Uniswap POOL/ETH LP greater than 0` + ).to.be.gt(0); + + let poolPoolTicket = await ethers.getContractAt( + abis.basic.erc20, + PT_POOL_TICKET_ADDR + ); + const poolPoolTicketBalance = await poolPoolTicket.balanceOf( + dsaWallet0.address + ); + expect( + poolPoolTicketBalance, + `PoolTogether POOL Ticket equals 0` + ).to.be.eq(0); + + // Run spell transaction + const tx = await dsaWallet0 + .connect(wallet0) + .cast(...encodeSpells(spells), wallet1.address); + const receipt = await tx.wait(); + + // After spell + const poolBalanceAfter = await poolToken.balanceOf(dsaWallet0.address); + expect( + poolBalanceAfter, + `Pool Token Balance equal to balance before spell` + ).to.be.eq(poolBalance); + + const uniswapPoolEthBalanceAfter = await uniswapLPToken.balanceOf( + dsaWallet0.address + ); + expect( + uniswapPoolEthBalanceAfter, + `Uniswap POOL/ETH LP to greater than 0` + ).to.be.gt(0); + + const ptUniswapPoolEthBalanceAfter = await ptUniswapPoolEthToken.balanceOf( + dsaWallet0.address + ); + expect( + ptUniswapPoolEthBalanceAfter, + `PT Uniswap POOL/ETH LP equal 0` + ).to.be.eq(0); + + const poolPoolTicketBalanceAfter = await poolPoolTicket.balanceOf( + dsaWallet0.address + ); + expect( + poolPoolTicketBalanceAfter, + `PoolTogether POOL Ticket greater than 0` + ).to.be.gt(0); + }); + }); + + describe("Main - WETH Prize Pool Test", function() { + it("Deposit 1 ETH into WETH Prize Pool and withdraw immediately", async function() { + const amount = ethers.utils.parseEther("1"); // 1 ETH + const setId = "83478237"; + const spells = [ + { + connector: ptConnectorName, + method: "depositTo", + args: [WETH_PRIZE_POOL_ADDR, amount, WETH_POOL_TICKET_ADDR, 0, setId], + }, + { + connector: ptConnectorName, + method: "withdrawInstantlyFrom", + args: [ + WETH_PRIZE_POOL_ADDR, + amount, + WETH_POOL_TICKET_ADDR, + amount, + setId, + 0, + ], + }, + ]; + // Before Spell + const ethBalanceBefore = await ethers.provider.getBalance( + dsaWallet0.address + ); + + // Run spell transaction + const tx = await dsaWallet0 + .connect(wallet0) + .cast(...encodeSpells(spells), wallet1.address); + const receipt = await tx.wait(); + + // After spell + const ethBalanceAfter = await ethers.provider.getBalance( + dsaWallet0.address + ); + + // ETH used for transaction + expect( + ethBalanceAfter, + `ETH Balance less than before spell because of early withdrawal fee` + ).to.be.lte(ethBalanceBefore); + }); + + it("Deposit 1 ETH into WETH Prize Pool, wait 14 days, then withdraw", async function() { + const amount = ethers.utils.parseEther("1"); // 1 ETH + const depositSpell = [ + { + connector: ptConnectorName, + method: "depositTo", + args: [WETH_PRIZE_POOL_ADDR, amount, WETH_POOL_TICKET_ADDR, 0, 0], + }, + ]; + + const withdrawSpell = [ + { + connector: ptConnectorName, + method: "withdrawInstantlyFrom", + args: [ + WETH_PRIZE_POOL_ADDR, + amount, + WETH_POOL_TICKET_ADDR, + amount, + 0, + 0, + ], + }, + ]; + + // Before Deposit Spell + let ethBalanceBefore = await ethers.provider.getBalance( + dsaWallet0.address + ); + + // Run deposit spell transaction + const tx = await dsaWallet0 + .connect(wallet0) + .cast(...encodeSpells(depositSpell), wallet1.address); + const receipt = await tx.wait(); + + // After Deposit spell + let ethBalanceAfter = await ethers.provider.getBalance( + dsaWallet0.address + ); + + expect(ethBalanceAfter, `ETH Balance less than before spell`).to.be.lte( + ethBalanceBefore + ); + + // Increase time by 11 days so we get back all ETH without early withdrawal fee + await ethers.provider.send("evm_increaseTime", [14 * 24 * 60 * 60]); + + // Run withdraw spell transaction + const tx2 = await dsaWallet0 + .connect(wallet0) + .cast(...encodeSpells(withdrawSpell), wallet1.address); + const receipt2 = await tx.wait(); + + // After Deposit spell + ethBalanceAfter = await ethers.provider.getBalance(dsaWallet0.address); + + expect( + ethBalanceAfter, + `ETH Balance equal to before spell because no early exit fee` + ).to.be.eq(ethBalanceBefore); + }); + }); + + describe("Main - WETH Pod Test", function() { + let podAddress: string; + it("Should deposit 1 ETH in WETH Pod and get Pod Ticket", async function() { + const amount = ethers.utils.parseEther("1"); + + // Create Pod for WETH Prize Pool (Rari) + const podFactoryContract = new ethers.Contract( + POD_FACTORY_ADDRESS, + podFactoryABI, + masterSigner + ); + podAddress = await podFactoryContract.callStatic.create( + WETH_PRIZE_POOL_ADDR, + WETH_POOL_TICKET_ADDR, + constants.address_zero, + wallet0.address, + 18 + ); + await podFactoryContract.create( + WETH_PRIZE_POOL_ADDR, + WETH_POOL_TICKET_ADDR, + constants.address_zero, + wallet0.address, + 18 + ); + + const spells = [ + { + connector: ptConnectorName, + method: "depositToPod", + args: [WETH_ADDR, podAddress, amount, 0, 0], + }, + ]; + + // Before Deposit Spell + const podContract = new ethers.Contract( + podAddress, + podABI, + ethers.provider + ); + let podBalanceBefore = await podContract.balanceOfUnderlying( + dsaWallet0.address + ); + expect(podBalanceBefore, `Pod balance equal to 0`).to.be.eq(0); + + let ethBalanceBefore = await ethers.provider.getBalance( + dsaWallet0.address + ); + + // Run spell transaction + const tx = await dsaWallet0 + .connect(wallet0) + .cast(...encodeSpells(spells), wallet1.address); + const receipt = await tx.wait(); + + // After Deposit spell + let ethBalanceAfter = await ethers.provider.getBalance( + dsaWallet0.address + ); + expect(ethBalanceAfter, `ETH balance less than before`).to.be.lt( + ethBalanceBefore + ); + + let podBalanceAfter = await podContract.balanceOfUnderlying( + dsaWallet0.address + ); + expect(podBalanceAfter, `Pod balance equal to 1`).to.be.eq( + ethers.utils.parseEther("1") + ); + }); + + it("Should withdraw 1 Ticket from WETH Pod and get back ETH", async function() { + const amount = ethers.utils.parseEther("1"); + + const podContract = new ethers.Contract( + podAddress, + podABI, + ethers.provider + ); + let maxFee = await podContract.callStatic["getEarlyExitFee"](amount); + expect( + maxFee, + "Exit Fee equal to 0 DAI because token still in float" + ).to.be.eq(0); + // maxFee depends on if token has been deposited to PrizePool yet + + const spells = [ + { + connector: ptConnectorName, + method: "withdrawFromPod", + args: [podAddress, amount, maxFee, 0, 0], + }, + ]; + + // Before Deposit Spell + let podBalanceBefore = await podContract.balanceOfUnderlying( + dsaWallet0.address + ); + expect(podBalanceBefore, `Pod balance equal to 1`).to.be.eq( + ethers.utils.parseEther("1") + ); + + let ethBalanceBefore = await ethers.provider.getBalance( + dsaWallet0.address + ); + + // Run spell transaction + const tx = await dsaWallet0 + .connect(wallet0) + .cast(...encodeSpells(spells), wallet1.address); + const receipt = await tx.wait(); + + // After Deposit spell + let ethBalanceAfter = await ethers.provider.getBalance( + dsaWallet0.address + ); + expect(ethBalanceAfter, `ETH balance greater than before`).to.be.gt( + ethBalanceBefore + ); + + let podBalanceAfter = await podContract.balanceOfUnderlying( + dsaWallet0.address + ); + expect(podBalanceAfter, `Pod balance equal to 0`).to.be.eq( + ethers.utils.parseEther("0") + ); + }); + }); +}); diff --git a/test/mainnet/spark/spark.test.ts b/test/mainnet/spark/spark.test.ts new file mode 100644 index 0000000..908cf18 --- /dev/null +++ b/test/mainnet/spark/spark.test.ts @@ -0,0 +1,301 @@ +import { expect } from "chai"; +import hre from "hardhat"; +import { abis } from "../../../scripts/constant/abis"; +import { addresses } from "../../../scripts/tests/mainnet/addresses"; +import { deployAndEnableConnector } from "../../../scripts/tests/deployAndEnableConnector"; +import { getMasterSigner } from "../../../scripts/tests/getMasterSigner"; +import { buildDSAv2 } from "../../../scripts/tests/buildDSAv2"; +import { ConnectV2Spark__factory } from "../../../typechain"; +import { parseEther } from "@ethersproject/units"; +import { encodeSpells } from "../../../scripts/tests/encodeSpells"; +import { tokens } from "../../../scripts/tests/mainnet/tokens"; +import { constants } from "../../../scripts/constant/constant"; +import { addLiquidity } from "../../../scripts/tests/addLiquidity"; +const { ethers } = hre; +import type { Signer, Contract } from "ethers"; + +describe("Sparklend", function () { + const connectorName = "SPARK-TEST-A"; + let connector: any; + + let wallet0: Signer, wallet1:Signer; + let dsaWallet0: any; + let instaConnectorsV2: Contract; + let masterSigner: Signer; + const account = "0x72a53cdbbcc1b9efa39c834a540550e23463aacb"; + let signer: any; + + const ABI = [ + "function balanceOf(address account) public view returns (uint256)", + "function approve(address spender, uint256 amount) external returns(bool)", + "function transfer(address recipient, uint256 amount) external returns (bool)" + ]; + + const wethContract = new ethers.Contract(tokens.weth.address, ABI); + const daiContract = new ethers.Contract(tokens.dai.address, ABI); + + before(async () => { + await hre.network.provider.request({ + method: "hardhat_reset", + params: [ + { + forking: { + // @ts-ignore + jsonRpcUrl: hre.config.networks.hardhat.forking.url, + // blockNumber: 12796965, + }, + }, + ], + }); + [wallet0, wallet1] = await ethers.getSigners(); + masterSigner = await getMasterSigner(); + instaConnectorsV2 = await ethers.getContractAt( + abis.core.connectorsV2, + addresses.core.connectorsV2 + ); + connector = await deployAndEnableConnector({ + connectorName, + contractArtifact: ConnectV2Spark__factory, + signer: masterSigner, + connectors: instaConnectorsV2, + }); + console.log("Connector address", connector.address); + + await hre.network.provider.request({ + method: "hardhat_impersonateAccount", + params: [account] + }); + signer = await ethers.getSigner(account); + }); + + it("should have contracts deployed", async () => { + expect(!!instaConnectorsV2.address).to.be.true; + expect(!!connector.address).to.be.true; + expect(!!(await masterSigner.getAddress())).to.be.true; + }); + + describe("DSA wallet setup", function () { + it("Should build DSA v2", async function () { + dsaWallet0 = await buildDSAv2(wallet0.getAddress()); + expect(!!dsaWallet0.address).to.be.true; + }); + + it("Deposit ETH into DSA wallet", async function () { + await wallet0.sendTransaction({ + to: dsaWallet0.address, + value: parseEther("10"), + }); + expect(await ethers.provider.getBalance(dsaWallet0.address)).to.be.gte( + parseEther("10") + ); + }); + }); + + describe("Main", function () { + it("should deposit ETH in Sparklend", async function () { + const amt = parseEther("1"); + const spells = [ + { + connector: connectorName, + method: "deposit", + args: [tokens.eth.address, amt, 0, 0], + }, + ]; + + const tx = await dsaWallet0 + .connect(wallet0) + .cast(...encodeSpells(spells), wallet1.getAddress()); + + await tx.wait(); + + expect(await ethers.provider.getBalance(dsaWallet0.address)).to.eq( + parseEther("9") + ); + }); + + it("Should borrow and payback DAI from Sparklend", async function () { + const amt = parseEther("100"); // 100 DAI + const setId = "83478237"; + const spells = [ + { + connector: connectorName, + method: "borrow", + args: [tokens.dai.address, amt, 2, 0, setId], + }, + { + connector: connectorName, + method: "payback", + args: [tokens.dai.address, amt, 2, setId, 0], + }, + ]; + + const tx = await dsaWallet0 + .connect(wallet0) + .cast(...encodeSpells(spells), wallet1.getAddress()); + await tx.wait(); + expect(await ethers.provider.getBalance(dsaWallet0.address)).to.be.lte( + ethers.utils.parseEther("9") + ); + }); + + it("Should borrow and payback half DAI from Sparklend", async function () { + const amt = parseEther("100"); // 100 DAI + // const setId = "83478237"; + await addLiquidity("dai", dsaWallet0.address, parseEther("1")); + let spells = [ + { + connector: connectorName, + method: "borrow", + args: [tokens.dai.address, amt, 2, 0, 0], + }, + { + connector: connectorName, + method: "payback", + args: [tokens.dai.address, amt.div(2), 2, 0, 0], + }, + ]; + + let tx = await dsaWallet0 + .connect(wallet0) + .cast(...encodeSpells(spells), wallet1.getAddress()); + await tx.wait(); + expect(await ethers.provider.getBalance(dsaWallet0.address)).to.be.lte( + ethers.utils.parseEther("9") + ); + + spells = [ + { + connector: connectorName, + method: "payback", + args: [tokens.dai.address, constants.max_value, 2, 0, 0], + }, + ]; + + tx = await dsaWallet0 + .connect(wallet0) + .cast(...encodeSpells(spells), wallet1.getAddress()); + await tx.wait(); + expect(await ethers.provider.getBalance(dsaWallet0.address)).to.be.lte( + ethers.utils.parseEther("9") + ); + }); + + it("Should deposit all ETH in Sparklend", async function () { + const spells = [ + { + connector: connectorName, + method: "deposit", + args: [tokens.eth.address, constants.max_value, 0, 0], + }, + ]; + + const tx = await dsaWallet0 + .connect(wallet0) + .cast(...encodeSpells(spells), wallet1.getAddress()); + await tx.wait(); + expect(await ethers.provider.getBalance(dsaWallet0.address)).to.be.lte( + ethers.utils.parseEther("0") + ); + }); + + it("Should withdraw all ETH from Sparklend", async function () { + const spells = [ + { + connector: connectorName, + method: "withdraw", + args: [tokens.eth.address, constants.max_value, 0, 0], + }, + ]; + + const tx = await dsaWallet0 + .connect(wallet0) + .cast(...encodeSpells(spells), wallet1.getAddress()); + await tx.wait(); + expect(await ethers.provider.getBalance(dsaWallet0.address)).to.be.gte( + ethers.utils.parseEther("10") + ); + }); + + it("should deposit and withdraw", async () => { + const amt = parseEther("1"); // 1 eth + const setId = "834782373"; + const spells = [ + { + connector: connectorName, + method: "deposit", + args: [tokens.eth.address, amt, 0, setId], + }, + { + connector: connectorName, + method: "withdraw", + args: [tokens.eth.address, amt, setId, 0], + }, + ]; + + const tx = await dsaWallet0 + .connect(wallet0) + .cast(...encodeSpells(spells), wallet1.getAddress()); + await tx.wait(); + expect(await ethers.provider.getBalance(dsaWallet0.address)).to.be.gte( + ethers.utils.parseEther("10") + ); + }); + + it("should deposit without collateral and withdraw", async () => { + const amt = parseEther("1"); // 1 eth + const setId = "834782373"; + const spells = [ + { + connector: connectorName, + method: "depositWithoutCollateral", + args: [tokens.eth.address, amt, 0, setId], + }, + { + connector: connectorName, + method: "withdraw", + args: [tokens.eth.address, amt, setId, 0], + }, + ]; + + const tx = await dsaWallet0 + .connect(wallet0) + .cast(...encodeSpells(spells), wallet1.getAddress()); + await tx.wait(); + expect(await ethers.provider.getBalance(dsaWallet0.address)).to.be.gte( + ethers.utils.parseEther("10") + ); + }); + + // it("should deposit ETH in Sparklend", async function () { + // const amt = parseEther("1"); + // const spells = [ + // { + // connector: connectorName, + // method: "deposit", + // args: [tokens.eth.address, amt, 0, 0], + // }, + // { + // connector: connectorName, + // method: "borrow", + // args: [tokens.dai.address, amt.mul(300), 2, 0, 0], + // }, + // { + // connector: connectorName, + // method: "paybackWithATokens", + // args: [tokens.dai.address, amt.mul(300), 2, 0, 0], + // }, + // ]; + + // const tx = await dsaWallet0 + // .connect(wallet0) + // .cast(...encodeSpells(spells), wallet1.getAddress()); + + // await tx.wait(); + + // expect(await ethers.provider.getBalance(dsaWallet0.address)).to.eq( + // parseEther("9") + // ); + // }); + + }); +}); diff --git a/test/mainnet/sushiswap/sushiswap.test.ts b/test/mainnet/sushiswap/sushiswap.test.ts new file mode 100644 index 0000000..8c70125 --- /dev/null +++ b/test/mainnet/sushiswap/sushiswap.test.ts @@ -0,0 +1,147 @@ +import { expect } from "chai"; +import hre from "hardhat"; +const { waffle, ethers } = hre; +const { provider } = waffle; + +import { deployAndEnableConnector } from "../../../scripts/tests/deployAndEnableConnector"; +import { buildDSAv2 } from "../../../scripts/tests/buildDSAv2"; +import { encodeSpells } from "../../../scripts/tests/encodeSpells"; +import { getMasterSigner } from "../../../scripts/tests/getMasterSigner"; +import { addLiquidity } from "../../../scripts/tests/addLiquidity"; + +import { addresses } from "../../../scripts/tests/mainnet/addresses"; +import { abis } from "../../../scripts/constant/abis"; +import { ConnectV2Sushiswap__factory, ConnectV2Sushiswap } from "../../../typechain"; +import type { Signer, Contract } from "ethers"; + +const DAI_ADDR = "0x6b175474e89094c44da98b954eedeac495271d0f"; + +describe("Sushiswap", function () { + const connectorName = "Sushiswap-v1"; + + let dsaWallet0: Contract; + let masterSigner: Signer; + let instaConnectorsV2: Contract; + let connector: Contract; + + const wallets = provider.getWallets(); + const [wallet0, wallet1, wallet2, wallet3] = wallets; + before(async () => { + await hre.network.provider.request({ + method: "hardhat_reset", + params: [ + { + forking: { + // @ts-ignore + jsonRpcUrl: hre.config.networks.hardhat.forking.url, + blockNumber: 13005785 + } + } + ] + }); + + masterSigner = await getMasterSigner(); + instaConnectorsV2 = await ethers.getContractAt(abis.core.connectorsV2, addresses.core.connectorsV2); + connector = await deployAndEnableConnector({ + connectorName, + contractArtifact: ConnectV2Sushiswap__factory, + signer: masterSigner, + connectors: instaConnectorsV2 + }); + console.log("Connector address", connector.address); + }); + + it("Should have contracts deployed.", async function () { + expect(!!instaConnectorsV2.address).to.be.true; + expect(!!connector.address).to.be.true; + expect(!!(await masterSigner.getAddress())).to.be.true; + }); + + describe("DSA wallet setup", function () { + it("Should build DSA v2", async function () { + dsaWallet0 = await buildDSAv2(wallet0.address); + expect(!!dsaWallet0.address).to.be.true; + }); + + it("Deposit ETH & DAI into DSA wallet", async function () { + await wallet0.sendTransaction({ + to: dsaWallet0.address, + value: ethers.utils.parseEther("10") + }); + expect(await ethers.provider.getBalance(dsaWallet0.address)).to.be.gte(ethers.utils.parseEther("10")); + + await addLiquidity("dai", dsaWallet0.address, ethers.utils.parseEther("100000")); + }); + + it("Deposit ETH & USDT into DSA wallet", async function () { + await wallet0.sendTransaction({ + to: dsaWallet0.address, + value: ethers.utils.parseEther("10") + }); + expect(await ethers.provider.getBalance(dsaWallet0.address)).to.be.gte(ethers.utils.parseEther("10")); + + await addLiquidity("usdt", dsaWallet0.address, ethers.utils.parseEther("100000")); + }); + }); + + describe("Main", function () { + it("Should deposit successfully", async function () { + const ethAmount = ethers.utils.parseEther("0.1"); // 1 ETH + const daiUnitAmount = ethers.utils.parseEther("4000"); // 1 ETH + const ethAddress = "0xeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee"; + + const getId = "0"; + const setId = "0"; + + const spells = [ + { + connector: connectorName, + method: "deposit", + args: [ethAddress, DAI_ADDR, ethAmount, daiUnitAmount, "500000000000000000", getId, setId] + } + ]; + + const tx = await dsaWallet0.connect(wallet0).cast(...encodeSpells(spells), wallet1.address); + let receipt = await tx.wait(); + }).timeout(10000000000); + + it("Should withdraw successfully", async function () { + const ethAmount = ethers.utils.parseEther("0.1"); // 1 ETH + const ethAddress = "0xeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee"; + + const getId = "0"; + const setIds = ["0", "0"]; + + const spells = [ + { + connector: connectorName, + method: "withdraw", + args: [ethAddress, DAI_ADDR, ethAmount, 0, 0, getId, setIds] + } + ]; + + const tx = await dsaWallet0.connect(wallet0).cast(...encodeSpells(spells), wallet1.address); + let receipt = await tx.wait(); + }); + + it("Should buy successfully", async function () { + const ethAmount = ethers.utils.parseEther("0.1"); // 1 ETH + const daiUnitAmount = ethers.utils.parseEther("4000"); // 1 ETH + const ethAddress = "0xeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee"; + + const getId = "0"; + const setId = "0"; + + const spells = [ + { + connector: connectorName, + method: "buy", + args: [ethAddress, DAI_ADDR, ethAmount, daiUnitAmount, getId, setId] + } + ]; + + const tx = await dsaWallet0.connect(wallet0).cast(...encodeSpells(spells), wallet1.address); + let receipt = await tx.wait(); + }); + }); +}); diff --git a/test/mainnet/sushiswapIncentive/sushiswapIncentive.test.ts b/test/mainnet/sushiswapIncentive/sushiswapIncentive.test.ts new file mode 100644 index 0000000..f0dca1a --- /dev/null +++ b/test/mainnet/sushiswapIncentive/sushiswapIncentive.test.ts @@ -0,0 +1,245 @@ +import { expect } from "chai"; +import hre from "hardhat"; +const { waffle, ethers } = hre; +const { provider } = waffle + +import {deployAndEnableConnector} from "../../../scripts/tests/deployAndEnableConnector"; +import {buildDSAv2} from "../../../scripts/tests/buildDSAv2"; +import {encodeSpells} from "../../../scripts/tests/encodeSpells"; +import {getMasterSigner} from "../../../scripts/tests/getMasterSigner"; +import {addLiquidity} from "../../../scripts/tests/addLiquidity"; +import {addresses} from "../../../scripts/tests/mainnet/addresses"; +import {abis} from "../../../scripts/constant/abis"; + +import { ConnectV2Sushiswap__factory, ConnectV2Sushiswap, ConnectV2SushiswapIncentive, ConnectV2SushiswapIncentive__factory } from "../../../typechain"; +import { Contract, Signer } from "ethers"; + +const DAI_ADDR = "0x6b175474e89094c44da98b954eedeac495271d0f" +const WETH_ADDR = "0xC02aaA39b223FE8D0A0e5C4F27eAD9083C756Cc2" + +describe("Sushiswap", function () { + const connectorName = "Sushiswap-v1" + const incentiveConnectorName = "Sushiswp-Incentive-v1" + + let dsaWallet0: Contract; + let masterSigner: Signer; + let instaConnectorsV2: Contract; + let connector: Contract, connectorIncentive; + + const wallets = provider.getWallets() + const [wallet0, wallet1, wallet2, wallet3] = wallets + before(async () => { + await hre.network.provider.request({ + method: "hardhat_reset", + params: [ + { + forking: { + // @ts-ignore + jsonRpcUrl: hre.config.networks.hardhat.forking.url, + blockNumber: 13005785, + }, + }, + ], + }); + masterSigner = await getMasterSigner() + instaConnectorsV2 = await ethers.getContractAt(abis.core.connectorsV2, addresses.core.connectorsV2); + connector = await deployAndEnableConnector({ + connectorName, + contractArtifact: ConnectV2Sushiswap__factory, + signer: masterSigner, + connectors: instaConnectorsV2 + }) + console.log("Connector address", connector.address) + + connectorIncentive = await deployAndEnableConnector({ + connectorName: incentiveConnectorName, + contractArtifact: ConnectV2SushiswapIncentive__factory, + signer: masterSigner, + connectors: instaConnectorsV2 + }) + console.log("Incentive Connector address", connectorIncentive.address) + }) + + it("Should have contracts deployed.", async function () { + expect(!!instaConnectorsV2.address).to.be.true; + expect(!!connector.address).to.be.true; + expect(!!(await masterSigner.getAddress())).to.be.true; + }); + + describe("DSA wallet setup", function () { + it("Should build DSA v2", async function () { + dsaWallet0 = await buildDSAv2(wallet0.address) + expect(!!dsaWallet0.address).to.be.true; + }); + + it("Deposit ETH & DAI into DSA wallet", async function () { + await wallet0.sendTransaction({ + to: dsaWallet0.address, + value: ethers.utils.parseEther("10") + }); + expect(await ethers.provider.getBalance(dsaWallet0.address)).to.be.gte(ethers.utils.parseEther("10")); + + await addLiquidity("dai", dsaWallet0.address, ethers.utils.parseEther("100000")); + }); + + it("Deposit ETH & USDT into DSA wallet", async function () { + await wallet0.sendTransaction({ + to: dsaWallet0.address, + value: ethers.utils.parseEther("10") + }); + expect(await ethers.provider.getBalance(dsaWallet0.address)).to.be.gte(ethers.utils.parseEther("10")); + + await addLiquidity("usdt", dsaWallet0.address, ethers.utils.parseEther("100000")); + }); + }); + + describe("Main", function () { + const data = { + poolId: 0, + version: 0, + lpToken: ethers.constants.AddressZero + } + + it("Should deposit successfully", async function () { + const ethAmount = ethers.utils.parseEther("2") // 1 ETH + const daiUnitAmount = ethers.utils.parseEther("4000") // 1 ETH + const ethAddress = "0xeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee" + + const getId = "0" + const setId = "0" + + const spells = [ + { + connector: connectorName, + method: "deposit", + args: [ + ethAddress, + DAI_ADDR, + ethAmount, + daiUnitAmount, + "500000000000000000", + getId, + setId + ], + } + ] + + const tx = await dsaWallet0.connect(wallet0).cast(...encodeSpells(spells), wallet1.address) + await tx.wait() + + describe("Incentive", () => { + it("Should deposit successfully", async () => { + const getId = 0 + const setId = 0 + const spells = [ + { + connector: incentiveConnectorName, + method: "deposit", + args: [ + WETH_ADDR, + DAI_ADDR, + ethers.utils.parseEther("10"), + getId, + setId, + data + ] + } + ] + + const tx = await dsaWallet0.connect(wallet0).cast(...encodeSpells(spells), wallet0.address) + await tx.wait(); + }) + + it("Should harvest successfully", async () => { + const setId = 0 + const spells = [ + { + connector: incentiveConnectorName, + method: "harvest", + args: [ + WETH_ADDR, + DAI_ADDR, + setId, + data + ] + } + ] + + const tx = await dsaWallet0.connect(wallet0).cast(...encodeSpells(spells), wallet0.address) + await tx.wait(); + }) + + it("Should harvest and withdraw successfully", async () => { + const getId = 0 + const setId = 0 + const spells = [ + { + connector: incentiveConnectorName, + method: "withdrawAndHarvest", + args: [ + WETH_ADDR, + DAI_ADDR, + ethers.utils.parseEther("1"), + getId, + setId, + data + ] + } + ] + + const tx = await dsaWallet0.connect(wallet0).cast(...encodeSpells(spells), wallet0.address) + await tx.wait(); + }) + + it("Should withdraw successfully", async () => { + const getId = 0 + const setId = 0 + const spells = [ + { + connector: incentiveConnectorName, + method: "withdraw", + args: [ + WETH_ADDR, + DAI_ADDR, + ethers.utils.parseEther("1"), + getId, + setId, + data + ] + } + ] + + const tx = await dsaWallet0.connect(wallet0).cast(...encodeSpells(spells), wallet0.address) + await tx.wait(); + }) + }) + }).timeout(10000000000); + + it("Should buy successfully", async function () { + const ethAmount = ethers.utils.parseEther("0.1") // 1 ETH + const daiUnitAmount = ethers.utils.parseEther("4000") // 1 ETH + const ethAddress = "0xeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee" + + const getId = "0" + const setId = "0" + + const spells = [ + { + connector: connectorName, + method: "buy", + args: [ + ethAddress, + DAI_ADDR, + ethAmount, + daiUnitAmount, + getId, + setId + ] + } + ] + + const tx = await dsaWallet0.connect(wallet0).cast(...encodeSpells(spells), wallet1.address) + let receipt = await tx.wait() + }); + }); +}) diff --git a/test/mainnet/swap/swap-test.ts b/test/mainnet/swap/swap-test.ts new file mode 100644 index 0000000..3c9102f --- /dev/null +++ b/test/mainnet/swap/swap-test.ts @@ -0,0 +1,211 @@ +import hre from "hardhat"; +import axios from "axios"; +import { expect } from "chai"; +const { ethers } = hre; //check +import { BigNumber } from "bignumber.js"; +import { deployAndEnableConnector } from "../../../scripts/tests/deployAndEnableConnector"; +import { buildDSAv2 } from "../../../scripts/tests/buildDSAv2"; +import { encodeSpells } from "../../../scripts/tests/encodeSpells"; +import { getMasterSigner } from "../../../scripts/tests/getMasterSigner"; +import { addresses } from "../../../scripts/tests/mainnet/addresses"; +import { abis } from "../../../scripts/constant/abis"; +import { ConnectV2SwapAggregator__factory } from "../../../typechain"; +import er20abi from "../../../scripts/constant/abi/basics/erc20.json"; +import type { Signer, Contract } from "ethers"; + +describe("Swap | Mainnet", function () { + const connectorName = "swap-test"; + + let dsaWallet0: Contract; + let wallet0: Signer, wallet1: Signer; + let masterSigner: Signer; + let instaConnectorsV2: Contract; + let connector: Contract; + + before(async () => { + await hre.network.provider.request({ + method: "hardhat_reset", + params: [ + { + forking: { + // @ts-ignore + jsonRpcUrl: hre.config.networks.hardhat.forking.url + } + } + ] + }); + [wallet0, wallet1] = await ethers.getSigners(); + + masterSigner = await getMasterSigner(); + instaConnectorsV2 = await ethers.getContractAt(abis.core.connectorsV2, addresses.core.connectorsV2); + connector = await deployAndEnableConnector({ + connectorName, + contractArtifact: ConnectV2SwapAggregator__factory, + signer: masterSigner, + connectors: instaConnectorsV2 + }); + console.log("Connector address", connector.address); + }); + + it("Should have contracts deployed.", async function () { + expect(!!instaConnectorsV2.address).to.be.true; + expect(!!connector.address).to.be.true; + expect(!!(await masterSigner.getAddress())).to.be.true; + }); + + describe("DSA wallet setup", function () { + it("Should build DSA v2", async function () { + dsaWallet0 = await buildDSAv2(await wallet0.getAddress()); + console.log(dsaWallet0.address); + expect(!!dsaWallet0.address).to.be.true; + }); + + it("Deposit matic into DSA wallet", async function () { + await wallet0.sendTransaction({ + to: dsaWallet0.address, + value: ethers.utils.parseEther("10") + }); + + expect(await ethers.provider.getBalance(dsaWallet0.address)).to.be.gte(ethers.utils.parseEther("10")); + }); + }); + + describe("Main", function () { + it("should swap the tokens", async function () { + let buyTokenAmountZeroX: any; + let buyTokenAmount1Inch: any; + let buyTokenAmountParaswap: any; + + async function getArg() { + const slippage = 0.5; + /* eth -> dai */ + const sellTokenAddress = "0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE"; // eth, decimals 18 + const sellTokenDecimals = 18; + const buyTokenAddress = "0x6B175474E89094C44Da98b954EedeAC495271d0F"; // DAI, decimals 18 + const buyTokenDecimals = 18; + const amount = 1; + + const srcAmount = new BigNumber(amount).times(new BigNumber(10).pow(sellTokenDecimals)).toFixed(0); + + let zeroXUrl = `https://api.0x.org/swap/v1/quote`; + let inchUrl = `https://api.1inch.exchange/v4.0/1/swap`; + let paraswapUrl1 = `https://apiv5.paraswap.io/prices/`; + let paraswapUrl2 = `https://apiv5.paraswap.io/transactions/1?ignoreChecks=true`; + + //zeroX + const paramsZeroX = { + buyToken: "DAI", + sellToken: "ETH", + sellAmount: "1000000000000000000" // Always denominated in wei + }; + + const responseZeroX = await axios.get(zeroXUrl, { params: paramsZeroX }).then((data: any) => data); + buyTokenAmountZeroX = responseZeroX.data.buyAmount; + const calldataZeroX = responseZeroX.data.data; + + //paraswap + let paramsPara = { + srcToken: sellTokenAddress, + destToken: buyTokenAddress, + srcDecimals: sellTokenDecimals, + destDecimals: buyTokenDecimals, + amount: srcAmount, + side: "SELL", + network: 1 + }; + + const priceRoute = (await axios.get(paraswapUrl1, { params: paramsPara })).data.priceRoute; + buyTokenAmountParaswap = priceRoute.destAmount; + let minAmount = new BigNumber(priceRoute.destAmount).times((100 - 1) / 100).toFixed(0); + + let txConfig = { + priceRoute: priceRoute, + srcToken: sellTokenAddress, + destToken: buyTokenAddress, + srcDecimals: sellTokenDecimals, + destDecimals: buyTokenDecimals, + srcAmount: srcAmount, + destAmount: minAmount, + userAddress: dsaWallet0.address + }; + const calldataPara = (await axios.post(paraswapUrl2, txConfig)).data.data; + + //1inch + const paramDaiUsdc = { + buyToken: buyTokenAddress, + sellToken: sellTokenAddress, + sellAmount: "1000000000000000000", + dsaAddress: dsaWallet0.address + }; + const response1 = await axios.get("https://api.instadapp.io/defi/mainnet/1inch/swap", { + params: paramDaiUsdc + }); + + const data1 = response1.data; + // console.log(data1); + let unitAmt1Inch = data1.unitAmt; + const calldata1Inch = data1.calldata; + buyTokenAmount1Inch = data1.buyTokenAmount; + console.log(buyTokenAmount1Inch); + + let calculateUnitAmt = (buyAmount: any) => { + const buyTokenAmountRes = new BigNumber(buyAmount) + .dividedBy(new BigNumber(10).pow(buyTokenDecimals)) + .toFixed(8); + + let unitAmt: any = new BigNumber(buyTokenAmountRes).dividedBy(new BigNumber(amount)); + + unitAmt = unitAmt.multipliedBy((100 - 1) / 100); + unitAmt = unitAmt.multipliedBy(1e18).toFixed(0); + return unitAmt; + }; + let unitAmt0x = calculateUnitAmt(buyTokenAmountZeroX); + let unitAmtParaswap = calculateUnitAmt(buyTokenAmountParaswap); + + function getCallData(connector: string, unitAmt: any, callData: any) { + var abi = [ + "function swap(address,address,uint256,uint256,bytes,uint256)", + "function sell(address,address,uint256,uint256,bytes,uint256)" + ]; + var iface = new ethers.utils.Interface(abi); + const spell = connector === "1INCH-A" ? "sell" : "swap"; + let data = iface.encodeFunctionData(spell, [ + buyTokenAddress, + sellTokenAddress, + srcAmount, + unitAmt, + callData, + 0 + ]); + return data; + } + let data1Inch = ethers.utils.hexlify(await getCallData("1INCH-A", unitAmt1Inch, calldata1Inch)); + let dataPara = ethers.utils.hexlify(await getCallData("PARASWAP-A", unitAmtParaswap, calldataPara)); + let dataZeroX = ethers.utils.hexlify(await getCallData("ZEROX-A", unitAmt0x, calldataZeroX)); + let datas = [data1Inch, dataPara, dataZeroX]; + + let connectors = ["1INCH-A", "PARASWAP-A", "ZEROX-A"]; + return [connectors, datas]; + } + + let arg = await getArg(); + const spells = [ + { + connector: connectorName, + method: "swap", + args: arg + } + ]; + const tx = await dsaWallet0.connect(wallet0).cast(...encodeSpells(spells), await wallet1.getAddress()); + const receipt = await tx.wait(); + + const daiToken = await ethers.getContractAt( + er20abi, + "0x6B175474E89094C44Da98b954EedeAC495271d0F" // dai address + ); + + expect(await daiToken.balanceOf(dsaWallet0.address)).to.be.gte(buyTokenAmount1Inch); + expect(await ethers.provider.getBalance(dsaWallet0.address)).to.be.lte(ethers.utils.parseEther("9")); + }); + }); +}); diff --git a/test/mainnet/ubiquity/ubiquity.test.ts b/test/mainnet/ubiquity/ubiquity.test.ts new file mode 100644 index 0000000..6c7409b --- /dev/null +++ b/test/mainnet/ubiquity/ubiquity.test.ts @@ -0,0 +1,462 @@ +import { expect } from "chai"; +import hre from "hardhat"; +const { waffle, ethers } = hre; +const { provider } = waffle; +const { BigNumber, utils } = ethers; + +import {deployAndEnableConnector} from "../../../scripts/tests/deployAndEnableConnector"; +import {buildDSAv2} from "../../../scripts/tests/buildDSAv2"; +import {encodeSpells} from "../../../scripts/tests/encodeSpells"; +import {addresses} from "../../../scripts/tests/mainnet/addresses"; +import {abis} from "../../../scripts/constant/abis"; +import {impersonateAccounts} from "../../../scripts/tests/impersonate"; +import type { Signer, Contract, BigNumberish } from "ethers"; +import {forkReset, sendEth, mineNBlock} from "./utils"; +import { ConnectV2Ubiquity__factory } from "../../../typechain"; + +import { abi as implementationsABI } from "../../../scripts/constant/abi/core/InstaImplementations.json"; +const implementationsMappingAddr = "0xCBA828153d3a85b30B5b912e1f2daCac5816aE9D"; + +describe("Ubiquity", function () { + const ubiquityTest = "UBIQUITY-TEST-A"; + + const BOND = "0x2dA07859613C14F6f05c97eFE37B9B4F212b5eF5"; + const UAD = "0x0F644658510c95CB46955e55D7BA9DDa9E9fBEc6"; + const DAI = "0x6B175474E89094C44Da98b954EedeAC495271d0F"; + const USDC = "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48"; + const USDT = "0xdAC17F958D2ee523a2206206994597C13D831ec7"; + const CRV3 = "0x6c3F90f043a72FA612cbac8115EE7e52BDe6E490"; + const POOL3 = "0xbEbc44782C7dB0a1A60Cb6fe97d0b483032FF1C7"; + const UAD3CRVF = "0x20955CB69Ae1515962177D164dfC9522feef567E"; + + const ethWhaleAddress = "0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2"; + const uadWhaleAddress = "0xefC0e701A824943b469a694aC564Aa1efF7Ab7dd"; + + const blockFork = 13097100; + const one = BigNumber.from(10).pow(18); + const onep = BigNumber.from(10).pow(6); + const ABI = [ + "function balanceOf(address owner) view returns (uint256)", + "function allowance(address owner, address spender) external view returns (uint256)", + "function transfer(address to, uint amount) returns (boolean)", + "function remove_liquidity_one_coin(uint256 _burn_amount, int128 i, uint256 _min_received) external returns (uint256)", + "function add_liquidity(uint256[3],uint256) returns (uint256)", + "function approve(address, uint256) external", + "function holderTokens(address) view returns (uint256[])", + "function getBond(uint256) view returns (tuple(address,uint256,uint256,uint256,uint256,uint256))" + ]; + let dsa: Contract; + let POOL3Contract: Contract; + let CRV3Contract: Contract; + let uAD3CRVfContract: Contract; + let uADContract: Contract; + let DAIContract: Contract; + let USDCContract: Contract; + let USDTContract: Contract; + let BONDContract: Contract; + let instaIndex: Contract; + let instaConnectorsV2: Contract; + let connector: Contract; + let instaImplementationsMapping; + let InstaAccountV2DefaultImpl; + + let uadWhale; + + const bondingShare = async function (address: any) { + let lpAmount = BigNumber.from(0); + let lpAmountTotal = BigNumber.from(0); + let bondId = -1; + + const bondIds = await BONDContract.holderTokens(address); + const bondN = bondIds?.length || 0; + + if (bondN) { + for await (bondId of bondIds) { + lpAmountTotal = lpAmountTotal.add((await BONDContract.getBond(bondId))[5]); + } + bondId = Number(bondIds[bondN - 1]); + lpAmount = (await BONDContract.getBond(bondId))[5]; + } + return { bondId, bondN, lpAmount, lpAmountTotal }; + }; + + const depositAndGetOneBond = async function () { + await dsaDepositUAD3CRVf(100); + dsa.cast( + ...encodeSpells([ + { + connector: ubiquityTest, + method: "deposit", + args: [UAD3CRVF, one.mul(100), 1, 0, 0] + } + ]), + uadWhaleAddress + ); + }; + + before(async () => { + // await forkReset(blockFork); + await hre.network.provider.request({ + method: "hardhat_reset", + params: [ + { + forking: { + // @ts-ignore + jsonRpcUrl: hre.config.networks.hardhat.forking.url, + blockNumber: 13097100, + }, + }, + ], + }); + [uadWhale] = await impersonateAccounts([uadWhaleAddress]); + const [ethWhale] = await impersonateAccounts([ethWhaleAddress]); + + await sendEth(ethWhale, uadWhaleAddress, 100); + POOL3Contract = new ethers.Contract(POOL3, ABI, uadWhale); + CRV3Contract = new ethers.Contract(CRV3, ABI, uadWhale); + uAD3CRVfContract = new ethers.Contract(UAD3CRVF, ABI, uadWhale); + uADContract = new ethers.Contract(UAD, ABI, uadWhale); + DAIContract = new ethers.Contract(DAI, ABI, uadWhale); + USDCContract = new ethers.Contract(USDC, ABI, uadWhale); + USDTContract = new ethers.Contract(USDT, ABI, uadWhale); + BONDContract = new ethers.Contract(BOND, ABI, uadWhale); + dsa = (await buildDSAv2(uadWhaleAddress)).connect(uadWhale); + await sendEth(ethWhale, dsa.address, 100); + await sendEth(ethWhale, uadWhaleAddress, 100); + + instaIndex = new ethers.Contract(addresses.core.instaIndex, abis.core.instaIndex, ethWhale); + + const masterAddress = await instaIndex.master(); + const [master] = await impersonateAccounts([masterAddress]); + await sendEth(ethWhale, masterAddress, 100); + + instaConnectorsV2 = new ethers.Contract(addresses.core.connectorsV2, abis.core.connectorsV2); + + instaImplementationsMapping = await ethers.getContractAt(implementationsABI, implementationsMappingAddr); + InstaAccountV2DefaultImpl = await ethers.getContractFactory("InstaDefaultImplementation"); + InstaAccountV2DefaultImpl = await InstaAccountV2DefaultImpl.deploy(addresses.core.instaIndex); + await InstaAccountV2DefaultImpl.deployed(); + await ( + await instaImplementationsMapping.connect(master).setDefaultImplementation(InstaAccountV2DefaultImpl.address) + ).wait(); + + connector = await deployAndEnableConnector({ + connectorName: ubiquityTest, + contractArtifact: ConnectV2Ubiquity__factory, + signer: master, + connectors: instaConnectorsV2 + }); + }); + + const logAll = async function () { + console.log("dsa eth", utils.formatEther(await ethers.provider.getBalance(dsa.address))); + console.log("dsa dai", utils.formatEther(await DAIContract.balanceOf(dsa.address))); + console.log("dsa usdc", utils.formatUnits(await USDCContract.balanceOf(dsa.address), 6)); + console.log("dsa usdt", utils.formatUnits(await USDTContract.balanceOf(dsa.address), 6)); + console.log("dsa uad", utils.formatEther(await uADContract.balanceOf(dsa.address))); + console.log("dsa 3CRV", utils.formatEther(await CRV3Contract.balanceOf(dsa.address))); + console.log("dsa uad3CRV-f", utils.formatEther(await uAD3CRVfContract.balanceOf(dsa.address))); + const { bondId, bondN, lpAmount, lpAmountTotal } = await bondingShare(dsa.address); + console.log("dsa n bonds", utils.formatEther(lpAmountTotal), bondN); + console.log("dsa last bond", utils.formatEther(lpAmount), bondId); + }; + + afterEach(logAll); + + const dsaDepositUAD3CRVf = async (amount: BigNumberish) => { + await uAD3CRVfContract.transfer(dsa.address, one.mul(amount)); + }; + + const dsaDepositUAD = async (amount: BigNumberish) => { + await uAD3CRVfContract.remove_liquidity_one_coin(one.mul(amount).mul(110).div(100), 0, one.mul(amount)); + await uADContract.transfer(dsa.address, one.mul(amount)); + }; + + const dsaDepositCRV3 = async (amount: BigNumberish) => { + await uAD3CRVfContract.remove_liquidity_one_coin(one.mul(amount).mul(110).div(100), 1, one.mul(amount)); + await CRV3Contract.transfer(dsa.address, one.mul(amount)); + }; + + const dsaDepositDAI = async (amount: BigNumberish) => { + await uAD3CRVfContract.remove_liquidity_one_coin( + one.mul(amount).mul(120).div(100), + 1, + one.mul(amount).mul(110).div(100) + ); + await POOL3Contract.remove_liquidity_one_coin(one.mul(amount).mul(110).div(100), 0, one.mul(amount)); + await DAIContract.transfer(dsa.address, one.mul(amount)); + }; + const dsaDepositUSDC = async (amount: BigNumberish) => { + await uAD3CRVfContract.remove_liquidity_one_coin( + one.mul(amount).mul(120).div(100), + 1, + one.mul(amount).mul(110).div(100) + ); + await POOL3Contract.remove_liquidity_one_coin(one.mul(amount).mul(110).div(100), 1, onep.mul(amount)); + await USDCContract.transfer(dsa.address, onep.mul(amount)); + }; + const dsaDepositUSDT = async (amount: BigNumberish) => { + await uAD3CRVfContract.remove_liquidity_one_coin( + one.mul(amount).mul(120).div(100), + 1, + one.mul(amount).mul(110).div(100) + ); + await POOL3Contract.remove_liquidity_one_coin(one.mul(amount).mul(110).div(100), 2, onep.mul(amount)); + await USDTContract.transfer(dsa.address, onep.mul(amount)); + }; + + describe("Deposit", function () { + it("should deposit uAD3CRVf to get Ubiquity Bonding Shares", async function () { + await logAll(); + await dsaDepositUAD3CRVf(100); + expect((await bondingShare(dsa.address)).lpAmount).to.be.equal(0); + await expect( + dsa.cast( + ...encodeSpells([ + { + connector: ubiquityTest, + method: "deposit", + args: [UAD3CRVF, one.mul(100), 4, 0, 0] + } + ]), + uadWhaleAddress + ) + ).to.be.not.reverted; + expect((await bondingShare(dsa.address)).lpAmount).to.be.gt(0); + }); + + it("should deposit uAD to get Ubiquity Bonding Shares", async function () { + await dsaDepositUAD(100); + await expect( + dsa.cast( + ...encodeSpells([ + { + connector: ubiquityTest, + method: "deposit", + args: [UAD, one.mul(100), 4, 0, 0] + } + ]), + uadWhaleAddress + ) + ).to.be.not.reverted; + expect((await bondingShare(dsa.address)).lpAmount).to.be.gt(0); + }); + + it("should deposit 3CRV to get Ubiquity Bonding Shares", async function () { + await dsaDepositCRV3(100); + await expect( + dsa.cast( + ...encodeSpells([ + { + connector: ubiquityTest, + method: "deposit", + args: [CRV3, one.mul(100), 4, 0, 0] + } + ]), + uadWhaleAddress + ) + ).to.be.not.reverted; + expect((await bondingShare(dsa.address)).lpAmount).to.be.gt(0); + }); + + it("should deposit DAI to get Ubiquity Bonding Shares", async function () { + await dsaDepositDAI(100); + await expect( + dsa.cast( + ...encodeSpells([ + { + connector: ubiquityTest, + method: "deposit", + args: [DAI, one.mul(100), 4, 0, 0] + } + ]), + uadWhaleAddress + ) + ).to.be.not.reverted; + expect((await bondingShare(dsa.address)).lpAmount).to.be.gt(0); + }); + + it("should deposit USDC to get Ubiquity Bonding Shares", async function () { + await dsaDepositUSDC(100); + await expect( + dsa.cast( + ...encodeSpells([ + { + connector: ubiquityTest, + method: "deposit", + args: [USDC, onep.mul(100), 4, 0, 0] + } + ]), + uadWhaleAddress + ) + ).to.be.not.reverted; + expect((await bondingShare(dsa.address)).lpAmount).to.be.gt(0); + }); + + it("should deposit USDT to get Ubiquity Bonding Shares", async function () { + await dsaDepositUSDT(100); + await expect( + dsa.cast( + ...encodeSpells([ + { + connector: ubiquityTest, + method: "deposit", + args: [USDT, onep.mul(100), 4, 0, 0] + } + ]), + uadWhaleAddress + ) + ).to.be.not.reverted; + expect((await bondingShare(dsa.address)).lpAmount).to.be.gt(0); + }); + }); + + describe("Withdraw", function () { + let bondId = -1; + + before(async () => { + await depositAndGetOneBond(); + await depositAndGetOneBond(); + await depositAndGetOneBond(); + await depositAndGetOneBond(); + await depositAndGetOneBond(); + await depositAndGetOneBond(); + ({ bondId } = await bondingShare(dsa.address)); + + await logAll(); + console.log("Mining 50 000 blocks for more than one week, please wait..."); + await mineNBlock(50000, 1); + }); + + it("Should deposit and withdraw DAI", async function () { + await expect( + dsa.cast( + ...encodeSpells([ + { + connector: ubiquityTest, + method: "withdraw", + args: [bondId, DAI, 0, 0] + } + ]), + uadWhaleAddress + ) + ).to.be.not.reverted; + }); + + it("Should deposit and withdraw USDC", async function () { + // await expect( + dsa.cast( + ...encodeSpells([ + { + connector: ubiquityTest, + method: "withdraw", + args: [bondId - 1, USDC, 0, 0] + } + ]), + uadWhaleAddress + ); + // ).to.be.not.reverted; + }); + + it("Should deposit and withdraw USDT", async function () { + await expect( + dsa.cast( + ...encodeSpells([ + { + connector: ubiquityTest, + method: "withdraw", + args: [bondId - 2, USDT, 0, 0] + } + ]), + uadWhaleAddress + ) + ).to.be.not.reverted; + }); + + it("Should deposit and withdraw UAD", async function () { + await expect( + dsa.cast( + ...encodeSpells([ + { + connector: ubiquityTest, + method: "withdraw", + args: [bondId - 3, UAD, 0, 0] + } + ]), + uadWhaleAddress + ) + ).to.be.not.reverted; + }); + + it("Should deposit and withdraw CRV3", async function () { + await expect( + dsa.cast( + ...encodeSpells([ + { + connector: ubiquityTest, + method: "withdraw", + args: [bondId - 4, CRV3, 0, 0] + } + ]), + uadWhaleAddress + ) + ).to.be.not.reverted; + }); + + it("Should deposit and withdraw UAD3CRVF", async function () { + await expect( + dsa.cast( + ...encodeSpells([ + { + connector: ubiquityTest, + method: "withdraw", + args: [bondId - 5, UAD3CRVF, 0, 0] + } + ]), + uadWhaleAddress + ) + ).to.be.not.reverted; + }); + }); + + describe("DSA wallet setup", function () { + it("Should have contracts deployed.", async function () { + expect(POOL3Contract.address).to.be.properAddress; + expect(CRV3Contract.address).to.be.properAddress; + expect(uADContract.address).to.be.properAddress; + expect(uAD3CRVfContract.address).to.be.properAddress; + expect(DAIContract.address).to.be.properAddress; + expect(USDCContract.address).to.be.properAddress; + expect(USDTContract.address).to.be.properAddress; + expect(BONDContract.address).to.be.properAddress; + expect(instaIndex.address).to.be.properAddress; + expect(instaConnectorsV2.address).to.be.properAddress; + expect(connector.address).to.be.properAddress; + expect(dsa.address).to.be.properAddress; + }); + it("Should deposit uAD3CRVf into DSA wallet", async function () { + await dsaDepositUAD3CRVf(100); + expect(await uAD3CRVfContract.balanceOf(dsa.address)).to.be.gte(one.mul(100)); + }); + it("Should deposit uAD into DSA wallet", async function () { + await dsaDepositUAD(100); + expect(await uADContract.balanceOf(dsa.address)).to.be.gte(one.mul(100)); + }); + it("Should deposit 3CRV into DSA wallet", async function () { + await dsaDepositCRV3(100); + expect(await CRV3Contract.balanceOf(dsa.address)).to.be.gte(one.mul(100)); + }); + it("Should deposit DAI into DSA wallet", async function () { + await dsaDepositDAI(100); + expect(await DAIContract.balanceOf(dsa.address)).to.be.gte(one.mul(100)); + }); + it("Should deposit USDC into DSA wallet", async function () { + await dsaDepositUSDC(100); + expect(await USDCContract.balanceOf(dsa.address)).to.be.gte(onep.mul(100)); + }); + it("Should deposit USDT into DSA wallet", async function () { + await dsaDepositUSDT(100); + expect(await USDTContract.balanceOf(dsa.address)).to.be.gte(onep.mul(100)); + }); + }); +}); diff --git a/test/mainnet/ubiquity/utils.ts b/test/mainnet/ubiquity/utils.ts new file mode 100644 index 0000000..0ab1daa --- /dev/null +++ b/test/mainnet/ubiquity/utils.ts @@ -0,0 +1,55 @@ +import hre, { ethers, network } from "hardhat"; +import hardhatConfig from "../../../hardhat.config"; + +export async function forkReset(blockNumber: any) { + await hre.network.provider.request({ + method: "hardhat_reset", + params: [ + { + forking: { + // @ts-ignore + jsonRpcUrl: hardhatConfig.networks.hardhat.forking.url, + blockNumber + } + } + ] + }); +} + +export async function mineBlock(timestamp: any) { + await network.provider.request({ + method: "evm_mine", + params: [timestamp] + }); +} + +export async function sendEth(from: any, to: any, amount: any) { + await from.sendTransaction({ + to: to, + value: ethers.BigNumber.from(10).pow(18).mul(amount) + }); +} + +export async function mineNBlock(blockCount: any, secondsBetweenBlock: any) { + const blockBefore = await ethers.provider.getBlock("latest"); + const maxMinedBlockPerBatch = 1000; + let blockToMine = blockCount; + let blockTime = blockBefore.timestamp; + while (blockToMine > maxMinedBlockPerBatch) { + // eslint-disable-next-line @typescript-eslint/no-loop-func + const minings: any = [maxMinedBlockPerBatch].map((_v, i) => { + const newTs = blockTime + i + (secondsBetweenBlock || 1); + return mineBlock(newTs); + }); + // eslint-disable-next-line no-await-in-loop + await Promise.all(minings); + blockToMine -= maxMinedBlockPerBatch; + blockTime = blockTime + maxMinedBlockPerBatch - 1 + maxMinedBlockPerBatch * (secondsBetweenBlock || 1); + } + const minings = [blockToMine].map((_v, i) => { + const newTs = blockTime + i + (secondsBetweenBlock || 1); + return mineBlock(newTs); + }); + // eslint-disable-next-line no-await-in-loop + await Promise.all(minings); +} diff --git a/test/mainnet/uniswap/uniswap-swap.test.ts b/test/mainnet/uniswap/uniswap-swap.test.ts new file mode 100644 index 0000000..21946dd --- /dev/null +++ b/test/mainnet/uniswap/uniswap-swap.test.ts @@ -0,0 +1,150 @@ +import { expect } from "chai"; +import hre from "hardhat"; +const { web3, deployments, waffle, ethers } = hre; +const { provider, deployContract } = waffle; + +import { deployAndEnableConnector } from "../../../scripts/tests/deployAndEnableConnector"; +import { buildDSAv2 } from "../../../scripts/tests/buildDSAv2"; +import { encodeSpells } from "../../../scripts/tests/encodeSpells"; +import { getMasterSigner } from "../../../scripts/tests/getMasterSigner"; +import { addLiquidity } from "../../../scripts/tests/addLiquidity"; +import { addresses } from "../../../scripts/tests/mainnet/addresses"; +import { abis } from "../../../scripts/constant/abis"; +import type { Signer, Contract } from "ethers"; + +import { abi } from "@uniswap/v3-periphery/artifacts/contracts/NonfungiblePositionManager.sol/NonfungiblePositionManager.json"; +import { ConnectV2UniswapV3Swap__factory } from "../../../typechain"; + +const FeeAmount = { + LOW: 500, + MEDIUM: 3000, + HIGH: 10000 +}; + +const TICK_SPACINGS: Record = { + 500: 10, + 3000: 60, + 10000: 200 +}; + +const USDT_ADDR = "0xdac17f958d2ee523a2206206994597c13d831ec7"; +const DAI_ADDR = "0x6b175474e89094c44da98b954eedeac495271d0f"; + +let tokenIds: any[] = []; +let liquidities: any[] = []; +const abiCoder = ethers.utils.defaultAbiCoder; + +describe("UniswapV3 [Mainnet]", function () { + const connectorName = "UniswapV3-Swap-v1"; + + let dsaWallet0: any; + let masterSigner: Signer; + let instaConnectorsV2: Contract; + let connector: Contract; + let nftManager: Contract; + + const wallets = provider.getWallets(); + const [wallet0, wallet1, wallet2, wallet3] = wallets; + before(async () => { + await hre.network.provider.request({ + method: "hardhat_reset", + params: [ + { + forking: { + // @ts-ignore + jsonRpcUrl: hre.config.networks.hardhat.forking.url, + blockNumber: 14674358 + } + } + ] + }); + masterSigner = await getMasterSigner(); + instaConnectorsV2 = await ethers.getContractAt(abis.core.connectorsV2, addresses.core.connectorsV2); + nftManager = await ethers.getContractAt(abi, "0xC36442b4a4522E871399CD717aBDD847Ab11FE88"); + connector = await deployAndEnableConnector({ + connectorName, + contractArtifact: ConnectV2UniswapV3Swap__factory, + signer: masterSigner, + connectors: instaConnectorsV2 + }); + console.log("Connector address", connector.address); + }); + + it("Should have contracts deployed.", async function () { + expect(!!instaConnectorsV2.address).to.be.true; + expect(!!connector.address).to.be.true; + expect(!!(await masterSigner.getAddress())).to.be.true; + }); + + describe("DSA wallet setup", function () { + it("Should build DSA v2", async function () { + dsaWallet0 = await buildDSAv2(wallet0.address); + expect(!!dsaWallet0.address).to.be.true; + }); + + it("Deposit ETH & DAI into DSA wallet", async function () { + await wallet0.sendTransaction({ + to: dsaWallet0.address, + value: ethers.utils.parseEther("10") + }); + expect(await ethers.provider.getBalance(dsaWallet0.address)).to.be.gte(ethers.utils.parseEther("10")); + + await addLiquidity("dai", dsaWallet0.address, ethers.utils.parseEther("100000")); + }); + + it("Deposit ETH & USDT into DSA wallet", async function () { + await wallet0.sendTransaction({ + to: dsaWallet0.address, + value: ethers.utils.parseEther("10") + }); + expect(await ethers.provider.getBalance(dsaWallet0.address)).to.be.gte(ethers.utils.parseEther("10")); + + await addLiquidity("usdt", dsaWallet0.address, ethers.utils.parseEther("100000")); + }); + }); + + describe("Main", function () { + it("Should buy successfully", async function () { + const ethAmount = ethers.utils.parseEther("0.1"); + const unitAmt = ethers.utils.parseEther("2994.474881115"); + const ethAddress = "0xeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee"; + const getId = "0"; + const setId = "0"; + + const spells = [ + { + connector: connectorName, + method: "buy", + args: [ethAddress, DAI_ADDR, FeeAmount.MEDIUM, unitAmt, ethAmount, getId, setId] + } + ]; + + const tx = await dsaWallet0.connect(wallet0).cast(...encodeSpells(spells), wallet1.address); + const receipt = await tx.wait(); + // console.log(receipt); + }); + + it("Should sell successfully", async function () { + const ethAmount = ethers.utils.parseEther("0.1"); + const unitAmt = ethers.utils.parseEther("2693.010801400"); + const ethAddress = "0xeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee"; + const getId = "0"; + const setId = "0"; + + const spells = [ + { + connector: connectorName, + method: "sell", + args: [DAI_ADDR, ethAddress, FeeAmount.MEDIUM, unitAmt, ethAmount, getId, setId] + } + ]; + + const tx = await dsaWallet0.connect(wallet0).cast(...encodeSpells(spells), wallet1.address); + const receipt = await tx.wait(); + // console.log(receipt); + }); + }); +}); + +const getMinTick = (tickSpacing: number) => Math.ceil(-887272 / tickSpacing) * tickSpacing; +const getMaxTick = (tickSpacing: number) => Math.floor(887272 / tickSpacing) * tickSpacing; diff --git a/test/mainnet/uniswap/uniswap.test.ts b/test/mainnet/uniswap/uniswap.test.ts new file mode 100644 index 0000000..4049d5d --- /dev/null +++ b/test/mainnet/uniswap/uniswap.test.ts @@ -0,0 +1,366 @@ +import { expect } from "chai"; +import hre from "hardhat"; +const { web3, deployments, waffle, ethers } = hre; +const { provider, deployContract } = waffle; + +import { deployAndEnableConnector } from "../../../scripts/tests/deployAndEnableConnector"; +import { buildDSAv2 } from "../../../scripts/tests/buildDSAv2"; +import { encodeSpells } from "../../../scripts/tests/encodeSpells"; +import { getMasterSigner } from "../../../scripts/tests/getMasterSigner"; +import { addLiquidity } from "../../../scripts/tests/addLiquidity"; +import { addresses } from "../../../scripts/tests/mainnet/addresses"; +import { abis } from "../../../scripts/constant/abis"; +import type { Signer, Contract } from "ethers"; + +import { abi } from "@uniswap/v3-periphery/artifacts/contracts/NonfungiblePositionManager.sol/NonfungiblePositionManager.json"; +import { ConnectV2UniswapV3__factory } from "../../../typechain"; + +const FeeAmount = { + LOW: 500, + MEDIUM: 3000, + HIGH: 10000, +}; + +const TICK_SPACINGS: Record = { + 500: 10, + 3000: 60, + 10000: 200, +}; + +const USDT_ADDR = "0xdac17f958d2ee523a2206206994597c13d831ec7"; +const DAI_ADDR = "0x6b175474e89094c44da98b954eedeac495271d0f"; + +let tokenIds: any[] = []; +let liquidities: any[] = []; +const abiCoder = ethers.utils.defaultAbiCoder; + +describe("UniswapV3", function() { + const connectorName = "UniswapV3-v1"; + + let dsaWallet0: any; + let masterSigner: Signer; + let instaConnectorsV2: Contract; + let connector: Contract; + let nftManager: Contract; + + const wallets = provider.getWallets(); + const [wallet0, wallet1, wallet2, wallet3] = wallets; + before(async () => { + await hre.network.provider.request({ + method: "hardhat_reset", + params: [ + { + forking: { + // @ts-ignore + jsonRpcUrl: hre.config.networks.hardhat.forking.url, + blockNumber: 13005785, + }, + }, + ], + }); + masterSigner = await getMasterSigner(); + instaConnectorsV2 = await ethers.getContractAt( + abis.core.connectorsV2, + addresses.core.connectorsV2 + ); + nftManager = await ethers.getContractAt( + abi, + "0xC36442b4a4522E871399CD717aBDD847Ab11FE88" + ); + connector = await deployAndEnableConnector({ + connectorName, + contractArtifact: ConnectV2UniswapV3__factory, + signer: masterSigner, + connectors: instaConnectorsV2, + }); + console.log("Connector address", connector.address); + }); + + it("Should have contracts deployed.", async function() { + expect(!!instaConnectorsV2.address).to.be.true; + expect(!!connector.address).to.be.true; + expect(!!(await masterSigner.getAddress())).to.be.true; + }); + + describe("DSA wallet setup", function() { + it("Should build DSA v2", async function() { + dsaWallet0 = await buildDSAv2(wallet0.address); + expect(!!dsaWallet0.address).to.be.true; + }); + + it("Deposit ETH & DAI into DSA wallet", async function() { + await wallet0.sendTransaction({ + to: dsaWallet0.address, + value: ethers.utils.parseEther("10"), + }); + expect(await ethers.provider.getBalance(dsaWallet0.address)).to.be.gte( + ethers.utils.parseEther("10") + ); + + await addLiquidity( + "dai", + dsaWallet0.address, + ethers.utils.parseEther("100000") + ); + }); + + it("Deposit ETH & USDT into DSA wallet", async function() { + await wallet0.sendTransaction({ + to: dsaWallet0.address, + value: ethers.utils.parseEther("10"), + }); + expect(await ethers.provider.getBalance(dsaWallet0.address)).to.be.gte( + ethers.utils.parseEther("10") + ); + + await addLiquidity( + "usdt", + dsaWallet0.address, + ethers.utils.parseEther("100000") + ); + }); + }); + + describe("Main", function() { + it("Should mint successfully", async function() { + const ethAmount = ethers.utils.parseEther("0.1"); // 1 ETH + const daiAmount = ethers.utils.parseEther("400"); // 1 ETH + const usdtAmount = Number(ethers.utils.parseEther("400")) / Math.pow(10, 12); // 1 ETH + const ethAddress = "0xeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee"; + + const getIds = ["0", "0"]; + const setId = "0"; + + const spells = [ + { + connector: connectorName, + method: "mint", + args: [ + ethAddress, + DAI_ADDR, + FeeAmount.MEDIUM, + getMinTick(TICK_SPACINGS[FeeAmount.MEDIUM]), + getMaxTick(TICK_SPACINGS[FeeAmount.MEDIUM]), + ethAmount, + daiAmount, + "500000000000000000", + getIds, + setId, + ], + }, + { + connector: connectorName, + method: "mint", + args: [ + DAI_ADDR, + USDT_ADDR, + FeeAmount.MEDIUM, + getMinTick(TICK_SPACINGS[FeeAmount.MEDIUM]), + getMaxTick(TICK_SPACINGS[FeeAmount.MEDIUM]), + daiAmount, + usdtAmount, + "300000000000000000", + getIds, + setId, + ], + }, + { + connector: connectorName, + method: "mint", + args: [ + ethAddress, + USDT_ADDR, + FeeAmount.MEDIUM, + getMinTick(TICK_SPACINGS[FeeAmount.MEDIUM]), + getMaxTick(TICK_SPACINGS[FeeAmount.MEDIUM]), + ethAmount, + usdtAmount, + "300000000000000000", + getIds, + setId, + ], + }, + ]; + + const tx = await dsaWallet0 + .connect(wallet0) + .cast(...encodeSpells(spells), wallet1.address); + let receipt = await tx.wait(); + let castEvent = new Promise((resolve, reject) => { + dsaWallet0.on( + "LogCast", + ( + origin: any, + sender: any, + value: any, + targetNames: any, + targets: any, + eventNames: any, + eventParams: any, + event: any + ) => { + const params = abiCoder.decode( + ["uint256", "uint256", "uint256", "uint256", "int24", "int24"], + eventParams[0] + ); + const params1 = abiCoder.decode( + ["uint256", "uint256", "uint256", "uint256", "int24", "int24"], + eventParams[2] + ); + tokenIds.push(params[0]); + tokenIds.push(params1[0]); + liquidities.push(params[1]); + event.removeListener(); + + resolve({ + eventNames, + }); + } + ); + + setTimeout(() => { + reject(new Error("timeout")); + }, 60000); + }); + + let event = await castEvent; + + const data = await nftManager.positions(tokenIds[0]); + + expect(data.liquidity).to.be.equals(liquidities[0]); + }).timeout(10000000000); + + it("Should deposit successfully", async function() { + const daiAmount = ethers.utils.parseEther("400"); // 1 ETH + const ethAmount = ethers.utils.parseEther("0.1"); // 1 ETH + + const getIds = ["0", "0"]; + const setId = "0"; + + const spells = [ + { + connector: connectorName, + method: "deposit", + args: [ + tokenIds[0], + daiAmount, + ethAmount, + "500000000000000000", + getIds, + setId, + ], + }, + ]; + + const tx = await dsaWallet0 + .connect(wallet0) + .cast(...encodeSpells(spells), wallet1.address); + const receipt = await tx.wait(); + + let castEvent = new Promise((resolve, reject) => { + dsaWallet0.on( + "LogCast", + ( + origin: any, + sender: any, + value: any, + targetNames: any, + targets: any, + eventNames: any, + eventParams: any, + event: any + ) => { + const params = abiCoder.decode( + ["uint256", "uint256", "uint256", "uint256"], + eventParams[0] + ); + liquidities[0] = liquidities[0].add(params[1]); + event.removeListener(); + + resolve({ + eventNames, + }); + } + ); + + setTimeout(() => { + reject(new Error("timeout")); + }, 60000); + }); + + let event = await castEvent; + + const data = await nftManager.positions(tokenIds[0]); + expect(data.liquidity).to.be.equals(liquidities[0]); + }); + + it("Should withdraw successfully", async function() { + const getId = "0"; + const setIds = ["0", "0"]; + + const data = await nftManager.positions(tokenIds[0]); + let data1 = await nftManager.positions(tokenIds[1]); + + const spells = [ + { + connector: connectorName, + method: "withdraw", + args: [tokenIds[0], data.liquidity, 0, 0, getId, setIds], + }, + { + connector: connectorName, + method: "withdraw", + args: [0, data1.liquidity, 0, 0, getId, setIds], + }, + ]; + + const tx = await dsaWallet0 + .connect(wallet0) + .cast(...encodeSpells(spells), wallet1.address); + const receipt = await tx.wait(); + + data1 = await nftManager.positions(tokenIds[1]); + expect(data1.liquidity.toNumber()).to.be.equals(0); + }); + + it("Should collect successfully", async function() { + const ethAmount = ethers.utils.parseEther("0.2"); // 1 ETH + const daiAmount = ethers.utils.parseEther("800"); // 1 ETH + const getIds = ["0", "0"]; + const setIds = ["0", "0"]; + + const spells = [ + { + connector: connectorName, + method: "collect", + args: [tokenIds[0], daiAmount, ethAmount, getIds, setIds], + }, + ]; + + const tx = await dsaWallet0 + .connect(wallet0) + .cast(...encodeSpells(spells), wallet1.address); + const receipt = await tx.wait(); + }); + + it("Should burn successfully", async function() { + const spells = [ + { + connector: connectorName, + method: "burn", + args: [tokenIds[0]], + }, + ]; + + const tx = await dsaWallet0 + .connect(wallet0) + .cast(...encodeSpells(spells), wallet1.address); + const receipt = await tx.wait(); + }); + }); +}); + +const getMinTick = (tickSpacing: number) => + Math.ceil(-887272 / tickSpacing) * tickSpacing; +const getMaxTick = (tickSpacing: number) => + Math.floor(887272 / tickSpacing) * tickSpacing; diff --git a/test/mainnet/uniswapStake/uniswapStake.test.ts b/test/mainnet/uniswapStake/uniswapStake.test.ts new file mode 100644 index 0000000..e2d1528 --- /dev/null +++ b/test/mainnet/uniswapStake/uniswapStake.test.ts @@ -0,0 +1,357 @@ +import { expect } from "chai"; +import hre from "hardhat"; +const { web3, deployments, waffle, ethers } = hre; +const { provider, deployContract } = waffle + +import { deployAndEnableConnector } from "../../../scripts/tests/deployAndEnableConnector"; +import { buildDSAv2 } from "../../../scripts/tests/buildDSAv2"; +import { encodeSpells } from "../../../scripts/tests/encodeSpells"; +import { getMasterSigner } from "../../../scripts/tests/getMasterSigner"; +import { addLiquidity } from "../../../scripts/tests/addLiquidity"; + +import { addresses } from "../../../scripts/tests/mainnet/addresses"; +import { abis } from "../../../scripts/constant/abis"; +import { abi } from "@uniswap/v3-periphery/artifacts/contracts/NonfungiblePositionManager.sol/NonfungiblePositionManager.json" +import type { Signer, Contract } from "ethers"; + +import { ConnectV2UniswapV3Staker__factory, ConnectV2UniswapV3__factory } from "../../../typechain"; + +const FeeAmount = { + LOW: 500, + MEDIUM: 3000, + HIGH: 10000, +} + +const TICK_SPACINGS: Record = { + 500: 10, + 3000: 60, + 10000: 200 +} + +const DAI_ADDR = "0x6b175474e89094c44da98b954eedeac495271d0f" +const ethAddress = "0xeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee" +const INST_ADDR = "0x6f40d4a6237c257fff2db00fa0510deeecd303eb" + +let tokenIds: any[] = [] +const abiCoder = ethers.utils.defaultAbiCoder + +describe("UniswapV3", function () { + const connectorStaker = "UniswapStaker-v1" + const connectorUniswap = "UniswapV3-v1" + + let dsaWallet0: any + let masterSigner: Signer; + let instaConnectorsV2: any; + let connector: any; + let startTime: any, endTime: any; + let nftManager: Contract; + + const wallets = provider.getWallets() + const [wallet0, wallet1, wallet2, wallet3] = wallets + before(async () => { + await hre.network.provider.request({ + method: "hardhat_reset", + params: [ + { + forking: { + // @ts-ignore + jsonRpcUrl: hre.config.networks.hardhat.forking.url, + blockNumber: 13300000, + }, + }, + ], + }); + masterSigner = await getMasterSigner() + instaConnectorsV2 = await ethers.getContractAt(abis.core.connectorsV2, addresses.core.connectorsV2); + nftManager = await ethers.getContractAt(abi, "0xC36442b4a4522E871399CD717aBDD847Ab11FE88"); + connector = await deployAndEnableConnector({ + connectorName: connectorStaker, + contractArtifact: ConnectV2UniswapV3Staker__factory, + signer: masterSigner, + connectors: instaConnectorsV2 + }) + console.log("Connector address", connector.address) + + let uniswapConnector = await deployAndEnableConnector({ + connectorName: connectorUniswap, + contractArtifact: ConnectV2UniswapV3__factory, + signer: masterSigner, + connectors: instaConnectorsV2 + }); + }) + + it("Should have contracts deployed.", async function () { + expect(!!instaConnectorsV2.address).to.be.true; + expect(!!connector.address).to.be.true; + expect(!!(await masterSigner.getAddress())).to.be.true; + }); + + describe("DSA wallet setup", function () { + it("Should build DSA v2", async function () { + dsaWallet0 = await buildDSAv2(wallet0.address) + expect(!!dsaWallet0.address).to.be.true; + }); + + it("Deposit ETH & DAI into DSA wallet", async function () { + await wallet0.sendTransaction({ + to: dsaWallet0.address, + value: ethers.utils.parseEther("10") + }); + expect(await ethers.provider.getBalance(dsaWallet0.address)).to.be.gte(ethers.utils.parseEther("10")); + + await addLiquidity("dai", dsaWallet0.address, ethers.utils.parseEther("100000")); + }); + + it("Deposit ETH & USDT & INST into DSA wallet", async function () { + await wallet0.sendTransaction({ + to: dsaWallet0.address, + value: ethers.utils.parseEther("10") + }); + expect(await ethers.provider.getBalance(dsaWallet0.address)).to.be.gte(ethers.utils.parseEther("10")); + + await addLiquidity("dai", dsaWallet0.address, ethers.utils.parseEther("100000")); + await addLiquidity("usdt", dsaWallet0.address, ethers.utils.parseEther("100000")); + await addLiquidity("inst", dsaWallet0.address, ethers.utils.parseEther("10000")); + }); + }); + + describe("Main", function () { + const ethAmount = ethers.utils.parseEther("0.1") // 1 ETH + const daiAmount = ethers.utils.parseEther("400") // 1 ETH + const instAmount = ethers.utils.parseEther("50") + + it("Should mint successfully", async function () { + const getIds = ["0", "0"] + const setId = "0" + + const spells = [ + { + connector: connectorUniswap, + method: "mint", + args: [ + DAI_ADDR, + ethAddress, + FeeAmount.MEDIUM, + getMinTick(TICK_SPACINGS[FeeAmount.MEDIUM]), + getMaxTick(TICK_SPACINGS[FeeAmount.MEDIUM]), + daiAmount, + ethAmount, + "500000000000000000", + getIds, + setId + ], + }, + { + connector: connectorUniswap, + method: "mint", + args: [ + INST_ADDR, + ethAddress, + FeeAmount.MEDIUM, + getMinTick(TICK_SPACINGS[FeeAmount.MEDIUM]), + getMaxTick(TICK_SPACINGS[FeeAmount.MEDIUM]), + instAmount, + ethAmount, + "500000000000000000", + getIds, + setId + ], + } + ] + + const tx = await dsaWallet0.connect(wallet0).cast(...encodeSpells(spells), wallet1.address) + let receipt = await tx.wait() + + let castEvent = new Promise((resolve, reject) => { + dsaWallet0.on('LogCast', (origin: any, sender: any, value: any, targetNames: any, targets: any, eventNames: any, eventParams: any, event: any) => { + const params = abiCoder.decode(["uint256", "uint256", "uint256", "uint256", "int24", "int24"], eventParams[0]); + const params1 = abiCoder.decode(["uint256", "uint256", "uint256", "uint256", "int24", "int24"], eventParams[1]); + tokenIds.push(params[0]); + tokenIds.push(params1[0]); + event.removeListener(); + + resolve({ + eventNames, + }); + }); + + setTimeout(() => { + reject(new Error('timeout')); + }, 60000) + }); + + let event = await castEvent + + let balance = await nftManager.connect(wallet0).balanceOf(dsaWallet0.address) + console.log("Balance", balance.toString()) + }); + + it("Should create incentive successfully", async function () { + const spells = [ + { + connector: connectorStaker, + method: "createIncentive", + args: [ + ethAddress, + "1000", + dsaWallet0.address, + "0xc2e9f25be6257c210d7adf0d4cd6e3e881ba25f8", + ethers.utils.parseEther("0.01") + ], + }, + { + connector: connectorStaker, + method: "createIncentive", + args: [ + INST_ADDR, + "50", + dsaWallet0.address, + "0xcba27c8e7115b4eb50aa14999bc0866674a96ecb", + ethers.utils.parseEther("0.01") + ], + }] + + const tx = await dsaWallet0.connect(wallet0).cast(...encodeSpells(spells), wallet0.address) + let receipt = await tx.wait() + + let castEvent = new Promise((resolve, reject) => { + dsaWallet0.on('LogCast', (origin: any, sender: any, value: any, targetNames: any, targets: any, eventNames: any, eventParams: any, event: any) => { + const params = abiCoder.decode(["bytes32", "address", "address", "uint256", "uint256", "uint256"], eventParams[0]); + const params1 = abiCoder.decode(["bytes32", "address", "address", "uint256", "uint256", "uint256"], eventParams[1]); + event.removeListener(); + + resolve({ start: [params[3], params1[3]], end: [params[4], params1[4]] }); + }); + + setTimeout(() => { + reject(new Error('timeout')); + }, 60000) + }); + + let event: any = await castEvent + startTime = event.start; + endTime = event.end; + }); + + it("Should stake successfully", async function () { + const spells = [ + { + connector: connectorStaker, + method: "deposit", + args: [ + tokenIds[0] + ], + }, + { + connector: connectorStaker, + method: "stake", + args: [ + ethAddress, + startTime[0], + endTime[0], + dsaWallet0.address, + tokenIds[0] + ], + }, + { + connector: connectorStaker, + method: "deposit", + args: [ + tokenIds[1] + ], + }, + { + connector: connectorStaker, + method: "stake", + args: [ + INST_ADDR, + startTime[1], + endTime[1], + dsaWallet0.address, + tokenIds[1] + ], + } + ] + + const tx = await dsaWallet0.connect(wallet0).cast(...encodeSpells(spells), wallet1.address) + let receipt = await tx.wait() + + let balance = await nftManager.connect(wallet0).balanceOf(dsaWallet0.address) + console.log("Balance", balance.toString()) + }); + + it("Should claim rewards successfully", async function () { + const spells = [ + { + connector: connectorStaker, + method: "claimRewards", + args: [ + ethAddress, + "1000", + ], + }, + { + connector: connectorStaker, + method: "claimRewards", + args: [ + INST_ADDR, + "1000", + ], + } + ] + + const tx = await dsaWallet0.connect(wallet0).cast(...encodeSpells(spells), wallet1.address) + let receipt = await tx.wait() + }); + + it("Should unstake successfully", async function () { + const spells = [ + { + connector: connectorStaker, + method: "unstake", + args: [ + ethAddress, + startTime[0], + endTime[0], + dsaWallet0.address, + tokenIds[0] + ], + }, + { + connector: connectorStaker, + method: "withdraw", + args: [ + tokenIds[0], + ], + }, + { + connector: connectorStaker, + method: "unstake", + args: [ + INST_ADDR, + startTime[1], + endTime[1], + dsaWallet0.address, + tokenIds[1] + ], + }, + { + connector: connectorStaker, + method: "withdraw", + args: [ + tokenIds[1] + ], + } + ] + + const tx = await dsaWallet0.connect(wallet0).cast(...encodeSpells(spells), wallet1.address) + let receipt = await tx.wait() + + let balance = await nftManager.connect(wallet0).balanceOf(dsaWallet0.address) + console.log("Balance", balance.toString()) + }); + }) +}) + +const getMinTick = (tickSpacing: number) => Math.ceil(-887272 / tickSpacing) * tickSpacing +const getMaxTick = (tickSpacing: number) => Math.floor(887272 / tickSpacing) * tickSpacing diff --git a/test/mainnet/uniswapV3Router/uniswapV3Router.test.ts b/test/mainnet/uniswapV3Router/uniswapV3Router.test.ts new file mode 100644 index 0000000..3162393 --- /dev/null +++ b/test/mainnet/uniswapV3Router/uniswapV3Router.test.ts @@ -0,0 +1,308 @@ +import hre from "hardhat"; +import { expect } from "chai"; +const { ethers } = hre; //check +import { BigNumber } from "bignumber.js"; +import { deployAndEnableConnector } from "../../../scripts/tests/deployAndEnableConnector"; +import { buildDSAv2 } from "../../../scripts/tests/buildDSAv2"; +import { encodeSpells } from "../../../scripts/tests/encodeSpells"; +import { getMasterSigner } from "../../../scripts/tests/getMasterSigner"; +import { addresses } from "../../../scripts/tests/mainnet/addresses"; +import { addLiquidity } from "../../../scripts/tests/addLiquidity"; +import { abis } from "../../../scripts/constant/abis"; +import { ConnectV2UniswapV3AutoRouter__factory } from "../../../typechain"; +import er20abi from "../../../scripts/constant/abi/basics/erc20.json"; +import type { Signer, Contract } from "ethers"; +import { CurrencyAmount, Token, TradeType, Currency, Percent } from "@uniswap/sdk-core"; +import { AlphaRouter } from "@uniswap/smart-order-router"; +const provider = new ethers.providers.JsonRpcProvider(process.env.ETH_NODE_URL); +const router = new AlphaRouter({ chainId: 1, provider: provider }); + +describe("Auto Router", function () { + const connectorName = "Auto-Router-test"; + + let dsaWallet0: Contract; + let wallet0: Signer, wallet1: Signer; + let masterSigner: Signer; + let instaConnectorsV2: Contract; + let connector: Contract; + + // @ts-ignore + const provider = new ethers.providers.JsonRpcProvider(hre.config.networks.hardhat.forking.url); + const router = new AlphaRouter({ chainId: 1, provider }); + + before(async () => { + await hre.network.provider.request({ + method: "hardhat_reset", + params: [ + { + forking: { + // @ts-ignore + jsonRpcUrl: hre.config.networks.hardhat.forking.url + } + } + ] + }); + [wallet0, wallet1] = await ethers.getSigners(); + + masterSigner = await getMasterSigner(); + instaConnectorsV2 = await ethers.getContractAt(abis.core.connectorsV2, addresses.core.connectorsV2); + connector = await deployAndEnableConnector({ + connectorName, + contractArtifact: ConnectV2UniswapV3AutoRouter__factory, + signer: masterSigner, + connectors: instaConnectorsV2 + }); + console.log("Connector address", connector.address); + }); + + it("Should have contracts deployed.", async function () { + expect(!!instaConnectorsV2.address).to.be.true; + expect(!!connector.address).to.be.true; + expect(!!(await masterSigner.getAddress())).to.be.true; + }); + + describe("DSA wallet setup", function () { + it("Should build DSA v2", async function () { + dsaWallet0 = await buildDSAv2(await wallet0.getAddress()); + expect(!!dsaWallet0.address).to.be.true; + }); + + it("Deposit ETH and DAI into DSA wallet", async function () { + await wallet0.sendTransaction({ + to: dsaWallet0.address, + value: ethers.utils.parseEther("10") + }); + await addLiquidity("dai", dsaWallet0.address, ethers.utils.parseEther("5000")); + // console.log(dsaWallet0.address); + const daiToken = await ethers.getContractAt( + er20abi, + "0x6b175474e89094c44da98b954eedeac495271d0f" // dai address + ); + + expect(await daiToken.balanceOf(dsaWallet0.address)).to.be.gte(10); + expect(await ethers.provider.getBalance(dsaWallet0.address)).to.be.gte(ethers.utils.parseEther("10")); + }); + }); + + describe("Main", function () { + it("should swap the tokens ", async function () { + const buyTokenAddress = "0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48"; //usdc + const sellTokenAddress = "0x6b175474e89094c44da98b954eedeac495271d0f"; //dai + const sellTokenDecimals = 18; + const buyTokenDecimals = 6; + const amount = 1; + + const srcAmount = new BigNumber(amount).times(new BigNumber(10).pow(sellTokenDecimals)).toFixed(0); + const sellToken = new Token(1, sellTokenAddress, sellTokenDecimals); + const buyToken = new Token(1, buyTokenAddress, buyTokenDecimals); + const daiAmount = CurrencyAmount.fromRawAmount(sellToken, srcAmount); + + const deadline = 1696000000 // Fri Sep 29 2023 15:06:40 GMT+0000 + const route = await router.route(daiAmount, buyToken , TradeType.EXACT_INPUT, { + recipient: dsaWallet0.address, + slippageTolerance: new Percent(5, 100), + deadline + }); + + const calldata = route?.methodParameters?.calldata; + + const _buyAmount = route?.quote.toFixed(); + const buyTokenAmount = new BigNumber(String(_buyAmount)).times(new BigNumber(10).pow(buyTokenDecimals)).toFixed(0); + + + function caculateUnitAmt( + buyAmount: any, + sellAmount: any, + buyDecimal: any, + sellDecimal: any, + maxSlippage: any + ) { + let unitAmt: any; + unitAmt = new BigNumber(buyAmount) + .dividedBy(10 ** buyDecimal) + .dividedBy(new BigNumber(sellAmount).dividedBy(10 ** sellDecimal)); + unitAmt = unitAmt.multipliedBy((100 - maxSlippage) / 100); + unitAmt = unitAmt.multipliedBy(1e18).toFixed(0); + return unitAmt; + } + + const unitAmt = caculateUnitAmt( + buyTokenAmount, + srcAmount, + buyTokenDecimals, + sellTokenDecimals, + 1 + ); + const spells = [ + { + connector: connectorName, + method: "sell", + args: [buyTokenAddress, sellTokenAddress, srcAmount, unitAmt, calldata, 0] + } + ]; + + const buyTokenContract = await ethers.getContractAt( + er20abi, + buyTokenAddress, + ); + + const initialBuyTokenBalance = await buyTokenContract.balanceOf(dsaWallet0.address) + + const tx = await dsaWallet0.connect(wallet0).cast(...encodeSpells(spells), await wallet1.getAddress()); + const receipt = await tx.wait(); + + const finalBuyTokenBalance = await buyTokenContract.balanceOf(dsaWallet0.address) + + + expect(finalBuyTokenBalance).to.be.gt(initialBuyTokenBalance); + }); + + it("should swap the tokens when selltoken is eth in the spell", async function () { + const buyTokenAddress = "0x6b175474e89094c44da98b954eedeac495271d0f"; //dai + const sellTokenAddress = "0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE"; //eth + const sellTokenDecimals = 18; + const buyTokenDecimals = 18; + const amount = 1; + + const wethAddr = "0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2"; + + const srcAmount = new BigNumber(amount).times(new BigNumber(10).pow(sellTokenDecimals)).toFixed(0); + const sellToken = new Token(1, wethAddr, sellTokenDecimals); + const buyToken = new Token(1, buyTokenAddress, buyTokenDecimals); + const sellAmount = CurrencyAmount.fromRawAmount(sellToken, srcAmount); + + const deadline = 1696000000 // Fri Sep 29 2023 15:06:40 GMT+0000 + const route = await router.route(sellAmount, buyToken, TradeType.EXACT_INPUT, { + recipient: dsaWallet0.address, + slippageTolerance: new Percent(5, 100), + deadline + }); + + const calldata = route?.methodParameters?.calldata; + + const _buyAmount = route?.quote.toFixed(); + const buyTokenAmount = new BigNumber(String(_buyAmount)).times(new BigNumber(10).pow(buyTokenDecimals)).toFixed(0); + + + function caculateUnitAmt( + buyAmount: any, + sellAmount: any, + buyDecimal: any, + sellDecimal: any, + maxSlippage: any + ) { + let unitAmt: any; + unitAmt = new BigNumber(buyAmount) + .dividedBy(10 ** buyDecimal) + .dividedBy(new BigNumber(sellAmount).dividedBy(10 ** sellDecimal)); + unitAmt = unitAmt.multipliedBy((100 - maxSlippage) / 100); + unitAmt = unitAmt.multipliedBy(1e18).toFixed(0); + return unitAmt; + } + + const unitAmt = caculateUnitAmt( + buyTokenAmount, + srcAmount, + buyTokenDecimals, + sellTokenDecimals, + 1 + ); + + const spells = [ + { + connector: connectorName, + method: "sell", + args: [buyTokenAddress, sellTokenAddress, srcAmount, unitAmt, calldata, 0] + } + ]; + + const buyTokenContract = await ethers.getContractAt( + er20abi, + buyTokenAddress, + ); + + const initialBuyTokenBalance = await buyTokenContract.balanceOf(dsaWallet0.address) + + const tx = await dsaWallet0.connect(wallet0).cast(...encodeSpells(spells), await wallet1.getAddress()); + const receipt = await tx.wait(); + + const finalBuyTokenBalance = await buyTokenContract.balanceOf(dsaWallet0.address) + + + expect(finalBuyTokenBalance).to.be.gt(initialBuyTokenBalance); + }); + + it("should swap the tokens when buytoken is weth in the spell", async function () { + const buyTokenAddress = "0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2"; // weth + const sellTokenAddress = "0x6b175474e89094c44da98b954eedeac495271d0f"; // dai + const sellTokenDecimals = 18; + const buyTokenDecimals = 18; + const amount = 4000; + + const srcAmount = new BigNumber(amount).times(new BigNumber(10).pow(sellTokenDecimals)).toFixed(0); + const sellToken = new Token(1, sellTokenAddress, sellTokenDecimals); + const buyToken = new Token(1, buyTokenAddress, buyTokenDecimals); + const daiAmount = CurrencyAmount.fromRawAmount(sellToken, srcAmount); + + const deadline = 1696000000 // Fri Sep 29 2023 15:06:40 GMT+0000 + const route = await router.route(daiAmount, buyToken , TradeType.EXACT_INPUT, { + recipient: dsaWallet0.address, + slippageTolerance: new Percent(5, 100), + deadline + }); + + const calldata = route?.methodParameters?.calldata; + + const _buyAmount = route?.quote.toFixed(); + const buyTokenAmount = new BigNumber(String(_buyAmount)).times(new BigNumber(10).pow(buyTokenDecimals)).toFixed(0); + + + function caculateUnitAmt( + buyAmount: any, + sellAmount: any, + buyDecimal: any, + sellDecimal: any, + maxSlippage: any + ) { + let unitAmt: any; + unitAmt = new BigNumber(buyAmount) + .dividedBy(10 ** buyDecimal) + .dividedBy(new BigNumber(sellAmount).dividedBy(10 ** sellDecimal)); + unitAmt = unitAmt.multipliedBy((100 - maxSlippage) / 100); + unitAmt = unitAmt.multipliedBy(1e18).toFixed(0); + return unitAmt; + } + + const unitAmt = caculateUnitAmt( + buyTokenAmount, + srcAmount, + buyTokenDecimals, + sellTokenDecimals, + 1 + ); + + const spells = [ + { + connector: connectorName, + method: "sell", + args: [buyTokenAddress, sellTokenAddress, srcAmount, unitAmt, calldata, 0] + } + ]; + + const buyTokenContract = await ethers.getContractAt( + er20abi, + buyTokenAddress, + ); + + const initialBuyTokenBalance = await buyTokenContract.balanceOf(dsaWallet0.address) + + const tx = await dsaWallet0.connect(wallet0).cast(...encodeSpells(spells), await wallet1.getAddress()); + const receipt = await tx.wait(); + + const finalBuyTokenBalance = await buyTokenContract.balanceOf(dsaWallet0.address) + + + expect(finalBuyTokenBalance).to.be.gt(initialBuyTokenBalance); + }); + }); +}); diff --git a/test/mainnet/yearn/yearn.test.ts b/test/mainnet/yearn/yearn.test.ts new file mode 100644 index 0000000..5c7bedc --- /dev/null +++ b/test/mainnet/yearn/yearn.test.ts @@ -0,0 +1,189 @@ +import { expect } from "chai"; +import hre from "hardhat"; +const { web3, deployments, waffle, ethers } = hre; +const { provider, deployContract } = waffle; + +import { deployAndEnableConnector } from "../../../scripts/tests/deployAndEnableConnector"; +import { buildDSAv2 } from "../../../scripts/tests/buildDSAv2"; +import { encodeSpells } from "../../../scripts/tests/encodeSpells"; +import { getMasterSigner } from "../../../scripts/tests/getMasterSigner"; + +import { addresses } from "../../../scripts/tests/mainnet/addresses"; +import { abis } from "../../../scripts/constant/abis"; +import { tokens } from "../../../scripts/tests/mainnet/tokens"; +import { Signer, Contract, BigNumber } from "ethers"; + +import { ConnectV2YearnV2__factory } from "../../../typechain"; + +const toBytes32 = (bn: BigNumber) => { + return ethers.utils.hexlify(ethers.utils.zeroPad(bn.toHexString(), 32)); +}; + +const setStorageAt = async (address: string, index: string, value: string) => { + await ethers.provider.send("hardhat_setStorageAt", [address, index, value]); + await ethers.provider.send("evm_mine", []); // Just mines to the next block +}; + +describe("Yearn", function() { + const connectorName = "YEARN-TEST-A"; + + let dsaWallet0: any; + let masterSigner: Signer; + let instaConnectorsV2: Contract; + let connector: Contract; + + const wallets = provider.getWallets(); + const [wallet0, wallet1, wallet2, wallet3] = wallets; + before(async () => { + await hre.network.provider.request({ + method: "hardhat_reset", + params: [ + { + forking: { + // @ts-ignore + jsonRpcUrl: hre.config.networks.hardhat.forking.url, + blockNumber: 12996975, + }, + }, + ], + }); + masterSigner = await getMasterSigner(); + instaConnectorsV2 = await ethers.getContractAt( + abis.core.connectorsV2, + addresses.core.connectorsV2 + ); + connector = await deployAndEnableConnector({ + connectorName, + contractArtifact: ConnectV2YearnV2__factory, + signer: masterSigner, + connectors: instaConnectorsV2, + }); + console.log("Connector address", connector.address); + }); + + it("Should have contracts deployed.", async function() { + expect(!!instaConnectorsV2.address).to.be.true; + expect(!!connector.address).to.be.true; + expect(!!(await masterSigner.getAddress())).to.be.true; + }); + + describe("DSA wallet setup", function() { + it("Should build DSA v2", async function() { + dsaWallet0 = await buildDSAv2(wallet0.address); + expect(!!dsaWallet0.address).to.be.true; + }); + + it("Deposit ETH into DSA wallet", async function() { + await wallet0.sendTransaction({ + to: dsaWallet0.address, + value: ethers.utils.parseEther("10"), + }); + expect(await ethers.provider.getBalance(dsaWallet0.address)).to.be.gte( + ethers.utils.parseEther("10") + ); + }); + }); + + describe("Main", function() { + it("Should increase the DAI balance to 100 DAI", async function() { + const DAI = new ethers.Contract( + tokens.dai.address, + abis.basic.erc20, + ethers.provider + ); + const DAI_SLOT = 2; + const locallyManipulatedBalance = ethers.utils.parseEther("100"); + + // Get storage slot index + const index = ethers.utils.solidityKeccak256( + ["uint256", "uint256"], + [dsaWallet0.address, DAI_SLOT] + ); + // Manipulate local balance (needs to be bytes32 string) + await setStorageAt( + tokens.dai.address, + index.toString(), + toBytes32(locallyManipulatedBalance).toString() + ); + + // Get DAI balance + const balance = await DAI.balanceOf(dsaWallet0.address); + expect( + await ethers.BigNumber.from(balance).eq(ethers.utils.parseEther("100")) + ); + }); + + it("Should deposit and withdraw 50 DAI in/out the Yearn Vault", async function() { + const DAI = new ethers.Contract( + tokens.dai.address, + abis.basic.erc20, + ethers.provider + ); + const DAI_VAULT = "0xdA816459F1AB5631232FE5e97a05BBBb94970c95"; + const amount = ethers.utils.parseEther("50"); // 50 DAI + const setId = "132456"; + const spells = [ + { + connector: connectorName, + method: "deposit", + args: [DAI_VAULT, amount, 0, setId], + }, + { + connector: connectorName, + method: "withdraw", + args: [DAI_VAULT, amount, setId, 0], + }, + ]; + + const tx = await dsaWallet0 + .connect(wallet0) + .cast(...encodeSpells(spells), wallet0.address); + await tx.wait(); + + // Get DAI balance + const balance = await DAI.balanceOf(dsaWallet0.address); + expect( + await ethers.BigNumber.from(balance).eq(ethers.utils.parseEther("100")) + ); + }); + + it("Should deposit 70 DAI in the Yearn Vault", async function() { + const DAI_VAULT = "0xdA816459F1AB5631232FE5e97a05BBBb94970c95"; + const DAI = new ethers.Contract( + tokens.dai.address, + abis.basic.erc20, + ethers.provider + ); + const YVDAI = new ethers.Contract( + DAI_VAULT, + abis.basic.erc20, + ethers.provider + ); + const amount = ethers.utils.parseEther("70"); // 70 DAI + const setId = "568445"; + const spells = [ + { + connector: connectorName, + method: "deposit", + args: [DAI_VAULT, amount, 0, setId], + }, + ]; + + const tx = await dsaWallet0 + .connect(wallet0) + .cast(...encodeSpells(spells), wallet0.address); + await tx.wait(); + + // Get DAI balance + const yvDAIBalance = await YVDAI.balanceOf(dsaWallet0.address); + const daiBalance = await DAI.balanceOf(dsaWallet0.address); + const correctDaiBalance = ethers.BigNumber.from(daiBalance).eq( + ethers.utils.parseEther("30") + ); + const correctYVDaiBalance = ethers.BigNumber.from(yvDAIBalance).lte( + ethers.utils.parseEther("70") + ); + expect(correctDaiBalance && correctYVDaiBalance); + }); + }); +}); diff --git a/test/optimism/aave/aaveV3-import-test.ts b/test/optimism/aave/aaveV3-import-test.ts new file mode 100644 index 0000000..6df9086 --- /dev/null +++ b/test/optimism/aave/aaveV3-import-test.ts @@ -0,0 +1,464 @@ +import { expect, should } from "chai"; +import hre, { ethers, waffle } from "hardhat"; +import type { Signer, Contract } from "ethers"; +import { ecsign, ecrecover, pubToAddress } from "ethereumjs-util"; +import { keccak256 } from "@ethersproject/keccak256"; +import { defaultAbiCoder } from "@ethersproject/abi"; +import { BigNumber } from "bignumber.js"; +import { buildDSAv2 } from "../../../scripts/tests/buildDSAv2"; +import { addresses } from "../../../scripts/tests/optimism/addresses"; +import { deployAndEnableConnector } from "../../../scripts/tests/deployAndEnableConnector"; +import { abis } from "../../../scripts/constant/abis"; +import { getMasterSigner } from "../../../scripts/tests/getMasterSigner"; +import { parseEther, parseUnits } from "ethers/lib/utils"; +import { encodeSpells } from "../../../scripts/tests/encodeSpells"; +import encodeFlashcastData from "../../../scripts/tests/encodeFlashcastData"; +import { ConnectV2AaveV3ImportPermitOptimism__factory, IERC20__factory } from "../../../typechain"; +import axios from "axios"; + +const aDaiAddress = "0x82E64f49Ed5EC1bC6e43DAD4FC8Af9bb3A2312EE"; +const aaveAddress = "0x794a61358D6845594F94dc1DB02A252b5b4814aD"; +let account = "0x31efc4aeaa7c39e54a33fdc3c46ee2bd70ae0a09"; +const DAI = "0xDA10009cBd5D07dd0CeCc66161FC93D7c9000da1"; +const USDC = "0x7F5c764cBc14f9669B88837ca1490cCa17c31607"; +const mnemonic = "test test test test test test test test test test test junk"; +const connectorName = "AAVE-V3-IMPORT-PERMIT-X"; +let signer: any, wallet0: any, wallet: any; + +const ABI = [ + "function DOMAIN_SEPARATOR() public view returns (bytes32)", + "function balanceOf(address account) public view returns (uint256)", + "function nonces(address owner) public view returns (uint256)" +]; + +const aaveAbi = [ + { + inputs: [ + { internalType: "address", name: "asset", type: "address" }, + { internalType: "uint256", name: "amount", type: "uint256" }, + { internalType: "uint256", name: "interestRateMode", type: "uint256" }, + { internalType: "uint16", name: "referralCode", type: "uint16" }, + { internalType: "address", name: "onBehalfOf", type: "address" } + ], + name: "borrow", + outputs: [], + stateMutability: "nonpayable", + type: "function" + }, + { + inputs: [ + { internalType: "address", name: "asset", type: "address" }, + { internalType: "uint256", name: "amount", type: "uint256" }, + { internalType: "address", name: "onBehalfOf", type: "address" }, + { internalType: "uint16", name: "referralCode", type: "uint16" } + ], + name: "supply", + outputs: [], + stateMutability: "nonpayable", + type: "function" + }, + { + inputs: [ + { + internalType: "address", + name: "user", + type: "address" + } + ], + name: "getUserAccountData", + outputs: [ + { + internalType: "uint256", + name: "totalCollateralBase", + type: "uint256" + }, + { + internalType: "uint256", + name: "totalDebtBase", + type: "uint256" + }, + { + internalType: "uint256", + name: "availableBorrowsBase", + type: "uint256" + }, + { + internalType: "uint256", + name: "currentLiquidationThreshold", + type: "uint256" + }, + { + internalType: "uint256", + name: "ltv", + type: "uint256" + }, + { + internalType: "uint256", + name: "healthFactor", + type: "uint256" + } + ], + stateMutability: "view", + type: "function" + } +]; + +const erc20Abi = [ + { + constant: false, + inputs: [ + { + name: "_spender", + type: "address" + }, + { + name: "_value", + type: "uint256" + } + ], + name: "approve", + outputs: [ + { + name: "", + type: "bool" + } + ], + payable: false, + stateMutability: "nonpayable", + type: "function" + }, + { + constant: true, + inputs: [ + { + name: "_owner", + type: "address" + } + ], + name: "balanceOf", + outputs: [ + { + name: "balance", + type: "uint256" + } + ], + payable: false, + stateMutability: "view", + type: "function" + }, + { + constant: false, + inputs: [ + { + name: "_to", + type: "address" + }, + { + name: "_value", + type: "uint256" + } + ], + name: "transfer", + outputs: [ + { + name: "", + type: "bool" + } + ], + payable: false, + stateMutability: "nonpayable", + type: "function" + } +]; + +const token = new ethers.Contract(DAI, erc20Abi); +const aDai = new ethers.Contract(aDaiAddress, ABI); +const usdcToken = new ethers.Contract(USDC, erc20Abi); +const aave = new ethers.Contract(aaveAddress, aaveAbi); + +describe("Import Aave v3 Position for Optimism", function () { + let dsaWallet0: any; + let masterSigner: Signer; + let instaConnectorsV2: Contract; + let connector: any; + + const wallet = ethers.Wallet.fromMnemonic(mnemonic); + + before(async () => { + await hre.network.provider.request({ + method: "hardhat_reset", + params: [ + { + forking: { + //@ts-ignore + jsonRpcUrl: hre.config.networks.hardhat.forking.url, + blockNumber: 5405099 + } + } + ] + }); + masterSigner = await getMasterSigner(); + [wallet0] = await ethers.getSigners(); + await hre.network.provider.send("hardhat_setBalance", [account, ethers.utils.parseEther("10").toHexString()]); + + await hre.network.provider.request({ + method: "hardhat_impersonateAccount", + params: [account] + }); + + signer = await ethers.getSigner(account); + + await token.connect(signer).transfer(wallet0.address, ethers.utils.parseEther("10")); + + instaConnectorsV2 = await ethers.getContractAt(abis.core.connectorsV2, addresses.core.connectorsV2); + connector = await deployAndEnableConnector({ + connectorName, + contractArtifact: ConnectV2AaveV3ImportPermitOptimism__factory, + signer: masterSigner, + connectors: instaConnectorsV2 + }); + }); + + describe("check user AAVE position", async () => { + it("Should create Aave v3 position of DAI(collateral), and USDC(debt)", async () => { + // approve DAI to aavePool + await token.connect(wallet0).approve(aaveAddress, parseEther("10")); + + //deposit DAI in aave + await aave.connect(wallet0).supply(DAI, parseEther("10"), wallet.address, 3228); + console.log("\tSupplied DAI on aave"); + + //borrow USDC from aave + await aave.connect(wallet0).borrow(USDC, parseUnits("1", 6), 1, 3228, wallet.address); + console.log("\tBorrowed USDC from aave"); + }); + + it("Should check position of user", async () => { + expect(await aDai.connect(wallet0).balanceOf(wallet.address)).to.be.gte( + new BigNumber(10).multipliedBy(1e18).toString() + ); + + expect(await usdcToken.connect(wallet0).balanceOf(wallet.address)).to.be.gte( + new BigNumber(1).multipliedBy(1e6).toString() + ); + }); + }); + + describe("Deployment", async () => { + it("Should set correct name", async () => { + expect(await connector.name()).to.eq("Aave-v3-import-permit-v1"); + }); + }); + + describe("DSA wallet setup", async () => { + it("Should build DSA v2", async () => { + dsaWallet0 = await buildDSAv2(wallet.address); + expect(!!dsaWallet0.address).to.be.true; + }); + + it("Deposit ETH into DSA wallet", async function () { + await wallet0.sendTransaction({ + to: dsaWallet0.address, + value: ethers.utils.parseEther("10") + }); + + expect(await ethers.provider.getBalance(dsaWallet0.address)).to.be.gte(ethers.utils.parseEther("10")); + }); + }); + + describe("User's borrows", async () => { + it("Should check the totalDebtBase of user", async () => { + console.log( + `\ttotalDebtBase: ${(await aave.connect(wallet0).getUserAccountData(wallet.address)).totalDebtBase.toString()}` + ); + }); + }); + + describe("Aave position migration", async () => { + it("Should migrate Aave position", async () => { + const DOMAIN_SEPARATOR = await aDai.connect(wallet0).DOMAIN_SEPARATOR(); + const PERMIT_TYPEHASH = "0x6e71edae12b1b97f4d1f60370fef10105fa2faae0126114a169c64845d6126c9"; + + let nonce = (await aDai.connect(wallet0).nonces(wallet.address)).toNumber(); + //Approving max amount + const amount = ethers.constants.MaxUint256; + const expiry = Date.now() + 20 * 60; + + const digest = keccak256( + ethers.utils.solidityPack( + ["bytes1", "bytes1", "bytes32", "bytes32"], + [ + "0x19", + "0x01", + DOMAIN_SEPARATOR, + keccak256( + defaultAbiCoder.encode( + ["bytes32", "address", "address", "uint256", "uint256", "uint256"], + [PERMIT_TYPEHASH, wallet.address, dsaWallet0.address, amount, nonce, expiry] + ) + ) + ] + ) + ); + const { v, r, s } = ecsign(Buffer.from(digest.slice(2), "hex"), Buffer.from(wallet.privateKey.slice(2), "hex")); + + const amount0 = new BigNumber( + new BigNumber((await aave.connect(wallet0).getUserAccountData(wallet.address)).totalDebtBase) + ); + let params = { + tokens: [USDC], + amounts: [amount0.toFixed(0)] + }; + const flashData = ( + await axios.get("https://api.instadapp.io/defi/optimism/flashloan/v2", { + params: params + }) + ).data; + + const fees = flashData.bestFee; + const amountB = new BigNumber(amount0.toString()).multipliedBy(fees).dividedBy(1e4); + const amountWithFee = amount0.plus(amountB); + const data = flashData.bestData[0]; + + const flashSpells = [ + { + connector: "AAVE-V3-IMPORT-PERMIT-X", + method: "importAave", + args: [ + wallet.address, + [[DAI], [USDC], false, [amountB.toFixed(0)]], + [[v], [ethers.utils.hexlify(r)], [ethers.utils.hexlify(s)], [expiry]] + ] + }, + { + connector: "INSTAPOOL-C", + method: "flashPayback", + args: [USDC, amountWithFee.toFixed(0), 0, 0] + } + ]; + + const spells = [ + { + connector: "INSTAPOOL-C", + method: "flashBorrowAndCast", + args: [USDC, amount0.toFixed(0), flashData.bestRoutes[0], encodeFlashcastData(flashSpells), data.toString()] + } + ]; + const tx = await dsaWallet0.connect(wallet0).cast(...encodeSpells(spells), wallet.address); + const receipt = await tx.wait(); + }); + + it("Should check DSA AAVE position", async () => { + expect(await aDai.connect(wallet0).balanceOf(dsaWallet0.address)).to.be.gte( + new BigNumber(10).multipliedBy(1e18).toString() + ); + }); + + describe("check user AAVE position", async () => { + it("Should create Aave v3 position of DAI(collateral), and USDC(debt)", async () => { + await token.connect(signer).transfer(wallet0.address, ethers.utils.parseEther("10")); + // approve DAI to aavePool + await token.connect(wallet0).approve(aaveAddress, parseEther("10")); + + //deposit DAI in aave + await aave.connect(wallet0).supply(DAI, parseEther("10"), wallet.address, 3228); + console.log("\tSupplied DAI on aave"); + + //borrow USDC from aave + await aave.connect(wallet0).borrow(USDC, parseUnits("1", 6), 1, 3228, wallet.address); + console.log("\tBorrowed USDC from aave"); + }); + + it("Should check position of user", async () => { + expect(await aDai.connect(wallet0).balanceOf(wallet.address)).to.be.gte( + new BigNumber(10).multipliedBy(1e18).toString() + ); + + expect(await usdcToken.connect(wallet0).balanceOf(wallet.address)).to.be.gte( + new BigNumber(1).multipliedBy(1e6).toString() + ); + }); + }); + }); + describe("Aave position import with collateral", async () => { + it("Should migrate Aave position", async () => { + const DOMAIN_SEPARATOR = await aDai.connect(wallet0).DOMAIN_SEPARATOR(); + const PERMIT_TYPEHASH = "0x6e71edae12b1b97f4d1f60370fef10105fa2faae0126114a169c64845d6126c9"; + + let nonce = (await aDai.connect(wallet0).nonces(wallet.address)).toNumber(); + //Approving max amount + const amount = ethers.constants.MaxUint256; + const expiry = Date.now() + 20 * 60; + + const digest = keccak256( + ethers.utils.solidityPack( + ["bytes1", "bytes1", "bytes32", "bytes32"], + [ + "0x19", + "0x01", + DOMAIN_SEPARATOR, + keccak256( + defaultAbiCoder.encode( + ["bytes32", "address", "address", "uint256", "uint256", "uint256"], + [PERMIT_TYPEHASH, wallet.address, dsaWallet0.address, amount, nonce, expiry] + ) + ) + ] + ) + ); + const { v, r, s } = ecsign(Buffer.from(digest.slice(2), "hex"), Buffer.from(wallet.privateKey.slice(2), "hex")); + + const amount0 = new BigNumber( + new BigNumber((await aave.connect(wallet0).getUserAccountData(wallet.address)).totalDebtBase) + ); + let params = { + tokens: [USDC], + amounts: [amount0.toFixed(0)] + }; + const flashData = ( + await axios.get("https://api.instadapp.io/defi/optimism/flashloan/v2", { + params: params + }) + ).data; + + const fees = flashData.bestFee; + const amountB = new BigNumber(amount0.toString()).multipliedBy(fees).dividedBy(1e4); + const amountWithFee = amount0.plus(amountB); + const data = flashData.bestData[0]; + + const flashSpells = [ + { + connector: "AAVE-V3-IMPORT-PERMIT-X", + method: "importAaveWithCollateral", + args: [ + wallet.address, + [[DAI], [USDC], false, [amountB.toFixed(0)]], + [[v], [ethers.utils.hexlify(r)], [ethers.utils.hexlify(s)], [expiry]], + [true] + ] + }, + { + connector: "INSTAPOOL-C", + method: "flashPayback", + args: [USDC, amountWithFee.toFixed(0), 0, 0] + } + ]; + + const spells = [ + { + connector: "INSTAPOOL-C", + method: "flashBorrowAndCast", + args: [USDC, amount0.toFixed(0), flashData.bestRoutes[0], encodeFlashcastData(flashSpells), data.toString()] + } + ]; + const tx = await dsaWallet0.connect(wallet0).cast(...encodeSpells(spells), wallet.address); + const receipt = await tx.wait(); + }); + + it("Should check DSA AAVE position", async () => { + expect(await aDai.connect(wallet0).balanceOf(dsaWallet0.address)).to.be.gte( + new BigNumber(10).multipliedBy(1e18).toString() + ); + }); + }); +}); diff --git a/test/optimism/aave/v3-test.ts b/test/optimism/aave/v3-test.ts new file mode 100644 index 0000000..5f6817f --- /dev/null +++ b/test/optimism/aave/v3-test.ts @@ -0,0 +1,268 @@ +import { expect, should } from "chai"; +import hre, { ethers, waffle } from "hardhat"; +import type { Signer, Contract } from "ethers"; +import { ecsign, ecrecover, pubToAddress } from "ethereumjs-util"; +import { keccak256 } from "@ethersproject/keccak256"; +import { defaultAbiCoder } from "@ethersproject/abi"; +import { BigNumber } from "bignumber.js"; +import { buildDSAv2 } from "../../../scripts/tests/buildDSAv2"; +import { addresses } from "../../../scripts/tests/optimism/addresses"; +import { deployAndEnableConnector } from "../../../scripts/tests/deployAndEnableConnector"; +import { abis } from "../../../scripts/constant/abis"; +import { getMasterSigner } from "../../../scripts/tests/getMasterSigner"; +import { parseEther, parseUnits } from "ethers/lib/utils"; +import { encodeSpells } from "../../../scripts/tests/encodeSpells"; +import encodeFlashcastData from "../../../scripts/tests/encodeFlashcastData"; +import { ConnectV2AaveV3Optimism__factory, IERC20__factory } from "../../../typechain"; + +const ABI = ["function balanceOf(address account) public view returns (uint256)"]; + +const aDaiAddress = "0x82E64f49Ed5EC1bC6e43DAD4FC8Af9bb3A2312EE"; +const aaveAddress = "0x69FA688f1Dc47d4B5d8029D5a35FB7a548310654"; +let account = "0xa06067164E76285cef0ac6C42B3AD3Ce9BE5039A"; +const DAI = "0xDA10009cBd5D07dd0CeCc66161FC93D7c9000da1"; +const USDC = "0x7F5c764cBc14f9669B88837ca1490cCa17c31607"; +const mnemonic = "test test test test test test test test test test test junk"; +const connectorName = "AAVE-V3-X"; +let signer: any, wallet0: any; + +const aaveAbi = [ + { + inputs: [ + { internalType: "address", name: "asset", type: "address" }, + { internalType: "address", name: "user", type: "address" } + ], + name: "getUserReserveData", + outputs: [ + { internalType: "uint256", name: "currentATokenBalance", type: "uint256" }, + { internalType: "uint256", name: "currentStableDebt", type: "uint256" }, + { internalType: "uint256", name: "currentVariableDebt", type: "uint256" }, + { internalType: "uint256", name: "principalStableDebt", type: "uint256" }, + { internalType: "uint256", name: "scaledVariableDebt", type: "uint256" }, + { internalType: "uint256", name: "stableBorrowRate", type: "uint256" }, + { internalType: "uint256", name: "liquidityRate", type: "uint256" }, + { internalType: "uint40", name: "stableRateLastUpdated", type: "uint40" }, + { internalType: "bool", name: "usageAsCollateralEnabled", type: "bool" } + ], + stateMutability: "view", + type: "function" + } +]; + +const erc20Abi = [ + { + constant: false, + inputs: [ + { + name: "_spender", + type: "address" + }, + { + name: "_value", + type: "uint256" + } + ], + name: "approve", + outputs: [ + { + name: "", + type: "bool" + } + ], + payable: false, + stateMutability: "nonpayable", + type: "function" + }, + { + constant: true, + inputs: [], + name: "totalSupply", + outputs: [ + { + name: "", + type: "uint256" + } + ], + payable: false, + stateMutability: "view", + type: "function" + }, + { + constant: true, + inputs: [ + { + name: "_owner", + type: "address" + } + ], + name: "balanceOf", + outputs: [ + { + name: "balance", + type: "uint256" + } + ], + payable: false, + stateMutability: "view", + type: "function" + }, + { + constant: false, + inputs: [ + { + name: "_to", + type: "address" + }, + { + name: "_value", + type: "uint256" + } + ], + name: "transfer", + outputs: [ + { + name: "", + type: "bool" + } + ], + payable: false, + stateMutability: "nonpayable", + type: "function" + } +]; + +const token = new ethers.Contract(DAI, erc20Abi); +const aDai = new ethers.Contract(aDaiAddress, ABI); +const usdcToken = new ethers.Contract(USDC, erc20Abi); +const aave = new ethers.Contract(aaveAddress, aaveAbi); + +describe("Aave v3 Position for Optimism", function () { + let dsaWallet0: any; + let masterSigner: Signer; + let instaConnectorsV2: Contract; + let connector: any; + + const wallet = ethers.Wallet.fromMnemonic(mnemonic); + + before(async () => { + await hre.network.provider.request({ + method: "hardhat_reset", + params: [ + { + forking: { + //@ts-ignore + jsonRpcUrl: hre.config.networks.hardhat.forking.url, + blockNumber: 12230000 + } + } + ] + }); + masterSigner = await getMasterSigner(); + [wallet0] = await ethers.getSigners(); + await hre.network.provider.send("hardhat_setBalance", [account, ethers.utils.parseEther("10").toHexString()]); + + await hre.network.provider.request({ + method: "hardhat_impersonateAccount", + params: [account] + }); + + signer = await ethers.getSigner(account); + + await token.connect(signer).transfer(wallet0.address, ethers.utils.parseEther("10")); + + instaConnectorsV2 = await ethers.getContractAt(abis.core.connectorsV2, addresses.core.connectorsV2); + connector = await deployAndEnableConnector({ + connectorName, + contractArtifact: ConnectV2AaveV3Optimism__factory, + signer: masterSigner, + connectors: instaConnectorsV2 + }); + }); + + describe("Deployment", async () => { + it("Should set correct name", async () => { + expect(await connector.name()).to.eq("AaveV3-v1.2"); + }); + }); + + describe("DSA wallet setup", async () => { + it("Should build DSA v2", async () => { + dsaWallet0 = await buildDSAv2(wallet0.address); + expect(!!dsaWallet0.address).to.be.true; + }); + + it("Deposit ETH into DSA wallet", async function () { + await wallet0.sendTransaction({ + to: dsaWallet0.address, + value: ethers.utils.parseEther("5") + }); + + expect(await ethers.provider.getBalance(dsaWallet0.address)).to.be.gte(ethers.utils.parseEther("5")); + }); + }); + + describe("check user AAVE position", async () => { + it("Should create DSA Aave v3 position of DAI(collateral) and USDC(debt)", async () => { + await token.connect(signer).transfer(dsaWallet0.address, ethers.utils.parseEther("10")); + + const spells = [ + //deposit DAI in aave + { + connector: connectorName, + method: "deposit", + args: [DAI, parseEther("10"), 0, 0] + }, + //borrow USDC from aave + { + connector: connectorName, + method: "borrow", + args: [USDC, parseUnits("3", 6), 2, 0, 0] + } + ]; + const tx = await dsaWallet0.connect(wallet0).cast(...encodeSpells(spells), wallet0.address); + const receipt = await tx.wait(); + }); + + it("Should check position of dsa", async () => { + expect(await aDai.connect(wallet0).balanceOf(dsaWallet0.address)).to.be.gte( + new BigNumber(10).multipliedBy(1e18).toString() + ); + + expect(await usdcToken.connect(wallet0).balanceOf(dsaWallet0.address)).to.be.gte( + new BigNumber(3).multipliedBy(1e6).toString() + ); + + expect((await aave.connect(wallet0).getUserReserveData(USDC, dsaWallet0.address)).currentStableDebt).to.be.equal(0); + expect((await aave.connect(wallet0).getUserReserveData(USDC, dsaWallet0.address)).currentVariableDebt).to.be.gte( + new BigNumber(3).multipliedBy(1e6).toString() + ); + }); + + it("Should swap borrowRateMode", async () => { + const spells = [ + //deposit DAI in aave + { + connector: connectorName, + method: "swapBorrowRateMode", + args: [USDC, 2] + } + ]; + const tx = await dsaWallet0.connect(wallet0).cast(...encodeSpells(spells), wallet0.address); + const receipt = await tx.wait(); + }); + + it("Should check position of dsa", async () => { + expect(await aDai.connect(wallet0).balanceOf(dsaWallet0.address)).to.be.gte( + new BigNumber(10).multipliedBy(1e18).toString() + ); + + expect(await usdcToken.connect(wallet0).balanceOf(dsaWallet0.address)).to.be.gte( + new BigNumber(3).multipliedBy(1e6).toString() + ); + expect((await aave.connect(wallet0).getUserReserveData(USDC, dsaWallet0.address)).currentVariableDebt).to.be.equal(0); + expect((await aave.connect(wallet0).getUserReserveData(USDC, dsaWallet0.address)).currentStableDebt).to.be.gte( + new BigNumber(3).multipliedBy(1e6).toString() + ); + }); + }); +}); diff --git a/test/optimism/connext/connext.test.ts b/test/optimism/connext/connext.test.ts new file mode 100644 index 0000000..ff85694 --- /dev/null +++ b/test/optimism/connext/connext.test.ts @@ -0,0 +1,154 @@ +import { expect } from "chai"; +import hre from "hardhat"; +const { ethers, waffle } = hre; +const { provider } = waffle; + +import { deployAndEnableConnector } from "../../../scripts/tests/deployAndEnableConnector"; +import { buildDSAv2 } from "../../../scripts/tests/buildDSAv2"; +import { encodeSpells } from "../../../scripts/tests/encodeSpells"; +import { getMasterSigner } from "../../../scripts/tests/getMasterSigner"; +import { addresses } from "../../../scripts/tests/optimism/addresses"; +import { abis } from "../../../scripts/constant/abis"; +import { ConnectV2ConnextOptimism__factory } from "../../../typechain"; +import { Signer, Contract } from "ethers"; + + +describe("Connext Connector [Optimism]", () => { + const connectorName = "CONNEXT-TEST-A"; + + let dsaWallet0: Contract; + let masterSigner: Signer; + let instaConnectorsV2: Contract; + let connector: Contract; + let usdcContract: Contract; + let signer: any; + + const usdcAddr = "0x7F5c764cBc14f9669B88837ca1490cCa17c31607"; + const ethAddr = "0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE"; + const account = "0xebe80f029b1c02862b9e8a70a7e5317c06f62cae"; + + const wallets = provider.getWallets(); + const [wallet0, wallet1] = wallets; + + before(async () => { + await hre.network.provider.request({ + method: "hardhat_reset", + params: [ + { + forking: { + // @ts-ignore + jsonRpcUrl: hre.config.networks.hardhat.forking.url, + blockNumber: 82686991 + } + } + ] + }); + + masterSigner = await getMasterSigner(); + instaConnectorsV2 = await ethers.getContractAt(abis.core.connectorsV2, addresses.core.connectorsV2); + connector = await deployAndEnableConnector({ + connectorName, + contractArtifact: ConnectV2ConnextOptimism__factory, + signer: masterSigner, + connectors: instaConnectorsV2 + }); + usdcContract = await ethers.getContractAt(abis.basic.erc20, usdcAddr); + signer = await ethers.getSigner(account); + + await hre.network.provider.send("hardhat_setBalance", [account, ethers.utils.parseEther("10").toHexString()]); + + await hre.network.provider.request({ + method: "hardhat_impersonateAccount", + params: [account] + }); + + await usdcContract.connect(signer).transfer(wallet0.address, ethers.utils.parseUnits("10000", 6)); + console.log("deployed connector: ", connector.address); + }); + + it("Should have contracts deployed.", async () => { + expect(!!instaConnectorsV2.address).to.be.true; + expect(!!connector.address).to.be.true; + expect(!!(await masterSigner.getAddress())).to.be.true; + }); + + describe("DSA wallet setup", () => { + it("Should build DSA v2", async () => { + dsaWallet0 = await buildDSAv2(wallet0.getAddress()); + expect(!!dsaWallet0.address).to.be.true; + }); + + it("Deposit ETH & USDC into DSA wallet", async () => { + await wallet0.sendTransaction({ + to: dsaWallet0.address, + value: ethers.utils.parseEther("10") + }); + expect(await ethers.provider.getBalance(dsaWallet0.address)).to.be.gte(ethers.utils.parseEther("10")); + + await usdcContract.connect(wallet0).transfer(dsaWallet0.address, ethers.utils.parseUnits("10", 6)); + expect(await usdcContract.balanceOf(dsaWallet0.address)).to.be.gte(ethers.utils.parseUnits("10", 6)); + }); + }); + + describe("Main", () => { + it("should xcall with eth", async () => { + const amount = ethers.utils.parseEther("5"); + const domainId = 6648936; + const slippage = 10000; + const relayerFee = ethers.utils.parseEther("1"); + const callData = "0x"; + + const xcallParams: any = [ + domainId, + wallet1.address, + ethAddr, + wallet1.address, + amount, + slippage, + relayerFee, + callData + ]; + + const spells = [ + { + connector: connectorName, + method: "xcall", + args: [xcallParams, 0, 0] + } + ]; + + const tx = await dsaWallet0.connect(wallet0).cast(...encodeSpells(spells), wallet1.address); + const receipt = await tx.wait(); + }); + + it("should xcall with usdc", async () => { + const amount = ethers.utils.parseUnits("5", 6); + const domainId = 6648936; + const slippage = 10000; + const relayerFee = ethers.utils.parseEther("1"); + const callData = "0x"; + + const xcallParams: any = [ + domainId, + wallet1.address, + usdcAddr, + wallet1.address, + amount, + slippage, + relayerFee, + callData + ]; + + const spells = [ + { + connector: connectorName, + method: "xcall", + args: [xcallParams, 0, 0] + } + ]; + + const tx = await dsaWallet0.connect(wallet0).cast(...encodeSpells(spells), wallet1.address); + const receipt = await tx.wait(); + }); + }); +}); diff --git a/test/optimism/dsa-spell/dsa-spell.test.ts b/test/optimism/dsa-spell/dsa-spell.test.ts new file mode 100644 index 0000000..af9fa8b --- /dev/null +++ b/test/optimism/dsa-spell/dsa-spell.test.ts @@ -0,0 +1,180 @@ +import hre from "hardhat"; +import axios from "axios"; +import { expect } from "chai"; +const { ethers } = hre; //check +import { deployAndEnableConnector } from "../../../scripts/tests/deployAndEnableConnector"; +import { buildDSAv2 } from "../../../scripts/tests/buildDSAv2"; +import { encodeSpells } from "../../../scripts/tests/encodeSpells"; +import { getMasterSigner } from "../../../scripts/tests/getMasterSigner"; +import { addresses } from "../../../scripts/tests/optimism/addresses"; +import { abis } from "../../../scripts/constant/abis"; +import { ConnectV2DSASpellOptimism__factory } from "../../../typechain"; +import type { Signer, Contract } from "ethers"; +import BigNumber from "bignumber.js"; + +describe("DSA Spell", function () { + const connectorName = "dsa-spell-test"; + + let dsaWallet0: any; + let dsaWallet1: any; + let dsaWallet2: any; + let walletB: any; + let wallet0: any; + let masterSigner: Signer; + let instaConnectorsV2: Contract; + let connector: any; + + before(async () => { + await hre.network.provider.request({ + method: "hardhat_reset", + params: [ + { + forking: { + // @ts-ignore + jsonRpcUrl: hre.config.networks.hardhat.forking.url + } + } + ] + }); + [wallet0] = await ethers.getSigners(); + + masterSigner = await getMasterSigner(); + instaConnectorsV2 = await ethers.getContractAt(abis.core.connectorsV2, addresses.core.connectorsV2); + connector = await deployAndEnableConnector({ + connectorName, + contractArtifact: ConnectV2DSASpellOptimism__factory, + signer: masterSigner, + connectors: instaConnectorsV2 + }); + console.log("\tConnector address", connector.address); + }); + + it("Should have contracts deployed.", async function () { + expect(!!instaConnectorsV2.address).to.be.true; + expect(!!connector.address).to.be.true; + expect(!!(await masterSigner.getAddress())).to.be.true; + }); + + describe("DSA wallet setup", function () { + it("Should build DSA v2", async function () { + dsaWallet0 = await buildDSAv2(wallet0.address); + expect(!!dsaWallet0.address).to.be.true; + walletB = await ethers.getSigner(dsaWallet0.address); + dsaWallet1 = await buildDSAv2(dsaWallet0.address); + expect(!!dsaWallet1.address).to.be.true; + console.log(`\t${dsaWallet1.address}`); + }); + + it("Deposit eth into DSA wallet 0", async function () { + await wallet0.sendTransaction({ + to: dsaWallet0.address, + value: ethers.utils.parseEther("10") + }); + + expect(await ethers.provider.getBalance(dsaWallet0.address)).to.be.gte(ethers.utils.parseEther("10")); + }); + + it("Deposit eth into DSA wallet 1", async function () { + await wallet0.sendTransaction({ + to: dsaWallet1.address, + value: ethers.utils.parseEther("10") + }); + + expect(await ethers.provider.getBalance(dsaWallet1.address)).to.be.gte(ethers.utils.parseEther("10")); + }); + }); + + describe("Main", function () { + let ETH = "0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE"; + let USDC = "0x7F5c764cBc14f9669B88837ca1490cCa17c31607"; + let usdc = new ethers.Contract(USDC, abis.basic.erc20); + let aETH = "0xe50fA9b3c56FfB159cB0FCA61F5c9D750e8128c8"; + let aEth = new ethers.Contract(aETH, abis.basic.aToken); + var abi = [ + "function withdraw(address,uint256,address,uint256,uint256)", + "function deposit(address,uint256,uint256,uint256)", + "function borrow(address,uint256,uint256,uint256,uint256)" + ]; + function getCallData(spell: string, params: any) { + var iface = new ethers.utils.Interface(abi); + let data = iface.encodeFunctionData(spell, params); + return ethers.utils.hexlify(data); + } + + it("should cast spells", async function () { + async function getArg(connectors: any, spells: any, params: any) { + let datas = []; + for (let i = 0; i < connectors.length; i++) { + datas.push(getCallData(spells[i], params[i])); + } + return [dsaWallet1.address, connectors, datas]; + } + + let connectors = ["BASIC-A", "AAVE-V3-A", "AAVE-V3-A"]; + let methods = ["withdraw", "deposit", "borrow"]; + let params = [ + [ETH, ethers.utils.parseEther("2"), dsaWallet0.address, 0, 0], + [ETH, ethers.constants.MaxUint256, 0, 0], + [USDC, ethers.utils.parseUnits("1", 6), 2, 0, 0] + ]; + let arg = await getArg(connectors, methods, params); + + const spells = [ + { + connector: connectorName, + method: "castOnDSA", + args: arg + } + ]; + const tx = await dsaWallet0.connect(wallet0).cast(...encodeSpells(spells), await wallet0.getAddress()); + const receipt = await tx.wait(); + }); + + it("should check balances after cast on DSA", async function () { + expect(await ethers.provider.getBalance(dsaWallet1.address)).to.be.lte(0); + expect(await usdc.connect(wallet0).balanceOf(dsaWallet1.address)).to.be.gte( + new BigNumber(1).multipliedBy(1e6).toString() + ); + expect(await ethers.provider.getBalance(dsaWallet0.address)).to.be.gte( + new BigNumber(12).multipliedBy(1e18).toString() + ); + }); + + it("should cast spell on the first successful", async function () { + async function getArg(connectors: any, spells: any, params: any) { + let datas = []; + for (let i = 0; i < connectors.length; i++) { + datas.push(getCallData(spells[i], params[i])); + } + return [connectors, datas]; + } + + let connectors = ["AAVE-V3-A"]; + let methods = ["deposit"]; + let params = [ + [ETH, ethers.utils.parseEther("10"), 0, 0] + ]; + let arg = await getArg(connectors, methods, params); + const spells = [ + { + connector: connectorName, + method: "castAny", + args: arg + } + ]; + const tx = await dsaWallet0 + .connect(wallet0) + .cast(...encodeSpells(spells), await wallet0.getAddress()); + const receipt = await tx.wait(); + }); + + it("should check balances after spells on DSA", async function () { + expect(await ethers.provider.getBalance(dsaWallet0.address)).to.be.lte( + new BigNumber(2).multipliedBy(1e18).toString() + ); + expect(await aEth.connect(wallet0).balanceOf(dsaWallet0.address)).to.be.gte( + new BigNumber(10).multipliedBy(1e18).toString() + ); + }); + }); +}); diff --git a/test/optimism/swap/swap-test.ts b/test/optimism/swap/swap-test.ts new file mode 100644 index 0000000..90949d1 --- /dev/null +++ b/test/optimism/swap/swap-test.ts @@ -0,0 +1,179 @@ +import hre from "hardhat"; +import axios from "axios"; +import { expect } from "chai"; +const { ethers } = hre; //check +import { BigNumber } from "bignumber.js"; +import { deployAndEnableConnector } from "../../../scripts/tests/deployAndEnableConnector"; +import { buildDSAv2 } from "../../../scripts/tests/buildDSAv2"; +import { encodeSpells } from "../../../scripts/tests/encodeSpells"; +import { getMasterSigner } from "../../../scripts/tests/getMasterSigner"; +import { addresses } from "../../../scripts/tests/optimism/addresses"; +import { abis } from "../../../scripts/constant/abis"; +import { ConnectV2SwapAggregatorOptimism__factory } from "../../../typechain"; +import er20abi from "../../../scripts/constant/abi/basics/erc20.json"; +import type { Signer, Contract } from "ethers"; + +describe("Swap | Optimism", function () { + const connectorName = "swap-test"; + + let dsaWallet0: Contract; + let wallet0: Signer, wallet1: Signer; + let masterSigner: Signer; + let instaConnectorsV2: Contract; + let connector: Contract; + + before(async () => { + await hre.network.provider.request({ + method: "hardhat_reset", + params: [ + { + forking: { + // @ts-ignore + jsonRpcUrl: hre.config.networks.hardhat.forking.url + } + } + ] + }); + [wallet0, wallet1] = await ethers.getSigners(); + + masterSigner = await getMasterSigner(); + instaConnectorsV2 = await ethers.getContractAt(abis.core.connectorsV2, addresses.core.connectorsV2); + connector = await deployAndEnableConnector({ + connectorName, + contractArtifact: ConnectV2SwapAggregatorOptimism__factory, + signer: masterSigner, + connectors: instaConnectorsV2 + }); + console.log("Connector address", connector.address); + }); + + it("Should have contracts deployed.", async function () { + expect(!!instaConnectorsV2.address).to.be.true; + expect(!!connector.address).to.be.true; + expect(!!(await masterSigner.getAddress())).to.be.true; + }); + + describe("DSA wallet setup", function () { + it("Should build DSA v2", async function () { + dsaWallet0 = await buildDSAv2(await wallet0.getAddress()); + console.log(dsaWallet0.address); + expect(!!dsaWallet0.address).to.be.true; + }); + + it("Deposit matic into DSA wallet", async function () { + await wallet0.sendTransaction({ + to: dsaWallet0.address, + value: ethers.utils.parseEther("10") + }); + + expect(await ethers.provider.getBalance(dsaWallet0.address)).to.be.gte(ethers.utils.parseEther("10")); + }); + }); + + describe("Main", function () { + it("should swap the tokens", async function () { + let buyTokenAmountZeroX: any; + let buyTokenAmount1Inch: any; + let buyTokenAmountParaswap: any; + + async function getArg() { + const slippage = 0.5; + /* eth -> dai */ + const sellTokenAddress = "0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE"; // eth, decimals 18 + const sellTokenDecimals = 18; + const buyTokenAddress = "0xDA10009cBd5D07dd0CeCc66161FC93D7c9000da1"; // DAI, decimals 6 + const buyTokenDecimals = 18; + const amount = 1; + + const srcAmount = new BigNumber(amount).times(new BigNumber(10).pow(sellTokenDecimals)).toFixed(0); + + let zeroXUrl = `https://optimism.api.0x.org/swap/v1/quote`; + + //zeroX + const paramsZeroX = { + buyToken: "DAI", + sellToken: "ETH", + sellAmount: "1000000000000000000" // Always denominated in wei + }; + + const responseZeroX = await axios.get(zeroXUrl, { params: paramsZeroX }).then((data: any) => data); + buyTokenAmountZeroX = responseZeroX.data.buyAmount; + const calldataZeroX = responseZeroX.data.data; + + //1inch + const paramDaiUsdc = { + buyToken: buyTokenAddress, + sellToken: sellTokenAddress, + sellAmount: "1000000000000000000", + dsaAddress: dsaWallet0.address + }; + const response1 = await axios.get("https://api.instadapp.io/defi/optimism/1inch/swap", { + params: paramDaiUsdc + }); + + const data1 = response1.data; + let unitAmt1Inch = data1.unitAmt; + const calldata1Inch = data1.calldata; + buyTokenAmount1Inch = data1.buyTokenAmount; + console.log(buyTokenAmount1Inch); + + let calculateUnitAmt = (buyAmount: any) => { + const buyTokenAmountRes = new BigNumber(buyAmount) + .dividedBy(new BigNumber(10).pow(buyTokenDecimals)) + .toFixed(8); + + let unitAmt: any = new BigNumber(buyTokenAmountRes).dividedBy(new BigNumber(amount)); + + unitAmt = unitAmt.multipliedBy((100 - 1) / 100); + unitAmt = unitAmt.multipliedBy(1e18).toFixed(0); + return unitAmt; + }; + let unitAmt0x = calculateUnitAmt(buyTokenAmountZeroX); + let unitAmtParaswap = calculateUnitAmt(buyTokenAmountParaswap); + + function getCallData(connector: string, unitAmt: any, callData: any) { + var abi = [ + "function swap(address,address,uint256,uint256,bytes,uint256)", + "function sell(address,address,uint256,uint256,bytes,uint256)" + ]; + var iface = new ethers.utils.Interface(abi); + const spell = connector === "1INCH-A" ? "sell" : "swap"; + let data = iface.encodeFunctionData(spell, [ + buyTokenAddress, + sellTokenAddress, + srcAmount, + unitAmt, + callData, + 0 + ]); + return data; + } + let data1Inch = ethers.utils.hexlify(await getCallData("1INCH-A", unitAmt1Inch, calldata1Inch)); + let dataZeroX = ethers.utils.hexlify(await getCallData("ZEROX-A", unitAmt0x, calldataZeroX)); + let datas = [data1Inch]; + + let connectors = ["1INCH-A"]; + return [connectors, datas]; + } + + let arg = await getArg(); + const spells = [ + { + connector: connectorName, + method: "swap", + args: arg + } + ]; + const tx = await dsaWallet0.connect(wallet0).cast(...encodeSpells(spells), await wallet1.getAddress()); + const receipt = await tx.wait(); + + const daiToken = await ethers.getContractAt( + er20abi, + "0xDA10009cBd5D07dd0CeCc66161FC93D7c9000da1" // dai address + ); + + expect(await daiToken.balanceOf(dsaWallet0.address)).to.be.gte(buyTokenAmount1Inch); + expect(await ethers.provider.getBalance(dsaWallet0.address)).to.be.lte(ethers.utils.parseEther("9")); + }); + }); +}); diff --git a/test/optimism/uniswap/uniswap-swap.test.ts b/test/optimism/uniswap/uniswap-swap.test.ts new file mode 100644 index 0000000..c80ab15 --- /dev/null +++ b/test/optimism/uniswap/uniswap-swap.test.ts @@ -0,0 +1,157 @@ +import { expect } from "chai"; +import hre from "hardhat"; +const { web3, deployments, waffle, ethers } = hre; +const { provider, deployContract } = waffle; + +import { deployAndEnableConnector } from "../../../scripts/tests/deployAndEnableConnector"; +import { buildDSAv2 } from "../../../scripts/tests/buildDSAv2"; +import { encodeSpells } from "../../../scripts/tests/encodeSpells"; +import { getMasterSigner } from "../../../scripts/tests/getMasterSigner"; +import { addLiquidity } from "../../../scripts/tests/addLiquidity"; +import { addresses } from "../../../scripts/tests/optimism/addresses"; +import { abis } from "../../../scripts/constant/abis"; +import type { Signer, Contract } from "ethers"; + +import { abi } from "@uniswap/v3-periphery/artifacts/contracts/NonfungiblePositionManager.sol/NonfungiblePositionManager.json"; +import { ConnectV2UniswapV3SwapOptimism__factory } from "../../../typechain"; + +const FeeAmount = { + LOW: 500, + MEDIUM: 3000, + HIGH: 10000, +}; + +const TICK_SPACINGS: Record = { + 500: 10, + 3000: 60, + 10000: 200, +}; + +const DAI_ADDR = "0xDA10009cBd5D07dd0CeCc66161FC93D7c9000da1"; + +let tokenIds: any[] = []; +let liquidities: any[] = []; +const abiCoder = ethers.utils.defaultAbiCoder; + +describe("UniswapV3 [Optimism]", function() { + const connectorName = "UniswapV3-Swap-v1"; + + let dsaWallet0: any; + let masterSigner: Signer; + let instaConnectorsV2: Contract; + let connector: Contract; + let nftManager: Contract; + + const wallets = provider.getWallets(); + const [wallet0, wallet1, wallet2, wallet3] = wallets; + before(async () => { + await hre.network.provider.request({ + method: "hardhat_reset", + params: [ + { + forking: { + // @ts-ignore + jsonRpcUrl: hre.config.networks.hardhat.forking.url, + blockNumber: 7093500, + }, + }, + ], + }); + masterSigner = await getMasterSigner(); + instaConnectorsV2 = await ethers.getContractAt( + abis.core.connectorsV2, + addresses.core.connectorsV2 + ); + connector = await deployAndEnableConnector({ + connectorName, + contractArtifact: ConnectV2UniswapV3SwapOptimism__factory, + signer: masterSigner, + connectors: instaConnectorsV2, + }); + console.log("Connector address", connector.address); + }); + + it("Should have contracts deployed.", async function() { + expect(!!instaConnectorsV2.address).to.be.true; + expect(!!connector.address).to.be.true; + expect(!!(await masterSigner.getAddress())).to.be.true; + }); + + describe("DSA wallet setup", function() { + it("Should build DSA v2", async function() { + dsaWallet0 = await buildDSAv2(wallet0.address); + expect(!!dsaWallet0.address).to.be.true; + }); + + it("Deposit ETH & DAI into DSA wallet", async function() { + await wallet0.sendTransaction({ + to: dsaWallet0.address, + value: ethers.utils.parseEther("10"), + }); + expect(await ethers.provider.getBalance(dsaWallet0.address)).to.be.gte( + ethers.utils.parseEther("10") + ); + + await addLiquidity( + "dai", + dsaWallet0.address, + ethers.utils.parseEther("100000") + ); + }); + }); + + describe("Main", function() { + + it("Should buy successfully", async function() { + const ethAmount = ethers.utils.parseEther("0.1"); + const unitAmt = ethers.utils.parseEther("3078") + const ethAddress = "0xeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee"; + const getId = "0"; + const setId = "0"; + + const spells = [ + { + connector: connectorName, + method: "buy", + args: [ethAddress, DAI_ADDR, FeeAmount.MEDIUM, unitAmt, ethAmount, getId, setId], + }, + ]; + + const tx = await dsaWallet0 + .connect(wallet0) + .cast(...encodeSpells(spells), wallet1.address); + const receipt = await tx.wait(); + // console.log(receipt); + }); + + it("Should sell successfully", async function() { + const ethAmount = ethers.utils.parseEther("0.1"); + const unitAmt = ethers.utils.parseEther("2784") + const ethAddress = "0xeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee"; + const getId = "0"; + const setId = "0"; + + const spells = [ + { + connector: connectorName, + method: "sell", + args: [DAI_ADDR, ethAddress, FeeAmount.MEDIUM, unitAmt, ethAmount, getId, setId], + }, + ]; + + const tx = await dsaWallet0 + .connect(wallet0) + .cast(...encodeSpells(spells), wallet1.address); + const receipt = await tx.wait(); + // console.log(receipt); + }); + }); +}); + +const getMinTick = (tickSpacing: number) => + Math.ceil(-887272 / tickSpacing) * tickSpacing; +const getMaxTick = (tickSpacing: number) => + Math.floor(887272 / tickSpacing) * tickSpacing; + + + \ No newline at end of file diff --git a/test/polygon/0x/zeroEx.test.ts b/test/polygon/0x/zeroEx.test.ts new file mode 100644 index 0000000..54758ce --- /dev/null +++ b/test/polygon/0x/zeroEx.test.ts @@ -0,0 +1,159 @@ +import hre from "hardhat"; +import axios from "axios"; +import { expect } from "chai"; +const { ethers } = hre; //check +import { BigNumber } from "bignumber.js"; +import { deployAndEnableConnector } from "../../../scripts/tests/deployAndEnableConnector"; +import { buildDSAv2 } from "../../../scripts/tests/buildDSAv2"; +import { encodeSpells } from "../../../scripts/tests/encodeSpells"; +import { getMasterSigner } from "../../../scripts/tests/getMasterSigner"; +import { addresses } from "../../../scripts/tests/polygon/addresses"; +import { abis } from "../../../scripts/constant/abis"; +import {ConnectV2ZeroExPolygon__factory } from "../../../typechain"; +import er20abi from "../../../scripts/constant/abi/basics/erc20.json"; +import type { Signer, Contract } from "ethers"; + +describe("ZeroEx", function() { + const connectorName = "zeroEx-test"; + + let dsaWallet0: Contract; + let wallet0: Signer, wallet1: Signer; + let masterSigner: Signer; + let instaConnectorsV2: Contract; + let connector: Contract; + + before(async () => { + await hre.network.provider.request({ + method: "hardhat_reset", + params: [ + { + forking: { + // @ts-ignore + jsonRpcUrl: hre.config.networks.hardhat.forking.url, + }, + }, + ], + }); + [wallet0, wallet1] = await ethers.getSigners(); + + masterSigner = await getMasterSigner(); + instaConnectorsV2 = await ethers.getContractAt( + abis.core.connectorsV2, + addresses.core.connectorsV2 + ); + connector = await deployAndEnableConnector({ + connectorName, + contractArtifact: ConnectV2ZeroExPolygon__factory, + signer: masterSigner, + connectors: instaConnectorsV2, + }); + console.log("Connector address", connector.address); + }); + + it("Should have contracts deployed.", async function() { + expect(!!instaConnectorsV2.address).to.be.true; + expect(!!connector.address).to.be.true; + expect(!!(await masterSigner.getAddress())).to.be.true; + }); + + describe("DSA wallet setup", function() { + it("Should build DSA v2", async function() { + dsaWallet0 = await buildDSAv2(await wallet0.getAddress()); + expect(!!dsaWallet0.address).to.be.true; + }); + + it("Deposit matic into DSA wallet", async function() { + await wallet0.sendTransaction({ + to: dsaWallet0.address, + value: ethers.utils.parseEther("10"), + }); + + expect(await ethers.provider.getBalance(dsaWallet0.address)).to.be.gte( + ethers.utils.parseEther("10") + ); + }); + }); + + describe("Main", function() { + it("should swap the tokens", async function() { + let buyTokenAmount: any; + async function getArg() { + // const slippage = 0.5; + /* matic -> dai */ + const sellTokenAddress = "0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE"; // matic, decimals 18 + const sellTokenDecimals = 18; + const buyTokenAddress = "0x8f3Cf7ad23Cd3CaDbD9735AFf958023239c6A063"; // dai, decimals 18 + const buyTokenDecimals = 18; + const amount = 1; + + const srcAmount = new BigNumber(amount) + .times(new BigNumber(10).pow(sellTokenDecimals)) + .toFixed(0); + + let url = `https://polygon.api.0x.org/swap/v1/quote`; + + const params = { + buyToken: "DAI", + sellToken: "MATIC", + sellAmount: "1000000000000000000", // Always denominated in wei + }; + + const response = await axios + .get(url, { params: params }) + .then((data: any) => data); + + buyTokenAmount = response.data.buyAmount; + const calldata = response.data.data; + + let caculateUnitAmt = () => { + const buyTokenAmountRes = new BigNumber(buyTokenAmount) + .dividedBy(new BigNumber(10).pow(buyTokenDecimals)) + .toFixed(8); + + let unitAmt: any = new BigNumber(buyTokenAmountRes).dividedBy( + new BigNumber(amount) + ); + + unitAmt = unitAmt.multipliedBy((100 - 0.3) / 100); + unitAmt = unitAmt.multipliedBy(1e18).toFixed(0); + return unitAmt; + }; + let unitAmt = caculateUnitAmt(); + + return [ + buyTokenAddress, + sellTokenAddress, + srcAmount, + unitAmt, + calldata, + 0, + ]; + } + + let arg = await getArg(); + const spells = [ + { + connector: connectorName, + method: "swap", + args: arg, + }, + ]; + const tx = await dsaWallet0 + .connect(wallet0) + .cast(...encodeSpells(spells), await wallet1.getAddress()); + const receipt = await tx.wait(); + + const daiToken = await ethers.getContractAt( + er20abi, + "0x8f3Cf7ad23Cd3CaDbD9735AFf958023239c6A063" // dai address + ); + + expect(await daiToken.balanceOf(dsaWallet0.address)).to.be.gte( + buyTokenAmount + ); + expect(await ethers.provider.getBalance(dsaWallet0.address)).to.be.lte( + ethers.utils.parseEther("9") + ); + }); + }); +}); diff --git a/test/polygon/aave/aaveV3-import-test.ts b/test/polygon/aave/aaveV3-import-test.ts new file mode 100644 index 0000000..86fd50d --- /dev/null +++ b/test/polygon/aave/aaveV3-import-test.ts @@ -0,0 +1,368 @@ +import { expect, should } from "chai"; +import hre, { ethers, waffle } from "hardhat"; +import type { Signer, Contract } from "ethers"; +import { ecsign, ecrecover, pubToAddress } from "ethereumjs-util"; +import { keccak256 } from "@ethersproject/keccak256"; +import { toUtf8Bytes } from "@ethersproject/strings"; +import { defaultAbiCoder } from "@ethersproject/abi"; +import { BigNumber } from "bignumber.js"; +import { buildDSAv2 } from "../../../scripts/tests/buildDSAv2"; +import { addresses } from "../../../scripts/tests/polygon/addresses"; +import { deployAndEnableConnector } from "../../../scripts/tests/deployAndEnableConnector"; +import { abis } from "../../../scripts/constant/abis"; +import { getMasterSigner } from "../../../scripts/tests/getMasterSigner"; +import { parseEther, parseUnits } from "ethers/lib/utils"; +import { encodeSpells } from "../../../scripts/tests/encodeSpells"; +import encodeFlashcastData from "../../../scripts/tests/encodeFlashcastData"; +import { ConnectV2AaveV3ImportPermitPolygon__factory, IERC20__factory } from "../../../typechain"; + +const ABI = [ + "function DOMAIN_SEPARATOR() public view returns (bytes32)", + "function balanceOf(address account) public view returns (uint256)", + "function nonces(address owner) public view returns (uint256)" +]; + +const aDaiAddress = "0x82E64f49Ed5EC1bC6e43DAD4FC8Af9bb3A2312EE"; +const aaveAddress = "0x794a61358D6845594F94dc1DB02A252b5b4814aD"; +const account = "0xf04adbf75cdfc5ed26eea4bbbb991db002036bdd"; +const DAI = "0x8f3cf7ad23cd3cadbd9735aff958023239c6a063"; +const USDC = "0x2791bca1f2de4661ed88a30c99a7a9449aa84174"; +const mnemonic = "test test test test test test test test test test test junk"; +const connectorName = "AAVE-V3-IMPORT-PERMIT-X"; +let signer: any, wallet0: any; + +const aaveAbi = [ + { + inputs: [ + { + internalType: "address", + name: "asset", + type: "address" + }, + { + internalType: "uint256", + name: "amount", + type: "uint256" + }, + { + internalType: "uint256", + name: "interestRateMode", + type: "uint256" + }, + { + internalType: "uint16", + name: "referralCode", + type: "uint16" + }, + { + internalType: "address", + name: "onBehalfOf", + type: "address" + } + ], + name: "borrow", + outputs: [], + stateMutability: "nonpayable", + type: "function" + }, + { + inputs: [ + { + internalType: "address", + name: "asset", + type: "address" + }, + { + internalType: "uint256", + name: "amount", + type: "uint256" + }, + { + internalType: "address", + name: "onBehalfOf", + type: "address" + }, + { + internalType: "uint16", + name: "referralCode", + type: "uint16" + } + ], + name: "deposit", + outputs: [], + stateMutability: "nonpayable", + type: "function" + }, + { + inputs: [ + { + internalType: "address", + name: "asset", + type: "address" + }, + { + internalType: "uint256", + name: "amount", + type: "uint256" + }, + { + internalType: "address", + name: "onBehalfOf", + type: "address" + }, + { + internalType: "uint16", + name: "referralCode", + type: "uint16" + } + ], + name: "supply", + outputs: [], + stateMutability: "nonpayable", + type: "function" + } +]; + +const erc20Abi = [ + { + constant: false, + inputs: [ + { + name: "_spender", + type: "address" + }, + { + name: "_value", + type: "uint256" + } + ], + name: "approve", + outputs: [ + { + name: "", + type: "bool" + } + ], + payable: false, + stateMutability: "nonpayable", + type: "function" + }, + { + constant: true, + inputs: [], + name: "totalSupply", + outputs: [ + { + name: "", + type: "uint256" + } + ], + payable: false, + stateMutability: "view", + type: "function" + }, + { + constant: true, + inputs: [ + { + name: "_owner", + type: "address" + } + ], + name: "balanceOf", + outputs: [ + { + name: "balance", + type: "uint256" + } + ], + payable: false, + stateMutability: "view", + type: "function" + }, + { + constant: false, + inputs: [ + { + name: "_to", + type: "address" + }, + { + name: "_value", + type: "uint256" + } + ], + name: "transfer", + outputs: [ + { + name: "", + type: "bool" + } + ], + payable: false, + stateMutability: "nonpayable", + type: "function" + } +]; + +const token = new ethers.Contract(DAI, erc20Abi); +const aDai = new ethers.Contract(aDaiAddress, ABI); +const usdcToken = new ethers.Contract(USDC, erc20Abi); +const aave = new ethers.Contract(aaveAddress, aaveAbi); + +describe("Import Aave v3 Position", function () { + let dsaWallet0: any; + let masterSigner: Signer; + let instaConnectorsV2: Contract; + let connector: any; + + const wallet = ethers.Wallet.fromMnemonic(mnemonic); + + before(async () => { + await hre.network.provider.request({ + method: "hardhat_reset", + params: [ + { + forking: { + //@ts-ignore + jsonRpcUrl: hre.config.networks.hardhat.forking.url, + blockNumber: 26652016 + } + } + ] + }); + masterSigner = await getMasterSigner(); + [wallet0] = await ethers.getSigners(); + await hre.network.provider.send("hardhat_setBalance", [account, ethers.utils.parseEther("10").toHexString()]); + + await hre.network.provider.request({ + method: "hardhat_impersonateAccount", + params: [account] + }); + + signer = await ethers.getSigner(account); + + await token.connect(signer).transfer(wallet0.address, ethers.utils.parseEther("8")); + + instaConnectorsV2 = await ethers.getContractAt(abis.core.connectorsV2, addresses.core.connectorsV2); + connector = await deployAndEnableConnector({ + connectorName, + contractArtifact: ConnectV2AaveV3ImportPermitPolygon__factory, + signer: masterSigner, + connectors: instaConnectorsV2 + }); + }); + + describe("check user AAVE position", async () => { + it("Should create Aave v3 position of DAI(collateral) and USDC(debt)", async () => { + // approve DAI to aavePool + await token.connect(wallet0).approve(aaveAddress, parseEther("8")); + + //deposit DAI in aave + await aave.connect(wallet0).supply(DAI, parseEther("8"), wallet.address, 3228); + console.log("Supplied DAI on aave"); + + //borrow USDC from aave + await aave.connect(wallet0).borrow(USDC, parseUnits("5", 6), 2, 3228, wallet.address); + console.log("Borrowed USDC from aave"); + }); + + it("Should check position of user", async () => { + expect(await aDai.connect(wallet0).balanceOf(wallet.address)).to.be.gte( + new BigNumber(8).multipliedBy(1e18).toString() + ); + + expect(await usdcToken.connect(wallet0).balanceOf(wallet.address)).to.be.gte( + new BigNumber(5).multipliedBy(1e6).toString() + ); + }); + }); + + describe("Deployment", async () => { + it("Should set correct name", async () => { + expect(await connector.name()).to.eq("Aave-v3-import-permit-v1"); + }); + }); + + describe("DSA wallet setup", async () => { + it("Should build DSA v2", async () => { + dsaWallet0 = await buildDSAv2(wallet.address); + expect(!!dsaWallet0.address).to.be.true; + }); + + it("Deposit ETH into DSA wallet", async function () { + await wallet0.sendTransaction({ + to: dsaWallet0.address, + value: ethers.utils.parseEther("5") + }); + + expect(await ethers.provider.getBalance(dsaWallet0.address)).to.be.gte(ethers.utils.parseEther("5")); + }); + }); + + describe("Aave position migration", async () => { + it("Should migrate Aave position", async () => { + const DOMAIN_SEPARATOR = await aDai.connect(wallet0).DOMAIN_SEPARATOR(); + const PERMIT_TYPEHASH = "0x6e71edae12b1b97f4d1f60370fef10105fa2faae0126114a169c64845d6126c9"; + + let nonce = (await aDai.connect(wallet0).nonces(wallet.address)).toNumber(); + //Approving max amount + const amount = ethers.constants.MaxUint256; + const expiry = Date.now() + 20 * 60; + + const digest = keccak256( + ethers.utils.solidityPack( + ["bytes1", "bytes1", "bytes32", "bytes32"], + [ + "0x19", + "0x01", + DOMAIN_SEPARATOR, + keccak256( + defaultAbiCoder.encode( + ["bytes32", "address", "address", "uint256", "uint256", "uint256"], + [PERMIT_TYPEHASH, wallet.address, dsaWallet0.address, amount, nonce, expiry] + ) + ) + ] + ) + ); + const { v, r, s } = ecsign(Buffer.from(digest.slice(2), "hex"), Buffer.from(wallet.privateKey.slice(2), "hex")); + const amount0 = new BigNumber(await usdcToken.connect(wallet0).balanceOf(wallet.address)); + const amountB = new BigNumber(amount0.toString()).multipliedBy(5).dividedBy(1e4); + const amountWithFee = amount0.plus(amountB); + + const flashSpells = [ + { + connector: "AAVE-V3-IMPORT-PERMIT-X", + method: "importAave", + args: [ + wallet.address, + [[DAI], [USDC], false, [amountB.toFixed(0)]], + [[ethers.utils.hexlify(v)], [ethers.utils.hexlify(r)], [ethers.utils.hexlify(s)], [expiry]] + ] + }, + { + connector: "INSTAPOOL-C", + method: "flashPayback", + args: [USDC, amountWithFee.toFixed(0), 0, 0] + } + ]; + + const spells = [ + { + connector: "INSTAPOOL-C", + method: "flashBorrowAndCast", + args: [USDC, amount0.toString(), 5, encodeFlashcastData(flashSpells), "0x"] + } + ]; + const tx = await dsaWallet0.connect(wallet0).cast(...encodeSpells(spells), wallet.address); + const receipt = await tx.wait(); + }); + + it("Should check DSA AAVE position", async () => { + expect(await aDai.connect(wallet0).balanceOf(dsaWallet0.address)).to.be.gte( + new BigNumber(8).multipliedBy(1e18).toString() + ); + }); + }); +}); diff --git a/test/polygon/aave/v3-test.ts b/test/polygon/aave/v3-test.ts new file mode 100644 index 0000000..e76c77b --- /dev/null +++ b/test/polygon/aave/v3-test.ts @@ -0,0 +1,264 @@ +import { expect, should } from "chai"; +import hre, { ethers, waffle } from "hardhat"; +import type { Signer, Contract } from "ethers"; +import { BigNumber } from "bignumber.js"; +import { buildDSAv2 } from "../../../scripts/tests/buildDSAv2"; +import { addresses } from "../../../scripts/tests/polygon/addresses"; +import { deployAndEnableConnector } from "../../../scripts/tests/deployAndEnableConnector"; +import { abis } from "../../../scripts/constant/abis"; +import { getMasterSigner } from "../../../scripts/tests/getMasterSigner"; +import { parseEther, parseUnits } from "ethers/lib/utils"; +import { encodeSpells } from "../../../scripts/tests/encodeSpells"; +import { ConnectV2AaveV3Polygon__factory, IERC20__factory } from "../../../typechain"; + +const ABI = ["function balanceOf(address account) public view returns (uint256)"]; + +const aDaiAddress = "0x82E64f49Ed5EC1bC6e43DAD4FC8Af9bb3A2312EE"; +const aaveAddress = "0x69FA688f1Dc47d4B5d8029D5a35FB7a548310654"; +const account = "0xf04adbf75cdfc5ed26eea4bbbb991db002036bdd"; +const DAI = "0x8f3cf7ad23cd3cadbd9735aff958023239c6a063"; +const USDC = "0x2791bca1f2de4661ed88a30c99a7a9449aa84174"; +const mnemonic = "test test test test test test test test test test test junk"; +const connectorName = "AAVE-V3-X"; +let signer: any, wallet0: any; + +const aaveAbi = [ + { + inputs: [ + { internalType: "address", name: "asset", type: "address" }, + { internalType: "address", name: "user", type: "address" } + ], + name: "getUserReserveData", + outputs: [ + { internalType: "uint256", name: "currentATokenBalance", type: "uint256" }, + { internalType: "uint256", name: "currentStableDebt", type: "uint256" }, + { internalType: "uint256", name: "currentVariableDebt", type: "uint256" }, + { internalType: "uint256", name: "principalStableDebt", type: "uint256" }, + { internalType: "uint256", name: "scaledVariableDebt", type: "uint256" }, + { internalType: "uint256", name: "stableBorrowRate", type: "uint256" }, + { internalType: "uint256", name: "liquidityRate", type: "uint256" }, + { internalType: "uint40", name: "stableRateLastUpdated", type: "uint40" }, + { internalType: "bool", name: "usageAsCollateralEnabled", type: "bool" } + ], + stateMutability: "view", + type: "function" + } +]; + +const erc20Abi = [ + { + constant: false, + inputs: [ + { + name: "_spender", + type: "address" + }, + { + name: "_value", + type: "uint256" + } + ], + name: "approve", + outputs: [ + { + name: "", + type: "bool" + } + ], + payable: false, + stateMutability: "nonpayable", + type: "function" + }, + { + constant: true, + inputs: [], + name: "totalSupply", + outputs: [ + { + name: "", + type: "uint256" + } + ], + payable: false, + stateMutability: "view", + type: "function" + }, + { + constant: true, + inputs: [ + { + name: "_owner", + type: "address" + } + ], + name: "balanceOf", + outputs: [ + { + name: "balance", + type: "uint256" + } + ], + payable: false, + stateMutability: "view", + type: "function" + }, + { + constant: false, + inputs: [ + { + name: "_to", + type: "address" + }, + { + name: "_value", + type: "uint256" + } + ], + name: "transfer", + outputs: [ + { + name: "", + type: "bool" + } + ], + payable: false, + stateMutability: "nonpayable", + type: "function" + } +]; + +const token = new ethers.Contract(DAI, erc20Abi); +const aDai = new ethers.Contract(aDaiAddress, ABI); +const usdcToken = new ethers.Contract(USDC, erc20Abi); +const aave = new ethers.Contract(aaveAddress, aaveAbi); + +describe("Aave v3 Position for Polygon", function () { + let dsaWallet0: any; + let masterSigner: Signer; + let instaConnectorsV2: Contract; + let connector: any; + + const wallet = ethers.Wallet.fromMnemonic(mnemonic); + + before(async () => { + await hre.network.provider.request({ + method: "hardhat_reset", + params: [ + { + forking: { + //@ts-ignore + jsonRpcUrl: hre.config.networks.hardhat.forking.url, + blockNumber: 26652016 + } + } + ] + }); + masterSigner = await getMasterSigner(); + [wallet0] = await ethers.getSigners(); + await hre.network.provider.send("hardhat_setBalance", [account, ethers.utils.parseEther("10").toHexString()]); + + await hre.network.provider.request({ + method: "hardhat_impersonateAccount", + params: [account] + }); + + signer = await ethers.getSigner(account); + + await token.connect(signer).transfer(wallet0.address, ethers.utils.parseEther("10")); + + instaConnectorsV2 = await ethers.getContractAt(abis.core.connectorsV2, addresses.core.connectorsV2); + connector = await deployAndEnableConnector({ + connectorName, + contractArtifact: ConnectV2AaveV3Polygon__factory, + signer: masterSigner, + connectors: instaConnectorsV2 + }); + }); + + describe("Deployment", async () => { + it("Should set correct name", async () => { + expect(await connector.name()).to.eq("AaveV3-v1.2"); + }); + }); + + describe("DSA wallet setup", async () => { + it("Should build DSA v2", async () => { + dsaWallet0 = await buildDSAv2(wallet0.address); + expect(!!dsaWallet0.address).to.be.true; + }); + + it("Deposit ETH into DSA wallet", async function () { + await wallet0.sendTransaction({ + to: dsaWallet0.address, + value: ethers.utils.parseEther("5") + }); + + expect(await ethers.provider.getBalance(dsaWallet0.address)).to.be.gte(ethers.utils.parseEther("5")); + }); + }); + + describe("check user AAVE position", async () => { + it("Should create DSA Aave v3 position of DAI(collateral) and USDC(debt)", async () => { + await token.connect(signer).transfer(dsaWallet0.address, ethers.utils.parseEther("10")); + + const spells = [ + //deposit DAI in aave + { + connector: connectorName, + method: "deposit", + args: [DAI, parseEther("10"), 0, 0] + }, + //borrow USDC from aave + { + connector: connectorName, + method: "borrow", + args: [USDC, parseUnits("3", 6), 2, 0, 0] + } + ]; + const tx = await dsaWallet0.connect(wallet0).cast(...encodeSpells(spells), wallet0.address); + const receipt = await tx.wait(); + }); + + it("Should check position of dsa", async () => { + expect(await aDai.connect(wallet0).balanceOf(dsaWallet0.address)).to.be.gte( + new BigNumber(10).multipliedBy(1e18).toString() + ); + + expect(await usdcToken.connect(wallet0).balanceOf(dsaWallet0.address)).to.be.gte( + new BigNumber(3).multipliedBy(1e6).toString() + ); + + expect((await aave.connect(wallet0).getUserReserveData(USDC, dsaWallet0.address)).currentStableDebt).to.be.equal(0); + expect((await aave.connect(wallet0).getUserReserveData(USDC, dsaWallet0.address)).currentVariableDebt).to.be.gte( + new BigNumber(3).multipliedBy(1e6).toString() + ); + }); + + it("Should swap borrowRateMode", async () => { + const spells = [ + //deposit DAI in aave + { + connector: connectorName, + method: "swapBorrowRateMode", + args: [USDC, 2] + } + ]; + const tx = await dsaWallet0.connect(wallet0).cast(...encodeSpells(spells), wallet0.address); + const receipt = await tx.wait(); + }); + + it("Should check position of dsa", async () => { + expect(await aDai.connect(wallet0).balanceOf(dsaWallet0.address)).to.be.gte( + new BigNumber(10).multipliedBy(1e18).toString() + ); + + expect(await usdcToken.connect(wallet0).balanceOf(dsaWallet0.address)).to.be.gte( + new BigNumber(3).multipliedBy(1e6).toString() + ); + expect((await aave.connect(wallet0).getUserReserveData(USDC, dsaWallet0.address)).currentVariableDebt).to.be.equal(0); + expect((await aave.connect(wallet0).getUserReserveData(USDC, dsaWallet0.address)).currentStableDebt).to.be.gte( + new BigNumber(3).multipliedBy(1e6).toString() + ); + }); + }); +}); diff --git a/test/polygon/compound/compound.iii.rewards.test.ts b/test/polygon/compound/compound.iii.rewards.test.ts new file mode 100644 index 0000000..08e688b --- /dev/null +++ b/test/polygon/compound/compound.iii.rewards.test.ts @@ -0,0 +1,448 @@ +import { expect } from "chai"; +import hre from "hardhat"; +const { waffle, ethers } = hre; +const { provider, deployContract } = waffle; + +import { Signer, Contract } from "ethers"; +import { BigNumber } from "bignumber.js"; + +import { deployAndEnableConnector } from "../../../scripts/tests/deployAndEnableConnector"; +import { buildDSAv2 } from "../../../scripts/tests/buildDSAv2"; +import { encodeSpells } from "../../../scripts/tests/encodeSpells"; +import { getMasterSigner } from "../../../scripts/tests/getMasterSigner"; +import { addresses } from "../../../scripts/tests/polygon/addresses"; +import { tokens, tokenMapping } from "../../../scripts/tests/polygon/tokens"; +import { abis } from "../../../scripts/constant/abis"; +import { ConnectV2CompoundV3PolygonRewards__factory, ConnectV2CompoundV3Polygon__factory } from "../../../typechain"; + +describe("Compound III Rewards", function () { + let connectorName = "COMPOUND-V3-REWARDS-TEST-A"; + const market = "0xF25212E676D1F7F89Cd72fFEe66158f541246445"; + const rewards = "0x45939657d1CA34A8FA39A924B71D28Fe8431e581"; + const base = "0x2791Bca1f2de4661ED88A30C99A7a9449Aa84174"; + const account = "0xf827c3E5fD68e78aa092245D442398E12988901C"; + const wethWhale = "0xadbF1854e5883eB8aa7BAf50705338739e558E5b"; + const baseWhale = "0x72a53cdbbcc1b9efa39c834a540550e23463aacb"; + + const ABI = [ + "function balanceOf(address account) public view returns (uint256)", + "function approve(address spender, uint256 amount) external returns(bool)", + "function transfer(address recipient, uint256 amount) external returns (bool)" + ]; + const wethContract = new ethers.Contract(tokens.wmatic.address, ABI); + const baseContract = new ethers.Contract(base, ABI); + + const cometABI = [ + { + inputs: [ + { internalType: "address", name: "comet", type: "address" }, + { internalType: "address", name: "src", type: "address" }, + { internalType: "bool", name: "shouldAccrue", type: "bool" } + ], + name: "claim", + outputs: [], + stateMutability: "nonpayable", + type: "function" + }, + { + inputs: [ + { internalType: "address", name: "comet", type: "address" }, + { internalType: "address", name: "src", type: "address" }, + { internalType: "address", name: "to", type: "address" }, + { internalType: "bool", name: "shouldAccrue", type: "bool" } + ], + name: "claimTo", + outputs: [], + stateMutability: "nonpayable", + type: "function" + }, + { + inputs: [ + { internalType: "address", name: "comet", type: "address" }, + { internalType: "address", name: "account", type: "address" } + ], + name: "getRewardOwed", + outputs: [ + { + components: [ + { internalType: "address", name: "token", type: "address" }, + { internalType: "uint256", name: "owed", type: "uint256" } + ], + internalType: "struct CometRewards.RewardOwed", + name: "", + type: "tuple" + } + ], + stateMutability: "nonpayable", + type: "function" + }, + { + inputs: [{ internalType: "address", name: "", type: "address" }], + name: "rewardConfig", + outputs: [ + { internalType: "address", name: "token", type: "address" }, + { internalType: "uint64", name: "rescaleFactor", type: "uint64" }, + { internalType: "bool", name: "shouldUpscale", type: "bool" } + ], + stateMutability: "view", + type: "function" + }, + { + inputs: [ + { internalType: "address", name: "", type: "address" }, + { internalType: "address", name: "", type: "address" } + ], + name: "rewardsClaimed", + outputs: [{ internalType: "uint256", name: "", type: "uint256" }], + stateMutability: "view", + type: "function" + } + ]; + + const marketABI = [ + { + inputs: [{ internalType: "address", name: "account", type: "address" }], + name: "balanceOf", + outputs: [{ internalType: "uint256", name: "", type: "uint256" }], + stateMutability: "view", + type: "function" + }, + { + inputs: [{ internalType: "address", name: "account", type: "address" }], + name: "borrowBalanceOf", + outputs: [{ internalType: "uint256", name: "", type: "uint256" }], + stateMutability: "view", + type: "function" + }, + { + inputs: [], + name: "baseBorrowMin", + outputs: [{ internalType: "uint256", name: "", type: "uint256" }], + stateMutability: "view", + type: "function" + }, + { + inputs: [], + name: "baseMinForRewards", + outputs: [{ internalType: "uint256", name: "", type: "uint256" }], + stateMutability: "view", + type: "function" + }, + { + inputs: [], + name: "baseToken", + outputs: [{ internalType: "address", name: "", type: "address" }], + stateMutability: "view", + type: "function" + }, + { + inputs: [], + name: "decimals", + outputs: [{ internalType: "uint8", name: "", type: "uint8" }], + stateMutability: "view", + type: "function" + }, + { + inputs: [{ internalType: "address", name: "priceFeed", type: "address" }], + name: "getPrice", + outputs: [{ internalType: "uint256", name: "", type: "uint256" }], + stateMutability: "view", + type: "function" + }, + { + inputs: [ + { internalType: "address", name: "owner", type: "address" }, + { internalType: "address", name: "manager", type: "address" } + ], + name: "hasPermission", + outputs: [{ internalType: "bool", name: "", type: "bool" }], + stateMutability: "view", + type: "function" + }, + { + inputs: [], + name: "numAssets", + outputs: [{ internalType: "uint8", name: "", type: "uint8" }], + stateMutability: "view", + type: "function" + }, + { + inputs: [ + { internalType: "address", name: "asset", type: "address" }, + { internalType: "uint256", name: "baseAmount", type: "uint256" } + ], + name: "quoteCollateral", + outputs: [{ internalType: "uint256", name: "", type: "uint256" }], + stateMutability: "view", + type: "function" + }, + { + inputs: [{ internalType: "address", name: "", type: "address" }], + name: "userBasic", + outputs: [ + { internalType: "int104", name: "principal", type: "int104" }, + { internalType: "uint64", name: "baseTrackingIndex", type: "uint64" }, + { internalType: "uint64", name: "baseTrackingAccrued", type: "uint64" }, + { internalType: "uint16", name: "assetsIn", type: "uint16" }, + { internalType: "uint8", name: "_reserved", type: "uint8" } + ], + stateMutability: "view", + type: "function" + }, + { + inputs: [ + { internalType: "address", name: "", type: "address" }, + { internalType: "address", name: "", type: "address" } + ], + name: "userCollateral", + outputs: [ + { internalType: "uint128", name: "balance", type: "uint128" }, + { internalType: "uint128", name: "_reserved", type: "uint128" } + ], + stateMutability: "view", + type: "function" + } + ]; + + let dsaWallet0: any; + let dsaWallet1: any; + let wallet: any; + let dsa0Signer: any; + let masterSigner: Signer; + let instaConnectorsV2: Contract; + let connector: any; + let connectorMain: any; + let signer: any; + let wethSigner: any; + let usdcSigner: any; + + const cometReward = new ethers.Contract(rewards, cometABI); + const comet = new ethers.Contract(market, marketABI); + + const wallets = provider.getWallets(); + const [wallet0, wallet1, wallet2, wallet3] = wallets; + + before(async () => { + await hre.network.provider.request({ + method: "hardhat_reset", + params: [ + { + forking: { + //@ts-ignore + jsonRpcUrl: hre.config.networks.hardhat.forking.url, + // blockNumber: 15444500 + } + } + ] + }); + masterSigner = await getMasterSigner(); + await hre.network.provider.send("hardhat_setBalance", [ + await masterSigner.getAddress(), + "0x3635c9adc5de9fffff", + ]); + instaConnectorsV2 = await ethers.getContractAt(abis.core.connectorsV2, addresses.core.connectorsV2); + connector = await deployAndEnableConnector({ + connectorName, + contractArtifact: ConnectV2CompoundV3PolygonRewards__factory, + signer: masterSigner, + connectors: instaConnectorsV2 + }); + console.log("Connector address", connector.address); + + await hre.network.provider.send("hardhat_setBalance", [account, ethers.utils.parseEther("10").toHexString()]); + + await hre.network.provider.request({ + method: "hardhat_impersonateAccount", + params: [account] + }); + + signer = await ethers.getSigner(account); + + await hre.network.provider.request({ + method: "hardhat_impersonateAccount", + params: [wethWhale] + }); + wethSigner = await ethers.getSigner(wethWhale); + + await hre.network.provider.request({ + method: "hardhat_impersonateAccount", + params: [baseWhale] + }); + usdcSigner = await ethers.getSigner(baseWhale); + await hre.network.provider.send("hardhat_setBalance", [ + usdcSigner.address, + ethers.utils.parseEther("10").toHexString() + ]); + }); + + it("Should have contracts deployed.", async function () { + expect(!!instaConnectorsV2.address).to.be.true; + expect(!!connector.address).to.be.true; + expect(!!(await masterSigner.getAddress())).to.be.true; + }); + + describe("DSA wallet setup", function () { + it("Should build DSA v2", async function () { + dsaWallet0 = await buildDSAv2(wallet0.address); + expect(!!dsaWallet0.address).to.be.true; + dsaWallet1 = await buildDSAv2(wallet0.address); + expect(!!dsaWallet1.address).to.be.true; + wallet = await ethers.getSigner(dsaWallet0.address); + expect(!!dsaWallet1.address).to.be.true; + }); + + it("Deposit ETH into DSA wallet", async function () { + await hre.network.provider.request({ + method: "hardhat_impersonateAccount", + params: [wallet.address] + }); + + dsa0Signer = await ethers.getSigner(wallet.address); + await wallet0.sendTransaction({ + to: dsaWallet0.address, + value: ethers.utils.parseEther("10") + }); + expect(await ethers.provider.getBalance(dsaWallet0.address)).to.be.gte(ethers.utils.parseEther("10")); + await wallet0.sendTransaction({ + to: dsaWallet1.address, + value: ethers.utils.parseEther("10") + }); + expect(await ethers.provider.getBalance(dsaWallet1.address)).to.be.gte(ethers.utils.parseEther("10")); + }); + + it("should deposit USDC in dsa wallet", async function () { + await baseContract.connect(usdcSigner).transfer(dsaWallet0.address, ethers.utils.parseUnits("500", 6)); + + expect(await baseContract.connect(usdcSigner).balanceOf(dsaWallet0.address)).to.be.gte( + ethers.utils.parseUnits("500", 6) + ); + }); + }); + + describe("Main", function () { + //deposit asset + it("Should supply USDC in Compound V3", async function () { + connectorName = "COMPOUND-V3-TEST-A"; + connectorMain = await deployAndEnableConnector({ + connectorName, + contractArtifact: ConnectV2CompoundV3Polygon__factory, + signer: masterSigner, + connectors: instaConnectorsV2 + }); + const amount = ethers.utils.parseUnits("400", 6); + const spells = [ + { + connector: "COMPOUND-V3-TEST-A", + method: "deposit", + args: [market, base, amount, 0, 0] + } + ]; + + const tx = await dsaWallet0.connect(wallet0).cast(...encodeSpells(spells), wallet1.address); + const receipt = await tx.wait(); + expect(new BigNumber(await baseContract.connect(signer).balanceOf(dsaWallet0.address)).toFixed(0)).to.be.lte( + ethers.utils.parseUnits("100", 6) + ); + expect(new BigNumber(await comet.connect(signer).balanceOf(dsaWallet0.address)).toFixed(0)).to.be.gte( + ethers.utils.parseUnits("399", 6) + ); + }); + + let connector_ = "COMPOUND-V3-REWARDS-TEST-A"; + it("Should claim rewards", async function () { + let reward = (await cometReward.connect(signer).rewardConfig(market)).token; + let rewardInterface = new ethers.Contract(reward, ABI); + let owed_ = await cometReward.connect(signer).callStatic.getRewardOwed(market, dsaWallet0.address); + let amt: number = owed_.owed; + console.log(new BigNumber(amt).toFixed(0)); + const spells = [ + { + connector: connector_, + method: "claimRewards", + args: [market, 0] + } + ]; + + const tx = await dsaWallet0.connect(wallet0).cast(...encodeSpells(spells), wallet1.address); + const receipt = await tx.wait(); + expect(new BigNumber(await rewardInterface.connect(signer).balanceOf(dsaWallet0.address)).toFixed(0)).to.be.gte( + amt + ); + }); + + it("Should supply USDC in Compound V3 through dsaWallet0", async function () { + const amount = ethers.utils.parseUnits("100", 6); // 1 ETH + const spells = [ + { + connector: "COMPOUND-V3-TEST-A", + method: "deposit", + args: [market, base, amount, 0, 0] + } + ]; + + const tx = await dsaWallet0.connect(wallet0).cast(...encodeSpells(spells), wallet1.address); + const receipt = await tx.wait(); + expect(new BigNumber(await baseContract.connect(signer).balanceOf(dsaWallet0.address)).toFixed(0)).to.be.lte( + ethers.utils.parseUnits("0", 6) + ); + expect(new BigNumber(await comet.connect(signer).balanceOf(dsaWallet0.address)).toFixed(0)).to.be.gte( + ethers.utils.parseUnits("499", 6) + ); + }); + + it("Should claim rewards to dsa1", async function () { + let reward = (await cometReward.connect(signer).rewardConfig(market)).token; + let rewardInterface = new ethers.Contract(reward, ABI); + let owed_ = await cometReward.connect(signer).callStatic.getRewardOwed(market, dsaWallet0.address); + let amt: number = owed_.owed; + + const spells = [ + { + connector: connector_, + method: "claimRewardsOnBehalfOf", + args: [market, dsaWallet0.address, dsaWallet1.address, 0] + } + ]; + + const tx = await dsaWallet0.connect(wallet0).cast(...encodeSpells(spells), wallet1.address); + const receipt = await tx.wait(); + expect(new BigNumber(await rewardInterface.connect(signer).balanceOf(dsaWallet1.address)).toFixed(0)).to.be.gte( + amt + ); + }); + + it("should allow manager for dsaWallet0's collateral and base", async function () { + const spells = [ + { + connector: connectorName, + method: "toggleAccountManager", + args: [market, dsaWallet1.address, true] + } + ]; + + const tx = await dsaWallet0.connect(wallet0).cast(...encodeSpells(spells), wallet1.address); + const receipt = await tx.wait(); + }); + + it("Should claim rewards to dsa1 using manager", async function () { + let reward = (await cometReward.connect(signer).rewardConfig(market)).token; + let rewardInterface = new ethers.Contract(reward, ABI); + let owed_ = await cometReward.connect(signer).callStatic.getRewardOwed(market, dsaWallet0.address); + let amt: number = owed_.owed; + + const spells = [ + { + connector: connector_, + method: "claimRewardsOnBehalfOf", + args: [market, dsaWallet0.address, dsaWallet1.address, 0] + } + ]; + + const tx = await dsaWallet1.connect(wallet0).cast(...encodeSpells(spells), wallet1.address); + const receipt = await tx.wait(); + expect(new BigNumber(await rewardInterface.connect(signer).balanceOf(dsaWallet1.address)).toFixed(0)).to.be.gte( + amt + ); + }); + }); +}); diff --git a/test/polygon/compound/compound.iii.test.ts b/test/polygon/compound/compound.iii.test.ts new file mode 100644 index 0000000..ca490f6 --- /dev/null +++ b/test/polygon/compound/compound.iii.test.ts @@ -0,0 +1,661 @@ +import { expect } from "chai"; +import hre from "hardhat"; +const { waffle, ethers } = hre; +const { provider, deployContract } = waffle; + +import { Signer, Contract } from "ethers"; +import { BigNumber } from "bignumber.js"; + +import { deployAndEnableConnector } from "../../../scripts/tests/deployAndEnableConnector"; +import { buildDSAv2 } from "../../../scripts/tests/buildDSAv2"; +import { encodeSpells } from "../../../scripts/tests/encodeSpells"; +import { getMasterSigner } from "../../../scripts/tests/getMasterSigner"; +import { addresses } from "../../../scripts/tests/polygon/addresses"; +import { tokens, tokenMapping } from "../../../scripts/tests/polygon/tokens"; +import { abis } from "../../../scripts/constant/abis"; +import { constants } from "../../../scripts/constant/constant"; +import { ConnectV2CompoundV3Polygon__factory } from "../../../typechain"; +import { MaxUint256 } from "@uniswap/sdk-core"; + +describe("Compound III", async function () { + const connectorName = "COMPOUND-POLYGON-V3-TEST-A"; + const market = "0xF25212E676D1F7F89Cd72fFEe66158f541246445"; + const base = "0x2791Bca1f2de4661ED88A30C99A7a9449Aa84174"; + const account = "0xF25212E676D1F7F89Cd72fFEe66158f541246445"; + const wethWhale = "0x06959153B974D0D5fDfd87D561db6d8d4FA0bb0B"; + + const ABI = [ + "function balanceOf(address account) public view returns (uint256)", + "function approve(address spender, uint256 amount) external returns(bool)", + "function transfer(address recipient, uint256 amount) external returns (bool)" + ]; + let wmaticContract: any; + let baseContract: any; + + const cometABI = [ + { + inputs: [{ internalType: "address", name: "account", type: "address" }], + name: "balanceOf", + outputs: [{ internalType: "uint256", name: "", type: "uint256" }], + stateMutability: "view", + type: "function" + }, + { + inputs: [{ internalType: "address", name: "account", type: "address" }], + name: "borrowBalanceOf", + outputs: [{ internalType: "uint256", name: "", type: "uint256" }], + stateMutability: "view", + type: "function" + }, + { + inputs: [], + name: "baseBorrowMin", + outputs: [{ internalType: "uint256", name: "", type: "uint256" }], + stateMutability: "view", + type: "function" + }, + { + inputs: [], + name: "baseMinForRewards", + outputs: [{ internalType: "uint256", name: "", type: "uint256" }], + stateMutability: "view", + type: "function" + }, + { + inputs: [], + name: "baseToken", + outputs: [{ internalType: "address", name: "", type: "address" }], + stateMutability: "view", + type: "function" + }, + { + inputs: [], + name: "decimals", + outputs: [{ internalType: "uint8", name: "", type: "uint8" }], + stateMutability: "view", + type: "function" + }, + { + inputs: [{ internalType: "address", name: "priceFeed", type: "address" }], + name: "getPrice", + outputs: [{ internalType: "uint256", name: "", type: "uint256" }], + stateMutability: "view", + type: "function" + }, + { + inputs: [ + { internalType: "address", name: "owner", type: "address" }, + { internalType: "address", name: "manager", type: "address" } + ], + name: "hasPermission", + outputs: [{ internalType: "bool", name: "", type: "bool" }], + stateMutability: "view", + type: "function" + }, + { + inputs: [], + name: "numAssets", + outputs: [{ internalType: "uint8", name: "", type: "uint8" }], + stateMutability: "view", + type: "function" + }, + { + inputs: [ + { internalType: "address", name: "asset", type: "address" }, + { internalType: "uint256", name: "baseAmount", type: "uint256" } + ], + name: "quoteCollateral", + outputs: [{ internalType: "uint256", name: "", type: "uint256" }], + stateMutability: "view", + type: "function" + }, + { + inputs: [{ internalType: "address", name: "", type: "address" }], + name: "userBasic", + outputs: [ + { internalType: "int104", name: "principal", type: "int104" }, + { internalType: "uint64", name: "baseTrackingIndex", type: "uint64" }, + { internalType: "uint64", name: "baseTrackingAccrued", type: "uint64" }, + { internalType: "uint16", name: "assetsIn", type: "uint16" }, + { internalType: "uint8", name: "_reserved", type: "uint8" } + ], + stateMutability: "view", + type: "function" + }, + { + inputs: [ + { internalType: "address", name: "", type: "address" }, + { internalType: "address", name: "", type: "address" } + ], + name: "userCollateral", + outputs: [ + { internalType: "uint128", name: "balance", type: "uint128" }, + { internalType: "uint128", name: "_reserved", type: "uint128" } + ], + stateMutability: "view", + type: "function" + } + ]; + + let dsaWallet0: any; + let dsaWallet1: any; + let dsaWallet2: any; + let dsaWallet3: any; + let wallet: any; + let dsa0Signer: any; + let masterSigner: Signer; + let instaConnectorsV2: Contract; + let connector: any; + let signer: any; + let wethSigner: any; + + let comet: any; + + const wallets = provider.getWallets(); + const [wallet0, wallet1, wallet2, wallet3] = wallets; + + before(async () => { + await hre.network.provider.request({ + method: "hardhat_reset", + params: [ + { + forking: { + //@ts-ignore + jsonRpcUrl: hre.config.networks.hardhat.forking.url, + // blockNumber: 41860206 + } + } + ] + }); + masterSigner = await getMasterSigner(); + await hre.network.provider.send("hardhat_setBalance", [ + await masterSigner.getAddress(), + "0x3635c9adc5de9fffff", + ]); + + signer = await ethers.getSigner(account); + + instaConnectorsV2 = await ethers.getContractAt(abis.core.connectorsV2, addresses.core.connectorsV2); + connector = await deployAndEnableConnector({ + connectorName, + contractArtifact: ConnectV2CompoundV3Polygon__factory, + signer: masterSigner, + connectors: instaConnectorsV2 + }); + console.log("Connector address", connector.address); + + await hre.network.provider.send("hardhat_setBalance", [account, ethers.utils.parseEther("10").toHexString()]); + + await hre.network.provider.request({ + method: "hardhat_impersonateAccount", + params: [account] + }); + + signer = await ethers.getSigner(account); + + await hre.network.provider.request({ + method: "hardhat_impersonateAccount", + params: [wethWhale] + }); + wethSigner = await ethers.getSigner(wethWhale); + baseContract = new ethers.Contract(base, ABI, wethSigner) + wmaticContract = new ethers.Contract(tokens.wmatic.address, ABI, wethSigner) + comet = new ethers.Contract(market, cometABI, wethSigner) + }); + + it("Should have contracts deployed.", async function () { + expect(!!instaConnectorsV2.address).to.be.true; + expect(!!connector.address).to.be.true; + expect(!!(await masterSigner.getAddress())).to.be.true; + }); + + it("Should build DSA v2", async function () { + dsaWallet0 = await buildDSAv2(wallet0.address); + expect(!!dsaWallet0.address).to.be.true; + dsaWallet1 = await buildDSAv2(wallet0.address); + expect(!!dsaWallet1.address).to.be.true; + dsaWallet2 = await buildDSAv2(wallet0.address); + expect(!!dsaWallet2.address).to.be.true; + dsaWallet3 = await buildDSAv2(wallet0.address); + expect(!!dsaWallet3.address).to.be.true; + wallet = await ethers.getSigner(dsaWallet0.address); + expect(!!dsaWallet1.address).to.be.true; + }); + + describe("DSA wallet setup", function () { + it("Deposit Matic into DSA wallet", async function () { + await hre.network.provider.request({ + method: "hardhat_impersonateAccount", + params: [wallet.address] + }); + + dsa0Signer = await ethers.getSigner(wallet.address); + await wallet0.sendTransaction({ + to: dsaWallet0.address, + value: ethers.utils.parseEther("1000") + }); + expect(await ethers.provider.getBalance(dsaWallet0.address)).to.be.gte(ethers.utils.parseEther("1000")); + await wallet0.sendTransaction({ + to: dsaWallet1.address, + value: ethers.utils.parseEther("1000") + }); + expect(await ethers.provider.getBalance(dsaWallet0.address)).to.be.gte(ethers.utils.parseEther("1000")); + await wallet0.sendTransaction({ + to: dsaWallet3.address, + value: ethers.utils.parseEther("1000") + }); + expect(await ethers.provider.getBalance(dsaWallet0.address)).to.be.gte(ethers.utils.parseEther("1000")); + }); + }); + + describe("Main", function () { + //deposit asset + it("Should supply Matic collateral in Compound V3", async function () { + const amount = ethers.utils.parseEther("500"); // 5 Matic + const spells = [ + { + connector: connectorName, + method: "deposit", + args: [market, tokens.matic.address, amount, 0, 0] + } + ]; + + const tx = await dsaWallet0.connect(wallet0).cast(...encodeSpells(spells), wallet1.address); + const receipt = await tx.wait(); + expect(await ethers.provider.getBalance(dsaWallet0.address)).to.be.lte(ethers.utils.parseEther("500")); + expect((await comet.connect(signer).userCollateral(dsaWallet0.address, tokens.wmatic.address)).balance).to.be.gte( + ethers.utils.parseEther("500") + ); + }); + + //deposit asset on behalf of + it("Should supply Matic collateral on behalf of dsaWallet0 in Compound V3", async function () { + const amount = ethers.utils.parseEther("100"); // 100 Matic + const spells = [ + { + connector: connectorName, + method: "depositOnBehalf", + args: [market, tokens.matic.address, dsaWallet0.address, amount, 0, 0] + } + ]; + + const tx = await dsaWallet1.connect(wallet0).cast(...encodeSpells(spells), wallet1.address); + const receipt = await tx.wait(); + expect(await ethers.provider.getBalance(dsaWallet1.address)).to.be.lte(ethers.utils.parseEther("900")); + expect((await comet.connect(wallet0).userCollateral(dsaWallet0.address, tokens.wmatic.address)).balance).to.be.gte( + ethers.utils.parseEther("600") + ); + }); + + it("Should borrow and payback base token from Compound", async function () { + const amount = ethers.utils.parseUnits("150", 6); + const spells = [ + { + connector: connectorName, + method: "borrow", + args: [market, base, amount, 0, 0] + }, + { + connector: connectorName, + method: "payback", + args: [market, base, ethers.utils.parseUnits("50", 6), 0, 0] + } + ]; + + const tx = await dsaWallet0.connect(wallet0).cast(...encodeSpells(spells), wallet1.address); + const receipt = await tx.wait(); + expect(await comet.connect(wallet0).borrowBalanceOf(dsaWallet0.address)).to.be.equal( + ethers.utils.parseUnits("100", 6) + ); + expect(await baseContract.connect(wallet0).balanceOf(dsaWallet0.address)).to.be.equal( + ethers.utils.parseUnits("100", 6) + ); + }); + + it("should allow manager for dsaWallet0's collateral and base", async function () { + const spells = [ + { + connector: connectorName, + method: "toggleAccountManager", + args: [market, dsaWallet2.address, true] + } + ]; + + const tx = await dsaWallet0.connect(wallet0).cast(...encodeSpells(spells), wallet1.address); + const receipt = await tx.wait(); + }); + + it("should payback base token on Compound using manager", async function () { + await baseContract.connect(signer).transfer(dsaWallet0.address, ethers.utils.parseUnits("5", 6)); + + const amount = ethers.utils.parseUnits("102", 6); + await baseContract.connect(dsa0Signer).approve(market, amount); + + const spells = [ + { + connector: connectorName, + method: "paybackFromUsingManager", + args: [market, base, dsaWallet0.address, dsaWallet0.address, ethers.constants.MaxUint256, 0, 0] + } + ]; + + const tx = await dsaWallet2.connect(wallet0).cast(...encodeSpells(spells), wallet1.address); + const receipt = await tx.wait(); + expect(await comet.connect(signer).borrowBalanceOf(dsaWallet0.address)).to.be.equal( + ethers.utils.parseUnits("0", 6) + ); + }); + + it("Should borrow to another dsa from Compound", async function () { + const amount = ethers.utils.parseUnits("100", 6); + const spells = [ + { + connector: connectorName, + method: "borrowTo", + args: [market, base, dsaWallet1.address, amount, 0, 0] + } + ]; + + const tx = await dsaWallet0.connect(wallet0).cast(...encodeSpells(spells), wallet1.address); + const receipt = await tx.wait(); + expect(new BigNumber(await comet.connect(signer).borrowBalanceOf(dsaWallet0.address)).toFixed()).to.be.equal( + ethers.utils.parseUnits("100", 6) + ); + }); + + it("Should payback on behalf of from Compound", async function () { + const spells = [ + { + connector: connectorName, + method: "paybackOnBehalf", + args: [market, base, dsaWallet0.address, ethers.constants.MaxUint256, 0, 0] + } + ]; + + const tx = await dsaWallet1.connect(wallet0).cast(...encodeSpells(spells), wallet1.address); + const receipt = await tx.wait(); + expect(await comet.connect(signer).borrowBalanceOf(dsaWallet0.address)).to.be.equal( + ethers.utils.parseUnits("0", 6) + ); + }); + + it("should withdraw some Matic collateral", async function () { + let initialBal = await ethers.provider.getBalance(dsaWallet0.address); + const amount_ = ethers.utils.parseEther("200"); + const spells = [ + { + connector: connectorName, + method: "withdraw", + args: [market, tokens.matic.address, amount_, 0, 0] + } + ]; + + const tx = await dsaWallet0.connect(wallet0).cast(...encodeSpells(spells), wallet1.address); + const receipt = await tx.wait(); + expect((await comet.connect(signer).userCollateral(dsaWallet0.address, tokens.wmatic.address)).balance).to.be.gte( + ethers.utils.parseEther("400") + ); + expect(await ethers.provider.getBalance(dsaWallet0.address)).to.be.gte(initialBal.add(amount_).toString()); + }); + + it("manager should be able to withdraw collateral from the position and transfer", async function () { + await wallet1.sendTransaction({ + to: tokens.wmatic.address, + value: ethers.utils.parseEther("1000") + }); + const amount = ethers.constants.MaxUint256; + const spells = [ + { + connector: connectorName, + method: "withdrawOnBehalfAndTransfer", + args: [market, tokens.matic.address, dsaWallet0.address, dsaWallet1.address, amount, 0, 0] + } + ]; + + const tx = await dsaWallet2.connect(wallet0).cast(...encodeSpells(spells), wallet1.address); + const receipt = await tx.wait(); + expect((await comet.connect(signer).userCollateral(dsaWallet0.address, tokens.wmatic.address)).balance).to.be.gte( + ethers.utils.parseEther("0") + ); + expect(await wmaticContract.connect(wallet0).balanceOf(dsaWallet1.address)).to.be.gte(ethers.utils.parseEther("400")); + }); + + it("Should withdraw collateral to another DSA", async function () { + const spells1 = [ + { + connector: connectorName, + method: "deposit", + args: [market, tokens.matic.address, ethers.utils.parseEther("500"), 0, 0] + } + ]; + + const tx1 = await dsaWallet1.connect(wallet0).cast(...encodeSpells(spells1), wallet1.address); + let initialBal = await ethers.provider.getBalance(dsaWallet0.address); + + const amount = ethers.utils.parseEther("200"); + const spells = [ + { + connector: connectorName, + method: "withdrawTo", + args: [market, tokens.matic.address, dsaWallet0.address, amount, 0, 0] + } + ]; + + const tx = await dsaWallet1.connect(wallet0).cast(...encodeSpells(spells), wallet1.address); + const receipt = await tx.wait(); + expect(await wmaticContract.connect(wallet0).balanceOf(dsaWallet0.address)).to.be.gte(amount); + + expect((await comet.connect(signer).userCollateral(dsaWallet1.address, tokens.wmatic.address)).balance).to.be.gte( + ethers.utils.parseEther("300") + ); + }); + + it("Should withdraw collateral to another DSA", async function () { + const spells1 = [ + { + connector: connectorName, + method: "deposit", + args: [market, tokens.matic.address, ethers.utils.parseEther("300"), 0, 0] + } + ]; + + const tx1 = await dsaWallet0.connect(wallet0).cast(...encodeSpells(spells1), wallet1.address); + let initialBal = await ethers.provider.getBalance(dsaWallet0.address); + + const amount = ethers.utils.parseEther("200"); + const spells = [ + { + connector: connectorName, + method: "withdrawTo", + args: [market, tokens.matic.address, dsaWallet0.address, amount, 0, 0] + } + ]; + + const tx = await dsaWallet0.connect(wallet0).cast(...encodeSpells(spells), wallet1.address); + const receipt = await tx.wait(); + expect(await ethers.provider.getBalance(dsaWallet0.address)).to.be.gte(initialBal.add(amount)); + + expect((await comet.connect(signer).userCollateral(dsaWallet1.address, tokens.wmatic.address)).balance).to.be.gte( + ethers.utils.parseEther("100") + ); + }); + + it("should transfer matic from dsaWallet1 to dsaWallet0 position", async function () { + const spells = [ + { + connector: connectorName, + method: "transferAsset", + args: [market, tokens.matic.address, dsaWallet0.address, ethers.utils.parseEther("300"), 0, 0] + } + ]; + + const tx = await dsaWallet1.connect(wallet0).cast(...encodeSpells(spells), wallet1.address); + const receipt = await tx.wait(); + expect((await comet.connect(signer).userCollateral(dsaWallet1.address, tokens.wmatic.address)).balance).to.be.gte( + ethers.utils.parseEther("0") + ); + expect((await comet.connect(signer).userCollateral(dsaWallet0.address, tokens.wmatic.address)).balance).to.be.gte( + ethers.utils.parseEther("300") + ); + }); + + it("should transfer base token from dsaWallet1 to dsaWallet0 position", async function () { + await baseContract.connect(wethSigner).transfer(dsaWallet1.address, ethers.utils.parseUnits("1000", 6)); + + const spells = [ + { + connector: connectorName, + method: "deposit", + args: [market, base, ethers.constants.MaxUint256, 0, 0] + } + ]; + const tx = await dsaWallet1.connect(wallet0).cast(...encodeSpells(spells), wallet1.address); + const receipt = await tx.wait(); + let initialBal = await baseContract.connect(signer).balanceOf(dsaWallet1.address); + let spells1 = [ + { + connector: connectorName, + method: "transferAsset", + args: [market, base, dsaWallet0.address, ethers.constants.MaxUint256, 0, 0] + } + ]; + + const tx1 = await dsaWallet1.connect(wallet0).cast(...encodeSpells(spells1), wallet1.address); + const receipt1 = await tx.wait(); + expect(await comet.connect(signer).balanceOf(dsaWallet1.address)).to.be.lte(ethers.utils.parseUnits("0", 6)); + expect(await comet.connect(signer).balanceOf(dsaWallet0.address)).to.be.gte(initialBal); + }); + + it("should transfer base token using manager from dsaWallet0 to dsaWallet1 position", async function () { + const spells = [ + { + connector: connectorName, + method: "transferAssetOnBehalf", + args: [market, base, dsaWallet0.address, dsaWallet1.address, ethers.constants.MaxUint256, 0, 0] + } + ]; + let initialBal = await baseContract.connect(signer).balanceOf(dsaWallet0.address); + + const tx = await dsaWallet2.connect(wallet0).cast(...encodeSpells(spells), wallet1.address); + const receipt = await tx.wait(); + expect(await comet.connect(signer).balanceOf(dsaWallet0.address)).to.be.lte(ethers.utils.parseUnits("0", 6)); + expect(await comet.connect(signer).balanceOf(dsaWallet1.address)).to.be.gte(initialBal); + }); + + it("should deposit weth using manager", async function () { + await wmaticContract.connect(signer).transfer(dsaWallet0.address, ethers.utils.parseEther("1000")); + let initialBal = await wmaticContract.connect(wallet0).balanceOf(dsaWallet0.address); + + const amount = ethers.utils.parseEther("100"); + await wmaticContract.connect(dsa0Signer).approve(market, amount); + + const spells = [ + { + connector: connectorName, + method: "depositFromUsingManager", + args: [market, tokens.matic.address, dsaWallet0.address, dsaWallet1.address, amount, 0, 0] + } + ]; + + const tx = await dsaWallet2.connect(wallet0).cast(...encodeSpells(spells), wallet1.address); + const receipt = await tx.wait(); + expect((await comet.connect(signer).userCollateral(dsaWallet1.address, tokens.wmatic.address)).balance).to.be.gte( + ethers.utils.parseEther("100") + ); + expect(await wmaticContract.connect(wallet0).balanceOf(dsaWallet0.address)).to.be.lte(initialBal.sub(amount)); + }); + + it("should allow manager for dsaWallet0's collateral", async function () { + const spells = [ + { + connector: connectorName, + method: "toggleAccountManager", + args: [market, dsaWallet2.address, true] + } + ]; + + const tx = await dsaWallet3.connect(wallet0).cast(...encodeSpells(spells), wallet1.address); + const receipt = await tx.wait(); + }); + it("should borrow on behalf using manager", async function () { + let initialBal = await baseContract.connect(wallet0).balanceOf(dsaWallet0.address); + await wallet0.sendTransaction({ + to: dsaWallet3.address, + value: ethers.utils.parseEther("1500") + }); + const spells1 = [ + { + connector: connectorName, + method: "deposit", + args: [market, tokens.matic.address, ethers.utils.parseEther("1500"), 0, 0] + } + ]; + const tx1 = await dsaWallet3.connect(wallet0).cast(...encodeSpells(spells1), wallet1.address); + const amount = ethers.utils.parseUnits("500", 6); + const spells = [ + { + connector: connectorName, + method: "borrowOnBehalfAndTransfer", + args: [market, base, dsaWallet3.address, dsaWallet0.address, amount, 0, 0] + } + ]; + + const tx = await dsaWallet2.connect(wallet0).cast(...encodeSpells(spells), wallet1.address); + const receipt = await tx.wait(); + expect(new BigNumber(await comet.connect(signer).borrowBalanceOf(dsaWallet3.address)).toFixed()).to.be.equal( + ethers.utils.parseUnits("500", 6) + ); + expect(await baseContract.connect(wallet0).balanceOf(dsaWallet0.address)).to.be.equal(initialBal.add(amount)); + }); + + it("should transferAsset collateral using manager", async function () { + let bal1 = (await comet.connect(signer).userCollateral(dsaWallet1.address, tokens.wmatic.address)).balance; + let bal0 = (await comet.connect(signer).userCollateral(dsaWallet0.address, tokens.wmatic.address)).balance; + const spells = [ + { + connector: connectorName, + method: "transferAssetOnBehalf", + args: [market, tokens.matic.address, dsaWallet0.address, dsaWallet1.address, ethers.utils.parseEther("100"), 0, 0] + } + ]; + + const tx = await dsaWallet2.connect(wallet0).cast(...encodeSpells(spells), wallet1.address); + const receipt = await tx.wait(); + expect((await comet.connect(signer).userCollateral(dsaWallet1.address, tokens.wmatic.address)).balance).to.be.gte( + bal1.add(ethers.utils.parseEther("100")).toString() + ); + expect((await comet.connect(signer).userCollateral(dsaWallet0.address, tokens.wmatic.address)).balance).to.be.gte( + bal0.sub(ethers.utils.parseEther("100")).toString() + ); + }); + + //can buy only when target reserves not reached. + + // it("should buy collateral", async function () { + // //deposit 10 usdc(base token) to dsa + // await baseContract.connect(signer).transfer(dsaWallet0.address, ethers.utils.parseUnits("10", 6)); + // console.log(await baseContract.connect(signer).balanceOf(dsaWallet0.address)); + + // //dsawallet0 --> collateral 0eth, balance 9eth 10usdc + // //dsaWallet1 --> balance 2eth coll: 3eth + // const amount = ethers.utils.parseUnits("1",6); + // const bal = await baseContract.connect(signer).balanceOf(dsaWallet0.address); + // const spells = [ + // { + // connector: connectorName, + // method: "buyCollateral", + // args: [market, tokens.link.address, dsaWallet0.address, amount, bal, 0, 0] + // } + // ]; + + // const tx = await dsaWallet0.connect(wallet0).cast(...encodeSpells(spells), wallet1.address); + // const receipt = await tx.wait(); + // expect(new BigNumber(await linkContract.connect(signer).balanceOf(dsaWallet0.address)).toFixed()).to.be.gte( + // ethers.utils.parseEther("100") + // ); + + // //dsawallet0 --> collateral 0eth, balance 9eth >1link + // //dsaWallet1 --> balance 2eth coll: 3eth + // }); + }); +}); diff --git a/test/polygon/connext/connext.test.ts b/test/polygon/connext/connext.test.ts new file mode 100644 index 0000000..ce0985e --- /dev/null +++ b/test/polygon/connext/connext.test.ts @@ -0,0 +1,123 @@ +import { expect } from "chai"; +import hre from "hardhat"; +const { ethers, waffle } = hre; +const { provider } = waffle; + +import { deployAndEnableConnector } from "../../../scripts/tests/deployAndEnableConnector"; +import { buildDSAv2 } from "../../../scripts/tests/buildDSAv2"; +import { encodeSpells } from "../../../scripts/tests/encodeSpells"; +import { getMasterSigner } from "../../../scripts/tests/getMasterSigner"; +import { addresses } from "../../../scripts/tests/polygon/addresses"; +import { abis } from "../../../scripts/constant/abis"; +import { ConnectV2ConnextPolygon__factory } from "../../../typechain"; +import { Signer, Contract } from "ethers"; + + +describe("Connext Connector [Polygon]", () => { + const connectorName = "CONNEXT-TEST-A"; + + let dsaWallet0: Contract; + let masterSigner: Signer; + let instaConnectorsV2: Contract; + let connector: Contract; + let usdcContract: Contract; + let signer: any; + + const usdcAddr = "0x2791Bca1f2de4661ED88A30C99A7a9449Aa84174"; + const account = "0xfa0b641678f5115ad8a8de5752016bd1359681b9"; + + const wallets = provider.getWallets(); + const [wallet0, wallet1] = wallets; + + before(async () => { + await hre.network.provider.request({ + method: "hardhat_reset", + params: [ + { + forking: { + // @ts-ignore + jsonRpcUrl: hre.config.networks.hardhat.forking.url, + blockNumber: 82686991 + } + } + ] + }); + + masterSigner = await getMasterSigner(); + instaConnectorsV2 = await ethers.getContractAt(abis.core.connectorsV2, addresses.core.connectorsV2); + connector = await deployAndEnableConnector({ + connectorName, + contractArtifact: ConnectV2ConnextPolygon__factory, + signer: masterSigner, + connectors: instaConnectorsV2 + }); + usdcContract = await ethers.getContractAt(abis.basic.erc20, usdcAddr); + signer = await ethers.getSigner(account); + + await hre.network.provider.send("hardhat_setBalance", [account, ethers.utils.parseEther("10").toHexString()]); + + await hre.network.provider.request({ + method: "hardhat_impersonateAccount", + params: [account] + }); + + await usdcContract.connect(signer).transfer(wallet0.address, ethers.utils.parseUnits("10000", 6)); + console.log("deployed connector: ", connector.address); + }); + + it("Should have contracts deployed.", async () => { + expect(!!instaConnectorsV2.address).to.be.true; + expect(!!connector.address).to.be.true; + expect(!!(await masterSigner.getAddress())).to.be.true; + }); + + describe("DSA wallet setup", () => { + it("Should build DSA v2", async () => { + dsaWallet0 = await buildDSAv2(wallet0.getAddress()); + expect(!!dsaWallet0.address).to.be.true; + }); + + it("Deposit ETH & USDC into DSA wallet", async () => { + await wallet0.sendTransaction({ + to: dsaWallet0.address, + value: ethers.utils.parseEther("10") + }); + expect(await ethers.provider.getBalance(dsaWallet0.address)).to.be.gte(ethers.utils.parseEther("10")); + + await usdcContract.connect(wallet0).transfer(dsaWallet0.address, ethers.utils.parseUnits("10", 6)); + expect(await usdcContract.balanceOf(dsaWallet0.address)).to.be.gte(ethers.utils.parseUnits("10", 6)); + }); + }); + + describe("Main", () => { + it("should xcall with usdc", async () => { + const amount = ethers.utils.parseUnits("5", 6); + const domainId = 6648936; + const slippage = 10000; + const relayerFee = ethers.utils.parseEther("1"); + const callData = "0x"; + + const xcallParams: any = [ + domainId, + wallet1.address, + usdcAddr, + wallet1.address, + amount, + slippage, + relayerFee, + callData + ]; + + const spells = [ + { + connector: connectorName, + method: "xcall", + args: [xcallParams, 0, 0] + } + ]; + + const tx = await dsaWallet0.connect(wallet0).cast(...encodeSpells(spells), wallet1.address); + const receipt = await tx.wait(); + }); + }); +}); diff --git a/test/polygon/dsa-spell/dsa-spell.test.ts b/test/polygon/dsa-spell/dsa-spell.test.ts new file mode 100644 index 0000000..11be803 --- /dev/null +++ b/test/polygon/dsa-spell/dsa-spell.test.ts @@ -0,0 +1,185 @@ +import hre from "hardhat"; +import axios from "axios"; +import { expect } from "chai"; +const { ethers } = hre; //check +import { BigNumber } from "bignumber.js"; +import { deployAndEnableConnector } from "../../../scripts/tests/deployAndEnableConnector"; +import { buildDSAv2 } from "../../../scripts/tests/buildDSAv2"; +import { encodeSpells } from "../../../scripts/tests/encodeSpells"; +import { getMasterSigner } from "../../../scripts/tests/getMasterSigner"; +import { addresses } from "../../../scripts/tests/polygon/addresses"; +import { abis } from "../../../scripts/constant/abis"; +import { ConnectV2DSASpellPolygon__factory } from "../../../typechain"; +import er20abi from "../../../scripts/constant/abi/basics/erc20.json"; +import type { Signer, Contract } from "ethers"; + +describe("DSA Spell", function () { + const connectorName = "dsa-spell-test"; + + let dsaWallet0: any; + let dsaWallet1: any; + let dsaWallet2: any; + let walletB: any; + let wallet0: any; + let walletBsigner: any; + let masterSigner: Signer; + let instaConnectorsV2: Contract; + let connector: any; + + before(async () => { + await hre.network.provider.request({ + method: "hardhat_reset", + params: [ + { + forking: { + // @ts-ignore + jsonRpcUrl: hre.config.networks.hardhat.forking.url + } + } + ] + }); + [wallet0] = await ethers.getSigners(); + + masterSigner = await getMasterSigner(); + instaConnectorsV2 = await ethers.getContractAt(abis.core.connectorsV2, addresses.core.connectorsV2); + connector = await deployAndEnableConnector({ + connectorName, + contractArtifact: ConnectV2DSASpellPolygon__factory, + signer: masterSigner, + connectors: instaConnectorsV2 + }); + console.log("\tConnector address", connector.address); + }); + + it("Should have contracts deployed.", async function () { + expect(!!instaConnectorsV2.address).to.be.true; + expect(!!connector.address).to.be.true; + expect(!!(await masterSigner.getAddress())).to.be.true; + }); + + describe("DSA wallet setup", function () { + it("Should build DSA v2", async function () { + dsaWallet0 = await buildDSAv2(wallet0.address); + expect(!!dsaWallet0.address).to.be.true; + walletB = await ethers.getSigner(dsaWallet0.address); + dsaWallet1 = await buildDSAv2(dsaWallet0.address); + expect(!!dsaWallet1.address).to.be.true; + console.log(`\t${dsaWallet1.address}`); + dsaWallet2 = await buildDSAv2(wallet0.address); + expect(!!dsaWallet2.address).to.be.true; + }); + + it("Deposit matic into DSA wallet 0", async function () { + await wallet0.sendTransaction({ + to: dsaWallet0.address, + value: ethers.utils.parseEther("10") + }); + + expect(await ethers.provider.getBalance(dsaWallet0.address)).to.be.gte(ethers.utils.parseEther("10")); + }); + + it("Deposit matic into DSA wallet 1", async function () { + await wallet0.sendTransaction({ + to: dsaWallet1.address, + value: ethers.utils.parseEther("10") + }); + + expect(await ethers.provider.getBalance(dsaWallet1.address)).to.be.gte(ethers.utils.parseEther("10")); + }); + }); + + describe("Main", function () { + let MATIC = "0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE"; + let USDC = "0x2791Bca1f2de4661ED88A30C99A7a9449Aa84174"; + let aMATIC = "0x6d80113e533a2C0fe82EaBD35f1875DcEA89Ea97"; + let usdc = new ethers.Contract(USDC, abis.basic.erc20); + let aMatic = new ethers.Contract(aMATIC, abis.basic.aToken); + var abi = [ + "function withdraw(address,uint256,address,uint256,uint256)", + "function deposit(address,uint256,uint256,uint256)", + "function borrow(address,uint256,uint256,uint256,uint256)" + ]; + function getCallData(spell: string, params: any) { + var iface = new ethers.utils.Interface(abi); + let data = iface.encodeFunctionData(spell, params); + return ethers.utils.hexlify(data); + } + + it("should cast spells", async function () { + async function getArg(connectors: any, spells: any, params: any) { + let datas = []; + for (let i = 0; i < connectors.length; i++) { + datas.push(getCallData(spells[i], params[i])); + } + return [dsaWallet1.address, connectors, datas]; + } + + let connectors = ["BASIC-A", "AAVE-V3-A", "AAVE-V3-A"]; + let methods = ["withdraw", "deposit", "borrow"]; + let params = [ + [MATIC, ethers.utils.parseEther("2"), dsaWallet0.address, 0, 0], + [MATIC, ethers.constants.MaxUint256, 0, 0], + [USDC, ethers.utils.parseUnits("1", 6), 2, 0, 0] + ]; + let arg = await getArg(connectors, methods, params); + + const spells = [ + { + connector: connectorName, + method: "castOnDSA", + args: arg + } + ]; + const tx = await dsaWallet0.connect(wallet0).cast(...encodeSpells(spells), await wallet0.getAddress()); + const receipt = await tx.wait(); + }); + + it("should check balances after cast on DSA", async function () { + expect(await ethers.provider.getBalance(dsaWallet1.address)).to.be.lte(0); + expect(await usdc.connect(wallet0).balanceOf(dsaWallet1.address)).to.be.gte( + new BigNumber(1).multipliedBy(1e6).toString() + ); + expect(await ethers.provider.getBalance(dsaWallet0.address)).to.be.gte( + new BigNumber(12).multipliedBy(1e18).toString() + ); + }); + + it("should cast spell on the first successful", async function () { + async function getArg(connectors: any, spells: any, params: any) { + let datas = []; + for (let i = 0; i < connectors.length; i++) { + datas.push(getCallData(spells[i], params[i])); + } + return [connectors, datas]; + } + + let connectors = ["AAVE-V3-A", "AAVE-V2-A"]; + let methods = ["deposit","deposit"]; + let params = [ + [MATIC, ethers.utils.parseEther("10"), 0, 0], + [MATIC, ethers.utils.parseEther("10"), 0, 0] + ]; + let arg = await getArg(connectors, methods, params); + const spells = [ + { + connector: connectorName, + method: "castAny", + args: arg + } + ]; + const tx = await dsaWallet0 + .connect(wallet0) + .cast(...encodeSpells(spells), await wallet0.getAddress()); + const receipt = await tx.wait(); + }); + + it("should check balances after spells on DSA", async function () { + expect(await ethers.provider.getBalance(dsaWallet0.address)).to.be.lte( + new BigNumber(2).multipliedBy(1e18).toString() + ); + expect(await aMatic.connect(wallet0).balanceOf(dsaWallet0.address)).to.be.gte( + new BigNumber(10).multipliedBy(1e18).toString() + ); + }); + }); +}); diff --git a/test/polygon/hop/hop.test.ts b/test/polygon/hop/hop.test.ts new file mode 100644 index 0000000..c7fc329 --- /dev/null +++ b/test/polygon/hop/hop.test.ts @@ -0,0 +1,240 @@ +import { expect } from "chai"; +import hre from "hardhat"; +const { waffle, ethers } = hre; +const { provider } = waffle; + +import { deployAndEnableConnector } from "../../../scripts/tests/deployAndEnableConnector"; +import { buildDSAv2 } from "../../../scripts/tests/buildDSAv2"; +import { encodeSpells } from "../../../scripts/tests/encodeSpells"; +import { getMasterSigner } from "../../../scripts/tests/getMasterSigner"; +import { addLiquidity } from "../../../scripts/tests/addLiquidity"; +import { addresses } from "../../../scripts/tests/polygon/addresses"; +import { abis } from "../../../scripts/constant/abis"; +import { ConnectV2HopPolygon__factory } from "../../../typechain"; +import { Signer, Contract } from "ethers"; +import BigNumber from "bignumber.js"; + +describe("Hop Connector", function () { + const connectorName = "HOP-A"; + + let dsaWallet0: Contract; + let masterSigner: Signer; + let instaConnectorsV2: Contract; + let connector: Contract; + let latestBlock: any; + + const wallets = provider.getWallets(); + const [wallet0, wallet1, wallet2, wallet3] = wallets; + + const saddleSwapABI = [ + { + inputs: [{ internalType: "address", name: "tokenAddress", type: "address" }], + name: "getTokenIndex", + outputs: [{ internalType: "uint8", name: "", type: "uint8" }], + stateMutability: "view", + type: "function" + }, + { + inputs: [ + { internalType: "uint8", name: "tokenIndexFrom", type: "uint8" }, + { internalType: "uint8", name: "tokenIndexTo", type: "uint8" }, + { internalType: "uint256", name: "dx", type: "uint256" } + ], + name: "calculateSwap", + outputs: [{ internalType: "uint256", name: "", type: "uint256" }], + stateMutability: "view", + type: "function" + } + ]; + + const l2BridgeABI = [ + { + anonymous: false, + inputs: [ + { indexed: true, internalType: "bytes32", name: "transferId", type: "bytes32" }, + { indexed: true, internalType: "uint256", name: "chainId", type: "uint256" }, + { indexed: true, internalType: "address", name: "recipient", type: "address" }, + { indexed: false, internalType: "uint256", name: "amount", type: "uint256" }, + { indexed: false, internalType: "bytes32", name: "transferNonce", type: "bytes32" }, + { indexed: false, internalType: "uint256", name: "bonderFee", type: "uint256" }, + { indexed: false, internalType: "uint256", name: "index", type: "uint256" }, + { indexed: false, internalType: "uint256", name: "amountOutMin", type: "uint256" }, + { indexed: false, internalType: "uint256", name: "deadline", type: "uint256" } + ], + name: "TransferSent", + type: "event" + } + ]; + + const DAI_ADDR = "0x8f3cf7ad23cd3cadbd9735aff958023239c6a063"; + const hDai = "0xb8901acB165ed027E32754E0FFe830802919727f"; + const l2AmmWrapper = "0x28529fec439cfF6d7D1D5917e956dEE62Cd3BE5c"; + const l2Bridge = "0xEcf268Be00308980B5b3fcd0975D47C4C8e1382a"; + const saddleSwap = "0x25FB92E505F752F730cAD0Bd4fa17ecE4A384266"; + const saddleSwapInstance = new ethers.Contract(saddleSwap, saddleSwapABI); + + const token = new ethers.Contract(DAI_ADDR, abis.basic.erc20); + const l2BridgeInstance = new ethers.Contract(l2Bridge, l2BridgeABI); + + before(async () => { + await hre.network.provider.request({ + method: "hardhat_reset", + params: [ + { + forking: { + // @ts-ignore + jsonRpcUrl: hre.config.networks.hardhat.forking.url, + blockNumber: 27054896 + } + } + ] + }); + + masterSigner = await getMasterSigner(); + instaConnectorsV2 = await ethers.getContractAt(abis.core.connectorsV2, addresses.core.connectorsV2); + connector = await deployAndEnableConnector({ + connectorName, + contractArtifact: ConnectV2HopPolygon__factory, + signer: masterSigner, + connectors: instaConnectorsV2 + }); + console.log("Connector address", connector.address); + }); + + it("Should have contracts deployed.", async function () { + expect(!!instaConnectorsV2.address).to.be.true; + expect(!!connector.address).to.be.true; + expect(!!(await masterSigner.getAddress())).to.be.true; + }); + + describe("DSA wallet setup", function () { + it("Should build DSA v2", async function () { + dsaWallet0 = await buildDSAv2(wallet0.address); + expect(!!dsaWallet0.address).to.be.true; + }); + + it("Deposit MATIC & DAI into DSA wallet", async function () { + await wallet0.sendTransaction({ + to: dsaWallet0.address, + value: ethers.utils.parseEther("10") + }); + expect(await ethers.provider.getBalance(dsaWallet0.address)).to.be.gte(ethers.utils.parseEther("10")); + + await addLiquidity("dai", dsaWallet0.address, ethers.utils.parseEther("10000")); + }); + }); + + describe("Main", function () { + it("should migrate from L2 to L1", async function () { + const amount = ethers.utils.parseEther("10"); + const bonderFee = new BigNumber(100 * 1e9) + .multipliedBy(150000) + .multipliedBy(1.5) + .plus(new BigNumber(amount.toString()).multipliedBy(0.18)); + const deadline = Date.now() + 604800; + const getId = "0"; + + await token.connect(wallet0).approve(l2AmmWrapper, amount.toString()); + + const hDaiIdx = await saddleSwapInstance.connect(wallet0).getTokenIndex(hDai); + const daiIdx = await saddleSwapInstance.connect(wallet0).getTokenIndex(DAI_ADDR); + + let amountOutMin = await saddleSwapInstance.connect(wallet0).calculateSwap(daiIdx, hDaiIdx, amount.toString()); + amountOutMin = new BigNumber(amountOutMin.toString()).multipliedBy(0.99); + + const params: any = [ + DAI_ADDR, + l2AmmWrapper, + wallet0.address, + 1, + amount.toString(), + bonderFee.toString(), + amountOutMin.toFixed(0), + deadline, + "0", + "0" + ]; + + const spells = [ + { + connector: connectorName, + method: "bridge", + args: [params, getId] + } + ]; + + const tx = await dsaWallet0.connect(wallet0).cast(...encodeSpells(spells), wallet1.address); + let receipt = await tx.wait(); + latestBlock = receipt.blockNumber; + }); + + it("Should fetch transferId from TransferSent Event", async function () { + const filter = l2BridgeInstance.connect(wallet0).filters.TransferSent(); + const events = await l2BridgeInstance.connect(wallet0).queryFilter(filter, 27054896, latestBlock); + + const transferSentEvent: any = events[0].args; + + expect(transferSentEvent[2].toLowerCase()).to.be.equals(wallet0.address.toLowerCase()); + console.log("TransferId", transferSentEvent[0]); + }); + + it("should migrate from L2 to L2", async function () { + const amount = ethers.utils.parseEther("10"); + + const hDaiIdx = await saddleSwapInstance.connect(wallet0).getTokenIndex(hDai); + const daiIdx = await saddleSwapInstance.connect(wallet0).getTokenIndex(DAI_ADDR); + + let destinationAmountOutMin = await saddleSwapInstance + .connect(wallet0) + .calculateSwap(hDaiIdx, daiIdx, amount.toString()); + destinationAmountOutMin = new BigNumber(destinationAmountOutMin.toString()).multipliedBy(0.99); + + let amountOutMin = await saddleSwapInstance.connect(wallet0).calculateSwap(daiIdx, hDaiIdx, amount.toString()); + amountOutMin = new BigNumber(amountOutMin.toString()).multipliedBy(0.99); + + const bonderFee = new BigNumber(1e9) + .multipliedBy(150000) + .multipliedBy(1.5) + .plus(new BigNumber(amount.toString()).multipliedBy(0.18)); + const deadline = Date.now() + 604800; + const getId = "0"; + + await token.connect(wallet0).approve(l2AmmWrapper, amount.toString()); + + const params: any = [ + DAI_ADDR, + l2AmmWrapper, + wallet0.address, + 10, + amount.toString(), + bonderFee.toString(), + amountOutMin.toFixed(0), + deadline, + destinationAmountOutMin.toFixed(0), + deadline + ]; + + const spells = [ + { + connector: connectorName, + method: "bridge", + args: [params, getId] + } + ]; + + const tx = await dsaWallet0.connect(wallet0).cast(...encodeSpells(spells), wallet1.address); + let receipt = await tx.wait(); + latestBlock = receipt.blockNumber; + }); + + it("Should fetch transferId from TransferSent Event", async function () { + const filter = l2BridgeInstance.connect(wallet0).filters.TransferSent(); + const events = await l2BridgeInstance.connect(wallet0).queryFilter(filter, 27054896, latestBlock); + + const transferSentEvent: any = events[1].args; + + expect(transferSentEvent[2].toLowerCase()).to.be.equals(wallet0.address.toLowerCase()); + console.log("TransferId", transferSentEvent[0]); + }); + }); +}); diff --git a/test/polygon/mstable/mstable.helpers.ts b/test/polygon/mstable/mstable.helpers.ts new file mode 100644 index 0000000..f717efb --- /dev/null +++ b/test/polygon/mstable/mstable.helpers.ts @@ -0,0 +1,159 @@ +import hre, { ethers } from "hardhat"; +import { IERC20Minimal__factory } from "../../../typechain"; +import { BigNumber as BN } from "ethers"; + +export const DEAD_ADDRESS = "0x0000000000000000000000000000000000000001"; +export const ZERO_ADDRESS = ethers.constants.AddressZero; + +export const DEFAULT_DECIMALS = 18; + +export const ZERO = BN.from(0); +export const ONE_MIN = BN.from(60); +export const TEN_MINS = BN.from(60 * 10); +export const ONE_HOUR = BN.from(60 * 60); +export const ONE_DAY = BN.from(60 * 60 * 24); +export const FIVE_DAYS = BN.from(60 * 60 * 24 * 5); +export const TEN_DAYS = BN.from(60 * 60 * 24 * 10); +export const ONE_WEEK = BN.from(60 * 60 * 24 * 7); +export const ONE_YEAR = BN.from(60 * 60 * 24 * 365); + +export const connectorName = "MStable"; + +interface TokenData { + tokenAddress: string; + tokenWhaleAddress?: string; + feederPool?: string; +} + +export const toEther = (amount: BN) => ethers.utils.formatEther(amount); + +export const getToken = (tokenSymbol: string): TokenData => { + switch (tokenSymbol) { + case "MTA": + return { + tokenAddress: "0xf501dd45a1198c2e1b5aef5314a68b9006d842e0" + }; + case "mUSD": + return { + tokenAddress: "0xe840b73e5287865eec17d250bfb1536704b43b21", + tokenWhaleAddress: "0x4393b9c542bf79e5235180d6da1915c0f9bc02c3" + }; + + case "DAI": + return { + tokenAddress: "0x8f3cf7ad23cd3cadbd9735aff958023239c6a063", + tokenWhaleAddress: "0x49854708A8c42eEB837A97Dd97D597890CEb1334" + }; + case "USDC": + return { + tokenAddress: "0x2791bca1f2de4661ed88a30c99a7a9449aa84174" + }; + case "imUSD": + return { + tokenAddress: "0x5290Ad3d83476CA6A2b178Cd9727eE1EF72432af" + }; + + case "imUSDVault": + return { + tokenAddress: "0x32aBa856Dc5fFd5A56Bcd182b13380e5C855aa29" + }; + + case "FRAX": + return { + tokenAddress: "0x104592a158490a9228070E0A8e5343B499e125D0", + tokenWhaleAddress: "0xAE0f77C239f72da36d4dA20a4bBdaAe4Ca48e03F", + feederPool: "0xb30a907084ac8a0d25dddab4e364827406fd09f0" + }; + + default: + throw new Error(`Token ${tokenSymbol} not supported`); + } +}; + +export const sendToken = async (token: string, amount: any, from: string, to: string): Promise => { + await hre.network.provider.request({ + method: "hardhat_impersonateAccount", + params: [from] + }); + const [signer] = await ethers.getSigners(); + const sender = hre.ethers.provider.getSigner(from); + + await signer.sendTransaction({ + to: from, + value: ethers.utils.parseEther("1") + }); + + return await IERC20Minimal__factory.connect(token, sender).transfer(to, amount); +}; + +export const fundWallet = async (token: string, amount: any, to: string) => { + const { tokenAddress, tokenWhaleAddress } = getToken(token); + await sendToken(tokenAddress, amount, tokenWhaleAddress!, to); +}; + +export const calcMinOut = (amount: BN, slippage: number): BN => { + const value = simpleToExactAmount(1 - slippage); + const minOut = amount.mul(value).div(ethers.BigNumber.from(10).pow(DEFAULT_DECIMALS)); + return minOut; +}; + +export const simpleToExactAmount = (amount: number | string | BN, decimals: number | BN = DEFAULT_DECIMALS): BN => { + let amountString = amount.toString(); + const decimalsBN = BN.from(decimals); + + if (decimalsBN.gt(100)) { + throw new Error(`Invalid decimals amount`); + } + + const scale = BN.from(10).pow(decimals); + const scaleString = scale.toString(); + + // Is it negative? + const negative = amountString.substring(0, 1) === "-"; + if (negative) { + amountString = amountString.substring(1); + } + + if (amountString === ".") { + throw new Error(`Error converting number ${amountString} to precise unit, invalid value`); + } + + // Split it into a whole and fractional part + // eslint-disable-next-line prefer-const + let [whole, fraction, ...rest] = amountString.split("."); + if (rest.length > 0) { + throw new Error(`Error converting number ${amountString} to precise unit, too many decimal points`); + } + + if (!whole) { + whole = "0"; + } + if (!fraction) { + fraction = "0"; + } + + if (fraction.length > scaleString.length - 1) { + throw new Error(`Error converting number ${amountString} to precise unit, too many decimal places`); + } + + while (fraction.length < scaleString.length - 1) { + fraction += "0"; + } + + const wholeBN = BN.from(whole); + const fractionBN = BN.from(fraction); + let result = wholeBN.mul(scale).add(fractionBN); + + if (negative) { + result = result.mul("-1"); + } + + return result; +}; + +export const advanceBlock = async (): Promise => ethers.provider.send("evm_mine", []); + +export const increaseTime = async (length: BN | number): Promise => { + await ethers.provider.send("evm_increaseTime", [BN.from(length).toNumber()]); + await advanceBlock(); +}; diff --git a/test/polygon/mstable/mstable.test.ts b/test/polygon/mstable/mstable.test.ts new file mode 100644 index 0000000..1015f8d --- /dev/null +++ b/test/polygon/mstable/mstable.test.ts @@ -0,0 +1,234 @@ +import { expect } from "chai"; +import hre from "hardhat"; +const { waffle, ethers } = hre; +const { provider } = waffle; + +import { deployAndEnableConnector } from "../../../scripts/tests/deployAndEnableConnector"; +import { buildDSAv2 } from "../../../scripts/tests/buildDSAv2"; +import { encodeSpells } from "../../../scripts/tests/encodeSpells"; +import { getMasterSigner } from "../../../scripts/tests/getMasterSigner"; + +import { addresses } from "../../../scripts/tests/polygon/addresses"; +import { abis } from "../../../scripts/constant/abis"; +import type { Signer, Contract } from "ethers"; + +import { ConnectV2PmStable__factory, IERC20Minimal__factory, IERC20Minimal } from "../../../typechain"; + +import { executeAndAssertDeposit, executeAndAssertSwap, executeAndAssertWithdraw } from "./mstable.utils"; + +import { + fundWallet, + getToken, + simpleToExactAmount, + DEAD_ADDRESS, + calcMinOut, + ONE_DAY, + increaseTime, + connectorName, + toEther +} from "./mstable.helpers"; + +describe("MStable", async () => { + let dsaWallet0: Contract; + let masterSigner: Signer; + let instaConnectorsV2: Contract; + let connector: Contract; + + let mtaToken: IERC20Minimal = IERC20Minimal__factory.connect(getToken("MTA").tokenAddress, provider); + let mUsdToken: IERC20Minimal = IERC20Minimal__factory.connect(getToken("mUSD").tokenAddress, provider); + let imUsdToken: IERC20Minimal = IERC20Minimal__factory.connect(getToken("imUSD").tokenAddress, provider); + let imUsdVault: IERC20Minimal = IERC20Minimal__factory.connect(getToken("imUSDVault").tokenAddress, provider); + + let daiToken: IERC20Minimal = IERC20Minimal__factory.connect(getToken("DAI").tokenAddress, provider); + let usdcToken: IERC20Minimal = IERC20Minimal__factory.connect(getToken("USDC").tokenAddress, provider); + let fraxToken: IERC20Minimal = IERC20Minimal__factory.connect(getToken("FRAX").tokenAddress, provider); + + const wallets = provider.getWallets(); + const [wallet0, wallet1, wallet2, wallet3] = wallets; + + describe("DSA wallet", async () => { + const fundAmount = simpleToExactAmount(10000); + + const setup = async () => { + await hre.network.provider.request({ + method: "hardhat_reset", + params: [ + { + forking: { + // @ts-ignore + jsonRpcUrl: hre.config.networks.hardhat.forking.url, + blockNumber: 23059414 + } + } + ] + }); + + masterSigner = await getMasterSigner(); + instaConnectorsV2 = await ethers.getContractAt(abis.core.connectorsV2, addresses.core.connectorsV2); + connector = await deployAndEnableConnector({ + connectorName, + contractArtifact: ConnectV2PmStable__factory, + signer: masterSigner, + connectors: instaConnectorsV2 + }); + + console.log("Connector address", connector.address); + + dsaWallet0 = await buildDSAv2(wallet0.address); + + await wallet0.sendTransaction({ + to: dsaWallet0.address, + value: simpleToExactAmount(10) + }); + + await fundWallet("mUSD", fundAmount, dsaWallet0.address); + await fundWallet("DAI", fundAmount, dsaWallet0.address); + await fundWallet("FRAX", fundAmount, dsaWallet0.address); + }; + + describe("Deploy", async () => { + before(async () => { + await setup(); + }); + + it("Should deploy properly", async () => { + expect(instaConnectorsV2.address).to.be.properAddress; + expect(connector.address).to.be.properAddress; + expect(await masterSigner.getAddress()).to.be.properAddress; + + expect(dsaWallet0.address).to.be.properAddress; + }); + it("Should fund the wallet", async () => { + expect(await ethers.provider.getBalance(dsaWallet0.address)).to.be.gte(ethers.utils.parseEther("10")); + + expect(await mUsdToken.balanceOf(dsaWallet0.address)).to.be.gte(fundAmount); + expect(await daiToken.balanceOf(dsaWallet0.address)).to.be.gte(fundAmount); + expect(await fraxToken.balanceOf(dsaWallet0.address)).to.be.gte(fundAmount); + }); + it("Should not have vault tokens prior", async () => { + // No deposits prior + expect(await imUsdToken.balanceOf(dsaWallet0.address)).to.be.eq(0); + expect(await imUsdVault.balanceOf(dsaWallet0.address)).to.be.eq(0); + }); + }); + + describe("Main SAVE", async () => { + before(async () => { + await setup(); + }); + it("Should deposit mUSD to Vault successfully", async () => { + const depositAmount = simpleToExactAmount(100); + const minOut = depositAmount; + + await executeAndAssertDeposit("deposit", mUsdToken, depositAmount, dsaWallet0, wallet0, [minOut, true]); + }); + it("Should deposit DAI to Vault successfully (mUSD bAsset)", async () => { + const depositAmount = simpleToExactAmount(100); + const minOut = calcMinOut(depositAmount, 0.02); + + await executeAndAssertDeposit("deposit", daiToken, depositAmount, dsaWallet0, wallet0, [minOut, true]); + }); + it("Should deposit FRAX to Vault successfully (via Feeder Pool)", async () => { + const depositAmount = simpleToExactAmount(100); + const minOut = calcMinOut(depositAmount, 0.02); + const path = getToken("FRAX").feederPool; + + await executeAndAssertDeposit("depositViaSwap", fraxToken, depositAmount, dsaWallet0, wallet0, [ + minOut, + path, + true + ]); + }); + it("Should withdraw from Vault to mUSD", async () => { + const withdrawAmount = simpleToExactAmount(100); + const minOut = simpleToExactAmount(1); + + await executeAndAssertWithdraw("withdraw", mUsdToken, withdrawAmount, dsaWallet0, wallet0, [minOut, true]); + }); + it("Should withdraw from Vault to DAI (mUSD bAsset)", async () => { + const withdrawAmount = simpleToExactAmount(100); + const minOut = simpleToExactAmount(1); + + await executeAndAssertWithdraw("withdraw", mUsdToken, withdrawAmount, dsaWallet0, wallet0, [minOut, true]); + }); + it("Should withdraw from Vault to FRAX (via Feeder Pool)", async () => { + const withdrawAmount = simpleToExactAmount(100); + const minOut = simpleToExactAmount(1); + const path = getToken("FRAX").feederPool; + + await executeAndAssertWithdraw("withdrawViaSwap", fraxToken, withdrawAmount, dsaWallet0, wallet0, [ + minOut, + path, + true + ]); + }); + it("Should claim Rewards", async () => { + const mtaBalanceBefore = await mtaToken.balanceOf(dsaWallet0.address); + console.log("MTA balance before: ", toEther(mtaBalanceBefore)); + + // Wait a bit and let the rewards accumulate + await increaseTime(ONE_DAY); + + const spells = [ + { + connector: connectorName, + method: "claimRewards", + args: [0, 0] + } + ]; + + const tx = await dsaWallet0.connect(wallet0).cast(...encodeSpells(spells), DEAD_ADDRESS); + + const mtaBalanceAfter = await mtaToken.balanceOf(dsaWallet0.address); + console.log("MTA balance after: ", toEther(mtaBalanceAfter)); + + expect(mtaBalanceAfter).to.be.gt(mtaBalanceBefore); + }); + }); + describe("Main SWAP", async () => { + before(async () => { + await setup(); + }); + it("Should swap mUSD to bAsset (redeem)", async () => { + const swapAmount = simpleToExactAmount(100); + await executeAndAssertSwap("swap", mUsdToken, 18, daiToken, 18, swapAmount, dsaWallet0, wallet0); + }); + it("Should swap mUSD to fAsset (via feeder pool)", async () => { + const swapAmount = simpleToExactAmount(100); + const path = getToken("FRAX").feederPool; + await executeAndAssertSwap("swapViaFeeder", mUsdToken, 18, fraxToken, 18, swapAmount, dsaWallet0, wallet0, [ + path + ]); + }); + it("Should swap bAsset to mUSD (mint)", async () => { + const swapAmount = simpleToExactAmount(100); + await executeAndAssertSwap("swap", daiToken, 18, mUsdToken, 18, swapAmount, dsaWallet0, wallet0); + }); + it("Should swap bAsset to bAsset (swap)", async () => { + const swapAmount = simpleToExactAmount(100); + await executeAndAssertSwap("swap", daiToken, 18, usdcToken, 6, swapAmount, dsaWallet0, wallet0); + }); + it("Should swap bAsset to fAsset (via feeder)", async () => { + const swapAmount = simpleToExactAmount(100); + const path = getToken("FRAX").feederPool; + await executeAndAssertSwap("swapViaFeeder", daiToken, 18, fraxToken, 18, swapAmount, dsaWallet0, wallet0, [ + path + ]); + }); + it("Should swap fAsset to bAsset (via feeder)", async () => { + const swapAmount = simpleToExactAmount(100); + const path = getToken("FRAX").feederPool; + await executeAndAssertSwap("swapViaFeeder", fraxToken, 18, daiToken, 18, swapAmount, dsaWallet0, wallet0, [ + path + ]); + }); + it("Should swap fAsset to mUSD (via feeder)", async () => { + const swapAmount = simpleToExactAmount(100); + const path = getToken("FRAX").feederPool; + await executeAndAssertSwap("swapViaFeeder", fraxToken, 18, mUsdToken, 18, swapAmount, dsaWallet0, wallet0, [ + path + ]); + }); + }); + }); +}); diff --git a/test/polygon/mstable/mstable.utils.ts b/test/polygon/mstable/mstable.utils.ts new file mode 100644 index 0000000..88bb21e --- /dev/null +++ b/test/polygon/mstable/mstable.utils.ts @@ -0,0 +1,137 @@ +import hre from "hardhat"; +import { ethers } from "hardhat"; +import { assert, expect } from "chai"; + +import { + DEFAULT_DECIMALS, + DEAD_ADDRESS, + toEther, + connectorName, + simpleToExactAmount, + getToken +} from "./mstable.helpers"; + +import { IERC20Minimal, IERC20Minimal__factory } from "../../../typechain"; +import { BigNumber, Contract, Wallet } from "ethers"; + +import { encodeSpells } from "../../../scripts/tests/encodeSpells"; + +const provider = hre.waffle.provider; + +let imUsdToken: IERC20Minimal = IERC20Minimal__factory.connect(getToken("imUSD").tokenAddress, provider); +let imUsdVault: IERC20Minimal = IERC20Minimal__factory.connect(getToken("imUSDVault").tokenAddress, provider); + +export const executeAndAssertSwap = async ( + method: string, + tokenFrom: IERC20Minimal, + tokenFromDecimals: number, + tokenTo: IERC20Minimal, + tokenToDecimals: number, + swapAmount: BigNumber, + dsaWallet0: Contract, + wallet0: Wallet, + args?: any[] +) => { + const diffFrom = ethers.BigNumber.from(10).pow(DEFAULT_DECIMALS - tokenFromDecimals); + const diffTo = ethers.BigNumber.from(10).pow(DEFAULT_DECIMALS - tokenToDecimals); + + const tokenFromBalanceBefore = (await tokenFrom.balanceOf(dsaWallet0.address)).mul(diffFrom); + console.log("Token From balance before: ", toEther(tokenFromBalanceBefore)); + + const tokenToBalanceBefore = (await tokenTo.balanceOf(dsaWallet0.address)).mul(diffTo); + console.log("Token To balance before: ", toEther(tokenToBalanceBefore)); + + const spells = [ + { + connector: connectorName, + method, + args: [tokenFrom.address, tokenTo.address, swapAmount, 1, ...(args ? args : []), 0, 0] + } + ]; + + console.log("Swapping...", toEther(swapAmount)); + + const tx = await dsaWallet0.connect(wallet0).cast(...encodeSpells(spells), DEAD_ADDRESS); + + const tokenFromBalanceAfter = (await tokenFrom.balanceOf(dsaWallet0.address)).mul(diffFrom); + console.log("Token From balance after: ", toEther(tokenFromBalanceAfter)); + + const tokenToBalanceAfter = (await tokenTo.balanceOf(dsaWallet0.address)).mul(diffTo); + console.log("Token To balance after: ", toEther(tokenToBalanceAfter)); + + expect(tokenFromBalanceAfter).to.be.eq(tokenFromBalanceBefore.sub(swapAmount)); + expect(tokenToBalanceAfter).to.be.gt(tokenToBalanceBefore); +}; + +export const executeAndAssertDeposit = async ( + method: string, + tokenFrom: IERC20Minimal, + depositAmount: BigNumber, + dsaWallet0: Contract, + wallet0: Wallet, + args?: any[] +) => { + const FromBalanceBefore = await tokenFrom.balanceOf(dsaWallet0.address); + console.log("Balance before: ", toEther(FromBalanceBefore)); + + const imUsdVaultBalanceBefore = await imUsdVault.balanceOf(dsaWallet0.address); + console.log("imUSD Vault balance before: ", toEther(imUsdVaultBalanceBefore)); + + const spells = [ + { + connector: connectorName, + method, + args: [tokenFrom.address, depositAmount, ...(args ? args : []), 0, 0] + } + ]; + + const tx = await dsaWallet0.connect(wallet0).cast(...encodeSpells(spells), DEAD_ADDRESS); + + const FromBalanceAfter = await tokenFrom.balanceOf(dsaWallet0.address); + console.log("Balance after: ", toEther(FromBalanceAfter)); + + const imUsdBalance = await imUsdToken.balanceOf(dsaWallet0.address); + console.log("imUSD balance: ", toEther(imUsdBalance)); + + const imUsdVaultBalance = await imUsdVault.balanceOf(dsaWallet0.address); + console.log("imUSD Vault balance: ", toEther(imUsdVaultBalance)); + + // Should have something in the vault but no imUSD + expect(await imUsdToken.balanceOf(dsaWallet0.address)).to.be.eq(0); + expect(await imUsdVault.balanceOf(dsaWallet0.address)).to.be.gt(imUsdVaultBalanceBefore); + expect(FromBalanceAfter).to.eq(FromBalanceBefore.sub(depositAmount)); +}; + +export const executeAndAssertWithdraw = async ( + method: string, + tokenTo: IERC20Minimal, + withdrawAmount: BigNumber, + dsaWallet0: Contract, + wallet0: Wallet, + args: any[] +) => { + const tokenToBalanceBefore = await tokenTo.balanceOf(dsaWallet0.address); + console.log("Balance before: ", toEther(tokenToBalanceBefore)); + + const imUsdVaultBalanceBefore = await imUsdVault.balanceOf(dsaWallet0.address); + console.log("imUSD Vault balance before: ", toEther(imUsdVaultBalanceBefore)); + + const spells = [ + { + connector: connectorName, + method, + args: [tokenTo.address, withdrawAmount, ...(args ? args : []), 0, 0] + } + ]; + + const tx = await dsaWallet0.connect(wallet0).cast(...encodeSpells(spells), DEAD_ADDRESS); + + const imUsdVaultBalanceAfter = await imUsdVault.balanceOf(dsaWallet0.address); + console.log("imUSD Vault balance after: ", toEther(imUsdVaultBalanceAfter)); + + const tokenToBalanceAfter = await tokenTo.balanceOf(dsaWallet0.address); + console.log("Balance after: ", toEther(tokenToBalanceAfter)); + + expect(imUsdVaultBalanceAfter).to.be.eq(imUsdVaultBalanceBefore.sub(withdrawAmount)); + expect(tokenToBalanceAfter).to.gt(tokenToBalanceBefore); +}; diff --git a/test/polygon/paraswap/paraswap.test.ts b/test/polygon/paraswap/paraswap.test.ts new file mode 100644 index 0000000..d1bd8f0 --- /dev/null +++ b/test/polygon/paraswap/paraswap.test.ts @@ -0,0 +1,171 @@ +import hre from "hardhat"; +import { expect } from "chai"; +import { abis } from "../../../scripts/constant/abis"; +import { addresses } from "../../../scripts/tests/polygon/addresses"; +import { deployAndEnableConnector } from "../../../scripts/tests/deployAndEnableConnector"; +import { getMasterSigner } from "../../../scripts/tests/getMasterSigner"; +import { buildDSAv2 } from "../../../scripts/tests/buildDSAv2"; +import { + ConnectV2ParaswapV5Polygon__factory +} from "../../../typechain"; +import { encodeSpells } from "../../../scripts/tests/encodeSpells"; +import BigNumber from "bignumber.js"; +import axios from "axios"; +// import { Signer } from "ethers"; +const { waffle, ethers } = hre; +const { provider, deployContract } = waffle; +import type { Signer, Contract } from "ethers"; +describe("Paraswap", function() { + const connectorName = "paraswap-test"; + let dsaWallet0: any; + let masterSigner: Signer; + let instaConnectorsV2: Contract; + let connector: any; + const wallets = provider.getWallets(); + const [wallet0, wallet1] = wallets; + before(async () => { + await hre.network.provider.request({ + method: "hardhat_reset", + params: [ + { + forking: { + // @ts-ignore + jsonRpcUrl: hre.config.networks.hardhat.forking.url, + }, + }, + ], + }); + masterSigner = await getMasterSigner(); + instaConnectorsV2 = await ethers.getContractAt( + abis.core.connectorsV2, + addresses.core.connectorsV2 + ); + connector = await deployAndEnableConnector({ + connectorName, + contractArtifact: ConnectV2ParaswapV5Polygon__factory, + signer: masterSigner, + connectors: instaConnectorsV2, + }); + console.log("Connector address", connector.address); + }); + it("Should have contracts deployed.", async function() { + expect(!!instaConnectorsV2.address).to.be.true; + expect(!!connector.address).to.be.true; + expect(!!masterSigner.getAddress()).to.be.true; + }); + describe("DSA wallet setup", function() { + it("Should build DSA v2", async function() { + dsaWallet0 = await buildDSAv2(wallet0.address); + expect(!!dsaWallet0.address).to.be.true; + }); + it("Deposit ETH into DSA wallet", async function() { + await wallet0.sendTransaction({ + to: dsaWallet0.address, + value: ethers.utils.parseEther("10"), + }); + + expect(await ethers.provider.getBalance(dsaWallet0.address)).to.be.gte( + ethers.utils.parseEther("10") + ); + }); + }); + describe("Main", function() { + it("should swap successfully", async function() { + async function getArg() { + const slippage = 1; + /* matic -> USDT */ + const sellTokenAddress = "0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE"; // matic, decimals 18 + const sellTokenDecimals = 18; + const buyTokenAddress = "0xc2132d05d31c914a87c6611c10748aeb04b58e8f"; // USDT, decimals 6 + const buyTokenDecimals = 6; + const amount = 2; + const srcAmount = new BigNumber(amount) + .times(new BigNumber(10).pow(sellTokenDecimals)) + .toFixed(0); + const fromAddress = dsaWallet0.address; + let url = `https://apiv5.paraswap.io/prices/`; + let params = { + srcToken: sellTokenAddress, + destToken: buyTokenAddress, + srcDecimals: sellTokenDecimals, + destDecimals: buyTokenDecimals, + amount: srcAmount, + side: "SELL", + network: 137, + }; + + const priceRoute = (await axios + .get(url, { params: params })).data.priceRoute + + + let buyTokenAmount = priceRoute.destAmount; + let minAmount = new BigNumber(priceRoute.destAmount) + .times(1 - slippage / 100) + .toFixed(0); + + let txConfig = { + priceRoute: priceRoute, + srcToken: sellTokenAddress, + destToken: buyTokenAddress, + srcDecimals: sellTokenDecimals, + destDecimals: buyTokenDecimals, + srcAmount: srcAmount, + destAmount: minAmount, + userAddress: fromAddress, + }; + let url2 = "https://apiv5.paraswap.io/transactions/137?ignoreChecks=true"; + const calldata = (await axios + .post(url2, txConfig)).data.data + + function caculateUnitAmt( + buyAmount: any, + sellAmount: any, + buyDecimal: any, + sellDecimal: any, + maxSlippage: any + ) { + let unitAmt: any; + unitAmt = new BigNumber(buyAmount) + .dividedBy(10 ** buyDecimal) + .dividedBy(new BigNumber(sellAmount).dividedBy(10 ** sellDecimal)); + unitAmt = unitAmt.multipliedBy((100 - maxSlippage) / 100); + unitAmt = unitAmt.multipliedBy(1e18).toFixed(0); + return unitAmt; + } + + let unitAmt = caculateUnitAmt( + buyTokenAmount, + srcAmount, + buyTokenDecimals, + sellTokenDecimals, + 1 + ); + + return [ + buyTokenAddress, + sellTokenAddress, + srcAmount, + unitAmt, + calldata, + 0, + ]; + } + let arg = await getArg(); + const spells = [ + { + connector: connectorName, + method: "swap", + args: arg, + }, + ]; + const tx = await dsaWallet0 + .connect(wallet0) + .cast(...encodeSpells(spells), wallet1.address); + const receipt = await tx.wait(); + + expect(await ethers.provider.getBalance(dsaWallet0.address)).to.be.lte( + ethers.utils.parseEther("8") + ); + }); + }); +}); \ No newline at end of file diff --git a/test/polygon/pooltogether/pooltogether.test.ts b/test/polygon/pooltogether/pooltogether.test.ts new file mode 100644 index 0000000..4d77dbf --- /dev/null +++ b/test/polygon/pooltogether/pooltogether.test.ts @@ -0,0 +1,561 @@ +import { expect } from "chai"; +import hre from "hardhat"; +const { web3, deployments, waffle, ethers } = hre; +const { provider, deployContract } = waffle; + +const ALCHEMY_ID = process.env.ALCHEMY_ID; + +import { deployAndEnableConnector } from "../../../scripts/tests/deployAndEnableConnector"; +import { buildDSAv2 } from "../../../scripts/tests/buildDSAv2"; +import { encodeSpells } from "../../../scripts/tests/encodeSpells"; +import { getMasterSigner } from "../../../scripts/tests/getMasterSigner"; + +import { addresses } from "../../../scripts/tests/polygon/addresses"; +import { abis } from "../../../scripts/constant/abis"; +import { tokens } from "../../../scripts/tests/polygon/tokens"; +import type { Signer, Contract } from "ethers"; + +import { ConnectV2AaveV2Polygon__factory, ConnectV2PoolTogetherPolygon__factory } from "../../../typechain"; + +const DAI_TOKEN_ADDR = tokens.dai.address; // DAI Token +// PoolTogether Address: https://docs.pooltogether.com/resources/networks/matic +const USDC_PRIZE_POOL_ADDR = "0xEE06AbE9e2Af61cabcb13170e01266Af2DEFa946"; // USDC Prize Pool +const PT_USDC_TICKET_ADDR = "0x473E484c722EF9ec6f63B509b07Bb9cfB258820b"; // PT USDC Ticket +const PT_USDC_SPONGSOR_TICKET_ADDR = + "0x19c0e557ee5a9b456f613ba3d025a4dc45b52c35"; // PT USDC Sponsor Ticket +const USDC_POOL_FAUCET_ADDR = "0x6cbc003fE015D753180f072d904bA841b2415498"; // USDC POOL Faucet +const POOL_TOKEN_ADDRESS = "0x25788a1a171ec66Da6502f9975a15B609fF54CF6"; // POOL Tocken +const TOKEN_FAUCET_PROXY_FACTORY_ADDR = + "0xeaa636304a7C8853324B6b603dCdE55F92dfbab1"; // TokenFaucetProxyFactory for claimAll + +// Community WETH Prize Pool (Rari): https://reference-app.pooltogether.com/pools/mainnet/0xa88ca010b32a54d446fc38091ddbca55750cbfc3/manage#stats +const WETH_PRIZE_POOL_ADDR = "0xa88ca010b32a54d446fc38091ddbca55750cbfc3"; // Community WETH Prize Pool (Rari) +const WETH_POOL_TICKET_ADDR = "0x9b5c30aeb9ce2a6a121cea9a85bc0d662f6d9b40"; // Community WETH Prize Pool Ticket (Rari) + +const prizePoolABI = [ + "function calculateEarlyExitFee( address from, address controlledToken, uint256 amount) external returns ( uint256 exitFee, uint256 burnedCredit)", + "function creditPlanOf( address controlledToken) external view returns ( uint128 creditLimitMantissa, uint128 creditRateMantissa)", +]; + +const connectorsABI = [ + "function isConnectors(string[] calldata connectorNames) external view returns (bool, address[] memory)", +]; + +describe("PoolTogether", function() { + const connectorName = "AAVEV2-TEST-A"; + const ptConnectorName = "POOLTOGETHER-TEST-A"; + + let dsaWallet0: any; + let masterSigner: Signer; + let instaConnectorsV2: Contract; + let connector: any; + let ptConnector: Contract; + + const wallets = provider.getWallets(); + const [wallet0, wallet1, wallet2, wallet3] = wallets; + before(async () => { + await hre.network.provider.request({ + method: "hardhat_reset", + params: [ + { + forking: { + jsonRpcUrl: `https://polygon-mainnet.g.alchemy.com/v2/${ALCHEMY_ID}`, + blockNumber: 18717337, + }, + }, + ], + }); + + masterSigner = await getMasterSigner(); + instaConnectorsV2 = await ethers.getContractAt( + abis.core.connectorsV2, + addresses.core.connectorsV2 + ); + + // Deploy and enable Compound Connector + connector = await deployAndEnableConnector({ + connectorName, + contractArtifact: ConnectV2AaveV2Polygon__factory, + signer: masterSigner, + connectors: instaConnectorsV2, + }); + + // Deploy and enable Pool Together Connector + ptConnector = await deployAndEnableConnector({ + connectorName: ptConnectorName, + contractArtifact: ConnectV2PoolTogetherPolygon__factory, + signer: masterSigner, + connectors: instaConnectorsV2, + }); + }); + + it("Should have contracts deployed.", async function() { + expect(!!instaConnectorsV2.address).to.be.true; + expect(!!connector.address).to.be.true; + expect(!!ptConnector.address).to.be.true; + expect(!!(await masterSigner.getAddress())).to.be.true; + }); + + describe("DSA wallet setup", function() { + it("Should build DSA v2", async function() { + dsaWallet0 = await buildDSAv2(wallet0.address); + expect(!!dsaWallet0.address).to.be.true; + }); + + it("Deposit 1000 MATIC into DSA wallet", async function() { + await wallet0.sendTransaction({ + to: dsaWallet0.address, + value: ethers.utils.parseEther("1000"), + }); + expect(await ethers.provider.getBalance(dsaWallet0.address)).to.be.gte( + ethers.utils.parseEther("1000") + ); + }); + }); + + describe("Main - USDC Prize Pool Test", function() { + it("Should deposit 100 MATIC in AAVE V2", async function() { + const amount = ethers.utils.parseEther("100"); // 100 MATIC + const spells = [ + { + connector: connectorName, + method: "deposit", + args: [tokens.eth.address, amount, 0, 0], + }, + ]; + + const tx = await dsaWallet0.cast( + ...encodeSpells(spells), + wallet1.address + ); + const receipt = await tx.wait(); + expect(await ethers.provider.getBalance(dsaWallet0.address)).to.be.lte( + ethers.utils.parseEther("900") + ); + }); + + it("Should borrow 10 USDC from AAVE V2 and deposit USDC into USDC Prize Pool", async function() { + const amount = ethers.utils.parseUnits("10", 6); // 10 USDC + const setId = "83478237"; + const spells = [ + { + connector: connectorName, + method: "borrow", + args: [tokens.usdc.address, amount, 2, 0, setId], + }, + { + connector: ptConnectorName, + method: "depositTo", + args: [ + USDC_PRIZE_POOL_ADDR, + amount, + PT_USDC_SPONGSOR_TICKET_ADDR, + setId, + 0, + ], + }, + ]; + // Before Spell + let usdcToken = await ethers.getContractAt( + abis.basic.erc20, + tokens.usdc.address + ); + let usdcBalance = await usdcToken.balanceOf(dsaWallet0.address); + expect(usdcBalance, `USDC balance is 0`).to.be.eq( + ethers.utils.parseUnits("0", 6) + ); + + let cToken = await ethers.getContractAt( + abis.basic.erc20, + PT_USDC_SPONGSOR_TICKET_ADDR + ); + const balance = await cToken.balanceOf(dsaWallet0.address); + expect(balance, `PoolTogether USDC Ticket balance is 0`).to.be.eq(0); + + // Run spell transaction + const tx = await dsaWallet0 + .connect(wallet0) + .cast(...encodeSpells(spells), wallet1.address); + const receipt = await tx.wait(); + + // After spell + usdcBalance = await usdcToken.balanceOf(dsaWallet0.address); + expect( + usdcBalance, + `Expect USDC balance to still equal 0 since it was deposited into Prize Pool` + ).to.be.eq(0); + + const balanceAfter = await cToken.balanceOf(dsaWallet0.address); + expect( + balanceAfter, + `PoolTogether USDC Ticket balance equals 10` + ).to.be.eq(ethers.utils.parseUnits("10", 6)); + + // ETH used for transaction + expect(await ethers.provider.getBalance(dsaWallet0.address)).to.be.lte( + ethers.utils.parseEther("900") + ); + }); + + it("Should wait 11 days, withdraw all PrizePool, get back 10 USDC, and claim POOL", async function() { + const amount = ethers.utils.parseUnits("10", 6); // 10 USDC + + let prizePoolContract = new ethers.Contract( + USDC_PRIZE_POOL_ADDR, + prizePoolABI, + ethers.provider + ); + // const { creditLimitMantissa, creditRateMantissa } = await prizePoolContract.creditPlanOf(PT_USDC_TICKET_ADDR); + // console.log("CreditLimitMantiss: ", creditLimitMantissa.toString()); + // console.log("CreditRateMantiss: ", creditRateMantissa.toString()); + let earlyExitFee = await prizePoolContract.callStatic[ + "calculateEarlyExitFee" + ](dsaWallet0.address, PT_USDC_SPONGSOR_TICKET_ADDR, amount); + expect( + earlyExitFee.exitFee, + "Exit Fee equal to 0 USDC because 0% fee for sponsorship ticket" + ).to.be.eq(ethers.utils.parseUnits("0", 6)); + + const spells = [ + { + connector: ptConnectorName, + method: "claim", + args: [USDC_POOL_FAUCET_ADDR, 0], + }, + { + connector: ptConnectorName, + method: "withdrawInstantlyFrom", + args: [ + USDC_PRIZE_POOL_ADDR, + amount, + PT_USDC_SPONGSOR_TICKET_ADDR, + earlyExitFee.exitFee, + 0, + 0, + ], + }, + ]; + + // Before spell + let usdcToken = await ethers.getContractAt( + abis.basic.erc20, + tokens.usdc.address + ); + let usdcBalance = await usdcToken.balanceOf(dsaWallet0.address); + expect(usdcBalance, `USDC balance equals 0`).to.be.eq( + ethers.utils.parseEther("0") + ); + + let cToken = await ethers.getContractAt( + abis.basic.erc20, + PT_USDC_SPONGSOR_TICKET_ADDR + ); + const balance = await cToken.balanceOf(dsaWallet0.address); + expect(balance, `PoolTogether USDC Ticket is 10`).to.be.eq( + ethers.utils.parseUnits("10", 6) + ); + + let poolToken = await ethers.getContractAt( + abis.basic.erc20, + POOL_TOKEN_ADDRESS + ); + const poolBalance = await poolToken.balanceOf(dsaWallet0.address); + expect(poolBalance, `POOL Token equals 0`).to.be.eq( + ethers.utils.parseEther("0") + ); + + // Increase time by 11 days so we get back all USDC without early withdrawal fee + await ethers.provider.send("evm_increaseTime", [15 * 24 * 60 * 60]); + + earlyExitFee = await prizePoolContract.callStatic[ + "calculateEarlyExitFee" + ](dsaWallet0.address, PT_USDC_SPONGSOR_TICKET_ADDR, amount); + expect( + earlyExitFee.exitFee, + "Exit Fee equal to 0 DAI because past 14 days" + ).to.be.eq(0); + + // Run spell transaction + const tx = await dsaWallet0 + .connect(wallet0) + .cast(...encodeSpells(spells), wallet1.address); + const receipt = await tx.wait(); + + // After spell + usdcBalance = await usdcToken.balanceOf(dsaWallet0.address); + console.log("USDC BALANCE: ", usdcBalance.toString()); + console.log( + "USDC BALANCE: ", + ethers.utils.parseUnits("10", 6).toString() + ); + expect( + usdcBalance, + `USDC balance to be equal to 10, because of no early withdrawal fee` + ).to.be.eq(ethers.utils.parseUnits("10", 6)); + + const balanceAfter = await cToken.balanceOf(dsaWallet0.address); + expect(balanceAfter, `PoolTogether USDC Ticket to equal 0`).to.be.eq(0); + + const poolBalanceAfter = await poolToken.balanceOf(dsaWallet0.address); + console.log("POOL BALANCE AFTER:", poolBalanceAfter.toString()); + expect( + poolBalanceAfter, + `POOL Token Balance to be greater than 0` + ).to.be.gt(ethers.utils.parseEther("0")); + }); + + it("Should deposit and withdraw all PrizePool, get back less than 10 USDC", async function() { + const amount = ethers.utils.parseUnits("10", 6); // 10 USDC + const exitFee = ethers.utils.parseUnits(".1", 6); // 1 USDC is 1% of 100 USDC + const spells = [ + { + connector: ptConnectorName, + method: "depositTo", + args: [USDC_PRIZE_POOL_ADDR, amount, PT_USDC_TICKET_ADDR, 0, 0], + }, + { + connector: ptConnectorName, + method: "withdrawInstantlyFrom", + args: [ + USDC_PRIZE_POOL_ADDR, + amount, + PT_USDC_TICKET_ADDR, + exitFee, + 0, + 0, + ], + }, + ]; + + // Before spell + let usdcToken = await ethers.getContractAt( + abis.basic.erc20, + tokens.usdc.address + ); + let usdcBalance = await usdcToken.balanceOf(dsaWallet0.address); + expect(usdcBalance, `USDC Balance equals 100`).to.be.eq( + ethers.utils.parseUnits("10", 6) + ); + + let cToken = await ethers.getContractAt( + abis.basic.erc20, + PT_USDC_TICKET_ADDR + ); + const balance = await cToken.balanceOf(dsaWallet0.address); + expect(balance, `PoolTogether USDC Ticket equals 0`).to.be.eq(0); + + let poolToken = await ethers.getContractAt( + abis.basic.erc20, + POOL_TOKEN_ADDRESS + ); + const poolBalance = await poolToken.balanceOf(dsaWallet0.address); + expect(poolBalance, `PoolTogether Token greater than 0`).to.be.gt(0); + + // Run spell transaction + const tx = await dsaWallet0 + .connect(wallet0) + .cast(...encodeSpells(spells), wallet1.address); + const receipt = await tx.wait(); + + // After spell + usdcBalance = await usdcToken.balanceOf(dsaWallet0.address); + expect( + usdcBalance, + `USDC balance to be less than 10, because of early withdrawal fee` + ).to.be.lt(ethers.utils.parseUnits("10", 6)); + + console.log("USDC BALANCE AFTER:", usdcBalance.toString()); + + const balanceAfter = await cToken.balanceOf(dsaWallet0.address); + expect(balanceAfter, `PoolTogether USDC Ticket to equal 0`).to.be.eq(0); + + const poolBalanceAfter = await poolToken.balanceOf(dsaWallet0.address); + expect(poolBalanceAfter, `POOL Token Balance to greater than 0`).to.be.gt( + ethers.utils.parseEther("0") + ); + }); + + it("Should deposit, wait 11 days, and withdraw all PrizePool, get 10 USDC, and claim all POOL using claimAll", async function() { + const amount = ethers.utils.parseUnits("9.9", 6); // 9 USDC + const depositSpells = [ + { + connector: ptConnectorName, + method: "depositTo", + args: [ + USDC_PRIZE_POOL_ADDR, + amount, + PT_USDC_SPONGSOR_TICKET_ADDR, + 0, + 0, + ], + }, + ]; + + // Before spell + let usdcToken = await ethers.getContractAt( + abis.basic.erc20, + tokens.usdc.address + ); + let usdcBalance = await usdcToken.balanceOf(dsaWallet0.address); + expect(usdcBalance, `USDC balance less than 10`).to.be.lt( + ethers.utils.parseUnits("10", 6) + ); + + let cToken = await ethers.getContractAt( + abis.basic.erc20, + PT_USDC_SPONGSOR_TICKET_ADDR + ); + const balance = await cToken.balanceOf(dsaWallet0.address); + expect(balance, `PoolTogether USDC Ticket equal 0`).to.be.eq(0); + + let poolToken = await ethers.getContractAt( + abis.basic.erc20, + POOL_TOKEN_ADDRESS + ); + const poolBalance = await poolToken.balanceOf(dsaWallet0.address); + expect(poolBalance, `POOL Token is greater than 0`).to.be.gt( + ethers.utils.parseEther("0") + ); + + // Run spell transaction + const tx = await dsaWallet0 + .connect(wallet0) + .cast(...encodeSpells(depositSpells), wallet1.address); + const receipt = await tx.wait(); + + const prizePoolContract = new ethers.Contract( + USDC_PRIZE_POOL_ADDR, + prizePoolABI, + ethers.provider + ); + let earlyExitFee = await prizePoolContract.callStatic[ + "calculateEarlyExitFee" + ](dsaWallet0.address, PT_USDC_SPONGSOR_TICKET_ADDR, amount); + expect( + earlyExitFee.exitFee, + "Exit Fee equal to 0 USDC because fee 0%" + ).to.be.eq(0); + + // Increase time by 11 days so we get back all DAI without early withdrawal fee + await ethers.provider.send("evm_increaseTime", [11 * 24 * 60 * 60]); + + const withdrawSpells = [ + { + connector: ptConnectorName, + method: "withdrawInstantlyFrom", + args: [ + USDC_PRIZE_POOL_ADDR, + amount, + PT_USDC_SPONGSOR_TICKET_ADDR, + earlyExitFee.exitFee, + 0, + 0, + ], + }, + { + connector: ptConnectorName, + method: "claimAll", + args: [TOKEN_FAUCET_PROXY_FACTORY_ADDR, [USDC_POOL_FAUCET_ADDR]], + }, + ]; + + // Run spell transaction + const tx2 = await dsaWallet0 + .connect(wallet0) + .cast(...encodeSpells(withdrawSpells), wallet1.address); + const receipt2 = await tx2.wait(); + + // After spell + usdcBalance = await usdcToken.balanceOf(dsaWallet0.address); + expect(usdcBalance, `USDC balance equals 9.9`).to.be.eq( + ethers.utils.parseUnits("9.9", 6) + ); + + const balanceAfter = await cToken.balanceOf(dsaWallet0.address); + expect(balanceAfter, `PoolTogether USDC Ticket equal 0`).to.be.eq(0); + + // Expect + const poolBalanceAfter = await poolToken.balanceOf(dsaWallet0.address); + console.log("POOL BALANCE AFTER:", poolBalanceAfter.toString()); + expect(poolBalanceAfter, `Pool Token to be greater than before`).to.be.gt( + poolBalance + ); + }); + // }) + + // NO WMATIC POOLS: https://reference-app.pooltogether.com/pools/polygon + // describe("Main - WETH Prize Pool Test", function () { + // it("Deposit 1 ETH into WETH Prize Pool and withdraw immediately", async function () { + // const amount = ethers.utils.parseEther("1") // 1 ETH + // const setId = "83478237" + // const spells = [ + // { + // connector: ptConnectorName, + // method: "depositTo", + // args: [WETH_PRIZE_POOL_ADDR, amount, WETH_POOL_TICKET_ADDR, 0, setId] + // }, + // { + // connector: ptConnectorName, + // method: "withdrawInstantlyFrom", + // args: [WETH_PRIZE_POOL_ADDR, amount, WETH_POOL_TICKET_ADDR, amount, setId, 0] + // }, + // ] + // // Before Spell + // const ethBalanceBefore = await ethers.provider.getBalance(dsaWallet0.address); + + // // Run spell transaction + // const tx = await dsaWallet0.connect(wallet0).cast(...encodeSpells(spells), wallet1.address) + // const receipt = await tx.wait() + + // // After spell + // const ethBalanceAfter = await ethers.provider.getBalance(dsaWallet0.address); + + // // ETH used for transaction + // expect(ethBalanceAfter, `ETH Balance less than before spell because of early withdrawal fee`).to.be.lte(ethBalanceBefore); + // }); + + // it("Deposit 1 ETH into WETH Prize Pool, wait 14 days, then withdraw", async function () { + // const amount = ethers.utils.parseEther("1") // 1 ETH + // const depositSpell = [ + // { + // connector: ptConnectorName, + // method: "depositTo", + // args: [WETH_PRIZE_POOL_ADDR, amount, WETH_POOL_TICKET_ADDR, 0, 0] + // } + // ] + + // const withdrawSpell = [ + // { + // connector: ptConnectorName, + // method: "withdrawInstantlyFrom", + // args: [WETH_PRIZE_POOL_ADDR, amount, WETH_POOL_TICKET_ADDR, amount, 0, 0] + // } + // ] + + // // Before Deposit Spell + // let ethBalanceBefore = await ethers.provider.getBalance(dsaWallet0.address); + + // // Run deposit spell transaction + // const tx = await dsaWallet0.connect(wallet0).cast(...encodeSpells(depositSpell), wallet1.address) + // const receipt = await tx.wait() + + // // After Deposit spell + // let ethBalanceAfter = await ethers.provider.getBalance(dsaWallet0.address); + + // expect(ethBalanceAfter, `ETH Balance less than before spell`).to.be.lte(ethBalanceBefore); + + // // Increase time by 11 days so we get back all ETH without early withdrawal fee + // await ethers.provider.send("evm_increaseTime", [14*24*60*60]); + // await ethers.provider.send("evm_mine"); + + // // Run withdraw spell transaction + // const tx2 = await dsaWallet0.connect(wallet0).cast(...encodeSpells(withdrawSpell), wallet1.address) + // const receipt2 = await tx.wait() + + // // After Deposit spell + // ethBalanceAfter = await ethers.provider.getBalance(dsaWallet0.address); + + // expect(ethBalanceAfter, `ETH Balance equal to before spell because no early exit fee`).to.be.eq(ethBalanceBefore); + // }); + }); +}); diff --git a/test/polygon/quickswap/quickswap.test.ts b/test/polygon/quickswap/quickswap.test.ts new file mode 100644 index 0000000..3d3a934 --- /dev/null +++ b/test/polygon/quickswap/quickswap.test.ts @@ -0,0 +1,137 @@ +import { expect } from "chai"; +import hre from "hardhat"; +const { waffle, ethers } = hre; +const { provider } = waffle; + +import { deployAndEnableConnector } from "../../../scripts/tests/deployAndEnableConnector"; +import { buildDSAv2 } from "../../../scripts/tests/buildDSAv2"; +import { encodeSpells } from "../../../scripts/tests/encodeSpells"; +import { getMasterSigner } from "../../../scripts/tests/getMasterSigner"; +import { addLiquidity } from "../../../scripts/tests/addLiquidity"; + +import { addresses } from "../../../scripts/tests/polygon/addresses"; +import { abis } from "../../../scripts/constant/abis"; +import { ConnectV2QuickswapPolygon__factory, ConnectV2QuickswapPolygon } from "../../../typechain"; +import type { Signer, Contract } from "ethers"; + +const DAI_ADDR = "0x8f3cf7ad23cd3cadbd9735aff958023239c6a063"; + +describe("Quickswap", function () { + const connectorName = "Quickswap-v1"; + + let dsaWallet0: Contract; + let masterSigner: Signer; + let instaConnectorsV2: Contract; + let connector: Contract; + + const wallets = provider.getWallets(); + const [wallet0, wallet1, wallet2, wallet3] = wallets; + before(async () => { + await hre.network.provider.request({ + method: "hardhat_reset", + params: [ + { + forking: { + // @ts-ignore + jsonRpcUrl: hre.config.networks.hardhat.forking.url + // blockNumber: 13005785 + } + } + ] + }); + + masterSigner = await getMasterSigner(); + instaConnectorsV2 = await ethers.getContractAt(abis.core.connectorsV2, addresses.core.connectorsV2); + connector = await deployAndEnableConnector({ + connectorName, + contractArtifact: ConnectV2QuickswapPolygon__factory, + signer: masterSigner, + connectors: instaConnectorsV2 + }); + console.log("Connector address", connector.address); + }); + + it("Should have contracts deployed.", async function () { + expect(!!instaConnectorsV2.address).to.be.true; + expect(!!connector.address).to.be.true; + expect(!!(await masterSigner.getAddress())).to.be.true; + }); + + describe("DSA wallet setup", function () { + it("Should build DSA v2", async function () { + dsaWallet0 = await buildDSAv2(wallet0.address); + expect(!!dsaWallet0.address).to.be.true; + }); + + it("Deposit ETH & DAI into DSA wallet", async function () { + await wallet0.sendTransaction({ + to: dsaWallet0.address, + value: ethers.utils.parseEther("10") + }); + expect(await ethers.provider.getBalance(dsaWallet0.address)).to.be.gte(ethers.utils.parseEther("10")); + + await addLiquidity("dai", dsaWallet0.address, ethers.utils.parseEther("10000")); + }); + }); + + describe("Main", function () { + it("Should deposit successfully", async function () { + const ethAmount = ethers.utils.parseEther("0.1"); // 1 ETH + const daiUnitAmount = ethers.utils.parseEther("1"); // 1 ETH + const ethAddress = "0xeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee"; + + const getId = "0"; + const setId = "0"; + + const spells = [ + { + connector: connectorName, + method: "deposit", + args: [ethAddress, DAI_ADDR, ethAmount, daiUnitAmount, "500000000000000000", getId, setId] + } + ]; + + const tx = await dsaWallet0.connect(wallet0).cast(...encodeSpells(spells), wallet1.address); + let receipt = await tx.wait(); + }).timeout(10000000000); + + it("Should withdraw successfully", async function () { + const ethAmount = ethers.utils.parseEther("0.001"); // 1 ETH + const ethAddress = "0xeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee"; + + const getId = "0"; + const setIds = ["0", "0"]; + + const spells = [ + { + connector: connectorName, + method: "withdraw", + args: [ethAddress, DAI_ADDR, ethAmount, 0, 0, getId, setIds] + } + ]; + + const tx = await dsaWallet0.connect(wallet0).cast(...encodeSpells(spells), wallet1.address); + let receipt = await tx.wait(); + }); + + it("Should buy successfully", async function () { + const ethAmount = ethers.utils.parseEther("0.1"); // 1 ETH + const daiUnitAmount = ethers.utils.parseEther("4000"); // 1 ETH + const ethAddress = "0xeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee"; + + const getId = "0"; + const setId = "0"; + + const spells = [ + { + connector: connectorName, + method: "buy", + args: [ethAddress, DAI_ADDR, ethAmount, daiUnitAmount, getId, setId] + } + ]; + + const tx = await dsaWallet0.connect(wallet0).cast(...encodeSpells(spells), wallet1.address); + let receipt = await tx.wait(); + }); + }); +}); diff --git a/test/polygon/sushiswap/sushiswap.test.ts b/test/polygon/sushiswap/sushiswap.test.ts new file mode 100644 index 0000000..4314e1b --- /dev/null +++ b/test/polygon/sushiswap/sushiswap.test.ts @@ -0,0 +1,148 @@ +import { expect } from "chai"; +import hre from "hardhat"; +const { waffle, ethers } = hre; +const { provider } = waffle; + +import { deployAndEnableConnector } from "../../../scripts/tests/deployAndEnableConnector"; +import { buildDSAv2 } from "../../../scripts/tests/buildDSAv2"; +import { encodeSpells } from "../../../scripts/tests/encodeSpells"; +import { getMasterSigner } from "../../../scripts/tests/getMasterSigner"; +import { addLiquidity } from "../../../scripts/tests/addLiquidity"; + +import { constants } from "../../../scripts/constant/constant"; +import { addresses } from "../../../scripts/tests/polygon/addresses"; +import { abis } from "../../../scripts/constant/abis"; +import { ConnectV2SushiswapPolygon__factory, ConnectV2SushiswapPolygon } from "../../../typechain"; +import type { Signer, Contract } from "ethers"; + +const DAI_ADDR = "0x8f3cf7ad23cd3cadbd9735aff958023239c6a063"; + +describe("Sushiswap", function () { + const connectorName = "Sushiswap-v1"; + + let dsaWallet0: Contract; + let masterSigner: Signer; + let instaConnectorsV2: Contract; + let connector: Contract; + + const wallets = provider.getWallets(); + const [wallet0, wallet1, wallet2, wallet3] = wallets; + before(async () => { + await hre.network.provider.request({ + method: "hardhat_reset", + params: [ + { + forking: { + // @ts-ignore + jsonRpcUrl: hre.config.networks.hardhat.forking.url, + blockNumber: 13005785 + } + } + ] + }); + + masterSigner = await getMasterSigner(); + instaConnectorsV2 = await ethers.getContractAt(abis.core.connectorsV2, addresses.core.connectorsV2); + connector = await deployAndEnableConnector({ + connectorName, + contractArtifact: ConnectV2SushiswapPolygon__factory, + signer: masterSigner, + connectors: instaConnectorsV2 + }); + console.log("Connector address", connector.address); + }); + + it("Should have contracts deployed.", async function () { + expect(!!instaConnectorsV2.address).to.be.true; + expect(!!connector.address).to.be.true; + expect(!!(await masterSigner.getAddress())).to.be.true; + }); + + describe("DSA wallet setup", function () { + it("Should build DSA v2", async function () { + dsaWallet0 = await buildDSAv2(wallet0.address); + expect(!!dsaWallet0.address).to.be.true; + }); + + it("Deposit MATIC & DAI into DSA wallet", async function () { + await wallet0.sendTransaction({ + to: dsaWallet0.address, + value: ethers.utils.parseEther("10") + }); + expect(await ethers.provider.getBalance(dsaWallet0.address)).to.be.gte(ethers.utils.parseEther("10")); + + await addLiquidity("dai", dsaWallet0.address, ethers.utils.parseEther("10000")); + }); + + it("Deposit MATIC & USDT into DSA wallet", async function () { + await wallet0.sendTransaction({ + to: dsaWallet0.address, + value: ethers.utils.parseEther("10") + }); + expect(await ethers.provider.getBalance(dsaWallet0.address)).to.be.gte(ethers.utils.parseEther("10")); + + await addLiquidity("usdt", dsaWallet0.address, ethers.utils.parseEther("10000")); + }); + }); + + describe("Main", function () { + it("Should deposit successfully", async function () { + const maticAmount = ethers.utils.parseEther("0.1"); + const daiUnitAmount = ethers.utils.parseEther("4000"); + const maticAddress = constants.native_address; + + const getId = "0"; + const setId = "0"; + + const spells = [ + { + connector: connectorName, + method: "deposit", + args: [maticAddress, DAI_ADDR, maticAmount, daiUnitAmount, "500000000000000000", getId, setId] + } + ]; + + const tx = await dsaWallet0.connect(wallet0).cast(...encodeSpells(spells), wallet1.address); + let receipt = await tx.wait(); + }).timeout(10000000000); + + it("Should withdraw successfully", async function () { + const maticAmount = ethers.utils.parseEther("0.1"); + const maticAddress = constants.native_address; + + const getId = "0"; + const setIds = ["0", "0"]; + + const spells = [ + { + connector: connectorName, + method: "withdraw", + args: [maticAddress, DAI_ADDR, maticAmount, 0, 0, getId, setIds] + } + ]; + + const tx = await dsaWallet0.connect(wallet0).cast(...encodeSpells(spells), wallet1.address); + let receipt = await tx.wait(); + }); + + it("Should buy successfully", async function () { + const maticAmount = ethers.utils.parseEther("0.1"); + const daiUnitAmount = ethers.utils.parseEther("4000"); + const maticAddress = constants.native_address; + + const getId = "0"; + const setId = "0"; + + const spells = [ + { + connector: connectorName, + method: "buy", + args: [maticAddress, DAI_ADDR, maticAmount, daiUnitAmount, getId, setId] + } + ]; + + const tx = await dsaWallet0.connect(wallet0).cast(...encodeSpells(spells), wallet1.address); + let receipt = await tx.wait(); + }); + }); +}); diff --git a/test/polygon/swap/swap-test.ts b/test/polygon/swap/swap-test.ts new file mode 100644 index 0000000..0e7cb9a --- /dev/null +++ b/test/polygon/swap/swap-test.ts @@ -0,0 +1,203 @@ +import hre from "hardhat"; +import axios from "axios"; +import { expect } from "chai"; +const { ethers } = hre; //check +import { BigNumber } from "bignumber.js"; +import { deployAndEnableConnector } from "../../../scripts/tests/deployAndEnableConnector"; +import { buildDSAv2 } from "../../../scripts/tests/buildDSAv2"; +import { encodeSpells } from "../../../scripts/tests/encodeSpells"; +import { getMasterSigner } from "../../../scripts/tests/getMasterSigner"; +import { addresses } from "../../../scripts/tests/polygon/addresses"; +import { abis } from "../../../scripts/constant/abis"; +import { ConnectV2SwapAggregatorPolygon__factory } from "../../../typechain"; +import er20abi from "../../../scripts/constant/abi/basics/erc20.json"; +import type { Signer, Contract } from "ethers"; + +describe("Swap", function () { + const connectorName = "swap-test"; + + let dsaWallet0: Contract; + let wallet0: Signer, wallet1: Signer; + let masterSigner: Signer; + let instaConnectorsV2: Contract; + let connector: Contract; + + before(async () => { + await hre.network.provider.request({ + method: "hardhat_reset", + params: [ + { + forking: { + // @ts-ignore + jsonRpcUrl: hre.config.networks.hardhat.forking.url + } + } + ] + }); + [wallet0, wallet1] = await ethers.getSigners(); + + masterSigner = await getMasterSigner(); + instaConnectorsV2 = await ethers.getContractAt(abis.core.connectorsV2, addresses.core.connectorsV2); + connector = await deployAndEnableConnector({ + connectorName, + contractArtifact: ConnectV2SwapAggregatorPolygon__factory, + signer: masterSigner, + connectors: instaConnectorsV2 + }); + console.log("Connector address", connector.address); + }); + + it("Should have contracts deployed.", async function () { + expect(!!instaConnectorsV2.address).to.be.true; + expect(!!connector.address).to.be.true; + expect(!!(await masterSigner.getAddress())).to.be.true; + }); + + describe("DSA wallet setup", function () { + it("Should build DSA v2", async function () { + dsaWallet0 = await buildDSAv2(await wallet0.getAddress()); + console.log(dsaWallet0.address); + expect(!!dsaWallet0.address).to.be.true; + }); + + it("Deposit matic into DSA wallet", async function () { + await wallet0.sendTransaction({ + to: dsaWallet0.address, + value: ethers.utils.parseEther("50") + }); + + expect(await ethers.provider.getBalance(dsaWallet0.address)).to.be.gte(ethers.utils.parseEther("10")); + }); + }); + + describe("Main", function () { + it("should swap the tokens", async function () { + let buyTokenAmountZeroX: any; + let buyTokenAmount1Inch: any; + let buyTokenAmountParaswap: any; + let unitAmount1Inch: any; + let calldata1Inch: any; + + async function getSelector(connector: string) { + var abi = [ + "function swap(address,address,uint256,uint256,bytes,uint256)", + "function sell(address,address,uint256,uint256,bytes,uint256)" + ]; + var iface = new ethers.utils.Interface(abi); + var id; + if (connector == "1INCH-A") { + id = iface.getSighash("sell"); + } else { + id = iface.getSighash("swap"); + } + + return id; + } + + async function getArg() { + // const slippage = 0.5; + /* matic -> usdt */ + const sellTokenAddress = "0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE"; // matic, decimals 18 + const sellTokenDecimals = 18; + const buyTokenAddress = "0xc2132d05d31c914a87c6611c10748aeb04b58e8f"; // USDT, decimals 6 + const buyTokenDecimals = 6; + const amount = 1; + + const srcAmount = new BigNumber(amount).times(new BigNumber(10).pow(sellTokenDecimals)).toFixed(0); + + let zeroXUrl = `https://polygon.api.0x.org/swap/v1/quote`; + let paraswapUrl1 = `https://apiv5.paraswap.io/prices/`; + let paraswapUrl2 = `https://apiv5.paraswap.io/transactions/137?ignoreChecks=true`; + + //zeroX + const paramsZeroX = { + buyToken: "USDT", + sellToken: "MATIC", + sellAmount: "1000000000000000000" // Always denominated in wei + }; + + const responseZeroX = await axios.get(zeroXUrl, { params: paramsZeroX }).then((data: any) => data); + buyTokenAmountZeroX = responseZeroX.data.buyAmount; + const calldataZeroX = responseZeroX.data.data; + + //paraswap + let paramsPara = { + srcToken: sellTokenAddress, + destToken: buyTokenAddress, + srcDecimals: sellTokenDecimals, + destDecimals: buyTokenDecimals, + amount: srcAmount, + side: "SELL", + network: 137 + }; + + const priceRoute = (await axios.get(paraswapUrl1, { params: paramsPara })).data.priceRoute; + buyTokenAmountParaswap = priceRoute.destAmount; + let minAmount = new BigNumber(priceRoute.destAmount).times((100 - 1) / 100).toFixed(0); + + let txConfig = { + priceRoute: priceRoute, + srcToken: sellTokenAddress, + destToken: buyTokenAddress, + srcDecimals: sellTokenDecimals, + destDecimals: buyTokenDecimals, + srcAmount: srcAmount, + destAmount: minAmount, + userAddress: dsaWallet0.address + }; + const calldataPara = (await axios.post(paraswapUrl2, txConfig)).data.data; + + let calculateUnitAmt = (buyAmount: any) => { + const buyTokenAmountRes = new BigNumber(buyAmount) + .dividedBy(new BigNumber(10).pow(buyTokenDecimals)) + .toFixed(8); + + let unitAmt: any = new BigNumber(buyTokenAmountRes).dividedBy(new BigNumber(amount)); + + unitAmt = unitAmt.multipliedBy((100 - 1) / 100); + unitAmt = unitAmt.multipliedBy(1e18).toFixed(0); + return unitAmt; + }; + let unitAmt0x = calculateUnitAmt(buyTokenAmountZeroX); + let unitAmtParaswap = calculateUnitAmt(buyTokenAmountParaswap); + + function getCallData(connector: string, unitAmt: any, callData: any) { + var abi = [ + "function swap(address,address,uint256,uint256,bytes,uint256)", + "function sell(address,address,uint256,uint256,bytes,uint256)" + ]; + var iface = new ethers.utils.Interface(abi); + const spell = connector === "1INCH-A" ? "sell" : "swap"; + let data = iface.encodeFunctionData(spell, [buyTokenAddress, sellTokenAddress, srcAmount, unitAmt, callData, 0]); + return data; + } + let dataPara = ethers.utils.hexlify(await getCallData("PARASWAP-A", unitAmtParaswap, calldataPara)); + let dataZeroX = ethers.utils.hexlify(await getCallData("ZEROX-A", unitAmt0x, calldataZeroX)); + let datas = [dataZeroX, dataPara]; + + let connectors = ["ZEROX-A", "PARASWAP-A"]; + + return [connectors, datas]; + } + + let arg = await getArg(); + const spells = [ + { + connector: connectorName, + method: "swap", + args: arg + } + ]; + const tx = await dsaWallet0.connect(wallet0).cast(...encodeSpells(spells), await wallet1.getAddress()); + const receipt = await tx.wait(); + + const usdtToken = await ethers.getContractAt( + er20abi, + "0xc2132d05d31c914a87c6611c10748aeb04b58e8f" // usdt address + ); + + expect(await usdtToken.balanceOf(dsaWallet0.address)).to.be.gte(buyTokenAmountZeroX); + expect(await ethers.provider.getBalance(dsaWallet0.address)).to.be.lte(ethers.utils.parseEther("9")); + }); + }); +}); diff --git a/test/polygon/uniswap/uniswap-swap.test.ts b/test/polygon/uniswap/uniswap-swap.test.ts new file mode 100644 index 0000000..29b480c --- /dev/null +++ b/test/polygon/uniswap/uniswap-swap.test.ts @@ -0,0 +1,139 @@ +import { expect } from "chai"; +import hre from "hardhat"; +const { web3, deployments, waffle, ethers } = hre; +const { provider, deployContract } = waffle; + +import { deployAndEnableConnector } from "../../../scripts/tests/deployAndEnableConnector"; +import { buildDSAv2 } from "../../../scripts/tests/buildDSAv2"; +import { encodeSpells } from "../../../scripts/tests/encodeSpells"; +import { getMasterSigner } from "../../../scripts/tests/getMasterSigner"; +import { addLiquidity } from "../../../scripts/tests/addLiquidity"; +import { addresses } from "../../../scripts/tests/polygon/addresses"; +import { abis } from "../../../scripts/constant/abis"; +import type { Signer, Contract } from "ethers"; + +import { abi } from "@uniswap/v3-periphery/artifacts/contracts/NonfungiblePositionManager.sol/NonfungiblePositionManager.json"; +import { ConnectV2UniswapV3SwapPolygon__factory } from "../../../typechain"; + +const FeeAmount = { + LOW: 500, + MEDIUM: 3000, + HIGH: 10000 +}; + +const TICK_SPACINGS: Record = { + 500: 10, + 3000: 60, + 10000: 200 +}; + +const DAI_ADDR = "0x8f3Cf7ad23Cd3CaDbD9735AFf958023239c6A063"; + +let tokenIds: any[] = []; +let liquidities: any[] = []; +const abiCoder = ethers.utils.defaultAbiCoder; + +describe("UniswapV3 [Polygon]", function () { + const connectorName = "UniswapV3-Swap-v1"; + + let dsaWallet0: any; + let masterSigner: Signer; + let instaConnectorsV2: Contract; + let connector: Contract; + let nftManager: Contract; + + const wallets = provider.getWallets(); + const [wallet0, wallet1, wallet2, wallet3] = wallets; + before(async () => { + await hre.network.provider.request({ + method: "hardhat_reset", + params: [ + { + forking: { + // @ts-ignore + jsonRpcUrl: hre.config.networks.hardhat.forking.url, + blockNumber: 27946900 + } + } + ] + }); + masterSigner = await getMasterSigner(); + instaConnectorsV2 = await ethers.getContractAt(abis.core.connectorsV2, addresses.core.connectorsV2); + nftManager = await ethers.getContractAt(abi, "0xC36442b4a4522E871399CD717aBDD847Ab11FE88"); + connector = await deployAndEnableConnector({ + connectorName, + contractArtifact: ConnectV2UniswapV3SwapPolygon__factory, + signer: masterSigner, + connectors: instaConnectorsV2 + }); + console.log("Connector address", connector.address); + }); + + it("Should have contracts deployed.", async function () { + expect(!!instaConnectorsV2.address).to.be.true; + expect(!!connector.address).to.be.true; + expect(!!(await masterSigner.getAddress())).to.be.true; + }); + + describe("DSA wallet setup", function () { + it("Should build DSA v2", async function () { + dsaWallet0 = await buildDSAv2(wallet0.address); + expect(!!dsaWallet0.address).to.be.true; + }); + + it("Deposit MATIC & DAI into DSA wallet", async function () { + await wallet0.sendTransaction({ + to: dsaWallet0.address, + value: ethers.utils.parseEther("10") + }); + expect(await ethers.provider.getBalance(dsaWallet0.address)).to.be.gte(ethers.utils.parseEther("10")); + + await addLiquidity("dai", dsaWallet0.address, ethers.utils.parseEther("100000")); + }); + }); + + describe("Main", function () { + it("Should buy successfully", async function () { + const maticAmount = ethers.utils.parseUnits("0.1", 18); + const unitAmt = ethers.utils.parseEther("1.21"); + const maticAddress = "0xeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee"; + const getId = "0"; + const setId = "0"; + + const spells = [ + { + connector: connectorName, + method: "buy", + args: [maticAddress, DAI_ADDR, FeeAmount.MEDIUM, unitAmt, maticAmount, getId, setId] + } + ]; + + const tx = await dsaWallet0.connect(wallet0).cast(...encodeSpells(spells), wallet1.address); + const receipt = await tx.wait(); + // console.log(receipt); + }); + + it("Should sell successfully", async function () { + const maticAmount = ethers.utils.parseUnits("0.1", 18); + const unitAmt = ethers.utils.parseEther("1.093"); + const maticAddress = "0xeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee"; + const getId = "0"; + const setId = "0"; + + const spells = [ + { + connector: connectorName, + method: "sell", + args: [DAI_ADDR, maticAddress, FeeAmount.MEDIUM, unitAmt, maticAmount, getId, setId] + } + ]; + + const tx = await dsaWallet0.connect(wallet0).cast(...encodeSpells(spells), wallet1.address); + const receipt = await tx.wait(); + // console.log(receipt); + }); + }); +}); + +const getMinTick = (tickSpacing: number) => Math.ceil(-887272 / tickSpacing) * tickSpacing; +const getMaxTick = (tickSpacing: number) => Math.floor(887272 / tickSpacing) * tickSpacing; diff --git a/test/polygon/uniswapV3Router/uniswapV3Router.test.ts b/test/polygon/uniswapV3Router/uniswapV3Router.test.ts new file mode 100644 index 0000000..94e27a0 --- /dev/null +++ b/test/polygon/uniswapV3Router/uniswapV3Router.test.ts @@ -0,0 +1,156 @@ +import hre from "hardhat"; +import { expect } from "chai"; +const { ethers } = hre; //check +import { BigNumber } from "bignumber.js"; +import { deployAndEnableConnector } from "../../../scripts/tests/deployAndEnableConnector"; +import { buildDSAv2 } from "../../../scripts/tests/buildDSAv2"; +import { encodeSpells } from "../../../scripts/tests/encodeSpells"; +import { getMasterSigner } from "../../../scripts/tests/getMasterSigner"; +import { addresses } from "../../../scripts/tests/polygon/addresses"; +import { addLiquidity } from "../../../scripts/tests/addLiquidity"; +import { abis } from "../../../scripts/constant/abis"; +import { ConnectV2UniswapV3AutoRouterPolygon__factory } from "../../../typechain"; +import er20abi from "../../../scripts/constant/abi/basics/erc20.json"; +import type { Signer, Contract } from "ethers"; +import { CurrencyAmount, Token, TradeType, Currency, Percent } from "@uniswap/sdk-core"; +import { AlphaRouter } from "@uniswap/smart-order-router"; + +describe("Auto Router", function () { + const connectorName = "Auto-Router-test"; + + let dsaWallet0: Contract; + let wallet0: Signer, wallet1: Signer; + let masterSigner: Signer; + let instaConnectorsV2: Contract; + let connector: Contract; + + // @ts-ignore + const provider = new ethers.providers.JsonRpcProvider(hre.config.networks.hardhat.forking.url); + const router = new AlphaRouter({ chainId: 137, provider }); + + before(async () => { + await hre.network.provider.request({ + method: "hardhat_reset", + params: [ + { + forking: { + // @ts-ignore + jsonRpcUrl: hre.config.networks.hardhat.forking.url + } + } + ] + }); + [wallet0, wallet1] = await ethers.getSigners(); + + masterSigner = await getMasterSigner(); + instaConnectorsV2 = await ethers.getContractAt(abis.core.connectorsV2, addresses.core.connectorsV2); + connector = await deployAndEnableConnector({ + connectorName, + contractArtifact: ConnectV2UniswapV3AutoRouterPolygon__factory, + signer: masterSigner, + connectors: instaConnectorsV2 + }); + console.log("Connector address", connector.address); + }); + + it("Should have contracts deployed.", async function () { + expect(!!instaConnectorsV2.address).to.be.true; + expect(!!connector.address).to.be.true; + expect(!!(await masterSigner.getAddress())).to.be.true; + }); + + describe("DSA wallet setup", function () { + it("Should build DSA v2", async function () { + dsaWallet0 = await buildDSAv2(await wallet0.getAddress()); + expect(!!dsaWallet0.address).to.be.true; + }); + + it("Deposit Matic into DSA wallet", async function () { + await wallet0.sendTransaction({ + to: dsaWallet0.address, + value: ethers.utils.parseEther("10") + }); + + expect(await ethers.provider.getBalance(dsaWallet0.address)).to.be.gte(ethers.utils.parseEther("10")); + }); + }); + + describe("Main", function () { + + it("should swap the tokens ", async function () { + const buyTokenAddress = "0x8f3cf7ad23cd3cadbd9735aff958023239c6a063"; //dai + const sellTokenAddress = "0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE"; //matic + const sellTokenDecimals = 18; + const buyTokenDecimals = 18; + const amount = 1; + + const wmaticAddr = "0x0d500b1d8e8ef31e21c99d1db9a6444d3adf1270"; + + const srcAmount = new BigNumber(amount).times(new BigNumber(10).pow(sellTokenDecimals)).toFixed(0); + const sellToken = new Token(137, wmaticAddr, sellTokenDecimals); + const buyToken = new Token(137, buyTokenAddress, buyTokenDecimals); + const sellAmount = CurrencyAmount.fromRawAmount(sellToken, srcAmount); + + const deadline = 1696000000 // Fri Sep 29 2023 15:06:40 GMT+0000 + const route = await router.route(sellAmount, buyToken, TradeType.EXACT_INPUT, { + recipient: dsaWallet0.address, + slippageTolerance: new Percent(5, 100), + deadline + }); + + const calldata = route?.methodParameters?.calldata; + + const _buyAmount = route?.quote.toFixed(); + const buyTokenAmount = new BigNumber(String(_buyAmount)).times(new BigNumber(10).pow(buyTokenDecimals)).toFixed(0); + + + function caculateUnitAmt( + buyAmount: any, + sellAmount: any, + buyDecimal: any, + sellDecimal: any, + maxSlippage: any + ) { + let unitAmt: any; + unitAmt = new BigNumber(buyAmount) + .dividedBy(10 ** buyDecimal) + .dividedBy(new BigNumber(sellAmount).dividedBy(10 ** sellDecimal)); + unitAmt = unitAmt.multipliedBy((100 - maxSlippage) / 100); + unitAmt = unitAmt.multipliedBy(1e18).toFixed(0); + return unitAmt; + } + + const unitAmt = caculateUnitAmt( + buyTokenAmount, + srcAmount, + buyTokenDecimals, + sellTokenDecimals, + 1 + ); + + const spells = [ + { + connector: connectorName, + method: "sell", + args: [buyTokenAddress, sellTokenAddress, srcAmount, unitAmt, calldata, 0] + } + ]; + + const buyTokenContract = await ethers.getContractAt( + er20abi, + buyTokenAddress, + ); + + const initialBuyTokenBalance = await buyTokenContract.balanceOf(dsaWallet0.address) + + const tx = await dsaWallet0.connect(wallet0).cast(...encodeSpells(spells), await wallet1.getAddress()); + const receipt = await tx.wait(); + + const finalBuyTokenBalance = await buyTokenContract.balanceOf(dsaWallet0.address) + + + expect(finalBuyTokenBalance).to.be.gt(initialBuyTokenBalance); + }); + + }); +}); 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 + } +}