diff --git a/package.json b/package.json index 098999d0b8..9478b2170f 100644 --- a/package.json +++ b/package.json @@ -98,8 +98,9 @@ "@sushiswap/bentobox-sdk": "1.0.0-canary.119", "@sushiswap/chainlink-whitelist": "0.2.8", "@sushiswap/core": "^2.0.0-canary.1", - "@sushiswap/core-sdk": "1.0.0-canary.138", - "@sushiswap/kashi-sdk": "1.0.0-canary.110", + "@sushiswap/core-sdk": "1.0.0-canary.133", + "@sushiswap/default-token-list": "^31.1.0", + "@sushiswap/kashi-sdk": "1.0.0-canary.105", "@sushiswap/limit-order-pair-list": "0.2.17", "@sushiswap/limit-order-sdk": "1.0.0-canary.119", "@sushiswap/miso": "1.0.0-canary.47", @@ -178,6 +179,7 @@ "lottie-react": "2.1.0", "madge": "^5.0.1", "millify": "^4.0.0", + "moralis": "^1.7.0", "ms.macro": "^2.0.0", "next": "^12.3.1", "next-pwa": "5.4.7", diff --git a/sentry.client.config.js b/sentry.client.config.js index d6f0b5706c..2b45cac007 100644 --- a/sentry.client.config.js +++ b/sentry.client.config.js @@ -2,7 +2,7 @@ // The config you add here will be used whenever a page is visited. // https://docs.sentry.io/platforms/javascript/guides/nextjs/ -import * as Sentry from '@sentry/nextjs'; +import * as Sentry from '@sentry/nextjs' const SENTRY_DSN = process.env.SENTRY_DSN || process.env.NEXT_PUBLIC_SENTRY_DSN diff --git a/sentry.server.config.js b/sentry.server.config.js index 8f7d077fd9..75d73dcf5b 100644 --- a/sentry.server.config.js +++ b/sentry.server.config.js @@ -12,4 +12,4 @@ Sentry.init({ // Note: if you want to override the automatic release value, do not set a // `release` value here - use the environment variable `SENTRY_RELEASE`, so // that it will also get attached to your source maps -}) \ No newline at end of file +}) diff --git a/src/components/Header/index.tsx b/src/components/Header/index.tsx index 331e339b12..d97342d167 100644 --- a/src/components/Header/index.tsx +++ b/src/components/Header/index.tsx @@ -7,11 +7,7 @@ import Desktop from './Desktop' const Header: FC = () => { const isDesktop = useDesktopMediaQuery() - return ( - <> - {isDesktop ? : } - - ) + return <>{isDesktop ? : } } export default Header diff --git a/src/components/Header/useMenu.tsx b/src/components/Header/useMenu.tsx index 39618f66c0..3bbf30d6c6 100644 --- a/src/components/Header/useMenu.tsx +++ b/src/components/Header/useMenu.tsx @@ -47,6 +47,12 @@ const useMenu: UseMenu = () => { link: '/limit-order', disabled: !featureEnabled(Feature.LIMIT_ORDERS, chainId), }, + { + key: 'stop', + title: i18n._(t`Stop loss`), + link: '/stop-loss', + disabled: !featureEnabled(Feature.STOP_LOSSES, chainId), + }, ] const liquidity = [ @@ -95,6 +101,12 @@ const useMenu: UseMenu = () => { link: '/limit-order', disabled: !featureEnabled(Feature.LIMIT_ORDERS, chainId), }, + { + key: 'stop', + title: i18n._(t`Stop loss`), + link: '/stop-loss', + disabled: !featureEnabled(Feature.STOP_LOSSES, chainId), + }, ].filter((item) => !item.disabled), }) diff --git a/src/config/features.ts b/src/config/features.ts index a8a11df8fe..41989d76b4 100644 --- a/src/config/features.ts +++ b/src/config/features.ts @@ -49,6 +49,7 @@ const features: FeatureMap = { Feature.KASHI, Feature.ANALYTICS, Feature.LIMIT_ORDERS, + Feature.STOP_LOSSES, Feature.TRIDENT, Feature.TRIDENT_MIGRATION, Feature.MISO, @@ -62,6 +63,7 @@ const features: FeatureMap = { Feature.BENTOBOX, Feature.KASHI, Feature.LIMIT_ORDERS, + Feature.STOP_LOSSES, Feature.ANALYTICS, Feature.MISO, Feature.SUBGRAPH, diff --git a/src/config/index.ts b/src/config/index.ts index fc9175e72d..f0205d8c99 100644 --- a/src/config/index.ts +++ b/src/config/index.ts @@ -5,36 +5,36 @@ const config = { defaultChainId: ChainId.ETHEREUM, blockedAddresses: [ // SDN OFAC addresses - 08.11.2022 - "0x098B716B8Aaf21512996dC57EB0615e2383E2f96", - "0xa0e1c89Ef1a489c9C7dE96311eD5Ce5D32c20E4B", - "0x3Cffd56B47B7b41c56258D9C7731ABaDc360E073", - "0x53b6936513e738f44FB50d2b9476730C0Ab3Bfc1", - "0x35fB6f6DB4fb05e6A4cE86f2C93691425626d4b1", - "0xF7B31119c2682c88d88D455dBb9d5932c65Cf1bE", - "0x3e37627dEAA754090fBFbb8bd226c1CE66D255e9", - "0x08723392Ed15743cc38513C4925f5e6be5c17243", - "0x7F367cC41522cE07553e823bf3be79A889DEbe1B", - "0xd882cfc20f52f2599d84b8e8d58c7fb62cfe344b", - "0x901bb9583b24d97e995513c6778dc6888ab6870e", - "0xa7e5d5a720f06526557c513402f2e6b5fa20b008", - "0x8576acc5c05d6ce88f4e49bf65bdf0c62f91353c", - "0x1da5821544e25c636c1417ba96ade4cf6d2f9b5a", - "0x7Db418b5D567A4e0E8c59Ad71BE1FcE48f3E6107", - "0x72a5843cc08275C8171E582972Aa4fDa8C397B2A", - "0x7F19720A857F834887FC9A7bC0a0fBe7Fc7f8102", - "0x9f4cda013e354b8fc285bf4b9a60460cee7f7ea9", - "0x3cbded43efdaf0fc77b9c55f6fc9988fcc9b757d", - "0x2f389ce8bd8ff92de3402ffce4691d17fc4f6535", - "0x19aa5fe80d33a56d56c78e82ea5e50e5d80b4dff", - "0xe7aa314c77f4233c18c6cc84384a9247c0cf367b", - "0x308ed4b7b49797e1a98d3818bff6fe5385410370", - "0x67d40EE1A85bf4a4Bb7Ffae16De985e8427B6b45", - "0x6f1ca141a28907f78ebaa64fb83a9088b02a8352", - "0x6acdfba02d390b97ac2b2d42a63e85293bcc160e", - "0x48549a34ae37b12f6a30566245176994e17c6b4a", - "0x5512d943ed1f7c8a43f3435c85f7ab68b30121b0", - "0xc455f7fd3e0e12afd51fba5c106909934d8a0e4a", - "0x7FF9cFad3877F21d41Da833E2F775dB0569eE3D9" + '0x098B716B8Aaf21512996dC57EB0615e2383E2f96', + '0xa0e1c89Ef1a489c9C7dE96311eD5Ce5D32c20E4B', + '0x3Cffd56B47B7b41c56258D9C7731ABaDc360E073', + '0x53b6936513e738f44FB50d2b9476730C0Ab3Bfc1', + '0x35fB6f6DB4fb05e6A4cE86f2C93691425626d4b1', + '0xF7B31119c2682c88d88D455dBb9d5932c65Cf1bE', + '0x3e37627dEAA754090fBFbb8bd226c1CE66D255e9', + '0x08723392Ed15743cc38513C4925f5e6be5c17243', + '0x7F367cC41522cE07553e823bf3be79A889DEbe1B', + '0xd882cfc20f52f2599d84b8e8d58c7fb62cfe344b', + '0x901bb9583b24d97e995513c6778dc6888ab6870e', + '0xa7e5d5a720f06526557c513402f2e6b5fa20b008', + '0x8576acc5c05d6ce88f4e49bf65bdf0c62f91353c', + '0x1da5821544e25c636c1417ba96ade4cf6d2f9b5a', + '0x7Db418b5D567A4e0E8c59Ad71BE1FcE48f3E6107', + '0x72a5843cc08275C8171E582972Aa4fDa8C397B2A', + '0x7F19720A857F834887FC9A7bC0a0fBe7Fc7f8102', + '0x9f4cda013e354b8fc285bf4b9a60460cee7f7ea9', + '0x3cbded43efdaf0fc77b9c55f6fc9988fcc9b757d', + '0x2f389ce8bd8ff92de3402ffce4691d17fc4f6535', + '0x19aa5fe80d33a56d56c78e82ea5e50e5d80b4dff', + '0xe7aa314c77f4233c18c6cc84384a9247c0cf367b', + '0x308ed4b7b49797e1a98d3818bff6fe5385410370', + '0x67d40EE1A85bf4a4Bb7Ffae16De985e8427B6b45', + '0x6f1ca141a28907f78ebaa64fb83a9088b02a8352', + '0x6acdfba02d390b97ac2b2d42a63e85293bcc160e', + '0x48549a34ae37b12f6a30566245176994e17c6b4a', + '0x5512d943ed1f7c8a43f3435c85f7ab68b30121b0', + '0xc455f7fd3e0e12afd51fba5c106909934d8a0e4a', + '0x7FF9cFad3877F21d41Da833E2F775dB0569eE3D9', ], // Network specific configuration [ChainId.ETHEREUM]: { diff --git a/src/constants/abis/autonomy/registry.json b/src/constants/abis/autonomy/registry.json new file mode 100644 index 0000000000..4a602cfa5f --- /dev/null +++ b/src/constants/abis/autonomy/registry.json @@ -0,0 +1 @@ +[{"inputs":[{"internalType":"contract IStakeManager","name":"stakeMan","type":"address"},{"internalType":"contract IOracle","name":"oracle","type":"address"},{"internalType":"contract IForwarder","name":"userForwarder","type":"address"},{"internalType":"contract IForwarder","name":"gasForwarder","type":"address"},{"internalType":"contract IForwarder","name":"userGasForwarder","type":"address"},{"internalType":"string","name":"tokenName","type":"string"},{"internalType":"string","name":"tokenSymbol","type":"string"},{"internalType":"uint256","name":"totalAUTOSupply","type":"uint256"}],"stateMutability":"nonpayable","type":"constructor"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint256","name":"id","type":"uint256"},{"indexed":true,"internalType":"address","name":"user","type":"address"},{"indexed":false,"internalType":"address","name":"target","type":"address"},{"indexed":false,"internalType":"address payable","name":"referer","type":"address"},{"indexed":false,"internalType":"bytes","name":"callData","type":"bytes"},{"indexed":false,"internalType":"uint112","name":"initEthSent","type":"uint112"},{"indexed":false,"internalType":"uint112","name":"ethForCall","type":"uint112"},{"indexed":false,"internalType":"bool","name":"verifyUser","type":"bool"},{"indexed":false,"internalType":"bool","name":"insertFeeAmount","type":"bool"},{"indexed":false,"internalType":"bool","name":"payWithAUTO","type":"bool"},{"indexed":false,"internalType":"bool","name":"isAlive","type":"bool"}],"name":"HashedReqAdded","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint256","name":"id","type":"uint256"}],"name":"HashedReqCancelled","type":"event"},{"anonymous":false,"inputs":[{"indexed":true,"internalType":"uint256","name":"id","type":"uint256"},{"indexed":false,"internalType":"bool","name":"wasRemoved","type":"bool"}],"name":"HashedReqExecuted","type":"event"},{"inputs":[],"name":"BASE_BPS","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"GAS_OVERHEAD_AUTO","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"GAS_OVERHEAD_ETH","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"PAY_AUTO_BPS","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"PAY_ETH_BPS","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"id","type":"uint256"},{"components":[{"internalType":"address payable","name":"user","type":"address"},{"internalType":"address","name":"target","type":"address"},{"internalType":"address payable","name":"referer","type":"address"},{"internalType":"bytes","name":"callData","type":"bytes"},{"internalType":"uint112","name":"initEthSent","type":"uint112"},{"internalType":"uint112","name":"ethForCall","type":"uint112"},{"internalType":"bool","name":"verifyUser","type":"bool"},{"internalType":"bool","name":"insertFeeAmount","type":"bool"},{"internalType":"bool","name":"payWithAUTO","type":"bool"},{"internalType":"bool","name":"isAlive","type":"bool"}],"internalType":"struct IRegistry.Request","name":"r","type":"tuple"}],"name":"cancelHashedReq","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[{"internalType":"uint256","name":"id","type":"uint256"},{"components":[{"internalType":"address payable","name":"user","type":"address"},{"internalType":"address","name":"target","type":"address"},{"internalType":"address payable","name":"referer","type":"address"},{"internalType":"bytes","name":"callData","type":"bytes"},{"internalType":"uint112","name":"initEthSent","type":"uint112"},{"internalType":"uint112","name":"ethForCall","type":"uint112"},{"internalType":"bool","name":"verifyUser","type":"bool"},{"internalType":"bool","name":"insertFeeAmount","type":"bool"},{"internalType":"bool","name":"payWithAUTO","type":"bool"},{"internalType":"bool","name":"isAlive","type":"bool"}],"internalType":"struct IRegistry.Request","name":"r","type":"tuple"},{"internalType":"uint256","name":"expectedGas","type":"uint256"}],"name":"executeHashedReq","outputs":[{"internalType":"uint256","name":"gasUsed","type":"uint256"}],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"getAUTOAddr","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"addr","type":"address"}],"name":"getExecCountOf","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getGasForwarder","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"id","type":"uint256"}],"name":"getHashedReq","outputs":[{"internalType":"bytes32","name":"","type":"bytes32"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getHashedReqs","outputs":[{"internalType":"bytes32[]","name":"","type":"bytes32[]"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getHashedReqsLen","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"startIdx","type":"uint256"},{"internalType":"uint256","name":"endIdx","type":"uint256"}],"name":"getHashedReqsSlice","outputs":[{"internalType":"bytes32[]","name":"","type":"bytes32[]"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getOracle","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"address","name":"addr","type":"address"}],"name":"getReferalCountOf","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[{"components":[{"internalType":"address payable","name":"user","type":"address"},{"internalType":"address","name":"target","type":"address"},{"internalType":"address payable","name":"referer","type":"address"},{"internalType":"bytes","name":"callData","type":"bytes"},{"internalType":"uint112","name":"initEthSent","type":"uint112"},{"internalType":"uint112","name":"ethForCall","type":"uint112"},{"internalType":"bool","name":"verifyUser","type":"bool"},{"internalType":"bool","name":"insertFeeAmount","type":"bool"},{"internalType":"bool","name":"payWithAUTO","type":"bool"},{"internalType":"bool","name":"isAlive","type":"bool"}],"internalType":"struct IRegistry.Request","name":"r","type":"tuple"}],"name":"getReqBytes","outputs":[{"internalType":"bytes","name":"","type":"bytes"}],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"address","name":"addr","type":"address"}],"name":"getReqCountOf","outputs":[{"internalType":"uint256","name":"","type":"uint256"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getStakeManager","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getUserForwarder","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"getUserGasForwarder","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"bytes","name":"callData","type":"bytes"},{"internalType":"uint256","name":"expectedGas","type":"uint256"},{"internalType":"uint256","name":"startIdx","type":"uint256"}],"name":"insertToCallData","outputs":[{"internalType":"bytes","name":"","type":"bytes"}],"stateMutability":"pure","type":"function"},{"inputs":[{"internalType":"address","name":"target","type":"address"},{"internalType":"address payable","name":"referer","type":"address"},{"internalType":"bytes","name":"callData","type":"bytes"},{"internalType":"uint112","name":"ethForCall","type":"uint112"},{"internalType":"bool","name":"verifyUser","type":"bool"},{"internalType":"bool","name":"insertFeeAmount","type":"bool"},{"internalType":"bool","name":"isAlive","type":"bool"}],"name":"newReq","outputs":[{"internalType":"uint256","name":"id","type":"uint256"}],"stateMutability":"payable","type":"function"},{"inputs":[{"internalType":"address","name":"target","type":"address"},{"internalType":"address payable","name":"referer","type":"address"},{"internalType":"bytes","name":"callData","type":"bytes"},{"internalType":"uint112","name":"ethForCall","type":"uint112"},{"internalType":"bool","name":"verifyUser","type":"bool"},{"internalType":"bool","name":"insertFeeAmount","type":"bool"},{"internalType":"bool","name":"payWithAUTO","type":"bool"},{"internalType":"bool","name":"isAlive","type":"bool"}],"name":"newReqPaySpecific","outputs":[{"internalType":"uint256","name":"id","type":"uint256"}],"stateMutability":"payable","type":"function"},{"stateMutability":"payable","type":"receive"}] \ No newline at end of file diff --git a/src/constants/abis/autonomy/stop-limit-order-wrapper.json b/src/constants/abis/autonomy/stop-limit-order-wrapper.json new file mode 100644 index 0000000000..b4a98f999c --- /dev/null +++ b/src/constants/abis/autonomy/stop-limit-order-wrapper.json @@ -0,0 +1 @@ +[{"inputs":[{"internalType":"address payable","name":"registry_","type":"address"},{"internalType":"address","name":"gasFeeForwarder_","type":"address"},{"internalType":"address","name":"bentoBox_","type":"address"},{"internalType":"address","name":"stopLimitOrderContract_","type":"address"},{"internalType":"address","name":"WETH_","type":"address"}],"stateMutability":"nonpayable","type":"constructor"},{"inputs":[],"name":"WETH","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"bentoBox","outputs":[{"internalType":"contract IBentoBoxV1","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[{"internalType":"uint256","name":"feeAmount","type":"uint256"},{"components":[{"internalType":"address","name":"maker","type":"address"},{"internalType":"uint256","name":"amountIn","type":"uint256"},{"internalType":"uint256","name":"amountOut","type":"uint256"},{"internalType":"address","name":"recipient","type":"address"},{"internalType":"uint256","name":"startTime","type":"uint256"},{"internalType":"uint256","name":"endTime","type":"uint256"},{"internalType":"uint256","name":"stopPrice","type":"uint256"},{"internalType":"contract IOracle","name":"oracleAddress","type":"address"},{"internalType":"bytes","name":"oracleData","type":"bytes"},{"internalType":"uint256","name":"amountToFill","type":"uint256"},{"internalType":"uint8","name":"v","type":"uint8"},{"internalType":"bytes32","name":"r","type":"bytes32"},{"internalType":"bytes32","name":"s","type":"bytes32"}],"internalType":"struct OrderArgs","name":"order","type":"tuple"},{"internalType":"address","name":"tokenIn","type":"address"},{"internalType":"address","name":"tokenOut","type":"address"},{"internalType":"address","name":"receiver","type":"address"},{"internalType":"bytes","name":"data","type":"bytes"}],"name":"fillOrder","outputs":[],"stateMutability":"nonpayable","type":"function"},{"inputs":[],"name":"gasFeeForwarder","outputs":[{"internalType":"address","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"registry","outputs":[{"internalType":"address payable","name":"","type":"address"}],"stateMutability":"view","type":"function"},{"inputs":[],"name":"stopLimitOrderContract","outputs":[{"internalType":"contract IStopLimitOrder","name":"","type":"address"}],"stateMutability":"view","type":"function"}] \ No newline at end of file diff --git a/src/constants/autonomy.ts b/src/constants/autonomy.ts new file mode 100644 index 0000000000..da47bd4083 --- /dev/null +++ b/src/constants/autonomy.ts @@ -0,0 +1,65 @@ +import { AddressMap, ChainId } from '@sushiswap/core-sdk' + +export const QUERY_REQUEST_LIMIT = 10000 + +export const AUTONOMY_REGISTRY_ADDRESSES: AddressMap = { + [ChainId.ETHEREUM]: '0x973107d4b9A5B69fd99c23a3C31eFA8fafE7Ae38', + [ChainId.FANTOM]: '0x6e5Ec7f4C98B34e0aAAA02D8D2136e626ED33B10', + [ChainId.BSC]: '0x18d087F8D22D409D3CD366AF00BD7AeF0BF225Db', + [ChainId.AVALANCHE]: '0x68FCbECa74A7E5D386f74E14682c94DE0e1bC56b', + [ChainId.MATIC]: '0x18d02301E534cab22267460eD8fBdf2B8382A3ff', +} + +export const STOP_LIMIT_ORDER_WRAPPER_ADDRESSES: AddressMap = { + [ChainId.ETHEREUM]: '0xeE1e77774225Fc9c1a4B5B32E0029c1768A9f338', + [ChainId.FANTOM]: '0xe2952F019d317d9e9FadaeeD4F61dF5879295c17', + [ChainId.AVALANCHE]: '0x2f57dabe3dd1ecAed3F79a0D6d2530fCa5A11912', + [ChainId.MATIC]: '0x849F9303AC8fb345e3D07c78A6795d1989d9CE16', +} + +export const CHAINLINK_ORACLE_ADDRESS: AddressMap = { + [ChainId.ETHEREUM]: '0x00632CFe43d8F9f8E6cD0d39Ffa3D4fa7ec73CFB', + [ChainId.FANTOM]: '0x4a8C72c1e443d2199D7D65D8EAE6cA7ac1E58226', + [ChainId.BSC]: '0x00632CFe43d8F9f8E6cD0d39Ffa3D4fa7ec73CFB', + [ChainId.AVALANCHE]: '0x232d595594585613F48aaE9c85861E4aB06CE3E5', + [ChainId.MATIC]: '0x4455AbEc4E3310F5Ba427D4Dd49e590c2A27f7d5', +} + +type FeeAmountOfEthMap = { + [chainId: number]: string +} +// it defines minimum amount of fee(ETH unit) that Autonomy wrapper contract charges when to execute orders. +export const STOP_LIMIT_ORDER_WRAPPER_FEE_MINIMUM: FeeAmountOfEthMap = { + [ChainId.ETHEREUM]: '0.007', // 0.007 ETH + [ChainId.FANTOM]: '0.1', // 0.1 FTM + [ChainId.AVALANCHE]: '0.025', // 0.025 AVAX + [ChainId.MATIC]: '0.03', // 0.03 MATIC +} + +interface MoralisInfo { + serverURL: string + key: string +} + +export const MORALIS_INFO: { [chainId: number]: MoralisInfo } = { + [ChainId.ETHEREUM]: { + serverURL: process.env.NEXT_PUBLIC_AUTONOMY_MORALIS_URL_ETH || '', + key: process.env.NEXT_PUBLIC_AUTONOMY_MORALIS_KEY_ETH || '', + }, + [ChainId.FANTOM]: { + serverURL: process.env.NEXT_PUBLIC_AUTONOMY_MORALIS_URL_FTM || '', + key: process.env.NEXT_PUBLIC_AUTONOMY_MORALIS_KEY_FTM || '', + }, + [ChainId.AVALANCHE]: { + serverURL: process.env.NEXT_PUBLIC_AUTONOMY_MORALIS_URL_AVAX || '', + key: process.env.NEXT_PUBLIC_AUTONOMY_MORALIS_KEY_AVAX || '', + }, + [ChainId.MATIC]: { + serverURL: process.env.NEXT_PUBLIC_AUTONOMY_MORALIS_URL_MATIC || '', + key: process.env.NEXT_PUBLIC_AUTONOMY_MORALIS_KEY_MATIC || '', + }, + [ChainId.BSC]: { + serverURL: process.env.NEXT_PUBLIC_AUTONOMY_MORALIS_URL_BSC || '', + key: process.env.NEXT_PUBLIC_AUTONOMY_MORALIS_KEY_BSC || '', + }, +} diff --git a/src/enums/Feature.ts b/src/enums/Feature.ts index 2da94223dc..f13aeb0b18 100644 --- a/src/enums/Feature.ts +++ b/src/enums/Feature.ts @@ -11,6 +11,7 @@ export enum Feature { TRIDENT = 'Trident', TRIDENT_MIGRATION = 'Trident Migration', LIMIT_ORDERS = 'LimitOrders', + STOP_LOSSES = 'StopLosses', INARI = 'Inari', MEOWSHI = 'MEOWSHI', VESTING = 'Vesting', diff --git a/src/features/stop-loss/CompletedOrders.tsx b/src/features/stop-loss/CompletedOrders.tsx new file mode 100644 index 0000000000..dab4af8f52 --- /dev/null +++ b/src/features/stop-loss/CompletedOrders.tsx @@ -0,0 +1,128 @@ +import { t } from '@lingui/macro' +import { useLingui } from '@lingui/react' +import Pagination from 'app/components/Pagination' +import Typography from 'app/components/Typography' +import { useCompletedOrdersTableConfig } from 'app/features/stop-loss/useCompletedOrdersTableConfig' +import { + TABLE_TABLE_CLASSNAME, + TABLE_TBODY_TD_CLASSNAME, + TABLE_TBODY_TR_CLASSNAME, + TABLE_TR_TH_CLASSNAME, + TABLE_WRAPPER_DIV_CLASSNAME, +} from 'app/features/trident/constants' +import { classNames } from 'app/functions' +import { useActiveWeb3React } from 'app/services/web3' +import Link from 'next/link' +import React, { FC } from 'react' +import { useFlexLayout, usePagination, useSortBy, useTable } from 'react-table' + +import useStopLossOrders from './useStopLossOrders' + +const CompletedOrders: FC = () => { + const { i18n } = useLingui() + const { account } = useActiveWeb3React() + const { loading, completed, setCompletedPage } = useStopLossOrders() + const { config } = useCompletedOrdersTableConfig({ orders: completed.data }) + + // @ts-ignore TYPE NEEDS FIXING + const { getTableProps, getTableBodyProps, headerGroups, prepareRow, page } = useTable( + // @ts-ignore TYPE NEEDS FIXING + config, + useSortBy, + usePagination, + useFlexLayout + ) + + return ( +
+
+ + + {headerGroups.map((headerGroup, i) => ( + + {headerGroup.headers.map((column, i) => ( + + ))} + + ))} + + + {page.length > 0 ? ( + // @ts-ignore TYPE NEEDS FIXING + page.map((row, i) => { + prepareRow(row) + return ( + + {/*@ts-ignore TYPE NEEDS FIXING*/} + {row.cells.map((cell, i) => { + return ( + + ) + })} + + ) + }) + ) : ( + + + + )} + + + + + + +
+ {column.render('Header')} +
+ {cell.render('Cell')} +
+ + {loading ? i18n._(t`Loading executed orders...`) : i18n._(t`No order history`)} + +
+ + Funds will be received in your{' '} + + + BentoBox + + {' '} + after order execution + +
+
+ + 1} + canNextPage={completed.page < completed.maxPages} + onChange={(page) => setCompletedPage(page + 1)} + totalPages={completed.maxPages} + currentPage={completed.page - 1} + pageNeighbours={1} + /> +
+ ) +} + +export default CompletedOrders diff --git a/src/features/stop-loss/DiscoverHeader.tsx b/src/features/stop-loss/DiscoverHeader.tsx new file mode 100644 index 0000000000..2af38e7ca1 --- /dev/null +++ b/src/features/stop-loss/DiscoverHeader.tsx @@ -0,0 +1,33 @@ +import { t } from '@lingui/macro' +import { useLingui } from '@lingui/react' +import Button from 'app/components/Button' +import Typography from 'app/components/Typography' +import { TridentHeader } from 'app/layouts/Trident' +import Link from 'next/link' +import React from 'react' + +const DiscoverHeader = () => { + const { i18n } = useLingui() + + return ( + +
+ + {i18n._(t`Stop Loss Orders`)} + + + {i18n._(t`Place a stop loss order or check the status of your past orders`)} + +
+
+ + + +
+
+ ) +} + +export default DiscoverHeader diff --git a/src/features/stop-loss/LimitPriceInputPanel.tsx b/src/features/stop-loss/LimitPriceInputPanel.tsx new file mode 100644 index 0000000000..9401ffc5c6 --- /dev/null +++ b/src/features/stop-loss/LimitPriceInputPanel.tsx @@ -0,0 +1,70 @@ +import { t } from '@lingui/macro' +import { useLingui } from '@lingui/react' +import { Currency, Price, Trade, TradeType } from '@sushiswap/core-sdk' +import Input from 'app/components/Input' +import QuestionHelper from 'app/components/QuestionHelper' +import Typography from 'app/components/Typography' +import { useAppDispatch } from 'app/state/hooks' +import { LimitPrice, setLimitOrderInvertState, setLimitPrice } from 'app/state/limit-order/actions' +import useLimitOrderDerivedCurrencies, { useLimitOrderState } from 'app/state/limit-order/hooks' +import React, { FC } from 'react' + +interface LimitPriceInputPanel { + trade?: Trade + limitPrice?: Price +} + +const LimitPriceInputPanel: FC = ({ trade, limitPrice }) => { + const { i18n } = useLingui() + const dispatch = useAppDispatch() + const { limitPrice: limitPriceString, invertRate } = useLimitOrderState() + const { inputCurrency, outputCurrency } = useLimitOrderDerivedCurrencies() + const disabled = !inputCurrency || !outputCurrency + + return ( +
+ + {i18n._(t`Minimum Rate`)} + + +
+ + dispatch(setLimitPrice(value))} + /> + + + dispatch( + setLimitOrderInvertState({ + invertRate: !invertRate, + limitPrice: limitPrice + ? !invertRate + ? limitPrice?.invert().toSignificant(6) + : limitPrice?.toSignificant(6) + : '', + }) + ) + } + > + {invertRate ? inputCurrency?.symbol : outputCurrency?.symbol} + +
+
+ ) +} + +export default LimitPriceInputPanel diff --git a/src/features/stop-loss/MyStopOrders.tsx b/src/features/stop-loss/MyStopOrders.tsx new file mode 100644 index 0000000000..8c66db9634 --- /dev/null +++ b/src/features/stop-loss/MyStopOrders.tsx @@ -0,0 +1,36 @@ +import { BookOpenIcon } from '@heroicons/react/outline' +import { t } from '@lingui/macro' +import { useLingui } from '@lingui/react' +import Badge from 'app/components/Badge' +import QuestionHelper from 'app/components/QuestionHelper' +import useStopLossOrders from 'app/features/stop-loss/useStopLossOrders' +import Link from 'next/link' +import React, { FC } from 'react' + +const MyStopOrders: FC = () => { + const { i18n } = useLingui() + const { pending } = useStopLossOrders() + + const content = ( + } + /> + ) + + return ( + + + {pending.allData.length > 0 ? ( + + {content} + + ) : ( + content + )} + + + ) +} + +export default MyStopOrders diff --git a/src/features/stop-loss/OpenOrders.tsx b/src/features/stop-loss/OpenOrders.tsx new file mode 100644 index 0000000000..d4f748d3b4 --- /dev/null +++ b/src/features/stop-loss/OpenOrders.tsx @@ -0,0 +1,146 @@ +import { t } from '@lingui/macro' +import { useLingui } from '@lingui/react' +import { LimitOrder } from '@sushiswap/limit-order-sdk' +import Pagination from 'app/components/Pagination' +import Typography from 'app/components/Typography' +import { + TABLE_TABLE_CLASSNAME, + TABLE_TBODY_TD_CLASSNAME, + TABLE_TBODY_TR_CLASSNAME, + TABLE_TR_TH_CLASSNAME, + TABLE_WRAPPER_DIV_CLASSNAME, +} from 'app/features/trident/constants' +import { classNames } from 'app/functions' +import { useLimitOrderContract } from 'app/hooks' +import { useActiveWeb3React } from 'app/services/web3' +import { useTransactionAdder } from 'app/state/transactions/hooks' +import Link from 'next/link' +import React, { FC, useCallback } from 'react' +import { useFlexLayout, usePagination, useSortBy, useTable } from 'react-table' + +import { useOpenOrdersTableConfig } from './useOpenOrdersTableConfig' +import useStopLossExecute from './useStopLossExecute' +import useStopLossOrders from './useStopLossOrders' + +const OpenOrders: FC = () => { + const { i18n } = useLingui() + const { account } = useActiveWeb3React() + const addTransaction = useTransactionAdder() + const limitOrderContract = useLimitOrderContract(true) + + const { cancelRequest } = useStopLossExecute() + const { loading, pending, setPendingPage } = useStopLossOrders() + + const cancelOrder = useCallback( + async (id: string, limitOrder: LimitOrder, summary: string) => { + if (!limitOrderContract) return + console.log('canceling stop-loss request #', id) + await cancelRequest(id) + }, + [addTransaction, limitOrderContract] + ) + + const { config } = useOpenOrdersTableConfig({ orders: pending.data, cancelOrder }) + + // @ts-ignore TYPE NEEDS FIXING + const { getTableProps, getTableBodyProps, headerGroups, prepareRow, page } = useTable( + // @ts-ignore TYPE NEEDS FIXING + config, + useSortBy, + usePagination, + useFlexLayout + ) + + return ( +
+
+ + + {headerGroups.map((headerGroup, i) => ( + + {headerGroup.headers.map((column, i) => ( + + ))} + + ))} + + + {page.length > 0 ? ( + // @ts-ignore TYPE NEEDS FIXING + page.map((row, i) => { + prepareRow(row) + return ( + + {/*@ts-ignore TYPE NEEDS FIXING*/} + {row.cells.map((cell, i) => { + return ( + + ) + })} + + ) + }) + ) : ( + + + + )} + + + + + + +
+ {column.render('Header')} +
+ {cell.render('Cell')} +
+ + {loading ? i18n._(t`Loading open orders...`) : i18n._(t`No open orders`)} + +
+ + Funds will be received in your{' '} + + + BentoBox + + {' '} + after order execution + +
+
+ + 1} + canNextPage={pending.page < pending.maxPages} + onChange={(page) => setPendingPage(page + 1)} + totalPages={pending.maxPages} + currentPage={pending.page - 1} + pageNeighbours={1} + /> +
+ ) +} + +export default OpenOrders diff --git a/src/features/stop-loss/OrderTableToggle.tsx b/src/features/stop-loss/OrderTableToggle.tsx new file mode 100644 index 0000000000..431b17c14d --- /dev/null +++ b/src/features/stop-loss/OrderTableToggle.tsx @@ -0,0 +1,57 @@ +import { t } from '@lingui/macro' +import { useLingui } from '@lingui/react' +import Typography from 'app/components/Typography' +import { classNames } from 'app/functions' +import Link from 'next/link' +import { useRouter } from 'next/router' +import React, { useMemo } from 'react' + +const OrdersTableToggle = () => { + const { i18n } = useLingui() + const { asPath } = useRouter() + + const items = useMemo( + () => [ + { + key: 'open', + label: i18n._(t`Orders`), + link: '/stop-loss/open', + }, + { + key: 'history', + label: i18n._(t`History`), + link: '/stop-loss/history', + }, + ], + [i18n] + ) + + return ( +
+ {items.map(({ label, link, key }, index) => ( + + + + {label} + +
+ + + ))} +
+ ) +} + +export default OrdersTableToggle diff --git a/src/features/stop-loss/StopLimitOrderButton.tsx b/src/features/stop-loss/StopLimitOrderButton.tsx new file mode 100644 index 0000000000..ba6e43a40b --- /dev/null +++ b/src/features/stop-loss/StopLimitOrderButton.tsx @@ -0,0 +1,131 @@ +import { t } from '@lingui/macro' +import { useLingui } from '@lingui/react' +import { Currency, CurrencyAmount, Trade, TradeType } from '@sushiswap/core-sdk' +import { STOP_LIMIT_ORDER_ADDRESS } from '@sushiswap/limit-order-sdk' +import Button from 'app/components/Button' +import Typography from 'app/components/Typography' +import { STOP_LIMIT_ORDER_WRAPPER_ADDRESSES } from 'app/constants/autonomy' +import useLimitOrderExecute, { DepositPayload } from 'app/features/legacy/limit-order/useLimitOrderExecute' +import { useDiffOfStopAndMinimumRate } from 'app/features/stop-loss/useStopLossExecute' +import TridentApproveGate from 'app/features/trident/TridentApproveGate' +import { useBentoBoxContract } from 'app/hooks' +import useENS from 'app/hooks/useENS' +import { useActiveWeb3React } from 'app/services/web3' +import { useAddPopup } from 'app/state/application/hooks' +import { useAppDispatch } from 'app/state/hooks' +import { setFromBentoBalance, setLimitOrderBentoPermit, setLimitOrderShowReview } from 'app/state/limit-order/actions' +import { useLimitOrderDerivedInputError, useLimitOrderState } from 'app/state/limit-order/hooks' +import React, { FC, useCallback, useState } from 'react' + +interface StopLimitOrderButton { + trade?: Trade + parsedAmounts: { + inputAmount?: CurrencyAmount + outputAmount?: CurrencyAmount + } +} + +const StopLimitOrderButton: FC = ({ trade, parsedAmounts }) => { + const { i18n } = useLingui() + const { chainId } = useActiveWeb3React() + const dispatch = useAppDispatch() + const { fromBentoBalance, bentoPermit, attemptingTxn, recipient } = useLimitOrderState() + const { address } = useENS(recipient) + const addPopup = useAddPopup() + + const error = useLimitOrderDerivedInputError({ trade, isStopLossOrder: true }) + const { deposit } = useLimitOrderExecute() + const bentoboxContract = useBentoBoxContract() + const masterContractAddress = chainId ? STOP_LIMIT_ORDER_ADDRESS[chainId] : undefined + const [permitError, setPermitError] = useState(false) + + // check if difference between stopRate and minimum rate is enough to cover autonomy fee + const { tooNarrowMarginOfRates } = useDiffOfStopAndMinimumRate({ + inputAmount: parsedAmounts.inputAmount, + }) + + const _deposit = useCallback( + async (payload: DepositPayload) => { + const tx = await deposit(payload) + if (tx?.hash) { + dispatch(setFromBentoBalance(true)) + } + }, + [deposit, dispatch] + ) + + const handler = useCallback(async () => { + if (!parsedAmounts?.inputAmount) return + + if (chainId && !STOP_LIMIT_ORDER_WRAPPER_ADDRESSES[chainId]) { + addPopup({ + txn: { hash: '', summary: 'Autonomy unsupported!', success: false }, + }) + return + } + + if (fromBentoBalance) { + dispatch(setLimitOrderShowReview(true)) + } else { + await _deposit({ + inputAmount: parsedAmounts?.inputAmount, + bentoPermit, + fromBentoBalance, + }) + } + }, [_deposit, bentoPermit, dispatch, fromBentoBalance, parsedAmounts?.inputAmount]) + + return ( + <> + {permitError && ( + + {i18n._( + t`Something went wrong during signing of the approval. This is expected for hardware wallets, such as Trezor and Ledger. Click 'Approve BentoBox' again for approving using the fallback method` + )} + + )} + dispatch(setLimitOrderBentoPermit(permit)), + onPermitError: () => setPermitError(true), + })} + > + {({ approved, loading }) => { + const disabled = + !!error || + !!tooNarrowMarginOfRates || + !approved || + loading || + attemptingTxn || + Boolean(recipient && !address && error) + return ( + + ) + }} + + + ) +} + +export default StopLimitOrderButton diff --git a/src/features/stop-loss/StopPriceInputPanel.tsx b/src/features/stop-loss/StopPriceInputPanel.tsx new file mode 100644 index 0000000000..c6a953a241 --- /dev/null +++ b/src/features/stop-loss/StopPriceInputPanel.tsx @@ -0,0 +1,69 @@ +import { t } from '@lingui/macro' +import { useLingui } from '@lingui/react' +import { Currency, Price, Trade, TradeType } from '@sushiswap/core-sdk' +import Input from 'app/components/Input' +import QuestionHelper from 'app/components/QuestionHelper' +import Typography from 'app/components/Typography' +import { useAppDispatch } from 'app/state/hooks' +import { LimitPrice, setStopLossInvertState, setStopPrice } from 'app/state/limit-order/actions' +import useLimitOrderDerivedCurrencies, { useLimitOrderState } from 'app/state/limit-order/hooks' +import React, { FC } from 'react' + +interface StopPriceInputPanel { + trade?: Trade + stopPrice?: Price +} + +const StopPriceInputPanel: FC = ({ trade, stopPrice }) => { + const { i18n } = useLingui() + const dispatch = useAppDispatch() + const { stopPrice: stopPriceString, invertStopRate } = useLimitOrderState() + const { inputCurrency, outputCurrency } = useLimitOrderDerivedCurrencies() + const disabled = !inputCurrency || !outputCurrency + + return ( +
+ + {i18n._(t`Stop Rate`)} + + +
+ + dispatch(setStopPrice(value))} + /> + + + dispatch( + setStopLossInvertState({ + invertStopRate: !invertStopRate, + stopPrice: stopPrice + ? !invertStopRate + ? stopPrice?.invert().toSignificant(6) + : stopPrice?.toSignificant(6) + : '', + }) + ) + } + > + {invertStopRate ? inputCurrency?.symbol : outputCurrency?.symbol} + +
+
+ ) +} + +export default StopPriceInputPanel diff --git a/src/features/stop-loss/useCompletedOrdersTableConfig.tsx b/src/features/stop-loss/useCompletedOrdersTableConfig.tsx new file mode 100644 index 0000000000..119cb6c7bb --- /dev/null +++ b/src/features/stop-loss/useCompletedOrdersTableConfig.tsx @@ -0,0 +1,150 @@ +import { ChevronRightIcon } from '@heroicons/react/outline' +import { OrderStatus } from '@sushiswap/limit-order-sdk' +import Chip from 'app/components/Chip' +import { CurrencyLogo } from 'app/components/CurrencyLogo' +import Typography from 'app/components/Typography' +import { DerivedOrder } from 'app/features/legacy/limit-order/types' +import React, { useMemo, useState } from 'react' +import { CellProps } from 'react-table' + +export const useCompletedOrdersTableConfig = ({ orders }: { orders?: DerivedOrder[] }) => { + const data = useMemo( + () => [ + { + accessor: 'tokenOut', + Header: 'Market', + minWidth: 300, + Cell: (props: CellProps) => { + return ( +
+
+ +
+ + + {props.cell.row.original.limitOrder.amountIn.toSignificant(6)} + {' '} + {props.cell.row.original.tokenIn.symbol} + + +
+ +
+ + + {props.cell.row.original.limitOrder.amountOut.toSignificant(6)} + {' '} + {props.cell.row.original.tokenOut.symbol} + +
+ ) + }, + }, + { + accessor: 'stopPrice', + Header: 'Stop Price', + width: 100, + minWidth: 100, + Cell: (props: CellProps) => { + const [invert, setInvert] = useState(false) + + return ( + setInvert(!invert)} + > + + {!invert + ? 1e18 / parseFloat(props.cell.row.original.limitOrder.stopPrice) + : parseFloat(props.cell.row.original.limitOrder.stopPrice) / 1e18} + {' '} + {invert ? props.cell.row.original.tokenIn.symbol : props.cell.row.original.tokenOut.symbol} + + ) + }, + }, + { + accessor: 'rate', + Header: 'Rate', + width: 100, + minWidth: 100, + Cell: (props: CellProps) => { + const [invert, setInvert] = useState(false) + + return ( + setInvert(!invert)} + > + + {invert ? props.cell.value.invert().toSignificant(6) : props.cell.value.toSignificant(6)} + {' '} + {invert ? props.cell.row.original.tokenIn.symbol : props.cell.row.original.tokenOut.symbol} + + ) + }, + }, + { + accessor: 'updated', + Header: 'Created at', + width: 150, + minWidth: 150, + Cell: (props: CellProps) => { + return ( + + {new Date(Number(props.cell.row.original.limitOrder.startTime) * 1000).toLocaleString('en-uS', { + year: 'numeric', + month: 'long', + day: 'numeric', + hour: 'numeric', + minute: 'numeric', + timeZone: 'UTC', + })} + + ) + }, + }, + { + accessor: 'status', + Header: 'Status', + minWidth: 100, + Cell: (props: CellProps) => { + return ( + + ) + }, + }, + ], + [] + ) + + const defaultColumn = React.useMemo(() => ({ minWidth: 0 }), []) + + return useMemo( + () => ({ + config: { + columns: data, + data: orders, + defaultColumn, + // initialState: { + // sortBy: [{ id: 'receive', desc: true }], + // }, + }, + }), + [orders, data, defaultColumn] + ) +} diff --git a/src/features/stop-loss/useOpenOrdersTableConfig.tsx b/src/features/stop-loss/useOpenOrdersTableConfig.tsx new file mode 100644 index 0000000000..095f214288 --- /dev/null +++ b/src/features/stop-loss/useOpenOrdersTableConfig.tsx @@ -0,0 +1,181 @@ +import { ChevronRightIcon } from '@heroicons/react/outline' +import { t } from '@lingui/macro' +import { useLingui } from '@lingui/react' +import { LimitOrder } from '@sushiswap/limit-order-sdk' +import Button from 'app/components/Button' +import { CurrencyLogo } from 'app/components/CurrencyLogo' +import Typography from 'app/components/Typography' +import { DerivedOrder } from 'app/features/legacy/limit-order/types' +import React, { useMemo, useState } from 'react' +import { CellProps } from 'react-table' + +export const useOpenOrdersTableConfig = ({ + orders, + cancelOrder, +}: { + orders?: DerivedOrder[] + cancelOrder: (id: string, limitOrder: LimitOrder, type: string) => void +}) => { + const { i18n } = useLingui() + + const data = useMemo( + () => [ + { + accessor: 'tokenOut', + Header: 'Market', + minWidth: 300, + Cell: (props: CellProps) => { + return ( +
+
+ +
+ + + {props.cell.row.original.limitOrder.amountIn.toSignificant(6)} + {' '} + {props.cell.row.original.tokenIn.symbol} + + +
+ +
+ + + {props.cell.row.original.limitOrder.amountOut.toSignificant(6)} + {' '} + {props.cell.row.original.tokenOut.symbol} + +
+ ) + }, + }, + { + accessor: 'stopPrice', + Header: 'Stop Price', + width: 100, + minWidth: 100, + Cell: (props: CellProps) => { + const [invert, setInvert] = useState(false) + + return ( + setInvert(!invert)} + > + + {!invert + ? 1e18 / parseFloat(props.cell.row.original.limitOrder.stopPrice) + : parseFloat(props.cell.row.original.limitOrder.stopPrice) / 1e18} + {' '} + {invert ? props.cell.row.original.tokenIn.symbol : props.cell.row.original.tokenOut.symbol} + + ) + }, + }, + { + accessor: 'rate', + Header: 'Rate', + width: 100, + minWidth: 100, + Cell: (props: CellProps) => { + const [invert, setInvert] = useState(false) + + return ( + setInvert(!invert)} + > + + {invert ? props.cell.value.invert().toSignificant(6) : props.cell.value.toSignificant(6)} + {' '} + {invert ? props.cell.row.original.tokenIn.symbol : props.cell.row.original.tokenOut.symbol} + + ) + }, + }, + { + accessor: 'timestamp', + Header: 'Expires', + width: 150, + minWidth: 150, + Cell: (props: CellProps) => { + return ( + + {Number(props.cell.row.original.limitOrder.endTime) === Number.MAX_SAFE_INTEGER + ? 'Never' + : new Date(Number(props.cell.row.original.limitOrder.endTime) * 1000).toLocaleString('en-uS', { + year: 'numeric', + month: 'long', + day: 'numeric', + hour: 'numeric', + minute: 'numeric', + timeZone: 'UTC', + })} + + ) + }, + }, + { + accessor: 'Updated', + Header: 'Created at', + width: 150, + minWidth: 150, + Cell: (props: CellProps) => { + return ( + + {new Date(Number(props.cell.row.original.limitOrder.startTime) * 1000).toLocaleString('en-uS', { + year: 'numeric', + month: 'long', + day: 'numeric', + hour: 'numeric', + minute: 'numeric', + timeZone: 'UTC', + })} + + ) + }, + }, + { + accessor: 'id', + Header: 'Cancel', + width: 100, + minWidth: 100, + Cell: (props: CellProps) => { + return ( + + ) + }, + }, + ], + [cancelOrder, i18n] + ) + + const defaultColumn = React.useMemo(() => ({ minWidth: 0 }), []) + + return useMemo( + () => ({ + config: { + columns: data, + data: orders, // orders?.filter((order) => Number(order.limitOrder.endTime) * 1000 > Date.now()), + defaultColumn, + // initialState: { + // sortBy: [{ id: 'receive', desc: true }], + // }, + }, + }), + [orders, data, defaultColumn] + ) +} diff --git a/src/features/stop-loss/useStopLossExecute.ts b/src/features/stop-loss/useStopLossExecute.ts new file mode 100644 index 0000000000..6dd98a5b2f --- /dev/null +++ b/src/features/stop-loss/useStopLossExecute.ts @@ -0,0 +1,305 @@ +import { defaultAbiCoder } from '@ethersproject/abi' +import { BigNumber } from '@ethersproject/bignumber' +import { Signature } from '@ethersproject/bytes' +import { AddressZero } from '@ethersproject/constants' +import { TransactionResponse } from '@ethersproject/providers' +import { formatUnits } from '@ethersproject/units' +import { ChainId, Currency, CurrencyAmount, Price, WNATIVE_ADDRESS } from '@sushiswap/core-sdk' +import { LimitOrder, ROUND_UP_RECEIVER_ADDRESS } from '@sushiswap/limit-order-sdk' +import { + CHAINLINK_ORACLE_ADDRESS, + MORALIS_INFO, + STOP_LIMIT_ORDER_WRAPPER_ADDRESSES, + STOP_LIMIT_ORDER_WRAPPER_FEE_MINIMUM, +} from 'app/constants/autonomy' +import useLimitOrders from 'app/features/legacy/limit-order/useLimitOrders' +import { ZERO } from 'app/functions' +import { useAutonomyLimitOrderWrapperContract, useAutonomyRegistryContract, useRouterContract } from 'app/hooks' +import { useActiveWeb3React } from 'app/services/web3' +import { useAddPopup } from 'app/state/application/hooks' +import { useAppDispatch } from 'app/state/hooks' +import { clear, setLimitOrderAttemptingTxn } from 'app/state/limit-order/actions' +import { useLimitOrderDerivedLimitPrice, useStopLossDerivedLimitPrice } from 'app/state/limit-order/hooks' +import { OrderExpiration } from 'app/state/limit-order/reducer' +import { useTransactionAdder } from 'app/state/transactions/hooks' +import Moralis from 'moralis' +import { useEffect, useMemo, useState } from 'react' +import { useCallback } from 'react' + +import { prepareStopPriceOracleData, ZERO_ORACLE_ADDRESS, ZERO_ORACLE_DATA } from './utils' + +const getEndTime = (orderExpiration: OrderExpiration | string): number => { + switch (orderExpiration) { + case OrderExpiration.hour: + return Math.floor(new Date().getTime() / 1000) + 3600 + case OrderExpiration.day: + return Math.floor(new Date().getTime() / 1000) + 86400 + case OrderExpiration.week: + return Math.floor(new Date().getTime() / 1000) + 604800 + case OrderExpiration.never: + default: + return Number.MAX_SAFE_INTEGER + } +} + +export type DepositAndApprovePayload = { inputAmount: CurrencyAmount; bentoPermit: Signature } +export type DepositPayload = { + inputAmount?: CurrencyAmount + bentoPermit?: Signature + fromBentoBalance: boolean +} +export type ExecutePayload = { + orderExpiration: OrderExpiration | string + inputAmount?: CurrencyAmount + outputAmount?: CurrencyAmount + amountExternal?: CurrencyAmount + recipient?: string + stopPrice?: Price | undefined +} + +export type UseLimitOrderExecuteDeposit = (x: DepositPayload) => Promise +export type UseLimitOrderExecuteExecute = (x: ExecutePayload) => void +export type UseLimitOrderExecute = () => { + execute: UseLimitOrderExecuteExecute + cancelRequest: (id: string) => void +} + +// estimate equivalent ETH amount from any input token amount +export function useEstimateEquivalentEthAmount(token: CurrencyAmount | undefined): string { + const { chainId } = useActiveWeb3React() + const router = useRouterContract() + const [equivalentEthAmount, setEquivalentEthAmount] = useState('0') + + useEffect(() => { + const updateEquivalentEthAmount = async () => { + if (!router || !chainId || !token) { + setEquivalentEthAmount('0') + return + } + if (token.wrapped.currency.address === WNATIVE_ADDRESS[chainId]) { + setEquivalentEthAmount(formatUnits(token.quotient.toString(), 18)) + return + } + + // estimate output amount with uniV2 router + const amountsOut = await router.getAmountsOut(token.quotient.toString(), [ + token.wrapped.currency.address, + WNATIVE_ADDRESS[chainId], + ]) + setEquivalentEthAmount(formatUnits(amountsOut[amountsOut.length - 1], 18)) + } + + updateEquivalentEthAmount() + }, [token]) + + return equivalentEthAmount +} + +interface DifferenceOfStopAndMinimumRateResult { + diffOfStopAndMinRate: CurrencyAmount | undefined + diffValueOfEth: string + tooNarrowMarginOfRates: boolean + externalAmountForFee: string +} + +// check if difference between stopRate and minimum rate is enough to cover autonomy fee +export function useDiffOfStopAndMinimumRate({ + inputAmount, +}: { + inputAmount: CurrencyAmount | undefined +}): DifferenceOfStopAndMinimumRateResult { + const { chainId } = useActiveWeb3React() + const rate = useLimitOrderDerivedLimitPrice() + const stopRate = useStopLossDerivedLimitPrice() + + // get difference of output amounts between stop and minimum rate + const diffOfStopAndMinRate = useMemo(() => { + if (!stopRate || !rate || !inputAmount || stopRate.equalTo(rate) || stopRate.lessThan(rate)) return undefined + return stopRate?.quote(inputAmount).subtract(rate?.quote(inputAmount)) + }, [rate, stopRate, inputAmount]) + // get equivalent ETH amount of difference + const diffValueOfEth = useEstimateEquivalentEthAmount(diffOfStopAndMinRate) + // compare with minimum fee amount + const tooNarrowMarginOfRates = useMemo( + () => (!!chainId ? parseFloat(diffValueOfEth) < parseFloat(STOP_LIMIT_ORDER_WRAPPER_FEE_MINIMUM[chainId]) : false), + [diffValueOfEth] + ) + + const convertIntWithMultiply = (s: string) => Math.floor(parseFloat(s) * 1000000) // 1000,000 aims to mitigate loss while in math operation + const getBigNumberFromTinyFloat = (s: string) => BigNumber.from(`${convertIntWithMultiply(s)}`) + // calculate amount for autonomy fee: (stopRate - minimumRate) * MIN_FEE_ETH_AMOUNT / equivalentEthAmountOf[stopRate - minimumRate] + // this will be cumulated to minimum output amount + const externalAmountForFee = + !!chainId && !!diffOfStopAndMinRate && convertIntWithMultiply(diffValueOfEth) > 0 + ? BigNumber.from(diffOfStopAndMinRate?.quotient.toString()) + .mul(getBigNumberFromTinyFloat(STOP_LIMIT_ORDER_WRAPPER_FEE_MINIMUM[chainId])) + .div(getBigNumberFromTinyFloat(diffValueOfEth)) + : 0 + + return { + diffOfStopAndMinRate, + diffValueOfEth, + tooNarrowMarginOfRates, + externalAmountForFee: externalAmountForFee.toString(), + } +} + +// register stop-limit order into autonomy registry +const useStopLossExecute: UseLimitOrderExecute = () => { + const { account, chainId, library } = useActiveWeb3React() + + const autonomyRegistryContract = useAutonomyRegistryContract() + const limitOrderWrapperContract = useAutonomyLimitOrderWrapperContract() + const addTransaction = useTransactionAdder() + + const dispatch = useAppDispatch() + const addPopup = useAddPopup() + const { mutate } = useLimitOrders() + + const execute = useCallback( + async ({ orderExpiration, inputAmount, outputAmount, amountExternal, recipient, stopPrice }) => { + if (!inputAmount || !outputAmount || !account || !library || !chainId) throw new Error('Dependencies unavailable') + + let oracleData + if (stopPrice) { + oracleData = prepareStopPriceOracleData(inputAmount.wrapped, outputAmount.wrapped, stopPrice, chainId) + // console.log('oracleData: ', JSON.stringify(oracleData)) + } + + const endTime = getEndTime(orderExpiration) + const order = new LimitOrder( + account, + inputAmount.wrapped, + outputAmount.wrapped, + recipient ? recipient : account, + Math.floor(new Date().getTime() / 1000).toString(), + endTime.toString(), + oracleData && oracleData.stopPrice ? oracleData.stopPrice : '0', + oracleData && oracleData.stopPrice ? CHAINLINK_ORACLE_ADDRESS[chainId] : ZERO_ORACLE_ADDRESS, + oracleData && oracleData.stopPrice ? oracleData.oracleData : ZERO_ORACLE_DATA + ) + + try { + dispatch(setLimitOrderAttemptingTxn(true)) + await order?.signOrderWithProvider(chainId || 1, library) + + if (autonomyRegistryContract && limitOrderWrapperContract && chainId) { + if (stopPrice && oracleData?.stopPrice == ZERO_ORACLE_DATA) { + throw new Error('Unsupported pair') + } + const data = defaultAbiCoder.encode( + ['address[]', 'uint256', 'address', 'bool'], + [ + [order.tokenInAddress, order.tokenOutAddress], // path + amountExternal?.wrapped.quotient.toString(), + chainId && STOP_LIMIT_ORDER_WRAPPER_ADDRESSES[chainId], // profit receiver + false, // keepTokenIn, charge fee, by outputToken always + ] + ) + + const encodedFillOrderData = limitOrderWrapperContract.interface.encodeFunctionData('fillOrder', [ + 0, // fee amount, that will be filled later + [ + order.maker, + order.amountInRaw, + order.amountOutRaw, + order.recipient, + order.startTime, + order.endTime, + order.stopPrice, + order.oracleAddress, + order.oracleData, + order.amountInRaw, + order.v, + order.r, + order.s, + ], + order.tokenInAddress, + order.tokenOutAddress, + chainId && + (chainId == ChainId.AVALANCHE + ? '0x802290173908ed30A9642D6872e252Ef4f6e59A2' + : ROUND_UP_RECEIVER_ADDRESS[chainId]), + data, + ]) + // console.log('encoded fillOrder() data: ', JSON.stringify(encodedFillOrderData)) + + await autonomyRegistryContract.newReq( + chainId && STOP_LIMIT_ORDER_WRAPPER_ADDRESSES[chainId], // target + AddressZero, // referer + encodedFillOrderData, // callData + ZERO, // ethForCall + false, // verifyUser + true, // insertFeeAmount + false // isAlive + ) + } + + addPopup({ + txn: { hash: '', summary: 'Stop loss order created', success: true }, + }) + + await mutate() + dispatch(clear()) + + dispatch(setLimitOrderAttemptingTxn(false)) + } catch (e: any) { + dispatch(setLimitOrderAttemptingTxn(false)) + addPopup({ + txn: { + hash: '', + // @ts-ignore TYPE NEEDS FIXING + summary: `Error: ${e?.message}`, + success: false, + }, + }) + } + }, + [account, addPopup, chainId, dispatch, library, autonomyRegistryContract, limitOrderWrapperContract] + ) + + const cancelRequest = useCallback( + async (requestId: string) => { + if (!account || !chainId || !library || !limitOrderWrapperContract || !autonomyRegistryContract) + throw new Error('Dependencies unavailable') + + try { + Moralis.initialize((chainId && MORALIS_INFO[chainId].key) || '') + Moralis.serverURL = (chainId && MORALIS_INFO[chainId].serverURL) || '' + const queryRequest = new Moralis.Query('RegistryRequests') + queryRequest.equalTo('uid', requestId) + let requests = await queryRequest.find() + + const callData = await requests[0].get('callData') + + const tx = await autonomyRegistryContract.cancelHashedReq(requestId, [ + account, + chainId && STOP_LIMIT_ORDER_WRAPPER_ADDRESSES[chainId], // target + AddressZero, // referer + callData, // callData + ZERO, // initEthSent + ZERO, // ethForCall + false, // verifyUser + true, // insertFeeAmount + false, // payWithAuto + false, // isAlive + ]) + + addTransaction(tx, { + summary: 'Stop loss order canceled', + }) + await tx.wait() + } catch (e) { + console.log('Error while fetching history from Moralis') + } + }, + [account, chainId, dispatch, library, limitOrderWrapperContract, autonomyRegistryContract] + ) + + return { + cancelRequest, + execute, + } +} + +export default useStopLossExecute diff --git a/src/features/stop-loss/useStopLossOrders.tsx b/src/features/stop-loss/useStopLossOrders.tsx new file mode 100644 index 0000000000..228f60646f --- /dev/null +++ b/src/features/stop-loss/useStopLossOrders.tsx @@ -0,0 +1,285 @@ +import { Price, Token } from '@sushiswap/core-sdk' +import DEFAULT_TOKEN_LIST from '@sushiswap/default-token-list' +import { LimitOrder, OrderStatus } from '@sushiswap/limit-order-sdk' +import { MORALIS_INFO, QUERY_REQUEST_LIMIT, STOP_LIMIT_ORDER_WRAPPER_ADDRESSES } from 'app/constants/autonomy' +import { DerivedOrder } from 'app/features/legacy/limit-order/types' +import { useAutonomyLimitOrderWrapperContract } from 'app/hooks' +import { useActiveWeb3React } from 'app/services/web3' +import Moralis from 'moralis' +import { useCallback, useEffect, useMemo, useState } from 'react' + +interface IToken { + chainId: number + address: string + name: string + symbol: string + decimals: number + logoUrl?: string +} + +interface ITokenList { + name: string + logoURL?: string + keywords: any + timestamp: any + tokens: IToken[] + version: any +} + +interface IRegistryRequest { + callData: string + uid: string +} + +interface OrdersData { + allData: Array + data: Array + page: number + maxPages: number +} + +interface StopLossOrdersData { + loading: boolean + totalOrders: number + all: Array + pending: OrdersData + completed: OrdersData +} + +// fetch stop-limit orders history from autonomy moralis +const useStopLossOrders = () => { + const { account, chainId } = useActiveWeb3React() + const limitOrderWrapperContract = useAutonomyLimitOrderWrapperContract() + + const tokens = useMemo( + () => (DEFAULT_TOKEN_LIST as ITokenList).tokens.filter((token) => token.chainId === chainId), + [chainId] + ) + + const [state, setState] = useState({ + loading: false, + totalOrders: 0, + all: [], + pending: { + allData: [], + data: [], + page: 1, + maxPages: 1, + }, + completed: { + allData: [], + data: [], + page: 1, + maxPages: 1, + }, + }) + + const fetchRegistryHistory = useCallback(async () => { + try { + Moralis.initialize((chainId && MORALIS_INFO[chainId].key) || '') + Moralis.serverURL = (chainId && MORALIS_INFO[chainId].serverURL) || '' + + const queryRequests = new Moralis.Query('RegistryRequests') + queryRequests.equalTo('user', account?.toLowerCase()) + queryRequests.equalTo('target', chainId && STOP_LIMIT_ORDER_WRAPPER_ADDRESSES[chainId].toLowerCase()) + let registryRequests = await queryRequests.find() + + return await Promise.all( + registryRequests.map(async (request) => { + return { + callData: request.get('callData'), + uid: request.get('uid'), + } + }) + ) + } catch (e) { + console.log('Error while fetching history from Moralis') + return [] + } + }, [account, chainId]) + + const fetchExecutedRegistryHistory = useCallback(async () => { + try { + Moralis.initialize((chainId && MORALIS_INFO[chainId].key) || '') + Moralis.serverURL = (chainId && MORALIS_INFO[chainId].serverURL) || '' + + const queryExecutes = new Moralis.Query('RegistryExecutedRequests') + queryExecutes.limit(QUERY_REQUEST_LIMIT) + let execRequests = await queryExecutes.find() + return execRequests + } catch (e) { + console.log('Error while fetching executed history from Moralis') + return [] + } + }, [account, chainId]) + + const fetchCanceledRegistryHistory = useCallback(async () => { + try { + Moralis.initialize((chainId && MORALIS_INFO[chainId].key) || '') + Moralis.serverURL = (chainId && MORALIS_INFO[chainId].serverURL) || '' + + const queryCanceled = new Moralis.Query('RegistryCancelRequests') + queryCanceled.limit(QUERY_REQUEST_LIMIT) + let canceledRequests = await queryCanceled.find() + return canceledRequests + } catch (e) { + console.log('Error while fetching canceled history from Moralis') + return [] + } + }, [account, chainId]) + + const transform = useCallback( + (callData: string, uid: string, status: OrderStatus = OrderStatus.PENDING): DerivedOrder | undefined => { + if (!chainId) return + + const fillOrderArgs = limitOrderWrapperContract?.interface.decodeFunctionData('fillOrder', callData) + const token0 = fillOrderArgs && tokens.find((token) => token.address === fillOrderArgs[2]) + const token1 = fillOrderArgs && tokens.find((token) => token.address === fillOrderArgs[3]) + if (!token0 || !token1) return + + const limitOrder: LimitOrder = LimitOrder.getLimitOrder({ + maker: fillOrderArgs[1][0] as string, + tokenIn: fillOrderArgs[2] as string, + tokenOut: fillOrderArgs[3] as string, + tokenInDecimals: token0.decimals, + tokenOutDecimals: token1.decimals, + tokenInSymbol: token0.symbol, + tokenOutSymbol: token1.symbol, + amountIn: fillOrderArgs[1][1] as string, + amountOut: fillOrderArgs[1][2] as string, + recipient: fillOrderArgs[1][3] as string, + startTime: fillOrderArgs[1][4] as string, + endTime: fillOrderArgs[1][5] as string, + stopPrice: fillOrderArgs[1][6] as string, + oracleAddress: fillOrderArgs[1][7] as string, + oracleData: fillOrderArgs[1][8] as string, + v: Number(fillOrderArgs[1][10]), + r: fillOrderArgs[1][11] as string, + s: fillOrderArgs[1][12] as string, + chainId: chainId, + }) + + const stopLossOrder: DerivedOrder = { + id: uid, + tokenIn: new Token(chainId, token0.address, token0.decimals, token0.symbol, token0.name), + tokenOut: new Token(chainId, token1.address, token1.decimals, token1.symbol, token1.name), + limitOrder, + filledPercent: '100', + status, + rate: new Price({ baseAmount: limitOrder.amountIn, quoteAmount: limitOrder.amountOut }), + } + + return stopLossOrder + }, + [limitOrderWrapperContract, tokens] + ) + + useEffect(() => { + const initOrdersData = async () => { + if (!account || !chainId) return + + setState((prevState) => ({ + ...prevState, + loading: true, + })) + + const executedOrdersCallData = await fetchExecutedRegistryHistory() + const executedUids = await Promise.all( + executedOrdersCallData.map(async (request) => { + return request.get('uid') + }) + ) + + const canceledOrdersCallData = await fetchCanceledRegistryHistory() + const canceledUids = await Promise.all( + canceledOrdersCallData.map(async (request) => { + return request.get('uid') + }) + ) + + const allRequests: IRegistryRequest[] = await fetchRegistryHistory() + const allOrdersData = allRequests + .map((request) => transform(request.callData, request.uid)) + .filter((order) => order) as DerivedOrder[] + + const pendingOrdersData = allRequests + .filter((request) => !executedUids.includes(request.uid) && !canceledUids.includes(request.uid)) + .map((request) => transform(request.callData, request.uid, OrderStatus.PENDING)) + .filter((order) => order) as DerivedOrder[] + + const completedOrdersData = allRequests + .filter((request) => executedUids.includes(request.uid) || canceledUids.includes(request.uid)) + .map((request) => + transform( + request.callData, + request.uid, + executedUids.includes(request.uid) ? OrderStatus.FILLED : OrderStatus.CANCELLED + ) + ) + .filter((order) => order) as DerivedOrder[] + + setState({ + loading: false, + totalOrders: allOrdersData.length, + all: allOrdersData, + completed: { + allData: completedOrdersData, + data: completedOrdersData.slice(0, 10), + page: 1, + maxPages: Math.ceil(completedOrdersData.length / 10), + }, + pending: { + allData: pendingOrdersData, + data: pendingOrdersData.slice(0, 10), + page: 1, + maxPages: Math.ceil(pendingOrdersData.length / 10), + }, + }) + } + + initOrdersData() + }, [account, chainId]) + + const setCompletedPage = useCallback( + (page: number) => { + if (page < 1 || page > state.completed.maxPages) return + + setState((prevState) => ({ + ...prevState, + completed: { + ...prevState.completed, + page, + data: prevState.completed.allData.slice(10 * (page - 1), 10 * page), + }, + })) + }, + [state.completed] + ) + + const setPendingPage = useCallback( + (page: number) => { + if (page < 1 || page > state.pending.maxPages) return + + setState((prevState) => ({ + ...prevState, + pending: { + ...prevState.pending, + page, + data: prevState.pending.allData.slice(10 * (page - 1), 10 * page), + }, + })) + }, + [state.pending] + ) + + return useMemo( + () => ({ + ...state, + setCompletedPage, + setPendingPage, + }), + [setCompletedPage, setPendingPage, state] + ) +} + +export default useStopLossOrders diff --git a/src/features/stop-loss/utils.ts b/src/features/stop-loss/utils.ts new file mode 100644 index 0000000000..0f351933e7 --- /dev/null +++ b/src/features/stop-loss/utils.ts @@ -0,0 +1,79 @@ +import { defaultAbiCoder } from '@ethersproject/abi' +import { Currency, CurrencyAmount, Price, Token } from '@sushiswap/core-sdk' +import { CHAINLINK_PRICE_FEED_MAP, ChainlinkPriceFeedEntry } from 'app/config/oracles/chainlink' +import { BigNumber } from 'ethers' + +export const STOP_LIMIT_ORDER_PROFIT_SLIPPAGE = 10 // percent unit + +export interface IStopPriceOracleData { + stopPrice?: string + oracleData: string +} + +export const ZERO_ORACLE_ADDRESS = '0x0000000000000000000000000000000000000000' +export const ZERO_ORACLE_DATA = '0x00000000000000000000000000000000000000000000000000000000000000' + +interface IChainlinkAggregator { + address: string + entry: ChainlinkPriceFeedEntry +} + +/** + * @dev It returns Chainlink Aggregator address for input currency. + * + * @param currencyAddr + * @param chainId + * @returns Chainlink aggregator address, for [currencyAddr] / USD price feed + */ +export function getOracleFeedEntry(currencyAddr: string, chainId: number): IChainlinkAggregator | undefined { + const NETWORK_FEED_MAPPING = CHAINLINK_PRICE_FEED_MAP[chainId] // {currency} / USD mapping + + for (const aggregatorAddr of Object.keys(NETWORK_FEED_MAPPING)) { + if (NETWORK_FEED_MAPPING[aggregatorAddr].from === currencyAddr) { + return { + address: aggregatorAddr, + entry: NETWORK_FEED_MAPPING[aggregatorAddr], + } + } + } + + return undefined +} + +/** + * @dev stopPrice should be inverse value, due to smart contract issue. + * + * @param tokenIn + * @param tokenOut + * @param stopPrice + * @returns + */ +export function prepareStopPriceOracleData( + tokenIn: CurrencyAmount, + tokenOut: CurrencyAmount, + stopPrice: Price, + chainId: number +): IStopPriceOracleData { + const inAggregator = getOracleFeedEntry(tokenIn.currency.address, chainId) + const outAggregator = getOracleFeedEntry(tokenOut.currency.address, chainId) + + // if priceFeed does not exists + if (!inAggregator || !outAggregator) { + return { + oracleData: ZERO_ORACLE_DATA, + } + } + + // make stopPrice decimals as 18 + const decimals = 36 + outAggregator.entry.decimals - inAggregator.entry.decimals - 18 + const oracleData = defaultAbiCoder.encode( + ['address', 'address', 'uint256'], // multiply, divide, decimals + [outAggregator.address, inAggregator.address, BigNumber.from(10).pow(decimals)] + ) + + const inverseStopPrice = Math.floor(parseFloat(stopPrice.invert().toSignificant(18)) * 1e18) + return { + stopPrice: inverseStopPrice.toString(), + oracleData, + } +} diff --git a/src/features/trade/HeaderNew.tsx b/src/features/trade/HeaderNew.tsx index 458cd05f8d..6b4eff12db 100644 --- a/src/features/trade/HeaderNew.tsx +++ b/src/features/trade/HeaderNew.tsx @@ -6,6 +6,7 @@ import Settings from 'app/components/Settings' import Typography from 'app/components/Typography' import { Feature } from 'app/enums' import MyOrders from 'app/features/legacy/limit-order/MyOrders' +import MyStopOrders from 'app/features/stop-loss/MyStopOrders' import { featureEnabled } from 'app/functions' import { useActiveWeb3React } from 'app/services/web3' import { useRouter } from 'next/router' @@ -34,6 +35,7 @@ const HeaderNew: FC = ({ inputCurrency, outputCurrency, trident const { i18n } = useLingui() const { asPath } = useRouter() const isLimitOrder = asPath.startsWith('/limit-order') + const isStopLoss = asPath.startsWith('/stop-loss') return (
@@ -62,9 +64,23 @@ const HeaderNew: FC = ({ inputCurrency, outputCurrency, trident ) : null} + {featureEnabled(Feature.STOP_LOSSES, chainId) ? ( + + + {i18n._(t`Stop`)} + + + ) : null}
{isLimitOrder && } + {isStopLoss && }
diff --git a/src/hooks/useContract.ts b/src/hooks/useContract.ts index fb83ed97d1..d851ec635a 100644 --- a/src/hooks/useContract.ts +++ b/src/hooks/useContract.ts @@ -28,6 +28,8 @@ import { ARGENT_WALLET_DETECTOR_ABI, ARGENT_WALLET_DETECTOR_MAINNET_ADDRESS, } from 'app/constants/abis/argent-wallet-detector' +import AUTONOMY_REGISTRY_ABI from 'app/constants/abis/autonomy/registry.json' +import STOP_LIMIT_ORDER_WRAPPER_ABI from 'app/constants/abis/autonomy/stop-limit-order-wrapper.json' import BAR_ABI from 'app/constants/abis/bar.json' import BENTOBOX_ABI from 'app/constants/abis/bentobox.json' import BORING_HELPER_ABI from 'app/constants/abis/boring-helper.json' @@ -62,6 +64,7 @@ import UNI_FACTORY_ABI from 'app/constants/abis/uniswap-v2-factory.json' import IUniswapV2PairABI from 'app/constants/abis/uniswap-v2-pair.json' import WETH9_ABI from 'app/constants/abis/weth.json' import ZENKO_ABI from 'app/constants/abis/zenko.json' +import { AUTONOMY_REGISTRY_ADDRESSES, STOP_LIMIT_ORDER_WRAPPER_ADDRESSES } from 'app/constants/autonomy' import LPToken from 'app/features/migration/LPToken' import { poolEntityMapper } from 'app/features/trident/poolEntityMapper' import { getContract } from 'app/functions' @@ -420,3 +423,17 @@ export function useDashboardContract(): Contract | null { export function useQuickSwapFactoryContract(): Contract | null { return useContract('0x5757371414417b8C6CAad45bAeF941aBc7d3Ab32', FACTORY_ABI, false) } + +export function useAutonomyRegistryContract(): Contract | null { + const { chainId } = useActiveWeb3React() + return useContract(chainId ? AUTONOMY_REGISTRY_ADDRESSES[chainId] : undefined, AUTONOMY_REGISTRY_ABI, true) +} + +export function useAutonomyLimitOrderWrapperContract(): Contract | null { + const { chainId } = useActiveWeb3React() + return useContract( + chainId ? STOP_LIMIT_ORDER_WRAPPER_ADDRESSES[chainId] : undefined, + STOP_LIMIT_ORDER_WRAPPER_ABI, + true + ) +} diff --git a/src/pages/stop-loss/StopLossReviewModal.tsx b/src/pages/stop-loss/StopLossReviewModal.tsx new file mode 100644 index 0000000000..a945f76184 --- /dev/null +++ b/src/pages/stop-loss/StopLossReviewModal.tsx @@ -0,0 +1,139 @@ +import { ArrowDownIcon } from '@heroicons/react/outline' +import { t } from '@lingui/macro' +import { useLingui } from '@lingui/react' +import { Currency, CurrencyAmount, Percent, Price, Trade, TradeType, ZERO } from '@sushiswap/core-sdk' +import Button from 'app/components/Button' +import ListPanel from 'app/components/ListPanel' +import { HeadlessUiModal } from 'app/components/Modal' +import Typography from 'app/components/Typography' +import TradePrice from 'app/features/legacy/swap/TradePrice' +import useStopLossExecute, { useDiffOfStopAndMinimumRate } from 'app/features/stop-loss/useStopLossExecute' +import { isAddress, shortenAddress } from 'app/functions' +import { useAppDispatch } from 'app/state/hooks' +import { setLimitOrderShowReview } from 'app/state/limit-order/actions' +import { useLimitOrderState, useStopLossDerivedLimitPrice } from 'app/state/limit-order/hooks' +import React, { FC, useCallback, useMemo, useState } from 'react' + +interface StopLossReviewModal { + trade?: Trade + limitPrice?: Price + parsedAmounts: { + inputAmount?: CurrencyAmount + outputAmount?: CurrencyAmount + } +} + +const StopLossReviewModal: FC = ({ parsedAmounts, trade, limitPrice }) => { + const [inverted, setInverted] = useState(false) + const { showReview, orderExpiration, recipient, attemptingTxn } = useLimitOrderState() + const dispatch = useAppDispatch() + const { i18n } = useLingui() + const { execute } = useStopLossExecute() + + const { externalAmountForFee } = useDiffOfStopAndMinimumRate({ + inputAmount: parsedAmounts.inputAmount, + }) + + const stopRate = useStopLossDerivedLimitPrice() + const _execute = useCallback(() => { + if (parsedAmounts?.inputAmount && parsedAmounts?.outputAmount && parseInt(externalAmountForFee) > 0) { + execute({ + orderExpiration: orderExpiration.value, + recipient, + outputAmount: parsedAmounts.outputAmount, + amountExternal: parsedAmounts.outputAmount?.add( + CurrencyAmount.fromRawAmount(parsedAmounts.outputAmount?.currency, externalAmountForFee) + ), // outputAmount + feeRelatedAmount + inputAmount: parsedAmounts.inputAmount, + stopPrice: stopRate, + }) + } + }, [execute, orderExpiration.value, parsedAmounts.inputAmount, parsedAmounts.outputAmount, recipient]) + + const deviation = useMemo(() => { + if (limitPrice && trade) { + const { numerator, denominator } = limitPrice.subtract(trade.executionPrice).divide(trade.executionPrice) + return new Percent(numerator, denominator) + } + }, [limitPrice, trade]) + + return ( + dispatch(setLimitOrderShowReview(false))} + maxWidth="sm" + > +
+ dispatch(setLimitOrderShowReview(false))} + /> + + + {i18n._(t`You'll pay`)} + + ]} /> +
+ +
+ + {i18n._(t`You'll receive`)} + + ]} /> +
+ + {recipient && ( +
+ + {i18n._(t`Recipient`)} + + + {isAddress(recipient) && shortenAddress(recipient)} + +
+ )} +
+ + {i18n._(t`Expiration`)} + + + {orderExpiration.label} + +
+
+
+ + {i18n._(t`Rate`)} + + +
+ {deviation && ( +
+ + {deviation.toSignificant(2)}% {deviation?.greaterThan(ZERO) ? 'above' : 'below'} market rate + +
+ )} +
+
+ + {i18n._(t`Please note that after order execution, your tokens will be received in your BentoBox`)} + + +
+
+ ) +} + +export default StopLossReviewModal diff --git a/src/pages/stop-loss/[[...tokens]].tsx b/src/pages/stop-loss/[[...tokens]].tsx new file mode 100644 index 0000000000..791f166a05 --- /dev/null +++ b/src/pages/stop-loss/[[...tokens]].tsx @@ -0,0 +1,215 @@ +import { CheckIcon, SwitchVerticalIcon } from '@heroicons/react/outline' +import { t } from '@lingui/macro' +import { useLingui } from '@lingui/react' +import { Percent } from '@sushiswap/core-sdk' +import limitOrderPairList from '@sushiswap/limit-order-pair-list/dist/limit-order.pairlist.json' +import CloseIcon from 'app/components/CloseIcon' +import RecipientField from 'app/components/RecipientField' +import Switch from 'app/components/Switch' +import Typography from 'app/components/Typography' +import { ZERO_PERCENT } from 'app/constants' +import { Feature } from 'app/enums' +import LimitOrderApprovalCheck from 'app/features/legacy/limit-order/LimitOrderApprovalCheck' +import LimitPriceInputPanel from 'app/features/stop-loss/LimitPriceInputPanel' +import StopLimitOrderButton from 'app/features/stop-loss/StopLimitOrderButton' +import StopPriceInputPanel from 'app/features/stop-loss/StopPriceInputPanel' +// import OrderExpirationDropdown from 'app/features/legacy/limit-order/OrderExpirationDropdown' +import HeaderNew from 'app/features/trade/HeaderNew' +import SwapAssetPanel from 'app/features/trident/swap/SwapAssetPanel' +import { featureEnabled } from 'app/functions' +import NetworkGuard from 'app/guards/Network' +import { SwapLayout, SwapLayoutCard } from 'app/layouts/SwapLayout' +import { useActiveWeb3React } from 'app/services/web3' +import { useAppDispatch } from 'app/state/hooks' +import { + Field, + setFromBentoBalance, + setRecipient, + toggleEnableHigherStopRateThanMarketPrice, +} from 'app/state/limit-order/actions' +import useLimitOrderDerivedCurrencies, { + useLimitOrderActionHandlers, + useLimitOrderDerivedLimitPrice, + useLimitOrderDerivedParsedAmounts, + useLimitOrderDerivedTrade, + useLimitOrderState, + useStopLossDerivedLimitPrice, +} from 'app/state/limit-order/hooks' +import { useExpertModeManager } from 'app/state/user/hooks' +import { NextSeo } from 'next-seo' +import React, { useMemo } from 'react' + +import StopLossReviewModal from './StopLossReviewModal' + +const StopLoss = () => { + const { i18n } = useLingui() + const dispatch = useAppDispatch() + const { chainId } = useActiveWeb3React() + const [isExpertMode] = useExpertModeManager() + const { enableHigherStopRateThanMarketPrice, typedField, typedValue, fromBentoBalance, recipient } = + useLimitOrderState() + const { inputCurrency, outputCurrency } = useLimitOrderDerivedCurrencies() + const trade = useLimitOrderDerivedTrade() + const rate = useLimitOrderDerivedLimitPrice() + const stopRate = useStopLossDerivedLimitPrice() + const parsedAmounts = useLimitOrderDerivedParsedAmounts({ rate, trade }) + const { onSwitchTokens, onCurrencySelection, onUserInput } = useLimitOrderActionHandlers() + + const pairs = useMemo( + // @ts-ignore TYPE NEEDS FIXING + () => (limitOrderPairList.pairs[chainId || 1] || []).map(([token0, token1]) => [token0.address, token1.address]), + [chainId] + ) + + const inputPanelHelperText = useMemo(() => { + if (rate && trade) { + const { numerator, denominator } = rate.subtract(trade.executionPrice).divide(trade.executionPrice) + return new Percent(numerator, denominator) + } + }, [rate, trade]) + + const inputTokenList = useMemo(() => { + if (pairs.length === 0) return [] + // @ts-ignore TYPE NEEDS FIXING + return pairs.reduce((acc, [token0, token1]) => { + acc.push(token0) + acc.push(token1) + return acc + }, []) + }, [pairs]) + + const outputTokenList = useMemo(() => { + if (pairs.length === 0) return [] + if (inputCurrency) { + // @ts-ignore TYPE NEEDS FIXING + return pairs.reduce((acc, [token0, token1]) => { + if (inputCurrency.wrapped.address === token0) acc.push(token1) + if (inputCurrency.wrapped.address === token1) acc.push(token0) + return acc + }, []) + } + // @ts-ignore TYPE NEEDS FIXING + return pairs.reduce((acc, [token0, token1]) => { + acc.push(token0) + acc.push(token1) + return acc + }, []) + }, [inputCurrency, pairs]) + + return ( + <> + + + +
+ {chainId && ( + + )} +
+
+ ( + dispatch(setFromBentoBalance(!fromBentoBalance))} + /> + )} + selected={true} + spendFromWallet={!fromBentoBalance} + currency={inputCurrency} + value={(typedField === Field.INPUT ? typedValue : parsedAmounts?.inputAmount?.toSignificant(6)) || ''} + onChange={(value) => onUserInput(Field.INPUT, value || '')} + onSelect={(inputCurrency) => onCurrencySelection(Field.INPUT, inputCurrency)} + currencies={inputTokenList} + /> + {isExpertMode && ( +
+
+ + {i18n._(t`Enable larger stop rate than market price`)} + +
+ dispatch(toggleEnableHigherStopRateThanMarketPrice())} + checkedIcon={} + uncheckedIcon={} + color="gradient" + /> +
+ )} +
+
+ +
+ +
+ +
+ {/*
+ +
*/} +
+ onUserInput(Field.OUTPUT, value || '')} + onSelect={(outputCurrency) => onCurrencySelection(Field.OUTPUT, outputCurrency)} + currencies={outputTokenList} + priceImpact={inputPanelHelperText} + priceImpactCss={inputPanelHelperText?.greaterThan(ZERO_PERCENT) ? 'text-green' : 'text-red'} + /> +
+ + {isExpertMode && } + +
+ Powered by  + + Autonomy Network + +
+ + + +
+ + {i18n._(t`Stop orders use funds from BentoBox, to create a stop order depositing into BentoBox is required.`)} + + + + Tip + + :{' '} + {i18n._(t`When expert mode is enabled, balance isn't checked when creating orders. You can use this to chain stop + orders.`)} + + + ) +} + +StopLoss.Guard = NetworkGuard(Feature.LIMIT_ORDERS) +StopLoss.Layout = SwapLayout('limit-order-page') + +export default StopLoss diff --git a/src/pages/stop-loss/history.tsx b/src/pages/stop-loss/history.tsx new file mode 100644 index 0000000000..742854952c --- /dev/null +++ b/src/pages/stop-loss/history.tsx @@ -0,0 +1,46 @@ +import { t } from '@lingui/macro' +import { useLingui } from '@lingui/react' +import { STOP_LIMIT_ORDER_ADDRESS } from '@sushiswap/limit-order-sdk' +import Typography from 'app/components/Typography' +import { Feature } from 'app/enums' +import LimitOrderApprovalCheck from 'app/features/legacy/limit-order/LimitOrderApprovalCheck' +import useLimitOrders from 'app/features/legacy/limit-order/useLimitOrders' +import CompletedOrders from 'app/features/stop-loss/CompletedOrders' +import DiscoverHeader from 'app/features/stop-loss/DiscoverHeader' +import OrdersTableToggle from 'app/features/stop-loss/OrderTableToggle' +import NetworkGuard from 'app/guards/Network' +import { TridentBody } from 'app/layouts/Trident' +import { useActiveWeb3React } from 'app/services/web3' +import { useBentoMasterContractAllowed } from 'app/state/bentobox/hooks' +import React from 'react' + +function OpenOrdersPage() { + const { chainId, account } = useActiveWeb3React() + const { i18n } = useLingui() + const { pending } = useLimitOrders() + const masterContract = chainId ? STOP_LIMIT_ORDER_ADDRESS[chainId] : undefined + const allowed = useBentoMasterContractAllowed(masterContract, account ?? undefined) + + return ( + <> + + + + {pending.totalOrders > 0 && typeof allowed !== 'undefined' && !allowed && ( +
+ + {i18n._(t`It seems like you have open orders while the limit order master contract is not yet approved. Please make + sure you have approved the limit order master contract or the order will not execute`)} + +
+ )} + + +
+ + ) +} + +OpenOrdersPage.Guard = NetworkGuard(Feature.LIMIT_ORDERS) + +export default OpenOrdersPage diff --git a/src/pages/stop-loss/open.tsx b/src/pages/stop-loss/open.tsx new file mode 100644 index 0000000000..800007c965 --- /dev/null +++ b/src/pages/stop-loss/open.tsx @@ -0,0 +1,46 @@ +import { t } from '@lingui/macro' +import { useLingui } from '@lingui/react' +import { STOP_LIMIT_ORDER_ADDRESS } from '@sushiswap/limit-order-sdk' +import Typography from 'app/components/Typography' +import { Feature } from 'app/enums' +import LimitOrderApprovalCheck from 'app/features/legacy/limit-order/LimitOrderApprovalCheck' +import useLimitOrders from 'app/features/legacy/limit-order/useLimitOrders' +import DiscoverHeader from 'app/features/stop-loss/DiscoverHeader' +import OpenOrders from 'app/features/stop-loss/OpenOrders' +import OrdersTableToggle from 'app/features/stop-loss/OrderTableToggle' +import NetworkGuard from 'app/guards/Network' +import { TridentBody } from 'app/layouts/Trident' +import { useActiveWeb3React } from 'app/services/web3' +import { useBentoMasterContractAllowed } from 'app/state/bentobox/hooks' +import React from 'react' + +function OpenOrdersPage() { + const { chainId, account } = useActiveWeb3React() + const { i18n } = useLingui() + const { pending } = useLimitOrders() + const masterContract = chainId ? STOP_LIMIT_ORDER_ADDRESS[chainId] : undefined + const allowed = useBentoMasterContractAllowed(masterContract, account ?? undefined) + + return ( + <> + + + + {pending.totalOrders > 0 && typeof allowed !== 'undefined' && !allowed && ( +
+ + {i18n._(t`It seems like you have open orders while the limit order master contract is not yet approved. Please make + sure you have approved the limit order master contract or the order will not execute`)} + +
+ )} + + +
+ + ) +} + +OpenOrdersPage.Guard = NetworkGuard(Feature.LIMIT_ORDERS) + +export default OpenOrdersPage diff --git a/src/state/limit-order/actions.ts b/src/state/limit-order/actions.ts index 1b9218d74c..c5d4b5dd66 100644 --- a/src/state/limit-order/actions.ts +++ b/src/state/limit-order/actions.ts @@ -11,12 +11,21 @@ export enum LimitPrice { } export const setLimitPrice = createAction('limit-order/setLimitPrice') +export const setStopPrice = createAction('limit-order/setStopPrice') export const setLimitOrderBentoPermit = createAction('limit-order/setLimitBentoPermit') export const setLimitOrderAttemptingTxn = createAction('limit-order/setLimitAttemptingTxn') export const setLimitOrderInvertRate = createAction('limit-order/setLimitOrderInvertRate') export const setLimitOrderInvertState = createAction<{ invertRate: boolean; limitPrice: string }>( 'limit-order/setLimitOrderInvertState' ) +export const setStopLossInvertRate = createAction('limit-order/setStopLossInvertRate') +export const setStopLossInvertState = createAction<{ invertStopRate: boolean; stopPrice: string }>( + 'limit-order/setStopLossInvertState' +) +export const toggleEnableHigherStopRateThanMarketPrice = createAction( + 'limit-order/toggleEnableHigherStopRateThanMarketPrice' +) + export const setOrderExpiration = createAction< | { value: string @@ -35,6 +44,7 @@ export const replaceLimitOrderState = createAction<{ recipient?: string fromBentoBalance?: boolean limitPrice?: string + stopPrice?: string orderExpiration?: { value: string; label: string } }>('limit-order/replaceLimitOrderState') export const selectCurrency = createAction<{ diff --git a/src/state/limit-order/hooks.ts b/src/state/limit-order/hooks.ts index be14e21c9e..6fb1810098 100644 --- a/src/state/limit-order/hooks.ts +++ b/src/state/limit-order/hooks.ts @@ -214,6 +214,19 @@ export const useLimitOrderDerivedLimitPrice: UseLimitOrderDerivedLimitPrice = () : undefined }, [inputCurrency, invertRate, limitPrice, outputCurrency]) } +export const useStopLossDerivedLimitPrice: UseLimitOrderDerivedLimitPrice = () => { + const { stopPrice, invertStopRate } = useLimitOrderState() + const { inputCurrency, outputCurrency } = useLimitOrderDerivedCurrencies() + + return useMemo(() => { + const baseAmount = tryParseAmount(invertStopRate ? stopPrice : '1', inputCurrency) + const quoteAmount = tryParseAmount(invertStopRate ? '1' : stopPrice, outputCurrency) + + return baseAmount && quoteAmount && inputCurrency && outputCurrency + ? new Price({ baseAmount, quoteAmount }) + : undefined + }, [inputCurrency, invertStopRate, stopPrice, outputCurrency]) +} type UseLimitOrderDerivedParsedAmounts = ({ rate, @@ -261,16 +274,45 @@ export const useLimitOrderDerivedTypedInputAmount: UseLimitOrderDerivedTypedInpu }, [inputCurrency, outputCurrency, typedField, typedValue]) } -type UseLimitOrderDerivedInputError = ({ trade }: { trade?: Trade }) => string -export const useLimitOrderDerivedInputError: UseLimitOrderDerivedInputError = ({ trade }) => { - const { recipient, orderExpiration, fromBentoBalance, limitPrice, typedValue } = useLimitOrderState() +type UseLimitOrderDerivedInputError = ({ + trade, + isStopLossOrder, +}: { + trade?: Trade + isStopLossOrder?: boolean +}) => string +export const useLimitOrderDerivedInputError: UseLimitOrderDerivedInputError = ({ trade, isStopLossOrder }) => { + const { + enableHigherStopRateThanMarketPrice, + recipient, + orderExpiration, + fromBentoBalance, + limitPrice, + stopPrice, + typedValue, + } = useLimitOrderState() const { account } = useActiveWeb3React() const { inputCurrency, outputCurrency } = useLimitOrderDerivedCurrencies() + const rate = useLimitOrderDerivedLimitPrice() + const stopRate = useStopLossDerivedLimitPrice() const recipientLookup = useENS(recipient) const to = !recipient ? account : recipientLookup.address const parsedRate = useLimitOrderDerivedLimitPrice() const balance = useBentoOrWalletBalance(account ?? undefined, inputCurrency, !fromBentoBalance) const [expertMode] = useExpertModeManager() + const limitPriceOrDefaultPrice = useMemo(() => (rate ? rate : trade?.executionPrice), [rate, trade]) + const stopPriceOrDefaultPrice = useMemo(() => (stopPrice ? stopRate : trade?.executionPrice), [stopPrice, trade]) + const isLimitPriceBiggerThanStopPrice = useMemo( + () => + limitPriceOrDefaultPrice && + stopPriceOrDefaultPrice && + parseFloat(limitPriceOrDefaultPrice?.toSignificant(6)) >= parseFloat(stopPriceOrDefaultPrice?.toSignificant(6)), + [limitPriceOrDefaultPrice, stopPriceOrDefaultPrice] + ) + const isStopPriceLargerThanMarketPrice = useMemo( + () => !enableHigherStopRateThanMarketPrice && trade && stopRate?.greaterThan(trade?.executionPrice), + [stopRate, trade, enableHigherStopRateThanMarketPrice] + ) return useMemo(() => { return !account @@ -283,6 +325,14 @@ export const useLimitOrderDerivedInputError: UseLimitOrderDerivedInputError = ({ ? i18n._(t`Enter a valid recipient address`) : limitPrice !== LimitPrice.CURRENT && parsedRate?.equalTo(ZERO) ? i18n._(t`Select a rate`) + : stopPrice !== LimitPrice.CURRENT && parsedRate?.equalTo(ZERO) + ? i18n._(t`Select a rate`) + : isStopLossOrder && (!rate || !stopRate) + ? i18n._(t`Fill all inputs to continue`) // for stop-loss orders only + : isStopLossOrder && isLimitPriceBiggerThanStopPrice // for stop-loss orders only + ? 'Minimum larger than stop rate' + : isStopLossOrder && isStopPriceLargerThanMarketPrice // for stop-loss orders only + ? 'Stop rate larger than market price' : !orderExpiration ? i18n._(t`Select an order expiration`) : !balance @@ -296,6 +346,7 @@ export const useLimitOrderDerivedInputError: UseLimitOrderDerivedInputError = ({ expertMode, inputCurrency, limitPrice, + stopPrice, orderExpiration, outputCurrency, parsedRate, diff --git a/src/state/limit-order/reducer.ts b/src/state/limit-order/reducer.ts index 52f156ccf6..07488a8708 100644 --- a/src/state/limit-order/reducer.ts +++ b/src/state/limit-order/reducer.ts @@ -17,7 +17,11 @@ import { setLimitPrice, setOrderExpiration, setRecipient, + setStopLossInvertRate, + setStopLossInvertState, + setStopPrice, switchCurrencies, + toggleEnableHigherStopRateThanMarketPrice, typeInput, } from './actions' @@ -33,6 +37,7 @@ export interface LimitOrderState { readonly typedField: Field readonly typedValue: string readonly limitPrice: string + readonly stopPrice: string readonly inputCurrencyId: string readonly outputCurrencyId: string readonly recipient?: string @@ -46,12 +51,15 @@ export interface LimitOrderState { readonly attemptingTxn: boolean readonly showReview: boolean readonly invertRate: boolean + readonly invertStopRate: boolean + readonly enableHigherStopRateThanMarketPrice: boolean } const initialState: LimitOrderState = { typedField: Field.INPUT, typedValue: '', limitPrice: '', + stopPrice: '', inputCurrencyId: 'ETH', outputCurrencyId: 'SUSHI', recipient: undefined, @@ -65,6 +73,8 @@ const initialState: LimitOrderState = { attemptingTxn: false, showReview: false, invertRate: false, + invertStopRate: false, + enableHigherStopRateThanMarketPrice: false, } export default createReducer(initialState, (builder) => @@ -83,6 +93,7 @@ export default createReducer(initialState, (builder) => outputCurrencyId, fromBentoBalance, limitPrice, + stopPrice, orderExpiration, }, } @@ -94,6 +105,7 @@ export default createReducer(initialState, (builder) => recipient, fromBentoBalance, limitPrice, + stopPrice, orderExpiration, limitOrderApprovalPending: state.limitOrderApprovalPending, }) @@ -102,6 +114,10 @@ export default createReducer(initialState, (builder) => // @ts-ignore TYPE NEEDS FIXING state.limitPrice = limitPrice }) + .addCase(setStopPrice, (state, { payload: stopPrice }) => { + // @ts-ignore TYPE NEEDS FIXING + state.stopPrice = stopPrice + }) .addCase(setLimitOrderApprovalPending, (state, { payload: limitOrderApprovalPending }) => { state.limitOrderApprovalPending = limitOrderApprovalPending }) @@ -157,6 +173,16 @@ export default createReducer(initialState, (builder) => state.invertRate = invertRate state.limitPrice = limitPrice }) + .addCase(setStopLossInvertRate, (state, { payload: invertStopRate }) => { + state.invertStopRate = invertStopRate + }) + .addCase(setStopLossInvertState, (state, { payload: { invertStopRate, stopPrice } }) => { + state.invertStopRate = invertStopRate + state.stopPrice = stopPrice + }) + .addCase(toggleEnableHigherStopRateThanMarketPrice, (state) => { + state.enableHigherStopRateThanMarketPrice = !state.enableHigherStopRateThanMarketPrice + }) ) type SelectLimitOrder = (state: AppState) => LimitOrderState diff --git a/yarn.lock b/yarn.lock index 493f858beb..dd793e6a3e 100644 --- a/yarn.lock +++ b/yarn.lock @@ -959,6 +959,14 @@ "@babel/plugin-transform-react-jsx-development" "^7.18.6" "@babel/plugin-transform-react-pure-annotations" "^7.18.6" +"@babel/runtime-corejs3@7.16.8": + version "7.16.8" + resolved "https://registry.yarnpkg.com/@babel/runtime-corejs3/-/runtime-corejs3-7.16.8.tgz#ea533d96eda6fdc76b1812248e9fbd0c11d4a1a7" + integrity sha512-3fKhuICS1lMz0plI5ktOE/yEtBRMVxplzRkdn6mJQ197XiY0JnrzYV0+Mxozq3JZ8SBV9Ecurmw1XsGbwOf+Sg== + dependencies: + core-js-pure "^3.20.2" + regenerator-runtime "^0.13.4" + "@babel/runtime-corejs3@^7.10.2": version "7.18.6" resolved "https://registry.yarnpkg.com/@babel/runtime-corejs3/-/runtime-corejs3-7.18.6.tgz#6f02c5536911f4b445946a2179554b95c8838635" @@ -967,6 +975,13 @@ core-js-pure "^3.20.2" regenerator-runtime "^0.13.4" +"@babel/runtime@7.16.7": + version "7.16.7" + resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.16.7.tgz#03ff99f64106588c9c403c6ecb8c3bafbbdff1fa" + integrity sha512-9E9FJowqAsytyOY6LG+1KuueckRL+aQW+mKvXRXnuFGyRAyepJPmEo9vgMfXUA6O9u3IeEdv9MAkppFcaQwogQ== + dependencies: + regenerator-runtime "^0.13.4" + "@babel/runtime@7.3.4": version "7.3.4" resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.3.4.tgz#73d12ba819e365fcf7fd152aed56d6df97d21c83" @@ -1379,7 +1394,22 @@ "@ethereumjs/common" "^2.6.4" ethereumjs-util "^7.1.5" -"@ethersproject/abi@5.6.4", "@ethersproject/abi@^5.4.0", "@ethersproject/abi@^5.6.3": +"@ethersproject/abi@5.6.0": + version "5.6.0" + resolved "https://registry.yarnpkg.com/@ethersproject/abi/-/abi-5.6.0.tgz#ea07cbc1eec2374d32485679c12408005895e9f3" + integrity sha512-AhVByTwdXCc2YQ20v300w6KVHle9g2OFc28ZAFCPnJyEpkv1xKXjZcSTgWOlv1i+0dqlgF8RCF2Rn2KC1t+1Vg== + dependencies: + "@ethersproject/address" "^5.6.0" + "@ethersproject/bignumber" "^5.6.0" + "@ethersproject/bytes" "^5.6.0" + "@ethersproject/constants" "^5.6.0" + "@ethersproject/hash" "^5.6.0" + "@ethersproject/keccak256" "^5.6.0" + "@ethersproject/logger" "^5.6.0" + "@ethersproject/properties" "^5.6.0" + "@ethersproject/strings" "^5.6.0" + +"@ethersproject/abi@5.6.4", "@ethersproject/abi@^5.4.0", "@ethersproject/abi@^5.6.0", "@ethersproject/abi@^5.6.3": version "5.6.4" resolved "https://registry.yarnpkg.com/@ethersproject/abi/-/abi-5.6.4.tgz#f6e01b6ed391a505932698ecc0d9e7a99ee60362" integrity sha512-TTeZUlCeIHG6527/2goZA6gW5F8Emoc7MrZDC7hhP84aRGvW3TEdTnZR08Ls88YXM1m2SuK42Osw/jSi3uO8gg== @@ -1394,7 +1424,20 @@ "@ethersproject/properties" "^5.6.0" "@ethersproject/strings" "^5.6.1" -"@ethersproject/abstract-provider@5.6.1", "@ethersproject/abstract-provider@^5.4.0", "@ethersproject/abstract-provider@^5.6.1": +"@ethersproject/abstract-provider@5.6.0": + version "5.6.0" + resolved "https://registry.yarnpkg.com/@ethersproject/abstract-provider/-/abstract-provider-5.6.0.tgz#0c4ac7054650dbd9c476cf5907f588bbb6ef3061" + integrity sha512-oPMFlKLN+g+y7a79cLK3WiLcjWFnZQtXWgnLAbHZcN3s7L4v90UHpTOrLk+m3yr0gt+/h9STTM6zrr7PM8uoRw== + dependencies: + "@ethersproject/bignumber" "^5.6.0" + "@ethersproject/bytes" "^5.6.0" + "@ethersproject/logger" "^5.6.0" + "@ethersproject/networks" "^5.6.0" + "@ethersproject/properties" "^5.6.0" + "@ethersproject/transactions" "^5.6.0" + "@ethersproject/web" "^5.6.0" + +"@ethersproject/abstract-provider@5.6.1", "@ethersproject/abstract-provider@^5.4.0", "@ethersproject/abstract-provider@^5.6.0", "@ethersproject/abstract-provider@^5.6.1": version "5.6.1" resolved "https://registry.yarnpkg.com/@ethersproject/abstract-provider/-/abstract-provider-5.6.1.tgz#02ddce150785caf0c77fe036a0ebfcee61878c59" integrity sha512-BxlIgogYJtp1FS8Muvj8YfdClk3unZH0vRMVX791Z9INBNT/kuACZ9GzaY1Y4yFq+YSy6/w4gzj3HCRKrK9hsQ== @@ -1407,7 +1450,18 @@ "@ethersproject/transactions" "^5.6.2" "@ethersproject/web" "^5.6.1" -"@ethersproject/abstract-signer@5.6.2", "@ethersproject/abstract-signer@^5", "@ethersproject/abstract-signer@^5.4.0", "@ethersproject/abstract-signer@^5.6.2": +"@ethersproject/abstract-signer@5.6.0": + version "5.6.0" + resolved "https://registry.yarnpkg.com/@ethersproject/abstract-signer/-/abstract-signer-5.6.0.tgz#9cd7ae9211c2b123a3b29bf47aab17d4d016e3e7" + integrity sha512-WOqnG0NJKtI8n0wWZPReHtaLkDByPL67tn4nBaDAhmVq8sjHTPbCdz4DRhVu/cfTOvfy9w3iq5QZ7BX7zw56BQ== + dependencies: + "@ethersproject/abstract-provider" "^5.6.0" + "@ethersproject/bignumber" "^5.6.0" + "@ethersproject/bytes" "^5.6.0" + "@ethersproject/logger" "^5.6.0" + "@ethersproject/properties" "^5.6.0" + +"@ethersproject/abstract-signer@5.6.2", "@ethersproject/abstract-signer@^5", "@ethersproject/abstract-signer@^5.4.0", "@ethersproject/abstract-signer@^5.6.0", "@ethersproject/abstract-signer@^5.6.2": version "5.6.2" resolved "https://registry.yarnpkg.com/@ethersproject/abstract-signer/-/abstract-signer-5.6.2.tgz#491f07fc2cbd5da258f46ec539664713950b0b33" integrity sha512-n1r6lttFBG0t2vNiI3HoWaS/KdOt8xyDjzlP2cuevlWLG6EX0OwcKLyG/Kp/cuwNxdy/ous+R/DEMdTUwWQIjQ== @@ -1418,7 +1472,18 @@ "@ethersproject/logger" "^5.6.0" "@ethersproject/properties" "^5.6.0" -"@ethersproject/address@5.6.1", "@ethersproject/address@^5", "@ethersproject/address@^5.4.0", "@ethersproject/address@^5.6.1": +"@ethersproject/address@5.6.0": + version "5.6.0" + resolved "https://registry.yarnpkg.com/@ethersproject/address/-/address-5.6.0.tgz#13c49836d73e7885fc148ad633afad729da25012" + integrity sha512-6nvhYXjbXsHPS+30sHZ+U4VMagFC/9zAk6Gd/h3S21YW4+yfb0WfRtaAIZ4kfM4rrVwqiy284LP0GtL5HXGLxQ== + dependencies: + "@ethersproject/bignumber" "^5.6.0" + "@ethersproject/bytes" "^5.6.0" + "@ethersproject/keccak256" "^5.6.0" + "@ethersproject/logger" "^5.6.0" + "@ethersproject/rlp" "^5.6.0" + +"@ethersproject/address@5.6.1", "@ethersproject/address@^5", "@ethersproject/address@^5.4.0", "@ethersproject/address@^5.6.0", "@ethersproject/address@^5.6.1": version "5.6.1" resolved "https://registry.yarnpkg.com/@ethersproject/address/-/address-5.6.1.tgz#ab57818d9aefee919c5721d28cd31fd95eff413d" integrity sha512-uOgF0kS5MJv9ZvCz7x6T2EXJSzotiybApn4XlOgoTX0xdtyVIJ7pF+6cGPxiEq/dpBiTfMiw7Yc81JcwhSYA0Q== @@ -1429,14 +1494,29 @@ "@ethersproject/logger" "^5.6.0" "@ethersproject/rlp" "^5.6.1" -"@ethersproject/base64@5.6.1", "@ethersproject/base64@^5.6.1": +"@ethersproject/base64@5.6.0": + version "5.6.0" + resolved "https://registry.yarnpkg.com/@ethersproject/base64/-/base64-5.6.0.tgz#a12c4da2a6fb86d88563216b0282308fc15907c9" + integrity sha512-2Neq8wxJ9xHxCF9TUgmKeSh9BXJ6OAxWfeGWvbauPh8FuHEjamgHilllx8KkSd5ErxyHIX7Xv3Fkcud2kY9ezw== + dependencies: + "@ethersproject/bytes" "^5.6.0" + +"@ethersproject/base64@5.6.1", "@ethersproject/base64@^5.6.0", "@ethersproject/base64@^5.6.1": version "5.6.1" resolved "https://registry.yarnpkg.com/@ethersproject/base64/-/base64-5.6.1.tgz#2c40d8a0310c9d1606c2c37ae3092634b41d87cb" integrity sha512-qB76rjop6a0RIYYMiB4Eh/8n+Hxu2NIZm8S/Q7kNo5pmZfXhHGHmS4MinUainiBC54SCyRnwzL+KZjj8zbsSsw== dependencies: "@ethersproject/bytes" "^5.6.1" -"@ethersproject/basex@5.6.1", "@ethersproject/basex@^5.6.1": +"@ethersproject/basex@5.6.0": + version "5.6.0" + resolved "https://registry.yarnpkg.com/@ethersproject/basex/-/basex-5.6.0.tgz#9ea7209bf0a1c3ddc2a90f180c3a7f0d7d2e8a69" + integrity sha512-qN4T+hQd/Md32MoJpc69rOwLYRUXwjTlhHDIeUkUmiN/JyWkkLLMoG0TqvSQKNqZOMgN5stbUYN6ILC+eD7MEQ== + dependencies: + "@ethersproject/bytes" "^5.6.0" + "@ethersproject/properties" "^5.6.0" + +"@ethersproject/basex@5.6.1", "@ethersproject/basex@^5.6.0", "@ethersproject/basex@^5.6.1": version "5.6.1" resolved "https://registry.yarnpkg.com/@ethersproject/basex/-/basex-5.6.1.tgz#badbb2f1d4a6f52ce41c9064f01eab19cc4c5305" integrity sha512-a52MkVz4vuBXR06nvflPMotld1FJWSj2QT0985v7P/emPZO00PucFAkbcmq2vpVU7Ts7umKiSI6SppiLykVWsA== @@ -1444,7 +1524,16 @@ "@ethersproject/bytes" "^5.6.1" "@ethersproject/properties" "^5.6.0" -"@ethersproject/bignumber@5.6.2", "@ethersproject/bignumber@^5.4.0", "@ethersproject/bignumber@^5.6.2": +"@ethersproject/bignumber@5.6.0": + version "5.6.0" + resolved "https://registry.yarnpkg.com/@ethersproject/bignumber/-/bignumber-5.6.0.tgz#116c81b075c57fa765a8f3822648cf718a8a0e26" + integrity sha512-VziMaXIUHQlHJmkv1dlcd6GY2PmT0khtAqaMctCIDogxkrarMzA9L94KN1NeXqqOfFD6r0sJT3vCTOFSmZ07DA== + dependencies: + "@ethersproject/bytes" "^5.6.0" + "@ethersproject/logger" "^5.6.0" + bn.js "^4.11.9" + +"@ethersproject/bignumber@5.6.2", "@ethersproject/bignumber@^5.4.0", "@ethersproject/bignumber@^5.6.0", "@ethersproject/bignumber@^5.6.2": version "5.6.2" resolved "https://registry.yarnpkg.com/@ethersproject/bignumber/-/bignumber-5.6.2.tgz#72a0717d6163fab44c47bcc82e0c550ac0315d66" integrity sha512-v7+EEUbhGqT3XJ9LMPsKvXYHFc8eHxTowFCG/HgJErmq4XHJ2WR7aeyICg3uTOAQ7Icn0GFHAohXEhxQHq4Ubw== @@ -1453,20 +1542,50 @@ "@ethersproject/logger" "^5.6.0" bn.js "^5.2.1" -"@ethersproject/bytes@5.6.1", "@ethersproject/bytes@^5.4.0", "@ethersproject/bytes@^5.6.1": +"@ethersproject/bytes@5.6.0": + version "5.6.0" + resolved "https://registry.yarnpkg.com/@ethersproject/bytes/-/bytes-5.6.0.tgz#81652f2a0e04533575befadce555213c11d8aa20" + integrity sha512-3hJPlYemb9V4VLfJF5BfN0+55vltPZSHU3QKUyP9M3Y2TcajbiRrz65UG+xVHOzBereB1b9mn7r12o177xgN7w== + dependencies: + "@ethersproject/logger" "^5.6.0" + +"@ethersproject/bytes@5.6.1", "@ethersproject/bytes@^5.4.0", "@ethersproject/bytes@^5.6.0", "@ethersproject/bytes@^5.6.1": version "5.6.1" resolved "https://registry.yarnpkg.com/@ethersproject/bytes/-/bytes-5.6.1.tgz#24f916e411f82a8a60412344bf4a813b917eefe7" integrity sha512-NwQt7cKn5+ZE4uDn+X5RAXLp46E1chXoaMmrxAyA0rblpxz8t58lVkrHXoRIn0lz1joQElQ8410GqhTqMOwc6g== dependencies: "@ethersproject/logger" "^5.6.0" -"@ethersproject/constants@5.6.1", "@ethersproject/constants@^5.4.0", "@ethersproject/constants@^5.6.1": +"@ethersproject/constants@5.6.0": + version "5.6.0" + resolved "https://registry.yarnpkg.com/@ethersproject/constants/-/constants-5.6.0.tgz#55e3eb0918584d3acc0688e9958b0cedef297088" + integrity sha512-SrdaJx2bK0WQl23nSpV/b1aq293Lh0sUaZT/yYKPDKn4tlAbkH96SPJwIhwSwTsoQQZxuh1jnqsKwyymoiBdWA== + dependencies: + "@ethersproject/bignumber" "^5.6.0" + +"@ethersproject/constants@5.6.1", "@ethersproject/constants@^5.4.0", "@ethersproject/constants@^5.6.0", "@ethersproject/constants@^5.6.1": version "5.6.1" resolved "https://registry.yarnpkg.com/@ethersproject/constants/-/constants-5.6.1.tgz#e2e974cac160dd101cf79fdf879d7d18e8cb1370" integrity sha512-QSq9WVnZbxXYFftrjSjZDUshp6/eKp6qrtdBtUCm0QxCV5z1fG/w3kdlcsjMCQuQHUnAclKoK7XpXMezhRDOLg== dependencies: "@ethersproject/bignumber" "^5.6.2" +"@ethersproject/contracts@5.6.0": + version "5.6.0" + resolved "https://registry.yarnpkg.com/@ethersproject/contracts/-/contracts-5.6.0.tgz#60f2cfc7addd99a865c6c8cfbbcec76297386067" + integrity sha512-74Ge7iqTDom0NX+mux8KbRUeJgu1eHZ3iv6utv++sLJG80FVuU9HnHeKVPfjd9s3woFhaFoQGf3B3iH/FrQmgw== + dependencies: + "@ethersproject/abi" "^5.6.0" + "@ethersproject/abstract-provider" "^5.6.0" + "@ethersproject/abstract-signer" "^5.6.0" + "@ethersproject/address" "^5.6.0" + "@ethersproject/bignumber" "^5.6.0" + "@ethersproject/bytes" "^5.6.0" + "@ethersproject/constants" "^5.6.0" + "@ethersproject/logger" "^5.6.0" + "@ethersproject/properties" "^5.6.0" + "@ethersproject/transactions" "^5.6.0" + "@ethersproject/contracts@5.6.2", "@ethersproject/contracts@^5", "@ethersproject/contracts@^5.4.0": version "5.6.2" resolved "https://registry.yarnpkg.com/@ethersproject/contracts/-/contracts-5.6.2.tgz#20b52e69ebc1b74274ff8e3d4e508de971c287bc" @@ -1492,7 +1611,21 @@ ethers "^5.6.8" scrypt-js "3.0.1" -"@ethersproject/hash@5.6.1", "@ethersproject/hash@^5.4.0", "@ethersproject/hash@^5.6.1": +"@ethersproject/hash@5.6.0": + version "5.6.0" + resolved "https://registry.yarnpkg.com/@ethersproject/hash/-/hash-5.6.0.tgz#d24446a5263e02492f9808baa99b6e2b4c3429a2" + integrity sha512-fFd+k9gtczqlr0/BruWLAu7UAOas1uRRJvOR84uDf4lNZ+bTkGl366qvniUZHKtlqxBRU65MkOobkmvmpHU+jA== + dependencies: + "@ethersproject/abstract-signer" "^5.6.0" + "@ethersproject/address" "^5.6.0" + "@ethersproject/bignumber" "^5.6.0" + "@ethersproject/bytes" "^5.6.0" + "@ethersproject/keccak256" "^5.6.0" + "@ethersproject/logger" "^5.6.0" + "@ethersproject/properties" "^5.6.0" + "@ethersproject/strings" "^5.6.0" + +"@ethersproject/hash@5.6.1", "@ethersproject/hash@^5.4.0", "@ethersproject/hash@^5.6.0", "@ethersproject/hash@^5.6.1": version "5.6.1" resolved "https://registry.yarnpkg.com/@ethersproject/hash/-/hash-5.6.1.tgz#224572ea4de257f05b4abf8ae58b03a67e99b0f4" integrity sha512-L1xAHurbaxG8VVul4ankNX5HgQ8PNCTrnVXEiFnE9xoRnaUcgfD12tZINtDinSllxPLCtGwguQxJ5E6keE84pA== @@ -1506,7 +1639,25 @@ "@ethersproject/properties" "^5.6.0" "@ethersproject/strings" "^5.6.1" -"@ethersproject/hdnode@5.6.2", "@ethersproject/hdnode@^5.6.2": +"@ethersproject/hdnode@5.6.0": + version "5.6.0" + resolved "https://registry.yarnpkg.com/@ethersproject/hdnode/-/hdnode-5.6.0.tgz#9dcbe8d629bbbcf144f2cae476337fe92d320998" + integrity sha512-61g3Jp3nwDqJcL/p4nugSyLrpl/+ChXIOtCEM8UDmWeB3JCAt5FoLdOMXQc3WWkc0oM2C0aAn6GFqqMcS/mHTw== + dependencies: + "@ethersproject/abstract-signer" "^5.6.0" + "@ethersproject/basex" "^5.6.0" + "@ethersproject/bignumber" "^5.6.0" + "@ethersproject/bytes" "^5.6.0" + "@ethersproject/logger" "^5.6.0" + "@ethersproject/pbkdf2" "^5.6.0" + "@ethersproject/properties" "^5.6.0" + "@ethersproject/sha2" "^5.6.0" + "@ethersproject/signing-key" "^5.6.0" + "@ethersproject/strings" "^5.6.0" + "@ethersproject/transactions" "^5.6.0" + "@ethersproject/wordlists" "^5.6.0" + +"@ethersproject/hdnode@5.6.2", "@ethersproject/hdnode@^5.6.0", "@ethersproject/hdnode@^5.6.2": version "5.6.2" resolved "https://registry.yarnpkg.com/@ethersproject/hdnode/-/hdnode-5.6.2.tgz#26f3c83a3e8f1b7985c15d1db50dc2903418b2d2" integrity sha512-tERxW8Ccf9CxW2db3WsN01Qao3wFeRsfYY9TCuhmG0xNpl2IO8wgXU3HtWIZ49gUWPggRy4Yg5axU0ACaEKf1Q== @@ -1524,7 +1675,26 @@ "@ethersproject/transactions" "^5.6.2" "@ethersproject/wordlists" "^5.6.1" -"@ethersproject/json-wallets@5.6.1", "@ethersproject/json-wallets@^5.6.1": +"@ethersproject/json-wallets@5.6.0": + version "5.6.0" + resolved "https://registry.yarnpkg.com/@ethersproject/json-wallets/-/json-wallets-5.6.0.tgz#4c2fc27f17e36c583e7a252fb938bc46f98891e5" + integrity sha512-fmh86jViB9r0ibWXTQipxpAGMiuxoqUf78oqJDlCAJXgnJF024hOOX7qVgqsjtbeoxmcLwpPsXNU0WEe/16qPQ== + dependencies: + "@ethersproject/abstract-signer" "^5.6.0" + "@ethersproject/address" "^5.6.0" + "@ethersproject/bytes" "^5.6.0" + "@ethersproject/hdnode" "^5.6.0" + "@ethersproject/keccak256" "^5.6.0" + "@ethersproject/logger" "^5.6.0" + "@ethersproject/pbkdf2" "^5.6.0" + "@ethersproject/properties" "^5.6.0" + "@ethersproject/random" "^5.6.0" + "@ethersproject/strings" "^5.6.0" + "@ethersproject/transactions" "^5.6.0" + aes-js "3.0.0" + scrypt-js "3.0.1" + +"@ethersproject/json-wallets@5.6.1", "@ethersproject/json-wallets@^5.6.0", "@ethersproject/json-wallets@^5.6.1": version "5.6.1" resolved "https://registry.yarnpkg.com/@ethersproject/json-wallets/-/json-wallets-5.6.1.tgz#3f06ba555c9c0d7da46756a12ac53483fe18dd91" integrity sha512-KfyJ6Zwz3kGeX25nLihPwZYlDqamO6pfGKNnVMWWfEVVp42lTfCZVXXy5Ie8IZTN0HKwAngpIPi7gk4IJzgmqQ== @@ -1543,7 +1713,15 @@ aes-js "3.0.0" scrypt-js "3.0.1" -"@ethersproject/keccak256@5.6.1", "@ethersproject/keccak256@^5.0.0-beta.130", "@ethersproject/keccak256@^5.4.0", "@ethersproject/keccak256@^5.6.1": +"@ethersproject/keccak256@5.6.0": + version "5.6.0" + resolved "https://registry.yarnpkg.com/@ethersproject/keccak256/-/keccak256-5.6.0.tgz#fea4bb47dbf8f131c2e1774a1cecbfeb9d606459" + integrity sha512-tk56BJ96mdj/ksi7HWZVWGjCq0WVl/QvfhFQNeL8fxhBlGoP+L80uDCiQcpJPd+2XxkivS3lwRm3E0CXTfol0w== + dependencies: + "@ethersproject/bytes" "^5.6.0" + js-sha3 "0.8.0" + +"@ethersproject/keccak256@5.6.1", "@ethersproject/keccak256@^5.0.0-beta.130", "@ethersproject/keccak256@^5.4.0", "@ethersproject/keccak256@^5.6.0", "@ethersproject/keccak256@^5.6.1": version "5.6.1" resolved "https://registry.yarnpkg.com/@ethersproject/keccak256/-/keccak256-5.6.1.tgz#b867167c9b50ba1b1a92bccdd4f2d6bd168a91cc" integrity sha512-bB7DQHCTRDooZZdL3lk9wpL0+XuG3XLGHLh3cePnybsO3V0rdCAOQGpn/0R3aODmnTOOkCATJiD2hnL+5bwthA== @@ -1556,14 +1734,29 @@ resolved "https://registry.yarnpkg.com/@ethersproject/logger/-/logger-5.6.0.tgz#d7db1bfcc22fd2e4ab574cba0bb6ad779a9a3e7a" integrity sha512-BiBWllUROH9w+P21RzoxJKzqoqpkyM1pRnEKG69bulE9TSQD8SAIvTQqIMZmmCO8pUNkgLP1wndX1gKghSpBmg== -"@ethersproject/networks@5.6.4", "@ethersproject/networks@^5.6.3": +"@ethersproject/networks@5.6.0": + version "5.6.0" + resolved "https://registry.yarnpkg.com/@ethersproject/networks/-/networks-5.6.0.tgz#486d03fff29b4b6b5414d47a232ded09fe10de5e" + integrity sha512-DaVzgyThzHgSDLuURhvkp4oviGoGe9iTZW4jMEORHDRCgSZ9K9THGFKqL+qGXqPAYLEgZTf5z2w56mRrPR1MjQ== + dependencies: + "@ethersproject/logger" "^5.6.0" + +"@ethersproject/networks@5.6.4", "@ethersproject/networks@^5.6.0", "@ethersproject/networks@^5.6.3": version "5.6.4" resolved "https://registry.yarnpkg.com/@ethersproject/networks/-/networks-5.6.4.tgz#51296d8fec59e9627554f5a8a9c7791248c8dc07" integrity sha512-KShHeHPahHI2UlWdtDMn2lJETcbtaJge4k7XSjDR9h79QTd6yQJmv6Cp2ZA4JdqWnhszAOLSuJEd9C0PRw7hSQ== dependencies: "@ethersproject/logger" "^5.6.0" -"@ethersproject/pbkdf2@5.6.1", "@ethersproject/pbkdf2@^5.6.1": +"@ethersproject/pbkdf2@5.6.0": + version "5.6.0" + resolved "https://registry.yarnpkg.com/@ethersproject/pbkdf2/-/pbkdf2-5.6.0.tgz#04fcc2d7c6bff88393f5b4237d906a192426685a" + integrity sha512-Wu1AxTgJo3T3H6MIu/eejLFok9TYoSdgwRr5oGY1LTLfmGesDoSx05pemsbrPT2gG4cQME+baTSCp5sEo2erZQ== + dependencies: + "@ethersproject/bytes" "^5.6.0" + "@ethersproject/sha2" "^5.6.0" + +"@ethersproject/pbkdf2@5.6.1", "@ethersproject/pbkdf2@^5.6.0", "@ethersproject/pbkdf2@^5.6.1": version "5.6.1" resolved "https://registry.yarnpkg.com/@ethersproject/pbkdf2/-/pbkdf2-5.6.1.tgz#f462fe320b22c0d6b1d72a9920a3963b09eb82d1" integrity sha512-k4gRQ+D93zDRPNUfmduNKq065uadC2YjMP/CqwwX5qG6R05f47boq6pLZtV/RnC4NZAYOPH1Cyo54q0c9sshRQ== @@ -1578,6 +1771,31 @@ dependencies: "@ethersproject/logger" "^5.6.0" +"@ethersproject/providers@5.6.0": + version "5.6.0" + resolved "https://registry.yarnpkg.com/@ethersproject/providers/-/providers-5.6.0.tgz#08ec8e2666771e3a347e66c8f664a2af97366534" + integrity sha512-6+5PKXTWAttJWFWF8+xCDTCa2/dtq9BNrdKQHGl0IyIOwj99vM6OeThmIRcsIAzIOb8m0XS6w+1KFZwrf3j9nw== + dependencies: + "@ethersproject/abstract-provider" "^5.6.0" + "@ethersproject/abstract-signer" "^5.6.0" + "@ethersproject/address" "^5.6.0" + "@ethersproject/basex" "^5.6.0" + "@ethersproject/bignumber" "^5.6.0" + "@ethersproject/bytes" "^5.6.0" + "@ethersproject/constants" "^5.6.0" + "@ethersproject/hash" "^5.6.0" + "@ethersproject/logger" "^5.6.0" + "@ethersproject/networks" "^5.6.0" + "@ethersproject/properties" "^5.6.0" + "@ethersproject/random" "^5.6.0" + "@ethersproject/rlp" "^5.6.0" + "@ethersproject/sha2" "^5.6.0" + "@ethersproject/strings" "^5.6.0" + "@ethersproject/transactions" "^5.6.0" + "@ethersproject/web" "^5.6.0" + bech32 "1.1.4" + ws "7.4.6" + "@ethersproject/providers@5.6.8", "@ethersproject/providers@^5", "@ethersproject/providers@^5.4.0": version "5.6.8" resolved "https://registry.yarnpkg.com/@ethersproject/providers/-/providers-5.6.8.tgz#22e6c57be215ba5545d3a46cf759d265bb4e879d" @@ -1604,7 +1822,15 @@ bech32 "1.1.4" ws "7.4.6" -"@ethersproject/random@5.6.1", "@ethersproject/random@^5.6.1": +"@ethersproject/random@5.6.0": + version "5.6.0" + resolved "https://registry.yarnpkg.com/@ethersproject/random/-/random-5.6.0.tgz#1505d1ab6a250e0ee92f436850fa3314b2cb5ae6" + integrity sha512-si0PLcLjq+NG/XHSZz90asNf+YfKEqJGVdxoEkSukzbnBgC8rydbgbUgBbBGLeHN4kAJwUFEKsu3sCXT93YMsw== + dependencies: + "@ethersproject/bytes" "^5.6.0" + "@ethersproject/logger" "^5.6.0" + +"@ethersproject/random@5.6.1", "@ethersproject/random@^5.6.0", "@ethersproject/random@^5.6.1": version "5.6.1" resolved "https://registry.yarnpkg.com/@ethersproject/random/-/random-5.6.1.tgz#66915943981bcd3e11bbd43733f5c3ba5a790255" integrity sha512-/wtPNHwbmng+5yi3fkipA8YBT59DdkGRoC2vWk09Dci/q5DlgnMkhIycjHlavrvrjJBkFjO/ueLyT+aUDfc4lA== @@ -1612,7 +1838,15 @@ "@ethersproject/bytes" "^5.6.1" "@ethersproject/logger" "^5.6.0" -"@ethersproject/rlp@5.6.1", "@ethersproject/rlp@^5.6.1": +"@ethersproject/rlp@5.6.0": + version "5.6.0" + resolved "https://registry.yarnpkg.com/@ethersproject/rlp/-/rlp-5.6.0.tgz#55a7be01c6f5e64d6e6e7edb6061aa120962a717" + integrity sha512-dz9WR1xpcTL+9DtOT/aDO+YyxSSdO8YIS0jyZwHHSlAmnxA6cKU3TrTd4Xc/bHayctxTgGLYNuVVoiXE4tTq1g== + dependencies: + "@ethersproject/bytes" "^5.6.0" + "@ethersproject/logger" "^5.6.0" + +"@ethersproject/rlp@5.6.1", "@ethersproject/rlp@^5.6.0", "@ethersproject/rlp@^5.6.1": version "5.6.1" resolved "https://registry.yarnpkg.com/@ethersproject/rlp/-/rlp-5.6.1.tgz#df8311e6f9f24dcb03d59a2bac457a28a4fe2bd8" integrity sha512-uYjmcZx+DKlFUk7a5/W9aQVaoEC7+1MOBgNtvNg13+RnuUwT4F0zTovC0tmay5SmRslb29V1B7Y5KCri46WhuQ== @@ -1620,7 +1854,16 @@ "@ethersproject/bytes" "^5.6.1" "@ethersproject/logger" "^5.6.0" -"@ethersproject/sha2@5.6.1", "@ethersproject/sha2@^5.6.1": +"@ethersproject/sha2@5.6.0": + version "5.6.0" + resolved "https://registry.yarnpkg.com/@ethersproject/sha2/-/sha2-5.6.0.tgz#364c4c11cc753bda36f31f001628706ebadb64d9" + integrity sha512-1tNWCPFLu1n3JM9t4/kytz35DkuF9MxqkGGEHNauEbaARdm2fafnOyw1s0tIQDPKF/7bkP1u3dbrmjpn5CelyA== + dependencies: + "@ethersproject/bytes" "^5.6.0" + "@ethersproject/logger" "^5.6.0" + hash.js "1.1.7" + +"@ethersproject/sha2@5.6.1", "@ethersproject/sha2@^5.6.0", "@ethersproject/sha2@^5.6.1": version "5.6.1" resolved "https://registry.yarnpkg.com/@ethersproject/sha2/-/sha2-5.6.1.tgz#211f14d3f5da5301c8972a8827770b6fd3e51656" integrity sha512-5K2GyqcW7G4Yo3uenHegbXRPDgARpWUiXc6RiF7b6i/HXUoWlb7uCARh7BAHg7/qT/Q5ydofNwiZcim9qpjB6g== @@ -1629,7 +1872,19 @@ "@ethersproject/logger" "^5.6.0" hash.js "1.1.7" -"@ethersproject/signing-key@5.6.2", "@ethersproject/signing-key@^5.4.0", "@ethersproject/signing-key@^5.6.2": +"@ethersproject/signing-key@5.6.0": + version "5.6.0" + resolved "https://registry.yarnpkg.com/@ethersproject/signing-key/-/signing-key-5.6.0.tgz#4f02e3fb09e22b71e2e1d6dc4bcb5dafa69ce042" + integrity sha512-S+njkhowmLeUu/r7ir8n78OUKx63kBdMCPssePS89So1TH4hZqnWFsThEd/GiXYp9qMxVrydf7KdM9MTGPFukA== + dependencies: + "@ethersproject/bytes" "^5.6.0" + "@ethersproject/logger" "^5.6.0" + "@ethersproject/properties" "^5.6.0" + bn.js "^4.11.9" + elliptic "6.5.4" + hash.js "1.1.7" + +"@ethersproject/signing-key@5.6.2", "@ethersproject/signing-key@^5.4.0", "@ethersproject/signing-key@^5.6.0", "@ethersproject/signing-key@^5.6.2": version "5.6.2" resolved "https://registry.yarnpkg.com/@ethersproject/signing-key/-/signing-key-5.6.2.tgz#8a51b111e4d62e5a62aee1da1e088d12de0614a3" integrity sha512-jVbu0RuP7EFpw82vHcL+GP35+KaNruVAZM90GxgQnGqB6crhBqW/ozBfFvdeImtmb4qPko0uxXjn8l9jpn0cwQ== @@ -1641,6 +1896,18 @@ elliptic "6.5.4" hash.js "1.1.7" +"@ethersproject/solidity@5.6.0": + version "5.6.0" + resolved "https://registry.yarnpkg.com/@ethersproject/solidity/-/solidity-5.6.0.tgz#64657362a596bf7f5630bdc921c07dd78df06dc3" + integrity sha512-YwF52vTNd50kjDzqKaoNNbC/r9kMDPq3YzDWmsjFTRBcIF1y4JCQJ8gB30wsTfHbaxgxelI5BfxQSxD/PbJOww== + dependencies: + "@ethersproject/bignumber" "^5.6.0" + "@ethersproject/bytes" "^5.6.0" + "@ethersproject/keccak256" "^5.6.0" + "@ethersproject/logger" "^5.6.0" + "@ethersproject/sha2" "^5.6.0" + "@ethersproject/strings" "^5.6.0" + "@ethersproject/solidity@5.6.1", "@ethersproject/solidity@^5.4.0": version "5.6.1" resolved "https://registry.yarnpkg.com/@ethersproject/solidity/-/solidity-5.6.1.tgz#5845e71182c66d32e6ec5eefd041fca091a473e2" @@ -1653,7 +1920,16 @@ "@ethersproject/sha2" "^5.6.1" "@ethersproject/strings" "^5.6.1" -"@ethersproject/strings@5.6.1", "@ethersproject/strings@^5.4.0", "@ethersproject/strings@^5.6.1": +"@ethersproject/strings@5.6.0": + version "5.6.0" + resolved "https://registry.yarnpkg.com/@ethersproject/strings/-/strings-5.6.0.tgz#9891b26709153d996bf1303d39a7f4bc047878fd" + integrity sha512-uv10vTtLTZqrJuqBZR862ZQjTIa724wGPWQqZrofaPI/kUsf53TBG0I0D+hQ1qyNtllbNzaW+PDPHHUI6/65Mg== + dependencies: + "@ethersproject/bytes" "^5.6.0" + "@ethersproject/constants" "^5.6.0" + "@ethersproject/logger" "^5.6.0" + +"@ethersproject/strings@5.6.1", "@ethersproject/strings@^5.4.0", "@ethersproject/strings@^5.6.0", "@ethersproject/strings@^5.6.1": version "5.6.1" resolved "https://registry.yarnpkg.com/@ethersproject/strings/-/strings-5.6.1.tgz#dbc1b7f901db822b5cafd4ebf01ca93c373f8952" integrity sha512-2X1Lgk6Jyfg26MUnsHiT456U9ijxKUybz8IM1Vih+NJxYtXhmvKBcHOmvGqpFSVJ0nQ4ZCoIViR8XlRw1v/+Cw== @@ -1662,7 +1938,22 @@ "@ethersproject/constants" "^5.6.1" "@ethersproject/logger" "^5.6.0" -"@ethersproject/transactions@5.6.2", "@ethersproject/transactions@^5.4.0", "@ethersproject/transactions@^5.6.2": +"@ethersproject/transactions@5.6.0": + version "5.6.0" + resolved "https://registry.yarnpkg.com/@ethersproject/transactions/-/transactions-5.6.0.tgz#4b594d73a868ef6e1529a2f8f94a785e6791ae4e" + integrity sha512-4HX+VOhNjXHZyGzER6E/LVI2i6lf9ejYeWD6l4g50AdmimyuStKc39kvKf1bXWQMg7QNVh+uC7dYwtaZ02IXeg== + dependencies: + "@ethersproject/address" "^5.6.0" + "@ethersproject/bignumber" "^5.6.0" + "@ethersproject/bytes" "^5.6.0" + "@ethersproject/constants" "^5.6.0" + "@ethersproject/keccak256" "^5.6.0" + "@ethersproject/logger" "^5.6.0" + "@ethersproject/properties" "^5.6.0" + "@ethersproject/rlp" "^5.6.0" + "@ethersproject/signing-key" "^5.6.0" + +"@ethersproject/transactions@5.6.2", "@ethersproject/transactions@^5.4.0", "@ethersproject/transactions@^5.6.0", "@ethersproject/transactions@^5.6.2": version "5.6.2" resolved "https://registry.yarnpkg.com/@ethersproject/transactions/-/transactions-5.6.2.tgz#793a774c01ced9fe7073985bb95a4b4e57a6370b" integrity sha512-BuV63IRPHmJvthNkkt9G70Ullx6AcM+SDc+a8Aw/8Yew6YwT51TcBKEp1P4oOQ/bP25I18JJr7rcFRgFtU9B2Q== @@ -1677,6 +1968,15 @@ "@ethersproject/rlp" "^5.6.1" "@ethersproject/signing-key" "^5.6.2" +"@ethersproject/units@5.6.0": + version "5.6.0" + resolved "https://registry.yarnpkg.com/@ethersproject/units/-/units-5.6.0.tgz#e5cbb1906988f5740254a21b9ded6bd51e826d9c" + integrity sha512-tig9x0Qmh8qbo1w8/6tmtyrm/QQRviBh389EQ+d8fP4wDsBrJBf08oZfoiz1/uenKK9M78yAP4PoR7SsVoTjsw== + dependencies: + "@ethersproject/bignumber" "^5.6.0" + "@ethersproject/constants" "^5.6.0" + "@ethersproject/logger" "^5.6.0" + "@ethersproject/units@5.6.1", "@ethersproject/units@^5.4.0": version "5.6.1" resolved "https://registry.yarnpkg.com/@ethersproject/units/-/units-5.6.1.tgz#ecc590d16d37c8f9ef4e89e2005bda7ddc6a4e6f" @@ -1686,6 +1986,27 @@ "@ethersproject/constants" "^5.6.1" "@ethersproject/logger" "^5.6.0" +"@ethersproject/wallet@5.6.0": + version "5.6.0" + resolved "https://registry.yarnpkg.com/@ethersproject/wallet/-/wallet-5.6.0.tgz#33d11a806d783864208f348709a5a3badac8e22a" + integrity sha512-qMlSdOSTyp0MBeE+r7SUhr1jjDlC1zAXB8VD84hCnpijPQiSNbxr6GdiLXxpUs8UKzkDiNYYC5DRI3MZr+n+tg== + dependencies: + "@ethersproject/abstract-provider" "^5.6.0" + "@ethersproject/abstract-signer" "^5.6.0" + "@ethersproject/address" "^5.6.0" + "@ethersproject/bignumber" "^5.6.0" + "@ethersproject/bytes" "^5.6.0" + "@ethersproject/hash" "^5.6.0" + "@ethersproject/hdnode" "^5.6.0" + "@ethersproject/json-wallets" "^5.6.0" + "@ethersproject/keccak256" "^5.6.0" + "@ethersproject/logger" "^5.6.0" + "@ethersproject/properties" "^5.6.0" + "@ethersproject/random" "^5.6.0" + "@ethersproject/signing-key" "^5.6.0" + "@ethersproject/transactions" "^5.6.0" + "@ethersproject/wordlists" "^5.6.0" + "@ethersproject/wallet@5.6.2": version "5.6.2" resolved "https://registry.yarnpkg.com/@ethersproject/wallet/-/wallet-5.6.2.tgz#cd61429d1e934681e413f4bc847a5f2f87e3a03c" @@ -1707,7 +2028,18 @@ "@ethersproject/transactions" "^5.6.2" "@ethersproject/wordlists" "^5.6.1" -"@ethersproject/web@5.6.1", "@ethersproject/web@^5.6.1": +"@ethersproject/web@5.6.0": + version "5.6.0" + resolved "https://registry.yarnpkg.com/@ethersproject/web/-/web-5.6.0.tgz#4bf8b3cbc17055027e1a5dd3c357e37474eaaeb8" + integrity sha512-G/XHj0hV1FxI2teHRfCGvfBUHFmU+YOSbCxlAMqJklxSa7QMiHFQfAxvwY2PFqgvdkxEKwRNr/eCjfAPEm2Ctg== + dependencies: + "@ethersproject/base64" "^5.6.0" + "@ethersproject/bytes" "^5.6.0" + "@ethersproject/logger" "^5.6.0" + "@ethersproject/properties" "^5.6.0" + "@ethersproject/strings" "^5.6.0" + +"@ethersproject/web@5.6.1", "@ethersproject/web@^5.6.0", "@ethersproject/web@^5.6.1": version "5.6.1" resolved "https://registry.yarnpkg.com/@ethersproject/web/-/web-5.6.1.tgz#6e2bd3ebadd033e6fe57d072db2b69ad2c9bdf5d" integrity sha512-/vSyzaQlNXkO1WV+RneYKqCJwualcUdx/Z3gseVovZP0wIlOFcCE1hkRhKBH8ImKbGQbMl9EAAyJFrJu7V0aqA== @@ -1718,7 +2050,18 @@ "@ethersproject/properties" "^5.6.0" "@ethersproject/strings" "^5.6.1" -"@ethersproject/wordlists@5.6.1", "@ethersproject/wordlists@^5.6.1": +"@ethersproject/wordlists@5.6.0": + version "5.6.0" + resolved "https://registry.yarnpkg.com/@ethersproject/wordlists/-/wordlists-5.6.0.tgz#79e62c5276e091d8575f6930ba01a29218ded032" + integrity sha512-q0bxNBfIX3fUuAo9OmjlEYxP40IB8ABgb7HjEZCL5IKubzV3j30CWi2rqQbjTS2HfoyQbfINoKcTVWP4ejwR7Q== + dependencies: + "@ethersproject/bytes" "^5.6.0" + "@ethersproject/hash" "^5.6.0" + "@ethersproject/logger" "^5.6.0" + "@ethersproject/properties" "^5.6.0" + "@ethersproject/strings" "^5.6.0" + +"@ethersproject/wordlists@5.6.1", "@ethersproject/wordlists@^5.6.0", "@ethersproject/wordlists@^5.6.1": version "5.6.1" resolved "https://registry.yarnpkg.com/@ethersproject/wordlists/-/wordlists-5.6.1.tgz#1e78e2740a8a21e9e99947e47979d72e130aeda1" integrity sha512-wiPRgBpNbNwCQFoCr8bcWO8o5I810cqO6mkdtKfLKFlLxeCWcnzDi4Alu8iyNzlhYuS9npCwivMbRWF19dyblw== @@ -2304,6 +2647,11 @@ messageformat-parser "^4.1.3" ramda "^0.27.1" +"@metamask/detect-provider@^1.2.0": + version "1.2.0" + resolved "https://registry.yarnpkg.com/@metamask/detect-provider/-/detect-provider-1.2.0.tgz#3667a7531f2a682e3c3a43eaf3a1958bdb42a696" + integrity sha512-ocA76vt+8D0thgXZ7LxFPyqw3H7988qblgzddTDA6B8a/yU0uKV42QR/DhA+Jh11rJjxW0jKvwb5htA6krNZDQ== + "@metamask/safe-event-emitter@2.0.0", "@metamask/safe-event-emitter@^2.0.0": version "2.0.0" resolved "https://registry.yarnpkg.com/@metamask/safe-event-emitter/-/safe-event-emitter-2.0.0.tgz#af577b477c683fad17c619a78208cede06f9605c" @@ -2689,6 +3037,22 @@ resolved "https://registry.yarnpkg.com/@sushiswap/chainlink-whitelist/-/chainlink-whitelist-0.2.8.tgz#832dc80963b890a2eb02aaaf11ea8b57b2b7dde0" integrity sha512-YhhGfF/smND4jUdVWuTr3e9Np08zLyxrOfbQ6GkAhmrld4lWBs2sk4TV6oWSBWPbuUb3A2mw203uCnbMHpSoXw== +"@sushiswap/core-sdk@1.0.0-canary.133": + version "1.0.0-canary.133" + resolved "https://registry.yarnpkg.com/@sushiswap/core-sdk/-/core-sdk-1.0.0-canary.133.tgz#8a6c2566e1ceb23eec7ae0463dd474b1900b6a23" + integrity sha512-40Nl1LIkbmNgfzJmZ2VoHHQKDw7Xo6LT7Gq8I1334mPduVpGw695YDRi+Z2g2WVLBggBI/Gk1yRYiYSB4T3M7A== + dependencies: + "@ethersproject/address" "^5.4.0" + "@ethersproject/bignumber" "^5.4.0" + "@ethersproject/providers" "^5.4.0" + "@ethersproject/solidity" "^5.4.0" + big.js "^6.1.1" + decimal.js-light "^2.5.0" + jsbi "^4.1.0" + tiny-invariant "^1.1.0" + tiny-warning "^1.0.0" + toformat "^2.0.0" + "@sushiswap/core-sdk@1.0.0-canary.138": version "1.0.0-canary.138" resolved "https://registry.yarnpkg.com/@sushiswap/core-sdk/-/core-sdk-1.0.0-canary.138.tgz#0951635f3d1dc199a4a663dfb5f71afa65a437d8" @@ -2710,14 +3074,19 @@ resolved "https://registry.yarnpkg.com/@sushiswap/core/-/core-2.0.0-canary.1.tgz#91c938668282d463e4fce6e0cf4d320ce5f3768f" integrity sha512-6YFT4EKgm0XgfwCBsKx5HrDtRI6EvhZHipjBKegdTV0b2zKuXnmdyZ+RwPCr0mCKq2pKB0qIiqCxfbgXYDlLCQ== -"@sushiswap/kashi-sdk@1.0.0-canary.110": - version "1.0.0-canary.110" - resolved "https://registry.yarnpkg.com/@sushiswap/kashi-sdk/-/kashi-sdk-1.0.0-canary.110.tgz#24aa0f20bb577d4bfe83ecb99b852621fc69e1a9" - integrity sha512-5KR84mU5bjbDRMzVu8ojC4HTu8jaC0+TibKP8zTT5590ZCwyJjGXD7mqcL5tNICl+w64tX3vNMNhCb/klyEJhQ== +"@sushiswap/default-token-list@^31.1.0": + version "31.1.0" + resolved "https://registry.yarnpkg.com/@sushiswap/default-token-list/-/default-token-list-31.1.0.tgz#d8babd1a1bfd0965b325aaae726beede930e7de3" + integrity sha512-uicM8HGvGaBUd+Rz/XcpLaXJt+3qfIp1IeZnNz/xB9blW7e/UT97gGG5pz2GeHaw/TQLf+2AZYqZgiWGJPmmiA== + +"@sushiswap/kashi-sdk@1.0.0-canary.105": + version "1.0.0-canary.105" + resolved "https://registry.yarnpkg.com/@sushiswap/kashi-sdk/-/kashi-sdk-1.0.0-canary.105.tgz#d1204ba18bd95d42376eb9efef5e44ea3eec1c81" + integrity sha512-GcfYMd7T5CeJiPsNf27UMLoC786W0yv3BUuAOaklgQVxb4ZBBuoMe6RhgHJXgxlkIRLpW4VrGoR11Ci6NKmP8A== dependencies: "@ethersproject/abi" "^5.4.0" "@ethersproject/contracts" "^5.4.0" - "@sushiswap/bentobox-sdk" "1.0.0-canary.119" + "@sushiswap/bentobox-sdk" "1.0.0-canary.114" "@sushiswap/limit-order-pair-list@0.2.17": version "0.2.17" @@ -4273,6 +4642,13 @@ axe-core@^4.4.2: resolved "https://registry.yarnpkg.com/axe-core/-/axe-core-4.4.3.tgz#11c74d23d5013c0fa5d183796729bc3482bd2f6f" integrity sha512-32+ub6kkdhhWick/UjvEwRchgoetXqTK14INLqbGm5U2TzBkBNF3nQtLYm8ovxSkQWArjEQvftCKryjZaATu3w== +axios@0.26.1: + version "0.26.1" + resolved "https://registry.yarnpkg.com/axios/-/axios-0.26.1.tgz#1ede41c51fcf51bbbd6fd43669caaa4f0495aaa9" + integrity sha512-fPwcX4EvnSHuInCMItEhAGnaSEXRBjtzh9fOtsE6E1G6p7vl7edEeZe11QHf18+6+9gR5PbKV/sGKNaD8YaMeA== + dependencies: + follow-redirects "^1.14.8" + axios@^0.18.0: version "0.18.1" resolved "https://registry.yarnpkg.com/axios/-/axios-0.18.1.tgz#ff3f0de2e7b5d180e757ad98000f1081b87bcea3" @@ -5258,9 +5634,9 @@ core-js-compat@^3.21.0, core-js-compat@^3.22.1: semver "7.0.0" core-js-pure@^3.20.2: - version "3.23.4" - resolved "https://registry.yarnpkg.com/core-js-pure/-/core-js-pure-3.23.4.tgz#aba5c7fb297063444f6bf93afb0362151679a012" - integrity sha512-lizxkcgj3XDmi7TUBFe+bQ1vNpD5E4t76BrBWI3HdUxdw/Mq1VF4CkiHzIKyieECKtcODK2asJttoofEeUKICQ== + version "3.24.1" + resolved "https://registry.yarnpkg.com/core-js-pure/-/core-js-pure-3.24.1.tgz#8839dde5da545521bf282feb7dc6d0b425f39fd3" + integrity sha512-r1nJk41QLLPyozHUUPmILCEMtMw24NG4oWK6RbsDdjzQgg9ZvrUsPBj1MnG0wXXp1DCDU6j+wUvEmBSrtRbLXg== core-util-is@1.0.2: version "1.0.2" @@ -5386,6 +5762,11 @@ cross-spawn@^7.0.1, cross-spawn@^7.0.2, cross-spawn@^7.0.3: shebang-command "^2.0.0" which "^2.0.1" +crypto-js@4.1.1: + version "4.1.1" + resolved "https://registry.yarnpkg.com/crypto-js/-/crypto-js-4.1.1.tgz#9e485bcf03521041bd85844786b83fb7619736cf" + integrity sha512-o2JlM7ydqd3Qk9CA0L4NL6mTzU2sdx96a+oOfPu8Mkl/PK51vSyoi8/rQ8NknZtk44vq15lmhAj9CIAGwgeWKw== + crypto-random-string@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/crypto-random-string/-/crypto-random-string-2.0.0.tgz#ef2a7a966ec11083388369baa02ebead229b30d5" @@ -6668,6 +7049,42 @@ ethereumjs-vm@^2.3.4: rustbn.js "~0.2.0" safe-buffer "^5.1.1" +ethers@5.6.0: + version "5.6.0" + resolved "https://registry.yarnpkg.com/ethers/-/ethers-5.6.0.tgz#924eb965dc03963fad0a09ce687efdf49aca3b45" + integrity sha512-00FP71jt6bW3ndO5DhgH9mLIZhoCGnAKFLu8qig5KmV03ubEChKf2ilB3g6fX512tTYo+tSMDJ5WpCJWdBHkBQ== + dependencies: + "@ethersproject/abi" "5.6.0" + "@ethersproject/abstract-provider" "5.6.0" + "@ethersproject/abstract-signer" "5.6.0" + "@ethersproject/address" "5.6.0" + "@ethersproject/base64" "5.6.0" + "@ethersproject/basex" "5.6.0" + "@ethersproject/bignumber" "5.6.0" + "@ethersproject/bytes" "5.6.0" + "@ethersproject/constants" "5.6.0" + "@ethersproject/contracts" "5.6.0" + "@ethersproject/hash" "5.6.0" + "@ethersproject/hdnode" "5.6.0" + "@ethersproject/json-wallets" "5.6.0" + "@ethersproject/keccak256" "5.6.0" + "@ethersproject/logger" "5.6.0" + "@ethersproject/networks" "5.6.0" + "@ethersproject/pbkdf2" "5.6.0" + "@ethersproject/properties" "5.6.0" + "@ethersproject/providers" "5.6.0" + "@ethersproject/random" "5.6.0" + "@ethersproject/rlp" "5.6.0" + "@ethersproject/sha2" "5.6.0" + "@ethersproject/signing-key" "5.6.0" + "@ethersproject/solidity" "5.6.0" + "@ethersproject/strings" "5.6.0" + "@ethersproject/transactions" "5.6.0" + "@ethersproject/units" "5.6.0" + "@ethersproject/wallet" "5.6.0" + "@ethersproject/web" "5.6.0" + "@ethersproject/wordlists" "5.6.0" + ethers@^5.1.4, ethers@^5.4.7, ethers@^5.6.8: version "5.6.9" resolved "https://registry.yarnpkg.com/ethers/-/ethers-5.6.9.tgz#4e12f8dfcb67b88ae7a78a9519b384c23c576a4d" @@ -7051,7 +7468,7 @@ follow-redirects@1.5.10: dependencies: debug "=3.1.0" -follow-redirects@^1.14.0: +follow-redirects@^1.14.0, follow-redirects@^1.14.8: version "1.15.1" resolved "https://registry.yarnpkg.com/follow-redirects/-/follow-redirects-1.15.1.tgz#0ca6a452306c9b276e4d3127483e29575e207ad5" integrity sha512-yLAMQs+k0b2m7cVxpS1VKJVvoz7SS9Td1zss3XRwXj+ZDH00RJgnuLx7E44wx02kQLrdM3aOOy+FpzS7+8OizA== @@ -9604,6 +10021,23 @@ module-lookup-amd@^7.0.1: requirejs "^2.3.5" requirejs-config-file "^4.0.0" +moralis@^1.7.0: + version "1.11.0" + resolved "https://registry.yarnpkg.com/moralis/-/moralis-1.11.0.tgz#aa736cf5acbd3f65ac83d44f2e4e12e0e88566f8" + integrity sha512-oqoKqP47RuHq+SnztN48+YgWsKLt1YBy7IfeSBOixUW0Tzn9RQDjNKpkSBcKFttNZQ/KzCEes6TCqA9IxSoTPQ== + dependencies: + "@babel/runtime" "7.16.7" + "@babel/runtime-corejs3" "7.16.8" + "@metamask/detect-provider" "^1.2.0" + axios "0.26.1" + ethers "5.6.0" + react-native-crypto-js "1.0.0" + uuid "^8.3.2" + ws "^8.3.0" + xmlhttprequest "1.8.0" + optionalDependencies: + crypto-js "4.1.1" + mrmime@^1.0.0: version "1.0.1" resolved "https://registry.yarnpkg.com/mrmime/-/mrmime-1.0.1.tgz#5f90c825fad4bdd41dc914eff5d1a8cfdaf24f27" @@ -11109,6 +11543,11 @@ react-lifecycles-compat@^3.0.4: resolved "https://registry.yarnpkg.com/react-lifecycles-compat/-/react-lifecycles-compat-3.0.4.tgz#4f1a273afdfc8f3488a8c516bfda78f872352362" integrity sha512-fBASbA6LnOU9dOU2eW7aQ8xmYBSXUIWr+UmF9b1efZBazGNO+rcXT/icdKnYm2pTwcRylVUYwW7H1PHfLekVzA== +react-native-crypto-js@1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/react-native-crypto-js/-/react-native-crypto-js-1.0.0.tgz#e677e022e147f41b35614416c92d655f87e2450a" + integrity sha512-FNbLuG/HAdapQoybeZSoes1PWdOj0w242gb+e1R0hicf3Gyj/Mf8M9NaED2AnXVOX01b2FXomwUiw1xP1K+8sA== + react-popper@^2.2.5: version "2.3.0" resolved "https://registry.yarnpkg.com/react-popper/-/react-popper-2.3.0.tgz#17891c620e1320dce318bad9fede46a5f71c70ba" @@ -13560,6 +13999,11 @@ ws@^7.3.1, ws@^7.4.0, ws@^7.4.6: resolved "https://registry.yarnpkg.com/ws/-/ws-7.5.8.tgz#ac2729881ab9e7cbaf8787fe3469a48c5c7f636a" integrity sha512-ri1Id1WinAX5Jqn9HejiGb8crfRio0Qgu8+MtL36rlTA6RLsMdWt1Az/19A2Qij6uSHUMphEFaTKa4WG+UNHNw== +ws@^8.3.0: + version "8.8.1" + resolved "https://registry.yarnpkg.com/ws/-/ws-8.8.1.tgz#5dbad0feb7ade8ecc99b830c1d77c913d4955ff0" + integrity sha512-bGy2JzvzkPowEJV++hF07hAD6niYSr0JzBNo/J29WsB57A2r7Wlc1UFcTR9IzrPvuNVO4B8LGqF8qcpsVOhJCA== + xhr@^2.0.1, xhr@^2.2.0: version "2.6.0" resolved "https://registry.yarnpkg.com/xhr/-/xhr-2.6.0.tgz#b69d4395e792b4173d6b7df077f0fc5e4e2b249d" @@ -13598,6 +14042,11 @@ xmlchars@^2.2.0: resolved "https://registry.yarnpkg.com/xmlchars/-/xmlchars-2.2.0.tgz#060fe1bcb7f9c76fe2a17db86a9bc3ab894210cb" integrity sha512-JZnDKK8B0RCDw84FNdDAIpZK+JuJw+s7Lz8nksI7SIuU3UXJJslUthsi+uWBUYOwPFwW7W7PRLRfUKpxjtjFCw== +xmlhttprequest@1.8.0: + version "1.8.0" + resolved "https://registry.yarnpkg.com/xmlhttprequest/-/xmlhttprequest-1.8.0.tgz#67fe075c5c24fef39f9d65f5f7b7fe75171968fc" + integrity sha512-58Im/U0mlVBLM38NdZjHyhuMtCqa61469k2YP/AaPbvCoV9aQGUpbJBj1QRm2ytRiVQBD/fsw7L2bJGDVQswBA== + xtend@^4.0.0, xtend@^4.0.1, xtend@^4.0.2, xtend@~4.0.0: version "4.0.2" resolved "https://registry.yarnpkg.com/xtend/-/xtend-4.0.2.tgz#bb72779f5fa465186b1f438f674fa347fdb5db54"