From c17dfcbf880a6621e439aaaf6bae9d91ba482aea Mon Sep 17 00:00:00 2001 From: tomjeatt <40243778+tomjeatt@users.noreply.github.com> Date: Wed, 5 Apr 2023 11:50:18 +0100 Subject: [PATCH] Release/interlay/2.29.0 (#1094) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * fix: remove external dependencies from component library (#942) * fix: remove redundant layout mock stuff * fix: hide the connected socket console log * chore: mark the points * fix(NumberInput): remove react-aria (#934) * feat: add formik (#863) * feat(CoinIcon): update INTR and IBTC icon (#941) * feat: take supply and borrow caps into account * chore: update lib * refactor: code review * hotfix: testnet banner * feat: added trollbox Discord chat embed via widgetbot @@ -35,4 +42,4 @@ --> - + \ No newline at end of file diff --git a/src/App.tsx b/src/App.tsx index 8221d77d73..fc2f89f241 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -11,9 +11,9 @@ import { Redirect, Route, Switch } from 'react-router-dom'; import { initGeneralDataAction, isFaucetLoaded, isVaultClientLoaded } from '@/common/actions/general.actions'; import { ParachainStatus, StoreType } from '@/common/types/util.types'; -import ErrorFallback from '@/components/ErrorFallback'; -import FullLoadingSpinner from '@/components/FullLoadingSpinner'; import { GOVERNANCE_TOKEN, RELAY_CHAIN_NATIVE_TOKEN, WRAPPED_TOKEN } from '@/config/relay-chains'; +import ErrorFallback from '@/legacy-components/ErrorFallback'; +import FullLoadingSpinner from '@/legacy-components/FullLoadingSpinner'; import { useSubstrate, useSubstrateSecureState } from '@/lib/substrate'; import Layout from '@/parts/Layout'; import graphqlFetcher, { GRAPHQL_FETCHER, GraphqlReturn } from '@/services/fetchers/graphql-fetcher'; @@ -21,8 +21,8 @@ import vaultsByAccountIdQuery from '@/services/queries/vaults-by-accountId-query import { BitcoinNetwork } from '@/types/bitcoin'; import { PAGES } from '@/utils/constants/links'; -import TestnetBanner from './components/TestnetBanner'; import * as constants from './constants'; +import TestnetBanner from './legacy-components/TestnetBanner'; import { FeatureFlags, useFeatureFlag } from './utils/hooks/use-feature-flag'; const Bridge = React.lazy(() => import(/* webpackChunkName: 'bridge' */ '@/pages/Bridge')); @@ -35,6 +35,9 @@ const Vaults = React.lazy(() => import(/* webpackChunkName: 'vaults' */ '@/pages // TODO: last task will be to delete legacy dashboard and rename vault dashboard const Vault = React.lazy(() => import(/* webpackChunkName: 'vault' */ '@/pages/Vaults/Vault')); const Loans = React.lazy(() => import(/* webpackChunkName: 'loans' */ '@/pages/Loans')); +const Swap = React.lazy(() => import(/* webpackChunkName: 'loans' */ '@/pages/AMM')); +const Pools = React.lazy(() => import(/* webpackChunkName: 'loans' */ '@/pages/AMM/Pools')); +const Wallet = React.lazy(() => import(/* webpackChunkName: 'loans' */ '@/pages/Wallet')); const Actions = React.lazy(() => import(/* webpackChunkName: 'actions' */ '@/pages/Actions')); const NoMatch = React.lazy(() => import(/* webpackChunkName: 'no-match' */ '@/pages/NoMatch')); @@ -45,6 +48,8 @@ const App = (): JSX.Element => { const { bridgeLoaded } = useSelector((state: StoreType) => state.general); const dispatch = useDispatch(); const isLendingEnabled = useFeatureFlag(FeatureFlags.LENDING); + const isAMMEnabled = useFeatureFlag(FeatureFlags.AMM); + const isWalletEnabled = useFeatureFlag(FeatureFlags.WALLET); // Loads the connection to the faucet - only for testnet purposes const loadFaucet = React.useCallback(async (): Promise => { @@ -59,7 +64,7 @@ const App = (): JSX.Element => { // Loads the faucet React.useEffect(() => { if (!bridgeLoaded) return; - if (process.env.REACT_APP_BITCOIN_NETWORK === BitcoinNetwork.Mainnet) return; + // if (process.env.REACT_APP_BITCOIN_NETWORK === BitcoinNetwork.Mainnet) return; (async () => { try { @@ -75,12 +80,14 @@ const App = (): JSX.Element => { [GRAPHQL_FETCHER, vaultsByAccountIdQuery(selectedAccount?.address ?? '')], graphqlFetcher>(), { - enabled: bridgeLoaded && !!selectedAccount, + enabled: process.env.NODE_ENV !== 'test' && bridgeLoaded && !!selectedAccount, onSuccess: ({ data }) => { const isVaultOperator = data?.vaults.length > 0; dispatch(isVaultClientLoaded(isVaultOperator)); }, - onError: (error) => console.log('[App useQuery 1] error.message => ', error.message) + onError: (error) => { + console.log('[App useQuery 1] error.message => ', error.message); + } } ); useErrorHandler(vaultsError); @@ -190,6 +197,21 @@ const App = (): JSX.Element => { )} + {isAMMEnabled && ( + + + + )} + {isAMMEnabled && ( + + + + )} + {isWalletEnabled && ( + + + + )} diff --git a/src/assets/icons/ArrowTopRightOnSquare.tsx b/src/assets/icons/ArrowTopRightOnSquare.tsx new file mode 100644 index 0000000000..68afeaa95c --- /dev/null +++ b/src/assets/icons/ArrowTopRightOnSquare.tsx @@ -0,0 +1,25 @@ +import { forwardRef } from 'react'; + +import { Icon, IconProps } from '@/component-library/Icon'; + +const ArrowTopRightOnSquare = forwardRef((props, ref) => ( + + + +)); + +ArrowTopRightOnSquare.displayName = 'ArrowTopRightOnSquare'; + +export { ArrowTopRightOnSquare }; diff --git a/src/assets/icons/ArrowsUpDown.tsx b/src/assets/icons/ArrowsUpDown.tsx new file mode 100644 index 0000000000..f28279ac5f --- /dev/null +++ b/src/assets/icons/ArrowsUpDown.tsx @@ -0,0 +1,25 @@ +import { forwardRef } from 'react'; + +import { Icon, IconProps } from '@/component-library/Icon'; + +const ArrowsUpDown = forwardRef((props, ref) => ( + + + +)); + +ArrowsUpDown.displayName = 'ArrowsUpDown'; + +export { ArrowsUpDown }; diff --git a/src/assets/icons/Cog.tsx b/src/assets/icons/Cog.tsx new file mode 100644 index 0000000000..b573975b60 --- /dev/null +++ b/src/assets/icons/Cog.tsx @@ -0,0 +1,26 @@ +import { forwardRef } from 'react'; + +import { Icon, IconProps } from '@/component-library/Icon'; + +const Cog = forwardRef((props, ref) => ( + + + + +)); + +Cog.displayName = 'Cog'; + +export { Cog }; diff --git a/src/assets/icons/DocumentDuplicate.tsx b/src/assets/icons/DocumentDuplicate.tsx new file mode 100644 index 0000000000..9cb33fb1f1 --- /dev/null +++ b/src/assets/icons/DocumentDuplicate.tsx @@ -0,0 +1,25 @@ +import { forwardRef } from 'react'; + +import { Icon, IconProps } from '@/component-library/Icon'; + +const DocumentDuplicate = forwardRef((props, ref) => ( + + + +)); + +DocumentDuplicate.displayName = 'DocumentDuplicate'; + +export { DocumentDuplicate }; diff --git a/src/assets/icons/InformationCircle.tsx b/src/assets/icons/InformationCircle.tsx new file mode 100644 index 0000000000..a1b3e516f3 --- /dev/null +++ b/src/assets/icons/InformationCircle.tsx @@ -0,0 +1,25 @@ +import { forwardRef } from 'react'; + +import { Icon, IconProps } from '@/component-library/Icon'; + +const InformationCircle = forwardRef((props, ref) => ( + + + +)); + +InformationCircle.displayName = 'InformationCircle'; + +export { InformationCircle }; diff --git a/src/assets/icons/PencilSquare.tsx b/src/assets/icons/PencilSquare.tsx new file mode 100644 index 0000000000..67493d6fa0 --- /dev/null +++ b/src/assets/icons/PencilSquare.tsx @@ -0,0 +1,25 @@ +import { forwardRef } from 'react'; + +import { Icon, IconProps } from '@/component-library/Icon'; + +const PencilSquare = forwardRef((props, ref) => ( + + + +)); + +PencilSquare.displayName = 'PencilSquare'; + +export { PencilSquare }; diff --git a/src/assets/icons/PlusCircle.tsx b/src/assets/icons/PlusCircle.tsx new file mode 100644 index 0000000000..75eba7d525 --- /dev/null +++ b/src/assets/icons/PlusCircle.tsx @@ -0,0 +1,21 @@ +import { forwardRef } from 'react'; + +import { Icon, IconProps } from '@/component-library/Icon'; + +const PlusCircle = forwardRef((props, ref) => ( + + + +)); + +PlusCircle.displayName = 'PlusCircle'; + +export { PlusCircle }; diff --git a/src/assets/icons/index.ts b/src/assets/icons/index.ts index e46356f42c..7888df9310 100644 --- a/src/assets/icons/index.ts +++ b/src/assets/icons/index.ts @@ -1,3 +1,10 @@ +export { ArrowsUpDown } from './ArrowsUpDown'; +export { ArrowTopRightOnSquare } from './ArrowTopRightOnSquare'; export { ChevronDown } from './ChevronDown'; +export { Cog } from './Cog'; +export { DocumentDuplicate } from './DocumentDuplicate'; +export { InformationCircle } from './InformationCircle'; +export { PencilSquare } from './PencilSquare'; +export { PlusCircle } from './PlusCircle'; export { Warning } from './Warning'; export { XMark } from './XMark'; diff --git a/src/assets/locales/en/translation.json b/src/assets/locales/en/translation.json index 501ebd19da..f9d78922fa 100644 --- a/src/assets/locales/en/translation.json +++ b/src/assets/locales/en/translation.json @@ -48,6 +48,7 @@ "collateralization": "Collateralization", "fees_earned": "Fees earned", "apy": "APR", + "apr": "APR", "here": "here", "refresh_page.": "Once you create a new account please refresh the page.", "btc_relay_status": "BTC Relay Status:", @@ -76,6 +77,7 @@ "nav_transfer": "Transfer", "nav_lending": "Lending", "nav_swap": "Swap", + "nav_pools": "Pools", "nav_transactions": "My Transactions", "nav_staking": "Staking", "nav_stats": "Stats", @@ -87,8 +89,9 @@ "nav_terms_and_conditions": "Terms and Conditions", "nav_use_wrapped": "Use {{wrappedTokenSymbol}}", "nav_governance": "Governance", + "nav_wallet": "Wallet", "report_bug": "Report a bug:", - "request_funds": "Token Faucet", + "request_funds": "Faucet", "request_btc": "BTC Faucet", "no_pending_status": "No pending status updates", "bridge_fee": "Bridge Fee", @@ -97,6 +100,7 @@ "you_received": "You received", "total_deposit": "Total Deposit", "deposit": "Deposit", + "withdraw": "Withdraw", "received": "Received", "balances": "Account Balances", "destination": "Destination", @@ -120,7 +124,7 @@ "get_governance_token": "Get {{governanceTokenSymbol}}", "get_governance_token_description": "Please check the individual terms and conditions of each exchange before you buy/trade {{governanceTokenSymbol}}.", "select_vault": "Please Select Vault", - "not_enough_vault_capacity": "None vault has enough capacity.", + "not_enough_vault_capacity": "No vault has enough capacity.", "view_all_transactions_on_subscan": "View all transactions on Subscan", "collateral": "Collateral", "error_oracle_offline": "You can't {{action}} {{wrappedTokenSymbol}} at the moment because oracle is offline.", @@ -129,6 +133,25 @@ "replace": "Replace", "canceled": "Canceled", "fees": "Fees", + "total_liquidity": "Total Liquidity", + "my_liquidity": "My Liquidity", + "7_day_volume": "7 Day Volume", + "rewards": "Rewards", + "supply_balance": "Supply Balance", + "insufficient_funds_fees": "Insufficient funds for fees", + "successfully_claimed_rewards": "Successfully claimed rewards", + "view_details": "View details", + "copied": "Copied", + "click_to_copy": "Click to copy", + "asset": "Asset", + "price": "Price", + "balance": "Balance", + "show_zero_balance": "Show Zero Balance", + "total_balance": "Total Balance", + "transferable_balance": "Transferable Balance", + "fund_wallet": "Fund Wallet", + "unlocks": "Unlocks", + "staked": "Staked", "redeem_page": { "maximum_in_single_request": "Max redeemable in single request", "redeem": "Redeem", @@ -142,7 +165,6 @@ "view_progress": "View Progress", "btc_destination_address": "BTC destination address", "you_will_receive": "Redeem {{wrappedTokenSymbol}} 1:1 for BTC", - "must_select_account_warning": "No account selected. Please select an account to redeem.", "waiting_for": "Waiting for", "vault": "Vault", "typically_takes": "This typically takes only a few minutes but may sometimes take up to 6 hours.", @@ -150,7 +172,7 @@ "we_will_inform_you_btc": "We will inform you when the BTC payment is executed.", "redeem_processed": "Your Redeem request is being processed", "retried": "Retried", - "error_more_than_6_blocks_behind": "You can't redeem {{wrappedTokenSymbol}} at the moment because {{wrappedTokenSymbol}} bridge is more than 6 blocks behind.", + "error_more_than_6_blocks_behind": "You can't redeem {{wrappedTokenSymbol}} at the moment because {{wrappedTokenSymbol}} parachain is more than 6 blocks behind.", "confirm_redeem": "Confirm Redeem Request", "verify_and_confirm": "Please verify and confirm your redeem request.", "burning": "Burning", @@ -226,8 +248,10 @@ } }, "burn_page": { - "burn_interbtc": "Burn {{wrappedTokenSymbol}} in exchange for {{collateralTokenSymbol}}", - "available": "{{wrappedTokenSymbol}} available to burn", + "burn_interbtc": "Burn {{wrappedTokenSymbol}}", + "available_total": "Total available {{wrappedTokenSymbol}} to burn", + "available_from_collateral": "{{wrappedTokenSymbol}} available to burn from {{collateralTokenSymbol}}", + "collateral_selector_label": "In exchange for", "burn": "Burn", "please_enter_the_amount": "Please enter the amount.", "successfully_burned": "Successfully burned.", @@ -335,12 +359,12 @@ "receive_interbtc_tokens": "You will now receive your BTC-backed {{wrappedTokenSymbol}} tokens. If this does not happen automatically within a few minutes, click the button below.", "issue_requests": "Issue Requests", "recent_requests": "Recent Requests", - "must_select_account_warning": "No account selected. Please select an account to proceed to issue {{wrappedTokenSymbol}}.", "transaction_not_set": "Transaction not set in", "time": "time", "issuing_title": "Issuing", "by_locking": "By locking", "deposit_transaction": "BTC deposit transaction", + "insufficient_funds": "Insufficient free {{governanceTokenSymbol}} for security deposit and fees", "waiting_for": "Waiting for", "pay_with": "pay with", "within": "within", @@ -545,12 +569,35 @@ "lend": "Lend", "my_lend_positions": "My Lend Positions", "my_borrow_positions": "My Borrow Positions", - "action_liquidation_risk": "{{action}} this amount will increase your LTV, thus also increasing the risk of liquidation." + "action_liquidation_risk": "{{action}} this amount will increase your LTV, thus also increasing the risk of liquidation.", + "no_loan_positions": "No {{loanType}} positions", + "your_loan_positions_will_show_here": "Your {{loanType}} positions will show here" + }, + "amm": { + "pools": { + "my_pools": "My Pools", + "other_pools": "Other Pools", + "pool_name": "Pool Name", + "add_liquidity": "Add Liquidity", + "remove_liquidity": "Remove Liquidity", + "receivable_assets": "Receivable Assets" + }, + "swap": "Swap", + "select_token": "Select Token", + "enter_token_amount": "Enter {{token}} amount", + "insufficient_token_balance": "Insufficient {{token}} balance", + "insufficient_liquidity_trade": "Insufficient liquidity for this trade", + "from": "From", + "to": "To", + "swap_has_price_inpact_of": "Considering external price sources, this swap has a price impact of", + "you_are_swapping_input_for_output": "Your are swapping {{inputAmount}} {{inputTicker}} ({{inputAmountUSD}}) for {{outputAmount}} {{outputTicker}} ({{outputAmountUSD}})", + "cancel_swap": "Cancel swap", + "confirm_swap": "Confirm swap" }, "forms": { "please_enter_your_field": "Please enter your {{field}}", "please_enter_the_amount_to": "Please enter the amount to {{field}}", - "amount_must_be_at_least": "Amount to {{action}} must be at least {{amount}}", + "amount_must_be_at_least": "Amount to {{action}} must be at least {{amount}} {{token}}", "amount_must_be_at_most": "Amount to {{action}} must be at most {{amount}}", "please_enter_no_higher_available_balance": "Please enter an amount no higher than your available balance", "field_amount": "{{field}} amount" @@ -562,5 +609,12 @@ "transfer_more_than_minimum": "Must transfer more than the minimum amount of {{ amount }}", "transfer_less_than_maximum": "Must transfer less than the maximum amount of {{ amount }}" } + }, + "wallet": { + "available_assets": "Available assets", + "no_assets_available": "No assets available", + "total_governance_locked": "Total {{token}} Locked", + "available_to_stake": "Available to stake", + "voting_power_governance": "Voting Power {{token}}" } } diff --git a/src/common/actions/general.actions.ts b/src/common/actions/general.actions.ts index c609d6b1cb..c6c464f6e1 100644 --- a/src/common/actions/general.actions.ts +++ b/src/common/actions/general.actions.ts @@ -13,7 +13,9 @@ import { IsFaucetLoaded, IsVaultClientLoaded, SHOW_ACCOUNT_MODAL, + SHOW_BUY_MODAL, ShowAccountModal, + ShowBuyModal, UPDATE_HEIGHTS, UPDATE_TOTALS, UpdateHeights, @@ -58,6 +60,11 @@ export const showAccountModalAction = (showAccountModal: boolean): ShowAccountMo showAccountModal }); +export const showBuyModal = (isBuyModalOpen: boolean): ShowBuyModal => ({ + type: SHOW_BUY_MODAL, + isBuyModalOpen +}); + export const updateHeightsAction = (btcRelayHeight: number, bitcoinHeight: number): UpdateHeights => ({ type: UPDATE_HEIGHTS, btcRelayHeight, diff --git a/src/common/reducers/general.reducer.ts b/src/common/reducers/general.reducer.ts index 6414b1e1bb..e4d93c5361 100644 --- a/src/common/reducers/general.reducer.ts +++ b/src/common/reducers/general.reducer.ts @@ -9,6 +9,7 @@ import { IS_BRIDGE_LOADED, IS_VAULT_CLIENT_LOADED, SHOW_ACCOUNT_MODAL, + SHOW_BUY_MODAL, UPDATE_HEIGHTS, UPDATE_TOTALS } from '../types/actions.types'; @@ -19,6 +20,7 @@ const initialState = { vaultClientLoaded: false, hasFeedbackModalBeenDisplayed: false, showAccountModal: false, + isBuyModalOpen: false, totalWrappedTokenAmount: BitcoinAmount.zero(), totalLockedCollateralTokenAmount: newMonetaryAmount(0, RELAY_CHAIN_NATIVE_TOKEN), btcRelayHeight: 0, @@ -57,6 +59,8 @@ export const generalReducer = (state: GeneralState = initialState, action: Gener return { ...state, vaultClientLoaded: action.isLoaded }; case SHOW_ACCOUNT_MODAL: return { ...state, showAccountModal: action.showAccountModal }; + case SHOW_BUY_MODAL: + return { ...state, isBuyModalOpen: action.isBuyModalOpen }; default: return state; } diff --git a/src/common/types/actions.types.ts b/src/common/types/actions.types.ts index c1ddaefba7..bfc8d94628 100644 --- a/src/common/types/actions.types.ts +++ b/src/common/types/actions.types.ts @@ -18,6 +18,7 @@ export const UPDATE_COLLATERAL_TOKEN_TRANSFERABLE_BALANCE = 'UPDATE_COLLATERAL_T export const SHOW_ACCOUNT_MODAL = 'SHOW_ACCOUNT_MODAL'; export const UPDATE_HEIGHTS = 'UPDATE_HEIGHTS'; export const UPDATE_TOTALS = 'UPDATE_TOTALS'; +export const SHOW_BUY_MODAL = 'SHOW_BUY_MODAL'; export interface UpdateTotals { type: typeof UPDATE_TOTALS; @@ -86,6 +87,11 @@ export interface ShowAccountModal { showAccountModal: boolean; } +export interface ShowBuyModal { + type: typeof SHOW_BUY_MODAL; + isBuyModalOpen: boolean; +} + export type GeneralActions = | IsBridgeLoaded | InitGeneralDataAction @@ -96,7 +102,8 @@ export type GeneralActions = | UpdateCollateralTokenTransferableBalance | ShowAccountModal | UpdateHeights - | UpdateTotals; + | UpdateTotals + | ShowBuyModal; // REDEEM export const ADD_VAULT_REDEEMS = 'ADD_VAULT_REDEEMS'; diff --git a/src/common/types/util.types.ts b/src/common/types/util.types.ts index f3cc043d6a..70f39523e6 100644 --- a/src/common/types/util.types.ts +++ b/src/common/types/util.types.ts @@ -49,6 +49,7 @@ export type GeneralState = { bridgeLoaded: boolean; vaultClientLoaded: boolean; showAccountModal: boolean; + isBuyModalOpen: boolean; totalWrappedTokenAmount: BitcoinAmount; totalLockedCollateralTokenAmount: MonetaryAmount; btcRelayHeight: number; diff --git a/src/common/utils/utils.ts b/src/common/utils/utils.ts index 56bd514dd2..7bd290766f 100644 --- a/src/common/utils/utils.ts +++ b/src/common/utils/utils.ts @@ -1,8 +1,10 @@ -import { CurrencyExt, InterbtcPrimitivesVaultId } from '@interlay/interbtc-api'; +import { CurrencyExt, InterbtcPrimitivesVaultId, newMonetaryAmount } from '@interlay/interbtc-api'; import { BitcoinAmount, MonetaryAmount } from '@interlay/monetary-js'; import Big from 'big.js'; import { PARACHAIN_URL } from '@/constants'; +import { getTokenPrice } from '@/utils/helpers/prices'; +import { Prices } from '@/utils/hooks/api/use-get-prices'; function shortAddress(address: string): string { if (address.length < 12) return address; @@ -33,6 +35,27 @@ function getLastMidnightTimestamps(daysBack: number, startFromTonight = false): .reverse(); } +const convertMonetaryAmountToUsdBig = ( + amount: MonetaryAmount, + rate: number | undefined +): Big => { + // If the rate is not available return 0. + if (rate === undefined) { + return Big(0); + } + + return amount.toBig().mul(rate); +}; + +const convertMonetaryBtcToUSD = (amount: BitcoinAmount, prices: Prices): Big => { + if (prices === undefined) { + return Big(0); + } + + const btcUsdPrice = getTokenPrice(prices, 'BTC')?.usd; + return convertMonetaryAmountToUsdBig(amount, btcUsdPrice); +}; + const convertMonetaryAmountToValueInUSD = ( amount: MonetaryAmount, rate: number | undefined @@ -84,9 +107,13 @@ const formatNumber = ( options?: { minimumFractionDigits?: number; maximumFractionDigits?: number; + compact?: boolean; } ): string => { - const { format } = new Intl.NumberFormat(undefined, options); + const { format } = new Intl.NumberFormat(undefined, { + ...options, + notation: options?.compact ? getFormatUSDNotation(amount) : undefined + }); return format(amount); }; @@ -141,8 +168,19 @@ function getPolkadotLink(blockHeight: number): string { const monetaryToNumber = (monetaryAmount: MonetaryAmount | undefined): number => monetaryAmount?.toBig().toNumber() || 0; +const newSafeMonetaryAmount: typeof newMonetaryAmount = (...args) => { + try { + return newMonetaryAmount(...args); + } catch (e) { + const [, ...rest] = args; + return newMonetaryAmount(0, ...rest); + } +}; + export { + convertMonetaryAmountToUsdBig as convertMonetaryAmountToBigUSD, convertMonetaryAmountToValueInUSD, + convertMonetaryBtcToUSD, displayMonetaryAmount, displayMonetaryAmountInUSDFormat, formatDateTime, @@ -154,6 +192,7 @@ export { getPolkadotLink, getRandomVaultIdWithCapacity, monetaryToNumber, + newSafeMonetaryAmount, shortAddress, shortTxId }; diff --git a/src/component-library/Accordion/Accordion.stories.tsx b/src/component-library/Accordion/Accordion.stories.tsx index d4501c70a8..2d3fbd80a0 100644 --- a/src/component-library/Accordion/Accordion.stories.tsx +++ b/src/component-library/Accordion/Accordion.stories.tsx @@ -5,13 +5,13 @@ import { Accordion, AccordionItem, AccordionProps } from '.'; const Template: Story = (args) => ( - +

This is item 1 section

- +

This is item 2 section

- +

This is item 3 section

diff --git a/src/component-library/Accordion/Accordion.tsx b/src/component-library/Accordion/Accordion.tsx index 0cad1895a1..eec9aad346 100644 --- a/src/component-library/Accordion/Accordion.tsx +++ b/src/component-library/Accordion/Accordion.tsx @@ -1,91 +1,44 @@ -import { Expandable } from '@react-types/shared'; -import { forwardRef, HTMLAttributes, Key, useEffect, useState } from 'react'; +import { AriaAccordionProps, useAccordion } from '@react-aria/accordion'; +import { mergeProps } from '@react-aria/utils'; +import { useTreeState } from '@react-stately/tree'; +import { forwardRef, HTMLAttributes, Ref } from 'react'; import { useDOMRef } from '../utils/dom'; -import { AccordionContext } from './AccordionContext'; -import { useCollection } from './use-collection'; - -const transformKeys = (keys: Key[]): Key[] => keys.map((key) => `.$${key}`); +import { FontSize } from '../utils/prop-types'; +import { AccordionItem } from './AccordionItem'; type Props = { - disabledKeys?: Array; - /** The currently expanded keys in the collection (controlled). */ - expandedKeys?: Array; - /** The initial expanded keys in the collection (uncontrolled). */ - defaultExpandedKeys?: Array; - /** Handler that is called when items are expanded or collapsed. */ - onExpandedChange?: (keys: Array) => any; + size?: FontSize; }; -type InheritAttrs = Omit; - -type NativeAttrs = Omit, keyof InheritAttrs & Props>; - -type AccordionProps = Props & InheritAttrs & NativeAttrs; - -// TODO: rewrite using react-aria accordion when it becomes more stable -// MEMO: we are only only able to target elements that have specified keys -// when trying to disable them or expand them. -const Accordion = forwardRef( - ( - { - defaultExpandedKeys = [], - expandedKeys: expandedKeysProp, - onExpandedChange, - disabledKeys: disabledKeysProp = [], - ...props - }, - ref - ): JSX.Element => { - const [expandedKeys, setExpandedKeys] = useState(new Set(transformKeys(defaultExpandedKeys))); - const disabledKeys = new Set(transformKeys(disabledKeysProp)); - - const accordionRef = useDOMRef(ref); - const collection = useCollection(props, accordionRef); - - // Checks for updates in expandedKeysProp, just in - // case the prop is being controlled from outside - useEffect(() => { - if (!expandedKeysProp) return; +type InheritAttrs = Omit, keyof Props>; - const isEqual = expandedKeysProp.every((value) => expandedKeys.has(value)); +type NativeAttrs = Omit, (keyof InheritAttrs & Props) | 'children'>; - if (isEqual) return; +type AccordionProps = Props & InheritAttrs & NativeAttrs; - setExpandedKeys(new Set(transformKeys(expandedKeysProp))); - }, [expandedKeys, expandedKeysProp]); +const Accordion = >( + { size = 'base', ...props }: AccordionProps, + ref: Ref +): JSX.Element => { + const state = useTreeState(props); + const accordionRef = useDOMRef(ref); + const { accordionProps } = useAccordion(props, state, accordionRef); - const updateKeys = (key: Key) => { - if (!expandedKeys) return; - - const newSet = new Set([...expandedKeys]); - - if (newSet.has(key)) { - newSet.delete(key); - } else { - newSet.add(key); - } - - setExpandedKeys(newSet); - onExpandedChange?.(Array.from(newSet)); - }; - - return ( - -
- - ); - } -); + return ( +
+ {[...state.collection].map((item) => ( + key={item.key} item={item} state={state} size={size} /> + ))} +
+ ); +}; Accordion.displayName = 'Accordion'; -export { Accordion }; +const _Accordion = forwardRef(Accordion) as ( + props: AccordionProps & { ref?: Ref } +) => ReturnType; + +export { _Accordion as Accordion }; export type { AccordionProps }; diff --git a/src/component-library/Accordion/AccordionContext.tsx b/src/component-library/Accordion/AccordionContext.tsx deleted file mode 100644 index 101272cfda..0000000000 --- a/src/component-library/Accordion/AccordionContext.tsx +++ /dev/null @@ -1,18 +0,0 @@ -import React, { Key } from 'react'; - -interface AccordionConfig { - disabledKeys?: Set; - expandedKeys?: Set; - defaultExpandedKeys?: Set; - collection?: Map; - updateKeys?: (key: Key) => void; -} - -const defaultContext = { collection: new Map() }; - -const AccordionContext = React.createContext(defaultContext); - -const useAccordionContext = (): AccordionConfig => React.useContext(AccordionContext); - -export { AccordionContext, useAccordionContext }; -export type { AccordionConfig }; diff --git a/src/component-library/Accordion/AccordionItem.tsx b/src/component-library/Accordion/AccordionItem.tsx index 8d73e08f70..5ec1dd7593 100644 --- a/src/component-library/Accordion/AccordionItem.tsx +++ b/src/component-library/Accordion/AccordionItem.tsx @@ -1,101 +1,52 @@ -import { useButton } from '@react-aria/button'; +import { useAccordionItem } from '@react-aria/accordion'; import { FocusRing, useFocusRing } from '@react-aria/focus'; -import { mergeProps, useId } from '@react-aria/utils'; -import { ButtonHTMLAttributes, Key, ReactNode, RefObject, useEffect, useRef, useState } from 'react'; +import { mergeProps } from '@react-aria/utils'; +import { TreeState } from '@react-stately/tree'; +import { Node } from '@react-types/shared'; +import { useRef } from 'react'; +import { FontSize } from '../utils/prop-types'; import { StyledAccordionItemButton, StyledAccordionItemHeading, StyledAccordionItemWrapper, StyledChevronDown } from './Accordion.style'; -import { useAccordionContext } from './AccordionContext'; import { AccordionItemRegion } from './AccordionItemRegion'; -// The `collection` variables contains a set o Keys mapped to -// elements references. Each component gets their identitiy from -// getting their key using their ref object. -const useKey = (ref: RefObject): Key | undefined => { - const { collection } = useAccordionContext(); - - const [key, setKey] = useState(); - - useEffect(() => { - if (!collection || !ref.current) return; - - const elementKey = collection.get(ref.current); - - if (!elementKey) return; - - setKey(elementKey); - }, [collection, key, ref]); - - return key; +type AccordionItemProps = { + item: Node; + state: TreeState; + size?: FontSize; }; - -type Props = { - title: ReactNode; -}; - -type NativeAttrs = Omit, keyof Props>; - -type AccordionItemProps = Props & NativeAttrs; - -const AccordionItem = ({ className, style, title, children, ...props }: AccordionItemProps): JSX.Element => { - const { disabledKeys, expandedKeys, updateKeys } = useAccordionContext(); - - const accordionItemRef = useRef(null); - const key = useKey(accordionItemRef); - - const isDisabled = !!key && !!disabledKeys?.has(key); - const isExpanded = !!key && !!expandedKeys?.has(key); - - const buttonId = useId(); - const regionId = useId(); - - const btnRef = useRef(null); - - const { buttonProps: ariaButtonProps } = useButton( - mergeProps(props as any, { - id: buttonId, - elementType: 'button', - isDisabled, - onPress: () => key && updateKeys?.(key) - }), - btnRef - ); - - const { focusProps, isFocusVisible } = useFocusRing(props); - - const buttonProps: ButtonHTMLAttributes = { - ...mergeProps(ariaButtonProps, focusProps), - 'aria-expanded': isExpanded, - 'aria-controls': isExpanded ? regionId : undefined - }; - - const regionProps = { - id: regionId, - role: 'region', - 'aria-labelledby': buttonId - }; +const AccordionItem = >({ + size = 'base', + ...props +}: AccordionItemProps): JSX.Element => { + const ref = useRef(null); + const { state, item } = props; + const { buttonProps, regionProps } = useAccordionItem(props, state, ref); + const isExpanded = state.expandedKeys.has(item.key); + const isDisabled = state.disabledKeys.has(item.key); + const { isFocusVisible, focusProps } = useFocusRing(); return ( - - + + - {title} + {item.props.title} - {children} + {item.props.children} ); diff --git a/src/component-library/Accordion/index.tsx b/src/component-library/Accordion/index.tsx index c451a92ab3..eebee03028 100644 --- a/src/component-library/Accordion/index.tsx +++ b/src/component-library/Accordion/index.tsx @@ -1,4 +1,4 @@ export type { AccordionProps } from './Accordion'; export { Accordion } from './Accordion'; -export type { AccordionItemProps } from './AccordionItem'; -export { AccordionItem } from './AccordionItem'; +export { Item as AccordionItem } from '@react-stately/collections'; +export type { ItemProps as AccordionItemProps } from '@react-types/shared'; diff --git a/src/component-library/Accordion/use-collection.tsx b/src/component-library/Accordion/use-collection.tsx deleted file mode 100644 index 4c3db18a80..0000000000 --- a/src/component-library/Accordion/use-collection.tsx +++ /dev/null @@ -1,35 +0,0 @@ -import { Children, isValidElement, Key, RefObject, useEffect, useState } from 'react'; - -import { AccordionProps } from './Accordion'; - -// We need to create a map that will help each `AcordionItem` -// identify themselfs. To do so, we need to access each element -// element from `children`, check if the element is valid and finally -// access exposed key. -// When the keys are gathered, they are used as keys for our `Map`, where -// the values will be the component to whom that key belongs. -const useCollection = ( - { children }: AccordionProps, - ref: RefObject -): Map | undefined => { - const [collection, setCollection] = useState>(); - - useEffect(() => { - if (!ref.current?.childNodes) return; - - // Gathering Keys - const keys = Children.toArray(children).map((child) => (isValidElement(child) ? child.key : null)); - - // Mapping keys to components - const nodesMap = Array.from(ref.current.childNodes).reduce((map, node, index) => { - const nodeKey = keys[index]; - return nodeKey ? map.set(node, nodeKey) : map; - }, new Map()); - - setCollection(nodesMap); - }, [ref, children]); - - return collection; -}; - -export { useCollection }; diff --git a/src/component-library/CTA/BaseCTA.tsx b/src/component-library/CTA/BaseCTA.tsx index 531dcce9f0..6f67107350 100644 --- a/src/component-library/CTA/BaseCTA.tsx +++ b/src/component-library/CTA/BaseCTA.tsx @@ -1,7 +1,7 @@ import { forwardRef, HTMLAttributes } from 'react'; import { StyledComponent } from 'styled-components'; -import { CTAVariants, ElementTypeProp, Sizes } from '../utils/prop-types'; +import { CTASizes, CTAVariants, ElementTypeProp } from '../utils/prop-types'; import { OutlinedCTA, PrimaryCTA, SecondaryCTA, StyledCTAProps, TextCTA } from './CTA.style'; const ctaElements: Record> = { @@ -14,7 +14,7 @@ const ctaElements: Record` text-decoration: none; width: ${(props) => (props.$fullWidth ? '100%' : 'auto')}; background: none; + // TODO: enforce outline outline: ${({ $isFocusVisible }) => !$isFocusVisible && 'none'}; &[aria-disabled='true'], diff --git a/src/component-library/CTA/CTA.tsx b/src/component-library/CTA/CTA.tsx index e626da222e..dfe98e7cf2 100644 --- a/src/component-library/CTA/CTA.tsx +++ b/src/component-library/CTA/CTA.tsx @@ -6,11 +6,12 @@ import { ButtonHTMLAttributes, forwardRef } from 'react'; import { LoadingSpinner } from '../LoadingSpinner'; import { useDOMRef } from '../utils/dom'; -import { Sizes } from '../utils/prop-types'; +import { CTASizes } from '../utils/prop-types'; import { BaseCTA, BaseCTAProps } from './BaseCTA'; import { LoadingWrapper } from './CTA.style'; -const loadingSizes: Record = { +const loadingSizes: Record = { + 'x-small': 14, small: 16, medium: 18, large: 20 @@ -18,7 +19,7 @@ const loadingSizes: Record = { type Props = { fullWidth?: boolean; - size?: Sizes; + size?: CTASizes; loading?: boolean; onPress?: (e: PressEvent) => void; }; diff --git a/src/component-library/CTA/CTALink.tsx b/src/component-library/CTA/CTALink.tsx index f0e3a3eea5..bc6809d758 100644 --- a/src/component-library/CTA/CTALink.tsx +++ b/src/component-library/CTA/CTALink.tsx @@ -1,6 +1,10 @@ +import { useFocusRing } from '@react-aria/focus'; +import { useLink } from '@react-aria/link'; +import { mergeProps } from '@react-aria/utils'; import { forwardRef } from 'react'; import { Link, LinkProps } from 'react-router-dom'; +import { useDOMRef } from '../utils/dom'; import { BaseCTA, BaseCTAProps } from './BaseCTA'; type Props = { @@ -8,28 +12,44 @@ type Props = { disabled?: boolean; }; -type NativeAttrs = Omit; +type NativeAttrs = Omit; -type InheritAttrs = Omit; +type AriaAttrs = Omit; -type CTALinkProps = Props & NativeAttrs & InheritAttrs; +type InheritAttrs = Omit; + +type CTALinkProps = Props & NativeAttrs & AriaAttrs & InheritAttrs; // TODO: Does this need to be changed to a React Router link component? const CTALink = forwardRef( - ({ disabled, onClick, external, to, ...props }, ref): JSX.Element => { - const linkProps: LinkProps = external - ? { to: { pathname: to as string }, target: '_blank', rel: 'noreferrer' } - : { to }; + ({ disabled, external, to: toProp, children, ...props }, ref): JSX.Element => { + const linkRef = useDOMRef(ref); + + const ariaProps = { + ...props, + isDisabled: disabled, + href: toProp, + ...(external && { target: '_blank', rel: 'noreferrer' }) + }; + + const { linkProps } = useLink(ariaProps, linkRef); + const { focusProps, isFocusVisible } = useFocusRing(); + + const to = external && typeof toProp === 'string' ? { pathname: toProp as string } : toProp; return ( + isFocusVisible={isFocusVisible} + {...mergeProps(props, linkProps, focusProps, { + href: undefined, + to, + ...(external && { target: '_blank', rel: 'noreferrer' }) + })} + > + {children} + ); } ); diff --git a/src/component-library/CTA/index.tsx b/src/component-library/CTA/index.tsx index 8e53ab575c..01bda90ceb 100644 --- a/src/component-library/CTA/index.tsx +++ b/src/component-library/CTA/index.tsx @@ -1,3 +1,5 @@ +export type { BaseCTAProps } from './BaseCTA'; +export { BaseCTA } from './BaseCTA'; export type { CTAProps } from './CTA'; export { CTA } from './CTA'; export type { CTALinkProps } from './CTALink'; diff --git a/src/component-library/CoinIcon/CoinIcon.stories.tsx b/src/component-library/CoinIcon/CoinIcon.stories.tsx index 9036e9e778..0aae32c842 100644 --- a/src/component-library/CoinIcon/CoinIcon.stories.tsx +++ b/src/component-library/CoinIcon/CoinIcon.stories.tsx @@ -1,15 +1,57 @@ import { Meta, Story } from '@storybook/react'; +import { Flex } from '../Flex'; +import { Span } from '../Text'; import { CoinIcon, CoinIconProps } from './CoinIcon'; +import * as ICONS from './icons'; -const Template: Story = (args) => ; +const Template: Story = (args) => ( + + {Object.keys(ICONS).map((icon) => ( + + + {icon} + + ))} + +); const Default = Template.bind({}); Default.args = { - ticker: 'IBTC' + size: 'xl', + ticker: undefined }; -export { Default }; +const LPTokensTemplate: Story = (args) => { + return ( + + + + LP KBTC-KSM + + + + LP IBTC-INTR-DOT + + + + LP IBTC-INTR-DOT-USDT + + + + KSM+(LKSM+VKSM+SKSM) + + + ); +}; + +const LPTokens = LPTokensTemplate.bind({}); +LPTokens.args = { + size: 'xl', + ticker: undefined +}; + +export { Default, LPTokens }; export default { title: 'Elements/CoinIcon', diff --git a/src/component-library/CoinIcon/CoinIcon.style.tsx b/src/component-library/CoinIcon/CoinIcon.style.tsx new file mode 100644 index 0000000000..9c5cc225c7 --- /dev/null +++ b/src/component-library/CoinIcon/CoinIcon.style.tsx @@ -0,0 +1,11 @@ +import styled from 'styled-components'; + +import { Icon } from '../Icon'; +import { theme } from '../theme'; + +const StyledFallbackIcon = styled(Icon)` + stroke: ${theme.icon.fallback.stroke}; + color: ${theme.icon.fallback.color}; +`; + +export { StyledFallbackIcon }; diff --git a/src/component-library/CoinIcon/CoinIcon.tsx b/src/component-library/CoinIcon/CoinIcon.tsx index d628b0118a..11276bd4f4 100644 --- a/src/component-library/CoinIcon/CoinIcon.tsx +++ b/src/component-library/CoinIcon/CoinIcon.tsx @@ -1,22 +1,14 @@ -import { FC, forwardRef } from 'react'; +import { forwardRef } from 'react'; import { IconProps } from '../Icon'; -import { BTC, DOT, IBTC, INTR, KBTC, KINT, KSM, LKSM, USDT } from './icons'; - -const coinsIcon: Record = { - BTC, - DOT, - IBTC, - INTR, - KBTC, - KINT, - KSM, - LKSM, - USDT -}; +import { FallbackIcon } from './FallbackIcon'; +import { LPCoinIcon } from './LPCoinIcon'; +import { coins } from './utils'; type Props = { ticker: string; + // Multi tickers icons + tickers?: string[]; }; type NativeAttrs = Omit; @@ -24,8 +16,17 @@ type NativeAttrs = Omit; type CoinIconProps = Props & NativeAttrs; const CoinIcon = forwardRef( - ({ ticker, ...props }, ref): JSX.Element => { - const CoinIcon = (coinsIcon[ticker] || (() => null)) as any; + ({ ticker, tickers, ...props }, ref): JSX.Element => { + // Only want to render multi-token if has more than 1 ticker + if (tickers && tickers?.length > 1) { + return ; + } + + const CoinIcon = coins[ticker]; + + if (!CoinIcon) { + return ; + } return ; } diff --git a/src/component-library/CoinIcon/FallbackIcon.tsx b/src/component-library/CoinIcon/FallbackIcon.tsx new file mode 100644 index 0000000000..6657a13bf2 --- /dev/null +++ b/src/component-library/CoinIcon/FallbackIcon.tsx @@ -0,0 +1,17 @@ +import { forwardRef } from 'react'; + +import { CoinIconProps } from './CoinIcon'; +import { StyledFallbackIcon } from './CoinIcon.style'; + +const FallbackIcon = forwardRef( + ({ ticker, ...props }, ref): JSX.Element => ( + + {ticker} + + + ) +); + +FallbackIcon.displayName = 'FallbackIcon'; + +export { FallbackIcon }; diff --git a/src/component-library/CoinIcon/LPCoinIcon.tsx b/src/component-library/CoinIcon/LPCoinIcon.tsx new file mode 100644 index 0000000000..9e09ddcc41 --- /dev/null +++ b/src/component-library/CoinIcon/LPCoinIcon.tsx @@ -0,0 +1,64 @@ +import { forwardRef, useCallback } from 'react'; + +import { Icon } from '../Icon'; +import { CoinIconProps } from './CoinIcon'; +import { FallbackIcon } from './FallbackIcon'; +import { coins } from './utils'; + +type Props = { + tickers: string[]; +}; + +type InheritAttrs = Omit; + +type LPCoinIconProps = Props & InheritAttrs; + +const LPCoinIcon = forwardRef( + ({ tickers, size = 'md', ticker, ...props }, ref): JSX.Element => { + const [tickerA, tickerB, tickerC, tickerD] = tickers; + + const getIcon = useCallback( + (ticker: string) => coins[ticker] || (() => ), + [size] + ); + + const IconA = getIcon(tickerA); + const IconB = getIcon(tickerB); + + if (tickers.length === 2) { + return ( + + {ticker} + + + + ); + } + + const IconC = getIcon(tickerC); + + const hasIconD = !!tickerD; + + const IconD = hasIconD && getIcon(tickerD); + + const commonSize = { + width: '60%', + height: '60%' + }; + + return ( + + {ticker} + + + + {IconD && } + + ); + } +); + +LPCoinIcon.displayName = 'LPCoinIcon'; + +export { LPCoinIcon }; +export type { LPCoinIconProps }; diff --git a/src/component-library/CoinIcon/icons/AUSD.tsx b/src/component-library/CoinIcon/icons/AUSD.tsx new file mode 100644 index 0000000000..823d9910f5 --- /dev/null +++ b/src/component-library/CoinIcon/icons/AUSD.tsx @@ -0,0 +1,52 @@ +import { forwardRef } from 'react'; + +import { Icon, IconProps } from '@/component-library/Icon'; + +const AUSD = forwardRef((props, ref) => ( + + AUSD + + + + + + + + + + + + + + + + + + + + +)); + +AUSD.displayName = 'AUSD'; + +export { AUSD }; diff --git a/src/component-library/CoinIcon/icons/ETH.tsx b/src/component-library/CoinIcon/icons/ETH.tsx new file mode 100644 index 0000000000..10ad8c7954 --- /dev/null +++ b/src/component-library/CoinIcon/icons/ETH.tsx @@ -0,0 +1,36 @@ +import { forwardRef } from 'react'; + +import { Icon, IconProps } from '@/component-library/Icon'; + +const ETH = forwardRef((props, ref) => ( + + ETH + + + + + + + + + + + + + + + + + + + + + + + + +)); + +ETH.displayName = 'ETH'; + +export { ETH }; diff --git a/src/component-library/CoinIcon/icons/IBTC.tsx b/src/component-library/CoinIcon/icons/IBTC.tsx index e2da08bcc3..c86996a02c 100644 --- a/src/component-library/CoinIcon/icons/IBTC.tsx +++ b/src/component-library/CoinIcon/icons/IBTC.tsx @@ -3,21 +3,33 @@ import { forwardRef } from 'react'; import { Icon, IconProps } from '@/component-library/Icon'; const IBTC = forwardRef((props, ref) => ( - + IBTC - - - - + + + + + + + + + + + + + + + + )); diff --git a/src/component-library/CoinIcon/icons/INTR.tsx b/src/component-library/CoinIcon/icons/INTR.tsx index ae916bd402..b8159ae338 100644 --- a/src/component-library/CoinIcon/icons/INTR.tsx +++ b/src/component-library/CoinIcon/icons/INTR.tsx @@ -3,12 +3,25 @@ import { forwardRef } from 'react'; import { Icon, IconProps } from '@/component-library/Icon'; const INTR = forwardRef((props, ref) => ( - + INTR - - - - + + + + + + + + + + + + + + )); diff --git a/src/component-library/CoinIcon/icons/KAR.tsx b/src/component-library/CoinIcon/icons/KAR.tsx new file mode 100644 index 0000000000..dd25f37ecd --- /dev/null +++ b/src/component-library/CoinIcon/icons/KAR.tsx @@ -0,0 +1,51 @@ +import { forwardRef } from 'react'; + +import { Icon, IconProps } from '@/component-library/Icon'; + +const KAR = forwardRef((props, ref) => ( + + KAR + + + + + + + + + + + + + + + + + + + +)); + +KAR.displayName = 'KAR'; + +export { KAR }; diff --git a/src/component-library/CoinIcon/icons/KBTC.tsx b/src/component-library/CoinIcon/icons/KBTC.tsx index 2a6ae92cdc..f94b4d65ef 100644 --- a/src/component-library/CoinIcon/icons/KBTC.tsx +++ b/src/component-library/CoinIcon/icons/KBTC.tsx @@ -3,48 +3,46 @@ import { forwardRef } from 'react'; import { Icon, IconProps } from '@/component-library/Icon'; const KBTC = forwardRef((props, ref) => ( - + KBTC - - - - - - - - + + + + + + + + + + @@ -54,6 +52,9 @@ const KBTC = forwardRef((props, ref) => ( + + + )); diff --git a/src/component-library/CoinIcon/icons/KINT.tsx b/src/component-library/CoinIcon/icons/KINT.tsx index e0e96bc01d..ddbee05c5e 100644 --- a/src/component-library/CoinIcon/icons/KINT.tsx +++ b/src/component-library/CoinIcon/icons/KINT.tsx @@ -3,38 +3,50 @@ import { forwardRef } from 'react'; import { Icon, IconProps } from '@/component-library/Icon'; const KINT = forwardRef((props, ref) => ( - + KINT - - - - - - + + + + + + + + - + - - - - + + + + + + + )); diff --git a/src/component-library/CoinIcon/icons/LSKSM.tsx b/src/component-library/CoinIcon/icons/LSKSM.tsx new file mode 100644 index 0000000000..ae7a26d940 --- /dev/null +++ b/src/component-library/CoinIcon/icons/LSKSM.tsx @@ -0,0 +1,29 @@ +import { forwardRef } from 'react'; + +import { Icon, IconProps } from '@/component-library/Icon'; + +const LSKSM = forwardRef((props, ref) => ( + + LSKSM + + + + + + + + + + + + + + +)); + +LSKSM.displayName = 'LSKSM'; + +export { LSKSM }; diff --git a/src/component-library/CoinIcon/icons/MOVR.tsx b/src/component-library/CoinIcon/icons/MOVR.tsx new file mode 100644 index 0000000000..3c1623057b --- /dev/null +++ b/src/component-library/CoinIcon/icons/MOVR.tsx @@ -0,0 +1,191 @@ +import { forwardRef } from 'react'; + +import { Icon, IconProps } from '@/component-library/Icon'; + +const MOVR = forwardRef((props, ref) => ( + + MOVR + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +)); + +MOVR.displayName = 'MOVR'; + +export { MOVR }; diff --git a/src/component-library/CoinIcon/icons/SKSM.tsx b/src/component-library/CoinIcon/icons/SKSM.tsx new file mode 100644 index 0000000000..f77da9ae73 --- /dev/null +++ b/src/component-library/CoinIcon/icons/SKSM.tsx @@ -0,0 +1,29 @@ +import { forwardRef } from 'react'; + +import { Icon, IconProps } from '@/component-library/Icon'; + +const SKSM = forwardRef((props, ref) => ( + + SKSM + + + + + + + + + + + + + + +)); + +SKSM.displayName = 'SKSM'; + +export { SKSM }; diff --git a/src/component-library/CoinIcon/icons/VKSM.tsx b/src/component-library/CoinIcon/icons/VKSM.tsx new file mode 100644 index 0000000000..01d4c9d94a --- /dev/null +++ b/src/component-library/CoinIcon/icons/VKSM.tsx @@ -0,0 +1,29 @@ +import { forwardRef } from 'react'; + +import { Icon, IconProps } from '@/component-library/Icon'; + +const VKSM = forwardRef((props, ref) => ( + + VKSM + + + + + + + + + + + + + + +)); + +VKSM.displayName = 'VKSM'; + +export { VKSM }; diff --git a/src/component-library/CoinIcon/icons/index.ts b/src/component-library/CoinIcon/icons/index.ts index 63d65cb5c5..610888ac36 100644 --- a/src/component-library/CoinIcon/icons/index.ts +++ b/src/component-library/CoinIcon/icons/index.ts @@ -1,9 +1,24 @@ +export { AUSD } from './AUSD'; export { BTC } from './BTC'; export { DOT } from './DOT'; +export { ETH } from './ETH'; export { IBTC } from './IBTC'; export { INTR } from './INTR'; +export { KAR } from './KAR'; export { KBTC } from './KBTC'; export { KINT } from './KINT'; export { KSM } from './KSM'; export { LKSM } from './LKSM'; +export { LSKSM } from './LSKSM'; +export { MOVR } from './MOVR'; +export { qDOT } from './qDOT'; +export { qIBTC } from './qIBTC'; +export { qINTR } from './qINTR'; +export { qKBTC } from './qKBTC'; +export { qKINT } from './qKINT'; +export { qKSM } from './qKSM'; +export { qMOVR } from './qMOVR'; +export { qUSDT } from './qUSDT'; +export { SKSM } from './SKSM'; export { USDT } from './USDT'; +export { VKSM } from './VKSM'; diff --git a/src/component-library/CoinIcon/icons/qDOT.tsx b/src/component-library/CoinIcon/icons/qDOT.tsx new file mode 100644 index 0000000000..340e41f295 --- /dev/null +++ b/src/component-library/CoinIcon/icons/qDOT.tsx @@ -0,0 +1,50 @@ +import { forwardRef } from 'react'; + +import { Icon, IconProps } from '@/component-library/Icon'; + +const qDOT = forwardRef((props, ref) => ( + + qDOT + + + + + + + + + + + + + + + + + + + + +)); + +qDOT.displayName = 'qDOT'; + +export { qDOT }; diff --git a/src/component-library/CoinIcon/icons/qIBTC.tsx b/src/component-library/CoinIcon/icons/qIBTC.tsx new file mode 100644 index 0000000000..1a44c96635 --- /dev/null +++ b/src/component-library/CoinIcon/icons/qIBTC.tsx @@ -0,0 +1,38 @@ +import { forwardRef } from 'react'; + +import { Icon, IconProps } from '@/component-library/Icon'; + +const qIBTC = forwardRef((props, ref) => ( + + qIBTC + + + + + + + + + + + + + + + + + +)); + +qIBTC.displayName = 'qIBTC'; + +export { qIBTC }; diff --git a/src/component-library/CoinIcon/icons/qINTR.tsx b/src/component-library/CoinIcon/icons/qINTR.tsx new file mode 100644 index 0000000000..5e1014b329 --- /dev/null +++ b/src/component-library/CoinIcon/icons/qINTR.tsx @@ -0,0 +1,30 @@ +import { forwardRef } from 'react'; + +import { Icon, IconProps } from '@/component-library/Icon'; + +const qINTR = forwardRef((props, ref) => ( + + qINTR + + + + + + + + + + + + + + + +)); + +qINTR.displayName = 'qINTR'; + +export { qINTR }; diff --git a/src/component-library/CoinIcon/icons/qKBTC.tsx b/src/component-library/CoinIcon/icons/qKBTC.tsx new file mode 100644 index 0000000000..8d1ea779b1 --- /dev/null +++ b/src/component-library/CoinIcon/icons/qKBTC.tsx @@ -0,0 +1,64 @@ +import { forwardRef } from 'react'; + +import { Icon, IconProps } from '@/component-library/Icon'; + +const qKBTC = forwardRef((props, ref) => ( + + qKBTC + + + + + + + + + + + + + + + + + + + + + + + + +)); + +qKBTC.displayName = 'qKBTC'; + +export { qKBTC }; diff --git a/src/component-library/CoinIcon/icons/qKINT.tsx b/src/component-library/CoinIcon/icons/qKINT.tsx new file mode 100644 index 0000000000..75e4c6cd6f --- /dev/null +++ b/src/component-library/CoinIcon/icons/qKINT.tsx @@ -0,0 +1,56 @@ +import { forwardRef } from 'react'; + +import { Icon, IconProps } from '@/component-library/Icon'; + +const qKINT = forwardRef((props, ref) => ( + + qKINT + + + + + + + + + + + + + + + + + + + + + + +)); + +qKINT.displayName = 'qKINT'; + +export { qKINT }; diff --git a/src/component-library/CoinIcon/icons/qKSM.tsx b/src/component-library/CoinIcon/icons/qKSM.tsx new file mode 100644 index 0000000000..63317200f2 --- /dev/null +++ b/src/component-library/CoinIcon/icons/qKSM.tsx @@ -0,0 +1,25 @@ +import { forwardRef } from 'react'; + +import { Icon, IconProps } from '@/component-library/Icon'; + +const qKSM = forwardRef((props, ref) => ( + + qKSM + + + + + + + + + + +)); + +qKSM.displayName = 'qKSM'; + +export { qKSM }; diff --git a/src/component-library/CoinIcon/icons/qMOVR.tsx b/src/component-library/CoinIcon/icons/qMOVR.tsx new file mode 100644 index 0000000000..92bff58bdd --- /dev/null +++ b/src/component-library/CoinIcon/icons/qMOVR.tsx @@ -0,0 +1,191 @@ +import { forwardRef } from 'react'; + +import { Icon, IconProps } from '@/component-library/Icon'; + +const qMOVR = forwardRef((props, ref) => ( + + qMOVR + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +)); + +qMOVR.displayName = 'qMOVR'; + +export { qMOVR }; diff --git a/src/component-library/CoinIcon/icons/qUSDT.tsx b/src/component-library/CoinIcon/icons/qUSDT.tsx new file mode 100644 index 0000000000..b556bd20c4 --- /dev/null +++ b/src/component-library/CoinIcon/icons/qUSDT.tsx @@ -0,0 +1,25 @@ +import { forwardRef } from 'react'; + +import { Icon, IconProps } from '@/component-library/Icon'; + +const qUSDT = forwardRef((props, ref) => ( + + qUSDT + + + + + + + + + + +)); + +qUSDT.displayName = 'qUSDT'; + +export { qUSDT }; diff --git a/src/component-library/CoinIcon/types.ts b/src/component-library/CoinIcon/types.ts new file mode 100644 index 0000000000..76b9b408aa --- /dev/null +++ b/src/component-library/CoinIcon/types.ts @@ -0,0 +1,5 @@ +import { ForwardRefExoticComponent, RefAttributes } from 'react'; + +import { IconProps } from '../Icon'; + +export type CoinComponent = ForwardRefExoticComponent>; diff --git a/src/component-library/CoinIcon/utils.ts b/src/component-library/CoinIcon/utils.ts new file mode 100644 index 0000000000..c9ebbb691f --- /dev/null +++ b/src/component-library/CoinIcon/utils.ts @@ -0,0 +1,54 @@ +import { + AUSD, + BTC, + DOT, + ETH, + IBTC, + INTR, + KAR, + KBTC, + KINT, + KSM, + LKSM, + LSKSM, + MOVR, + qDOT, + qIBTC, + qINTR, + qKBTC, + qKINT, + qKSM, + qMOVR, + qUSDT, + SKSM, + USDT, + VKSM +} from './icons'; +import { CoinComponent } from './types'; + +export const coins: Record = { + BTC, + DOT, + IBTC, + INTR, + KBTC, + KINT, + KSM, + LKSM, + USDT, + VKSM, + LSKSM, + MOVR, + SKSM, + qUSDT, + qKINT, + qKBTC, + qKSM, + qMOVR, + AUSD, + KAR, + qDOT, + qIBTC, + qINTR, + ETH +}; diff --git a/src/component-library/Divider/Divider.style.tsx b/src/component-library/Divider/Divider.style.tsx index 9e76398fe0..28d8b361bc 100644 --- a/src/component-library/Divider/Divider.style.tsx +++ b/src/component-library/Divider/Divider.style.tsx @@ -1,17 +1,19 @@ import styled from 'styled-components'; -import { Colors, Orientation } from '../utils/prop-types'; +import { theme } from '../theme'; +import { DividerVariants, Orientation, Sizes } from '../utils/prop-types'; import { resolveColor } from '../utils/theme'; type StyledDividerProps = { - $color: Colors; + $color: DividerVariants; $orientation: Orientation; + $size: Sizes; }; const StyledDivider = styled.hr` - background-color: ${({ $color }) => resolveColor($color)}; - height: ${({ $orientation }) => ($orientation === 'horizontal' ? '2px' : 'auto')}; - width: ${({ $orientation }) => ($orientation === 'horizontal' ? '' : '2px')}; + background-color: ${({ $color }) => ($color === 'default' ? 'var(--colors-border)' : resolveColor($color))}; + height: ${({ $orientation, $size }) => ($orientation === 'horizontal' ? theme.divider.size[$size] : 'auto')}; + width: ${({ $orientation, $size }) => ($orientation === 'horizontal' ? '' : theme.divider.size[$size])}; border: 0; margin: 0; align-self: stretch; diff --git a/src/component-library/Divider/Divider.tsx b/src/component-library/Divider/Divider.tsx index 3d00633b53..6d1e3cebfa 100644 --- a/src/component-library/Divider/Divider.tsx +++ b/src/component-library/Divider/Divider.tsx @@ -2,12 +2,13 @@ import { useSeparator } from '@react-aria/separator'; import { mergeProps } from '@react-aria/utils'; import { forwardRef, HTMLAttributes } from 'react'; -import { Colors, ElementTypeProp, Orientation } from '../utils/prop-types'; +import { DividerVariants, ElementTypeProp, Orientation, Sizes } from '../utils/prop-types'; import { StyledDivider } from './Divider.style'; type Props = { orientation?: Orientation; - color?: Colors; + color?: DividerVariants; + size?: Sizes; }; type NativeAttrs = Omit, keyof Props>; @@ -15,7 +16,10 @@ type NativeAttrs = Omit, keyof Props>; type DividerProps = Props & NativeAttrs & ElementTypeProp; const Divider = forwardRef( - ({ elementType: elementTypeProp, orientation = 'horizontal', color = 'primary', ...props }, ref): JSX.Element => { + ( + { elementType: elementTypeProp, orientation = 'horizontal', color = 'primary', size = 'small', ...props }, + ref + ): JSX.Element => { const elementType = elementTypeProp || orientation === 'vertical' ? 'div' : 'hr'; const { separatorProps } = useSeparator({ @@ -29,6 +33,7 @@ const Divider = forwardRef( as={elementType} $color={color} $orientation={orientation} + $size={size} {...mergeProps(separatorProps, props)} /> ); diff --git a/src/component-library/Field/Field.style.tsx b/src/component-library/Field/Field.style.tsx new file mode 100644 index 0000000000..3533b0c3b4 --- /dev/null +++ b/src/component-library/Field/Field.style.tsx @@ -0,0 +1,12 @@ +import styled from 'styled-components'; + +import { Flex } from '../Flex'; +import { theme } from '../theme'; + +const Wrapper = styled(Flex)` + position: relative; + color: ${theme.colors.textPrimary}; + box-sizing: border-box; +`; + +export { Wrapper }; diff --git a/src/component-library/Field/Field.tsx b/src/component-library/Field/Field.tsx new file mode 100644 index 0000000000..e10882d375 --- /dev/null +++ b/src/component-library/Field/Field.tsx @@ -0,0 +1,76 @@ +import { forwardRef, HTMLAttributes, ReactNode } from 'react'; + +import { Flex } from '../Flex'; +import { HelperText, HelperTextProps } from '../HelperText'; +import { hasErrorMessage } from '../HelperText/HelperText'; +import { Label, LabelProps } from '../Label'; +import { Wrapper } from './Field.style'; + +type Props = { + label?: ReactNode; + labelProps?: LabelProps; +}; + +type NativeAttrs = Omit, keyof Props>; + +type InheritAttrs = Omit; + +type FieldProps = Props & NativeAttrs & InheritAttrs; + +const Field = forwardRef( + ( + { label, labelProps, errorMessage, errorMessageProps, description, descriptionProps, children, ...props }, + ref + ): JSX.Element => { + const hasError = hasErrorMessage(errorMessage); + const hasHelpText = !!description || hasError; + + return ( + + {label && } + {children} + {hasHelpText && ( + + )} + + ); + } +); + +Field.displayName = 'Field'; + +const useFieldProps = ({ + label, + labelProps, + errorMessage, + errorMessageProps, + description, + descriptionProps, + className, + hidden, + style, + ...props +}: FieldProps): { fieldProps: FieldProps; elementProps: any } => { + return { + fieldProps: { + label, + labelProps, + errorMessage, + errorMessageProps, + description, + descriptionProps, + className, + hidden, + style + }, + elementProps: props + }; +}; + +export { Field, useFieldProps }; +export type { FieldProps }; diff --git a/src/component-library/Field/index.tsx b/src/component-library/Field/index.tsx new file mode 100644 index 0000000000..c875a54455 --- /dev/null +++ b/src/component-library/Field/index.tsx @@ -0,0 +1,2 @@ +export type { FieldProps } from './Field'; +export { Field, useFieldProps } from './Field'; diff --git a/src/component-library/Flex/Flex.style.tsx b/src/component-library/Flex/Flex.style.tsx index d51fe623e5..a039d16252 100644 --- a/src/component-library/Flex/Flex.style.tsx +++ b/src/component-library/Flex/Flex.style.tsx @@ -1,5 +1,6 @@ import styled from 'styled-components'; +import { marginCSS, StyledMarginProps } from '../css/margin'; import { theme } from '../theme'; import { AlignItems, AlignSelf, Direction, JustifyContent, Spacing, Wrap } from '../utils/prop-types'; @@ -11,7 +12,7 @@ type StyledFlexProps = { $flex?: string | number; $wrap?: Wrap; $alignSelf?: AlignSelf; -}; +} & StyledMarginProps; const StyledFlex = styled.div` display: flex; @@ -22,6 +23,7 @@ const StyledFlex = styled.div` flex: ${(props) => props.$flex}; flex-wrap: ${(props) => (typeof props.$wrap === 'boolean' ? 'wrap' : props.$wrap)}; align-self: ${(props) => props.$alignSelf}; + ${(props) => marginCSS(props)}; `; export { StyledFlex }; diff --git a/src/component-library/Flex/Flex.tsx b/src/component-library/Flex/Flex.tsx index ecc9da416c..827db075dc 100644 --- a/src/component-library/Flex/Flex.tsx +++ b/src/component-library/Flex/Flex.tsx @@ -1,6 +1,16 @@ -import { ElementType, forwardRef, HTMLAttributes } from 'react'; +import { forwardRef, HTMLAttributes } from 'react'; -import { AlignItems, AlignSelf, Direction, ElementTypeProp, JustifyContent, Spacing, Wrap } from '../utils/prop-types'; +import { + AlignItems, + AlignSelf, + Direction, + ElementTypeProp, + JustifyContent, + MarginProps, + Spacing, + Wrap +} from '../utils/prop-types'; +import { useStyleProps } from '../utils/use-style-props'; import { StyledFlex } from './Flex.style'; type Props = { @@ -11,33 +21,37 @@ type Props = { flex?: string | number; wrap?: Wrap | boolean; alignSelf?: AlignSelf; - elementType?: ElementType; }; type NativeAttrs = Omit, keyof Props>; -type FlexProps = Props & NativeAttrs & ElementTypeProp; +type FlexProps = Props & NativeAttrs & ElementTypeProp & MarginProps; const Flex = forwardRef( ( { children, gap, justifyContent, alignItems, direction, flex, wrap, alignSelf, elementType, ...props }, ref - ): JSX.Element => ( - - {children} - - ) + ): JSX.Element => { + const { styleProps, componentProps } = useStyleProps(props); + + return ( + + {children} + + ); + } ); Flex.displayName = 'Flex'; diff --git a/src/component-library/InfoBox/InfoBox.stories.tsx b/src/component-library/InfoBox/InfoBox.stories.tsx deleted file mode 100644 index b33a6f17cc..0000000000 --- a/src/component-library/InfoBox/InfoBox.stories.tsx +++ /dev/null @@ -1,28 +0,0 @@ -import { Meta, Story } from '@storybook/react'; - -import { InfoBox, InfoBoxProps } from '.'; - -const Template: Story = (args) => ; - -const WithoutCTA = Template.bind({}); -WithoutCTA.args = { - title: 'My vaults at risk', - text: '0' -}; - -const WithCTA = Template.bind({}); -WithCTA.args = { - title: 'My vaults at risk', - text: '0', - ctaOnClick: () => { - alert('CTA action'); - }, - ctaText: 'Claim' -}; - -export { WithCTA, WithoutCTA }; - -export default { - title: 'Components/InfoBox', - component: InfoBox -} as Meta; diff --git a/src/component-library/Input/BaseInput.tsx b/src/component-library/Input/BaseInput.tsx index eff8793294..a9715ac736 100644 --- a/src/component-library/Input/BaseInput.tsx +++ b/src/component-library/Input/BaseInput.tsx @@ -1,17 +1,17 @@ -import { forwardRef, InputHTMLAttributes, ReactNode } from 'react'; +import { forwardRef, InputHTMLAttributes, ReactNode, useEffect, useRef, useState } from 'react'; -import { HelperText, HelperTextProps } from '../HelperText'; +import { Field, useFieldProps } from '../Field'; +import { HelperTextProps } from '../HelperText'; import { hasErrorMessage } from '../HelperText/HelperText'; -import { Label, LabelProps } from '../Label'; +import { LabelProps } from '../Label'; import { Sizes } from '../utils/prop-types'; -import { Adornment, BaseInputWrapper, PaddingX, StyledBaseInput, Wrapper } from './Input.style'; +import { Adornment, StyledBaseInput } from './Input.style'; type Props = { label?: ReactNode; labelProps?: LabelProps; startAdornment?: ReactNode; endAdornment?: ReactNode; - paddingX?: PaddingX; bottomAdornment?: ReactNode; value?: string | ReadonlyArray | number; defaultValue?: string | ReadonlyArray | number; @@ -26,58 +26,41 @@ type InheritAttrs = Omit; type BaseInputProps = Props & NativeAttrs & InheritAttrs; const BaseInput = forwardRef( - ( - { - className, - style, - hidden, - startAdornment, - endAdornment, - paddingX, - bottomAdornment, - label, - labelProps, - errorMessage, - errorMessageProps, - description, - descriptionProps, - disabled, - size = 'medium', - ...props - }, - ref - ): JSX.Element => { - const hasError = hasErrorMessage(errorMessage); - const hasHelpText = !!description || hasError; + ({ startAdornment, endAdornment, bottomAdornment, disabled, size = 'medium', ...props }, ref): JSX.Element => { + const endAdornmentRef = useRef(null); + const [endAdornmentWidth, setEndAdornmentWidth] = useState(0); + + const { fieldProps, elementProps } = useFieldProps(props); + + useEffect(() => { + if (!endAdornmentRef.current || !endAdornment) return; + + setEndAdornmentWidth(endAdornmentRef.current.getBoundingClientRect().width); + }, [endAdornment]); + + const hasError = hasErrorMessage(props.errorMessage); return ( - + ); } ); diff --git a/src/component-library/Input/Input.style.tsx b/src/component-library/Input/Input.style.tsx index 6eaefbe70b..a21ef02826 100644 --- a/src/component-library/Input/Input.style.tsx +++ b/src/component-library/Input/Input.style.tsx @@ -3,14 +3,12 @@ import styled from 'styled-components'; import { theme } from '../theme'; import { Placement, Sizes } from '../utils/prop-types'; -type PaddingX = { left?: keyof typeof theme.input.paddingX; right?: keyof typeof theme.input.paddingX }; - type BaseInputProps = { $size: Sizes; - $paddingX?: PaddingX; $adornments: { bottom: boolean; left: boolean; right: boolean }; $isDisabled: boolean; $hasError: boolean; + $endAdornmentWidth: number; }; type AdornmentProps = { @@ -46,15 +44,17 @@ const StyledBaseInput = styled.input` box-shadow ${theme.transition.duration.duration150}ms ease-in-out; padding-top: ${theme.spacing.spacing2}; - padding-left: ${({ $adornments, $paddingX }) => { - if (!$adornments.left) return theme.spacing.spacing2; + padding-left: ${({ $adornments }) => ($adornments.left ? theme.input.paddingX.md : theme.spacing.spacing2)}; - return $paddingX?.left ? theme.input.paddingX[$paddingX.left] : theme.input.paddingX.md; - }}; - padding-right: ${({ $adornments, $paddingX }) => { + padding-right: ${({ $adornments, $endAdornmentWidth }) => { if (!$adornments.right) return theme.spacing.spacing2; - return $paddingX?.right ? theme.input.paddingX[$paddingX.right] : theme.input.paddingX.md; + // MEMO: adding `spacing6` is a hacky solution because + // the `endAdornmentWidth` does not update width correctly + // after fonts are loaded. Instead of falling back to a more + // complex solution, an extra offset does the job of not allowing + // the input overlap the adornment. + return `calc(${$endAdornmentWidth}px + ${theme.spacing.spacing6})`; }}; padding-bottom: ${({ $adornments }) => ($adornments.bottom ? theme.spacing.spacing6 : theme.spacing.spacing2)}; @@ -102,13 +102,13 @@ const Adornment = styled.div` right: ${({ $position }) => $position === 'right' && theme.spacing.spacing2}; transform: ${({ $position }) => ($position === 'left' || $position === 'right') && 'translateY(-50%)'}; bottom: ${({ $position }) => $position === 'bottom' && theme.spacing.spacing1}; + // to not allow adornment to take more than 50% of the input. We might want to reduce this in the future. + max-width: 50%; `; -const Wrapper = styled.div>` +const Wrapper = styled.div` display: flex; flex-direction: column; - opacity: ${({ $isDisabled }) => $isDisabled && 0.5}; `; export { Adornment, BaseInputWrapper, StyledBaseInput, Wrapper }; -export type { PaddingX }; diff --git a/src/component-library/Input/Input.tsx b/src/component-library/Input/Input.tsx index bc11ad40d4..b537fef356 100644 --- a/src/component-library/Input/Input.tsx +++ b/src/component-library/Input/Input.tsx @@ -20,9 +20,13 @@ type AriaAttrs = Omit, (keyof Props & InheritAttrs type InputProps = Props & InheritAttrs & AriaAttrs; const Input = forwardRef( - ({ onChange, ...props }, ref): JSX.Element => { + ({ onChange, validationState, ...props }, ref): JSX.Element => { const inputRef = useDOMRef(ref); - const { inputProps, descriptionProps, errorMessageProps, labelProps } = useTextField(props, inputRef); + // We are specifing `validationState` so that when there are errors, `aria-invalid` is set to `true` + const { inputProps, descriptionProps, errorMessageProps, labelProps } = useTextField( + { ...props, validationState: props.errorMessage ? 'invalid' : validationState }, + inputRef + ); return ( = (args) => ( - - - IBTC - - - KINT - - - INTR - - - KSM - - - DOT - - +
+ + + IBTC + + + KINT + + + INTR + + + KSM + + + DOT + + +
); const Default = Template.bind({}); @@ -27,7 +29,13 @@ Default.args = { selectionMode: 'single' }; -export { Default }; +const Cards = Template.bind({}); +Cards.args = { + selectionMode: 'single', + variant: 'card' +}; + +export { Cards, Default }; export default { title: 'Collections/List', diff --git a/src/component-library/List/List.style.tsx b/src/component-library/List/List.style.tsx index 6117eacd01..1e6d436bfd 100644 --- a/src/component-library/List/List.style.tsx +++ b/src/component-library/List/List.style.tsx @@ -1,10 +1,22 @@ -import styled from 'styled-components'; +import styled, { css } from 'styled-components'; +import { Flex } from '../Flex'; import { theme } from '../theme'; -import { Variants } from '../utils/prop-types'; +import { ListVariants, Variants } from '../utils/prop-types'; + +type StyledListProps = { + $variant: ListVariants; +}; + +const StyledList = styled(Flex)` + background-color: ${({ $variant }) => theme.list?.[$variant]?.bg}; + border-radius: ${({ $variant }) => theme.list[$variant].rounded}; + border: ${({ $variant }) => theme.list[$variant].border}; + overflow: hidden; +`; type StyledListItemProps = { - $variant: Variants; + $variant: Variants | 'card'; $isDisabled: boolean; $isHovered: boolean; $isInteractable: boolean; @@ -15,19 +27,31 @@ const StyledListItem = styled.li` flex: 1; align-self: stretch; padding: ${theme.spacing.spacing3}; - border-radius: ${theme.rounded.md}; + border-radius: ${({ $variant }) => theme.list.item[$variant].rounded}; background-color: ${({ $variant, $isHovered, $isFocusVisible }) => - $isHovered || $isFocusVisible ? theme.list[$variant].hover.bg : theme.list[$variant].bg}; - border: ${({ $variant }) => theme.list[$variant].border}; + $isHovered || $isFocusVisible ? theme.list.item[$variant].hover.bg : theme.list.item[$variant].bg}; + border: ${({ $variant }) => $variant !== 'card' && theme.list.item[$variant].border}; color: ${theme.colors.textPrimary}; cursor: ${({ $isInteractable }) => $isInteractable && 'pointer'}; outline: ${({ $isFocusVisible }) => !$isFocusVisible && 'none'}; - &[aria-selected='true'] { - background-color: ${theme.colors.textSecondary}; - color: ${theme.list.text}; - border-color: ${theme.colors.textSecondary}; - } + ${({ $variant }) => { + if ($variant === 'card') { + return css` + &:not(:first-of-type) { + border-top: ${theme.list.item.card.border}; + } + `; + } + + return css` + &[aria-selected='true'] { + background-color: ${theme.colors.textSecondary}; + color: ${theme.list.text}; + border-color: ${theme.colors.textSecondary}; + } + `; + }} `; -export { StyledListItem }; +export { StyledList, StyledListItem }; diff --git a/src/component-library/List/List.tsx b/src/component-library/List/List.tsx index 5901f9f6e2..34b5e81eea 100644 --- a/src/component-library/List/List.tsx +++ b/src/component-library/List/List.tsx @@ -1,18 +1,22 @@ -import { useGridList } from '@react-aria/gridlist'; +import { AriaGridListOptions, useGridList } from '@react-aria/gridlist'; import { mergeProps } from '@react-aria/utils'; import { ListProps as StatelyListProps, useListState } from '@react-stately/list'; import { forwardRef } from 'react'; -import { Flex, FlexProps } from '../Flex'; +import { FlexProps } from '../Flex'; import { useDOMRef } from '../utils/dom'; -import { Variants } from '../utils/prop-types'; +import { ListVariants } from '../utils/prop-types'; +import { StyledList } from './List.style'; import { ListItem } from './ListItem'; type Props = { - variant?: Variants; + variant?: ListVariants; }; -type InheritAttrs = Omit>, keyof Props>; +type InheritAttrs = Omit< + StatelyListProps> & AriaGridListOptions>, + keyof Props +>; type NativeAttrs = Omit; @@ -26,11 +30,18 @@ const List = forwardRef( const { gridProps } = useGridList(ariaProps, state, listRef); return ( - + {[...state.collection].map((item) => ( ))} - + ); } ); diff --git a/src/component-library/List/ListItem.tsx b/src/component-library/List/ListItem.tsx index 07f0c38bcf..d3c9540d43 100644 --- a/src/component-library/List/ListItem.tsx +++ b/src/component-library/List/ListItem.tsx @@ -8,11 +8,11 @@ import { Node } from '@react-types/shared'; import { useMemo, useRef } from 'react'; import { Flex, FlexProps } from '../Flex'; -import { Variants } from '../utils/prop-types'; +import { ListVariants } from '../utils/prop-types'; import { StyledListItem } from './List.style'; type Props = { - variant?: Variants; + variant?: ListVariants; }; type InheritAttrs = Omit; diff --git a/src/component-library/Meter/Meter.style.tsx b/src/component-library/Meter/Meter.style.tsx index 547880d241..1d23a075a5 100644 --- a/src/component-library/Meter/Meter.style.tsx +++ b/src/component-library/Meter/Meter.style.tsx @@ -28,6 +28,7 @@ const StyledWrapper = styled.div` `; const StyledMeter = styled.div` + // TODO: add overflow: hidden position: relative; height: ${theme.meter.bar.height}; background: ${({ $variant }) => theme.meter.bar[$variant].bg}; diff --git a/src/component-library/Meter/Meter.tsx b/src/component-library/Meter/Meter.tsx index cf2537bc70..a907c9ca26 100644 --- a/src/component-library/Meter/Meter.tsx +++ b/src/component-library/Meter/Meter.tsx @@ -1,7 +1,5 @@ import { HTMLAttributes, useEffect, useState } from 'react'; -import { formatPercentage } from '@/common/utils/utils'; - import { Span } from '../Text'; import { Status, Variants } from '../utils/prop-types'; import { Indicator } from './Indicator'; @@ -35,7 +33,7 @@ const Meter = ({ className, style, hidden, - formatOptions, + formatOptions = { maximumFractionDigits: 2 }, ...props }: MeterProps): JSX.Element => { const [status, setStatus] = useState(); @@ -65,7 +63,7 @@ const Meter = ({ $variant={variant} > - {!isPrimary && {formatPercentage(position, formatOptions)}} + {!isPrimary && {Intl.NumberFormat(undefined, formatOptions).format(position)}%} {!isPrimary && !!ranges && } diff --git a/src/component-library/Modal/Dialog.tsx b/src/component-library/Modal/Dialog.tsx new file mode 100644 index 0000000000..ecd782729a --- /dev/null +++ b/src/component-library/Modal/Dialog.tsx @@ -0,0 +1,46 @@ +import { AriaDialogProps, useDialog } from '@react-aria/dialog'; +import { forwardRef, ReactNode } from 'react'; + +import { XMark } from '@/assets/icons'; + +import { useDOMRef } from '../utils/dom'; +import { StyledCloseCTA, StyledDialog } from './Modal.style'; +import { ModalContext } from './ModalContext'; + +type Props = { + children: ReactNode; + align?: 'top' | 'center'; + hasMaxHeight?: boolean; + onClose: () => void; +}; + +type InheritAttrs = Omit; + +type DialogProps = Props & InheritAttrs; + +const Dialog = forwardRef( + ({ children, align = 'center', hasMaxHeight, onClose, ...props }, ref): JSX.Element | null => { + const dialogRef = useDOMRef(ref); + + // Get props for the dialog and its title + const { dialogProps, titleProps } = useDialog(props, dialogRef); + + const isCentered = align === 'center'; + + return ( + + + + + + {children} + + + ); + } +); + +Dialog.displayName = 'Dialog'; + +export { Dialog }; +export type { DialogProps }; diff --git a/src/component-library/Modal/Modal.stories.tsx b/src/component-library/Modal/Modal.stories.tsx index 66b00208ab..8022cd50d0 100644 --- a/src/component-library/Modal/Modal.stories.tsx +++ b/src/component-library/Modal/Modal.stories.tsx @@ -15,13 +15,13 @@ const Template: Story = return ( <> - + Open modal {hasTitle && ( <> - + Title @@ -30,7 +30,7 @@ const Template: Story = {children} {hasFooter && ( - Procced + Procced )} @@ -96,6 +96,7 @@ LargeContent.args = { isOpen: false, hasFooter: true, hasTitle: true, + align: 'top', children: ( <> Cras mattis consectetur purus sit amet fermentum. Cras justo odio, dapibus ac facilisis in, egestas eget quam. diff --git a/src/component-library/Modal/Modal.style.tsx b/src/component-library/Modal/Modal.style.tsx index ed41afae97..3f9867f6d5 100644 --- a/src/component-library/Modal/Modal.style.tsx +++ b/src/component-library/Modal/Modal.style.tsx @@ -1,20 +1,21 @@ import styled from 'styled-components'; -import { TransitionTrigger } from '@/utils/hooks/use-mount-transition'; - +import { overlayCSS } from '../css/overlay'; import { CTA } from '../CTA'; import { Divider } from '../Divider'; import { Flex } from '../Flex'; import { H3 } from '../Text'; import { theme } from '../theme'; -import { NormalAlignments, Overflow } from '../utils/prop-types'; +import { Overflow } from '../utils/prop-types'; -type StyledDialogWrapperProps = { - $transitionTrigger?: TransitionTrigger; +type StyledModalProps = { + $isOpen?: boolean; + $isCentered?: boolean; }; -type StyledModalHeaderProps = { - $alignment?: NormalAlignments; +type StyledDialogProps = { + $isCentered?: boolean; + $hasMaxHeight?: boolean; }; type StyledModalBodyProps = { @@ -22,53 +23,75 @@ type StyledModalBodyProps = { $noPadding?: boolean; }; -const StyledUnderlay = styled.div` +type StyledUnderlayProps = { + $isOpen: boolean; + $isCentered?: boolean; +}; + +const StyledUnderlay = styled.div` position: fixed; z-index: ${theme.modal.underlay.zIndex}; - inset: 0; - background: ${theme.modal.underlay.bg}; - display: flex; - align-items: center; - justify-content: center; + top: 0; + left: 0; + width: 100vw; height: 100vh; - overflow: hidden; - pointer-events: auto; + + background: ${theme.modal.underlay.bg}; + + ${({ $isOpen }) => overlayCSS($isOpen)} + transition: ${({ $isOpen }) => + $isOpen ? theme.modal.underlay.transition.entering : theme.modal.underlay.transition.exiting}; `; -const StyledDialogWrapper = styled.div` - width: 100vw; - height: 100vh; - justify-content: center; - align-items: center; - display: flex; +const StyledWrapper = styled.div` position: fixed; - pointer-events: none; - z-index: ${theme.modal.zIndex}; top: 0; left: 0; + z-index: ${theme.modal.zIndex}; + width: 100vw; + height: 100vh; + visibility: visible; + + display: flex; + justify-content: center; + align-items: ${({ $isCentered }) => ($isCentered ? 'center' : 'flex-start')}; + overflow: ${({ $isCentered }) => !$isCentered && 'auto'}; +`; + +const StyledModal = styled.div` + width: 100%; + max-width: ${theme.modal.maxWidth}; + max-height: ${({ $isCentered }) => $isCentered && theme.modal.maxHeight}; + margin: ${({ $isCentered }) => ($isCentered ? 0 : theme.spacing.spacing16)} ${theme.spacing.spacing6}; + + transform: ${({ $isOpen }) => ($isOpen ? 'translateY(0)' : `translateY(20px)`)}; + ${({ $isOpen }) => overlayCSS(!!$isOpen)} + // Overrides overlayCSS properties, because react-aria Overlay + // contains a FocusScope that will ignore this element if it is + // visually hidden + visibility: visible; + // Allows scroll on the modal + pointer-events: auto; + transition: ${({ $isOpen }) => ($isOpen ? theme.modal.transition.entering : theme.modal.transition.exiting)}; - transition: opacity ${theme.transition.duration.duration100}ms ease-out; - transition-property: opacity, transform; - ${({ $transitionTrigger }) => - $transitionTrigger === 'in' ? `opacity: 1; transform: translateY(0);` : `opacity: 0; transform: translateY(2em);`} + outline: none; `; -const StyledDialog = styled.section` +const StyledDialog = styled.section` background: ${theme.colors.bgPrimary}; border: ${theme.border.default}; border-radius: ${theme.rounded.md}; color: ${theme.colors.textPrimary}; - max-width: ${theme.modal.maxWidth}; width: 100%; - max-height: ${theme.modal.maxHeight}; - margin: 0 ${theme.spacing.spacing6}; - pointer-events: auto; - overflow: hidden; + max-height: ${({ $hasMaxHeight }) => $hasMaxHeight && '560px'}; + overflow: ${({ $isCentered }) => $isCentered && 'hidden'}; display: flex; flex-direction: column; position: relative; + + outline: none; `; const StyledCloseCTA = styled(CTA)` @@ -78,10 +101,7 @@ const StyledCloseCTA = styled(CTA)` z-index: ${theme.modal.closeBtn.zIndex}; `; -const StyledModalHeader = styled(H3)` - font-size: ${theme.text.xl}; - line-height: ${theme.lineHeight.base}; - text-align: ${({ $alignment }) => $alignment}; +const StyledModalHeader = styled(H3)` padding: ${theme.modal.header.paddingY} ${theme.modal.header.paddingX}; flex-shrink: 0; `; @@ -105,10 +125,11 @@ const StyledModalFooter = styled(Flex)` export { StyledCloseCTA, StyledDialog, - StyledDialogWrapper, + StyledModal, StyledModalBody, StyledModalDivider, StyledModalFooter, StyledModalHeader, - StyledUnderlay + StyledUnderlay, + StyledWrapper }; diff --git a/src/component-library/Modal/Modal.tsx b/src/component-library/Modal/Modal.tsx index 44d0725693..9bff5876a4 100644 --- a/src/component-library/Modal/Modal.tsx +++ b/src/component-library/Modal/Modal.tsx @@ -1,60 +1,56 @@ -import { AriaDialogProps, useDialog } from '@react-aria/dialog'; -import { FocusScope } from '@react-aria/focus'; -import { AriaOverlayProps, OverlayContainer, useModalOverlay } from '@react-aria/overlays'; -import { OverlayTriggerState } from '@react-stately/overlays'; -import { forwardRef, ReactNode } from 'react'; +import { forwardRef } from 'react'; -import { XMark } from '@/assets/icons'; -import { useMountTransition } from '@/utils/hooks/use-mount-transition'; - -import { theme } from '../theme'; +import { Overlay } from '../Overlay'; import { useDOMRef } from '../utils/dom'; -import { StyledCloseCTA, StyledDialog, StyledDialogWrapper, StyledUnderlay } from './Modal.style'; -import { ModalContext } from './ModalContext'; +import { Dialog, DialogProps } from './Dialog'; +import { ModalWrapper, ModalWrapperProps } from './ModalWrapper'; type Props = { - children: ReactNode; + container?: Element; + hasMaxHeight?: boolean; + align?: 'top' | 'center'; }; -type InheritAttrs = Omit; +type InheritAttrs = Omit; type ModalProps = Props & InheritAttrs; const Modal = forwardRef( - ({ children, isDismissable = true, ...props }, ref): JSX.Element | null => { - const dialogRef = useDOMRef(ref); + ( + { + children, + isDismissable = true, + align = 'center', + hasMaxHeight, + isKeyboardDismissDisabled, + shouldCloseOnBlur, + shouldCloseOnInteractOutside, + container, + ...props + }, + ref + ): JSX.Element | null => { + const domRef = useDOMRef(ref); const { isOpen, onClose } = props; - const { shouldRender, transitionTrigger } = useMountTransition(!!isOpen, theme.transition.duration.duration100); - // Handle interacting outside the dialog and pressing - // the Escape key to close the modal. - const { modalProps, underlayProps } = useModalOverlay( - { isDismissable, ...props }, - // These are the only props needed - { isOpen: !!isOpen, close: onClose } as OverlayTriggerState, - dialogRef + return ( + + + + {children} + + + ); - - // Get props for the dialog and its title - const { dialogProps, titleProps } = useDialog(props, dialogRef); - - return isOpen || shouldRender ? ( - - - - - - - - - - {children} - - - - - - ) : null; } ); diff --git a/src/component-library/Modal/ModalBody.tsx b/src/component-library/Modal/ModalBody.tsx index c21a197d51..b888725293 100644 --- a/src/component-library/Modal/ModalBody.tsx +++ b/src/component-library/Modal/ModalBody.tsx @@ -1,6 +1,7 @@ import { FlexProps } from '../Flex'; import { Overflow } from '../utils/prop-types'; import { StyledModalBody } from './Modal.style'; +import { useModalContext } from './ModalContext'; type Props = { overflow?: Overflow; @@ -11,9 +12,18 @@ type InheritAttrs = Omit; type ModalBodyProps = Props & InheritAttrs; -const ModalBody = ({ overflow = 'auto', noPadding, direction = 'column', ...props }: ModalBodyProps): JSX.Element => ( - -); +const ModalBody = ({ overflow, noPadding, direction = 'column', ...props }: ModalBodyProps): JSX.Element => { + const { bodyProps } = useModalContext(); + + return ( + + ); +}; export { ModalBody }; export type { ModalBodyProps }; diff --git a/src/component-library/Modal/ModalContext.tsx b/src/component-library/Modal/ModalContext.tsx index ec507b1087..4c890537fe 100644 --- a/src/component-library/Modal/ModalContext.tsx +++ b/src/component-library/Modal/ModalContext.tsx @@ -1,8 +1,11 @@ import { DOMAttributes } from '@react-types/shared'; import React from 'react'; +import { ModalBodyProps } from './ModalBody'; + interface ModalConfig { titleProps?: DOMAttributes; + bodyProps?: ModalBodyProps; } const defaultContext = {}; diff --git a/src/component-library/Modal/ModalHeader.tsx b/src/component-library/Modal/ModalHeader.tsx index 7f292d3c87..38545726fc 100644 --- a/src/component-library/Modal/ModalHeader.tsx +++ b/src/component-library/Modal/ModalHeader.tsx @@ -2,12 +2,10 @@ import { mergeProps } from '@react-aria/utils'; import { ElementType } from 'react'; import { TextProps } from '../Text'; -import { NormalAlignments } from '../utils/prop-types'; import { StyledModalHeader } from './Modal.style'; import { useModalContext } from './ModalContext'; type Props = { - alignment?: NormalAlignments; elementType?: ElementType; }; @@ -15,11 +13,24 @@ type InheritAttrs = Omit; type ModalHeaderProps = Props & InheritAttrs; -const ModalHeader = ({ alignment = 'center', elementType, children, ...props }: ModalHeaderProps): JSX.Element => { +const ModalHeader = ({ + align = 'center', + size = 'xl', + weight = 'semibold', + elementType, + children, + ...props +}: ModalHeaderProps): JSX.Element => { const { titleProps } = useModalContext(); return ( - + {children} ); diff --git a/src/component-library/Modal/ModalWrapper.tsx b/src/component-library/Modal/ModalWrapper.tsx new file mode 100644 index 0000000000..3bb7dab4c9 --- /dev/null +++ b/src/component-library/Modal/ModalWrapper.tsx @@ -0,0 +1,67 @@ +import { AriaModalOverlayProps, AriaOverlayProps, useModalOverlay } from '@react-aria/overlays'; +import { mergeProps } from '@react-aria/utils'; +import { OverlayTriggerState } from '@react-stately/overlays'; +import { forwardRef, ReactNode, RefObject } from 'react'; + +import { StyledModal, StyledUnderlay, StyledWrapper } from './Modal.style'; + +type Props = { + children: ReactNode; + align?: 'top' | 'center'; + isOpen?: boolean; + onClose: () => void; +}; + +type InheritAttrs = Omit; + +type ModalWrapperProps = Props & InheritAttrs; + +const ModalWrapper = forwardRef( + ( + { + children, + isDismissable = true, + align = 'center', + onClose, + isKeyboardDismissDisabled, + isOpen, + shouldCloseOnInteractOutside, + shouldCloseOnBlur, + ...props + }, + ref + ): JSX.Element | null => { + // Handle interacting outside the dialog and pressing + // the Escape key to close the modal. + const { modalProps, underlayProps } = useModalOverlay( + { + isDismissable, + isKeyboardDismissDisabled, + shouldCloseOnInteractOutside, + shouldCloseOnBlur, + ...props + } as AriaOverlayProps, + // These are the only props needed + { isOpen: !!isOpen, close: onClose } as OverlayTriggerState, + ref as RefObject + ); + + const isCentered = align === 'center'; + + return ( + <> + + + + {children} + + + + ); + } +); + +ModalWrapper.displayName = 'ModalWrapper'; + +export { ModalWrapper }; +export type { ModalWrapperProps }; diff --git a/src/component-library/NumberInput/NumberInput.tsx b/src/component-library/NumberInput/NumberInput.tsx index d62b4509b5..a449c558cb 100644 --- a/src/component-library/NumberInput/NumberInput.tsx +++ b/src/component-library/NumberInput/NumberInput.tsx @@ -1,69 +1,75 @@ -import { useNumberField } from '@react-aria/numberfield'; +import { AriaTextFieldOptions, useTextField } from '@react-aria/textfield'; import { mergeProps } from '@react-aria/utils'; -import type { NumberFieldStateProps } from '@react-stately/numberfield'; -import { useNumberFieldState } from '@react-stately/numberfield'; -import { ChangeEventHandler, forwardRef, useEffect } from 'react'; +import { ChangeEventHandler, forwardRef, useEffect, useState } from 'react'; import { useDOMRef } from '@/component-library/utils/dom'; import { BaseInput, BaseInputProps } from '../Input'; -// Prevents the user from changing the input value using mouse wheel -const handleWheel = (event: WheelEvent) => event.preventDefault(); - -const defaultFormatOptions: Intl.NumberFormatOptions = { - style: 'decimal', - maximumFractionDigits: 20, - useGrouping: false +const escapeRegExp = (string: string): string => { + // $& means the whole matched string + return string.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'); }; -// Static locale for react-stately -// TODO: To be replaced when we manage our locales -const locale = 'en-US'; +// match escaped "." characters via in a non-capturing group +const inputRegex = RegExp(`^\\d*(?:\\\\[.])?\\d*$`); type Props = { - value?: number; - defaultValue?: number; + value?: string | number; + defaultValue?: string | number; }; -type InheritAttrs = Omit; +type InheritAttrs = Omit< + BaseInputProps, + keyof Props | 'errorMessageProps' | 'descriptionProps' | 'disabled' | 'required' | 'readOnly' +>; -type AriaAttrs = Omit; +type AriaAttrs = Omit, (keyof Props & InheritAttrs) | 'onChange'>; type NumberInputProps = Props & InheritAttrs & AriaAttrs; const NumberInput = forwardRef( - ({ onChange, formatOptions, ...props }, ref): JSX.Element => { + ({ onChange, validationState, value: valueProp, defaultValue = '', ...props }, ref): JSX.Element => { + const [value, setValue] = useState(defaultValue?.toString()); const inputRef = useDOMRef(ref); - const state = useNumberFieldState({ - ...props, - formatOptions: formatOptions || defaultFormatOptions, - locale - }); - const { inputProps, descriptionProps, errorMessageProps, labelProps } = useNumberField(props, state, inputRef); - - useEffect(() => { - const input = inputRef.current; - - input?.addEventListener('wheel', handleWheel, { passive: false }); - - return () => input?.removeEventListener('wheel', handleWheel); - }, [inputRef]); - - // Only emit event when value is valid const handleChange: ChangeEventHandler = (e) => { - if ((e.target as HTMLInputElement).value.match(/^[0-9]*[.]?[0-9]*$/)) { + const value = e.target.value; + + if (inputRegex.test(escapeRegExp(value))) { onChange?.(e); + setValue(value); } }; + const { inputProps, descriptionProps, errorMessageProps, labelProps } = useTextField( + { + ...props, + validationState: props.errorMessage ? 'invalid' : validationState, + value: value, + pattern: '^[0-9]*[.,]?[0-9]*$', + inputMode: 'decimal', + autoComplete: 'off', + minLength: 1, + maxLength: 79 + }, + inputRef + ); + + useEffect(() => { + if (valueProp === undefined) return; + + setValue(valueProp.toString()); + }, [valueProp]); + return ( ); diff --git a/src/component-library/Overlay/OpenTransition.tsx b/src/component-library/Overlay/OpenTransition.tsx new file mode 100644 index 0000000000..78430d201d --- /dev/null +++ b/src/component-library/Overlay/OpenTransition.tsx @@ -0,0 +1,37 @@ +import { Children, cloneElement, ReactNode } from 'react'; +import { Transition, TransitionStatus } from 'react-transition-group'; +import { TransitionProps } from 'react-transition-group/Transition'; + +const OPEN_STATES: Partial> = { + entered: true, + entering: false +}; + +type Props = { children: ReactNode }; + +type InheritAttrs = Omit, keyof Props>; + +type OpenTransitionProps = Props & InheritAttrs; + +/** + * `enter` is set to 0 ms to let our css animation play + * `exit` is set to 350 ms to let + */ + +const OpenTransition = (props: OpenTransitionProps): any => { + // Do not apply any transition if in chromatic (based on react-spectrum) + if (process.env.CHROMATIC) { + return Children.map(props.children, (child) => child && cloneElement(child as any, { isOpen: props.in })); + } + + return ( + + {(state) => + Children.map(props.children, (child) => child && cloneElement(child as any, { isOpen: !!OPEN_STATES[state] })) + } + + ); +}; + +export { OpenTransition }; +export type { OpenTransitionProps }; diff --git a/src/component-library/Overlay/Overlay.style.tsx b/src/component-library/Overlay/Overlay.style.tsx new file mode 100644 index 0000000000..770f816067 --- /dev/null +++ b/src/component-library/Overlay/Overlay.style.tsx @@ -0,0 +1,8 @@ +import styled from 'styled-components'; + +const StyledOverlayWrapper = styled.div` + isolation: isolate; + background: transparent; +`; + +export { StyledOverlayWrapper }; diff --git a/src/component-library/Overlay/Overlay.tsx b/src/component-library/Overlay/Overlay.tsx new file mode 100644 index 0000000000..45cafff805 --- /dev/null +++ b/src/component-library/Overlay/Overlay.tsx @@ -0,0 +1,76 @@ +import { Overlay as AriaOverlay } from '@react-aria/overlays'; +import { ReactNode, useCallback, useState } from 'react'; + +import { OpenTransition } from './OpenTransition'; +import { StyledOverlayWrapper } from './Overlay.style'; + +type OverlayProps = { + children: ReactNode; + isOpen?: boolean; + container?: Element; + onEnter?: () => void; + onEntering?: () => void; + onEntered?: () => void; + onExit?: () => void; + onExiting?: () => void; + onExited?: () => void; +}; + +const Overlay = ({ + children, + isOpen, + container, + onEnter, + onEntering, + onEntered, + onExit, + onExiting, + onExited +}: OverlayProps): JSX.Element | null => { + const [exited, setExited] = useState(!isOpen); + + const handleEntered = useCallback(() => { + setExited(false); + + if (onEntered) { + onEntered(); + } + }, [onEntered]); + + const handleExited = useCallback(() => { + setExited(true); + + if (onExited) { + onExited(); + } + }, [onExited]); + + // Don't un-render the overlay while it's transitioning out. + const mountOverlay = isOpen || !exited; + if (!mountOverlay) { + // Don't bother showing anything if we don't have to. + return null; + } + + return ( + + + + {children} + + + + ); +}; + +export { Overlay }; +export type { OverlayProps }; diff --git a/src/component-library/Overlay/index.tsx b/src/component-library/Overlay/index.tsx new file mode 100644 index 0000000000..9c047f467d --- /dev/null +++ b/src/component-library/Overlay/index.tsx @@ -0,0 +1,2 @@ +export type { OverlayProps } from './Overlay'; +export { Overlay } from './Overlay'; diff --git a/src/component-library/Select/Select.stories.tsx b/src/component-library/Select/Select.stories.tsx new file mode 100644 index 0000000000..9651ad243f --- /dev/null +++ b/src/component-library/Select/Select.stories.tsx @@ -0,0 +1,24 @@ +import { Meta, Story } from '@storybook/react'; + +import { CoinIcon } from '../CoinIcon'; +import { Flex } from '../Flex'; +import { SelectTrigger, SelectTriggerProps } from './SelectTrigger'; + +const Template: Story = (args) => ; +const Trigger = Template.bind({}); +Trigger.args = { + size: 'large', + children: ( + + + BTC + + ) +}; + +export { Trigger }; + +export default { + title: 'Forms/Select', + component: Trigger +} as Meta; diff --git a/src/component-library/Select/Select.style.tsx b/src/component-library/Select/Select.style.tsx new file mode 100644 index 0000000000..69c775ca8f --- /dev/null +++ b/src/component-library/Select/Select.style.tsx @@ -0,0 +1,76 @@ +import styled from 'styled-components'; + +import { Span } from '../Text'; +import { theme } from '../theme'; +import { Sizes } from '../utils/prop-types'; + +type StyledTriggerProps = { + $isOpen?: boolean; + $isFocusVisible?: boolean; + $size: Sizes; + $isDisabled?: boolean; + $hasError?: boolean; +}; + +type StyledTriggerValueProps = { + $isDisabled?: boolean; + $isSelected?: boolean; +}; + +const StyledTrigger = styled.button` + outline: none; + font: inherit; + letter-spacing: inherit; + background: none; + appearance: none; + + display: inline-flex; + align-items: center; + justify-content: space-between; + width: 100%; + text-align: left; + // TODO: figure out this z-index when select is fully added + z-index: 1000; + + font-size: ${({ $size }) => theme.select.size[$size].text}; + line-height: ${theme.lineHeight.base}; + + // Inherited from Input styled + color: ${(props) => (props.disabled ? theme.input.disabled.color : theme.input.color)}; + border: ${(props) => + props.$isDisabled + ? theme.input.disabled.border + : props.$hasError + ? theme.input.error.border + : theme.border.default}; + background-color: ${theme.input.background}; + + max-height: calc(${theme.spacing.spacing16} - 1px); + + overflow: hidden; + + border-radius: ${theme.rounded.md}; + transition: border-color ${theme.transition.duration.duration150}ms ease-in-out, + box-shadow ${theme.transition.duration.duration150}ms ease-in-out; + padding: ${({ $size }) => theme.select.size[$size].padding}; + + cursor: pointer; + + &:hover:not(:disabled):not(:focus) { + border: ${(props) => !props.$isDisabled && !props.$hasError && theme.input.hover.border}; + } + + &:focus { + border: ${(props) => !props.$isDisabled && theme.input.focus.border}; + box-shadow: ${(props) => !props.$isDisabled && theme.input.focus.boxShadow}; + } +`; + +const StyledTriggerValue = styled(Span)` + display: inline-flex; + align-items: center; + color: ${({ $isDisabled, $isSelected }) => + $isDisabled ? theme.input.disabled.color : $isSelected ? theme.select.color : theme.select.placeholder}; +`; + +export { StyledTrigger, StyledTriggerValue }; diff --git a/src/component-library/Select/SelectTrigger.tsx b/src/component-library/Select/SelectTrigger.tsx new file mode 100644 index 0000000000..2c0583f4c8 --- /dev/null +++ b/src/component-library/Select/SelectTrigger.tsx @@ -0,0 +1,63 @@ +import { useButton } from '@react-aria/button'; +import { useFocusRing } from '@react-aria/focus'; +import { mergeProps } from '@react-aria/utils'; +import { PressEvent } from '@react-types/shared'; +import { ButtonHTMLAttributes, forwardRef, ReactNode } from 'react'; + +import { ChevronDown } from '@/assets/icons'; + +import { TextProps } from '../Text'; +import { useDOMRef } from '../utils/dom'; +import { Sizes } from '../utils/prop-types'; +import { StyledTrigger, StyledTriggerValue } from './Select.style'; + +type Props = { + size?: Sizes; + isOpen?: boolean; + hasError?: boolean; + placeholder?: ReactNode; + valueProps?: TextProps; + onPress?: (e: PressEvent) => void; +}; + +type NativeAttrs = Omit, keyof Props>; + +type SelectTriggerProps = Props & NativeAttrs; + +// MEMO: this is prune to change when `Select` is added +const SelectTrigger = forwardRef( + ( + { size = 'medium', hasError, isOpen, children, valueProps, placeholder = 'Select an option', ...props }, + ref + ): JSX.Element => { + const { disabled } = props; + + const buttonRef = useDOMRef(ref); + + const { buttonProps } = useButton(props, buttonRef); + + const { focusProps, isFocusVisible } = useFocusRing(); + + return ( + + + {children || placeholder} + + + + ); + } +); + +SelectTrigger.displayName = 'SelectTrigger'; + +export { SelectTrigger }; +export type { SelectTriggerProps }; diff --git a/src/component-library/Select/index.tsx b/src/component-library/Select/index.tsx new file mode 100644 index 0000000000..c27c1e8969 --- /dev/null +++ b/src/component-library/Select/index.tsx @@ -0,0 +1,2 @@ +export type { SelectTriggerProps } from './SelectTrigger'; +export { SelectTrigger } from './SelectTrigger'; diff --git a/src/component-library/Switch/Switch.tsx b/src/component-library/Switch/Switch.tsx index e407011a51..b478e0ef52 100644 --- a/src/component-library/Switch/Switch.tsx +++ b/src/component-library/Switch/Switch.tsx @@ -4,13 +4,13 @@ import { AriaSwitchProps, useSwitch } from '@react-aria/switch'; import { mergeProps } from '@react-aria/utils'; import { useToggleState } from '@react-stately/toggle'; import { PressEvent } from '@react-types/shared'; -import React, { forwardRef, HTMLAttributes, useRef } from 'react'; +import { ChangeEvent, forwardRef, HTMLAttributes, useRef } from 'react'; import { useDOMRef } from '../utils/dom'; import { StyledInput, StyledLabel, StyledSwitch, StyledWrapper } from './Switch.style'; type Props = { - onChange?: (e: React.ChangeEvent) => void; + onChange?: (e: ChangeEvent) => void; onPress?: (e: PressEvent) => void; }; @@ -20,13 +20,16 @@ type InheritAttrs = Omit; type SwitchProps = Props & NativeAttrs & InheritAttrs; +// TODO: add size const Switch = forwardRef( ({ children, onChange, className, style, hidden, ...props }, ref): JSX.Element => { const labelRef = useDOMRef(ref); const inputRef = useRef(null); - const state = useToggleState(props); - const { inputProps } = useSwitch(props, state, inputRef); + const ariaProps: AriaSwitchProps = { children, ...props }; + + const state = useToggleState(ariaProps); + const { inputProps } = useSwitch(ariaProps, state, inputRef); const { focusProps, isFocusVisible } = useFocusRing({ autoFocus: inputProps.autoFocus diff --git a/src/component-library/Table/Table.stories.tsx b/src/component-library/Table/Table.stories.tsx index f24086cafb..5aff3651b7 100644 --- a/src/component-library/Table/Table.stories.tsx +++ b/src/component-library/Table/Table.stories.tsx @@ -21,10 +21,7 @@ const Template: Story = (args) => { const Default = Template.bind({}); Default.args = {}; -const RowAction = Template.bind({}); -RowAction.args = { onRowAction: (key) => console.log(key) }; - -export { Default, RowAction }; +export { Default }; export default { title: 'Components/Table', diff --git a/src/component-library/TextLink/TextLink.tsx b/src/component-library/TextLink/TextLink.tsx index a31f6b54dd..87c8241811 100644 --- a/src/component-library/TextLink/TextLink.tsx +++ b/src/component-library/TextLink/TextLink.tsx @@ -13,6 +13,7 @@ type NativeAttrs = Omit; type TextLinkProps = Props & NativeAttrs; +// TODO: merge this with CTALink const TextLink = forwardRef( ({ color = 'primary', external, to, ...props }, ref): JSX.Element => { const linkProps: TextLinkProps = external diff --git a/src/component-library/TokenInput/TokenInput.stories.tsx b/src/component-library/TokenInput/TokenInput.stories.tsx index 69ae80edca..dc04d91dd5 100644 --- a/src/component-library/TokenInput/TokenInput.stories.tsx +++ b/src/component-library/TokenInput/TokenInput.stories.tsx @@ -4,24 +4,22 @@ import { useState } from 'react'; import { TokenInput, TokenInputProps } from '.'; const Template: Story = (args) => { - const [value, setValue] = useState(); + const [value, setValue] = useState(); return ( setValue(Number(e.target.value || 0))} - aria-label='token field' - valueUSD={(value || 0) * 10} + onChange={(e) => setValue(e.target.value)} + valueUSD={isNaN(value as any) ? 0 : Number(value) * 10} /> ); }; const WithBalance = Template.bind({}); WithBalance.args = { - ticker: 'KSM', + defaultTicker: 'KSM', balance: 1000.0, - decimals: 8, balanceLabel: 'Balance', placeholder: '0.00', label: 'Amount', @@ -30,7 +28,7 @@ WithBalance.args = { const WithoutBalance = Template.bind({}); WithoutBalance.args = { - ticker: 'KSM', + defaultTicker: 'KSM', label: 'Amount', placeholder: '0.00', isDisabled: false @@ -40,11 +38,9 @@ const WithCurrencySelect = Template.bind({}); WithCurrencySelect.args = { balance: 1000.0, isDisabled: false, - decimals: 8, balanceLabel: 'Balance', placeholder: '0.00', label: 'From', - selectProps: {}, tokens: [ { balance: 200, ticker: 'KSM', balanceUSD: '$200' }, { balance: 200, ticker: 'BTC', balanceUSD: '$200' }, @@ -56,7 +52,26 @@ WithCurrencySelect.args = { ] }; -export { WithBalance, WithCurrencySelect, WithoutBalance }; +const MultiToken = Template.bind({}); +MultiToken.args = { + ticker: { text: 'LP Token', icons: ['KSM', 'KBTC', 'KINT', 'USDT'] }, + balance: 1000.0, + balanceLabel: 'Balance', + placeholder: '0.00', + label: 'Amount', + isDisabled: false, + tokens: [ + { balance: 200, ticker: 'KSM', balanceUSD: '$200' }, + { balance: 200, ticker: 'BTC', balanceUSD: '$200' }, + { balance: 200, ticker: 'IBTC', balanceUSD: '$200' }, + { balance: 200, ticker: 'KBTC', balanceUSD: '$200' }, + { balance: 200, ticker: 'DOT', balanceUSD: '$200' }, + { balance: 200, ticker: 'INTR', balanceUSD: '$200' }, + { balance: 200, ticker: { text: 'LP Token', icons: ['KSM', 'KBTC', 'KINT', 'USDT'] }, balanceUSD: '$200' } + ] +}; + +export { MultiToken, WithBalance, WithCurrencySelect, WithoutBalance }; export default { title: 'Forms/TokenInput', diff --git a/src/component-library/TokenInput/TokenInput.style.tsx b/src/component-library/TokenInput/TokenInput.style.tsx index 895ef2e169..a7896ce06c 100644 --- a/src/component-library/TokenInput/TokenInput.style.tsx +++ b/src/component-library/TokenInput/TokenInput.style.tsx @@ -2,7 +2,6 @@ import styled from 'styled-components'; import { ChevronDown } from '@/assets/icons'; -import { CoinIcon } from '../CoinIcon'; import { Flex } from '../Flex'; import { List } from '../List'; import { Span } from '../Text'; @@ -10,12 +9,10 @@ import { theme } from '../theme'; type StyledClickableProps = { $isClickable: boolean; - $hasToken: boolean; }; -type StyledTokenInputBalanceValueProps = { +type StyledUSDAdornmentProps = { $isDisabled?: boolean; - $isFocusVisible: boolean; }; type StyledListItemSelectedLabelProps = { @@ -25,14 +22,15 @@ type StyledListItemSelectedLabelProps = { const StyledTicker = styled.span` font-size: ${theme.text.s}; color: ${theme.colors.textPrimary}; - flex: 0 1 auto; + overflow: hidden; + text-overflow: ellipsis; `; -const StyledUSDAdornment = styled.span` +const StyledUSDAdornment = styled.span` display: block; font-size: ${theme.text.xs}; line-height: ${theme.lineHeight.s}; - color: ${theme.colors.textTertiary}; + color: ${({ $isDisabled }) => ($isDisabled ? theme.input.disabled.color : theme.colors.textTertiary)}; white-space: nowrap; align-self: flex-start; `; @@ -44,20 +42,12 @@ const StyledTokenSelect = styled(Flex)` padding: ${theme.spacing.spacing3}; cursor: ${({ $isClickable }) => $isClickable && 'pointer'}; height: 3rem; - width: ${({ $hasToken, $isClickable }) => { - if (!$hasToken) return '8.5rem'; - - return $isClickable ? '7rem' : '5.25rem'; - }}; + width: auto; + overflow: hidden; `; const StyledChevronDown = styled(ChevronDown)` margin-left: ${theme.spacing.spacing1}; - flex: 1 0 auto; -`; - -const StyledCoinIcon = styled(CoinIcon)` - flex: 1 0 auto; `; const StyledTokenInputBalanceWrapper = styled.dl` @@ -77,16 +67,16 @@ const StyledTokenInputBalanceLabel = styled.dt` } `; -const StyledTokenInputBalanceValue = styled.span` +const StyledTokenInputBalanceValue = styled.span` display: block; color: ${theme.colors.textSecondary}; - cursor: ${({ $isDisabled }) => !$isDisabled && 'pointer'}; - outline: ${({ $isFocusVisible }) => !$isFocusVisible && 'none'}; `; const StyledListItemLabel = styled(Span)` color: ${({ $isSelected }) => $isSelected ? theme.tokenInput.list.item.selected.text : theme.tokenInput.list.item.default.text}; + text-overflow: ellipsis; + overflow: hidden; `; const StyledList = styled(List)` @@ -98,12 +88,16 @@ const StyledListHeader = styled(Flex)` padding: ${theme.modal.body.paddingY} ${theme.modal.body.paddingX}; `; +const StyledListTokenWrapper = styled(Flex)` + overflow: hidden; +`; + export { StyledChevronDown, - StyledCoinIcon, StyledList, StyledListHeader, StyledListItemLabel, + StyledListTokenWrapper, StyledTicker, StyledTokenInputBalanceLabel, StyledTokenInputBalanceValue, diff --git a/src/component-library/TokenInput/TokenInput.tsx b/src/component-library/TokenInput/TokenInput.tsx index 95af8968a0..1f996f87fa 100644 --- a/src/component-library/TokenInput/TokenInput.tsx +++ b/src/component-library/TokenInput/TokenInput.tsx @@ -2,36 +2,32 @@ import { useLabel } from '@react-aria/label'; import { mergeProps } from '@react-aria/utils'; import { forwardRef, InputHTMLAttributes, ReactNode, useEffect, useState } from 'react'; -import { formatUSD } from '@/common/utils/utils'; - import { Flex } from '../Flex'; import { NumberInput, NumberInputProps } from '../NumberInput'; import { useDOMRef } from '../utils/dom'; +import { formatUSD } from '../utils/format'; import { triggerChangeEvent } from '../utils/input'; import { StyledUSDAdornment } from './TokenInput.style'; import { TokenInputLabel } from './TokenInputLabel'; import { TokenData } from './TokenList'; import { TokenSelect } from './TokenSelect'; -const getFormatOptions = (decimals?: number): Intl.NumberFormatOptions | undefined => { - if (!decimals) return; +type SingleToken = string; - return { - style: 'decimal', - maximumFractionDigits: decimals || 20, - useGrouping: false - }; -}; +type MultiToken = { text: string; icons: string[] }; + +type TokenTicker = SingleToken | MultiToken; type Props = { - decimals?: number; - valueUSD: number; - balance?: number; + valueUSD?: number; + balance?: string | number; + humanBalance?: string | number; balanceLabel?: ReactNode; - balanceDecimals?: number; - ticker?: string; + ticker?: TokenTicker; + defaultTicker?: TokenTicker; tokens?: TokenData[]; - onClickBalance?: (balance?: number) => void; + onClickBalance?: (balance?: string | number) => void; + onChangeTicker?: (ticker?: string) => void; selectProps?: InputHTMLAttributes; }; @@ -42,79 +38,102 @@ type TokenInputProps = Props & InheritAttrs; const TokenInput = forwardRef( ( { - decimals, valueUSD, balance, + humanBalance, balanceLabel, - balanceDecimals, isDisabled, label, ticker: tickerProp, + defaultTicker = '', tokens = [], style, hidden, className, onClickBalance, + onChangeTicker, selectProps, + placeholder = '0', ...props }, ref ): JSX.Element => { const inputRef = useDOMRef(ref); - const [ticker, setTicker] = useState((selectProps?.value as string) || tickerProp); + const [tickerValue, setTickerValue] = useState( + (selectProps?.defaultValue as string) || (typeof defaultTicker === 'string' ? defaultTicker : defaultTicker?.text) + ); - const { labelProps, fieldProps } = useLabel({ label }); + const { labelProps, fieldProps } = useLabel({ label, ...props }); useEffect(() => { - if (!tickerProp) return; + if (tickerProp === undefined) return; - setTicker(tickerProp); + setTickerValue(typeof tickerProp === 'string' ? tickerProp : tickerProp?.text); }, [tickerProp]); + useEffect(() => { + if (selectProps?.value === undefined) return; + + setTickerValue(selectProps.value as string); + }, [selectProps?.value]); + const handleClickBalance = () => { + if (!balance) return; + triggerChangeEvent(inputRef, balance); onClickBalance?.(balance); }; - const handleTokenChange = (ticker: string) => setTicker(ticker); + const handleTokenChange = (ticker: string) => { + onChangeTicker?.(ticker); + setTickerValue(ticker); + }; + + const customIcons = typeof tickerProp === 'object' ? tickerProp.icons : undefined; - const isSelectDisabled = !selectProps || !tokens?.length; + const isSelectDisabled = !tokens?.length; const endAdornment = ( ); - const formatOptions = getFormatOptions(decimals); + const hasLabel = !!label || balance !== undefined; return ( @@ -125,4 +144,4 @@ const TokenInput = forwardRef( TokenInput.displayName = 'TokenInput'; export { TokenInput }; -export type { TokenInputProps }; +export type { TokenInputProps, TokenTicker }; diff --git a/src/component-library/TokenInput/TokenInputBalance.tsx b/src/component-library/TokenInput/TokenInputBalance.tsx index e2718b0fdf..187527971e 100644 --- a/src/component-library/TokenInput/TokenInputBalance.tsx +++ b/src/component-library/TokenInput/TokenInputBalance.tsx @@ -1,10 +1,6 @@ -import { useFocusRing } from '@react-aria/focus'; -import { usePress } from '@react-aria/interactions'; -import { mergeProps } from '@react-aria/utils'; import { ReactNode } from 'react'; -import { formatNumber } from '@/common/utils/utils'; - +import { CTA } from '../CTA'; import { StyledTokenInputBalanceLabel, StyledTokenInputBalanceValue, @@ -13,12 +9,11 @@ import { type TokenInputBalanceProps = { ticker?: string; - value: number; + value: string | number; onClickBalance?: () => void; isDisabled?: boolean; className?: string; label?: ReactNode; - decimals?: number; }; const TokenInputBalance = ({ @@ -26,33 +21,20 @@ const TokenInputBalance = ({ value, onClickBalance, className, - isDisabled, - decimals, + isDisabled: isDisabledProp, label = 'Balance' }: TokenInputBalanceProps): JSX.Element => { - const { pressProps } = usePress({ onPress: onClickBalance, isDisabled: isDisabled }); - const { focusProps, isFocusVisible } = useFocusRing(); - const balanceValueProps = isDisabled - ? {} - : { - role: 'button', - tabIndex: 0, - 'aria-label': 'apply balance', - ...mergeProps(pressProps, focusProps) - }; - - const balanceLabel = ticker - ? formatNumber(value, { minimumFractionDigits: 0, maximumFractionDigits: decimals || 20 }) - : 0; + const isDisabled = isDisabledProp || !ticker || value === 0; return ( {label}
- - {balanceLabel} - + {ticker ? value : 0}
+ + MAX +
); }; diff --git a/src/component-library/TokenInput/TokenInputLabel.tsx b/src/component-library/TokenInput/TokenInputLabel.tsx index 2c6f6dceb5..7eb390561f 100644 --- a/src/component-library/TokenInput/TokenInputLabel.tsx +++ b/src/component-library/TokenInput/TokenInputLabel.tsx @@ -6,9 +6,8 @@ import { TokenInputBalance } from './TokenInputBalance'; type Props = { ticker?: string; - balance?: number; + balance?: string | number; balanceLabel?: ReactNode; - balanceDecimals?: number; isDisabled?: boolean; onClickBalance?: (balance?: number) => void; }; @@ -22,25 +21,27 @@ const TokenInputLabel = ({ balanceLabel, isDisabled, onClickBalance, - balanceDecimals, ticker, children, ...props -}: TokenInputLabelProps): JSX.Element => ( - - - {balance !== undefined && ( - - )} - -); +}: TokenInputLabelProps): JSX.Element => { + const hasLabel = !!children; + + return ( + + {hasLabel && } + {balance !== undefined && ( + + )} + + ); +}; export { TokenInputLabel }; export type { TokenInputLabelProps }; diff --git a/src/component-library/TokenInput/TokenList.tsx b/src/component-library/TokenInput/TokenList.tsx index 8a36999e86..8a3cc4b6c1 100644 --- a/src/component-library/TokenInput/TokenList.tsx +++ b/src/component-library/TokenInput/TokenList.tsx @@ -2,11 +2,13 @@ import { CoinIcon } from '../CoinIcon'; import { Flex } from '../Flex'; import { ListItem, ListProps } from '../List'; import { Span } from '../Text'; -import { StyledList, StyledListItemLabel } from './TokenInput.style'; +import { TokenStack } from '../TokenStack'; +import { TokenTicker } from './TokenInput'; +import { StyledList, StyledListItemLabel, StyledListTokenWrapper } from './TokenInput.style'; type TokenData = { - ticker: string; - balance: number; + ticker: TokenTicker; + balance: string | number; balanceUSD: string; }; @@ -31,28 +33,35 @@ const TokenList = ({ items, selectedTicker, onSelectionChange, ...props }: Token return ( {items.map((item) => { - const isSelected = selectedTicker === item.ticker; + const tickerText = typeof item.ticker === 'string' ? item.ticker : item.ticker.text; + + const isSelected = selectedTicker === tickerText; return ( - - - {item.ticker} - - + + {typeof item.ticker === 'string' ? ( + + ) : ( + + )} + {tickerText} + + {item.balance} {item.balanceUSD} diff --git a/src/component-library/TokenInput/TokenListModal.tsx b/src/component-library/TokenInput/TokenListModal.tsx index dc13db28c6..1a1d54ecc8 100644 --- a/src/component-library/TokenInput/TokenListModal.tsx +++ b/src/component-library/TokenInput/TokenListModal.tsx @@ -14,7 +14,7 @@ type InheritAttrs = Omit; type TokenListModalProps = Props & InheritAttrs; const TokenListModal = ({ selectedTicker, tokens, onSelectionChange, ...props }: TokenListModalProps): JSX.Element => ( - + Select Token diff --git a/src/component-library/TokenInput/TokenSelect.tsx b/src/component-library/TokenInput/TokenSelect.tsx index 3f7cdb8656..0096690a54 100644 --- a/src/component-library/TokenInput/TokenSelect.tsx +++ b/src/component-library/TokenInput/TokenSelect.tsx @@ -1,30 +1,58 @@ import { useButton } from '@react-aria/button'; +import { useField } from '@react-aria/label'; import { chain, mergeProps } from '@react-aria/utils'; import { VisuallyHidden } from '@react-aria/visually-hidden'; -import { ChangeEventHandler, InputHTMLAttributes, useRef, useState } from 'react'; +import { InputHTMLAttributes, ReactNode, useRef, useState } from 'react'; -import { assignFormRef, triggerChangeEvent } from '../utils/input'; -import { StyledChevronDown, StyledCoinIcon, StyledTicker, StyledTokenSelect } from './TokenInput.style'; +import { CoinIcon } from '../CoinIcon'; +import { TokenStack } from '../TokenStack'; +import { useDOMRef } from '../utils/dom'; +import { StyledChevronDown, StyledTicker, StyledTokenSelect } from './TokenInput.style'; import { TokenData } from './TokenList'; import { TokenListModal } from './TokenListModal'; -type SelectProps = InputHTMLAttributes & { ref?: any }; +const Icon = ({ value, icons }: Pick) => { + if (!value) return null; -type TokenSelectProps = { - ticker?: string; + if (icons?.length) { + return 2 ? 'lg' : 'md'} tickers={icons} />; + } + + return ; +}; + +type SelectProps = InputHTMLAttributes & { ref?: any; onSelectionChange?: (ticker: string) => void }; + +type Props = { + label?: ReactNode; + value?: string; + icons?: string[]; isDisabled: boolean; tokens: TokenData[]; onChange: (ticker: string) => void; selectProps?: SelectProps; }; -const TokenSelect = ({ ticker, tokens, isDisabled, onChange, selectProps }: TokenSelectProps): JSX.Element => { +type NativeAttrs = Omit, keyof Props>; + +type TokenSelectProps = Props & NativeAttrs; + +const TokenSelect = ({ + value, + icons, + tokens, + isDisabled, + onChange, + label: labelProp, + 'aria-label': ariaLabel, + selectProps: selectPropsProp +}: TokenSelectProps): JSX.Element => { const [isOpen, setOpen] = useState(false); const tokenButtonRef = useRef(null); - const { ref: selectRef, ...inputProps } = selectProps || {}; - const inputRef = useRef(null); + const { ref, onSelectionChange, ...selectProps } = selectPropsProp || {}; + const inputRef = useDOMRef(ref); const { buttonProps } = useButton( { @@ -35,46 +63,47 @@ const TokenSelect = ({ ticker, tokens, isDisabled, onChange, selectProps }: Toke tokenButtonRef ); - const handleClose = () => setOpen(false); + const label = labelProp || ariaLabel; - const handleSelectionChange = (ticker: string) => triggerChangeEvent(inputRef, ticker); + const { labelProps, fieldProps } = useField({ label }); - const handleChange: ChangeEventHandler = (e) => onChange(e.target.value); + const handleClose = () => setOpen(false); + + const isSelect = !isDisabled; return ( <> + {isSelect && ( + + + + + )} - {ticker && } - {ticker || 'Select Token'} - {!isDisabled && ( - <> - - - - - - )} + + {value || 'Select Token'} + {isSelect && } - {!isDisabled && ( + {isSelect && ( )} diff --git a/src/component-library/TokenInput/index.tsx b/src/component-library/TokenInput/index.tsx index eb26e1360c..c0ceba899e 100644 --- a/src/component-library/TokenInput/index.tsx +++ b/src/component-library/TokenInput/index.tsx @@ -1,2 +1,2 @@ -export type { TokenInputProps } from './TokenInput'; +export type { TokenInputProps, TokenTicker } from './TokenInput'; export { TokenInput } from './TokenInput'; diff --git a/src/component-library/TokenStack/TokenStack.style.tsx b/src/component-library/TokenStack/TokenStack.style.tsx index ea140af558..b6015ec29f 100644 --- a/src/component-library/TokenStack/TokenStack.style.tsx +++ b/src/component-library/TokenStack/TokenStack.style.tsx @@ -4,7 +4,19 @@ import { Flex } from '../Flex'; import { theme } from '../theme'; import { IconSize } from '../utils/prop-types'; -type TokenOffset = 'none' | '1/2' | '1/3' | '1/4'; +const getOffset = (offset: TokenOffset) => { + switch (offset) { + case 'lg': + return '-0.5'; + default: + case 'md': + return '-0.33'; + case 's': + return '-0.25'; + } +}; + +type TokenOffset = 'none' | 's' | 'md' | 'lg'; type StyledWrapperProps = { $size: IconSize; @@ -17,7 +29,7 @@ const StyledWrapper = styled(Flex)` > :not(:last-child) { // Coin one covers 30% of coin two margin-right: ${({ $size, $offset }) => - $offset !== 'none' && `calc(${theme.icon.sizes[$size]} * calc(${$offset}) * -1)`}; + $offset !== 'none' && `calc(${theme.icon.sizes[$size]} * ${getOffset($offset)})`}; } `; diff --git a/src/component-library/TokenStack/TokenStack.tsx b/src/component-library/TokenStack/TokenStack.tsx index 8dc3bb915b..29b5418f3c 100644 --- a/src/component-library/TokenStack/TokenStack.tsx +++ b/src/component-library/TokenStack/TokenStack.tsx @@ -13,7 +13,7 @@ type InheritAttrs = Omit; type TokenStackProps = Props & InheritAttrs; -const TokenStack = ({ tickers, gap, size = 'md', offset = '1/3', ...props }: TokenStackProps): JSX.Element => ( +const TokenStack = ({ tickers, gap, size = 'md', offset = 'md', ...props }: TokenStackProps): JSX.Element => ( {tickers.map((ticker, key) => ( diff --git a/src/component-library/VaultCard/VaultCard.stories.tsx b/src/component-library/VaultCard/VaultCard.stories.tsx deleted file mode 100644 index b3b298b3d1..0000000000 --- a/src/component-library/VaultCard/VaultCard.stories.tsx +++ /dev/null @@ -1,36 +0,0 @@ -import { Meta, Story } from '@storybook/react'; - -import { formatNumber, formatPercentage } from '@/common/utils/utils'; - -import { VaultCard, VaultCardProps } from '.'; - -const Template: Story = (args) => ; - -const Default = Template.bind({}); -Default.args = { - collateralTokenTicker: 'DOT', - wrappedSymbol: 'BTC', - pendingRequests: formatNumber(3), - apy: formatPercentage(0.1623), - collateralScore: '115.45', - link: '#', - atRisk: false -}; - -const AtRisk = Template.bind({}); -AtRisk.args = { - collateralTokenTicker: 'DOT', - wrappedSymbol: 'BTC', - pendingRequests: formatNumber(3), - apy: formatPercentage(0.1623), - collateralScore: '115.45', - link: '#', - atRisk: true -}; - -export { AtRisk, Default }; - -export default { - title: 'Components/VaultCard', - component: VaultCard -} as Meta; diff --git a/src/component-library/WalletIcon/FallbackIcon.tsx b/src/component-library/WalletIcon/FallbackIcon.tsx new file mode 100644 index 0000000000..5520635a0d --- /dev/null +++ b/src/component-library/WalletIcon/FallbackIcon.tsx @@ -0,0 +1,17 @@ +import { forwardRef } from 'react'; + +import { WalletIconProps } from './WalletIcon'; +import { StyledFallbackIcon } from './WalletIcon.style'; + +const FallbackIcon = forwardRef( + ({ name, ...props }, ref): JSX.Element => ( + + {name} + + + ) +); + +FallbackIcon.displayName = 'FallbackIcon'; + +export { FallbackIcon }; diff --git a/src/component-library/WalletIcon/WalletIcon.stories.tsx b/src/component-library/WalletIcon/WalletIcon.stories.tsx new file mode 100644 index 0000000000..fc65b0077b --- /dev/null +++ b/src/component-library/WalletIcon/WalletIcon.stories.tsx @@ -0,0 +1,34 @@ +import { Meta, Story } from '@storybook/react'; + +import { Flex } from '../Flex'; +import { Span } from '../Text'; +import { WalletIcon, WalletIconProps } from './WalletIcon'; + +const Template: Story = (args) => ( + + + + SubWallet + + + + Talisman + + + + Polkadot.js + + +); + +const Default = Template.bind({}); +Default.args = { + size: 'xl' +}; + +export { Default }; + +export default { + title: 'Elements/WalletIcon', + component: WalletIcon +} as Meta; diff --git a/src/component-library/WalletIcon/WalletIcon.style.tsx b/src/component-library/WalletIcon/WalletIcon.style.tsx new file mode 100644 index 0000000000..9c5cc225c7 --- /dev/null +++ b/src/component-library/WalletIcon/WalletIcon.style.tsx @@ -0,0 +1,11 @@ +import styled from 'styled-components'; + +import { Icon } from '../Icon'; +import { theme } from '../theme'; + +const StyledFallbackIcon = styled(Icon)` + stroke: ${theme.icon.fallback.stroke}; + color: ${theme.icon.fallback.color}; +`; + +export { StyledFallbackIcon }; diff --git a/src/component-library/WalletIcon/WalletIcon.tsx b/src/component-library/WalletIcon/WalletIcon.tsx new file mode 100644 index 0000000000..b6834d6b12 --- /dev/null +++ b/src/component-library/WalletIcon/WalletIcon.tsx @@ -0,0 +1,38 @@ +import { forwardRef, ForwardRefExoticComponent, RefAttributes } from 'react'; + +import { IconProps } from '../Icon'; +import { FallbackIcon } from './FallbackIcon'; +import { PolkadotJS, SubWallet, Talisman } from './icons'; + +type WalletComponent = ForwardRefExoticComponent>; + +const wallet: Record = { + 'polkadot-js': PolkadotJS, + 'subwallet-js': SubWallet, + talisman: Talisman +}; + +type Props = { + name: string; +}; + +type NativeAttrs = Omit; + +type WalletIconProps = Props & NativeAttrs; + +const WalletIcon = forwardRef( + ({ name, ...props }, ref): JSX.Element => { + const WalletIcon = wallet[name]; + + if (!WalletIcon) { + return ; + } + + return ; + } +); + +WalletIcon.displayName = 'WalletIcon'; + +export { WalletIcon }; +export type { WalletIconProps }; diff --git a/src/component-library/WalletIcon/icons/PolkadotJS.tsx b/src/component-library/WalletIcon/icons/PolkadotJS.tsx new file mode 100644 index 0000000000..eeb174462e --- /dev/null +++ b/src/component-library/WalletIcon/icons/PolkadotJS.tsx @@ -0,0 +1,45 @@ +import { forwardRef } from 'react'; + +import { Icon, IconProps } from '@/component-library/Icon'; + +const PolkadotJS = forwardRef((props, ref) => ( + + Polakdot.js + + + + + + + + +)); + +PolkadotJS.displayName = 'PolkadotJS'; + +export { PolkadotJS }; diff --git a/src/component-library/WalletIcon/icons/SubWallet.tsx b/src/component-library/WalletIcon/icons/SubWallet.tsx new file mode 100644 index 0000000000..6c760f802a --- /dev/null +++ b/src/component-library/WalletIcon/icons/SubWallet.tsx @@ -0,0 +1,101 @@ +import { forwardRef } from 'react'; + +import { Icon, IconProps } from '@/component-library/Icon'; + +const SubWallet = forwardRef((props, ref) => ( + + SubWallet + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +)); + +SubWallet.displayName = 'SubWallet'; + +export { SubWallet }; diff --git a/src/component-library/WalletIcon/icons/Talisman.tsx b/src/component-library/WalletIcon/icons/Talisman.tsx new file mode 100644 index 0000000000..03eb5c8501 --- /dev/null +++ b/src/component-library/WalletIcon/icons/Talisman.tsx @@ -0,0 +1,39 @@ +import { forwardRef } from 'react'; + +import { Icon, IconProps } from '@/component-library/Icon'; + +const Talisman = forwardRef((props, ref) => ( + + Talisman + + + + + + + + + + +)); + +Talisman.displayName = 'Talisman'; + +export { Talisman }; diff --git a/src/component-library/WalletIcon/icons/index.ts b/src/component-library/WalletIcon/icons/index.ts new file mode 100644 index 0000000000..b8e4fc140a --- /dev/null +++ b/src/component-library/WalletIcon/icons/index.ts @@ -0,0 +1,3 @@ +export { PolkadotJS } from './PolkadotJS'; +export { SubWallet } from './SubWallet'; +export { Talisman } from './Talisman'; diff --git a/src/component-library/WalletIcon/index.tsx b/src/component-library/WalletIcon/index.tsx new file mode 100644 index 0000000000..131f35ecae --- /dev/null +++ b/src/component-library/WalletIcon/index.tsx @@ -0,0 +1,2 @@ +export type { WalletIconProps } from './WalletIcon'; +export { WalletIcon } from './WalletIcon'; diff --git a/src/component-library/css/margin.ts b/src/component-library/css/margin.ts new file mode 100644 index 0000000000..7214ed4d91 --- /dev/null +++ b/src/component-library/css/margin.ts @@ -0,0 +1,21 @@ +import { css, DefaultTheme, FlattenInterpolation } from 'styled-components'; + +import { theme } from '../theme'; +import { MarginProps, Spacing } from '../utils/prop-types'; + +type StyledMarginProps = { + [K in keyof MarginProps as `$${string & K}`]: MarginProps[K]; +}; + +const getThemeSpacing = (spacing?: Spacing) => spacing && theme.spacing[spacing]; + +const marginCSS = (props: StyledMarginProps): FlattenInterpolation => css` + margin: ${getThemeSpacing(props.$margin)}; + margin-top: ${getThemeSpacing(props.$marginTop || props.$marginY)}; + margin-bottom: ${getThemeSpacing(props.$marginBottom || props.$marginY)}; + margin-left: ${getThemeSpacing(props.$marginLeft || props.$marginX)}; + margin-right: ${getThemeSpacing(props.$marginRight || props.$marginX)}; +`; + +export type { StyledMarginProps }; +export { marginCSS }; diff --git a/src/component-library/css/overlay.ts b/src/component-library/css/overlay.ts index ed175b2dc9..e699201790 100644 --- a/src/component-library/css/overlay.ts +++ b/src/component-library/css/overlay.ts @@ -30,6 +30,9 @@ const overlayCSS = (isOpen: boolean): FlattenInterpolation => visibility: ${isOpen ? 'visible' : 'hidden'}; opacity: ${isOpen ? 1 : 0}; pointer-events: ${isOpen ? 'auto' : 'none'}; + transition: transform ${theme.transition.duration.duration100}ms ease-in-out, + opacity ${theme.transition.duration.duration100}ms ease-in-out, + visibility 0ms linear ${theme.transition.duration.duration100}ms; `; export { getOverlayPlacementCSS, overlayCSS }; diff --git a/src/component-library/css/responsive.ts b/src/component-library/css/responsive.ts new file mode 100644 index 0000000000..f8b293b1a8 --- /dev/null +++ b/src/component-library/css/responsive.ts @@ -0,0 +1,26 @@ +import { theme } from '../theme'; +import { BreakPoints, ResponsiveProp } from '../utils/prop-types'; + +const getResponsiveCSS = (key: string, prop?: ResponsiveProp): string | undefined => { + if (!prop) return undefined; + + if (typeof prop === 'object') { + let finalQuery = ''; + + for (const breakpoint of Object.keys(prop)) { + const query = ` + @media (min-width: ${theme.breakpoints.values[breakpoint as BreakPoints]}px){ + ${key}: ${prop[breakpoint as BreakPoints]}; + } + `; + + finalQuery = finalQuery.concat(query); + } + + return finalQuery; + } + + return `${key}: ${prop};`; +}; + +export { getResponsiveCSS }; diff --git a/src/component-library/index.tsx b/src/component-library/index.tsx index 65f9bad511..809606ecc7 100644 --- a/src/component-library/index.tsx +++ b/src/component-library/index.tsx @@ -12,14 +12,16 @@ export type { CTALinkProps, CTAProps } from './CTA'; export { CTA, CTALink } from './CTA'; export type { DividerProps } from './Divider'; export { Divider } from './Divider'; +export type { FieldProps } from './Field'; +export { Field, useFieldProps } from './Field'; export type { FlexProps } from './Flex'; export { Flex } from './Flex'; export type { GridItemProps, GridProps } from './Grid'; export { Grid, GridItem } from './Grid'; -export type { InfoBoxProps } from './InfoBox'; -export { InfoBox } from './InfoBox'; export type { InputProps } from './Input'; export { Input } from './Input'; +export type { LabelProps } from './Label'; +export { Label } from './Label'; export type { ListItemProps, ListProps } from './List'; export { List, ListItem } from './List'; export type { LoadingSpinnerProps } from './LoadingSpinner'; @@ -34,7 +36,7 @@ export type { StackProps } from './Stack'; export { Stack } from './Stack'; export type { SwitchProps } from './Switch'; export { Switch } from './Switch'; -export type { TableProps } from './Table'; +export type { ColumnProps, RowProps, TableProps } from './Table'; export { Table } from './Table'; export type { TabsItemProps, TabsProps } from './Tabs'; export { Tabs, TabsItem } from './Tabs'; @@ -52,5 +54,6 @@ export type { TooltipProps } from './Tooltip'; export { Tooltip } from './Tooltip'; export * from './types'; export * from './utils/prop-types'; -export type { VaultCardProps } from './VaultCard'; -export { VaultCard } from './VaultCard'; +export { useMediaQuery } from './utils/use-media-query'; +export type { WalletIconProps } from './WalletIcon'; +export { WalletIcon } from './WalletIcon'; diff --git a/src/component-library/theme/theme.base.css b/src/component-library/theme/theme.base.css index 1c4480a301..532086f98e 100644 --- a/src/component-library/theme/theme.base.css +++ b/src/component-library/theme/theme.base.css @@ -97,6 +97,7 @@ --spacing-10: 2.5rem; --spacing-12: 3rem; --spacing-14: 3.5rem; + --spacing-16: 4rem; --spacing-28: 7rem; --rounded-sm: 2px; diff --git a/src/component-library/theme/theme.interlay.css b/src/component-library/theme/theme.interlay.css index c43f5d877f..fef6d86da4 100644 --- a/src/component-library/theme/theme.interlay.css +++ b/src/component-library/theme/theme.interlay.css @@ -56,7 +56,7 @@ --colors-input-default-border: var(--colors-neutral-light-grey); --colors-input-hover-border: var(--colors-lighter-blue); --colors-input-focus-border: var(--colors-light-blue-90); - --colors-input-disabled-text: var(--colors-neutral-lighter-grey); + --colors-input-disabled-text: #9a9a9a; --colors-input-disabled-border: var(--colors-neutral-lighter-grey); --colors-input-background: var(--colors-neutral-white); /* Token Input */ @@ -86,4 +86,9 @@ --color-list-secondary-bg: var(--color-light-grey); --color-list-secondary-hover-bg: #f0f1f2; --color-list-selected-text: var(--colors-neutral-white); + /* Icon */ + --color-icon-fallback-stroke: var(--colors-neutral-white); + --color-icon-fallback-color: #f4f3f5; + /* Select */ + --color-select-text: var(--colors-neutral-black); } diff --git a/src/component-library/theme/theme.kintsugi.css b/src/component-library/theme/theme.kintsugi.css index eba9ad5e0a..386ac48a09 100644 --- a/src/component-library/theme/theme.kintsugi.css +++ b/src/component-library/theme/theme.kintsugi.css @@ -59,9 +59,9 @@ --colors-input-default-border: var(--colors-neutral-white-25); --colors-input-hover-border: var(--colors-slate-gray); --colors-input-focus-border: var(--colors-neutral-white); - --colors-input-disabled-text: var(--colors-mid-blue); + --colors-input-disabled-text: var(--colors-slate-gray); --colors-input-disabled-border: var(--colors-mid-blue); - --colors-input-background: transparent; + --colors-input-background: var(--colors-blue); /* Token Input */ --colors-token-input-end-adornment-bg: var(--colors-neutral-white-25); /* Token List */ @@ -86,7 +86,12 @@ /* List */ --color-list-primary-bg: var(--colors-bg-primary); --color-list-primary-hover-bg: #1c2c46; - --color-list-secondary-bg: var(--colors-blue); + --color-list-secondary-bg: #101e3d; --color-list-secondary-hover-bg: #1c2c46; --color-list-selected-text: var(--colors-neutral-black); + /* Icon */ + --color-icon-fallback-stroke: #232323; + --color-icon-fallback-color: #040816; + /* Select */ + --color-select-text: var(--colors-neutral-white); } diff --git a/src/component-library/theme/theme.ts b/src/component-library/theme/theme.ts index 3496d4dda9..d4d69a1fff 100644 --- a/src/component-library/theme/theme.ts +++ b/src/component-library/theme/theme.ts @@ -1,3 +1,5 @@ +import { breakpoints } from '../utils/breakpoints'; + const theme = { // Layout layout: { @@ -7,6 +9,7 @@ const theme = { lg: '48em' } }, + breakpoints, // Generic colors: { textPrimary: 'var(--colors-text-primary)', @@ -56,6 +59,7 @@ const theme = { spacing10: 'var(--spacing-10)', spacing12: 'var(--spacing-12)', spacing14: 'var(--spacing-14)', + spacing16: 'var(--spacing-16)', spacing28: 'var(--spacing-28)' }, rounded: { @@ -163,6 +167,12 @@ const theme = { text: 'var(--colors-cta-text-text)', bgHover: 'var(--colors-cta-text-hover)' }, + 'x-small': { + padding: 'var(--spacing-1)', + text: 'var(--text-xs)', + // TODO: revist on redesign + lineHeight: '1' + }, small: { padding: 'var(--spacing-2)', text: 'var(--text-xs)', @@ -367,7 +377,11 @@ const theme = { zIndex: 2, underlay: { zIndex: 1, - bg: 'var(--colors-neutral-black-60)' + bg: 'var(--colors-neutral-black-60)', + transition: { + entering: 'opacity .15s cubic-bezier(0,0,.4,1)', + exiting: 'opacity .1s cubic-bezier(0.5,0,1,1), visibility 0s linear .1s' + } }, header: { paddingY: 'var(--spacing-4)', @@ -388,6 +402,10 @@ const theme = { }, closeBtn: { zIndex: 100 + }, + transition: { + entering: 'transform .15s cubic-bezier(0,0,0.4,1) .1s, opacity .15s cubic-bezier(0,0,0.4,1)', + exiting: 'opacity .1s cubic-bezier(0.5,0,1,1), visibility 0s linear, transform 0s linear .1s' } }, switch: { @@ -410,7 +428,12 @@ const theme = { } }, divider: { - bg: 'var(--colors-border)' + bg: 'var(--colors-border)', + size: { + small: '1px', + medium: '2px', + large: '3px' + } }, icon: { sizes: { @@ -419,22 +442,71 @@ const theme = { lg: 'var(--spacing-8)', xl: 'var(--spacing-10)', xl2: 'var(--spacing-12)' + }, + fallback: { + color: 'var(--color-icon-fallback-color)', + stroke: 'var(--color-icon-fallback-stroke)' } }, list: { text: 'var(--color-list-selected-text)', primary: { - bg: 'var(--color-list-primary-bg)', - border: '1px solid var(--colors-border)', - hover: { - bg: 'var(--color-list-primary-hover-bg)' - } + bg: '', + border: '', + rounded: '' }, secondary: { - bg: 'var(--color-list-secondary-bg)', - border: 'none', - hover: { - bg: 'var(--color-list-secondary-hover-bg)' + bg: '', + border: '', + rounded: '' + }, + card: { + bg: 'var(--colors-table-odd-row-bg)', + border: '1px solid var(--colors-border)', + rounded: 'var(--rounded-md)' + }, + item: { + primary: { + bg: 'var(--color-list-primary-bg)', + border: '1px solid var(--colors-border)', + hover: { + bg: 'var(--color-list-primary-hover-bg)' + }, + rounded: 'var(--rounded-md)' + }, + secondary: { + bg: 'var(--color-list-secondary-bg)', + border: 'none', + hover: { + bg: 'var(--color-list-secondary-hover-bg)' + }, + rounded: 'var(--rounded-md)' + }, + card: { + bg: 'var(--colors-table-odd-row-bg)', + border: '1px solid var(--colors-border)', + hover: { + bg: 'var(--color-list-primary-hover-bg)' + }, + rounded: '' + } + } + }, + select: { + placeholder: 'var(--colors-text-tertiary)', + color: 'var(--color-select-text)', + size: { + small: { + padding: 'var(--spacing-1)', + text: 'var(--text-s)' + }, + medium: { + padding: 'var(--spacing-2)', + text: 'var(--text-base)' + }, + large: { + padding: 'var(--spacing-5) var(--spacing-2)', + text: 'var(--text-lg)' } } } diff --git a/src/component-library/utils/breakpoints.ts b/src/component-library/utils/breakpoints.ts new file mode 100644 index 0000000000..ab6781e397 --- /dev/null +++ b/src/component-library/utils/breakpoints.ts @@ -0,0 +1,17 @@ +import { BreakPoints } from './prop-types'; + +const values: Record = { + xs: 0, // phone + sm: 600, // tablet + md: 900, // small laptop + lg: 1200, // desktop + xl: 1536 // large screen +}; + +const breakpoints = { + values, + up: (key: BreakPoints): string => `(min-width:${values[key]}px)`, + down: (key: BreakPoints): string => `(max-width:${values[key]}px)` +}; + +export { breakpoints }; diff --git a/src/component-library/utils/format.ts b/src/component-library/utils/format.ts new file mode 100644 index 0000000000..8f520ada3f --- /dev/null +++ b/src/component-library/utils/format.ts @@ -0,0 +1,17 @@ +const getFormatUSDNotation = (amount: number) => { + const amountLength = amount.toFixed(0).length; + + return amountLength >= 6 ? 'compact' : 'standard'; +}; + +const formatUSD = (amount: number, options?: { compact?: boolean }): string => { + const { format } = new Intl.NumberFormat(undefined, { + style: 'currency', + currency: 'USD', + notation: options?.compact ? getFormatUSDNotation(amount) : undefined + }); + + return format(amount); +}; + +export { formatUSD }; diff --git a/src/component-library/utils/input.ts b/src/component-library/utils/input.ts index 68cc7f05ce..23ecc20360 100644 --- a/src/component-library/utils/input.ts +++ b/src/component-library/utils/input.ts @@ -1,10 +1,12 @@ -import { MutableRefObject, RefObject } from 'react'; +import { RefObject } from 'react'; /** * @param {RefObject} ref - input ref. * @param {string | ReadonlyArray | number} value - value to be included in the event * @return {void} - Manually emits onChange event */ +// TODO: consider moving away from this type of strategy or narrow +// the usage, because it only works on native events and `onPress` const triggerChangeEvent = ( ref: RefObject, value: string | ReadonlyArray | number = '' @@ -16,14 +18,4 @@ const triggerChangeEvent = ( ref.current?.dispatchEvent(e); }; -const assignFormRef = (formRef?: (instance: any) => void, domRef?: MutableRefObject) => ( - el: HTMLInputElement | null -): void => { - formRef?.(el); - - if (domRef) { - domRef.current = el; - } -}; - -export { assignFormRef, triggerChangeEvent }; +export { triggerChangeEvent }; diff --git a/src/component-library/utils/prop-types.ts b/src/component-library/utils/prop-types.ts index 943aa948af..74e793a13b 100644 --- a/src/component-library/utils/prop-types.ts +++ b/src/component-library/utils/prop-types.ts @@ -48,6 +48,12 @@ export type Variants = typeof variant[number]; export type CTAVariants = typeof ctaVariant[number]; +export type ListVariants = Variants | 'card'; + +export type DividerVariants = Colors | 'default'; + +export type CTASizes = 'x-small' | 'small' | 'medium' | 'large'; + export type Status = typeof status[number]; export type Sizes = typeof sizes[number]; @@ -76,6 +82,18 @@ export interface ElementTypeProp { elementType?: ElementType; } +export interface MarginProps { + margin?: Spacing; + marginTop?: Spacing; + marginBottom?: Spacing; + marginLeft?: Spacing; + marginRight?: Spacing; + marginX?: Spacing; + marginY?: Spacing; +} + +export type ResponsiveProp = T | Partial<{ [K in BreakPoints]: T }>; + export type FontWeight = keyof typeof theme.fontWeight; export type Orientation = 'horizontal' | 'vertical'; @@ -83,3 +101,5 @@ export type Orientation = 'horizontal' | 'vertical'; export type IconSize = keyof typeof theme.icon.sizes; export type Overflow = 'auto' | 'hidden' | 'scroll' | 'visible' | 'inherit'; + +export type BreakPoints = 'xs' | 'sm' | 'md' | 'lg' | 'xl'; diff --git a/src/component-library/utils/use-media-query.tsx b/src/component-library/utils/use-media-query.tsx new file mode 100644 index 0000000000..d689b1be9c --- /dev/null +++ b/src/component-library/utils/use-media-query.tsx @@ -0,0 +1,44 @@ +import { useEffect, useState } from 'react'; + +const useMediaQuery = (query: string): boolean => { + const getMatches = (query: string): boolean => { + // Prevents SSR issues + if (typeof window !== 'undefined') { + return window.matchMedia(query).matches; + } + return false; + }; + + const [matches, setMatches] = useState(getMatches(query)); + + function handleChange() { + setMatches(getMatches(query)); + } + + useEffect(() => { + const matchMedia = window.matchMedia(query); + + // Triggered at the first client-side load and if query changes + handleChange(); + + // Listen matchMedia + if (matchMedia.addListener) { + matchMedia.addListener(handleChange); + } else { + matchMedia.addEventListener('change', handleChange); + } + + return () => { + if (matchMedia.removeListener) { + matchMedia.removeListener(handleChange); + } else { + matchMedia.removeEventListener('change', handleChange); + } + }; + // eslint-disable-next-line react-hooks/exhaustive-deps + }, [query]); + + return matches; +}; + +export { useMediaQuery }; diff --git a/src/component-library/utils/use-style-props.tsx b/src/component-library/utils/use-style-props.tsx new file mode 100644 index 0000000000..40aeb26e1f --- /dev/null +++ b/src/component-library/utils/use-style-props.tsx @@ -0,0 +1,30 @@ +import { StyledMarginProps } from '../css/margin'; +import { MarginProps } from './prop-types'; + +type StyleProps = StyledMarginProps; + +type ComponentProps> = Omit; + +type UseStylePropsResult> = { styleProps: StyleProps; componentProps: ComponentProps }; + +// Extracts props that are solely for styling so that they get mapped to styled props +// This approach is used for a set of styling props that could be reused across different component +const useStyleProps = >(props: T): UseStylePropsResult => { + const { margin, marginTop, marginBottom, marginLeft, marginRight, marginX, marginY, ...componentProps } = props; + + return { + styleProps: { + $margin: margin, + $marginTop: marginTop, + $marginBottom: marginBottom, + $marginLeft: marginLeft, + $marginRight: marginRight, + $marginX: marginX, + $marginY: marginY + }, + componentProps + }; +}; + +export type { StyleProps, UseStylePropsResult }; +export { useStyleProps }; diff --git a/src/components/AuthCTA/AuthCTA.tsx b/src/components/AuthCTA/AuthCTA.tsx new file mode 100644 index 0000000000..b853b064c4 --- /dev/null +++ b/src/components/AuthCTA/AuthCTA.tsx @@ -0,0 +1,40 @@ +import { mergeProps } from '@react-aria/utils'; +import { forwardRef } from 'react'; +import { useTranslation } from 'react-i18next'; +import { useDispatch } from 'react-redux'; + +import { showAccountModalAction } from '@/common/actions/general.actions'; +import { CTA, CTAProps } from '@/component-library'; +import { useSubstrateSecureState } from '@/lib/substrate'; + +type AuthCTAProps = CTAProps; + +const AuthCTA = forwardRef( + ({ onPress, children, type: typeProp, disabled, ...props }, ref): JSX.Element => { + const { t } = useTranslation(); + + const { selectedAccount } = useSubstrateSecureState(); + const dispatch = useDispatch(); + + const otherProps = selectedAccount + ? { + type: typeProp, + disabled, + onPress, + children + } + : { + type: 'button', + disabled: false, + onPress: () => dispatch(showAccountModalAction(true)), + children: t('connect_wallet') + }; + + return ; + } +); + +AuthCTA.displayName = 'AuthCTA'; + +export { AuthCTA }; +export type { AuthCTAProps }; diff --git a/src/components/AuthCTA/index.tsx b/src/components/AuthCTA/index.tsx new file mode 100644 index 0000000000..0e4dfa7549 --- /dev/null +++ b/src/components/AuthCTA/index.tsx @@ -0,0 +1,2 @@ +export type { AuthCTAProps } from './AuthCTA'; +export { AuthCTA } from './AuthCTA'; diff --git a/src/components/DataGrid/AssetCell.tsx b/src/components/DataGrid/AssetCell.tsx new file mode 100644 index 0000000000..c708f5898f --- /dev/null +++ b/src/components/DataGrid/AssetCell.tsx @@ -0,0 +1,24 @@ +import { CoinIcon, Flex, FlexProps, FontSize, IconSize, Span } from '@/component-library'; + +type Props = { + ticker: string; + tickers?: string[]; + size?: IconSize; + textSize?: FontSize; +}; + +type InheritAttrs = Omit; + +type AssetCellProps = Props & InheritAttrs; + +const AssetCell = ({ ticker, tickers, size, textSize = 's', ...props }: AssetCellProps): JSX.Element => ( + + + + {ticker} + + +); + +export { AssetCell }; +export type { AssetCellProps }; diff --git a/src/components/DataGrid/BalanceCell.tsx b/src/components/DataGrid/BalanceCell.tsx new file mode 100644 index 0000000000..c7d14845bb --- /dev/null +++ b/src/components/DataGrid/BalanceCell.tsx @@ -0,0 +1,20 @@ +import { CurrencyExt } from '@interlay/interbtc-api'; +import { MonetaryAmount } from '@interlay/monetary-js'; + +import { formatUSD } from '@/common/utils/utils'; +import { AlignItems } from '@/component-library'; + +import { Cell } from './Cell'; + +type BalanceCellProps = { + amount: MonetaryAmount; + amountUSD: number; + alignItems?: AlignItems; +}; + +const BalanceCell = ({ amount, amountUSD, alignItems }: BalanceCellProps): JSX.Element => ( + +); + +export { BalanceCell }; +export type { BalanceCellProps }; diff --git a/src/components/DataGrid/Cell.tsx b/src/components/DataGrid/Cell.tsx new file mode 100644 index 0000000000..e1b8cef6ae --- /dev/null +++ b/src/components/DataGrid/Cell.tsx @@ -0,0 +1,33 @@ +import { forwardRef } from 'react'; + +import { Colors, Flex, FlexProps } from '@/component-library'; + +import { StyledCellLabel, StyledCellSubLabel } from './DataGrid.style'; + +type Props = { + label?: string; + sublabel?: string; + labelColor?: Colors; +}; + +type InheritAttrs = Omit; + +type CellProps = Props & InheritAttrs; + +const Cell = forwardRef( + ({ label, sublabel, labelColor: labelColorProp, alignItems, ...props }, ref): JSX.Element => { + const labelColor = labelColorProp || sublabel ? 'secondary' : undefined; + + return ( + + {label} + {sublabel && {sublabel}} + + ); + } +); + +Cell.displayName = 'Cell'; + +export { Cell }; +export type { CellProps }; diff --git a/src/components/DataGrid/DataGrid.style.tsx b/src/components/DataGrid/DataGrid.style.tsx new file mode 100644 index 0000000000..f66ce9de0b --- /dev/null +++ b/src/components/DataGrid/DataGrid.style.tsx @@ -0,0 +1,21 @@ +import styled from 'styled-components'; + +import { Span, theme } from '@/component-library'; + +const StyledCellLabel = styled(Span)` + font-weight: ${theme.fontWeight.bold}; + font-size: ${theme.text.s}; + display: inline-flex; + gap: ${theme.spacing.spacing1}; + align-items: baseline; +`; + +const StyledCellTickerLabel = styled(Span)` + font-size: ${theme.text.xs}; +`; + +const StyledCellSubLabel = styled(Span)` + font-size: ${theme.text.xs}; +`; + +export { StyledCellLabel, StyledCellSubLabel, StyledCellTickerLabel }; diff --git a/src/components/DataGrid/DataGrid.tsx b/src/components/DataGrid/DataGrid.tsx new file mode 100644 index 0000000000..54b9d08da4 --- /dev/null +++ b/src/components/DataGrid/DataGrid.tsx @@ -0,0 +1,35 @@ +import { useId } from '@react-aria/utils'; +import { ReactNode } from 'react'; + +import { BreakPoints, theme } from '@/component-library'; +import { useMediaQuery } from '@/component-library/utils/use-media-query'; + +import { List } from './List'; +import { Table, TableProps } from './Table'; + +type Props = { + title?: ReactNode; + className?: string; + actions?: ReactNode; + placeholder?: ReactNode; + responsive?: boolean; + breakpoint?: BreakPoints; +}; + +type InheritAttrs = Omit, keyof Props>; + +type DataGridProps = Props & InheritAttrs; + +const DataGrid = ({ responsive = true, breakpoint = 'md', ...props }: DataGridProps): JSX.Element => { + const titleId = useId(); + const isMobile = useMediaQuery(theme.breakpoints.down(breakpoint)); + + if (isMobile && responsive) { + return ; + } + + return ; +}; + +export { DataGrid }; +export type { DataGridProps }; diff --git a/src/components/DataGrid/List.tsx b/src/components/DataGrid/List.tsx new file mode 100644 index 0000000000..1985b1214a --- /dev/null +++ b/src/components/DataGrid/List.tsx @@ -0,0 +1,44 @@ +import { ReactNode } from 'react'; + +import { + Card, + List as LibList, + ListItem as LibListItem, + ListProps as LibListProps, + TableProps +} from '@/component-library'; + +import { ListItem } from './ListItem'; +import { TableWrapper } from './TableWrapper'; + +type Props = { + title?: ReactNode; + titleId?: string; + className?: string; + actions?: ReactNode; + placeholder?: ReactNode; +}; + +type TableAttrs = Omit, keyof Props>; + +type InheritAttrs = Omit; + +type ListProps = Props & TableAttrs & InheritAttrs; + +const List = ({ title, titleId, rows, columns, className, actions, placeholder, ...props }: ListProps): JSX.Element => ( + + {rows.length ? ( + + {rows.map((row) => ( + + + + ))} + + ) : ( + {placeholder} + )} + +); +export { List }; +export type { ListProps }; diff --git a/src/components/DataGrid/ListItem.tsx b/src/components/DataGrid/ListItem.tsx new file mode 100644 index 0000000000..14005810a9 --- /dev/null +++ b/src/components/DataGrid/ListItem.tsx @@ -0,0 +1,33 @@ +import { Fragment } from 'react'; + +import { Dd, Dl, DlGroup, DlProps, Dt, RowProps, TableProps } from '@/component-library'; + +type Props = { + row: RowProps; +}; + +type TableAttrs = Omit, keyof Props>; + +type InheritAttrs = Omit; + +type ListItemProps = Props & TableAttrs & InheritAttrs; + +const ListItem = ({ row, columns, ...props }: ListItemProps): JSX.Element => ( +
+ {columns.map((column) => { + const title = column.name; + const children = row[column.uid]; + + return title ? ( + +
{title}
+
{children}
+
+ ) : ( + {children} + ); + })} +
+); +export { ListItem }; +export type { ListItemProps }; diff --git a/src/components/DataGrid/Table.tsx b/src/components/DataGrid/Table.tsx new file mode 100644 index 0000000000..0b7cd8ab9d --- /dev/null +++ b/src/components/DataGrid/Table.tsx @@ -0,0 +1,38 @@ +import { ReactNode } from 'react'; + +import { Card, Table as LiTable, TableProps as LibTableProps } from '@/component-library'; + +import { TableWrapper } from './TableWrapper'; + +type Props = { + title?: ReactNode; + titleId?: string; + className?: string; + actions?: ReactNode; + placeholder?: ReactNode; +}; + +type InheritAttrs = Omit; + +type TableProps = Props & InheritAttrs; + +const Table = ({ + title, + titleId, + rows, + columns, + className, + actions, + placeholder, + ...props +}: TableProps): JSX.Element => ( + + + + {!rows.length && placeholder} + + +); + +export { Table }; +export type { TableProps }; diff --git a/src/components/DataGrid/TableWrapper.tsx b/src/components/DataGrid/TableWrapper.tsx new file mode 100644 index 0000000000..2cb273227b --- /dev/null +++ b/src/components/DataGrid/TableWrapper.tsx @@ -0,0 +1,32 @@ +import { ReactNode } from 'react'; + +import { Flex, FlexProps, H2 } from '@/component-library'; + +type Props = { + title?: ReactNode; + titleId?: string; + actions?: ReactNode; +}; + +type InheritAttrs = Omit; + +type TableWrapperProps = Props & InheritAttrs; + +const TableWrapper = ({ title, titleId, actions, children, ...props }: TableWrapperProps): JSX.Element => { + return ( + + + {title && ( +

+ {title} +

+ )} + {actions} +
+ {children} +
+ ); +}; + +export { TableWrapper }; +export type { TableWrapperProps }; diff --git a/src/components/DataGrid/index.tsx b/src/components/DataGrid/index.tsx new file mode 100644 index 0000000000..39b01973cd --- /dev/null +++ b/src/components/DataGrid/index.tsx @@ -0,0 +1,10 @@ +export type { AssetCellProps } from './AssetCell'; +export { AssetCell } from './AssetCell'; +export type { BalanceCellProps } from './BalanceCell'; +export { BalanceCell } from './BalanceCell'; +export type { CellProps } from './Cell'; +export { Cell } from './Cell'; +export type { DataGridProps } from './DataGrid'; +export { DataGrid } from './DataGrid'; +export type { TableProps } from './Table'; +export { Table } from './Table'; diff --git a/src/pages/Loans/LoansOverview/components/ApyTooltip/AssetGroup.tsx b/src/components/LoanApyTooltip/AssetGroup.tsx similarity index 60% rename from src/pages/Loans/LoansOverview/components/ApyTooltip/AssetGroup.tsx rename to src/components/LoanApyTooltip/AssetGroup.tsx index cf5a10a2f3..7c2a88ae38 100644 --- a/src/pages/Loans/LoansOverview/components/ApyTooltip/AssetGroup.tsx +++ b/src/components/LoanApyTooltip/AssetGroup.tsx @@ -1,11 +1,12 @@ import { CurrencyExt } from '@interlay/interbtc-api'; import { MonetaryAmount } from '@interlay/monetary-js'; -import { displayMonetaryAmountInUSDFormat, formatNumber } from '@/common/utils/utils'; +import { displayMonetaryAmountInUSDFormat } from '@/common/utils/utils'; import { Dd, Dt } from '@/component-library'; +import { getTokenPrice } from '@/utils/helpers/prices'; import { Prices } from '@/utils/hooks/api/use-get-prices'; -import { StyledApyTooltipGroup } from './ApyTooltip.style'; +import { StyledApyTooltipGroup } from './LoanApyTooltip.style'; type AssetGroupProps = { amount: MonetaryAmount; @@ -13,17 +14,13 @@ type AssetGroupProps = { }; const AssetGroup = ({ amount, prices }: AssetGroupProps): JSX.Element => { - const amountUSD = displayMonetaryAmountInUSDFormat(amount, prices?.[amount.currency.ticker].usd); - - const amountLabel = formatNumber(amount?.toBig().toNumber(), { - maximumFractionDigits: amount?.currency.humanDecimals - }); + const amountUSD = displayMonetaryAmountInUSDFormat(amount, getTokenPrice(prices, amount.currency.ticker)?.usd); return (
{amount.currency.ticker}:
- {amountLabel} ({amountUSD}) + {amount.toHuman()} ({amountUSD})
); diff --git a/src/pages/Loans/LoansOverview/components/ApyTooltip/BreakdownGroup.tsx b/src/components/LoanApyTooltip/BreakdownGroup.tsx similarity index 94% rename from src/pages/Loans/LoansOverview/components/ApyTooltip/BreakdownGroup.tsx rename to src/components/LoanApyTooltip/BreakdownGroup.tsx index eeba3c432d..2a11c2cb78 100644 --- a/src/pages/Loans/LoansOverview/components/ApyTooltip/BreakdownGroup.tsx +++ b/src/components/LoanApyTooltip/BreakdownGroup.tsx @@ -1,9 +1,9 @@ import Big from 'big.js'; import { Dd, Dl, DlGroup, Dt } from '@/component-library'; +import { getApyLabel } from '@/utils/helpers/loans'; -import { getApyLabel } from '../../utils/apy'; -import { StyledApyTooltipGroup, StyledApyTooltipTitle } from './ApyTooltip.style'; +import { StyledApyTooltipGroup, StyledApyTooltipTitle } from './LoanApyTooltip.style'; type BreakdownGroupProps = { apy: Big; diff --git a/src/pages/Loans/LoansOverview/components/ApyTooltip/ApyTooltip.style.tsx b/src/components/LoanApyTooltip/LoanApyTooltip.style.tsx similarity index 100% rename from src/pages/Loans/LoansOverview/components/ApyTooltip/ApyTooltip.style.tsx rename to src/components/LoanApyTooltip/LoanApyTooltip.style.tsx diff --git a/src/pages/Loans/LoansOverview/components/ApyTooltip/ApyTooltip.tsx b/src/components/LoanApyTooltip/LoanApyTooltip.tsx similarity index 85% rename from src/pages/Loans/LoansOverview/components/ApyTooltip/ApyTooltip.tsx rename to src/components/LoanApyTooltip/LoanApyTooltip.tsx index 8734cbb192..37a2dbc5d2 100644 --- a/src/pages/Loans/LoansOverview/components/ApyTooltip/ApyTooltip.tsx +++ b/src/components/LoanApyTooltip/LoanApyTooltip.tsx @@ -6,9 +6,9 @@ import Big from 'big.js'; import { Dd, Dl, DlGroup } from '@/component-library'; import { Prices } from '@/utils/hooks/api/use-get-prices'; -import { StyledApyTooltipTitle, StyledTooltip } from './ApyTooltip.style'; import { AssetGroup } from './AssetGroup'; import { BreakdownGroup } from './BreakdownGroup'; +import { StyledApyTooltipTitle, StyledTooltip } from './LoanApyTooltip.style'; import { RewardsGroup } from './RewardsGroup'; type Props = { @@ -24,9 +24,9 @@ type Props = { type InheritAttrs = Omit; -type ApyTooltipProps = Props & InheritAttrs; +type LoanApyTooltipProps = Props & InheritAttrs; -const ApyTooltip = ({ +const LoanApyTooltip = ({ apy, currency, earnedInterest, @@ -36,9 +36,8 @@ const ApyTooltip = ({ prices, isBorrow, ...props -}: ApyTooltipProps): JSX.Element => { - // MEMO: in assets table there is no earn or debt asset to be shown - const showEarnedRewards = !!accumulatedDebt || !!earnedInterest; +}: LoanApyTooltipProps): JSX.Element => { + const showEarnedRewards = !!rewards || !!earnedInterest; const label = (
@@ -76,5 +75,5 @@ const ApyTooltip = ({ return ; }; -export { ApyTooltip }; -export type { ApyTooltipProps }; +export { LoanApyTooltip }; +export type { LoanApyTooltipProps }; diff --git a/src/pages/Loans/LoansOverview/components/ApyTooltip/RewardsGroup.tsx b/src/components/LoanApyTooltip/RewardsGroup.tsx similarity index 78% rename from src/pages/Loans/LoansOverview/components/ApyTooltip/RewardsGroup.tsx rename to src/components/LoanApyTooltip/RewardsGroup.tsx index 22b02d8310..dfeb949ccc 100644 --- a/src/pages/Loans/LoansOverview/components/ApyTooltip/RewardsGroup.tsx +++ b/src/components/LoanApyTooltip/RewardsGroup.tsx @@ -3,9 +3,10 @@ import { MonetaryAmount } from '@interlay/monetary-js'; import { displayMonetaryAmountInUSDFormat, formatNumber } from '@/common/utils/utils'; import { Dd, Dt } from '@/component-library'; +import { getTokenPrice } from '@/utils/helpers/prices'; import { Prices } from '@/utils/hooks/api/use-get-prices'; -import { StyledApyTooltipGroup } from './ApyTooltip.style'; +import { StyledApyTooltipGroup } from './LoanApyTooltip.style'; type RewardsGroupProps = { rewards: MonetaryAmount; @@ -13,7 +14,7 @@ type RewardsGroupProps = { }; const RewardsGroup = ({ rewards, prices }: RewardsGroupProps): JSX.Element => { - const rewardsUSD = displayMonetaryAmountInUSDFormat(rewards, prices?.[rewards.currency.ticker].usd); + const rewardsUSD = displayMonetaryAmountInUSDFormat(rewards, getTokenPrice(prices, rewards.currency.ticker)?.usd); const rewardsLabel = `${formatNumber(rewards?.toBig().toNumber(), { maximumFractionDigits: rewards?.currency.humanDecimals diff --git a/src/components/LoanApyTooltip/index.tsx b/src/components/LoanApyTooltip/index.tsx new file mode 100644 index 0000000000..062155f827 --- /dev/null +++ b/src/components/LoanApyTooltip/index.tsx @@ -0,0 +1,2 @@ +export type { LoanApyTooltipProps } from './LoanApyTooltip'; +export { LoanApyTooltip } from './LoanApyTooltip'; diff --git a/src/pages/Loans/LoansOverview/components/LoansBaseTable/ApyCell.tsx b/src/components/LoanPositionsTable/ApyCell.tsx similarity index 65% rename from src/pages/Loans/LoansOverview/components/LoansBaseTable/ApyCell.tsx rename to src/components/LoanPositionsTable/ApyCell.tsx index 2f82a27cfa..7a0a7d0b4a 100644 --- a/src/pages/Loans/LoansOverview/components/LoansBaseTable/ApyCell.tsx +++ b/src/components/LoanPositionsTable/ApyCell.tsx @@ -2,14 +2,12 @@ import { CurrencyExt } from '@interlay/interbtc-api'; import { MonetaryAmount } from '@interlay/monetary-js'; import Big from 'big.js'; -import { formatNumber } from '@/common/utils/utils'; import { Flex } from '@/component-library'; +import { getApyLabel, getSubsidyRewardApy } from '@/utils/helpers/loans'; import { Prices } from '@/utils/hooks/api/use-get-prices'; -import { getApyLabel } from '../../utils/apy'; -import { getSubsidyRewardApy } from '../../utils/get-subsidy-rewards-apy'; -import { ApyTooltip } from '../ApyTooltip'; -import { MonetaryCell } from './MonetaryCell'; +import { Cell } from '../DataGrid'; +import { LoanApyTooltip } from '../LoanApyTooltip'; type ApyCellProps = { apy: Big; @@ -39,17 +37,9 @@ const ApyCell = ({ const earnedAsset = accumulatedDebt || earnedInterest; - const earnedAssetAmount = earnedAsset - ? formatNumber(earnedAsset?.toBig().toNumber(), { - maximumFractionDigits: earnedAsset?.currency.humanDecimals - }) - : undefined; + const earnedAssetLabel = earnedAsset ? `${earnedAsset.toHuman(8)} ${earnedAsset.currency.ticker}` : undefined; - const earnedAssetLabel = earnedAsset ? `${earnedAssetAmount} ${earnedAsset.currency.ticker}` : undefined; - - const children = ( - - ); + const children = ; if (!prices) { return children; @@ -58,7 +48,7 @@ const ApyCell = ({ // MEMO: wrapping around a Flex so tooltip is placed correctly return ( - {children} - + ); }; diff --git a/src/components/LoanPositionsTable/LoanPositionsTable.tsx b/src/components/LoanPositionsTable/LoanPositionsTable.tsx new file mode 100644 index 0000000000..5dd60b7919 --- /dev/null +++ b/src/components/LoanPositionsTable/LoanPositionsTable.tsx @@ -0,0 +1,153 @@ +import { BorrowPosition, CollateralPosition, LoanAsset, TickerToData } from '@interlay/interbtc-api'; +import { useId } from '@react-aria/utils'; +import { Key, ReactNode, useMemo } from 'react'; +import { useTranslation } from 'react-i18next'; + +import { convertMonetaryAmountToValueInUSD } from '@/common/utils/utils'; +import { Switch } from '@/component-library'; +import { LoanType } from '@/types/loans'; +import { getTokenPrice } from '@/utils/helpers/prices'; +import { useGetPrices } from '@/utils/hooks/api/use-get-prices'; + +import { AssetCell, BalanceCell, Table, TableProps } from '../DataGrid'; +import { ApyCell } from './ApyCell'; +import { LoanTablePlaceholder } from './LoanTablePlaceholder'; + +enum LoanPositionTableColumns { + ASSET = 'asset', + APY = 'apy', + AMOUNT = 'amount', + COLLATERAL = 'collateral' +} + +type LoanPositionTableRow = { + id: string; + [LoanPositionTableColumns.ASSET]: ReactNode; + [LoanPositionTableColumns.APY]: ReactNode; + [LoanPositionTableColumns.AMOUNT]: ReactNode; + [LoanPositionTableColumns.COLLATERAL]?: ReactNode; +}; + +type Props = { + variant?: LoanType; + title?: ReactNode; + assets: TickerToData; + positions: BorrowPosition[] | CollateralPosition[]; + onPressCollateralSwitch?: (ticker: string) => void; +}; + +type InheritAttrs = Omit; + +type LoanPositionsTableProps = Props & InheritAttrs; + +const LoanPositionsTable = ({ + variant = 'lend', + title, + assets, + positions, + onRowAction, + onPressCollateralSwitch, + ...props +}: LoanPositionsTableProps): JSX.Element | null => { + const titleId = useId(); + const { t } = useTranslation(); + const prices = useGetPrices(); + + const isLending = variant === 'lend'; + const showCollateral = !!onPressCollateralSwitch && isLending; + + const columns = useMemo(() => { + if (isLending) { + const lendingColumns = [ + { name: 'Asset', uid: LoanPositionTableColumns.ASSET }, + { name: 'APY / Earned', uid: LoanPositionTableColumns.APY }, + { name: 'Supplied', uid: LoanPositionTableColumns.AMOUNT } + ]; + + if (showCollateral) { + lendingColumns.push({ name: 'Collateral', uid: LoanPositionTableColumns.COLLATERAL }); + } + + return lendingColumns; + } + + return [ + { name: 'Asset', uid: LoanPositionTableColumns.ASSET }, + { name: 'APY / Accrued', uid: LoanPositionTableColumns.APY }, + { name: 'Borrowed', uid: LoanPositionTableColumns.AMOUNT } + ]; + }, [isLending, showCollateral]); + + const rows: LoanPositionTableRow[] = useMemo( + () => + positions.map(({ amount: amountProp, ...position }) => { + const { currency } = amountProp; + const asset = ; + + const { borrowApy, borrowReward, lendApy, lendReward } = assets[currency.ticker]; + + const apyCellProps = isLending + ? { apy: lendApy, rewards: lendReward } + : { + apy: borrowApy, + rewards: borrowReward, + accumulatedDebt: (position as BorrowPosition).accumulatedDebt, + isBorrow: true + }; + + const apy = ( + onRowAction?.(currency.ticker as Key)} + /> + ); + + const amountUSD = amountProp + ? convertMonetaryAmountToValueInUSD(amountProp, getTokenPrice(prices, currency.ticker)?.usd) + : 0; + + const amount = ( + + ); + + const collateral = showCollateral ? ( + onPressCollateralSwitch?.(currency.ticker)} + isSelected={(position as CollateralPosition).isCollateral} + aria-label={`toggle ${currency.ticker} collateral`} + /> + ) : undefined; + + return { + id: currency.ticker, + asset, + apy, + amount, + collateral + }; + }), + [assets, isLending, onPressCollateralSwitch, onRowAction, positions, prices, showCollateral] + ); + + return ( +
} + {...props} + /> + ); +}; + +export { LoanPositionsTable }; +export type { LoanPositionsTableProps }; diff --git a/src/components/LoanPositionsTable/LoanTablePlaceholder.tsx b/src/components/LoanPositionsTable/LoanTablePlaceholder.tsx new file mode 100644 index 0000000000..902a831b79 --- /dev/null +++ b/src/components/LoanPositionsTable/LoanTablePlaceholder.tsx @@ -0,0 +1,22 @@ +import { useTranslation } from 'react-i18next'; + +import { Flex, P, Strong } from '@/component-library'; +import { LoanType } from '@/types/loans'; + +type LoanTablePlaceholderProps = { + variant?: LoanType; +}; + +const LoanTablePlaceholder = ({ variant = 'lend' }: LoanTablePlaceholderProps): JSX.Element | null => { + const { t } = useTranslation(); + + return ( + + {t('loans.no_loan_positions', { loanType: variant })} +

{t('loans.your_loan_positions_will_show_here', { loanType: variant })}

+
+ ); +}; + +export { LoanTablePlaceholder }; +export type { LoanTablePlaceholderProps }; diff --git a/src/components/LoanPositionsTable/index.tsx b/src/components/LoanPositionsTable/index.tsx new file mode 100644 index 0000000000..32994089c7 --- /dev/null +++ b/src/components/LoanPositionsTable/index.tsx @@ -0,0 +1,4 @@ +export type { LoanPositionsTableProps } from './LoanPositionsTable'; +export { LoanPositionsTable } from './LoanPositionsTable'; +export type { LoanTablePlaceholderProps } from './LoanTablePlaceholder'; +export { LoanTablePlaceholder } from './LoanTablePlaceholder'; diff --git a/src/components/PoolsTable/PoolsTable.tsx b/src/components/PoolsTable/PoolsTable.tsx new file mode 100644 index 0000000000..3285d3ca5c --- /dev/null +++ b/src/components/PoolsTable/PoolsTable.tsx @@ -0,0 +1,114 @@ +import { LiquidityPool, LpCurrency } from '@interlay/interbtc-api'; +import { MonetaryAmount } from '@interlay/monetary-js'; +import { useId } from '@react-aria/utils'; +import { ReactNode, useMemo } from 'react'; +import { useTranslation } from 'react-i18next'; + +import { formatPercentage, formatUSD } from '@/common/utils/utils'; +import { calculateAccountLiquidityUSD, calculateTotalLiquidityUSD } from '@/pages/AMM/shared/utils'; +import { getCoinIconProps } from '@/utils/helpers/coin-icon'; +import { getFarmingApr } from '@/utils/helpers/pools'; +import { useGetPrices } from '@/utils/hooks/api/use-get-prices'; + +import { AssetCell, BalanceCell, Cell, Table, TableProps } from '../DataGrid'; + +enum PoolsTableColumns { + POOL_NAME = 'poolName', + APR = 'apr', + TOTAL_LIQUIDITY = 'totalLiquidity', + SEVEN_DAY_VOLUME = 'sevenDayVolume', + ACCOUNT_LIQUIDITY = 'accountLiquidity' +} + +type PoolsTableRow = { + id: string; + [PoolsTableColumns.POOL_NAME]: ReactNode; + [PoolsTableColumns.APR]: ReactNode; + [PoolsTableColumns.TOTAL_LIQUIDITY]: ReactNode; + // [PoolsTableColumns.SEVEN_DAY_VOLUME]: ReactNode; + [PoolsTableColumns.ACCOUNT_LIQUIDITY]?: ReactNode; +}; + +type PoolsTableProps = { + variant: 'available-pools' | 'account-pools'; + pools: Array<{ data: LiquidityPool; amount?: MonetaryAmount }>; + onRowAction?: TableProps['onRowAction']; + title?: ReactNode; +}; + +const PoolsTable = ({ variant, pools, onRowAction, title }: PoolsTableProps): JSX.Element => { + const { t } = useTranslation(); + const prices = useGetPrices(); + const titleId = useId(); + + const isAccountPools = variant === 'account-pools'; + + const commonColumns = [ + { name: t('amm.pools.pool_name'), uid: PoolsTableColumns.POOL_NAME }, + { name: t('apr'), uid: PoolsTableColumns.APR }, + { name: t('total_liquidity'), uid: PoolsTableColumns.TOTAL_LIQUIDITY } + // { name: t('7_day_volume'), uid: PoolsTableColumns.SEVEN_DAY_VOLUME } + ]; + + const borrowAssetsColumns = isAccountPools + ? [...commonColumns, { name: t('my_liquidity'), uid: PoolsTableColumns.ACCOUNT_LIQUIDITY }] + : commonColumns; + + const rows: PoolsTableRow[] = useMemo( + () => + pools.map(({ data, amount: accountLPTokenAmount }) => { + const { pooledCurrencies, lpToken, rewardAmountsYearly, totalSupply } = data; + const poolName = ; + + const totalLiquidityUSD = calculateTotalLiquidityUSD(pooledCurrencies, prices); + + const farmingApr = getFarmingApr(rewardAmountsYearly, totalSupply, totalLiquidityUSD, prices); + // TODO: add also APR from trading volume based on squid data + const aprAmount = farmingApr; + const apr = ; + + // TODO: revert alignItems prop when `sevenDayVolume` is adressed + const totalLiquidity = ( + + ); + + // TODO: uncomment and add real value when squid is ready + // const sevenDayVolume = ; + + const accountLiquidityUSD = accountLPTokenAmount + ? calculateAccountLiquidityUSD(accountLPTokenAmount, totalLiquidityUSD, totalSupply) + : 0; + + const accountLiquidity = + variant === 'account-pools' && !!accountLPTokenAmount ? ( + + ) : undefined; + + return { + id: lpToken.ticker, + poolName, + apr, + totalLiquidity, + // sevenDayVolume, + accountLiquidity + }; + }), + [isAccountPools, pools, prices, variant] + ); + + return ( +
+ ); +}; + +export { PoolsTable }; +export type { PoolsTableProps }; diff --git a/src/components/PoolsTable/index.tsx b/src/components/PoolsTable/index.tsx new file mode 100644 index 0000000000..2e9b184fea --- /dev/null +++ b/src/components/PoolsTable/index.tsx @@ -0,0 +1,2 @@ +export type { PoolsTableProps } from './PoolsTable'; +export { PoolsTable } from './PoolsTable'; diff --git a/src/components/Tokens/index.tsx b/src/components/Tokens/index.tsx deleted file mode 100644 index 569d151c59..0000000000 --- a/src/components/Tokens/index.tsx +++ /dev/null @@ -1,134 +0,0 @@ -import { GovernanceCurrency, WrappedCurrency } from '@interlay/interbtc-api'; -import * as React from 'react'; -import { withErrorBoundary } from 'react-error-boundary'; - -import { TokenType } from '@/common/types/util.types'; -import ErrorFallback from '@/components/ErrorFallback'; -import { SELECT_VARIANTS, SelectVariants } from '@/components/Select'; -import { - GOVERNANCE_TOKEN, - GOVERNANCE_TOKEN_SYMBOL, - GovernanceTokenLogoIcon, - RELAY_CHAIN_NATIVE_TOKEN, - RELAY_CHAIN_NATIVE_TOKEN_SYMBOL, - RelayChainNativeToken, - RelayChainNativeTokenLogoIcon, - WRAPPED_TOKEN, - WRAPPED_TOKEN_SYMBOL, - WrappedTokenLogoIcon -} from '@/config/relay-chains'; -import { useGetBalances } from '@/utils/hooks/api/tokens/use-get-balances'; - -import TokenSelector from './TokenSelector'; - -interface TokenOption { - token: WrappedCurrency | RelayChainNativeToken | GovernanceCurrency; - type: TokenType; - balance: string; - transferableBalance: string; - symbol: string; - icon: JSX.Element; -} - -interface Props { - variant?: SelectVariants; - callbackFunction?: (token: TokenOption) => void; - showBalances?: boolean; -} - -const Tokens = ({ variant = 'optionSelector', callbackFunction, showBalances = true }: Props): JSX.Element => { - const [tokenOptions, setTokenOptions] = React.useState | undefined>(undefined); - const [currentToken, setCurrentToken] = React.useState(undefined); - - const { data: balances } = useGetBalances(); - - const getTokenOption = React.useCallback((type: TokenType) => tokenOptions?.find((token) => token.type === type), [ - tokenOptions - ]); - - React.useEffect(() => { - if (!tokenOptions) return; - - if (!currentToken) { - // Set relay-chain native token as default - setCurrentToken(getTokenOption(TokenType.RelayChainNative)); - } - - if (callbackFunction && currentToken) { - callbackFunction(currentToken); - } - }, [tokenOptions, currentToken, getTokenOption, callbackFunction]); - - const handleUpdateToken = (tokenType: TokenType) => { - const token = getTokenOption(tokenType); - setCurrentToken(token); - - if (callbackFunction && token) { - callbackFunction(token); - } - }; - - React.useEffect(() => { - if (!balances) return; - - const tokenOptions: Array = [ - { - token: RELAY_CHAIN_NATIVE_TOKEN, - type: TokenType.RelayChainNative, - balance: balances[RELAY_CHAIN_NATIVE_TOKEN.ticker].free.toHuman(5), - transferableBalance: balances[RELAY_CHAIN_NATIVE_TOKEN.ticker].transferable.toHuman(5), - icon: , - symbol: RELAY_CHAIN_NATIVE_TOKEN_SYMBOL - }, - { - token: WRAPPED_TOKEN, - type: TokenType.Wrapped, - balance: balances[WRAPPED_TOKEN.ticker].free.toHuman(8), - transferableBalance: balances[WRAPPED_TOKEN.ticker].transferable.toHuman(8), - icon: , - symbol: WRAPPED_TOKEN_SYMBOL - }, - { - token: GOVERNANCE_TOKEN, - type: TokenType.Governance, - balance: balances[GOVERNANCE_TOKEN.ticker].free.toHuman(5), - transferableBalance: balances[GOVERNANCE_TOKEN.ticker].transferable.toHuman(5), - icon: , - symbol: GOVERNANCE_TOKEN_SYMBOL - } - ]; - - setTokenOptions(tokenOptions); - }, [balances, variant]); - - // Reset currentToken to get updated values if tokenOptions change - // while a current token is set. This will always happen because - // the token balances update after the initial render. - React.useEffect(() => { - if (!currentToken) return; - - setCurrentToken(getTokenOption(currentToken.type)); - }, [currentToken, getTokenOption, tokenOptions]); - - return ( - <> - {tokenOptions && currentToken ? ( - - ) : null} - - ); -}; - -export type { TokenOption }; -export default withErrorBoundary(Tokens, { - FallbackComponent: ErrorFallback, - onReset: () => { - window.location.reload(); - } -}); diff --git a/src/components/ViewRequestDetailsLink/index.tsx b/src/components/ViewRequestDetailsLink/index.tsx new file mode 100644 index 0000000000..17f9a86edf --- /dev/null +++ b/src/components/ViewRequestDetailsLink/index.tsx @@ -0,0 +1,21 @@ +import { shortAddress } from '@/common/utils/utils'; +import InterlayRouterLink from '@/legacy-components/UI/InterlayRouterLink'; +import { TXType } from '@/types/general.d'; +import { PAGES } from '@/utils/constants/links'; + +interface Props { + id: string; + txType: `${TXType}`; +} + +const ViewRequestDetailsLink = ({ id, txType }: Props): JSX.Element => { + const path = `${PAGES.TX}/${txType}/${id}`; + + return ( + + {shortAddress(id)} + + ); +}; + +export default ViewRequestDetailsLink; diff --git a/src/components/index.tsx b/src/components/index.tsx new file mode 100644 index 0000000000..76f6f67fba --- /dev/null +++ b/src/components/index.tsx @@ -0,0 +1,8 @@ +export type { AuthCTAProps } from './AuthCTA'; +export { AuthCTA } from './AuthCTA'; +export type { AssetCellProps, BalanceCellProps, CellProps, TableProps } from './DataGrid'; +export { AssetCell, BalanceCell, Cell, Table } from './DataGrid'; +export type { LoanPositionsTableProps } from './LoanPositionsTable'; +export { LoanPositionsTable } from './LoanPositionsTable'; +export type { PoolsTableProps } from './PoolsTable'; +export { PoolsTable } from './PoolsTable'; diff --git a/src/config/links.ts b/src/config/links.ts index ec3ca6e1ab..038a6585c8 100644 --- a/src/config/links.ts +++ b/src/config/links.ts @@ -7,9 +7,9 @@ const INTERLAY_DOCS_LINK = 'https://docs.interlay.io'; const INTERLAY_CROWDLOAN_LINK = 'https://crowdloan.interlay.io'; const KINTSUGI_CROWDLOAN_LINK = 'https://claim-kint.interlay.io'; const INTERLAY_TERMS_AND_CONDITIONS_LINK = - 'https://drive.google.com/file/d/1_oA3ZHoqzIFcE7Z-YeNTJ8g3bpeB9Glt/view?usp=sharing'; + 'https://github.com/interlay/terms/blob/master/interlay-dapp-terms-of-use.pdf'; const KINTSUGI_TERMS_AND_CONDITIONS_LINK = - 'https://drive.google.com/file/d/1TuTu49EoTdEoZX8LrysdgbDmuHS0uAvr/view?usp=sharing '; + 'https://github.com/interlay/terms/blob/master/kintsugi-dapp-terms-of-use.pdf'; const INTERLAY_USE_WRAPPED_CURRENCY_LINK = 'https://docs.interlay.io/#/interlay/earn-with-ibtc'; const KINTSUGI_USE_WRAPPED_CURRENCY_LINK = 'https://docs.interlay.io/#/kintsugi/Earn-With-kBTC'; const INTERLAY_GOVERNANCE_LINK = 'https://interlay.subsquare.io/'; diff --git a/src/config/parachain.ts b/src/config/parachain.ts index da1fade817..b906f34973 100644 --- a/src/config/parachain.ts +++ b/src/config/parachain.ts @@ -5,4 +5,25 @@ const BLOCKS_BEHIND_LIMIT = 6; const ISSUE_REDEEM_REQUEST_REFETCH_INTERVAL = 10000; // Milliseconds -export { BLOCK_TIME, BLOCKS_BEHIND_LIMIT, ISSUE_REDEEM_REQUEST_REFETCH_INTERVAL }; +// Should be the same as the ones from the parachain + +const DEFAULT_REDEEM_BRIDGE_FEE_RATE = 0.005; // Set default to 0.5% + +const DEFAULT_ISSUE_BRIDGE_FEE_RATE = 0.005; // Set default to 0.5% + +const DEFAULT_ISSUE_GRIEFING_COLLATERAL_RATE = 0.00005; // Set default to 0.005% + +const DEFAULT_ISSUE_DUST_AMOUNT = 0.00001; + +const DEFAULT_REDEEM_DUST_AMOUNT = 0.00001; + +export { + BLOCK_TIME, + BLOCKS_BEHIND_LIMIT, + DEFAULT_ISSUE_BRIDGE_FEE_RATE, + DEFAULT_ISSUE_DUST_AMOUNT, + DEFAULT_ISSUE_GRIEFING_COLLATERAL_RATE, + DEFAULT_REDEEM_BRIDGE_FEE_RATE, + DEFAULT_REDEEM_DUST_AMOUNT, + ISSUE_REDEEM_REQUEST_REFETCH_INTERVAL +}; diff --git a/src/containers/DarkModeToggle/index.tsx b/src/containers/DarkModeToggle/index.tsx deleted file mode 100644 index c6d33dc814..0000000000 --- a/src/containers/DarkModeToggle/index.tsx +++ /dev/null @@ -1,18 +0,0 @@ -import useDarkMode from 'use-dark-mode'; - -import Toggle from '@/components/Toggle'; -import { KUSAMA } from '@/utils/constants/relay-chain-names'; -import { CLASS_NAMES } from '@/utils/constants/styles'; - -// TODO: not used for now -const DarkModeToggle = (): JSX.Element => { - const darkMode = useDarkMode(process.env.REACT_APP_RELAY_CHAIN_NAME === KUSAMA, { - classNameDark: CLASS_NAMES.DARK, - classNameLight: CLASS_NAMES.LIGHT, - element: document.documentElement - }); - - return ; -}; - -export default DarkModeToggle; diff --git a/src/index.tsx b/src/index.tsx index 89f7fd7f1e..4a8ef1ba46 100644 --- a/src/index.tsx +++ b/src/index.tsx @@ -3,6 +3,7 @@ import './index.css'; import '@/component-library/theme/theme.interlay.css'; import '@/component-library/theme/theme.kintsugi.css'; +import { configGlobalBig } from '@interlay/monetary-js'; import { OverlayProvider } from '@react-aria/overlays'; import * as React from 'react'; import ReactDOM from 'react-dom'; @@ -20,40 +21,35 @@ import App from './App'; import reportWebVitals from './reportWebVitals'; import { store } from './store'; -const DeveloperConsole = React.lazy( - () => import(/* webpackChunkName: 'developer-console' */ '@/lib/substrate/components/DeveloperConsole') -); +configGlobalBig(); window.isFetchingActive = false; const queryClient = new QueryClient(); +// MEMO: temporarily removed React.StrictMode. We should add back when react-spectrum handles +// it across their library. (Issue: https://github.com/adobe/react-spectrum/issues/779#issuecomment-1353734729) ReactDOM.render( - - - - - - - - - - - - - - - - - - - - - - - - - , + + + + + + + + + + + + + + + + + + + + , document.getElementById('root') ); diff --git a/src/components/Accounts/AccountSelector/index.tsx b/src/legacy-components/Accounts/AccountSelector/index.tsx similarity index 98% rename from src/components/Accounts/AccountSelector/index.tsx rename to src/legacy-components/Accounts/AccountSelector/index.tsx index 54dc09c01c..e4603d2bc7 100644 --- a/src/components/Accounts/AccountSelector/index.tsx +++ b/src/legacy-components/Accounts/AccountSelector/index.tsx @@ -11,7 +11,7 @@ import Select, { SelectOption, SelectOptions, SelectText -} from '@/components/Select'; +} from '@/legacy-components/Select'; import { KeyringPair } from '@/lib/substrate'; interface Props { diff --git a/src/components/Accounts/index.tsx b/src/legacy-components/Accounts/index.tsx similarity index 100% rename from src/components/Accounts/index.tsx rename to src/legacy-components/Accounts/index.tsx diff --git a/src/components/AddressWithCopyUI/index.tsx b/src/legacy-components/AddressWithCopyUI/index.tsx similarity index 58% rename from src/components/AddressWithCopyUI/index.tsx rename to src/legacy-components/AddressWithCopyUI/index.tsx index a4eed28ecc..a701737dc3 100644 --- a/src/components/AddressWithCopyUI/index.tsx +++ b/src/legacy-components/AddressWithCopyUI/index.tsx @@ -1,17 +1,18 @@ import clsx from 'clsx'; import { shortAddress } from '@/common/utils/utils'; -import CopyToClipboardButton from '@/components/CopyToClipboardButton'; +import CopyToClipboardButton from '@/legacy-components/CopyToClipboardButton'; interface Props extends React.ComponentPropsWithRef<'div'> { address: string; + shortenAddress?: boolean; className?: string; } -const AddressWithCopyUI = ({ address, className, ...rest }: Props): JSX.Element => { +const AddressWithCopyUI = ({ address, shortenAddress = true, className, ...rest }: Props): JSX.Element => { return (
- {shortAddress(address)} + {shortenAddress ? shortAddress(address) : address}
); diff --git a/src/components/AvailableBalanceUI/index.tsx b/src/legacy-components/AvailableBalanceUI/index.tsx similarity index 83% rename from src/components/AvailableBalanceUI/index.tsx rename to src/legacy-components/AvailableBalanceUI/index.tsx index d8a89489c8..6fff0dc128 100644 --- a/src/components/AvailableBalanceUI/index.tsx +++ b/src/legacy-components/AvailableBalanceUI/index.tsx @@ -1,7 +1,6 @@ import clsx from 'clsx'; -import React from 'react'; -import InterlayButtonBase from '@/components/UI/InterlayButtonBase'; +import InterlayButtonBase from '@/legacy-components/UI/InterlayButtonBase'; import { KUSAMA, POLKADOT } from '@/utils/constants/relay-chain-names'; interface Props extends React.HTMLAttributes { @@ -10,11 +9,11 @@ interface Props extends React.HTMLAttributes { tokenSymbol: string; } -const AvailableBalanceUI = ({ balance, label, tokenSymbol, onClick, className, ...props }: Props): JSX.Element => { +const AvailableBalanceUI = ({ balance, label, tokenSymbol, onClick, className, ...rest }: Props): JSX.Element => { const Balance = onClick ? InterlayButtonBase : 'span'; return ( -
+
{ diff --git a/src/components/FormTitle/index.tsx b/src/legacy-components/FormTitle/index.tsx similarity index 100% rename from src/components/FormTitle/index.tsx rename to src/legacy-components/FormTitle/index.tsx diff --git a/src/components/FullLoadingSpinner/index.tsx b/src/legacy-components/FullLoadingSpinner/index.tsx similarity index 100% rename from src/components/FullLoadingSpinner/index.tsx rename to src/legacy-components/FullLoadingSpinner/index.tsx diff --git a/src/components/IssueUI/BTCPaymentPendingStatusUI/index.tsx b/src/legacy-components/IssueUI/BTCPaymentPendingStatusUI/index.tsx similarity index 95% rename from src/components/IssueUI/BTCPaymentPendingStatusUI/index.tsx rename to src/legacy-components/IssueUI/BTCPaymentPendingStatusUI/index.tsx index ef393da65b..94ba0e29e1 100644 --- a/src/components/IssueUI/BTCPaymentPendingStatusUI/index.tsx +++ b/src/legacy-components/IssueUI/BTCPaymentPendingStatusUI/index.tsx @@ -7,9 +7,9 @@ import { useSelector } from 'react-redux'; import { StoreType } from '@/common/types/util.types'; import { displayMonetaryAmountInUSDFormat } from '@/common/utils/utils'; -import AddressWithCopyUI from '@/components/AddressWithCopyUI'; -import Timer from '@/components/Timer'; import { BLOCK_TIME } from '@/config/parachain'; +import AddressWithCopyUI from '@/legacy-components/AddressWithCopyUI'; +import Timer from '@/legacy-components/Timer'; import { ForeignAssetIdLiteral } from '@/types/currency'; import { KUSAMA, POLKADOT } from '@/utils/constants/relay-chain-names'; import { getColorShade } from '@/utils/helpers/colors'; @@ -94,7 +94,11 @@ const BTCPaymentPendingStatusUI = ({ request }: Props): JSX.Element => { > {t('issue_page.single_transaction')}

- + {initialLeftSeconds && (

{ + const { selectedAccount } = useSubstrateSecureState(); + const { t } = useTranslation(); const queryParams = useQueryParams(); @@ -75,8 +79,7 @@ const ManualIssueExecutionUI = ({ request }: Props): JSX.Element => { const issue = await window.bridge.issue.getRequestById(request.id); const collateralToken = await currencyIdToMonetaryCurrency( - window.bridge.assetRegistry, - window.bridge.loans, + window.bridge.api, issue.vaultId.currencies.collateral ); @@ -100,6 +103,8 @@ const ManualIssueExecutionUI = ({ request }: Props): JSX.Element => { executable = true; } + const isOwner = request.userParachainAddress === selectedAccount?.address; + return (

{vaultCapacity && ( @@ -129,7 +134,7 @@ const ManualIssueExecutionUI = ({ request }: Props): JSX.Element => { {t('issue_page.claim_interbtc', { diff --git a/src/components/IssueUI/IssueRequestStatusUI/ReceivedIssueRequest/index.tsx b/src/legacy-components/IssueUI/IssueRequestStatusUI/ReceivedIssueRequest/index.tsx similarity index 93% rename from src/components/IssueUI/IssueRequestStatusUI/ReceivedIssueRequest/index.tsx rename to src/legacy-components/IssueUI/IssueRequestStatusUI/ReceivedIssueRequest/index.tsx index 66f6623e88..c824c7e26f 100644 --- a/src/components/IssueUI/IssueRequestStatusUI/ReceivedIssueRequest/index.tsx +++ b/src/legacy-components/IssueUI/IssueRequestStatusUI/ReceivedIssueRequest/index.tsx @@ -3,11 +3,11 @@ import { useErrorHandler, withErrorBoundary } from 'react-error-boundary'; import { useTranslation } from 'react-i18next'; import { formatNumber } from '@/common/utils/utils'; -import AddressWithCopyUI from '@/components/AddressWithCopyUI'; -import ErrorFallback from '@/components/ErrorFallback'; -import ExternalLink from '@/components/ExternalLink'; -import Ring48, { Ring48Title, Ring48Value } from '@/components/Ring48'; import { BTC_EXPLORER_TRANSACTION_API } from '@/config/blockstream-explorer-links'; +import AddressWithCopyUI from '@/legacy-components/AddressWithCopyUI'; +import ErrorFallback from '@/legacy-components/ErrorFallback'; +import ExternalLink from '@/legacy-components/ExternalLink'; +import Ring48, { Ring48Title, Ring48Value } from '@/legacy-components/Ring48'; import RequestWrapper from '@/pages/Bridge/RequestWrapper'; import useCurrentActiveBlockNumber from '@/services/hooks/use-current-active-block-number'; import useStableBitcoinConfirmations from '@/services/hooks/use-stable-bitcoin-confirmations'; diff --git a/src/components/IssueUI/IssueRequestStatusUI/index.tsx b/src/legacy-components/IssueUI/IssueRequestStatusUI/index.tsx similarity index 100% rename from src/components/IssueUI/IssueRequestStatusUI/index.tsx rename to src/legacy-components/IssueUI/IssueRequestStatusUI/index.tsx diff --git a/src/components/IssueUI/WhoopsStatusUI/index.tsx b/src/legacy-components/IssueUI/WhoopsStatusUI/index.tsx similarity index 96% rename from src/components/IssueUI/WhoopsStatusUI/index.tsx rename to src/legacy-components/IssueUI/WhoopsStatusUI/index.tsx index 357f40fd21..922f9b5e30 100644 --- a/src/components/IssueUI/WhoopsStatusUI/index.tsx +++ b/src/legacy-components/IssueUI/WhoopsStatusUI/index.tsx @@ -5,10 +5,10 @@ import { useTranslation } from 'react-i18next'; import { ReactComponent as BitcoinLogoIcon } from '@/assets/img/bitcoin-logo.svg'; import { displayMonetaryAmount, displayMonetaryAmountInUSDFormat } from '@/common/utils/utils'; -import AddressWithCopyUI from '@/components/AddressWithCopyUI'; -import Hr2 from '@/components/hrs/Hr2'; -import PriceInfo from '@/components/PriceInfo'; import { WRAPPED_TOKEN_SYMBOL } from '@/config/relay-chains'; +import AddressWithCopyUI from '@/legacy-components/AddressWithCopyUI'; +import Hr2 from '@/legacy-components/hrs/Hr2'; +import PriceInfo from '@/legacy-components/PriceInfo'; import RequestWrapper from '@/pages/Bridge/RequestWrapper'; import { ForeignAssetIdLiteral } from '@/types/currency'; import { KUSAMA, POLKADOT } from '@/utils/constants/relay-chain-names'; diff --git a/src/components/IssueUI/index.tsx b/src/legacy-components/IssueUI/index.tsx similarity index 97% rename from src/components/IssueUI/index.tsx rename to src/legacy-components/IssueUI/index.tsx index b1da07a4ee..2158916386 100644 --- a/src/components/IssueUI/index.tsx +++ b/src/legacy-components/IssueUI/index.tsx @@ -4,10 +4,10 @@ import { useTranslation } from 'react-i18next'; import { ReactComponent as BitcoinLogoIcon } from '@/assets/img/bitcoin-logo.svg'; import { displayMonetaryAmountInUSDFormat, formatNumber } from '@/common/utils/utils'; -import AddressWithCopyUI from '@/components/AddressWithCopyUI'; -import Hr2 from '@/components/hrs/Hr2'; -import PriceInfo from '@/components/PriceInfo'; import { WRAPPED_TOKEN_SYMBOL, WrappedTokenAmount } from '@/config/relay-chains'; +import AddressWithCopyUI from '@/legacy-components/AddressWithCopyUI'; +import Hr2 from '@/legacy-components/hrs/Hr2'; +import PriceInfo from '@/legacy-components/PriceInfo'; import { ForeignAssetIdLiteral } from '@/types/currency'; import { KUSAMA, POLKADOT } from '@/utils/constants/relay-chain-names'; import { getColorShade } from '@/utils/helpers/colors'; diff --git a/src/components/NumberInput/index.tsx b/src/legacy-components/NumberInput/index.tsx similarity index 92% rename from src/components/NumberInput/index.tsx rename to src/legacy-components/NumberInput/index.tsx index 56ab666f3d..d04e47d87b 100644 --- a/src/components/NumberInput/index.tsx +++ b/src/legacy-components/NumberInput/index.tsx @@ -1,6 +1,6 @@ import * as React from 'react'; -import InterlayInput, { Props as InterlayInputProps } from '@/components/UI/InterlayInput'; +import InterlayInput, { Props as InterlayInputProps } from '@/legacy-components/UI/InterlayInput'; // `onWheel` prop can't be used with `preventDefault` because // React implements passive event listeners. diff --git a/src/components/Panel/index.tsx b/src/legacy-components/Panel/index.tsx similarity index 100% rename from src/components/Panel/index.tsx rename to src/legacy-components/Panel/index.tsx diff --git a/src/components/PriceInfo/index.tsx b/src/legacy-components/PriceInfo/index.tsx similarity index 84% rename from src/components/PriceInfo/index.tsx rename to src/legacy-components/PriceInfo/index.tsx index 385a461461..a3ab02a879 100644 --- a/src/components/PriceInfo/index.tsx +++ b/src/legacy-components/PriceInfo/index.tsx @@ -10,10 +10,20 @@ interface Props { approxUSD: string; // TODO: should be number tooltip?: JSX.Element; className?: string; + dataTestId?: string; } -const PriceInfo = ({ title, unitIcon, value, unitName, approxUSD, tooltip, className }: Props): JSX.Element => ( -
+const PriceInfo = ({ + title, + unitIcon, + value, + unitName, + approxUSD, + tooltip, + className, + dataTestId +}: Props): JSX.Element => ( +
{title} {tooltip} diff --git a/src/components/PrimaryColorEllipsisLoader/index.tsx b/src/legacy-components/PrimaryColorEllipsisLoader/index.tsx similarity index 88% rename from src/components/PrimaryColorEllipsisLoader/index.tsx rename to src/legacy-components/PrimaryColorEllipsisLoader/index.tsx index 8cd1c2d098..95c338e958 100644 --- a/src/components/PrimaryColorEllipsisLoader/index.tsx +++ b/src/legacy-components/PrimaryColorEllipsisLoader/index.tsx @@ -1,6 +1,6 @@ import clsx from 'clsx'; -import EllipsisLoader from '@/components/EllipsisLoader'; +import EllipsisLoader from '@/legacy-components/EllipsisLoader'; import { KUSAMA, POLKADOT } from '@/utils/constants/relay-chain-names'; const PrimaryColorEllipsisLoader = (): JSX.Element => ( diff --git a/src/components/PrimaryColorSpan/index.tsx b/src/legacy-components/PrimaryColorSpan/index.tsx similarity index 100% rename from src/components/PrimaryColorSpan/index.tsx rename to src/legacy-components/PrimaryColorSpan/index.tsx diff --git a/src/components/RedeemUI/RedeemRequestStatusUI/CompletedRedeemRequest/index.tsx b/src/legacy-components/RedeemUI/RedeemRequestStatusUI/CompletedRedeemRequest/index.tsx similarity index 88% rename from src/components/RedeemUI/RedeemRequestStatusUI/CompletedRedeemRequest/index.tsx rename to src/legacy-components/RedeemUI/RedeemRequestStatusUI/CompletedRedeemRequest/index.tsx index 791fae85cb..d7efad9ffc 100644 --- a/src/components/RedeemUI/RedeemRequestStatusUI/CompletedRedeemRequest/index.tsx +++ b/src/legacy-components/RedeemUI/RedeemRequestStatusUI/CompletedRedeemRequest/index.tsx @@ -2,11 +2,11 @@ import clsx from 'clsx'; import { useTranslation } from 'react-i18next'; import { formatNumber, getPolkadotLink } from '@/common/utils/utils'; -import AddressWithCopyUI from '@/components/AddressWithCopyUI'; -import ExternalLink from '@/components/ExternalLink'; -import PrimaryColorSpan from '@/components/PrimaryColorSpan'; -import Ring48, { Ring48Title, Ring48Value } from '@/components/Ring48'; import { BTC_EXPLORER_TRANSACTION_API } from '@/config/blockstream-explorer-links'; +import AddressWithCopyUI from '@/legacy-components/AddressWithCopyUI'; +import ExternalLink from '@/legacy-components/ExternalLink'; +import PrimaryColorSpan from '@/legacy-components/PrimaryColorSpan'; +import Ring48, { Ring48Title, Ring48Value } from '@/legacy-components/Ring48'; import RequestWrapper from '@/pages/Bridge/RequestWrapper'; import { KUSAMA, POLKADOT } from '@/utils/constants/relay-chain-names'; import { getColorShade } from '@/utils/helpers/colors'; diff --git a/src/components/RedeemUI/RedeemRequestStatusUI/DefaultRedeemRequest/index.tsx b/src/legacy-components/RedeemUI/RedeemRequestStatusUI/DefaultRedeemRequest/index.tsx similarity index 95% rename from src/components/RedeemUI/RedeemRequestStatusUI/DefaultRedeemRequest/index.tsx rename to src/legacy-components/RedeemUI/RedeemRequestStatusUI/DefaultRedeemRequest/index.tsx index cf3be0d7d6..a95aa49ae7 100644 --- a/src/components/RedeemUI/RedeemRequestStatusUI/DefaultRedeemRequest/index.tsx +++ b/src/legacy-components/RedeemUI/RedeemRequestStatusUI/DefaultRedeemRequest/index.tsx @@ -3,8 +3,8 @@ import { useErrorHandler } from 'react-error-boundary'; import { useTranslation } from 'react-i18next'; import { formatNumber } from '@/common/utils/utils'; -import AddressWithCopyUI from '@/components/AddressWithCopyUI'; -import Ring48, { Ring48Title, Ring48Value } from '@/components/Ring48'; +import AddressWithCopyUI from '@/legacy-components/AddressWithCopyUI'; +import Ring48, { Ring48Title, Ring48Value } from '@/legacy-components/Ring48'; import RequestWrapper from '@/pages/Bridge/RequestWrapper'; import useCurrentActiveBlockNumber from '@/services/hooks/use-current-active-block-number'; import useStableBitcoinConfirmations from '@/services/hooks/use-stable-bitcoin-confirmations'; diff --git a/src/components/RedeemUI/RedeemRequestStatusUI/PendingWithBtcTxNotFoundRedeemRequest/index.tsx b/src/legacy-components/RedeemUI/RedeemRequestStatusUI/PendingWithBtcTxNotFoundRedeemRequest/index.tsx similarity index 95% rename from src/components/RedeemUI/RedeemRequestStatusUI/PendingWithBtcTxNotFoundRedeemRequest/index.tsx rename to src/legacy-components/RedeemUI/RedeemRequestStatusUI/PendingWithBtcTxNotFoundRedeemRequest/index.tsx index 117a01294f..7eeac859db 100644 --- a/src/components/RedeemUI/RedeemRequestStatusUI/PendingWithBtcTxNotFoundRedeemRequest/index.tsx +++ b/src/legacy-components/RedeemUI/RedeemRequestStatusUI/PendingWithBtcTxNotFoundRedeemRequest/index.tsx @@ -4,9 +4,9 @@ import { useTranslation } from 'react-i18next'; import { useSelector } from 'react-redux'; import { StoreType } from '@/common/types/util.types'; -import Ring48, { Ring48Title, Ring48Value } from '@/components/Ring48'; -import Timer from '@/components/Timer'; import { BLOCK_TIME } from '@/config/parachain'; +import Ring48, { Ring48Title, Ring48Value } from '@/legacy-components/Ring48'; +import Timer from '@/legacy-components/Timer'; import RequestWrapper from '@/pages/Bridge/RequestWrapper'; import { KUSAMA, POLKADOT } from '@/utils/constants/relay-chain-names'; diff --git a/src/components/RedeemUI/RedeemRequestStatusUI/ReimbursedRedeemRequest/index.tsx b/src/legacy-components/RedeemUI/RedeemRequestStatusUI/ReimbursedRedeemRequest/index.tsx similarity index 96% rename from src/components/RedeemUI/RedeemRequestStatusUI/ReimbursedRedeemRequest/index.tsx rename to src/legacy-components/RedeemUI/RedeemRequestStatusUI/ReimbursedRedeemRequest/index.tsx index 1a32a25c66..ccc50e5649 100644 --- a/src/components/RedeemUI/RedeemRequestStatusUI/ReimbursedRedeemRequest/index.tsx +++ b/src/legacy-components/RedeemUI/RedeemRequestStatusUI/ReimbursedRedeemRequest/index.tsx @@ -9,17 +9,17 @@ import { useSelector } from 'react-redux'; import { StoreType } from '@/common/types/util.types'; import { displayMonetaryAmount, displayMonetaryAmountInUSDFormat, getPolkadotLink } from '@/common/utils/utils'; -import ErrorFallback from '@/components/ErrorFallback'; -import ExternalLink from '@/components/ExternalLink'; -import Hr2 from '@/components/hrs/Hr2'; -import PriceInfo from '@/components/PriceInfo'; -import PrimaryColorSpan from '@/components/PrimaryColorSpan'; import { RELAY_CHAIN_NATIVE_TOKEN, RELAY_CHAIN_NATIVE_TOKEN_SYMBOL, RelayChainNativeTokenLogoIcon, WRAPPED_TOKEN_SYMBOL } from '@/config/relay-chains'; +import ErrorFallback from '@/legacy-components/ErrorFallback'; +import ExternalLink from '@/legacy-components/ExternalLink'; +import Hr2 from '@/legacy-components/hrs/Hr2'; +import PriceInfo from '@/legacy-components/PriceInfo'; +import PrimaryColorSpan from '@/legacy-components/PrimaryColorSpan'; import RequestWrapper from '@/pages/Bridge/RequestWrapper'; import { ForeignAssetIdLiteral } from '@/types/currency'; import { KUSAMA, POLKADOT } from '@/utils/constants/relay-chain-names'; diff --git a/src/components/RedeemUI/RedeemRequestStatusUI/RetriedRedeemRequest/index.tsx b/src/legacy-components/RedeemUI/RedeemRequestStatusUI/RetriedRedeemRequest/index.tsx similarity index 95% rename from src/components/RedeemUI/RedeemRequestStatusUI/RetriedRedeemRequest/index.tsx rename to src/legacy-components/RedeemUI/RedeemRequestStatusUI/RetriedRedeemRequest/index.tsx index 637387438c..747b97f1c9 100644 --- a/src/components/RedeemUI/RedeemRequestStatusUI/RetriedRedeemRequest/index.tsx +++ b/src/legacy-components/RedeemUI/RedeemRequestStatusUI/RetriedRedeemRequest/index.tsx @@ -9,16 +9,16 @@ import { useSelector } from 'react-redux'; import { StoreType } from '@/common/types/util.types'; import { displayMonetaryAmount, displayMonetaryAmountInUSDFormat, getPolkadotLink } from '@/common/utils/utils'; -import ErrorFallback from '@/components/ErrorFallback'; -import ExternalLink from '@/components/ExternalLink'; -import Hr2 from '@/components/hrs/Hr2'; -import PriceInfo from '@/components/PriceInfo'; -import PrimaryColorSpan from '@/components/PrimaryColorSpan'; import { RELAY_CHAIN_NATIVE_TOKEN, RELAY_CHAIN_NATIVE_TOKEN_SYMBOL, RelayChainNativeTokenLogoIcon } from '@/config/relay-chains'; +import ErrorFallback from '@/legacy-components/ErrorFallback'; +import ExternalLink from '@/legacy-components/ExternalLink'; +import Hr2 from '@/legacy-components/hrs/Hr2'; +import PriceInfo from '@/legacy-components/PriceInfo'; +import PrimaryColorSpan from '@/legacy-components/PrimaryColorSpan'; import RequestWrapper from '@/pages/Bridge/RequestWrapper'; import { KUSAMA, POLKADOT } from '@/utils/constants/relay-chain-names'; import { getColorShade } from '@/utils/helpers/colors'; diff --git a/src/components/RedeemUI/RedeemRequestStatusUI/index.tsx b/src/legacy-components/RedeemUI/RedeemRequestStatusUI/index.tsx similarity index 100% rename from src/components/RedeemUI/RedeemRequestStatusUI/index.tsx rename to src/legacy-components/RedeemUI/RedeemRequestStatusUI/index.tsx diff --git a/src/components/RedeemUI/ReimburseStatusUI/index.tsx b/src/legacy-components/RedeemUI/ReimburseStatusUI/index.tsx similarity index 96% rename from src/components/RedeemUI/ReimburseStatusUI/index.tsx rename to src/legacy-components/RedeemUI/ReimburseStatusUI/index.tsx index dbead5ce38..8125d52d69 100644 --- a/src/components/RedeemUI/ReimburseStatusUI/index.tsx +++ b/src/legacy-components/RedeemUI/ReimburseStatusUI/index.tsx @@ -11,11 +11,11 @@ import { toast } from 'react-toastify'; import { StoreType } from '@/common/types/util.types'; import { displayMonetaryAmount, displayMonetaryAmountInUSDFormat } from '@/common/utils/utils'; -import InterlayConiferOutlinedButton from '@/components/buttons/InterlayConiferOutlinedButton'; -import InterlayDenimOrKintsugiMidnightOutlinedButton from '@/components/buttons/InterlayDenimOrKintsugiMidnightOutlinedButton'; -import ErrorFallback from '@/components/ErrorFallback'; -import PrimaryColorSpan from '@/components/PrimaryColorSpan'; import { RELAY_CHAIN_NATIVE_TOKEN, RELAY_CHAIN_NATIVE_TOKEN_SYMBOL, WRAPPED_TOKEN_SYMBOL } from '@/config/relay-chains'; +import InterlayConiferOutlinedButton from '@/legacy-components/buttons/InterlayConiferOutlinedButton'; +import InterlayDenimOrKintsugiMidnightOutlinedButton from '@/legacy-components/buttons/InterlayDenimOrKintsugiMidnightOutlinedButton'; +import ErrorFallback from '@/legacy-components/ErrorFallback'; +import PrimaryColorSpan from '@/legacy-components/PrimaryColorSpan'; import { useSubstrateSecureState } from '@/lib/substrate'; import RequestWrapper from '@/pages/Bridge/RequestWrapper'; import { REDEEMS_FETCHER } from '@/services/fetchers/redeems-fetcher'; diff --git a/src/components/RedeemUI/index.tsx b/src/legacy-components/RedeemUI/index.tsx similarity index 96% rename from src/components/RedeemUI/index.tsx rename to src/legacy-components/RedeemUI/index.tsx index 7769af19b0..34820878c5 100644 --- a/src/components/RedeemUI/index.tsx +++ b/src/legacy-components/RedeemUI/index.tsx @@ -4,11 +4,11 @@ import { useTranslation } from 'react-i18next'; import { ReactComponent as BitcoinLogoIcon } from '@/assets/img/bitcoin-logo.svg'; import { displayMonetaryAmountInUSDFormat, formatNumber } from '@/common/utils/utils'; -import AddressWithCopyUI from '@/components/AddressWithCopyUI'; -import Hr2 from '@/components/hrs/Hr2'; -import PriceInfo from '@/components/PriceInfo'; -import PrimaryColorSpan from '@/components/PrimaryColorSpan'; import { WRAPPED_TOKEN_SYMBOL } from '@/config/relay-chains'; +import AddressWithCopyUI from '@/legacy-components/AddressWithCopyUI'; +import Hr2 from '@/legacy-components/hrs/Hr2'; +import PriceInfo from '@/legacy-components/PriceInfo'; +import PrimaryColorSpan from '@/legacy-components/PrimaryColorSpan'; import { ForeignAssetIdLiteral } from '@/types/currency'; import { KUSAMA, POLKADOT } from '@/utils/constants/relay-chain-names'; import { getTokenPrice } from '@/utils/helpers/prices'; diff --git a/src/components/Ring48/index.tsx b/src/legacy-components/Ring48/index.tsx similarity index 100% rename from src/components/Ring48/index.tsx rename to src/legacy-components/Ring48/index.tsx diff --git a/src/components/Ring64/index.tsx b/src/legacy-components/Ring64/index.tsx similarity index 100% rename from src/components/Ring64/index.tsx rename to src/legacy-components/Ring64/index.tsx diff --git a/src/components/Select/index.tsx b/src/legacy-components/Select/index.tsx similarity index 100% rename from src/components/Select/index.tsx rename to src/legacy-components/Select/index.tsx diff --git a/src/components/SubmitButton/index.tsx b/src/legacy-components/SubmitButton/index.tsx similarity index 84% rename from src/components/SubmitButton/index.tsx rename to src/legacy-components/SubmitButton/index.tsx index 3f1f37931d..246d6ce00f 100644 --- a/src/components/SubmitButton/index.tsx +++ b/src/legacy-components/SubmitButton/index.tsx @@ -2,7 +2,7 @@ import clsx from 'clsx'; import InterlayDenimOrKintsugiSupernovaContainedButton, { Props as InterlayDenimOrKintsugiMidnightContainedButtonProps -} from '@/components/buttons/InterlayDenimOrKintsugiSupernovaContainedButton'; +} from '@/legacy-components/buttons/InterlayDenimOrKintsugiSupernovaContainedButton'; const SubmitButton = ({ className, ...rest }: InterlayDenimOrKintsugiMidnightContainedButtonProps): JSX.Element => ( ( {...rest} /> ): JSX.Element => ( -

+

); export { TextFieldContainer, TextFieldHelperText, TextFieldLabel }; diff --git a/src/components/Timer/index.tsx b/src/legacy-components/Timer/index.tsx similarity index 100% rename from src/components/Timer/index.tsx rename to src/legacy-components/Timer/index.tsx diff --git a/src/components/TitleWithUnderline/index.tsx b/src/legacy-components/TitleWithUnderline/index.tsx similarity index 92% rename from src/components/TitleWithUnderline/index.tsx rename to src/legacy-components/TitleWithUnderline/index.tsx index 81dfcd051c..aca6c55c91 100644 --- a/src/components/TitleWithUnderline/index.tsx +++ b/src/legacy-components/TitleWithUnderline/index.tsx @@ -1,6 +1,6 @@ import clsx from 'clsx'; -import Hr1 from '@/components/hrs/Hr1'; +import Hr1 from '@/legacy-components/hrs/Hr1'; import { KUSAMA, POLKADOT } from '@/utils/constants/relay-chain-names'; interface Props { diff --git a/src/components/Toggle/index.tsx b/src/legacy-components/Toggle/index.tsx similarity index 100% rename from src/components/Toggle/index.tsx rename to src/legacy-components/Toggle/index.tsx diff --git a/src/components/TokenField/index.tsx b/src/legacy-components/TokenField/index.tsx similarity index 93% rename from src/components/TokenField/index.tsx rename to src/legacy-components/TokenField/index.tsx index f74d5f6acb..fe0a682295 100644 --- a/src/components/TokenField/index.tsx +++ b/src/legacy-components/TokenField/index.tsx @@ -1,8 +1,8 @@ import clsx from 'clsx'; import * as React from 'react'; -import NumberInput, { Props as NumberInputProps } from '@/components/NumberInput'; -import { TextFieldContainer, TextFieldHelperText, TextFieldLabel } from '@/components/TextField'; +import NumberInput, { Props as NumberInputProps } from '@/legacy-components/NumberInput'; +import { TextFieldContainer, TextFieldHelperText, TextFieldLabel } from '@/legacy-components/TextField'; import { KUSAMA, POLKADOT } from '@/utils/constants/relay-chain-names'; interface CustomProps { @@ -33,6 +33,7 @@ const TokenField = React.forwardRef( {...rest} /> @@ -54,6 +55,7 @@ const TokenField = React.forwardRef( void; + fullWidth?: boolean; } -const TokenSelector = ({ variant, tokenOptions, currentToken, onChange, showBalances }: Props): JSX.Element => { +const TokenSelector = ({ + variant, + tokenOptions, + currentToken, + onChange, + showBalances, + fullWidth +}: Props): JSX.Element => { return ( <> {currentToken && ( - {({ open }) => ( <> - + {showBalances && ( - {formatNumber(Number(currentToken.balance), { + {formatNumber(Number(currentToken.transferableBalance), { minimumFractionDigits: 0, maximumFractionDigits: 8 })} @@ -59,7 +67,7 @@ const TokenSelector = ({ variant, tokenOptions, currentToken, onChange, showBala {tokenOptions.map((tokenOption: TokenOption) => { return ( - + {({ selected, active }) => ( <>

{showBalances && - formatNumber(Number(tokenOption.balance), { + formatNumber(Number(tokenOption.transferableBalance), { minimumFractionDigits: 0, maximumFractionDigits: 8 })}{' '} diff --git a/src/legacy-components/Tokens/index.tsx b/src/legacy-components/Tokens/index.tsx new file mode 100644 index 0000000000..24460b15d7 --- /dev/null +++ b/src/legacy-components/Tokens/index.tsx @@ -0,0 +1,122 @@ +import { CurrencyExt } from '@interlay/interbtc-api'; +import clsx from 'clsx'; +import * as React from 'react'; +import { withErrorBoundary } from 'react-error-boundary'; + +import { TokenType } from '@/common/types/util.types'; +import { CoinIcon } from '@/component-library'; +import { RELAY_CHAIN_NATIVE_TOKEN } from '@/config/relay-chains'; +import ErrorFallback from '@/legacy-components/ErrorFallback'; +import { SelectVariants } from '@/legacy-components/Select'; +import { getCoinIconProps } from '@/utils/helpers/coin-icon'; +import { useGetBalances } from '@/utils/hooks/api/tokens/use-get-balances'; + +import TokenSelector from './TokenSelector'; + +interface TokenOption { + token: CurrencyExt; + balance: string; + transferableBalance: string; + symbol: string; + icon: JSX.Element; +} + +interface Props { + variant?: SelectVariants; + callbackFunction?: (token: TokenOption) => void; + showBalances?: boolean; + tickers?: Array; + label?: string; + fullWidth?: boolean; +} + +const Tokens = ({ + variant = 'optionSelector', + callbackFunction, + showBalances = true, + tickers, + label, + fullWidth = false +}: Props): JSX.Element => { + const [tokenOptions, setTokenOptions] = React.useState | undefined>(undefined); + const [currentToken, setCurrentToken] = React.useState(undefined); + + const { data: balances, getBalance } = useGetBalances(); + + const getTokenOption = React.useCallback((ticker) => tokenOptions?.find((token) => token.symbol === ticker), [ + tokenOptions + ]); + + React.useEffect(() => { + if (!tokenOptions) return; + + if (!currentToken) { + // Set default + setCurrentToken(tickers ? getTokenOption(tickers[0]) : getTokenOption(RELAY_CHAIN_NATIVE_TOKEN.ticker)); + } + + if (callbackFunction && currentToken) { + callbackFunction(currentToken); + } + }, [tokenOptions, currentToken, getTokenOption, callbackFunction, tickers]); + + const handleUpdateToken = (tokenType: TokenType) => { + const token = getTokenOption(tokenType); + setCurrentToken(token); + + if (callbackFunction && token) { + callbackFunction(token); + } + }; + + React.useEffect(() => { + if (!balances) return; + + const filteredBalances = tickers + ? Object.values(balances).filter((value) => tickers?.includes(value.currency.ticker)) + : balances; + + const tokenOptions: Array = Object.values(filteredBalances).map((balance) => ({ + token: balance.currency, + balance: getBalance(balance.currency.ticker)?.free.toHuman(5) || '0', + transferableBalance: getBalance(balance.currency.ticker)?.transferable.toHuman(5) || '0', + icon: , + symbol: balance.currency.ticker + })); + + setTokenOptions(tokenOptions); + }, [balances, getBalance, variant, tickers]); + + // Reset currentToken to get updated values if tokenOptions change + // while a current token is set. This will always happen because + // the token balances update after the initial render. + React.useEffect(() => { + if (!currentToken) return; + + setCurrentToken(getTokenOption(currentToken.symbol)); + }, [currentToken, getTokenOption, tokenOptions]); + + return ( +
+ {label && } + {tokenOptions && currentToken ? ( + + ) : null} +
+ ); +}; + +export type { TokenOption }; +export default withErrorBoundary(Tokens, { + FallbackComponent: ErrorFallback, + onReset: () => { + window.location.reload(); + } +}); diff --git a/src/components/UI/InterlayButton/index.tsx b/src/legacy-components/UI/InterlayButton/index.tsx similarity index 98% rename from src/components/UI/InterlayButton/index.tsx rename to src/legacy-components/UI/InterlayButton/index.tsx index 6dfa7b4a8c..7b3d46128c 100644 --- a/src/components/UI/InterlayButton/index.tsx +++ b/src/legacy-components/UI/InterlayButton/index.tsx @@ -1,7 +1,7 @@ import clsx from 'clsx'; import { ReactComponent as SpinIcon } from '@/assets/img/icons/spin.svg'; -import InterlayButtonBase, { Props as InterlayButtonBaseProps } from '@/components/UI/InterlayButtonBase'; +import InterlayButtonBase, { Props as InterlayButtonBaseProps } from '@/legacy-components/UI/InterlayButtonBase'; const VARIANTS = Object.freeze({ contained: 'contained', diff --git a/src/components/UI/InterlayButtonBase/index.tsx b/src/legacy-components/UI/InterlayButtonBase/index.tsx similarity index 100% rename from src/components/UI/InterlayButtonBase/index.tsx rename to src/legacy-components/UI/InterlayButtonBase/index.tsx diff --git a/src/components/UI/InterlayImage/index.tsx b/src/legacy-components/UI/InterlayImage/index.tsx similarity index 100% rename from src/components/UI/InterlayImage/index.tsx rename to src/legacy-components/UI/InterlayImage/index.tsx diff --git a/src/components/UI/InterlayInput/index.tsx b/src/legacy-components/UI/InterlayInput/index.tsx similarity index 100% rename from src/components/UI/InterlayInput/index.tsx rename to src/legacy-components/UI/InterlayInput/index.tsx diff --git a/src/components/UI/InterlayInput/interlay-input.module.css b/src/legacy-components/UI/InterlayInput/interlay-input.module.css similarity index 100% rename from src/components/UI/InterlayInput/interlay-input.module.css rename to src/legacy-components/UI/InterlayInput/interlay-input.module.css diff --git a/src/components/UI/InterlayLink/index.tsx b/src/legacy-components/UI/InterlayLink/index.tsx similarity index 100% rename from src/components/UI/InterlayLink/index.tsx rename to src/legacy-components/UI/InterlayLink/index.tsx diff --git a/src/components/UI/InterlayModal/index.tsx b/src/legacy-components/UI/InterlayModal/index.tsx similarity index 100% rename from src/components/UI/InterlayModal/index.tsx rename to src/legacy-components/UI/InterlayModal/index.tsx diff --git a/src/components/UI/InterlayPagination/index.tsx b/src/legacy-components/UI/InterlayPagination/index.tsx similarity index 100% rename from src/components/UI/InterlayPagination/index.tsx rename to src/legacy-components/UI/InterlayPagination/index.tsx diff --git a/src/components/UI/InterlayRadioGroup/index.tsx b/src/legacy-components/UI/InterlayRadioGroup/index.tsx similarity index 100% rename from src/components/UI/InterlayRadioGroup/index.tsx rename to src/legacy-components/UI/InterlayRadioGroup/index.tsx diff --git a/src/components/UI/InterlayRouterLink/index.tsx b/src/legacy-components/UI/InterlayRouterLink/index.tsx similarity index 100% rename from src/components/UI/InterlayRouterLink/index.tsx rename to src/legacy-components/UI/InterlayRouterLink/index.tsx diff --git a/src/components/UI/InterlayRouterNavLink/index.tsx b/src/legacy-components/UI/InterlayRouterNavLink/index.tsx similarity index 100% rename from src/components/UI/InterlayRouterNavLink/index.tsx rename to src/legacy-components/UI/InterlayRouterNavLink/index.tsx diff --git a/src/components/UI/InterlayTabGroup/index.tsx b/src/legacy-components/UI/InterlayTabGroup/index.tsx similarity index 100% rename from src/components/UI/InterlayTabGroup/index.tsx rename to src/legacy-components/UI/InterlayTabGroup/index.tsx diff --git a/src/components/UI/InterlayTable/DefaultColumnFilter/index.tsx b/src/legacy-components/UI/InterlayTable/DefaultColumnFilter/index.tsx similarity index 100% rename from src/components/UI/InterlayTable/DefaultColumnFilter/index.tsx rename to src/legacy-components/UI/InterlayTable/DefaultColumnFilter/index.tsx diff --git a/src/components/UI/InterlayTable/GlobalFilter/index.tsx b/src/legacy-components/UI/InterlayTable/GlobalFilter/index.tsx similarity index 100% rename from src/components/UI/InterlayTable/GlobalFilter/index.tsx rename to src/legacy-components/UI/InterlayTable/GlobalFilter/index.tsx diff --git a/src/components/UI/InterlayTable/NumberRangeColumnFilter/index.tsx b/src/legacy-components/UI/InterlayTable/NumberRangeColumnFilter/index.tsx similarity index 100% rename from src/components/UI/InterlayTable/NumberRangeColumnFilter/index.tsx rename to src/legacy-components/UI/InterlayTable/NumberRangeColumnFilter/index.tsx diff --git a/src/components/UI/InterlayTable/SortBy/index.tsx b/src/legacy-components/UI/InterlayTable/SortBy/index.tsx similarity index 100% rename from src/components/UI/InterlayTable/SortBy/index.tsx rename to src/legacy-components/UI/InterlayTable/SortBy/index.tsx diff --git a/src/components/UI/InterlayTable/SortBy/sort-by.module.css b/src/legacy-components/UI/InterlayTable/SortBy/sort-by.module.css similarity index 100% rename from src/components/UI/InterlayTable/SortBy/sort-by.module.css rename to src/legacy-components/UI/InterlayTable/SortBy/sort-by.module.css diff --git a/src/components/UI/InterlayTable/StatusCell/index.tsx b/src/legacy-components/UI/InterlayTable/StatusCell/index.tsx similarity index 100% rename from src/components/UI/InterlayTable/StatusCell/index.tsx rename to src/legacy-components/UI/InterlayTable/StatusCell/index.tsx diff --git a/src/components/UI/InterlayTable/index.tsx b/src/legacy-components/UI/InterlayTable/index.tsx similarity index 100% rename from src/components/UI/InterlayTable/index.tsx rename to src/legacy-components/UI/InterlayTable/index.tsx diff --git a/src/components/UI/InterlayTooltip/index.tsx b/src/legacy-components/UI/InterlayTooltip/index.tsx similarity index 100% rename from src/components/UI/InterlayTooltip/index.tsx rename to src/legacy-components/UI/InterlayTooltip/index.tsx diff --git a/src/components/UI/InterlayTooltip/interlay-tooltip.css b/src/legacy-components/UI/InterlayTooltip/interlay-tooltip.css similarity index 100% rename from src/components/UI/InterlayTooltip/interlay-tooltip.css rename to src/legacy-components/UI/InterlayTooltip/interlay-tooltip.css diff --git a/src/components/VaultsSelector/VaultSelect/index.tsx b/src/legacy-components/VaultsSelector/VaultSelect/index.tsx similarity index 100% rename from src/components/VaultsSelector/VaultSelect/index.tsx rename to src/legacy-components/VaultsSelector/VaultSelect/index.tsx diff --git a/src/components/VaultsSelector/index.tsx b/src/legacy-components/VaultsSelector/index.tsx similarity index 100% rename from src/components/VaultsSelector/index.tsx rename to src/legacy-components/VaultsSelector/index.tsx diff --git a/src/legacy-components/ViewRequestDetailsLink/index.tsx b/src/legacy-components/ViewRequestDetailsLink/index.tsx new file mode 100644 index 0000000000..17f9a86edf --- /dev/null +++ b/src/legacy-components/ViewRequestDetailsLink/index.tsx @@ -0,0 +1,21 @@ +import { shortAddress } from '@/common/utils/utils'; +import InterlayRouterLink from '@/legacy-components/UI/InterlayRouterLink'; +import { TXType } from '@/types/general.d'; +import { PAGES } from '@/utils/constants/links'; + +interface Props { + id: string; + txType: `${TXType}`; +} + +const ViewRequestDetailsLink = ({ id, txType }: Props): JSX.Element => { + const path = `${PAGES.TX}/${txType}/${id}`; + + return ( + + {shortAddress(id)} + + ); +}; + +export default ViewRequestDetailsLink; diff --git a/src/components/WarningBanner/index.tsx b/src/legacy-components/WarningBanner/index.tsx similarity index 100% rename from src/components/WarningBanner/index.tsx rename to src/legacy-components/WarningBanner/index.tsx diff --git a/src/components/badges/InterlayCinnabarBadge/index.tsx b/src/legacy-components/badges/InterlayCinnabarBadge/index.tsx similarity index 100% rename from src/components/badges/InterlayCinnabarBadge/index.tsx rename to src/legacy-components/badges/InterlayCinnabarBadge/index.tsx diff --git a/src/components/buttons/CloseIconButton/index.tsx b/src/legacy-components/buttons/CloseIconButton/index.tsx similarity index 89% rename from src/components/buttons/CloseIconButton/index.tsx rename to src/legacy-components/buttons/CloseIconButton/index.tsx index c092e2e893..0f45ec538f 100644 --- a/src/components/buttons/CloseIconButton/index.tsx +++ b/src/legacy-components/buttons/CloseIconButton/index.tsx @@ -2,7 +2,7 @@ import clsx from 'clsx'; import * as React from 'react'; import { ReactComponent as CloseIcon } from '@/assets/img/icons/close.svg'; -import IconButton, { Props as IconButtonProps } from '@/components/buttons/IconButton'; +import IconButton, { Props as IconButtonProps } from '@/legacy-components/buttons/IconButton'; import { KUSAMA, POLKADOT } from '@/utils/constants/relay-chain-names'; type Ref = HTMLButtonElement; diff --git a/src/components/buttons/IconButton/index.tsx b/src/legacy-components/buttons/IconButton/index.tsx similarity index 96% rename from src/components/buttons/IconButton/index.tsx rename to src/legacy-components/buttons/IconButton/index.tsx index 36646202d9..ac9e93e506 100644 --- a/src/components/buttons/IconButton/index.tsx +++ b/src/legacy-components/buttons/IconButton/index.tsx @@ -2,7 +2,7 @@ import clsx from 'clsx'; import * as React from 'react'; import { ReactComponent as SpinIcon } from '@/assets/img/icons/spin.svg'; -import InterlayButtonBase, { Props as InterlayButtonBaseProps } from '@/components/UI/InterlayButtonBase'; +import InterlayButtonBase, { Props as InterlayButtonBaseProps } from '@/legacy-components/UI/InterlayButtonBase'; import { KUSAMA, POLKADOT } from '@/utils/constants/relay-chain-names'; type CustomProps = { diff --git a/src/components/buttons/InterlayCaliforniaContainedButton/index.tsx b/src/legacy-components/buttons/InterlayCaliforniaContainedButton/index.tsx similarity index 97% rename from src/components/buttons/InterlayCaliforniaContainedButton/index.tsx rename to src/legacy-components/buttons/InterlayCaliforniaContainedButton/index.tsx index d1de6ac781..cbb5bc38cc 100644 --- a/src/components/buttons/InterlayCaliforniaContainedButton/index.tsx +++ b/src/legacy-components/buttons/InterlayCaliforniaContainedButton/index.tsx @@ -2,7 +2,7 @@ import clsx from 'clsx'; import * as React from 'react'; import { ReactComponent as SpinIcon } from '@/assets/img/icons/spin.svg'; -import InterlayButtonBase, { Props as InterlayButtonBaseProps } from '@/components/UI/InterlayButtonBase'; +import InterlayButtonBase, { Props as InterlayButtonBaseProps } from '@/legacy-components/UI/InterlayButtonBase'; import { DISABLED_BACKGROUND_CLASSES, DISABLED_TEXT_CLASSES } from '@/utils/constants/styles'; interface CustomProps { diff --git a/src/components/buttons/InterlayCaliforniaOutlinedButton/index.tsx b/src/legacy-components/buttons/InterlayCaliforniaOutlinedButton/index.tsx similarity index 97% rename from src/components/buttons/InterlayCaliforniaOutlinedButton/index.tsx rename to src/legacy-components/buttons/InterlayCaliforniaOutlinedButton/index.tsx index 3f800c9d20..b624ed9283 100644 --- a/src/components/buttons/InterlayCaliforniaOutlinedButton/index.tsx +++ b/src/legacy-components/buttons/InterlayCaliforniaOutlinedButton/index.tsx @@ -2,7 +2,7 @@ import clsx from 'clsx'; import * as React from 'react'; import { ReactComponent as SpinIcon } from '@/assets/img/icons/spin.svg'; -import InterlayButtonBase, { Props as InterlayButtonBaseProps } from '@/components/UI/InterlayButtonBase'; +import InterlayButtonBase, { Props as InterlayButtonBaseProps } from '@/legacy-components/UI/InterlayButtonBase'; import { DISABLED_BORDER_CLASSES, DISABLED_TEXT_CLASSES } from '@/utils/constants/styles'; import { getColorShade } from '@/utils/helpers/colors'; diff --git a/src/components/buttons/InterlayCinnabarOutlinedButton/index.tsx b/src/legacy-components/buttons/InterlayCinnabarOutlinedButton/index.tsx similarity index 97% rename from src/components/buttons/InterlayCinnabarOutlinedButton/index.tsx rename to src/legacy-components/buttons/InterlayCinnabarOutlinedButton/index.tsx index b74db41237..52d3a64920 100644 --- a/src/components/buttons/InterlayCinnabarOutlinedButton/index.tsx +++ b/src/legacy-components/buttons/InterlayCinnabarOutlinedButton/index.tsx @@ -2,7 +2,7 @@ import clsx from 'clsx'; import * as React from 'react'; import { ReactComponent as SpinIcon } from '@/assets/img/icons/spin.svg'; -import InterlayButtonBase, { Props as InterlayButtonBaseProps } from '@/components/UI/InterlayButtonBase'; +import InterlayButtonBase, { Props as InterlayButtonBaseProps } from '@/legacy-components/UI/InterlayButtonBase'; import { DISABLED_BORDER_CLASSES, DISABLED_TEXT_CLASSES } from '@/utils/constants/styles'; import { getColorShade } from '@/utils/helpers/colors'; diff --git a/src/components/buttons/InterlayConiferOutlinedButton/index.tsx b/src/legacy-components/buttons/InterlayConiferOutlinedButton/index.tsx similarity index 97% rename from src/components/buttons/InterlayConiferOutlinedButton/index.tsx rename to src/legacy-components/buttons/InterlayConiferOutlinedButton/index.tsx index b795f19026..6c42f3f3cc 100644 --- a/src/components/buttons/InterlayConiferOutlinedButton/index.tsx +++ b/src/legacy-components/buttons/InterlayConiferOutlinedButton/index.tsx @@ -2,7 +2,7 @@ import clsx from 'clsx'; import * as React from 'react'; import { ReactComponent as SpinIcon } from '@/assets/img/icons/spin.svg'; -import InterlayButtonBase, { Props as InterlayButtonBaseProps } from '@/components/UI/InterlayButtonBase'; +import InterlayButtonBase, { Props as InterlayButtonBaseProps } from '@/legacy-components/UI/InterlayButtonBase'; import { DISABLED_BORDER_CLASSES, DISABLED_TEXT_CLASSES } from '@/utils/constants/styles'; import { getColorShade } from '@/utils/helpers/colors'; diff --git a/src/components/buttons/InterlayDefaultContainedButton/index.tsx b/src/legacy-components/buttons/InterlayDefaultContainedButton/index.tsx similarity index 97% rename from src/components/buttons/InterlayDefaultContainedButton/index.tsx rename to src/legacy-components/buttons/InterlayDefaultContainedButton/index.tsx index 69e2ddea5c..e381ee79b3 100644 --- a/src/components/buttons/InterlayDefaultContainedButton/index.tsx +++ b/src/legacy-components/buttons/InterlayDefaultContainedButton/index.tsx @@ -2,7 +2,7 @@ import clsx from 'clsx'; import * as React from 'react'; import { ReactComponent as SpinIcon } from '@/assets/img/icons/spin.svg'; -import InterlayButtonBase, { Props as InterlayButtonBaseProps } from '@/components/UI/InterlayButtonBase'; +import InterlayButtonBase, { Props as InterlayButtonBaseProps } from '@/legacy-components/UI/InterlayButtonBase'; import { DISABLED_BACKGROUND_CLASSES, DISABLED_TEXT_CLASSES, TEXT_CLASSES } from '@/utils/constants/styles'; interface CustomProps { diff --git a/src/components/buttons/InterlayDefaultOutlinedButton/index.tsx b/src/legacy-components/buttons/InterlayDefaultOutlinedButton/index.tsx similarity index 97% rename from src/components/buttons/InterlayDefaultOutlinedButton/index.tsx rename to src/legacy-components/buttons/InterlayDefaultOutlinedButton/index.tsx index b190c195eb..0d5863f60d 100644 --- a/src/components/buttons/InterlayDefaultOutlinedButton/index.tsx +++ b/src/legacy-components/buttons/InterlayDefaultOutlinedButton/index.tsx @@ -2,7 +2,7 @@ import clsx from 'clsx'; import * as React from 'react'; import { ReactComponent as SpinIcon } from '@/assets/img/icons/spin.svg'; -import InterlayButtonBase, { Props as InterlayButtonBaseProps } from '@/components/UI/InterlayButtonBase'; +import InterlayButtonBase, { Props as InterlayButtonBaseProps } from '@/legacy-components/UI/InterlayButtonBase'; import { BORDER_CLASSES, DISABLED_BORDER_CLASSES, DISABLED_TEXT_CLASSES, TEXT_CLASSES } from '@/utils/constants/styles'; interface CustomProps { diff --git a/src/components/buttons/InterlayDenimOrKintsugiMidnightOutlinedButton/index.tsx b/src/legacy-components/buttons/InterlayDenimOrKintsugiMidnightOutlinedButton/index.tsx similarity index 98% rename from src/components/buttons/InterlayDenimOrKintsugiMidnightOutlinedButton/index.tsx rename to src/legacy-components/buttons/InterlayDenimOrKintsugiMidnightOutlinedButton/index.tsx index e9080a13b6..f45d5008dc 100644 --- a/src/components/buttons/InterlayDenimOrKintsugiMidnightOutlinedButton/index.tsx +++ b/src/legacy-components/buttons/InterlayDenimOrKintsugiMidnightOutlinedButton/index.tsx @@ -2,7 +2,7 @@ import clsx from 'clsx'; import * as React from 'react'; import { ReactComponent as SpinIcon } from '@/assets/img/icons/spin.svg'; -import InterlayButtonBase, { Props as InterlayButtonBaseProps } from '@/components/UI/InterlayButtonBase'; +import InterlayButtonBase, { Props as InterlayButtonBaseProps } from '@/legacy-components/UI/InterlayButtonBase'; import { KUSAMA, POLKADOT } from '@/utils/constants/relay-chain-names'; import { DISABLED_BORDER_CLASSES, DISABLED_TEXT_CLASSES } from '@/utils/constants/styles'; diff --git a/src/components/buttons/InterlayDenimOrKintsugiSupernovaContainedButton/index.tsx b/src/legacy-components/buttons/InterlayDenimOrKintsugiSupernovaContainedButton/index.tsx similarity index 98% rename from src/components/buttons/InterlayDenimOrKintsugiSupernovaContainedButton/index.tsx rename to src/legacy-components/buttons/InterlayDenimOrKintsugiSupernovaContainedButton/index.tsx index 523f8270f1..d6df6ecac1 100644 --- a/src/components/buttons/InterlayDenimOrKintsugiSupernovaContainedButton/index.tsx +++ b/src/legacy-components/buttons/InterlayDenimOrKintsugiSupernovaContainedButton/index.tsx @@ -2,7 +2,7 @@ import clsx from 'clsx'; import * as React from 'react'; import { ReactComponent as SpinIcon } from '@/assets/img/icons/spin.svg'; -import InterlayButtonBase, { Props as InterlayButtonBaseProps } from '@/components/UI/InterlayButtonBase'; +import InterlayButtonBase, { Props as InterlayButtonBaseProps } from '@/legacy-components/UI/InterlayButtonBase'; import { KUSAMA, POLKADOT } from '@/utils/constants/relay-chain-names'; import { DISABLED_BACKGROUND_CLASSES, DISABLED_TEXT_CLASSES } from '@/utils/constants/styles'; diff --git a/src/components/buttons/InterlayMulberryOutlinedButton/index.tsx b/src/legacy-components/buttons/InterlayMulberryOutlinedButton/index.tsx similarity index 97% rename from src/components/buttons/InterlayMulberryOutlinedButton/index.tsx rename to src/legacy-components/buttons/InterlayMulberryOutlinedButton/index.tsx index 99c945eee0..61b7aa81db 100644 --- a/src/components/buttons/InterlayMulberryOutlinedButton/index.tsx +++ b/src/legacy-components/buttons/InterlayMulberryOutlinedButton/index.tsx @@ -2,7 +2,7 @@ import clsx from 'clsx'; import * as React from 'react'; import { ReactComponent as SpinIcon } from '@/assets/img/icons/spin.svg'; -import InterlayButtonBase, { Props as InterlayButtonBaseProps } from '@/components/UI/InterlayButtonBase'; +import InterlayButtonBase, { Props as InterlayButtonBaseProps } from '@/legacy-components/UI/InterlayButtonBase'; import { DISABLED_BORDER_CLASSES, DISABLED_TEXT_CLASSES } from '@/utils/constants/styles'; interface CustomProps { diff --git a/src/components/hrs/Hr1/index.tsx b/src/legacy-components/hrs/Hr1/index.tsx similarity index 100% rename from src/components/hrs/Hr1/index.tsx rename to src/legacy-components/hrs/Hr1/index.tsx diff --git a/src/components/hrs/Hr2/index.tsx b/src/legacy-components/hrs/Hr2/index.tsx similarity index 100% rename from src/components/hrs/Hr2/index.tsx rename to src/legacy-components/hrs/Hr2/index.tsx diff --git a/src/components/toggle-button-groups/InterlayDenimToggleButtonGroup/index.tsx b/src/legacy-components/toggle-button-groups/InterlayDenimToggleButtonGroup/index.tsx similarity index 97% rename from src/components/toggle-button-groups/InterlayDenimToggleButtonGroup/index.tsx rename to src/legacy-components/toggle-button-groups/InterlayDenimToggleButtonGroup/index.tsx index 7dd53b7209..bc987a7a76 100644 --- a/src/components/toggle-button-groups/InterlayDenimToggleButtonGroup/index.tsx +++ b/src/legacy-components/toggle-button-groups/InterlayDenimToggleButtonGroup/index.tsx @@ -2,7 +2,7 @@ import { RadioGroup } from '@headlessui/react'; import { PropsOf } from '@headlessui/react/dist/types'; import clsx from 'clsx'; -import InterlayButtonBase, { Props as InterlayButtonBaseProps } from '@/components/UI/InterlayButtonBase'; +import InterlayButtonBase, { Props as InterlayButtonBaseProps } from '@/legacy-components/UI/InterlayButtonBase'; // TODO: not used for now const InterlayDenimToggleButtonGroup = ({ className, ...rest }: PropsOf): JSX.Element => ( diff --git a/src/components/tooltips/InformationTooltip/index.tsx b/src/legacy-components/tooltips/InformationTooltip/index.tsx similarity index 55% rename from src/components/tooltips/InformationTooltip/index.tsx rename to src/legacy-components/tooltips/InformationTooltip/index.tsx index f9f009246c..ce8633473a 100644 --- a/src/components/tooltips/InformationTooltip/index.tsx +++ b/src/legacy-components/tooltips/InformationTooltip/index.tsx @@ -1,7 +1,7 @@ import clsx from 'clsx'; import { ReactComponent as InformationCircleIcon } from '@/assets/img/hero-icons/information-circle.svg'; -import InterlayTooltip from '@/components/UI/InterlayTooltip'; +import InterlayTooltip from '@/legacy-components/UI/InterlayTooltip'; interface Props { label: string; @@ -11,12 +11,14 @@ interface Props { const InformationTooltip = ({ label, forDisabledAction, className }: Props): JSX.Element => ( - { - forDisabledAction && event.stopPropagation(); - }} - className={clsx('w-5', 'h-5', { 'pointer-events-auto': forDisabledAction }, className)} - /> + + { + forDisabledAction && event.stopPropagation(); + }} + className={clsx('w-5', 'h-5', { 'pointer-events-auto': forDisabledAction }, className)} + /> + ); diff --git a/src/lib/form-validation/common/balance.ts b/src/lib/form-validation/common/balance.ts deleted file mode 100644 index 230faaa059..0000000000 --- a/src/lib/form-validation/common/balance.ts +++ /dev/null @@ -1,43 +0,0 @@ -import { CurrencyExt } from '@interlay/interbtc-api'; -import { MonetaryAmount } from '@interlay/monetary-js'; -import * as z from 'zod'; - -import { GOVERNANCE_TOKEN_SYMBOL } from '@/config/relay-chains'; - -import { Validation } from './types'; - -type TransactionFeeBalanceValidationParams = { - availableBalance: MonetaryAmount; - transactionFee: MonetaryAmount; -}; - -const transactionFee: Validation = { - validate: ({ availableBalance, transactionFee }) => availableBalance.gte(transactionFee), - issue: (t) => ({ - code: z.ZodIssueCode.custom, - message: t('insufficient_funds_governance_token', { - governanceTokenSymbol: GOVERNANCE_TOKEN_SYMBOL - }) - }) -}; - -type CurrencyBalanceValidationParams = { - inputAmount: MonetaryAmount; - availableBalance: MonetaryAmount; -}; - -const currency: Validation = { - validate: ({ availableBalance, inputAmount }) => inputAmount.lte(availableBalance), - issue: (t) => ({ - code: z.ZodIssueCode.custom, - message: t('forms.please_enter_no_higher_available_balance') - }) -}; - -const balance = { - transactionFee, - currency -}; - -export default balance; -export type { CurrencyBalanceValidationParams, TransactionFeeBalanceValidationParams }; diff --git a/src/lib/form-validation/common/field.ts b/src/lib/form-validation/common/field.ts deleted file mode 100644 index 90242b0fa0..0000000000 --- a/src/lib/form-validation/common/field.ts +++ /dev/null @@ -1,87 +0,0 @@ -import Big from 'big.js'; -import * as z from 'zod'; - -import { Validation } from './types'; - -type RequiredFieldValidationParams = { - value: string; -}; - -type RequiredFieldIssueParams = { - fieldName: string; - fieldType?: 'text' | 'number'; -}; - -const required: Validation = { - validate: ({ value }): boolean => !!value, - issue: (t, params) => { - const translationField = - params?.fieldType === 'number' ? 'forms.please_enter_the_amount_to' : 'forms.please_enter_your_field'; - - return { - code: z.ZodIssueCode.too_small, - minimum: 1, - inclusive: true, - type: 'string', - message: t(translationField, { field: params?.fieldName }) - }; - } -}; - -type MinFieldValidationParams = { - minAmount: Big; - inputAmount: Big; -}; - -type MinFieldIssueParams = { - action: string; - amount: number | string; -}; - -const min: Validation = { - validate: ({ inputAmount, minAmount }): boolean => inputAmount.gte(minAmount), - issue: (t, params) => ({ - code: z.ZodIssueCode.too_small, - minimum: 1, - inclusive: true, - type: 'string', - message: t('forms.amount_must_be_at_least', { action: params?.action, amount: params?.amount }) - }) -}; - -type MaxFieldValidationParams = { - maxAmount: Big; - inputAmount: Big; -}; - -type MaxFieldIssueParams = { - action: string; - amount: number | string; -}; - -const max: Validation = { - validate: ({ inputAmount, maxAmount }): boolean => inputAmount.lte(maxAmount), - issue: (t, params) => ({ - code: z.ZodIssueCode.too_small, - minimum: 1, - inclusive: true, - type: 'string', - message: t('forms.amount_must_be_at_most', { action: params?.action, amount: params?.amount }) - }) -}; - -const field = { - required, - min, - max -}; - -export default field; -export type { - MaxFieldIssueParams, - MaxFieldValidationParams, - MinFieldIssueParams, - MinFieldValidationParams, - RequiredFieldIssueParams, - RequiredFieldValidationParams -}; diff --git a/src/lib/form-validation/common/types.ts b/src/lib/form-validation/common/types.ts deleted file mode 100644 index 1f6ef428ef..0000000000 --- a/src/lib/form-validation/common/types.ts +++ /dev/null @@ -1,17 +0,0 @@ -import { TFunction } from 'i18next'; -import * as z from 'zod'; - -/** - * This type contains the structure of a validation - * @function validate contains one or multiple conditions - * that should return true if `params` respect condition restrictions - * @function issue should return the necessary meta data for zod `addIssue` - * and it's here also where error `message` is declared - * @note This kind of validation should only be used in reusable validations - */ -type Validation = { - validate: (params: V) => boolean; - issue: (t: TFunction, params?: I) => z.IssueData; -}; - -export type { Validation }; diff --git a/src/lib/form-validation/index.ts b/src/lib/form-validation/index.ts deleted file mode 100644 index f5aef70d5e..0000000000 --- a/src/lib/form-validation/index.ts +++ /dev/null @@ -1,16 +0,0 @@ -import loans from './loans'; -import vaults from './vaults'; - -const validation = { - vaults, - loans -}; - -export type { - LoanBorrowSchemaParams, - LoanLendSchemaParams, - LoanRepaySchemaParams, - LoanWithdrawSchemaParams -} from './loans'; -export type { VaultDepositSchemaParams } from './vaults'; -export default validation; diff --git a/src/lib/form-validation/loans/borrow.ts b/src/lib/form-validation/loans/borrow.ts deleted file mode 100644 index 011632f74d..0000000000 --- a/src/lib/form-validation/loans/borrow.ts +++ /dev/null @@ -1,83 +0,0 @@ -import { newMonetaryAmount } from '@interlay/interbtc-api'; -import Big from 'big.js'; -import { TFunction } from 'i18next'; -import * as z from 'zod'; - -import balance from '../common/balance'; -import field from '../common/field'; -import { AvailableBalanceSchemaParams, CommonSchemaParams, MaxAmountSchemaParams } from '../types'; - -type LoanBorrowSchemaParams = CommonSchemaParams & MaxAmountSchemaParams; - -const borrow = (t: TFunction, params: LoanBorrowSchemaParams): z.ZodEffects => - z.string().superRefine((value, ctx) => { - const { governanceBalance, transactionFee, minAmount, maxAmount } = params; - - if (!field.required.validate({ value })) { - const issueArg = field.required.issue(t, { fieldName: t('loans.borrow').toLowerCase(), fieldType: 'number' }); - return ctx.addIssue(issueArg); - } - - if (!balance.transactionFee.validate({ availableBalance: governanceBalance, transactionFee })) { - return ctx.addIssue(balance.transactionFee.issue(t)); - } - - const inputAmount = new Big(value); - - if (!field.min.validate({ inputAmount, minAmount: minAmount.toBig() })) { - const issueArg = field.min.issue(t, { - action: t('loans.borrow').toLowerCase(), - amount: minAmount.toString() - }); - return ctx.addIssue(issueArg); - } - - if (!field.max.validate({ inputAmount, maxAmount: maxAmount.toBig() })) { - const issueArg = field.max.issue(t, { - action: t('loans.borrow').toLowerCase(), - amount: maxAmount.toString() - }); - return ctx.addIssue(issueArg); - } - }); - -type LoanRepaySchemaParams = CommonSchemaParams & MaxAmountSchemaParams & AvailableBalanceSchemaParams; - -const repay = (t: TFunction, params: LoanRepaySchemaParams): z.ZodEffects => - z.string().superRefine((value, ctx) => { - const { governanceBalance, transactionFee, availableBalance, minAmount, maxAmount } = params; - - if (!field.required.validate({ value })) { - const issueArg = field.required.issue(t, { fieldName: t('loans.repay').toLowerCase(), fieldType: 'number' }); - return ctx.addIssue(issueArg); - } - - if (!balance.transactionFee.validate({ availableBalance: governanceBalance, transactionFee })) { - return ctx.addIssue(balance.transactionFee.issue(t)); - } - - const inputAmount = newMonetaryAmount(value, availableBalance.currency, true); - - if (!field.min.validate({ inputAmount: inputAmount.toBig(), minAmount: minAmount.toBig() })) { - const issueArg = field.min.issue(t, { - action: t('loans.repay').toLowerCase(), - amount: minAmount.toString() - }); - return ctx.addIssue(issueArg); - } - - if (!field.max.validate({ inputAmount: inputAmount.toBig(), maxAmount: maxAmount.toBig() })) { - const issueArg = field.max.issue(t, { - action: t('loans.repay').toLowerCase(), - amount: maxAmount.toString() - }); - return ctx.addIssue(issueArg); - } - - if (!balance.currency.validate({ availableBalance, inputAmount })) { - return ctx.addIssue(balance.currency.issue(t)); - } - }); - -export { borrow, repay }; -export type { LoanBorrowSchemaParams, LoanRepaySchemaParams }; diff --git a/src/lib/form-validation/loans/index.ts b/src/lib/form-validation/loans/index.ts deleted file mode 100644 index 51773a02db..0000000000 --- a/src/lib/form-validation/loans/index.ts +++ /dev/null @@ -1,13 +0,0 @@ -import { borrow, repay } from './borrow'; -import { lend, withdraw } from './lend'; - -const loans = { - borrow, - repay, - lend, - withdraw -}; - -export default loans; -export type { LoanBorrowSchemaParams, LoanRepaySchemaParams } from './borrow'; -export type { LoanLendSchemaParams, LoanWithdrawSchemaParams } from './lend'; diff --git a/src/lib/form-validation/loans/lend.ts b/src/lib/form-validation/loans/lend.ts deleted file mode 100644 index cdd1009539..0000000000 --- a/src/lib/form-validation/loans/lend.ts +++ /dev/null @@ -1,75 +0,0 @@ -import { newMonetaryAmount } from '@interlay/interbtc-api'; -import Big from 'big.js'; -import { TFunction } from 'i18next'; -import * as z from 'zod'; - -import balance from '../common/balance'; -import field from '../common/field'; -import { AvailableBalanceSchemaParams, CommonSchemaParams, MaxAmountSchemaParams } from '../types'; - -type LoanLendSchemaParams = CommonSchemaParams & AvailableBalanceSchemaParams; - -const lend = (t: TFunction, params: LoanLendSchemaParams): z.ZodEffects => - z.string().superRefine((value, ctx) => { - const { governanceBalance, transactionFee, availableBalance, minAmount } = params; - - if (!field.required.validate({ value })) { - const issueArg = field.required.issue(t, { fieldName: t('loans.lend').toLowerCase(), fieldType: 'number' }); - return ctx.addIssue(issueArg); - } - - if (!balance.transactionFee.validate({ availableBalance: governanceBalance, transactionFee })) { - return ctx.addIssue(balance.transactionFee.issue(t)); - } - - const inputAmount = newMonetaryAmount(value, availableBalance.currency, true); - - if (!field.min.validate({ inputAmount: inputAmount.toBig(), minAmount: minAmount.toBig() })) { - const issueArg = field.min.issue(t, { - action: t('loans.lend').toLowerCase(), - amount: minAmount.toString() - }); - return ctx.addIssue(issueArg); - } - - if (!balance.currency.validate({ availableBalance: availableBalance, inputAmount })) { - return ctx.addIssue(balance.currency.issue(t)); - } - }); - -type LoanWithdrawSchemaParams = CommonSchemaParams & MaxAmountSchemaParams; - -const withdraw = (t: TFunction, params: LoanWithdrawSchemaParams): z.ZodEffects => - z.string().superRefine((value, ctx) => { - const { governanceBalance, transactionFee, minAmount, maxAmount } = params; - - if (!field.required.validate({ value })) { - const issueArg = field.required.issue(t, { fieldName: t('loans.withdraw').toLowerCase(), fieldType: 'number' }); - return ctx.addIssue(issueArg); - } - - if (!balance.transactionFee.validate({ availableBalance: governanceBalance, transactionFee })) { - return ctx.addIssue(balance.transactionFee.issue(t)); - } - - const inputAmount = new Big(value); - - if (!field.min.validate({ inputAmount, minAmount: minAmount.toBig() })) { - const issueArg = field.min.issue(t, { - action: t('loans.withdraw').toLowerCase(), - amount: minAmount.toString() - }); - return ctx.addIssue(issueArg); - } - - if (!field.max.validate({ inputAmount, maxAmount: maxAmount.toBig() })) { - const issueArg = field.max.issue(t, { - action: t('loans.withdraw').toLowerCase(), - amount: maxAmount.toString() - }); - return ctx.addIssue(issueArg); - } - }); - -export { lend, withdraw }; -export type { LoanLendSchemaParams, LoanWithdrawSchemaParams }; diff --git a/src/lib/form-validation/types.ts b/src/lib/form-validation/types.ts deleted file mode 100644 index 414ce4e1e7..0000000000 --- a/src/lib/form-validation/types.ts +++ /dev/null @@ -1,18 +0,0 @@ -import { CurrencyExt } from '@interlay/interbtc-api'; -import { MonetaryAmount } from '@interlay/monetary-js'; - -type CommonSchemaParams = { - governanceBalance: MonetaryAmount; - transactionFee: MonetaryAmount; - minAmount: MonetaryAmount; -}; - -type AvailableBalanceSchemaParams = { - availableBalance: MonetaryAmount; -}; - -type MaxAmountSchemaParams = { - maxAmount: MonetaryAmount; -}; - -export type { AvailableBalanceSchemaParams, CommonSchemaParams, MaxAmountSchemaParams }; diff --git a/src/lib/form-validation/vaults/create.ts b/src/lib/form-validation/vaults/create.ts deleted file mode 100644 index 44701225fd..0000000000 --- a/src/lib/form-validation/vaults/create.ts +++ /dev/null @@ -1,40 +0,0 @@ -import { newMonetaryAmount } from '@interlay/interbtc-api'; -import { TFunction } from 'i18next'; -import * as z from 'zod'; - -import balance from '../common/balance'; -import field from '../common/field'; -import { AvailableBalanceSchemaParams, CommonSchemaParams } from '../types'; - -type VaultDepositSchemaParams = CommonSchemaParams & AvailableBalanceSchemaParams; - -const deposit = (t: TFunction, params: VaultDepositSchemaParams): z.ZodEffects => - z.string().superRefine((value, ctx) => { - const { availableBalance, governanceBalance, minAmount, transactionFee } = params; - - if (!field.required.validate({ value })) { - const issueArg = field.required.issue(t, { fieldName: t('vault.deposit').toLowerCase(), fieldType: 'number' }); - return ctx.addIssue(issueArg); - } - - if (!balance.transactionFee.validate({ availableBalance: governanceBalance, transactionFee })) { - return ctx.addIssue(balance.transactionFee.issue(t)); - } - - const inputAmount = newMonetaryAmount(value, availableBalance.currency, true); - - if (!field.min.validate({ inputAmount: inputAmount.toBig(), minAmount: minAmount.toBig() })) { - const issueArg = field.min.issue(t, { - action: t('vault.deposit').toLowerCase(), - amount: minAmount.toBig().toString() - }); - return ctx.addIssue(issueArg); - } - - if (!balance.currency.validate({ availableBalance, inputAmount })) { - return ctx.addIssue(balance.currency.issue(t)); - } - }); - -export { deposit }; -export type { VaultDepositSchemaParams }; diff --git a/src/lib/form-validation/vaults/index.ts b/src/lib/form-validation/vaults/index.ts deleted file mode 100644 index b3fec9b4b8..0000000000 --- a/src/lib/form-validation/vaults/index.ts +++ /dev/null @@ -1,8 +0,0 @@ -import { deposit } from './create'; - -const vaults = { - deposit -}; - -export default vaults; -export type { VaultDepositSchemaParams } from './create'; diff --git a/src/lib/form/index.tsx b/src/lib/form/index.tsx new file mode 100644 index 0000000000..60fbf3b63b --- /dev/null +++ b/src/lib/form/index.tsx @@ -0,0 +1,4 @@ +export * from './schemas'; +export type { FormErrors } from './use-form'; +export { useForm } from './use-form'; +export { isFormDisabled } from './utils'; diff --git a/src/lib/form/schemas/amm.ts b/src/lib/form/schemas/amm.ts new file mode 100644 index 0000000000..def382e26f --- /dev/null +++ b/src/lib/form/schemas/amm.ts @@ -0,0 +1,51 @@ +import yup, { FeesValidationParams, MaxAmountValidationParams, MinAmountValidationParams } from '../yup.custom'; + +type DepositLiquidityPoolFormData = Record; + +type DepositLiquidityPoolValidationParams = FeesValidationParams & { + tokens: Record; +}; + +// MEMO: schema is dynamic because is deals with one to many fields +const depositLiquidityPoolSchema = (params: DepositLiquidityPoolValidationParams): yup.ObjectSchema => { + const shape = Object.keys(params.tokens).reduce((acc, ticker, idx) => { + const tokenParams = params.tokens[ticker]; + const validation = yup.string().requiredAmount('deposit').maxAmount(tokenParams).minAmount(tokenParams, 'deposit'); + + if (idx === 0) { + return { ...acc, [ticker]: validation.fees(params) }; + } + + return { ...acc, [ticker]: validation }; + }, {}); + + return yup.object().shape(shape); +}; + +const WITHDRAW_LIQUIDITY_POOL_FIELD = 'withdraw'; + +type WithdrawLiquidityPoolFormData = { + [WITHDRAW_LIQUIDITY_POOL_FIELD]?: number; +}; + +type WithdrawLiquidityPoolValidationParams = FeesValidationParams & + MaxAmountValidationParams & + MinAmountValidationParams; + +const withdrawLiquidityPoolSchema = (params: WithdrawLiquidityPoolValidationParams): yup.ObjectSchema => + yup.object().shape({ + [WITHDRAW_LIQUIDITY_POOL_FIELD]: yup + .string() + .requiredAmount(WITHDRAW_LIQUIDITY_POOL_FIELD) + .maxAmount(params) + .minAmount(params, WITHDRAW_LIQUIDITY_POOL_FIELD) + .fees(params) + }); + +export { depositLiquidityPoolSchema, WITHDRAW_LIQUIDITY_POOL_FIELD, withdrawLiquidityPoolSchema }; +export type { + DepositLiquidityPoolFormData, + DepositLiquidityPoolValidationParams, + WithdrawLiquidityPoolFormData, + WithdrawLiquidityPoolValidationParams +}; diff --git a/src/lib/form/schemas/index.ts b/src/lib/form/schemas/index.ts new file mode 100644 index 0000000000..1d584fcd5d --- /dev/null +++ b/src/lib/form/schemas/index.ts @@ -0,0 +1,19 @@ +export type { + DepositLiquidityPoolFormData, + DepositLiquidityPoolValidationParams, + WithdrawLiquidityPoolFormData, + WithdrawLiquidityPoolValidationParams +} from './amm'; +export { depositLiquidityPoolSchema, WITHDRAW_LIQUIDITY_POOL_FIELD, withdrawLiquidityPoolSchema } from './amm'; +export type { LoanFormData, LoanValidationParams } from './loans'; +export { loanSchema } from './loans'; +export type { SwapFormData, SwapValidationParams } from './swap'; +export { + SWAP_INPUT_AMOUNT_FIELD, + SWAP_INPUT_TOKEN_FIELD, + SWAP_OUTPUT_TOKEN_FIELD, + SwapErrorMessage, + swapSchema +} from './swap'; +export type { CreateVaultFormData } from './vaults'; +export { CREATE_VAULT_DEPOSIT_FIELD, createVaultSchema } from './vaults'; diff --git a/src/lib/form/schemas/loans.ts b/src/lib/form/schemas/loans.ts new file mode 100644 index 0000000000..42dcefcc52 --- /dev/null +++ b/src/lib/form/schemas/loans.ts @@ -0,0 +1,16 @@ +import { LoanAction } from '@/types/loans'; + +import yup, { FeesValidationParams, MaxAmountValidationParams, MinAmountValidationParams } from '../yup.custom'; + +type LoanFormData = Partial>; + +type LoanValidationParams = FeesValidationParams & MaxAmountValidationParams & MinAmountValidationParams; + +const loanSchema = (loanAction: LoanAction, params: LoanValidationParams): yup.ObjectSchema => { + return yup.object().shape({ + [loanAction]: yup.string().requiredAmount(loanAction).maxAmount(params).minAmount(params, loanAction).fees(params) + }); +}; + +export { loanSchema }; +export type { LoanFormData, LoanValidationParams }; diff --git a/src/lib/form/schemas/swap.ts b/src/lib/form/schemas/swap.ts new file mode 100644 index 0000000000..8b9a7855b2 --- /dev/null +++ b/src/lib/form/schemas/swap.ts @@ -0,0 +1,47 @@ +import yup, { FeesValidationParams, MaxAmountValidationParams, MinAmountValidationParams } from '../yup.custom'; + +enum SwapErrorMessage { + SELECT_TOKEN = 'SELECT_TOKEN', + INPUT_REQUIRED = 'INPUT_REQUIRED', + INSUFFICIENT_FUNDS = 'INSUFFICIENT_FUNDS', + UNAVAILABLE_TRADE = 'UNAVAILABLE_TRADE' +} + +const SWAP_INPUT_AMOUNT_FIELD = 'input-amount'; +const SWAP_INPUT_TOKEN_FIELD = 'input-token'; +const SWAP_OUTPUT_TOKEN_FIELD = 'output-token'; + +type SwapFormData = { + [SWAP_INPUT_AMOUNT_FIELD]?: string; + [SWAP_INPUT_TOKEN_FIELD]?: string; + [SWAP_OUTPUT_TOKEN_FIELD]?: string; +}; + +type SwapValidationParams = FeesValidationParams & + Partial & + Partial; + +// Does not follow the normal pattern because this form has a +// custom validation, specially when it comes to error messages +const swapSchema = (params: { [SWAP_INPUT_AMOUNT_FIELD]: SwapValidationParams }): yup.ObjectSchema => + yup.object().shape({ + [SWAP_INPUT_TOKEN_FIELD]: yup.string().required('amm.select_token'), + [SWAP_OUTPUT_TOKEN_FIELD]: yup.string().required('amm.select_token'), + [SWAP_INPUT_AMOUNT_FIELD]: yup.string().when([SWAP_INPUT_TOKEN_FIELD, SWAP_OUTPUT_TOKEN_FIELD], { + is: (input: string, output: string) => input && output, + then: (schema) => + schema + .requiredAmount(undefined, 'amm.enter_token_amount') + // validates if the user inputs 0 + .minAmount(params[SWAP_INPUT_AMOUNT_FIELD] as MinAmountValidationParams, undefined, 'amm.enter_token_amount') + .maxAmount( + params[SWAP_INPUT_AMOUNT_FIELD] as MaxAmountValidationParams, + undefined, + 'amm.insufficient_token_balance' + ) + .fees(params[SWAP_INPUT_AMOUNT_FIELD], 'insufficient_funds_fees') + }) + }); + +export { SWAP_INPUT_AMOUNT_FIELD, SWAP_INPUT_TOKEN_FIELD, SWAP_OUTPUT_TOKEN_FIELD, SwapErrorMessage, swapSchema }; +export type { SwapFormData, SwapValidationParams }; diff --git a/src/lib/form/schemas/vaults.ts b/src/lib/form/schemas/vaults.ts new file mode 100644 index 0000000000..0a348422c0 --- /dev/null +++ b/src/lib/form/schemas/vaults.ts @@ -0,0 +1,22 @@ +import yup, { FeesValidationParams, MaxAmountValidationParams, MinAmountValidationParams } from '../yup.custom'; + +const CREATE_VAULT_DEPOSIT_FIELD = 'deposit'; + +type CreateVaultFormData = { + [CREATE_VAULT_DEPOSIT_FIELD]?: number; +}; + +type CreateVaultValidationParams = FeesValidationParams & MaxAmountValidationParams & MinAmountValidationParams; + +const createVaultSchema = (params: CreateVaultValidationParams): yup.ObjectSchema => + yup.object().shape({ + [CREATE_VAULT_DEPOSIT_FIELD]: yup + .string() + .requiredAmount(CREATE_VAULT_DEPOSIT_FIELD) + .maxAmount(params) + .minAmount(params, CREATE_VAULT_DEPOSIT_FIELD) + .fees(params) + }); + +export { CREATE_VAULT_DEPOSIT_FIELD, createVaultSchema }; +export type { CreateVaultFormData }; diff --git a/src/lib/form/use-form.tsx b/src/lib/form/use-form.tsx new file mode 100644 index 0000000000..48c668f13e --- /dev/null +++ b/src/lib/form/use-form.tsx @@ -0,0 +1,69 @@ +import { + FieldInputProps, + FormikConfig, + FormikErrors as FormErrors, + FormikValues, + useFormik, + validateYupSchema, + yupToFormErrors +} from 'formik'; +import { useCallback } from 'react'; +import { useTranslation } from 'react-i18next'; + +type GetFieldProps = ( + nameOrOptions: any, + withErrorMessage?: boolean +) => FieldInputProps & { errorMessage?: string | string[] }; + +type UseFormAgrs = FormikConfig & { + disableValidation?: boolean; + getFieldProps?: GetFieldProps; +}; + +// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types +const useForm = ({ + validationSchema, + disableValidation, + ...args +}: UseFormAgrs) => { + const { t } = useTranslation(); + const { validateForm, values, getFieldProps: getFormikFieldProps, ...formik } = useFormik({ + ...args, + validate: (values) => { + if (disableValidation) return; + + try { + validateYupSchema(values, validationSchema, true, { t }); + } catch (err) { + return yupToFormErrors(err); + } + } + }); + + const getFieldProps: GetFieldProps = useCallback( + (nameOrOptions, withErrorMessage = true) => { + if (withErrorMessage) { + const isOptions = nameOrOptions !== null && typeof nameOrOptions === 'object'; + const errorMessage = isOptions ? formik.errors[nameOrOptions.name] : formik.errors[nameOrOptions]; + + return { + ...getFormikFieldProps(nameOrOptions), + errorMessage: errorMessage as string | string[] | undefined + }; + } + + return getFormikFieldProps(nameOrOptions); + }, + [formik.errors, getFormikFieldProps] + ); + + return { + values, + validateForm, + getFieldProps, + ...formik + }; +}; + +export { useForm }; +export type { FormErrors }; diff --git a/src/lib/form/utils.ts b/src/lib/form/utils.ts new file mode 100644 index 0000000000..5121b13b12 --- /dev/null +++ b/src/lib/form/utils.ts @@ -0,0 +1,5 @@ +import { useForm } from './use-form'; + +const isFormDisabled = (form: ReturnType): boolean => !form.isValid || !form.dirty; + +export { isFormDisabled }; diff --git a/src/lib/form/yup.custom.ts b/src/lib/form/yup.custom.ts new file mode 100644 index 0000000000..f2195f81aa --- /dev/null +++ b/src/lib/form/yup.custom.ts @@ -0,0 +1,140 @@ +/* eslint-disable no-invalid-this */ +import { CurrencyExt } from '@interlay/interbtc-api'; +import { MonetaryAmount } from '@interlay/monetary-js'; +import Big from 'big.js'; +import { TFunction } from 'react-i18next'; +import * as yup from 'yup'; +import { AnyObject, Maybe } from 'yup/lib/types'; + +type YupContext = { + t: TFunction; +}; + +yup.addMethod(yup.string, 'requiredAmount', function (action: string, customMessage?: string) { + return this.transform((value) => (isNaN(value) ? undefined : value)).test('requiredAmount', (value, ctx) => { + if (value === undefined) { + const { t } = ctx.options.context as YupContext; + + const message = customMessage || t('forms.please_enter_the_amount_to', { field: action }); + return ctx.createError({ message }); + } + + return true; + }); +}); + +type FeesValidationParams = { + transactionFee: MonetaryAmount; + governanceBalance: MonetaryAmount; +}; + +// TODO: remove when fees are moved out of form validation +yup.addMethod( + yup.string, + 'fees', + function ({ transactionFee, governanceBalance }: FeesValidationParams, customMessage?: string) { + return this.test('fees', (_, ctx) => { + const { t } = ctx.options.context as YupContext; + + if (governanceBalance.lt(transactionFee)) { + const message = + customMessage || + t('insufficient_funds_governance_token', { + governanceTokenSymbol: transactionFee.currency.ticker + }); + + return ctx.createError({ message }); + } + + return true; + }); + } +); + +type MaxAmountValidationParams = { + maxAmount: MonetaryAmount | Big; +}; + +yup.addMethod( + yup.string, + 'maxAmount', + function ({ maxAmount }: MaxAmountValidationParams, action?: string, customMessage?: string) { + return this.test('maxAmount', (value, ctx) => { + const { t } = ctx.options.context as YupContext; + + if (value === undefined) return true; + + const amount = new Big(value); + + const isMonetaryAmount = (maxAmount as MonetaryAmount).currency; + + // same validation, just different data types that lead to different implementation + if (isMonetaryAmount && amount.gt((maxAmount as MonetaryAmount).toBig())) { + const message = customMessage || t('forms.please_enter_no_higher_available_balance'); + return ctx.createError({ message }); + } + + if (amount.gt(maxAmount as Big)) { + const message = customMessage || t('forms.amount_must_be_at_most', { action, amount: maxAmount.toString() }); + return ctx.createError({ message }); + } + + return true; + }); + } +); + +type MinAmountValidationParams = { + minAmount: MonetaryAmount; +}; + +yup.addMethod( + yup.string, + 'minAmount', + function ({ minAmount }: MinAmountValidationParams, action: string, customMessage?: string) { + return this.test('balance', (value, ctx) => { + const { t } = ctx.options.context as YupContext; + + if (value === undefined) return true; + + const amount = new Big(value); + + if (amount.lt(minAmount.toBig())) { + const message = + customMessage || + t('forms.amount_must_be_at_least', { + action, + amount: minAmount.toString(), + token: minAmount.currency.ticker + }); + return ctx.createError({ message }); + } + + return true; + }); + } +); + +declare module 'yup' { + interface StringSchema< + TType extends Maybe = string | undefined, + TContext extends AnyObject = AnyObject, + TOut extends TType = TType + > extends yup.BaseSchema { + requiredAmount(action?: string, customMessage?: string): StringSchema; + fees(params: FeesValidationParams, customMessage?: string): StringSchema; + maxAmount( + params: MaxAmountValidationParams, + action?: string, + customMessage?: string + ): StringSchema; + minAmount( + params: MinAmountValidationParams, + action?: string, + customMessage?: string + ): StringSchema; + } +} + +export default yup; +export type { FeesValidationParams, MaxAmountValidationParams, MinAmountValidationParams, YupContext }; diff --git a/src/lib/substrate/components/DeveloperConsole/index.ts b/src/lib/substrate/components/DeveloperConsole/index.ts deleted file mode 100644 index c1669264b3..0000000000 --- a/src/lib/substrate/components/DeveloperConsole/index.ts +++ /dev/null @@ -1,33 +0,0 @@ -// This component will simply add utility functions to your developer console. -import { ApiPromise } from '@polkadot/api'; -import { Keyring } from '@polkadot/ui-keyring/Keyring'; - -import { useSubstrateState } from '@/lib/substrate/context/hooks'; -import { ApiStatus, KeyringStatus } from '@/lib/substrate/context/types'; - -declare global { - interface Window { - keyring: Keyring | undefined; - api: ApiPromise | undefined; - // TODO: should type properly - util: any; - utilCrypto: any; - } -} - -const DeveloperConsole = (): null => { - const { api, apiStatus, keyring, keyringStatus } = useSubstrateState(); - - if (apiStatus === ApiStatus.Ready) { - window.api = api; - } - if (keyringStatus === KeyringStatus.Ready) { - window.keyring = keyring; - } - window.util = require('@polkadot/util'); - window.utilCrypto = require('@polkadot/util-crypto'); - - return null; -}; - -export default DeveloperConsole; diff --git a/src/lib/substrate/components/SubstrateLoadingAndErrorHandlingWrapper/index.tsx b/src/lib/substrate/components/SubstrateLoadingAndErrorHandlingWrapper/index.tsx index 8aff1c31ef..1c378b87cf 100644 --- a/src/lib/substrate/components/SubstrateLoadingAndErrorHandlingWrapper/index.tsx +++ b/src/lib/substrate/components/SubstrateLoadingAndErrorHandlingWrapper/index.tsx @@ -2,7 +2,7 @@ import { useDispatch } from 'react-redux'; import { toast, ToastContainer } from 'react-toastify'; import { isBridgeLoaded } from '@/common/actions/general.actions'; -import FullLoadingSpinner from '@/components/FullLoadingSpinner'; +import FullLoadingSpinner from '@/legacy-components/FullLoadingSpinner'; import { useSubstrateState } from '@/lib/substrate/context/hooks'; import { ActionType, ApiStatus, KeyringStatus } from '@/lib/substrate/context/types'; import InterlayHelmet from '@/parts/InterlayHelmet'; diff --git a/src/lib/substrate/context/provider.tsx b/src/lib/substrate/context/provider.tsx index c0eb0e66f0..23f4e6d840 100644 --- a/src/lib/substrate/context/provider.tsx +++ b/src/lib/substrate/context/provider.tsx @@ -51,7 +51,9 @@ const connect = async (state: State, dispatch: Dispatch) => { dispatch({ type: ActionType.ConnectInit }); - console.log(`Connected socket: ${socket}`); + if (socket) { + console.log(`Connected socket: ${socket}`); + } window.bridge = await createInterBtcApi(constants.PARACHAIN_URL, constants.BITCOIN_NETWORK); const _api = window.bridge.api; diff --git a/src/pages/AMM/Pools/Pools.tsx b/src/pages/AMM/Pools/Pools.tsx new file mode 100644 index 0000000000..55ddf9d1b3 --- /dev/null +++ b/src/pages/AMM/Pools/Pools.tsx @@ -0,0 +1,29 @@ +import FullLoadingSpinner from '@/legacy-components/FullLoadingSpinner'; +import MainContainer from '@/parts/MainContainer'; +import { useGetAccountPools } from '@/utils/hooks/api/amm/use-get-account-pools'; +import { useGetLiquidityPools } from '@/utils/hooks/api/amm/use-get-liquidity-pools'; +import useAccountId from '@/utils/hooks/use-account-id'; + +import { PoolsInsights, PoolsTables } from './components'; + +const Pools = (): JSX.Element => { + const accountId = useAccountId(); + const { data: accountPoolsData, refetch: refetchAccountPools } = useGetAccountPools(); + const { data: pools } = useGetLiquidityPools(); + + const accountPositions = accountPoolsData?.positions; + const isLoadingAccountData = accountId !== undefined && accountPoolsData === undefined; + + if (pools === undefined || isLoadingAccountData) { + return ; + } + + return ( + + + + + ); +}; + +export default Pools; diff --git a/src/pages/AMM/Pools/components/DepositForm/DepositDivider.tsx b/src/pages/AMM/Pools/components/DepositForm/DepositDivider.tsx new file mode 100644 index 0000000000..89ff283a43 --- /dev/null +++ b/src/pages/AMM/Pools/components/DepositForm/DepositDivider.tsx @@ -0,0 +1,15 @@ +import { PlusCircle } from '@/assets/icons'; + +import { StyledBackground, StyledCircle, StyledDivider, StyledWrapper } from './DepositForm.styles'; + +const DepositDivider = (): JSX.Element => ( + + + + + + + +); + +export { DepositDivider }; diff --git a/src/pages/AMM/Pools/components/DepositForm/DepositForm.styles.tsx b/src/pages/AMM/Pools/components/DepositForm/DepositForm.styles.tsx new file mode 100644 index 0000000000..eb266ea036 --- /dev/null +++ b/src/pages/AMM/Pools/components/DepositForm/DepositForm.styles.tsx @@ -0,0 +1,48 @@ +import styled from 'styled-components'; + +import { Divider, Dl, DlGroup, theme } from '@/component-library'; + +const StyledDl = styled(Dl)` + background-color: ${theme.card.secondaryBg}; + padding: ${theme.spacing.spacing4}; + font-size: ${theme.text.xs}; + border-radius: ${theme.rounded.rg}; +`; + +const StyledWrapper = styled.div` + position: relative; +`; + +const StyledCircle = styled.div` + display: inline-flex; + position: absolute; + left: 50%; + top: 50%; + transform: translate(-50%, -50%); + padding: ${theme.spacing.spacing2}; + background-color: var(--colors-token-input-end-adornment-bg); + border-radius: ${theme.rounded.full}; +`; + +const StyledBackground = styled.div` + position: absolute; + left: 50%; + top: 50%; + transform: translate(-50%, -50%); + padding: ${theme.spacing.spacing1} ${theme.spacing.spacing8}; + background-color: ${theme.colors.bgPrimary}; +`; + +const StyledDivider = styled(Divider)` + background-color: var(--colors-token-input-end-adornment-bg); +`; + +const StyledDlGroup = styled(DlGroup)` + flex-direction: column; + + @media (min-width: 30em) { + flex-direction: row; + } +`; + +export { StyledBackground, StyledCircle, StyledDivider, StyledDl, StyledDlGroup, StyledWrapper }; diff --git a/src/pages/AMM/Pools/components/DepositForm/DepositForm.tsx b/src/pages/AMM/Pools/components/DepositForm/DepositForm.tsx new file mode 100644 index 0000000000..9f37555e45 --- /dev/null +++ b/src/pages/AMM/Pools/components/DepositForm/DepositForm.tsx @@ -0,0 +1,203 @@ +import { CurrencyExt, LiquidityPool, newMonetaryAmount, PooledCurrencies } from '@interlay/interbtc-api'; +import { AccountId } from '@polkadot/types/interfaces'; +import { mergeProps } from '@react-aria/utils'; +import Big from 'big.js'; +import { ChangeEventHandler, RefObject, useState } from 'react'; +import { useTranslation } from 'react-i18next'; +import { useMutation } from 'react-query'; +import { toast } from 'react-toastify'; + +import { displayMonetaryAmountInUSDFormat, newSafeMonetaryAmount } from '@/common/utils/utils'; +import { Dd, DlGroup, Dt, Flex, TokenInput } from '@/component-library'; +import { AuthCTA } from '@/components'; +import { GOVERNANCE_TOKEN, TRANSACTION_FEE_AMOUNT } from '@/config/relay-chains'; +import { + DepositLiquidityPoolFormData, + depositLiquidityPoolSchema, + DepositLiquidityPoolValidationParams, + isFormDisabled, + useForm +} from '@/lib/form'; +import { SlippageManager } from '@/pages/AMM/shared/components'; +import { AMM_DEADLINE_INTERVAL } from '@/utils/constants/api'; +import { getTokenPrice } from '@/utils/helpers/prices'; +import { useGetBalances } from '@/utils/hooks/api/tokens/use-get-balances'; +import { useGetPrices } from '@/utils/hooks/api/use-get-prices'; +import useAccountId from '@/utils/hooks/use-account-id'; + +import { PoolName } from '../PoolName'; +import { DepositDivider } from './DepositDivider'; +import { StyledDl } from './DepositForm.styles'; +import { DepositOutputAssets } from './DepositOutputAssets'; + +const isCustomAmountsMode = (form: ReturnType) => + form.dirty && Object.values(form.touched).filter(Boolean).length > 0; + +type DepositData = { + amounts: PooledCurrencies; + pool: LiquidityPool; + slippage: number; + deadline: number; + accountId: AccountId; +}; + +const mutateDeposit = ({ amounts, pool, slippage, deadline, accountId }: DepositData) => + window.bridge.amm.addLiquidity(amounts, pool, slippage, deadline, accountId); + +type DepositFormProps = { + pool: LiquidityPool; + slippageModalRef: RefObject; + onDeposit?: () => void; +}; + +const DepositForm = ({ pool, slippageModalRef, onDeposit }: DepositFormProps): JSX.Element => { + const { pooledCurrencies } = pool; + const defaultValues = pooledCurrencies.reduce((acc, amount) => ({ ...acc, [amount.currency.ticker]: '' }), {}); + + const [slippage, setSlippage] = useState(0.1); + + const accountId = useAccountId(); + const { t } = useTranslation(); + const { getAvailableBalance, getBalance } = useGetBalances(); + const prices = useGetPrices(); + + const governanceBalance = getBalance(GOVERNANCE_TOKEN.ticker)?.free || newMonetaryAmount(0, GOVERNANCE_TOKEN); + + const depositMutation = useMutation(mutateDeposit, { + onSuccess: () => { + onDeposit?.(); + toast.success('Deposit successful'); + }, + onError: (error) => { + toast.error(error.message); + } + }); + + const handleSubmit = async (data: DepositLiquidityPoolFormData) => { + if (!accountId) return; + + try { + const amounts = pooledCurrencies.map((amount) => + newSafeMonetaryAmount(data[amount.currency.ticker] || 0, amount.currency, true) + ); + + const deadline = await window.bridge.system.getFutureBlockNumber(AMM_DEADLINE_INTERVAL); + + return depositMutation.mutate({ amounts, pool, slippage, deadline, accountId }); + } catch (err: any) { + toast.error(err.toString()); + } + }; + + const tokens = pooledCurrencies.reduce( + (acc: DepositLiquidityPoolValidationParams['tokens'], pooled) => ({ + ...acc, + [pooled.currency.ticker]: { + minAmount: newMonetaryAmount(1, pooled.currency), + maxAmount: getAvailableBalance(pooled.currency.ticker) || newMonetaryAmount(0, pooled.currency) + } + }), + {} + ); + + const form = useForm({ + initialValues: defaultValues, + validationSchema: depositLiquidityPoolSchema({ transactionFee: TRANSACTION_FEE_AMOUNT, governanceBalance, tokens }), + onSubmit: handleSubmit, + disableValidation: depositMutation.isLoading + }); + + const handleChange: ChangeEventHandler = (e) => { + if (isCustomAmountsMode(form)) return; + + if (!e.target.value || isNaN(Number(e.target.value))) { + return form.setValues(defaultValues); + } + + const inputCurrency = pooledCurrencies.find((currency) => currency.currency.ticker === e.target.name); + const inputAmount = newSafeMonetaryAmount(e.target.value || 0, inputCurrency?.currency as CurrencyExt, true); + + const amounts = pool.getLiquidityDepositInputAmounts(inputAmount); + + const newValues = amounts.reduce((acc, val) => { + if (val.currency.ticker === inputCurrency?.currency.ticker) { + return { ...acc, [val.currency.ticker]: e.target.value ? e.target.value : undefined }; + } + + return { ...acc, [val.currency.ticker]: val.toBig().toString() }; + }, {}); + + form.setValues(newValues); + }; + + const poolName = ( + amount.currency.ticker)} /> + ); + + const isBtnDisabled = isFormDisabled(form); + + return ( +
+ + setSlippage(slippage)} /> + {poolName} + + + {pooledCurrencies.map((amount, index) => { + const { + currency: { ticker } + } = amount; + + const isLastItem = index === pooledCurrencies.length - 1; + + const balance = getAvailableBalance(ticker); + + return ( + + + {!isLastItem && } + + ); + })} + + + + +
+ {t('fees')} +
+
+ {TRANSACTION_FEE_AMOUNT.toHuman()} {TRANSACTION_FEE_AMOUNT.currency.ticker} ( + {displayMonetaryAmountInUSDFormat( + TRANSACTION_FEE_AMOUNT, + getTokenPrice(prices, TRANSACTION_FEE_AMOUNT.currency.ticker)?.usd + )} + ) +
+
+
+ + {t('amm.pools.add_liquidity')} + +
+
+ + ); +}; + +DepositForm.displayName = 'DepositForm'; + +export { DepositForm }; +export type { DepositFormProps }; diff --git a/src/pages/AMM/Pools/components/DepositForm/DepositOutputAssets.tsx b/src/pages/AMM/Pools/components/DepositForm/DepositOutputAssets.tsx new file mode 100644 index 0000000000..5a17465ec9 --- /dev/null +++ b/src/pages/AMM/Pools/components/DepositForm/DepositOutputAssets.tsx @@ -0,0 +1,78 @@ +import { LiquidityPool } from '@interlay/interbtc-api'; +import Big from 'big.js'; +import { useMemo } from 'react'; +import { useTranslation } from 'react-i18next'; + +import { + convertMonetaryAmountToValueInUSD, + formatNumber, + formatUSD, + newSafeMonetaryAmount +} from '@/common/utils/utils'; +import { Dd, Dl, Dt, Flex, P } from '@/component-library'; +import { getTokenPrice } from '@/utils/helpers/prices'; +import { Prices } from '@/utils/hooks/api/use-get-prices'; + +import { PoolName } from '../PoolName'; +import { StyledDlGroup } from './DepositForm.styles'; + +type DepositOutputAssetsProps = { + pool: LiquidityPool; + values: Record; + prices?: Prices; +}; + +const DepositOutputAssets = ({ pool, values, prices }: DepositOutputAssetsProps): JSX.Element => { + const { t } = useTranslation(); + const { pooledCurrencies } = pool; + + const poolName = ( + currency.currency.ticker)} /> + ); + + const lpTokenMonetaryAmount = pool.getLiquidityDepositLpTokenAmount( + newSafeMonetaryAmount(values[pooledCurrencies[0].currency.ticker] || 0, pooledCurrencies[0].currency, true) + ); + + const lpTokenAmountUSD = useMemo( + () => + pooledCurrencies + .reduce((acc, curr) => { + const assetMonetary = newSafeMonetaryAmount(values[curr.currency.ticker] || 0, curr.currency, true); + const amountUSD = + convertMonetaryAmountToValueInUSD(assetMonetary, getTokenPrice(prices, curr.currency.ticker)?.usd) || 0; + + return acc.add(amountUSD); + }, new Big(0)) + .toNumber(), + [pooledCurrencies, prices, values] + ); + + const lpTokenAmount = formatNumber(lpTokenMonetaryAmount.toBig().toNumber(), { + maximumFractionDigits: lpTokenMonetaryAmount.currency.humanDecimals || lpTokenMonetaryAmount.currency.decimals, + compact: true + }); + + return ( + +

+ {t('amm.pools.receivable_assets')} +

+
+ +
+ {poolName} +
+
+ {lpTokenAmount} ({formatUSD(lpTokenAmountUSD, { compact: true })}) +
+
+
+
+ ); +}; + +DepositOutputAssets.displayName = 'DepositOutputAssets'; + +export { DepositOutputAssets }; +export type { DepositOutputAssetsProps }; diff --git a/src/pages/AMM/Pools/components/DepositForm/index.tsx b/src/pages/AMM/Pools/components/DepositForm/index.tsx new file mode 100644 index 0000000000..27a929a23e --- /dev/null +++ b/src/pages/AMM/Pools/components/DepositForm/index.tsx @@ -0,0 +1,2 @@ +export type { DepositFormProps } from './DepositForm'; +export { DepositForm } from './DepositForm'; diff --git a/src/pages/AMM/Pools/components/PoolModal/PoolModal.style.tsx b/src/pages/AMM/Pools/components/PoolModal/PoolModal.style.tsx new file mode 100644 index 0000000000..a5d267eb8a --- /dev/null +++ b/src/pages/AMM/Pools/components/PoolModal/PoolModal.style.tsx @@ -0,0 +1,13 @@ +import styled from 'styled-components'; + +import { Tabs, theme } from '@/component-library'; + +const StyledTabs = styled(Tabs)` + padding: ${theme.spacing.spacing14} ${theme.modal.header.paddingX} ${theme.modal.footer.paddingBottom}; +`; + +const StyledWrapper = styled.div` + margin-top: ${theme.spacing.spacing2}; +`; + +export { StyledTabs, StyledWrapper }; diff --git a/src/pages/AMM/Pools/components/PoolModal/PoolModal.tsx b/src/pages/AMM/Pools/components/PoolModal/PoolModal.tsx new file mode 100644 index 0000000000..724e4d8ea3 --- /dev/null +++ b/src/pages/AMM/Pools/components/PoolModal/PoolModal.tsx @@ -0,0 +1,62 @@ +import { LiquidityPool } from '@interlay/interbtc-api'; +import { useRef } from 'react'; +import { useTranslation } from 'react-i18next'; + +import { Modal, ModalBody, ModalProps, TabsItem } from '@/component-library'; +import { useGetAccountPools } from '@/utils/hooks/api/amm/use-get-account-pools'; + +import { DepositForm } from '../DepositForm'; +import { WithdrawForm } from '../WithdrawForm'; +import { StyledTabs, StyledWrapper } from './PoolModal.style'; + +type Props = { + pool?: LiquidityPool; +}; + +type InheritAttrs = Omit; + +type PoolModalProps = Props & InheritAttrs; + +const PoolModal = ({ pool, onClose, ...props }: PoolModalProps): JSX.Element | null => { + const { t } = useTranslation(); + const { refetch } = useGetAccountPools(); + const ref = useRef(null); + + if (!pool) { + return null; + } + + const handleAction = () => { + refetch(); + onClose?.(); + }; + + return ( + !ref.current?.contains(el)} + {...props} + > + + + + + + + + + + + + + + + + ); +}; + +export { PoolModal }; +export type { PoolModalProps }; diff --git a/src/pages/AMM/Pools/components/PoolModal/index.tsx b/src/pages/AMM/Pools/components/PoolModal/index.tsx new file mode 100644 index 0000000000..1124222148 --- /dev/null +++ b/src/pages/AMM/Pools/components/PoolModal/index.tsx @@ -0,0 +1,2 @@ +export type { PoolModalProps } from './PoolModal'; +export { PoolModal } from './PoolModal'; diff --git a/src/pages/AMM/Pools/components/PoolName/PoolName.style.tsx b/src/pages/AMM/Pools/components/PoolName/PoolName.style.tsx new file mode 100644 index 0000000000..2397be496d --- /dev/null +++ b/src/pages/AMM/Pools/components/PoolName/PoolName.style.tsx @@ -0,0 +1,14 @@ +import styled from 'styled-components'; + +import { Span } from '@/component-library'; + +const StyledSpan = styled(Span)` + display: none; + white-space: nowrap; + + @media (min-width: 30em) { + display: block; + } +`; + +export { StyledSpan }; diff --git a/src/pages/AMM/Pools/components/PoolName/PoolName.tsx b/src/pages/AMM/Pools/components/PoolName/PoolName.tsx new file mode 100644 index 0000000000..26ed2bcea3 --- /dev/null +++ b/src/pages/AMM/Pools/components/PoolName/PoolName.tsx @@ -0,0 +1,23 @@ +import { Flex, FlexProps, TokenStack } from '@/component-library'; + +import { StyledSpan } from './PoolName.style'; + +type Props = { + tickers: string[]; +}; + +type InheritAttrs = Omit; + +type PoolNameProps = Props & InheritAttrs; + +const PoolName = ({ tickers, ...props }: PoolNameProps): JSX.Element => ( + + + + {tickers.join(' - ')} + + +); + +export { PoolName }; +export type { PoolNameProps }; diff --git a/src/pages/AMM/Pools/components/PoolName/index.tsx b/src/pages/AMM/Pools/components/PoolName/index.tsx new file mode 100644 index 0000000000..cd2ccf76dd --- /dev/null +++ b/src/pages/AMM/Pools/components/PoolName/index.tsx @@ -0,0 +1,2 @@ +export type { PoolNameProps } from './PoolName'; +export { PoolName } from './PoolName'; diff --git a/src/pages/AMM/Pools/components/PoolsBaseTable/BalanceCell.tsx b/src/pages/AMM/Pools/components/PoolsBaseTable/BalanceCell.tsx new file mode 100644 index 0000000000..5bc768c357 --- /dev/null +++ b/src/pages/AMM/Pools/components/PoolsBaseTable/BalanceCell.tsx @@ -0,0 +1,25 @@ +import { CurrencyExt } from '@interlay/interbtc-api'; +import { MonetaryAmount } from '@interlay/monetary-js'; + +import { formatUSD } from '@/common/utils/utils'; +import { AlignItems } from '@/component-library'; + +import { MonetaryCell } from './MonetaryCell'; + +type BalanceCellProps = { + amount: MonetaryAmount; + amountUSD: number; + alignItems?: AlignItems; +}; + +const BalanceCell = ({ amount, amountUSD, alignItems }: BalanceCellProps): JSX.Element => ( + +); + +export { BalanceCell }; +export type { BalanceCellProps }; diff --git a/src/pages/Loans/LoansOverview/components/LoansBaseTable/MonetaryCell.tsx b/src/pages/AMM/Pools/components/PoolsBaseTable/MonetaryCell.tsx similarity index 92% rename from src/pages/Loans/LoansOverview/components/LoansBaseTable/MonetaryCell.tsx rename to src/pages/AMM/Pools/components/PoolsBaseTable/MonetaryCell.tsx index b9b3f97d26..2f1de2b3db 100644 --- a/src/pages/Loans/LoansOverview/components/LoansBaseTable/MonetaryCell.tsx +++ b/src/pages/AMM/Pools/components/PoolsBaseTable/MonetaryCell.tsx @@ -2,7 +2,7 @@ import { forwardRef } from 'react'; import { AlignItems, Colors, Flex, FlexProps } from '@/component-library'; -import { StyledCellLabel, StyledCellSubLabel } from './LoansBaseTable.style'; +import { StyledCellLabel, StyledCellSubLabel } from './PoolsBaseTable.style'; type Props = { label?: string; diff --git a/src/pages/AMM/Pools/components/PoolsBaseTable/PoolsBaseTable.style.tsx b/src/pages/AMM/Pools/components/PoolsBaseTable/PoolsBaseTable.style.tsx new file mode 100644 index 0000000000..3f87340283 --- /dev/null +++ b/src/pages/AMM/Pools/components/PoolsBaseTable/PoolsBaseTable.style.tsx @@ -0,0 +1,26 @@ +import styled from 'styled-components'; + +import { H2, Span, theme } from '@/component-library'; + +const StyledTitle = styled(H2)` + font-size: ${theme.text.xl}; + font-weight: ${theme.fontWeight.bold}; +`; + +const StyledCellLabel = styled(Span)` + font-weight: ${theme.fontWeight.bold}; + font-size: ${theme.text.s}; + display: inline-flex; + gap: ${theme.spacing.spacing1}; + align-items: baseline; +`; + +const StyledCellTickerLabel = styled(Span)` + font-size: ${theme.text.xs}; +`; + +const StyledCellSubLabel = styled(Span)` + font-size: ${theme.text.xs}; +`; + +export { StyledCellLabel, StyledCellSubLabel, StyledCellTickerLabel, StyledTitle }; diff --git a/src/pages/Loans/LoansOverview/components/LoansBaseTable/LoansBaseTable.tsx b/src/pages/AMM/Pools/components/PoolsBaseTable/PoolsBaseTable.tsx similarity index 81% rename from src/pages/Loans/LoansOverview/components/LoansBaseTable/LoansBaseTable.tsx rename to src/pages/AMM/Pools/components/PoolsBaseTable/PoolsBaseTable.tsx index d660e48a60..9e5e40f518 100644 --- a/src/pages/Loans/LoansOverview/components/LoansBaseTable/LoansBaseTable.tsx +++ b/src/pages/AMM/Pools/components/PoolsBaseTable/PoolsBaseTable.tsx @@ -2,7 +2,7 @@ import { useId } from '@react-aria/utils'; import { Card, Flex, P, Strong, Table, TableProps } from '@/component-library'; -import { StyledTitle } from './LoansBaseTable.style'; +import { StyledTitle } from './PoolsBaseTable.style'; type Props = { title: string; @@ -13,9 +13,9 @@ type Props = { type InheritAttrs = Omit; -type LoansBaseTableProps = Props & InheritAttrs; +type PoolsBaseTableProps = Props & InheritAttrs; -const LoansBaseTable = ({ +const PoolsBaseTable = ({ title, rows, columns, @@ -23,7 +23,7 @@ const LoansBaseTable = ({ emptyDescription, className, ...props -}: LoansBaseTableProps): JSX.Element => { +}: PoolsBaseTableProps): JSX.Element => { const titleId = useId(); const hasRows = !!rows.length; @@ -47,5 +47,5 @@ const LoansBaseTable = ({ ); }; -export { LoansBaseTable }; -export type { LoansBaseTableProps }; +export { PoolsBaseTable }; +export type { PoolsBaseTableProps }; diff --git a/src/pages/AMM/Pools/components/PoolsBaseTable/index.tsx b/src/pages/AMM/Pools/components/PoolsBaseTable/index.tsx new file mode 100644 index 0000000000..e18bbf1944 --- /dev/null +++ b/src/pages/AMM/Pools/components/PoolsBaseTable/index.tsx @@ -0,0 +1,4 @@ +export type { BalanceCellProps } from './BalanceCell'; +export { BalanceCell } from './BalanceCell'; +export type { PoolsBaseTableProps } from './PoolsBaseTable'; +export { PoolsBaseTable } from './PoolsBaseTable'; diff --git a/src/pages/AMM/Pools/components/PoolsInsights/PoolsInsights.style.tsx b/src/pages/AMM/Pools/components/PoolsInsights/PoolsInsights.style.tsx new file mode 100644 index 0000000000..700a6824d8 --- /dev/null +++ b/src/pages/AMM/Pools/components/PoolsInsights/PoolsInsights.style.tsx @@ -0,0 +1,14 @@ +import styled from 'styled-components'; + +import { Dd, Dt, theme } from '@/component-library'; + +const StyledDt = styled(Dt)` + font-weight: ${theme.fontWeight.semibold}; + white-space: nowrap; +`; + +const StyledDd = styled(Dd)` + font-weight: ${theme.fontWeight.bold}; +`; + +export { StyledDd, StyledDt }; diff --git a/src/pages/AMM/Pools/components/PoolsInsights/PoolsInsights.tsx b/src/pages/AMM/Pools/components/PoolsInsights/PoolsInsights.tsx new file mode 100644 index 0000000000..ffbea39d84 --- /dev/null +++ b/src/pages/AMM/Pools/components/PoolsInsights/PoolsInsights.tsx @@ -0,0 +1,98 @@ +import { LiquidityPool } from '@interlay/interbtc-api'; +import Big from 'big.js'; +import { useTranslation } from 'react-i18next'; +import { useMutation } from 'react-query'; +import { toast } from 'react-toastify'; + +import { formatUSD } from '@/common/utils/utils'; +import { Card, CTA, Dl, DlGroup } from '@/component-library'; +import { calculateAccountLiquidityUSD, calculateTotalLiquidityUSD } from '@/pages/AMM/shared/utils'; +import { AccountPoolsData } from '@/utils/hooks/api/amm/use-get-account-pools'; +import { useGetPrices } from '@/utils/hooks/api/use-get-prices'; + +import { StyledDd, StyledDt } from './PoolsInsights.style'; +import { calculateClaimableFarmingRewardUSD } from './utils'; + +type PoolsInsightsProps = { + pools: LiquidityPool[]; + accountPoolsData?: AccountPoolsData; + refetch: () => void; +}; + +const PoolsInsights = ({ pools, accountPoolsData, refetch }: PoolsInsightsProps): JSX.Element => { + const { t } = useTranslation(); + const prices = useGetPrices(); + + const accountPositions = accountPoolsData?.positions; + + const supplyAmountUSD = accountPositions?.reduce((acc, curr) => { + const totalLiquidityUSD = calculateTotalLiquidityUSD(curr.data.pooledCurrencies, prices); + + const accountLiquidityUSD = curr.amount + ? calculateAccountLiquidityUSD(curr.amount, totalLiquidityUSD, curr.data.totalSupply) + : 0; + + return acc.add(accountLiquidityUSD); + }, new Big(0)); + + const supplyBalanceLabel = supplyAmountUSD ? formatUSD(supplyAmountUSD.toNumber() || 0, { compact: true }) : '-'; + + const totalLiquidity = pools.reduce((acc, pool) => { + const poolLiquidityUSD = calculateTotalLiquidityUSD(pool.pooledCurrencies, prices); + + return acc.add(poolLiquidityUSD); + }, new Big(0)); + + const totalLiquidityUSD = formatUSD(totalLiquidity?.toNumber() || 0, { compact: true }); + + const totalClaimableRewardUSD = calculateClaimableFarmingRewardUSD(accountPoolsData?.claimableRewards, prices); + + const handleSuccess = () => { + toast.success(t('successfully_claimed_rewards')); + refetch(); + }; + + const mutateClaimRewards = async () => { + if (accountPoolsData !== undefined) { + await window.bridge.amm.claimFarmingRewards(accountPoolsData.claimableRewards); + } + }; + + const claimRewardsMutation = useMutation(mutateClaimRewards, { + onSuccess: handleSuccess + }); + + const handleClickClaimRewards = () => claimRewardsMutation.mutate(); + + const hasClaimableRewards = totalClaimableRewardUSD > 0; + return ( +
+ + + {t('supply_balance')} + {supplyBalanceLabel} + + + + + {t('total_liquidity')} + {totalLiquidityUSD} + + + + + {t('rewards')} + {formatUSD(totalClaimableRewardUSD, { compact: true })} + + {hasClaimableRewards && ( + + Claim + + )} + +
+ ); +}; + +export { PoolsInsights }; +export type { PoolsInsightsProps }; diff --git a/src/pages/AMM/Pools/components/PoolsInsights/index.tsx b/src/pages/AMM/Pools/components/PoolsInsights/index.tsx new file mode 100644 index 0000000000..a904d1161a --- /dev/null +++ b/src/pages/AMM/Pools/components/PoolsInsights/index.tsx @@ -0,0 +1,2 @@ +export type { PoolsInsightsProps } from './PoolsInsights'; +export { PoolsInsights } from './PoolsInsights'; diff --git a/src/pages/AMM/Pools/components/PoolsInsights/utils.ts b/src/pages/AMM/Pools/components/PoolsInsights/utils.ts new file mode 100644 index 0000000000..f5d171d2a3 --- /dev/null +++ b/src/pages/AMM/Pools/components/PoolsInsights/utils.ts @@ -0,0 +1,24 @@ +import { CurrencyExt, LpCurrency } from '@interlay/interbtc-api'; +import { MonetaryAmount } from '@interlay/monetary-js'; + +import { calculateTotalLiquidityUSD } from '@/pages/AMM/shared/utils'; +import { Prices } from '@/utils/hooks/api/use-get-prices'; + +const calculateClaimableFarmingRewardUSD = ( + claimableRewards: Map[]> | undefined, + prices: Prices | undefined +): number => { + if (claimableRewards === undefined) { + return 0; + } + const flattenedRewardAmounts: Array> = []; + for (const [, rewardAmounts] of claimableRewards.entries()) { + flattenedRewardAmounts.push(...rewardAmounts); + } + + const totalRewardUSD = calculateTotalLiquidityUSD(flattenedRewardAmounts, prices); + + return totalRewardUSD; +}; + +export { calculateClaimableFarmingRewardUSD }; diff --git a/src/pages/AMM/Pools/components/PoolsTables/PoolsTables.tsx b/src/pages/AMM/Pools/components/PoolsTables/PoolsTables.tsx new file mode 100644 index 0000000000..7f4d6840ff --- /dev/null +++ b/src/pages/AMM/Pools/components/PoolsTables/PoolsTables.tsx @@ -0,0 +1,51 @@ +import { isCurrencyEqual, LiquidityPool } from '@interlay/interbtc-api'; +import { Key, useState } from 'react'; + +import { Flex } from '@/component-library'; +import { PoolsTable } from '@/components'; +import { AccountLiquidityPool } from '@/utils/hooks/api/amm/use-get-account-pools'; + +import { PoolModal } from '../PoolModal/PoolModal'; + +type PoolsTablesProps = { + pools: LiquidityPool[]; + accountPools?: AccountLiquidityPool[]; +}; + +const PoolsTables = ({ pools, accountPools }: PoolsTablesProps): JSX.Element => { + const [liquidityPool, setLiquidityPool] = useState(); + + const handleRowAction = (ticker: Key) => { + const pool = pools.find((pool) => pool.lpToken.ticker === ticker); + setLiquidityPool(pool); + }; + + const handleClose = () => setLiquidityPool(undefined); + + const otherPools = accountPools + ? pools.filter( + (pool) => !accountPools.find((accountPool) => isCurrencyEqual(accountPool.amount.currency, pool.lpToken)) + ) + : pools; + + return ( + <> + + {!!accountPools?.length && ( + + )} + {!!otherPools.length && ( + ({ data: pool }))} + onRowAction={handleRowAction} + /> + )} + + + + ); +}; + +export { PoolsTables }; +export type { PoolsTablesProps }; diff --git a/src/pages/AMM/Pools/components/PoolsTables/index.tsx b/src/pages/AMM/Pools/components/PoolsTables/index.tsx new file mode 100644 index 0000000000..99dcbc93aa --- /dev/null +++ b/src/pages/AMM/Pools/components/PoolsTables/index.tsx @@ -0,0 +1,2 @@ +export type { PoolsTablesProps } from './PoolsTables'; +export { PoolsTables } from './PoolsTables'; diff --git a/src/pages/AMM/Pools/components/PoolsTables/utils.ts b/src/pages/AMM/Pools/components/PoolsTables/utils.ts new file mode 100644 index 0000000000..fb29bc9922 --- /dev/null +++ b/src/pages/AMM/Pools/components/PoolsTables/utils.ts @@ -0,0 +1,24 @@ +import { CurrencyExt, LpCurrency } from '@interlay/interbtc-api'; +import { MonetaryAmount } from '@interlay/monetary-js'; +import Big from 'big.js'; + +import { calculateTotalLiquidityUSD } from '@/pages/AMM/shared/utils'; +import { Prices } from '@/utils/hooks/api/use-get-prices'; + +const getFarmingApr = ( + rewardAmountsYearly: Array>, + lpTotalSupply: MonetaryAmount, + totalLiquidityUSD: number, + prices: Prices | undefined +): Big => { + if (prices === undefined || lpTotalSupply.toBig().eq(0) || totalLiquidityUSD === 0) { + return new Big(0); + } + const totalRewardsPerTokenUSD = calculateTotalLiquidityUSD(rewardAmountsYearly, prices); + + const farmingApr = new Big(totalRewardsPerTokenUSD).div(totalLiquidityUSD).mul(100); + + return farmingApr; +}; + +export { getFarmingApr }; diff --git a/src/pages/AMM/Pools/components/WithdrawForm/WithdrawAssets.tsx b/src/pages/AMM/Pools/components/WithdrawForm/WithdrawAssets.tsx new file mode 100644 index 0000000000..0558749d28 --- /dev/null +++ b/src/pages/AMM/Pools/components/WithdrawForm/WithdrawAssets.tsx @@ -0,0 +1,56 @@ +import { CurrencyExt } from '@interlay/interbtc-api'; +import { MonetaryAmount } from '@interlay/monetary-js'; +import { useTranslation } from 'react-i18next'; + +import { convertMonetaryAmountToValueInUSD, formatNumber, formatUSD } from '@/common/utils/utils'; +import { CoinIcon, Dd, Dl, DlGroup, Dt, Flex, P } from '@/component-library'; +import { getTokenPrice } from '@/utils/helpers/prices'; +import { Prices } from '@/utils/hooks/api/use-get-prices'; + +type WithdrawAssetsProps = { + pooledAmounts: MonetaryAmount[]; + prices?: Prices; +}; + +const WithdrawAssets = ({ pooledAmounts, prices }: WithdrawAssetsProps): JSX.Element => { + const { t } = useTranslation(); + + return ( + +

+ {t('amm.pools.receivable_assets')} +

+
+ {pooledAmounts.map((amount) => { + const assetAmount = formatNumber(amount.toBig().toNumber(), { + maximumFractionDigits: amount.currency.humanDecimals + }); + + const assetAmountUSD = formatUSD( + convertMonetaryAmountToValueInUSD(amount, getTokenPrice(prices, amount.currency.ticker)?.usd) || 0, + { compact: true } + ); + + return ( + +
+ + + {amount.currency.ticker} + +
+
+ {assetAmount} ({assetAmountUSD}) +
+
+ ); + })} +
+
+ ); +}; + +WithdrawAssets.displayName = 'WithdrawAssets'; + +export { WithdrawAssets }; +export type { WithdrawAssetsProps }; diff --git a/src/pages/AMM/Pools/components/WithdrawForm/WithdrawForm.styles.tsx b/src/pages/AMM/Pools/components/WithdrawForm/WithdrawForm.styles.tsx new file mode 100644 index 0000000000..1d764b7675 --- /dev/null +++ b/src/pages/AMM/Pools/components/WithdrawForm/WithdrawForm.styles.tsx @@ -0,0 +1,12 @@ +import styled from 'styled-components'; + +import { Dl, theme } from '@/component-library'; + +const StyledDl = styled(Dl)` + background-color: ${theme.card.secondaryBg}; + padding: ${theme.spacing.spacing4}; + font-size: ${theme.text.xs}; + border-radius: ${theme.rounded.rg}; +`; + +export { StyledDl }; diff --git a/src/pages/AMM/Pools/components/WithdrawForm/WithdrawForm.tsx b/src/pages/AMM/Pools/components/WithdrawForm/WithdrawForm.tsx new file mode 100644 index 0000000000..9b81f9f0e4 --- /dev/null +++ b/src/pages/AMM/Pools/components/WithdrawForm/WithdrawForm.tsx @@ -0,0 +1,170 @@ +import { LiquidityPool, LpCurrency, newMonetaryAmount } from '@interlay/interbtc-api'; +import { MonetaryAmount } from '@interlay/monetary-js'; +import { AccountId } from '@polkadot/types/interfaces'; +import Big from 'big.js'; +import { RefObject, useState } from 'react'; +import { useTranslation } from 'react-i18next'; +import { useMutation } from 'react-query'; +import { toast } from 'react-toastify'; + +import { + convertMonetaryAmountToValueInUSD, + displayMonetaryAmountInUSDFormat, + newSafeMonetaryAmount +} from '@/common/utils/utils'; +import { Dd, DlGroup, Dt, Flex, TokenInput } from '@/component-library'; +import { AuthCTA } from '@/components'; +import { GOVERNANCE_TOKEN, TRANSACTION_FEE_AMOUNT } from '@/config/relay-chains'; +import { isFormDisabled, useForm, WITHDRAW_LIQUIDITY_POOL_FIELD } from '@/lib/form'; +import { WithdrawLiquidityPoolFormData, withdrawLiquidityPoolSchema } from '@/lib/form/schemas'; +import { SlippageManager } from '@/pages/AMM/shared/components'; +import { AMM_DEADLINE_INTERVAL } from '@/utils/constants/api'; +import { getTokenPrice } from '@/utils/helpers/prices'; +import { useGetBalances } from '@/utils/hooks/api/tokens/use-get-balances'; +import { useGetPrices } from '@/utils/hooks/api/use-get-prices'; +import useAccountId from '@/utils/hooks/use-account-id'; + +import { PoolName } from '../PoolName'; +import { WithdrawAssets } from './WithdrawAssets'; +import { StyledDl } from './WithdrawForm.styles'; + +type DepositData = { + amount: MonetaryAmount; + pool: LiquidityPool; + slippage: number; + deadline: number; + accountId: AccountId; +}; + +const mutateWithdraw = ({ amount, pool, slippage, deadline, accountId }: DepositData) => + window.bridge.amm.removeLiquidity(amount, pool, slippage, deadline, accountId); + +type WithdrawFormProps = { + pool: LiquidityPool; + slippageModalRef: RefObject; + onWithdraw?: () => void; +}; + +const WithdrawForm = ({ pool, slippageModalRef, onWithdraw }: WithdrawFormProps): JSX.Element => { + const [slippage, setSlippage] = useState(0.1); + + const accountId = useAccountId(); + const { t } = useTranslation(); + const prices = useGetPrices(); + const { getBalance } = useGetBalances(); + + const withdrawMutation = useMutation(mutateWithdraw, { + onSuccess: () => { + onWithdraw?.(); + toast.success('Withdraw successful'); + }, + onError: (err) => { + toast.error(err.message); + } + }); + + const { lpToken } = pool; + + const governanceBalance = getBalance(GOVERNANCE_TOKEN.ticker)?.free || newMonetaryAmount(0, GOVERNANCE_TOKEN); + const balance = getBalance(lpToken.ticker)?.reserved; + + const zeroAssetAmount = newMonetaryAmount(0, lpToken); + const schemaParams = { + governanceBalance, + maxAmount: balance || zeroAssetAmount, + minAmount: newMonetaryAmount(1, lpToken), + transactionFee: TRANSACTION_FEE_AMOUNT + }; + + const handleSubmit = async (data: WithdrawLiquidityPoolFormData) => { + if (!accountId) return; + + try { + const amount = newMonetaryAmount(data[WITHDRAW_LIQUIDITY_POOL_FIELD] || 0, lpToken, true); + const deadline = await window.bridge.system.getFutureBlockNumber(AMM_DEADLINE_INTERVAL); + + return withdrawMutation.mutate({ amount, pool, deadline, slippage, accountId }); + } catch (err: any) { + toast.error(err.toString()); + } + }; + + const form = useForm({ + initialValues: { withdraw: undefined }, + validationSchema: withdrawLiquidityPoolSchema(schemaParams), + onSubmit: handleSubmit + }); + + const lpTokenMonetaryAmount = newSafeMonetaryAmount( + form.values[WITHDRAW_LIQUIDITY_POOL_FIELD] || 0, + pool.lpToken, + true + ); + + const isBtnDisabled = isFormDisabled(form); + + const tickers = pool.pooledCurrencies.map((currency) => currency.currency.ticker); + const poolName = ; + + const pooledAmounts = pool.getLiquidityWithdrawalPooledCurrencyAmounts(lpTokenMonetaryAmount as any); + + const pooledAmountsUSD = pooledAmounts + .reduce((acc, pooledAmount) => { + const pooledAmountUSD = convertMonetaryAmountToValueInUSD( + pooledAmount, + getTokenPrice(prices, pooledAmount.currency.ticker)?.usd + ); + + return acc.add(pooledAmountUSD || 0); + }, new Big(0)) + .toNumber(); + + return ( +
+ + setSlippage(slippage)} /> + {poolName} + + + + + + + +
+ Fees +
+
+ {TRANSACTION_FEE_AMOUNT.toHuman()} {TRANSACTION_FEE_AMOUNT.currency.ticker} ( + {displayMonetaryAmountInUSDFormat( + TRANSACTION_FEE_AMOUNT, + getTokenPrice(prices, TRANSACTION_FEE_AMOUNT.currency.ticker)?.usd + )} + ) +
+
+
+ + {t('amm.pools.remove_liquidity')} + +
+
+ + ); +}; + +WithdrawForm.displayName = 'WithdrawForm'; + +export { WithdrawForm }; +export type { WithdrawFormProps }; diff --git a/src/pages/AMM/Pools/components/WithdrawForm/index.tsx b/src/pages/AMM/Pools/components/WithdrawForm/index.tsx new file mode 100644 index 0000000000..43ce575bd4 --- /dev/null +++ b/src/pages/AMM/Pools/components/WithdrawForm/index.tsx @@ -0,0 +1,2 @@ +export type { WithdrawFormProps } from './WithdrawForm'; +export { WithdrawForm } from './WithdrawForm'; diff --git a/src/pages/AMM/Pools/components/index.tsx b/src/pages/AMM/Pools/components/index.tsx new file mode 100644 index 0000000000..a7abb85016 --- /dev/null +++ b/src/pages/AMM/Pools/components/index.tsx @@ -0,0 +1,5 @@ +import { PoolsInsights, PoolsInsightsProps } from './PoolsInsights'; +import { PoolsTables, PoolsTablesProps } from './PoolsTables'; + +export { PoolsInsights, PoolsTables }; +export type { PoolsInsightsProps, PoolsTablesProps }; diff --git a/src/pages/AMM/Pools/index.tsx b/src/pages/AMM/Pools/index.tsx new file mode 100644 index 0000000000..b874fd52bc --- /dev/null +++ b/src/pages/AMM/Pools/index.tsx @@ -0,0 +1,3 @@ +import Pools from './Pools'; + +export default Pools; diff --git a/src/pages/AMM/Swap/Swap.style.tsx b/src/pages/AMM/Swap/Swap.style.tsx new file mode 100644 index 0000000000..3be489dd2b --- /dev/null +++ b/src/pages/AMM/Swap/Swap.style.tsx @@ -0,0 +1,11 @@ +import styled from 'styled-components'; + +import { Flex } from '@/component-library'; + +const StyledWrapper = styled(Flex)` + max-width: 480px; + width: 100%; + margin: 0 auto; +`; + +export { StyledWrapper }; diff --git a/src/pages/AMM/Swap/Swap.tsx b/src/pages/AMM/Swap/Swap.tsx new file mode 100644 index 0000000000..7dab9ff3b1 --- /dev/null +++ b/src/pages/AMM/Swap/Swap.tsx @@ -0,0 +1,75 @@ +import { isCurrencyEqual } from '@interlay/interbtc-api'; +import { useEffect, useMemo, useState } from 'react'; +import { useSelector } from 'react-redux'; + +import { StoreType } from '@/common/types/util.types'; +import { RELAY_CHAIN_NATIVE_TOKEN } from '@/config/relay-chains'; +import FullLoadingSpinner from '@/legacy-components/FullLoadingSpinner'; +import MainContainer from '@/parts/MainContainer'; +import { SwapPair } from '@/types/swap'; +import { QUERY_PARAMETERS } from '@/utils/constants/links'; +import { getPooledTickers } from '@/utils/helpers/pools'; +import { useGetLiquidityPools } from '@/utils/hooks/api/amm/use-get-liquidity-pools'; +import { useGetCurrencies } from '@/utils/hooks/api/use-get-currencies'; +import useQueryParams from '@/utils/hooks/use-query-params'; + +import { SwapForm, SwapLiquidity } from './components'; +import { StyledWrapper } from './Swap.style'; + +const DEFAULT_PAIR: SwapPair = { input: RELAY_CHAIN_NATIVE_TOKEN }; + +const Swap = (): JSX.Element => { + const query = useQueryParams(); + const { bridgeLoaded } = useSelector((state: StoreType) => state.general); + + const { data: liquidityPools, refetch } = useGetLiquidityPools(); + const { data, getCurrencyFromTicker } = useGetCurrencies(bridgeLoaded); + + const [pair, setPair] = useState(DEFAULT_PAIR); + + const pooledTickers = useMemo(() => liquidityPools && getPooledTickers(liquidityPools), [liquidityPools]); + + useEffect(() => { + if (!pooledTickers || !data) return; + + const inputQuery = query.get(QUERY_PARAMETERS.SWAP.FROM); + const outputQuery = query.get(QUERY_PARAMETERS.SWAP.TO); + + const fromCurrency = inputQuery ? getCurrencyFromTicker(inputQuery) : DEFAULT_PAIR.input; + const toCurrency = outputQuery ? getCurrencyFromTicker(outputQuery) : DEFAULT_PAIR.output; + + setPair({ input: fromCurrency, output: toCurrency }); + // eslint-disable-next-line react-hooks/exhaustive-deps + }, [pooledTickers, data]); + + if (liquidityPools === undefined || pooledTickers === undefined) { + return ; + } + + const liquidityPool = liquidityPools.find( + (obj) => + obj.pooledCurrencies.some((amount) => pair.input && isCurrencyEqual(amount.currency, pair.input)) && + obj.pooledCurrencies.some((amount) => pair.output && isCurrencyEqual(amount.currency, pair.output)) + ); + + const handleChangePair = (pair: SwapPair) => setPair(pair); + + return ( + + + + {pair.input && pair.output && liquidityPool && ( + + )} + + + ); +}; + +export default Swap; diff --git a/src/pages/AMM/Swap/components/PriceImpactModal/PriceImpactModal.style.tsx b/src/pages/AMM/Swap/components/PriceImpactModal/PriceImpactModal.style.tsx new file mode 100644 index 0000000000..6faf0f46b3 --- /dev/null +++ b/src/pages/AMM/Swap/components/PriceImpactModal/PriceImpactModal.style.tsx @@ -0,0 +1,15 @@ +import styled from 'styled-components'; + +import { ModalHeader, P, theme } from '@/component-library'; + +const StyledModalHeader = styled(ModalHeader)` + display: flex; + flex-direction: column; + color: ${theme.alert.status.error}; +`; + +const StyledPriceImpact = styled(P)` + color: ${theme.alert.status.error}; +`; + +export { StyledModalHeader, StyledPriceImpact }; diff --git a/src/pages/AMM/Swap/components/PriceImpactModal/PriceImpactModal.tsx b/src/pages/AMM/Swap/components/PriceImpactModal/PriceImpactModal.tsx new file mode 100644 index 0000000000..9201510461 --- /dev/null +++ b/src/pages/AMM/Swap/components/PriceImpactModal/PriceImpactModal.tsx @@ -0,0 +1,70 @@ +import { CurrencyExt } from '@interlay/interbtc-api'; +import { MonetaryAmount } from '@interlay/monetary-js'; +import { useTranslation } from 'react-i18next'; + +import { formatPercentage, formatUSD } from '@/common/utils/utils'; +import { CTA, Flex, Modal, ModalBody, ModalFooter, ModalProps, P } from '@/component-library'; +import { SwapPair } from '@/types/swap'; + +import { StyledModalHeader, StyledPriceImpact } from './PriceImpactModal.style'; + +type Props = { + onConfirm?: () => void; + inputValueUSD: number; + outputValueUSD: number; + inputAmount?: MonetaryAmount; + outputAmount?: MonetaryAmount; + pair: SwapPair; + priceImpact: number; +}; + +type InheritAttrs = Omit; + +type PriceImpactModalProps = Props & InheritAttrs; + +const PriceImpactModal = ({ + onConfirm, + onClose, + inputValueUSD, + outputValueUSD, + inputAmount, + outputAmount, + pair, + priceImpact, + ...props +}: PriceImpactModalProps): JSX.Element => { + const { t } = useTranslation(); + + return ( + + External Price Impact Warning + + +

{t('amm.swap_has_price_inpact_of')}:

+ {formatPercentage(priceImpact)} +
+

+ {t('amm.you_are_swapping_input_for_output', { + inputAmount: inputAmount?.toHuman(), + inputTicker: pair.input?.ticker, + inputAmountUSD: formatUSD(inputValueUSD, { compact: true }), + outputAmount: outputAmount?.toHuman(), + outputTicker: pair.output?.ticker, + outputAmountUSD: formatUSD(outputValueUSD, { compact: true }) + })} +

+
+ + + {t('amm.cancel_swap')} + + + {t('amm.confirm_swap')} + + +
+ ); +}; + +export { PriceImpactModal }; +export type { PriceImpactModalProps }; diff --git a/src/pages/AMM/Swap/components/PriceImpactModal/index.tsx b/src/pages/AMM/Swap/components/PriceImpactModal/index.tsx new file mode 100644 index 0000000000..afa4029d8d --- /dev/null +++ b/src/pages/AMM/Swap/components/PriceImpactModal/index.tsx @@ -0,0 +1,2 @@ +export type { PriceImpactModalProps } from './PriceImpactModal'; +export { PriceImpactModal } from './PriceImpactModal'; diff --git a/src/pages/AMM/Swap/components/SwapForm/SwapCTA.tsx b/src/pages/AMM/Swap/components/SwapForm/SwapCTA.tsx new file mode 100644 index 0000000000..3ef5503393 --- /dev/null +++ b/src/pages/AMM/Swap/components/SwapForm/SwapCTA.tsx @@ -0,0 +1,69 @@ +import { Trade } from '@interlay/interbtc-api'; +import { TFunction, useTranslation } from 'react-i18next'; + +import { CTAProps } from '@/component-library'; +import { AuthCTA } from '@/components'; +import { + FormErrors, + SWAP_INPUT_AMOUNT_FIELD, + SWAP_INPUT_TOKEN_FIELD, + SWAP_OUTPUT_TOKEN_FIELD, + SwapFormData +} from '@/lib/form'; +import { SwapPair } from '@/types/swap'; + +const getProps = ( + pair: SwapPair, + trade: Trade | null | undefined, + errors: FormErrors, + t: TFunction +): Pick => { + const tickersError = errors[SWAP_INPUT_TOKEN_FIELD] || errors[SWAP_OUTPUT_TOKEN_FIELD]; + + if (tickersError) { + return { + disabled: true, + children: t(tickersError) + }; + } + + const inputError = errors[SWAP_INPUT_AMOUNT_FIELD]; + + if (inputError) { + return { + disabled: true, + children: t(inputError, { token: pair.input?.ticker }) + }; + } + + if (trade === undefined) { + return { children: 'Loading...', disabled: true }; + } + + if (trade === null) { + return { children: t('amm.insufficient_liquidity_trade'), disabled: true }; + } + + return { + children: t('amm.swap'), + disabled: false + }; +}; + +type SwapCTAProps = { + pair: SwapPair; + trade: Trade | null | undefined; + errors: FormErrors; + loading: boolean; +}; + +const SwapCTA = ({ pair, trade, errors, loading }: SwapCTAProps): JSX.Element | null => { + const { t } = useTranslation(); + + const otherProps = getProps(pair, trade, errors, t); + + return ; +}; + +export { SwapCTA }; +export type { SwapCTAProps }; diff --git a/src/pages/AMM/Swap/components/SwapForm/SwapDivider.tsx b/src/pages/AMM/Swap/components/SwapForm/SwapDivider.tsx new file mode 100644 index 0000000000..fa33bb2cd5 --- /dev/null +++ b/src/pages/AMM/Swap/components/SwapForm/SwapDivider.tsx @@ -0,0 +1,31 @@ +import { useButton } from '@react-aria/button'; +import { useFocusRing } from '@react-aria/focus'; +import { mergeProps } from '@react-aria/utils'; +import { PressEvent } from '@react-types/shared'; +import { useRef } from 'react'; + +import { ArrowsUpDown } from '@/assets/icons'; + +import { StyledBackground, StyledCircle, StyledDivider, StyledWrapper } from './SwapForm.style'; + +type SwapDividerProps = { + onPress: (e: PressEvent) => void; +}; + +const SwapDivider = ({ onPress }: SwapDividerProps): JSX.Element | null => { + const ref = useRef(null); + const { buttonProps } = useButton({ onPress, 'aria-label': 'switch tokens' }, ref); + const { focusProps, isFocusVisible } = useFocusRing(); + + return ( + + + + + + + + ); +}; + +export { SwapDivider }; diff --git a/src/pages/AMM/Swap/components/SwapForm/SwapForm.style.tsx b/src/pages/AMM/Swap/components/SwapForm/SwapForm.style.tsx new file mode 100644 index 0000000000..5a662b756d --- /dev/null +++ b/src/pages/AMM/Swap/components/SwapForm/SwapForm.style.tsx @@ -0,0 +1,44 @@ +import styled from 'styled-components'; + +import { Divider, theme } from '@/component-library'; + +type StyledCircleProps = { + $isFocusVisible: boolean; +}; + +const StyledWrapper = styled.div` + position: relative; +`; + +const StyledCircle = styled.button` + display: inline-flex; + position: absolute; + left: 50%; + top: 50%; + transform: translate(-50%, -50%); + padding: ${theme.spacing.spacing2}; + background-color: var(--colors-token-input-end-adornment-bg); + border-radius: ${theme.rounded.full}; + outline: ${({ $isFocusVisible }) => !$isFocusVisible && 'none'}; + transition: transform ${theme.transition.duration.duration150}ms ease-in; + + &:hover, + &:focus-visible { + transform: translate(-50%, -50%) rotate(180deg); + } +`; + +const StyledBackground = styled.div` + position: absolute; + left: 50%; + top: 50%; + transform: translate(-50%, -50%); + padding: ${theme.spacing.spacing1} ${theme.spacing.spacing8}; + background-color: ${theme.colors.bgPrimary}; +`; + +const StyledDivider = styled(Divider)` + background-color: var(--colors-token-input-end-adornment-bg); +`; + +export { StyledBackground, StyledCircle, StyledDivider, StyledWrapper }; diff --git a/src/pages/AMM/Swap/components/SwapForm/SwapForm.tsx b/src/pages/AMM/Swap/components/SwapForm/SwapForm.tsx new file mode 100644 index 0000000000..06d2b17131 --- /dev/null +++ b/src/pages/AMM/Swap/components/SwapForm/SwapForm.tsx @@ -0,0 +1,363 @@ +import { CurrencyExt, LiquidityPool, newMonetaryAmount, Trade } from '@interlay/interbtc-api'; +import { MonetaryAmount } from '@interlay/monetary-js'; +import { AddressOrPair } from '@polkadot/api/types'; +import { mergeProps } from '@react-aria/utils'; +import Big from 'big.js'; +import { ChangeEventHandler, useEffect, useMemo, useState } from 'react'; +import { useTranslation } from 'react-i18next'; +import { useMutation } from 'react-query'; +import { useSelector } from 'react-redux'; +import { toast } from 'react-toastify'; +import { useDebounce } from 'react-use'; + +import { StoreType } from '@/common/types/util.types'; +import { convertMonetaryAmountToValueInUSD, formatUSD, newSafeMonetaryAmount } from '@/common/utils/utils'; +import { Card, CardProps, Divider, Flex, H1, TokenInput, TokenInputProps } from '@/component-library'; +import { GOVERNANCE_TOKEN, TRANSACTION_FEE_AMOUNT } from '@/config/relay-chains'; +import { + SWAP_INPUT_AMOUNT_FIELD, + SWAP_INPUT_TOKEN_FIELD, + SWAP_OUTPUT_TOKEN_FIELD, + SwapFormData, + swapSchema, + useForm +} from '@/lib/form'; +import { SlippageManager } from '@/pages/AMM/shared/components'; +import { SwapPair } from '@/types/swap'; +import { SWAP_PRICE_IMPACT_LIMIT } from '@/utils/constants/swap'; +import { getTokenPrice } from '@/utils/helpers/prices'; +import { useGetBalances } from '@/utils/hooks/api/tokens/use-get-balances'; +import { useGetCurrencies } from '@/utils/hooks/api/use-get-currencies'; +import { Prices, useGetPrices } from '@/utils/hooks/api/use-get-prices'; +import useAccountId from '@/utils/hooks/use-account-id'; + +import { PriceImpactModal } from '../PriceImpactModal'; +import { SwapInfo } from '../SwapInfo'; +import { SwapCTA } from './SwapCTA'; +import { SwapDivider } from './SwapDivider'; + +const getPairChange = (pair: SwapPair, currency: CurrencyExt, name: string): SwapPair => { + switch (name) { + case SWAP_INPUT_TOKEN_FIELD: { + return currency.ticker === pair.output?.ticker + ? { input: currency, output: pair.input } + : { ...pair, input: currency }; + } + case SWAP_OUTPUT_TOKEN_FIELD: { + return currency.ticker === pair.input?.ticker + ? { input: pair.output, output: currency } + : { ...pair, output: currency }; + } + default: + return pair; + } +}; + +const getAmountsUSD = (pair: SwapPair, prices?: Prices, trade?: Trade | null, inputAmount?: string) => { + const monetaryAmount = pair.input && newSafeMonetaryAmount(inputAmount || 0, pair.input, true); + + const inputAmountUSD = monetaryAmount + ? convertMonetaryAmountToValueInUSD(monetaryAmount, getTokenPrice(prices, monetaryAmount.currency.ticker)?.usd) || 0 + : 0; + + const outputAmountUSD = + trade?.outputAmount && pair.output + ? convertMonetaryAmountToValueInUSD(trade.outputAmount, getTokenPrice(prices, pair.output.ticker)?.usd) || 0 + : 0; + + return { + inputAmountUSD, + outputAmountUSD, + inputMonetary: monetaryAmount, + outputMonetary: trade?.outputAmount + }; +}; + +const getPoolPriceImpact = (trade: Trade | null | undefined, inputAmountUSD: number, outputAmountUSD: number) => ({ + poolImpact: trade?.priceImpact, + marketPrice: + outputAmountUSD && inputAmountUSD + ? new Big(inputAmountUSD - outputAmountUSD).div(inputAmountUSD).mul(100) + : new Big(0) +}); + +type SwapData = { + trade: Trade; + minimumAmountOut: MonetaryAmount; + recipient: AddressOrPair; + deadline: string | number; +}; + +const mutateSwap = ({ deadline, minimumAmountOut, recipient, trade }: SwapData) => + window.bridge.amm.swap(trade, minimumAmountOut, recipient, deadline); + +type Props = { + pair: SwapPair; + liquidityPools: LiquidityPool[]; + onChangePair: (pair: SwapPair) => void; + pooledTickers: Set; + onSwap: () => void; +}; + +type InheritAttrs = CardProps & Props; + +type SwapFormProps = Props & InheritAttrs; + +const SwapForm = ({ + pair, + liquidityPools, + pooledTickers, + onChangePair, + onSwap, + ...props +}: SwapFormProps): JSX.Element | null => { + const [slippage, setSlippage] = useState(0.1); + const [inputAmount, setInputAmount] = useState(); + const [trade, setTrade] = useState(); + const [isPriceImpactModalOpen, setPriceImpactModal] = useState(false); + + const prices = useGetPrices(); + const accountId = useAccountId(); + const { t } = useTranslation(); + const { bridgeLoaded } = useSelector((state: StoreType) => state.general); + const { getCurrencyFromTicker } = useGetCurrencies(bridgeLoaded); + const { data: balances, getBalance, getAvailableBalance } = useGetBalances(); + const { data: currencies } = useGetCurrencies(bridgeLoaded); + + useDebounce( + () => { + if (!pair.input || !pair.output || !inputAmount) { + return setTrade(undefined); + } + + const inputMonetaryAmount = newMonetaryAmount(inputAmount, pair.input, true); + const trade = window.bridge.amm.getOptimalTrade(inputMonetaryAmount, pair.output, liquidityPools); + + setTrade(trade); + }, + 500, + [inputAmount, pair] + ); + + const swapMutation = useMutation(mutateSwap, { + onSuccess: () => { + toast.success('Swap successful'); + setTrade(undefined); + setInputAmount(undefined); + onSwap(); + }, + onError: (err) => { + toast.error(err.message); + } + }); + + const inputBalance = pair.input && getAvailableBalance(pair.input.ticker); + const outputBalance = pair.output && getAvailableBalance(pair.output.ticker); + + const governanceBalance = getBalance(GOVERNANCE_TOKEN.ticker)?.free || newMonetaryAmount(0, GOVERNANCE_TOKEN); + const minAmount = pair.input && newMonetaryAmount(1, pair.input); + + const inputSchemaParams = { + governanceBalance, + maxAmount: inputBalance, + minAmount, + transactionFee: TRANSACTION_FEE_AMOUNT + }; + + const handleSwap = async () => { + if (!trade || !accountId) return; + + try { + const minimumAmountOut = trade.getMinimumOutputAmount(slippage); + + const deadline = await window.bridge.system.getFutureBlockNumber(30 * 60); + + return swapMutation.mutate({ + trade, + recipient: accountId, + minimumAmountOut, + deadline + }); + } catch (err: any) { + toast.error(err.toString()); + } + }; + + const handleSubmit = async (values: SwapFormData) => { + const { inputAmountUSD, outputAmountUSD } = getAmountsUSD(pair, prices, trade, values[SWAP_INPUT_AMOUNT_FIELD]); + + const isOverPricedBuy = inputAmountUSD >= outputAmountUSD; + const { poolImpact, marketPrice } = getPoolPriceImpact(trade, inputAmountUSD, outputAmountUSD); + + if (isOverPricedBuy && (marketPrice.gte(SWAP_PRICE_IMPACT_LIMIT) || poolImpact?.gte(SWAP_PRICE_IMPACT_LIMIT))) { + return setPriceImpactModal(true); + } + + handleSwap(); + }; + + const initialValues = useMemo( + () => ({ + [SWAP_INPUT_AMOUNT_FIELD]: '', + [SWAP_INPUT_TOKEN_FIELD]: pair.input?.ticker, + [SWAP_OUTPUT_TOKEN_FIELD]: pair.output?.ticker + }), + // eslint-disable-next-line react-hooks/exhaustive-deps + [] + ); + + const form = useForm({ + initialValues, + validationSchema: swapSchema({ [SWAP_INPUT_AMOUNT_FIELD]: inputSchemaParams }), + onSubmit: handleSubmit, + disableValidation: swapMutation.isLoading, + validateOnMount: true + }); + + // MEMO: re-validate form on balances refetch + useEffect(() => { + form.validateForm(); + // eslint-disable-next-line react-hooks/exhaustive-deps + }, [balances]); + + // MEMO: trigger validation after pair state change + useEffect(() => { + form.setValues( + { + ...form.values, + [SWAP_INPUT_TOKEN_FIELD]: pair.input?.ticker || '', + [SWAP_OUTPUT_TOKEN_FIELD]: pair.output?.ticker || '' + }, + true + ); + // eslint-disable-next-line react-hooks/exhaustive-deps + }, [pair]); + + // MEMO: amount field cleaned up after successful swap + useEffect(() => { + const isAmountFieldEmpty = form.values[SWAP_INPUT_AMOUNT_FIELD] === ''; + + if (isAmountFieldEmpty || !swapMutation.isSuccess) return; + + form.setFieldValue(SWAP_INPUT_AMOUNT_FIELD, ''); + // eslint-disable-next-line react-hooks/exhaustive-deps + }, [swapMutation.isSuccess]); + + const handleChangeInput: ChangeEventHandler = (e) => { + setInputAmount(e.target.value); + setTrade(undefined); + }; + + const handlePairChange = (pair: SwapPair) => { + onChangePair(pair); + setTrade(undefined); + }; + + const handleTickerChange = (ticker: string, name: string) => { + form.setFieldValue(name, ticker, true); + const currency = getCurrencyFromTicker(ticker); + const newPair = getPairChange(pair, currency, name); + + handlePairChange(newPair); + }; + + const handlePairSwap = () => handlePairChange({ input: pair.output, output: pair.input }); + + const handleConfirmPriceImpactModal = () => { + setPriceImpactModal(false); + handleSwap(); + }; + + const handleClosePriceImpactModal = () => setPriceImpactModal(false); + + const { inputAmountUSD, outputAmountUSD, inputMonetary, outputMonetary } = getAmountsUSD( + pair, + prices, + trade, + form.values[SWAP_INPUT_AMOUNT_FIELD] + ); + + const tokens: TokenInputProps['tokens'] = useMemo( + () => + currencies + ?.filter((currency) => pooledTickers.has(currency.ticker)) + .map((currency) => { + const balance = getAvailableBalance(currency.ticker); + const balanceUSD = balance + ? convertMonetaryAmountToValueInUSD(balance, getTokenPrice(prices, currency.ticker)?.usd) + : 0; + + return { + balance: balance?.toHuman() || 0, + balanceUSD: formatUSD(balanceUSD || 0, { compact: true }), + ticker: currency.ticker + }; + }), + [currencies, getAvailableBalance, pooledTickers, prices] + ); + + const { poolImpact, marketPrice } = getPoolPriceImpact(trade, inputAmountUSD, outputAmountUSD); + const priceImpact = (marketPrice || poolImpact).toNumber(); + + return ( + <> + +

+ Swap +

+ + + setSlippage(slippage)} /> +
+ + + handleTickerChange(ticker, SWAP_INPUT_TOKEN_FIELD) + })} + {...mergeProps(form.getFieldProps(SWAP_INPUT_AMOUNT_FIELD, false), { onChange: handleChangeInput })} + /> + + handleTickerChange(ticker, SWAP_OUTPUT_TOKEN_FIELD) + })} + /> + + {trade && } + + + +
+
+ + + ); +}; + +export { SwapForm }; +export type { SwapFormProps }; diff --git a/src/pages/AMM/Swap/components/SwapForm/index.tsx b/src/pages/AMM/Swap/components/SwapForm/index.tsx new file mode 100644 index 0000000000..d51a451640 --- /dev/null +++ b/src/pages/AMM/Swap/components/SwapForm/index.tsx @@ -0,0 +1,2 @@ +export type { SwapFormProps } from './SwapForm'; +export { SwapForm } from './SwapForm'; diff --git a/src/pages/AMM/Swap/components/SwapInfo/SwapInfo.style.tsx b/src/pages/AMM/Swap/components/SwapInfo/SwapInfo.style.tsx new file mode 100644 index 0000000000..18491cf623 --- /dev/null +++ b/src/pages/AMM/Swap/components/SwapInfo/SwapInfo.style.tsx @@ -0,0 +1,11 @@ +import styled from 'styled-components'; + +import { theme } from '@/component-library'; + +const StyledCard = styled.div` + background-color: ${theme.card.secondaryBg}; + padding: ${theme.spacing.spacing2}; + border-radius: ${theme.rounded.md}; +`; + +export { StyledCard }; diff --git a/src/pages/AMM/Swap/components/SwapInfo/SwapInfo.tsx b/src/pages/AMM/Swap/components/SwapInfo/SwapInfo.tsx new file mode 100644 index 0000000000..23e343b8ae --- /dev/null +++ b/src/pages/AMM/Swap/components/SwapInfo/SwapInfo.tsx @@ -0,0 +1,74 @@ +import { Trade } from '@interlay/interbtc-api'; + +import { displayMonetaryAmountInUSDFormat, formatPercentage } from '@/common/utils/utils'; +import { Accordion, AccordionItem, Dd, Dl, DlGroup, Dt } from '@/component-library'; +import { TRANSACTION_FEE_AMOUNT } from '@/config/relay-chains'; +import { getTokenPrice } from '@/utils/helpers/prices'; +import { useGetPrices } from '@/utils/hooks/api/use-get-prices'; + +import { StyledCard } from './SwapInfo.style'; + +type SwapInfoProps = { + trade: Trade; + slippage: number; +}; + +const SwapInfo = ({ trade, slippage }: SwapInfoProps): JSX.Element | null => { + const prices = useGetPrices(); + + const { inputAmount, outputAmount, executionPrice, priceImpact } = trade; + + const title = `1 ${inputAmount.currency.ticker} = ${executionPrice.toHuman()} ${outputAmount.currency.ticker}`; + + const minimumReceived = outputAmount.sub(outputAmount.mul(slippage).div(100)); + + return ( + + + +
+ +
+ Expected Output +
+
+ {outputAmount.toHuman()} {outputAmount.currency.ticker} +
+
+ +
+ Minimum Received +
+
+ {minimumReceived.toHuman()} {outputAmount.currency.ticker} +
+
+ +
+ Price Impact +
+ {/* TODO: handle small percentages */} +
{formatPercentage(priceImpact.toNumber())}
+
+ +
+ Fees +
+
+ {TRANSACTION_FEE_AMOUNT.toHuman()} {TRANSACTION_FEE_AMOUNT.currency.ticker} ( + {displayMonetaryAmountInUSDFormat( + TRANSACTION_FEE_AMOUNT, + getTokenPrice(prices, TRANSACTION_FEE_AMOUNT.currency.ticker)?.usd + )} + ) +
+
+
+
+
+
+ ); +}; + +export { SwapInfo }; +export type { SwapInfoProps }; diff --git a/src/pages/AMM/Swap/components/SwapInfo/index.tsx b/src/pages/AMM/Swap/components/SwapInfo/index.tsx new file mode 100644 index 0000000000..31b417a246 --- /dev/null +++ b/src/pages/AMM/Swap/components/SwapInfo/index.tsx @@ -0,0 +1,2 @@ +export type { SwapInfoProps } from './SwapInfo'; +export { SwapInfo } from './SwapInfo'; diff --git a/src/pages/AMM/Swap/components/SwapLiquidity/SwapLiquidity.tsx b/src/pages/AMM/Swap/components/SwapLiquidity/SwapLiquidity.tsx new file mode 100644 index 0000000000..6aacf94db4 --- /dev/null +++ b/src/pages/AMM/Swap/components/SwapLiquidity/SwapLiquidity.tsx @@ -0,0 +1,47 @@ +import { CurrencyExt, LiquidityPool } from '@interlay/interbtc-api'; + +import { formatUSD } from '@/common/utils/utils'; +import { Card, CardProps, CoinPair, Dd, Dl, DlGroup, Dt, Flex, H2 } from '@/component-library'; +import { useGetPrices } from '@/utils/hooks/api/use-get-prices'; + +import { calculateTotalLiquidityUSD } from '../../../shared/utils'; + +type Props = { + input: CurrencyExt; + output: CurrencyExt; + // TODO: not used + liquidityPool: LiquidityPool; +}; + +type InheritAttrs = Omit; + +type SwapLiquidityProps = Props & InheritAttrs; + +const SwapLiquidity = ({ input, output, liquidityPool, ...props }: SwapLiquidityProps): JSX.Element | null => { + const prices = useGetPrices(); + const liquidity = calculateTotalLiquidityUSD(liquidityPool.pooledCurrencies, prices); + + return ( + + + +

+ {input.ticker} - {output.ticker} +

+
+
+ +
Volume (24h)
+
-
+
+ +
Liquidity
+
{formatUSD(liquidity, { compact: true })}
+
+
+
+ ); +}; + +export { SwapLiquidity }; +export type { SwapLiquidityProps }; diff --git a/src/pages/AMM/Swap/components/SwapLiquidity/index.tsx b/src/pages/AMM/Swap/components/SwapLiquidity/index.tsx new file mode 100644 index 0000000000..609bcfcfa1 --- /dev/null +++ b/src/pages/AMM/Swap/components/SwapLiquidity/index.tsx @@ -0,0 +1,2 @@ +export type { SwapLiquidityProps } from './SwapLiquidity'; +export { SwapLiquidity } from './SwapLiquidity'; diff --git a/src/pages/AMM/Swap/components/index.tsx b/src/pages/AMM/Swap/components/index.tsx new file mode 100644 index 0000000000..9cf7997580 --- /dev/null +++ b/src/pages/AMM/Swap/components/index.tsx @@ -0,0 +1,5 @@ +import { SwapForm, SwapFormProps } from './SwapForm'; +import { SwapLiquidity, SwapLiquidityProps } from './SwapLiquidity'; + +export { SwapForm, SwapLiquidity }; +export type { SwapFormProps, SwapLiquidityProps }; diff --git a/src/pages/AMM/Swap/index.tsx b/src/pages/AMM/Swap/index.tsx new file mode 100644 index 0000000000..b5aab9cb83 --- /dev/null +++ b/src/pages/AMM/Swap/index.tsx @@ -0,0 +1,3 @@ +import Swap from './Swap'; + +export default Swap; diff --git a/src/pages/AMM/Swap/types.ts b/src/pages/AMM/Swap/types.ts new file mode 100644 index 0000000000..c74fc3905f --- /dev/null +++ b/src/pages/AMM/Swap/types.ts @@ -0,0 +1,3 @@ +import { Trade as TradeLib } from '@interlay/interbtc-api'; + +export type Trade = { data: TradeLib | null | undefined; isLoading: boolean }; diff --git a/src/pages/AMM/index.tsx b/src/pages/AMM/index.tsx new file mode 100644 index 0000000000..b5aab9cb83 --- /dev/null +++ b/src/pages/AMM/index.tsx @@ -0,0 +1,3 @@ +import Swap from './Swap'; + +export default Swap; diff --git a/src/pages/AMM/shared/components/SlippageManager/SlippageManager.style.tsx b/src/pages/AMM/shared/components/SlippageManager/SlippageManager.style.tsx new file mode 100644 index 0000000000..3e623e5171 --- /dev/null +++ b/src/pages/AMM/shared/components/SlippageManager/SlippageManager.style.tsx @@ -0,0 +1,9 @@ +import styled from 'styled-components'; + +import { CTA } from '@/component-library'; + +const StyledCTA = styled(CTA)` + align-self: flex-end; +`; + +export { StyledCTA }; diff --git a/src/pages/AMM/shared/components/SlippageManager/SlippageManager.tsx b/src/pages/AMM/shared/components/SlippageManager/SlippageManager.tsx new file mode 100644 index 0000000000..126db75d19 --- /dev/null +++ b/src/pages/AMM/shared/components/SlippageManager/SlippageManager.tsx @@ -0,0 +1,68 @@ +import { forwardRef, useState } from 'react'; + +import { Cog } from '@/assets/icons'; +import { List, ListItem, ListProps, Modal, ModalBody, ModalHeader } from '@/component-library'; + +import { StyledCTA } from './SlippageManager.style'; + +type SlippageManagerProps = { + value: number; + onChange: (slippage: number) => void; +}; + +const SlippageManager = forwardRef( + ({ value, onChange }, ref): JSX.Element => { + const [isOpen, setOpen] = useState(false); + + const handleSelectionChange: ListProps['onSelectionChange'] = (key) => { + const [selectedKey] = [...key]; + + if (!selectedKey) { + return setOpen(false); + } + + onChange?.(Number(selectedKey)); + setOpen(false); + }; + + return ( + <> + setOpen(true)}> + + + setOpen(false)}> + + Set Slippage Tolerance + + + + + 0.1% + + + 0.5% + + + 1% + + + 3% + + + + + + ); + } +); + +SlippageManager.displayName = 'SlippageManager'; + +export { SlippageManager }; +export type { SlippageManagerProps }; diff --git a/src/pages/AMM/shared/components/SlippageManager/index.tsx b/src/pages/AMM/shared/components/SlippageManager/index.tsx new file mode 100644 index 0000000000..501bc2249c --- /dev/null +++ b/src/pages/AMM/shared/components/SlippageManager/index.tsx @@ -0,0 +1,2 @@ +export type { SlippageManagerProps } from './SlippageManager'; +export { SlippageManager } from './SlippageManager'; diff --git a/src/pages/AMM/shared/components/index.tsx b/src/pages/AMM/shared/components/index.tsx new file mode 100644 index 0000000000..501bc2249c --- /dev/null +++ b/src/pages/AMM/shared/components/index.tsx @@ -0,0 +1,2 @@ +export type { SlippageManagerProps } from './SlippageManager'; +export { SlippageManager } from './SlippageManager'; diff --git a/src/pages/AMM/shared/utils.ts b/src/pages/AMM/shared/utils.ts new file mode 100644 index 0000000000..d49ca19fbd --- /dev/null +++ b/src/pages/AMM/shared/utils.ts @@ -0,0 +1,23 @@ +import { CurrencyExt, PooledCurrencies } from '@interlay/interbtc-api'; +import { MonetaryAmount } from '@interlay/monetary-js'; + +import { convertMonetaryAmountToValueInUSD } from '@/common/utils/utils'; +import { getTokenPrice } from '@/utils/helpers/prices'; +import { Prices } from '@/utils/hooks/api/use-get-prices'; + +const calculateTotalLiquidityUSD = (pooledCurrencies: PooledCurrencies, prices?: Prices): number => + pooledCurrencies.reduce((total, currentAmount) => { + const currentAmountUSD = convertMonetaryAmountToValueInUSD( + currentAmount, + getTokenPrice(prices, currentAmount.currency.ticker)?.usd + ); + return total + (currentAmountUSD || 0); + }, 0); + +const calculateAccountLiquidityUSD = ( + lpTokenAmount: MonetaryAmount, + totalLiquidityUSD: number, + totalSupply: MonetaryAmount +): number => lpTokenAmount.mul(totalLiquidityUSD).div(totalSupply.toBig()).toBig().toNumber(); + +export { calculateAccountLiquidityUSD, calculateTotalLiquidityUSD }; diff --git a/src/pages/Actions/Actions/components/ManualIssueExecutionActionsTable/ManualIssueExecutionActionsTable.tsx b/src/pages/Actions/Actions/components/ManualIssueExecutionActionsTable/ManualIssueExecutionActionsTable.tsx index 6ea3c6902a..8ab0ffc851 100644 --- a/src/pages/Actions/Actions/components/ManualIssueExecutionActionsTable/ManualIssueExecutionActionsTable.tsx +++ b/src/pages/Actions/Actions/components/ManualIssueExecutionActionsTable/ManualIssueExecutionActionsTable.tsx @@ -5,8 +5,8 @@ import { useQuery } from 'react-query'; import { H3, Stack, Table, TableProps } from '@/component-library'; import { CTALink } from '@/component-library'; -import ErrorFallback from '@/components/ErrorFallback'; -import PrimaryColorEllipsisLoader from '@/components/PrimaryColorEllipsisLoader'; +import ErrorFallback from '@/legacy-components/ErrorFallback'; +import PrimaryColorEllipsisLoader from '@/legacy-components/PrimaryColorEllipsisLoader'; import { useSubstrateSecureState } from '@/lib/substrate'; import graphqlFetcher, { GRAPHQL_FETCHER, GraphqlReturn } from '@/services/fetchers/graphql-fetcher'; import { useManualIssueRequests } from '@/services/hooks/issue-requests'; diff --git a/src/pages/Bridge/BurnForm/index.tsx b/src/pages/Bridge/BurnForm/index.tsx index 0ef1a34ed1..96a218ed50 100644 --- a/src/pages/Bridge/BurnForm/index.tsx +++ b/src/pages/Bridge/BurnForm/index.tsx @@ -1,39 +1,35 @@ -import { CollateralCurrencyExt, newMonetaryAmount } from '@interlay/interbtc-api'; -import { Bitcoin, BitcoinAmount, ExchangeRate } from '@interlay/monetary-js'; -import Big from 'big.js'; +import { CollateralCurrencyExt, CurrencyExt, newMonetaryAmount } from '@interlay/interbtc-api'; +import { Bitcoin, BitcoinAmount, Currency, ExchangeRate, MonetaryAmount } from '@interlay/monetary-js'; import clsx from 'clsx'; import * as React from 'react'; import { useErrorHandler, withErrorBoundary } from 'react-error-boundary'; import { useForm } from 'react-hook-form'; import { useTranslation } from 'react-i18next'; import { useDispatch, useSelector } from 'react-redux'; +import { toast } from 'react-toastify'; import { showAccountModalAction } from '@/common/actions/general.actions'; import { ParachainStatus, StoreType } from '@/common/types/util.types'; import { displayMonetaryAmountInUSDFormat } from '@/common/utils/utils'; -import ErrorFallback from '@/components/ErrorFallback'; -import ErrorModal from '@/components/ErrorModal'; -import FormTitle from '@/components/FormTitle'; -import Hr2 from '@/components/hrs/Hr2'; -import PriceInfo from '@/components/PriceInfo'; -import PrimaryColorEllipsisLoader from '@/components/PrimaryColorEllipsisLoader'; -import SubmitButton from '@/components/SubmitButton'; -import TokenField from '@/components/TokenField'; -import { - RELAY_CHAIN_NATIVE_TOKEN, - RELAY_CHAIN_NATIVE_TOKEN_SYMBOL, - RelayChainNativeTokenLogoIcon, - WRAPPED_TOKEN, - WRAPPED_TOKEN_SYMBOL, - WrappedTokenLogoIcon -} from '@/config/relay-chains'; +import { CoinIcon } from '@/component-library'; +import { WRAPPED_TOKEN, WRAPPED_TOKEN_SYMBOL, WrappedTokenLogoIcon } from '@/config/relay-chains'; import { BALANCE_MAX_INTEGER_LENGTH } from '@/constants'; +import ErrorFallback from '@/legacy-components/ErrorFallback'; +import ErrorModal from '@/legacy-components/ErrorModal'; +import FormTitle from '@/legacy-components/FormTitle'; +import Hr2 from '@/legacy-components/hrs/Hr2'; +import PriceInfo from '@/legacy-components/PriceInfo'; +import PrimaryColorEllipsisLoader from '@/legacy-components/PrimaryColorEllipsisLoader'; +import SubmitButton from '@/legacy-components/SubmitButton'; +import TokenField from '@/legacy-components/TokenField'; +import Tokens, { TokenOption } from '@/legacy-components/Tokens'; import { useSubstrateSecureState } from '@/lib/substrate'; import { ForeignAssetIdLiteral } from '@/types/currency'; import { KUSAMA, POLKADOT } from '@/utils/constants/relay-chain-names'; import STATUSES from '@/utils/constants/statuses'; import { getTokenPrice } from '@/utils/helpers/prices'; import { useGetBalances } from '@/utils/hooks/api/tokens/use-get-balances'; +import { useGetCollateralCurrencies } from '@/utils/hooks/api/use-get-collateral-currencies'; import { useGetPrices } from '@/utils/hooks/api/use-get-prices'; const WRAPPED_TOKEN_AMOUNT = 'wrapped-token-amount'; @@ -42,6 +38,12 @@ type BurnFormData = { [WRAPPED_TOKEN_AMOUNT]: string; }; +type BurnableCollateral = { + currency: Currency; + burnableTokens: MonetaryAmount; + burnRate: ExchangeRate; +}; + const BurnForm = (): JSX.Element | null => { const dispatch = useDispatch(); const { t } = useTranslation(); @@ -53,6 +55,7 @@ const BurnForm = (): JSX.Element | null => { const { selectedAccount } = useSubstrateSecureState(); const { bridgeLoaded, parachainStatus } = useSelector((state: StoreType) => state.general); const { data: balances } = useGetBalances(); + const { data: collateralCurrencies } = useGetCollateralCurrencies(bridgeLoaded); const { register, @@ -65,41 +68,85 @@ const BurnForm = (): JSX.Element | null => { }); const wrappedTokenAmount = watch(WRAPPED_TOKEN_AMOUNT); - const [burnRate, setBurnRate] = React.useState( - new ExchangeRate(Bitcoin, RELAY_CHAIN_NATIVE_TOKEN, new Big(0)) - ); - const [burnableTokens, setBurnableTokens] = React.useState(BitcoinAmount.zero()); + const [totalBurnableTokens, setTotalBurnableTokens] = React.useState(BitcoinAmount.zero()); + + const [burnableCollateral, setBurnableCollateral] = React.useState(); + const [selectedCollateral, setSelectedCollateral] = React.useState(); const [submitStatus, setSubmitStatus] = React.useState(STATUSES.IDLE); const [submitError, setSubmitError] = React.useState(null); + const handleUpdateCollateral = (collateral: TokenOption) => { + const selectedCollateral = burnableCollateral?.find( + (token: BurnableCollateral) => token.currency.ticker === collateral.token.ticker + ); + + setSelectedCollateral(selectedCollateral); + }; + + React.useEffect(() => { + if (!burnableCollateral) return; + + const totalBurnable = burnableCollateral.reduce( + (total: MonetaryAmount, collateral: BurnableCollateral) => total.add(collateral.burnableTokens), + new MonetaryAmount(Bitcoin, 0) + ); + + setTotalBurnableTokens(totalBurnable); + }, [burnableCollateral]); + React.useEffect(() => { if (!bridgeLoaded) return; if (!handleError) return; + if (!collateralCurrencies) return; (async () => { try { setStatus(STATUSES.PENDING); - const [theBurnRate, theBurnableTokens] = await Promise.all([ - window.bridge.redeem.getBurnExchangeRate(RELAY_CHAIN_NATIVE_TOKEN), - window.bridge.redeem.getMaxBurnableTokens(RELAY_CHAIN_NATIVE_TOKEN) - ]); - setBurnRate(theBurnRate); - setBurnableTokens(theBurnableTokens); + + const collateralData: BurnableCollateral[] = await Promise.all( + collateralCurrencies.map(async (currency: CollateralCurrencyExt) => { + const burnableTokens = await window.bridge.redeem.getMaxBurnableTokens(currency); + + const burnRate = burnableTokens.gt(BitcoinAmount.zero()) + ? await window.bridge.redeem.getBurnExchangeRate(currency) + : undefined; + + return { currency, burnableTokens, burnRate } as BurnableCollateral; + }) + ); + + const filteredCollateral = collateralData.filter((item) => item.burnRate); + + setBurnableCollateral(filteredCollateral); + setSelectedCollateral(filteredCollateral[0]); + setStatus(STATUSES.RESOLVED); } catch (error) { setStatus(STATUSES.REJECTED); handleError(error); } })(); - }, [bridgeLoaded, handleError]); + }, [bridgeLoaded, collateralCurrencies, handleError]); + + // This ensures that triggering the notification and clearing + // the form happen at the same time. + React.useEffect(() => { + if (submitStatus !== STATUSES.RESOLVED) return; + + toast.success(t('burn_page.successfully_burned')); + + reset({ + [WRAPPED_TOKEN_AMOUNT]: '' + }); + }, [submitStatus, reset, t]); if (status === STATUSES.IDLE || status === STATUSES.PENDING) { return ; } - if (status === STATUSES.RESOLVED) { - if (!burnRate) { + if (status === STATUSES.RESOLVED && selectedCollateral) { + if (!selectedCollateral.burnRate) { throw new Error('Something went wrong!'); } @@ -113,10 +160,8 @@ const BurnForm = (): JSX.Element | null => { const onSubmit = async (data: BurnFormData) => { try { setSubmitStatus(STATUSES.PENDING); - await window.bridge.redeem.burn(new BitcoinAmount(data[WRAPPED_TOKEN_AMOUNT]), RELAY_CHAIN_NATIVE_TOKEN); - reset({ - [WRAPPED_TOKEN_AMOUNT]: '' - }); + await window.bridge.redeem.burn(new BitcoinAmount(data[WRAPPED_TOKEN_AMOUNT]), selectedCollateral.currency); + setSubmitStatus(STATUSES.RESOLVED); } catch (error) { setSubmitStatus(STATUSES.REJECTED); @@ -128,12 +173,12 @@ const BurnForm = (): JSX.Element | null => { // TODO: should use wrapped token amount type (e.g. InterBtcAmount or KBtcAmount) const bitcoinAmountValue = new BitcoinAmount(value); - if (bitcoinAmountValue.gt(burnableTokens)) { - return `Only ${burnableTokens.toString()} ${WRAPPED_TOKEN_SYMBOL} available to burn. + if (bitcoinAmountValue.gt(selectedCollateral.burnableTokens)) { + return `Only ${selectedCollateral.burnableTokens.toString()} ${WRAPPED_TOKEN_SYMBOL} available to burn. Please enter a smaller amount.`; } - const wrappedTokenBalance = balances?.[WRAPPED_TOKEN.ticker].free || newMonetaryAmount(0, WRAPPED_TOKEN); + const wrappedTokenBalance = balances?.[WRAPPED_TOKEN.ticker].transferable || newMonetaryAmount(0, WRAPPED_TOKEN); if (bitcoinAmountValue.gt(wrappedTokenBalance)) { return `${t('redeem_page.current_balance')}${wrappedTokenBalance.toString()}`; @@ -161,9 +206,10 @@ const BurnForm = (): JSX.Element | null => { }; const parsedInterBTCAmount = new BitcoinAmount(wrappedTokenAmount || 0); - const earnedCollateralTokenAmount = burnRate.rate.eq(0) - ? newMonetaryAmount(0, RELAY_CHAIN_NATIVE_TOKEN) - : burnRate.toCounter(parsedInterBTCAmount || BitcoinAmount.zero()); + + const earnedCollateralTokenAmount = selectedCollateral.burnRate.rate.eq(0) + ? newMonetaryAmount(0, selectedCollateral.currency) + : selectedCollateral.burnRate.toCounter(parsedInterBTCAmount || BitcoinAmount.zero()); const accountSet = !!selectedAccount; return ( @@ -171,8 +217,7 @@ const BurnForm = (): JSX.Element | null => {
{t('burn_page.burn_interbtc', { - wrappedTokenSymbol: WRAPPED_TOKEN_SYMBOL, - collateralTokenSymbol: RELAY_CHAIN_NATIVE_TOKEN_SYMBOL + collateralTokenSymbol: selectedCollateral.currency.ticker })} { { 'dark:text-kintsugiTextSecondaryInDarkMode': process.env.REACT_APP_RELAY_CHAIN_NAME === KUSAMA } )} > - {t('burn_page.available', { + {t('burn_page.available_total', { wrappedTokenSymbol: WRAPPED_TOKEN_SYMBOL })} } unitIcon={} - value={burnableTokens.toString()} + value={totalBurnableTokens.toString()} unitName={WRAPPED_TOKEN_SYMBOL} approxUSD={displayMonetaryAmountInUSDFormat( - burnableTokens, + totalBurnableTokens, getTokenPrice(prices, ForeignAssetIdLiteral.BTC)?.usd )} /> @@ -213,6 +258,36 @@ const BurnForm = (): JSX.Element | null => { error={!!errors[WRAPPED_TOKEN_AMOUNT]} helperText={errors[WRAPPED_TOKEN_AMOUNT]?.message} /> + collateral.currency.ticker)} + variant='formField' + showBalances={false} + callbackFunction={handleUpdateCollateral} + fullWidth={true} + /> + + {t('burn_page.available_from_collateral', { + wrappedTokenSymbol: WRAPPED_TOKEN_SYMBOL, + collateralTokenSymbol: selectedCollateral.currency.ticker + })} + + } + unitIcon={} + value={selectedCollateral.burnableTokens.toString()} + unitName={WRAPPED_TOKEN_SYMBOL} + approxUSD={displayMonetaryAmountInUSDFormat( + selectedCollateral.burnableTokens, + getTokenPrice(prices, ForeignAssetIdLiteral.BTC)?.usd + )} + /> { {t('you_will_receive')} } - unitIcon={} + unitIcon={} value={earnedCollateralTokenAmount.toString()} - unitName={RELAY_CHAIN_NATIVE_TOKEN_SYMBOL} + unitName={selectedCollateral.currency.ticker} approxUSD={displayMonetaryAmountInUSDFormat( earnedCollateralTokenAmount, - getTokenPrice(prices, RELAY_CHAIN_NATIVE_TOKEN_SYMBOL)?.usd + getTokenPrice(prices, selectedCollateral.currency.ticker)?.usd )} /> { const [status, setStatus] = React.useState(STATUSES.IDLE); // Additional info: bridge fee, security deposit, amount BTC // Current fee model specification taken from: https://interlay.gitlab.io/polkabtc-spec/spec/fee.html - const [feeRate, setFeeRate] = React.useState(new Big(0.005)); // Set default to 0.5% - const [depositRate, setDepositRate] = React.useState(new Big(0.00005)); // Set default to 0.005% + const [issueFeeRate, setIssueFeeRate] = React.useState(new Big(DEFAULT_ISSUE_BRIDGE_FEE_RATE)); + const [depositRate, setDepositRate] = React.useState(new Big(DEFAULT_ISSUE_GRIEFING_COLLATERAL_RATE)); const [btcToGovernanceTokenRate, setBTCToGovernanceTokenRate] = React.useState( new ExchangeRate(Bitcoin, GOVERNANCE_TOKEN, new Big(0)) ); - const [dustValue, setDustValue] = React.useState(BitcoinAmount.zero()); + const [dustValue, setDustValue] = React.useState(new BitcoinAmount(DEFAULT_ISSUE_DUST_AMOUNT)); const [submitStatus, setSubmitStatus] = React.useState(STATUSES.IDLE); const [submitError, setSubmitError] = React.useState(null); const [submittedRequest, setSubmittedRequest] = React.useState(); @@ -145,10 +150,10 @@ const IssueForm = (): JSX.Element | null => { try { setStatus(STATUSES.PENDING); const [ - theFeeRateResult, - theDepositRateResult, - theDustValueResult, - theBtcToGovernanceTokenResult + feeRateResult, + depositRateResult, + dustValueResult, + btcToGovernanceTokenResult ] = await Promise.allSettled([ // Loading this data is not strictly required as long as the constantly set values did // not change. However, you will not see the correct value for the security deposit. @@ -159,32 +164,32 @@ const IssueForm = (): JSX.Element | null => { ]); setStatus(STATUSES.RESOLVED); - if (theFeeRateResult.status === 'rejected') { - throw new Error(theFeeRateResult.reason); + if (feeRateResult.status === 'rejected') { + throw new Error(feeRateResult.reason); } - if (theDepositRateResult.status === 'rejected') { - throw new Error(theDepositRateResult.reason); + if (depositRateResult.status === 'rejected') { + throw new Error(depositRateResult.reason); } - if (theDustValueResult.status === 'rejected') { - throw new Error(theDustValueResult.reason); + if (dustValueResult.status === 'rejected') { + throw new Error(dustValueResult.reason); } - if (theBtcToGovernanceTokenResult.status === 'rejected') { + if (btcToGovernanceTokenResult.status === 'rejected') { setError(BTC_AMOUNT, { type: 'validate', message: t('error_oracle_offline', { action: 'issue', wrappedTokenSymbol: WRAPPED_TOKEN_SYMBOL }) }); } - if (theBtcToGovernanceTokenResult.status === 'fulfilled') { - setBTCToGovernanceTokenRate(theBtcToGovernanceTokenResult.value); + if (btcToGovernanceTokenResult.status === 'fulfilled') { + setBTCToGovernanceTokenRate(btcToGovernanceTokenResult.value); } - setFeeRate(theFeeRateResult.value); - setDepositRate(theDepositRateResult.value); - setDustValue(theDustValueResult.value); + setIssueFeeRate(feeRateResult.value); + setDepositRate(depositRateResult.value); + setDustValue(dustValueResult.value); } catch (error) { setStatus(STATUSES.REJECTED); handleError(error); @@ -251,8 +256,8 @@ const IssueForm = (): JSX.Element | null => { const securityDeposit = btcToGovernanceTokenRate.toCounter(btcAmount).mul(depositRate); const minRequiredGovernanceTokenAmount = TRANSACTION_FEE_AMOUNT.add(securityDeposit); - if (governanceTokenBalance.free.lte(minRequiredGovernanceTokenAmount)) { - return t('insufficient_funds_governance_token', { + if (governanceTokenBalance.transferable.lte(minRequiredGovernanceTokenAmount)) { + return t('issue_page.insufficient_funds', { governanceTokenSymbol: GOVERNANCE_TOKEN_SYMBOL }); } @@ -274,14 +279,6 @@ const IssueForm = (): JSX.Element | null => { }); } - if (!bridgeLoaded) { - return 'Bridge must be loaded!'; - } - - if (btcAmount === undefined) { - return 'Invalid BTC amount input!'; - } - if (isOracleOffline) { return t('error_oracle_offline', { action: 'issue', wrappedTokenSymbol: WRAPPED_TOKEN_SYMBOL }); } @@ -328,11 +325,7 @@ const IssueForm = (): JSX.Element | null => { vaultId = getRandomVaultIdWithCapacity(Array.from(vaults), monetaryBtcAmount); } - const collateralToken = await currencyIdToMonetaryCurrency( - window.bridge.assetRegistry, - window.bridge.loans, - vaultId.currencies.collateral - ); + const collateralToken = await currencyIdToMonetaryCurrency(window.bridge.api, vaultId.currencies.collateral); const result = await window.bridge.issue.request( monetaryBtcAmount, @@ -354,10 +347,33 @@ const IssueForm = (): JSX.Element | null => { }; const monetaryBtcAmount = new BitcoinAmount(btcAmount); - const bridgeFee = monetaryBtcAmount.mul(feeRate); + + const bridgeFee = monetaryBtcAmount.mul(issueFeeRate); + const bridgeFeeInBTC = bridgeFee.toHuman(8); + const bridgeFeeInUSD = displayMonetaryAmountInUSDFormat( + bridgeFee, + getTokenPrice(prices, ForeignAssetIdLiteral.BTC)?.usd + ); + const securityDeposit = btcToGovernanceTokenRate.toCounter(monetaryBtcAmount).mul(depositRate); - const wrappedTokenAmount = monetaryBtcAmount.sub(bridgeFee); + const securityDepositInGovernanceToken = displayMonetaryAmount(securityDeposit); + const securityDepositInUSD = displayMonetaryAmountInUSDFormat( + securityDeposit, + getTokenPrice(prices, GOVERNANCE_TOKEN_SYMBOL)?.usd + ); + + const txFeeInGovernanceToken = displayMonetaryAmount(TRANSACTION_FEE_AMOUNT); + const txFeeInUSD = displayMonetaryAmountInUSDFormat( + TRANSACTION_FEE_AMOUNT, + getTokenPrice(prices, GOVERNANCE_TOKEN_SYMBOL)?.usd + ); + + const total = monetaryBtcAmount.sub(bridgeFee); + const totalInBTC = total.toHuman(8); + const totalInUSD = displayMonetaryAmountInUSDFormat(total, getTokenPrice(prices, ForeignAssetIdLiteral.BTC)?.usd); + const accountSet = !!selectedAccount; + const isSelectVaultCheckboxDisabled = monetaryBtcAmount.gt(requestLimits.singleVaultMaxIssuable); // `btcToGovernanceTokenRate` has 0 value only if oracle call fails @@ -376,11 +392,13 @@ const IssueForm = (): JSX.Element | null => {
{ } unitIcon={} - value={wrappedTokenAmount.toHuman(8)} + dataTestId='total-receiving-amount' + value={totalInBTC} unitName={WRAPPED_TOKEN_SYMBOL} - approxUSD={displayMonetaryAmountInUSDFormat( - wrappedTokenAmount, - getTokenPrice(prices, ForeignAssetIdLiteral.BTC)?.usd - )} + approxUSD={totalInUSD} /> { } unitIcon={} - value={bridgeFee.toHuman(8)} + dataTestId='issue-bridge-fee' + value={bridgeFeeInBTC} unitName='BTC' - approxUSD={displayMonetaryAmountInUSDFormat( - bridgeFee, - getTokenPrice(prices, ForeignAssetIdLiteral.BTC)?.usd - )} + approxUSD={bridgeFeeInUSD} tooltip={ { } unitIcon={} - value={displayMonetaryAmount(securityDeposit)} + dataTestId='security-deposit' + value={securityDepositInGovernanceToken} unitName={GOVERNANCE_TOKEN_SYMBOL} - approxUSD={displayMonetaryAmountInUSDFormat( - securityDeposit, - getTokenPrice(prices, GOVERNANCE_TOKEN_SYMBOL)?.usd - )} + approxUSD={securityDepositInUSD} tooltip={ { } unitIcon={} - value={displayMonetaryAmount(TRANSACTION_FEE_AMOUNT)} + dataTestId='transaction-fee' + value={txFeeInGovernanceToken} unitName={GOVERNANCE_TOKEN_SYMBOL} - approxUSD={displayMonetaryAmountInUSDFormat( - TRANSACTION_FEE_AMOUNT, - getTokenPrice(prices, GOVERNANCE_TOKEN_SYMBOL)?.usd - )} + approxUSD={txFeeInUSD} tooltip={ { } = useForm({ mode: 'onChange' }); - const wrappedTokenAmount = watch(WRAPPED_TOKEN_AMOUNT); - const [dustValue, setDustValue] = React.useState(BitcoinAmount.zero()); + const wrappedTokenAmount = watch(WRAPPED_TOKEN_AMOUNT) || '0'; + + const monetaryWrappedTokenAmount = React.useMemo(() => { + return new BitcoinAmount(wrappedTokenAmount); + }, [wrappedTokenAmount]); + + const [dustValue, setDustValue] = React.useState(new BitcoinAmount(DEFAULT_REDEEM_DUST_AMOUNT)); const [status, setStatus] = React.useState(STATUSES.IDLE); - const [redeemFee, setRedeemFee] = React.useState(BitcoinAmount.zero()); - const [redeemFeeRate, setRedeemFeeRate] = React.useState(new Big(0.005)); + const [redeemFeeRate, setRedeemFeeRate] = React.useState(new Big(DEFAULT_REDEEM_BRIDGE_FEE_RATE)); const [btcToRelayChainNativeTokenRate, setBtcToRelayChainNativeTokenRate] = React.useState( new ExchangeRate(Bitcoin, RELAY_CHAIN_NATIVE_TOKEN, new Big(0)) ); @@ -110,22 +116,19 @@ const RedeemForm = (): JSX.Element | null => { const [selectedVault, setSelectedVault] = React.useState(); React.useEffect(() => { - if (!wrappedTokenAmount) return; + if (!monetaryWrappedTokenAmount) return; if (!maxRedeemableCapacity) return; - const monetaryWrappedTokenAmount = new BitcoinAmount(wrappedTokenAmount); if (monetaryWrappedTokenAmount.gt(maxRedeemableCapacity)) { setSelectVaultManually(false); } - }, [wrappedTokenAmount, maxRedeemableCapacity]); + }, [monetaryWrappedTokenAmount, maxRedeemableCapacity]); React.useEffect(() => { - if (!wrappedTokenAmount) return; + if (!monetaryWrappedTokenAmount) return; if (!setError) return; if (!clearErrors) return; - const monetaryWrappedTokenAmount = new BitcoinAmount(wrappedTokenAmount); - if (selectVaultManually && selectedVault === undefined) { setError(VAULT_SELECTION, { type: 'validate', message: t('issue_page.vault_must_be_selected') }); } else if (selectVaultManually && selectedVault?.[1].lt(monetaryWrappedTokenAmount)) { @@ -133,17 +136,9 @@ const RedeemForm = (): JSX.Element | null => { } else { clearErrors(VAULT_SELECTION); } - }, [selectVaultManually, selectedVault, setError, clearErrors, t, wrappedTokenAmount]); + }, [selectVaultManually, selectedVault, setError, clearErrors, t, monetaryWrappedTokenAmount]); - React.useEffect(() => { - if (!bridgeLoaded) return; - if (!wrappedTokenAmount) return; - if (!redeemFeeRate) return; - - const monetaryWrappedTokenAmount = new BitcoinAmount(wrappedTokenAmount); - const theRedeemFee = monetaryWrappedTokenAmount.mul(redeemFeeRate); - setRedeemFee(theRedeemFee); - }, [bridgeLoaded, wrappedTokenAmount, redeemFeeRate]); + const bridgeFee = monetaryWrappedTokenAmount.mul(redeemFeeRate); React.useEffect(() => { if (!bridgeLoaded) return; @@ -324,43 +319,31 @@ const RedeemForm = (): JSX.Element | null => { const monetaryValue = new BitcoinAmount(value); const wrappedTokenBalance = balances?.[WRAPPED_TOKEN.ticker].free || newMonetaryAmount(0, WRAPPED_TOKEN); - if (monetaryValue.gt(wrappedTokenBalance)) { return `${t('redeem_page.current_balance')}${displayMonetaryAmount(wrappedTokenBalance)}`; } - if (monetaryValue.gte(maxRedeemableCapacity)) { + if (monetaryValue.gt(maxRedeemableCapacity)) { return `${t('redeem_page.request_exceeds_capacity', { maxRedeemableAmount: `${maxRedeemableCapacity.toHuman(8)} ${ForeignAssetIdLiteral.BTC}`, - redeemRequestAmount: `${monetaryValue.toHuman()} ${ForeignAssetIdLiteral.BTC}`, btcIdLiteral: `${ForeignAssetIdLiteral.BTC}` })}`; } - const monetaryWrappedTokenAmount = new BitcoinAmount(value); - const theRedeemFee = monetaryWrappedTokenAmount.mul(redeemFeeRate); - const minValue = dustValue.add(currentInclusionFee).add(theRedeemFee); - + const bridgeFee = monetaryValue.mul(redeemFeeRate); + const minValue = dustValue.add(currentInclusionFee).add(bridgeFee); if (monetaryValue.lte(minValue)) { return `${t('redeem_page.amount_greater_dust_inclusion')}${displayMonetaryAmount(minValue)} BTC).`; } - if (!selectedAccount) { - return t('redeem_page.must_select_account_warning'); - } - - if (!bridgeLoaded) { - return 'Bridge must be loaded!'; - } - if (bitcoinHeight - btcRelayHeight > BLOCKS_BEHIND_LIMIT) { return t('redeem_page.error_more_than_6_blocks_behind', { wrappedTokenSymbol: WRAPPED_TOKEN_SYMBOL }); } - const polkaBTCAmountInteger = value.toString().split('.')[0]; - if (polkaBTCAmountInteger.length > BALANCE_MAX_INTEGER_LENGTH) { + const wrappedTokenAmountInteger = value.toString().split('.')[0]; + if (wrappedTokenAmountInteger.length > BALANCE_MAX_INTEGER_LENGTH) { return 'Input value is too high!'; } @@ -376,21 +359,19 @@ const RedeemForm = (): JSX.Element | null => { dispatch(togglePremiumRedeemAction(!premiumRedeemSelected)); }; - const redeemFeeInBTC = redeemFee.toHuman(8); - const redeemFeeInUSD = displayMonetaryAmountInUSDFormat( - redeemFee, + const bridgeFeeInBTC = bridgeFee.toHuman(8); + const bridgeFeeInUSD = displayMonetaryAmountInUSDFormat( + bridgeFee, getTokenPrice(prices, ForeignAssetIdLiteral.BTC)?.usd ); - const monetaryWrappedTokenAmount = new BitcoinAmount(wrappedTokenAmount || 0); - const totalBTC = wrappedTokenAmount - ? monetaryWrappedTokenAmount.sub(redeemFee).sub(currentInclusionFee) + + const total = monetaryWrappedTokenAmount.gt(bridgeFee.add(currentInclusionFee)) + ? monetaryWrappedTokenAmount.sub(bridgeFee).sub(currentInclusionFee) : BitcoinAmount.zero(); - const totalBTCInUSD = displayMonetaryAmountInUSDFormat( - totalBTC, - getTokenPrice(prices, ForeignAssetIdLiteral.BTC)?.usd - ); + const totalInBTC = total.toHuman(8); + const totalInUSD = displayMonetaryAmountInUSDFormat(total, getTokenPrice(prices, ForeignAssetIdLiteral.BTC)?.usd); - const totalRelayChainNativeToken = wrappedTokenAmount + const totalRelayChainNativeToken = monetaryWrappedTokenAmount.gt(BitcoinAmount.zero()) ? btcToRelayChainNativeTokenRate.toCounter(monetaryWrappedTokenAmount).mul(premiumRedeemFee) : newMonetaryAmount(0, RELAY_CHAIN_NATIVE_TOKEN); const totalRelayChainNativeTokenInUSD = displayMonetaryAmountInUSDFormat( @@ -403,6 +384,7 @@ const RedeemForm = (): JSX.Element | null => { currentInclusionFee, getTokenPrice(prices, ForeignAssetIdLiteral.BTC)?.usd ); + const accountSet = !!selectedAccount; // `btcToDotRate` has 0 value only if oracle call fails @@ -420,6 +402,7 @@ const RedeemForm = (): JSX.Element | null => {
{ }, validate: (value) => validateForm(value) })} - approxUSD={`≈ ${displayMonetaryAmountInUSDFormat( - monetaryWrappedTokenAmount || BitcoinAmount.zero(), - usdPrice - )}`} + approxUSD={`≈ ${displayMonetaryAmountInUSDFormat(monetaryWrappedTokenAmount, usdPrice)}`} error={!!errors[WRAPPED_TOKEN_AMOUNT]} helperText={errors[WRAPPED_TOKEN_AMOUNT]?.message} /> @@ -458,7 +438,7 @@ const RedeemForm = (): JSX.Element | null => { { } unitIcon={} - value={totalBTC.toHuman(8)} + dataTestId='total-receiving-amount' + value={totalInBTC} unitName='BTC' - approxUSD={totalBTCInUSD} + approxUSD={totalInUSD} /> { } unitIcon={} - value={redeemFeeInBTC} + dataTestId='redeem-bridge-fee' + value={bridgeFeeInBTC} unitName='BTC' - approxUSD={redeemFeeInUSD} + approxUSD={bridgeFeeInUSD} /> { } unitIcon={} + dataTestId='redeem-bitcoin-network-fee' value={bitcoinNetworkFeeInBTC} unitName='BTC' approxUSD={bitcoinNetworkFeeInUSD} @@ -573,6 +556,8 @@ const RedeemForm = (): JSX.Element | null => { return null; }; +export { BTC_ADDRESS_LABEL }; + export default withErrorBoundary(RedeemForm, { FallbackComponent: ErrorFallback, onReset: () => { diff --git a/src/pages/Bridge/index.tsx b/src/pages/Bridge/index.tsx index fa6d124a4a..50a3bc78e1 100644 --- a/src/pages/Bridge/index.tsx +++ b/src/pages/Bridge/index.tsx @@ -5,18 +5,18 @@ import { useTranslation } from 'react-i18next'; import { useSelector } from 'react-redux'; import { StoreType } from '@/common/types/util.types'; -import Hr1 from '@/components/hrs/Hr1'; -import Panel from '@/components/Panel'; +import Hr1 from '@/legacy-components/hrs/Hr1'; +import Panel from '@/legacy-components/Panel'; import InterlayTabGroup, { InterlayTab, InterlayTabList, InterlayTabPanel, InterlayTabPanels -} from '@/components/UI/InterlayTabGroup'; -import { RELAY_CHAIN_NATIVE_TOKEN } from '@/config/relay-chains'; +} from '@/legacy-components/UI/InterlayTabGroup'; import MainContainer from '@/parts/MainContainer'; import { QUERY_PARAMETERS } from '@/utils/constants/links'; import TAB_IDS from '@/utils/constants/tab-ids'; +import { useGetCollateralCurrencies } from '@/utils/hooks/api/use-get-collateral-currencies'; import useQueryParams from '@/utils/hooks/use-query-params'; import useUpdateQueryParameters, { QueryParameters } from '@/utils/hooks/use-update-query-parameters'; @@ -53,6 +53,8 @@ const Bridge = (): JSX.Element | null => { const [burnable, setBurnable] = React.useState(false); + const { data: collateralCurrencies } = useGetCollateralCurrencies(bridgeLoaded); + const updateQueryParametersRef = React.useRef<(newQueryParameters: QueryParameters) => void>(); // MEMO: inspired by https://epicreact.dev/the-latest-ref-pattern-in-react/ React.useLayoutEffect(() => { @@ -75,16 +77,25 @@ const Bridge = (): JSX.Element | null => { React.useEffect(() => { if (!bridgeLoaded) return; + if (!collateralCurrencies) return; + (async () => { try { - const maxBurnableTokens = await window.bridge.redeem.getMaxBurnableTokens(RELAY_CHAIN_NATIVE_TOKEN); - setBurnable(maxBurnableTokens.gt(BitcoinAmount.zero())); + const burnableTokens = await Promise.all( + collateralCurrencies.map(async (collateral) => { + const maxBurnable = await window.bridge.redeem.getMaxBurnableTokens(collateral); + + return maxBurnable.gt(BitcoinAmount.zero()); + }) + ); + + setBurnable(burnableTokens.includes(true)); } catch (error) { // TODO: should add error handling console.log('[Bridge] error => ', error); } })(); - }, [bridgeLoaded]); + }, [bridgeLoaded, collateralCurrencies]); if (selectedTabId === null) { return null; diff --git a/src/pages/Dashboard/IssuedChart/index.tsx b/src/pages/Dashboard/IssuedChart/index.tsx index 293e822569..c3f3c3a117 100644 --- a/src/pages/Dashboard/IssuedChart/index.tsx +++ b/src/pages/Dashboard/IssuedChart/index.tsx @@ -4,9 +4,9 @@ import { useTranslation } from 'react-i18next'; import { useQuery } from 'react-query'; import { getLastMidnightTimestamps } from '@/common/utils/utils'; -import ErrorFallback from '@/components/ErrorFallback'; import { COUNT_OF_DATES_FOR_CHART } from '@/config/charts'; import { WRAPPED_TOKEN, WRAPPED_TOKEN_SYMBOL } from '@/config/relay-chains'; +import ErrorFallback from '@/legacy-components/ErrorFallback'; import cumulativeVolumesFetcher, { CUMULATIVE_VOLUMES_FETCHER, VolumeDataPoint, diff --git a/src/pages/Dashboard/Stats/index.tsx b/src/pages/Dashboard/Stats/index.tsx index 440b680e11..dc69e52cac 100644 --- a/src/pages/Dashboard/Stats/index.tsx +++ b/src/pages/Dashboard/Stats/index.tsx @@ -1,7 +1,7 @@ import clsx from 'clsx'; import * as React from 'react'; -import InterlayRouterLink, { Props as InterlayRouterLinkProps } from '@/components/UI/InterlayRouterLink'; +import InterlayRouterLink, { Props as InterlayRouterLinkProps } from '@/legacy-components/UI/InterlayRouterLink'; import { KUSAMA, POLKADOT } from '@/utils/constants/relay-chain-names'; const StatsDt = ({ className, ...rest }: React.ComponentPropsWithRef<'dt'>): JSX.Element => ( diff --git a/src/pages/Dashboard/cards/ActiveVaultsCard/index.tsx b/src/pages/Dashboard/cards/ActiveVaultsCard/index.tsx index 35288e57f1..cf433a86c5 100644 --- a/src/pages/Dashboard/cards/ActiveVaultsCard/index.tsx +++ b/src/pages/Dashboard/cards/ActiveVaultsCard/index.tsx @@ -3,8 +3,8 @@ import { useTranslation } from 'react-i18next'; import { useQuery } from 'react-query'; import { formatNumber, getLastMidnightTimestamps } from '@/common/utils/utils'; -import ErrorFallback from '@/components/ErrorFallback'; import { COUNT_OF_DATES_FOR_CHART } from '@/config/charts'; +import ErrorFallback from '@/legacy-components/ErrorFallback'; import graphqlFetcher, { GRAPHQL_FETCHER, GraphqlReturn } from '@/services/fetchers/graphql-fetcher'; import { INTERLAY_DENIM, KINTSUGI_SUNDOWN } from '@/utils/constants/colors'; import { PAGES } from '@/utils/constants/links'; diff --git a/src/pages/Dashboard/cards/BTCRelayCard/index.tsx b/src/pages/Dashboard/cards/BTCRelayCard/index.tsx index 99103fa75a..0fe8b4ba7a 100644 --- a/src/pages/Dashboard/cards/BTCRelayCard/index.tsx +++ b/src/pages/Dashboard/cards/BTCRelayCard/index.tsx @@ -4,7 +4,7 @@ import { useSelector } from 'react-redux'; import { StoreType } from '@/common/types/util.types'; import { formatNumber } from '@/common/utils/utils'; -import Ring64, { Ring64Title, Ring64Value } from '@/components/Ring64'; +import Ring64, { Ring64Title, Ring64Value } from '@/legacy-components/Ring64'; import { PAGES } from '@/utils/constants/links'; import { getColorShade } from '@/utils/helpers/colors'; diff --git a/src/pages/Dashboard/cards/CollateralizationCard/index.tsx b/src/pages/Dashboard/cards/CollateralizationCard/index.tsx index 3a3346cf27..8d252d5f86 100644 --- a/src/pages/Dashboard/cards/CollateralizationCard/index.tsx +++ b/src/pages/Dashboard/cards/CollateralizationCard/index.tsx @@ -8,9 +8,9 @@ import { useSelector } from 'react-redux'; import { StoreType } from '@/common/types/util.types'; import { formatPercentage } from '@/common/utils/utils'; -import ErrorFallback from '@/components/ErrorFallback'; -import Ring64, { Ring64Title, Ring64Value } from '@/components/Ring64'; import { RELAY_CHAIN_NATIVE_TOKEN, WRAPPED_TOKEN_SYMBOL } from '@/config/relay-chains'; +import ErrorFallback from '@/legacy-components/ErrorFallback'; +import Ring64, { Ring64Title, Ring64Value } from '@/legacy-components/Ring64'; import genericFetcher, { GENERIC_FETCHER } from '@/services/fetchers/generic-fetcher'; import { PAGES } from '@/utils/constants/links'; import { KUSAMA, POLKADOT } from '@/utils/constants/relay-chain-names'; diff --git a/src/pages/Dashboard/cards/DashboardCard/index.tsx b/src/pages/Dashboard/cards/DashboardCard/index.tsx index 6f1151ff61..c9f00809db 100644 --- a/src/pages/Dashboard/cards/DashboardCard/index.tsx +++ b/src/pages/Dashboard/cards/DashboardCard/index.tsx @@ -1,6 +1,6 @@ import clsx from 'clsx'; -import Panel from '@/components/Panel'; +import Panel from '@/legacy-components/Panel'; const DashboardCard = ({ className, ...rest }: React.ComponentPropsWithRef<'div'>): JSX.Element => ( { return <>{formatDateTimePrecise(new Date(date))}; } }, + { + Header: t('view_details'), + classNames: ['text-left'], + // TODO: should type properly (`Relay`) + Cell: ({ row: { original: issue } }: any) => { + return ; + } + }, { Header: t('issue_page.parachain_block'), classNames: ['text-right'], diff --git a/src/pages/Dashboard/sub-pages/IssueRequests/UpperContent/index.tsx b/src/pages/Dashboard/sub-pages/IssueRequests/UpperContent/index.tsx index 12caad0856..58531e4921 100644 --- a/src/pages/Dashboard/sub-pages/IssueRequests/UpperContent/index.tsx +++ b/src/pages/Dashboard/sub-pages/IssueRequests/UpperContent/index.tsx @@ -6,9 +6,9 @@ import { useSelector } from 'react-redux'; import { StoreType } from '@/common/types/util.types'; import { displayMonetaryAmountInUSDFormat, formatNumber } from '@/common/utils/utils'; -import ErrorFallback from '@/components/ErrorFallback'; -import Panel from '@/components/Panel'; import { WRAPPED_TOKEN_SYMBOL } from '@/config/relay-chains'; +import ErrorFallback from '@/legacy-components/ErrorFallback'; +import Panel from '@/legacy-components/Panel'; import IssuedChart from '@/pages/Dashboard/IssuedChart'; import graphqlFetcher, { GRAPHQL_FETCHER, GraphqlReturn } from '@/services/fetchers/graphql-fetcher'; import { issuesCountQuery } from '@/services/queries/issues'; diff --git a/src/pages/Dashboard/sub-pages/IssueRequests/index.tsx b/src/pages/Dashboard/sub-pages/IssueRequests/index.tsx index e221a98ddc..c1065c9d83 100644 --- a/src/pages/Dashboard/sub-pages/IssueRequests/index.tsx +++ b/src/pages/Dashboard/sub-pages/IssueRequests/index.tsx @@ -1,6 +1,6 @@ import { useTranslation } from 'react-i18next'; -import Hr1 from '@/components/hrs/Hr1'; +import Hr1 from '@/legacy-components/hrs/Hr1'; import PageTitle from '@/parts/PageTitle'; import TimerIncrement from '@/parts/TimerIncrement'; diff --git a/src/pages/Dashboard/sub-pages/Oracles/OraclesTable/index.tsx b/src/pages/Dashboard/sub-pages/Oracles/OraclesTable/index.tsx index 6a15f2f0cb..fbbaa2b69b 100644 --- a/src/pages/Dashboard/sub-pages/Oracles/OraclesTable/index.tsx +++ b/src/pages/Dashboard/sub-pages/Oracles/OraclesTable/index.tsx @@ -11,8 +11,9 @@ import { ReactComponent as CancelIcon } from '@/assets/img/icons/cancel.svg'; import { ReactComponent as CheckCircleIcon } from '@/assets/img/icons/check-circle.svg'; import { StoreType } from '@/common/types/util.types'; import { formatDateTime, formatNumber } from '@/common/utils/utils'; -import ErrorFallback from '@/components/ErrorFallback'; -import PrimaryColorEllipsisLoader from '@/components/PrimaryColorEllipsisLoader'; +import { RELAY_CHAIN_NATIVE_TOKEN, RELAY_CHAIN_NATIVE_TOKEN_SYMBOL } from '@/config/relay-chains'; +import ErrorFallback from '@/legacy-components/ErrorFallback'; +import PrimaryColorEllipsisLoader from '@/legacy-components/PrimaryColorEllipsisLoader'; import InterlayTable, { InterlayTableContainer, InterlayTbody, @@ -20,8 +21,7 @@ import InterlayTable, { InterlayTh, InterlayThead, InterlayTr -} from '@/components/UI/InterlayTable'; -import { RELAY_CHAIN_NATIVE_TOKEN, RELAY_CHAIN_NATIVE_TOKEN_SYMBOL } from '@/config/relay-chains'; +} from '@/legacy-components/UI/InterlayTable'; import SectionTitle from '@/parts/SectionTitle'; import genericFetcher, { GENERIC_FETCHER } from '@/services/fetchers/generic-fetcher'; import { diff --git a/src/pages/Dashboard/sub-pages/Oracles/index.tsx b/src/pages/Dashboard/sub-pages/Oracles/index.tsx index 7aa14f0ed7..8c43332005 100644 --- a/src/pages/Dashboard/sub-pages/Oracles/index.tsx +++ b/src/pages/Dashboard/sub-pages/Oracles/index.tsx @@ -1,6 +1,6 @@ import { useTranslation } from 'react-i18next'; -import Hr1 from '@/components/hrs/Hr1'; +import Hr1 from '@/legacy-components/hrs/Hr1'; import PageTitle from '@/parts/PageTitle'; import TimerIncrement from '@/parts/TimerIncrement'; diff --git a/src/pages/Dashboard/sub-pages/Parachain/index.tsx b/src/pages/Dashboard/sub-pages/Parachain/index.tsx index 405f3e6aa5..46493244d2 100644 --- a/src/pages/Dashboard/sub-pages/Parachain/index.tsx +++ b/src/pages/Dashboard/sub-pages/Parachain/index.tsx @@ -1,6 +1,6 @@ import { useTranslation } from 'react-i18next'; -import Hr1 from '@/components/hrs/Hr1'; +import Hr1 from '@/legacy-components/hrs/Hr1'; import PageTitle from '@/parts/PageTitle'; import TimerIncrement from '@/parts/TimerIncrement'; diff --git a/src/pages/Dashboard/sub-pages/RedeemRequests/RedeemRequestsTable/index.tsx b/src/pages/Dashboard/sub-pages/RedeemRequests/RedeemRequestsTable/index.tsx index 986e78b735..a9edfa287e 100644 --- a/src/pages/Dashboard/sub-pages/RedeemRequests/RedeemRequestsTable/index.tsx +++ b/src/pages/Dashboard/sub-pages/RedeemRequests/RedeemRequestsTable/index.tsx @@ -7,11 +7,12 @@ import { useQuery } from 'react-query'; import { useTable } from 'react-table'; import { formatDateTimePrecise, formatNumber, shortAddress, shortTxId } from '@/common/utils/utils'; -import AddressWithCopyUI from '@/components/AddressWithCopyUI'; -import ErrorFallback from '@/components/ErrorFallback'; -import ExternalLink from '@/components/ExternalLink'; -import PrimaryColorEllipsisLoader from '@/components/PrimaryColorEllipsisLoader'; -import InterlayPagination from '@/components/UI/InterlayPagination'; +import { BTC_EXPLORER_ADDRESS_API, BTC_EXPLORER_TRANSACTION_API } from '@/config/blockstream-explorer-links'; +import AddressWithCopyUI from '@/legacy-components/AddressWithCopyUI'; +import ErrorFallback from '@/legacy-components/ErrorFallback'; +import ExternalLink from '@/legacy-components/ExternalLink'; +import PrimaryColorEllipsisLoader from '@/legacy-components/PrimaryColorEllipsisLoader'; +import InterlayPagination from '@/legacy-components/UI/InterlayPagination'; import InterlayTable, { InterlayTableContainer, InterlayTbody, @@ -19,9 +20,9 @@ import InterlayTable, { InterlayTh, InterlayThead, InterlayTr -} from '@/components/UI/InterlayTable'; -import StatusCell from '@/components/UI/InterlayTable/StatusCell'; -import { BTC_EXPLORER_ADDRESS_API, BTC_EXPLORER_TRANSACTION_API } from '@/config/blockstream-explorer-links'; +} from '@/legacy-components/UI/InterlayTable'; +import StatusCell from '@/legacy-components/UI/InterlayTable/StatusCell'; +import ViewRequestDetailsLink from '@/legacy-components/ViewRequestDetailsLink'; import SectionTitle from '@/parts/SectionTitle'; import graphqlFetcher, { GRAPHQL_FETCHER, GraphqlReturn } from '@/services/fetchers/graphql-fetcher'; import redeemsFetcher, { getRedeemWithStatus, REDEEMS_FETCHER } from '@/services/fetchers/redeems-fetcher'; @@ -29,6 +30,7 @@ import useCurrentActiveBlockNumber from '@/services/hooks/use-current-active-blo import useStableBitcoinConfirmations from '@/services/hooks/use-stable-bitcoin-confirmations'; import useStableParachainConfirmations from '@/services/hooks/use-stable-parachain-confirmations'; import redeemCountQuery from '@/services/queries/redeem-count-query'; +import { TXType } from '@/types/general.d'; import { TABLE_PAGE_LIMIT } from '@/utils/constants/general'; import { QUERY_PARAMETERS } from '@/utils/constants/links'; import useQueryParams from '@/utils/hooks/use-query-params'; @@ -67,6 +69,14 @@ const RedeemRequestsTable = (): JSX.Element => { return <>{formatDateTimePrecise(new Date(date))}; } }, + { + Header: t('view_details'), + classNames: ['text-left'], + // TODO: should type properly (`Relay`) + Cell: ({ row: { original: redeem } }: any) => { + return ; + } + }, { Header: t('parachain_block'), classNames: ['text-right'], diff --git a/src/pages/Dashboard/sub-pages/RedeemRequests/UpperContent/RedeemedChart/index.tsx b/src/pages/Dashboard/sub-pages/RedeemRequests/UpperContent/RedeemedChart/index.tsx index faec84b1bd..095ff5999f 100644 --- a/src/pages/Dashboard/sub-pages/RedeemRequests/UpperContent/RedeemedChart/index.tsx +++ b/src/pages/Dashboard/sub-pages/RedeemRequests/UpperContent/RedeemedChart/index.tsx @@ -4,9 +4,9 @@ import { useTranslation } from 'react-i18next'; import { useQuery } from 'react-query'; import { getLastMidnightTimestamps } from '@/common/utils/utils'; -import ErrorFallback from '@/components/ErrorFallback'; import { COUNT_OF_DATES_FOR_CHART } from '@/config/charts'; import { WRAPPED_TOKEN } from '@/config/relay-chains'; +import ErrorFallback from '@/legacy-components/ErrorFallback'; import cumulativeVolumesFetcher, { CUMULATIVE_VOLUMES_FETCHER, VolumeDataPoint, diff --git a/src/pages/Dashboard/sub-pages/RedeemRequests/UpperContent/index.tsx b/src/pages/Dashboard/sub-pages/RedeemRequests/UpperContent/index.tsx index 518c8e6aec..f8f23c8f19 100644 --- a/src/pages/Dashboard/sub-pages/RedeemRequests/UpperContent/index.tsx +++ b/src/pages/Dashboard/sub-pages/RedeemRequests/UpperContent/index.tsx @@ -4,9 +4,9 @@ import { useTranslation } from 'react-i18next'; import { useQuery } from 'react-query'; import { displayMonetaryAmountInUSDFormat, formatNumber } from '@/common/utils/utils'; -import ErrorFallback from '@/components/ErrorFallback'; -import Panel from '@/components/Panel'; import { WRAPPED_TOKEN } from '@/config/relay-chains'; +import ErrorFallback from '@/legacy-components/ErrorFallback'; +import Panel from '@/legacy-components/Panel'; import cumulativeVolumesFetcher, { CUMULATIVE_VOLUMES_FETCHER, VolumeDataPoint, diff --git a/src/pages/Dashboard/sub-pages/RedeemRequests/index.tsx b/src/pages/Dashboard/sub-pages/RedeemRequests/index.tsx index c06f375f87..96bc281bff 100644 --- a/src/pages/Dashboard/sub-pages/RedeemRequests/index.tsx +++ b/src/pages/Dashboard/sub-pages/RedeemRequests/index.tsx @@ -1,6 +1,6 @@ import { useTranslation } from 'react-i18next'; -import Hr1 from '@/components/hrs/Hr1'; +import Hr1 from '@/legacy-components/hrs/Hr1'; import RedeemRequestsTable from '@/pages/Dashboard/sub-pages/RedeemRequests/RedeemRequestsTable'; import PageTitle from '@/parts/PageTitle'; import TimerIncrement from '@/parts/TimerIncrement'; diff --git a/src/pages/Dashboard/sub-pages/Vaults/LockedCollateralCard/index.tsx b/src/pages/Dashboard/sub-pages/Vaults/LockedCollateralCard/index.tsx index a3546602a3..f02e3d314e 100644 --- a/src/pages/Dashboard/sub-pages/Vaults/LockedCollateralCard/index.tsx +++ b/src/pages/Dashboard/sub-pages/Vaults/LockedCollateralCard/index.tsx @@ -7,8 +7,8 @@ import { displayMonetaryAmountInUSDFormat, getLastMidnightTimestamps } from '@/common/utils/utils'; -import ErrorFallback from '@/components/ErrorFallback'; import { COUNT_OF_DATES_FOR_CHART } from '@/config/charts'; +import ErrorFallback from '@/legacy-components/ErrorFallback'; import DashboardCard from '@/pages/Dashboard/cards/DashboardCard'; import LineChart from '@/pages/Dashboard/LineChart'; import Stats, { StatsDd, StatsDt } from '@/pages/Dashboard/Stats'; diff --git a/src/pages/Dashboard/sub-pages/Vaults/VaultsTable/index.tsx b/src/pages/Dashboard/sub-pages/Vaults/VaultsTable/index.tsx index d24331f035..2603201de4 100644 --- a/src/pages/Dashboard/sub-pages/Vaults/VaultsTable/index.tsx +++ b/src/pages/Dashboard/sub-pages/Vaults/VaultsTable/index.tsx @@ -12,10 +12,12 @@ import { useTable } from 'react-table'; import { StoreType } from '@/common/types/util.types'; import { displayMonetaryAmount, formatPercentage } from '@/common/utils/utils'; -import AddressWithCopyUI from '@/components/AddressWithCopyUI'; -import ErrorFallback from '@/components/ErrorFallback'; -import PrimaryColorEllipsisLoader from '@/components/PrimaryColorEllipsisLoader'; -import InformationTooltip from '@/components/tooltips/InformationTooltip'; +import { WRAPPED_TOKEN_SYMBOL } from '@/config/relay-chains'; +import * as constants from '@/constants'; +import AddressWithCopyUI from '@/legacy-components/AddressWithCopyUI'; +import ErrorFallback from '@/legacy-components/ErrorFallback'; +import PrimaryColorEllipsisLoader from '@/legacy-components/PrimaryColorEllipsisLoader'; +import InformationTooltip from '@/legacy-components/tooltips/InformationTooltip'; import InterlayTable, { InterlayTableContainer, InterlayTbody, @@ -23,9 +25,7 @@ import InterlayTable, { InterlayTh, InterlayThead, InterlayTr -} from '@/components/UI/InterlayTable'; -import { WRAPPED_TOKEN_SYMBOL } from '@/config/relay-chains'; -import * as constants from '@/constants'; +} from '@/legacy-components/UI/InterlayTable'; import SectionTitle from '@/parts/SectionTitle'; import genericFetcher, { GENERIC_FETCHER } from '@/services/fetchers/generic-fetcher'; import useCurrentActiveBlockNumber from '@/services/hooks/use-current-active-block-number'; diff --git a/src/pages/Dashboard/sub-pages/Vaults/index.tsx b/src/pages/Dashboard/sub-pages/Vaults/index.tsx index 1964eb5894..dcdb03afac 100644 --- a/src/pages/Dashboard/sub-pages/Vaults/index.tsx +++ b/src/pages/Dashboard/sub-pages/Vaults/index.tsx @@ -4,8 +4,8 @@ import { useTranslation } from 'react-i18next'; import { useSelector } from 'react-redux'; import { StoreType } from '@/common/types/util.types'; -import Hr1 from '@/components/hrs/Hr1'; -import PrimaryColorEllipsisLoader from '@/components/PrimaryColorEllipsisLoader'; +import Hr1 from '@/legacy-components/hrs/Hr1'; +import PrimaryColorEllipsisLoader from '@/legacy-components/PrimaryColorEllipsisLoader'; import PageTitle from '@/parts/PageTitle'; import TimerIncrement from '@/parts/TimerIncrement'; import { getTokenPrice } from '@/utils/helpers/prices'; diff --git a/src/pages/Loans/Loans.tsx b/src/pages/Loans/Loans.tsx index 1002a64655..6cb344a423 100644 --- a/src/pages/Loans/Loans.tsx +++ b/src/pages/Loans/Loans.tsx @@ -1,6 +1,6 @@ import { withErrorBoundary } from 'react-error-boundary'; -import ErrorFallback from '@/components/ErrorFallback'; +import ErrorFallback from '@/legacy-components/ErrorFallback'; import LoansOverview from './LoansOverview'; diff --git a/src/pages/Loans/LoansOverview/LoansOverview.tsx b/src/pages/Loans/LoansOverview/LoansOverview.tsx index f62685e977..143e82ee36 100644 --- a/src/pages/Loans/LoansOverview/LoansOverview.tsx +++ b/src/pages/Loans/LoansOverview/LoansOverview.tsx @@ -1,6 +1,7 @@ import { Flex } from '@/component-library'; -import FullLoadingSpinner from '@/components/FullLoadingSpinner'; +import FullLoadingSpinner from '@/legacy-components/FullLoadingSpinner'; import MainContainer from '@/parts/MainContainer'; +import { useGetAccountLendingStatistics } from '@/utils/hooks/api/loans/use-get-account-lending-statistics'; import { useGetAccountPositions } from '@/utils/hooks/api/loans/use-get-account-positions'; import { useGetLoanAssets } from '@/utils/hooks/api/loans/use-get-loan-assets'; import useAccountId from '@/utils/hooks/use-account-id'; @@ -9,11 +10,15 @@ import { LoansInsights, LoansTables, LTVSection } from './components'; const LoansOverview = (): JSX.Element => { const accountId = useAccountId(); + const { data: assets } = useGetLoanAssets(); + const { - data: { borrowPositions, lendPositions, hasCollateral, statistics } + data: { borrowPositions, lendPositions, hasCollateral } } = useGetAccountPositions(); + const { data: statistics } = useGetAccountLendingStatistics(); + const isLoadingPositions = accountId !== undefined && (lendPositions === undefined || borrowPositions === undefined); if (assets === undefined || isLoadingPositions) { diff --git a/src/pages/Loans/LoansOverview/components/ApyTooltip/index.tsx b/src/pages/Loans/LoansOverview/components/ApyTooltip/index.tsx deleted file mode 100644 index 97b1e1d6b0..0000000000 --- a/src/pages/Loans/LoansOverview/components/ApyTooltip/index.tsx +++ /dev/null @@ -1,2 +0,0 @@ -export type { ApyTooltipProps } from './ApyTooltip'; -export { ApyTooltip } from './ApyTooltip'; diff --git a/src/pages/Loans/LoansOverview/components/BorrowAssetsTable/BorrowAssetsTable.style.tsx b/src/pages/Loans/LoansOverview/components/BorrowAssetsTable/BorrowAssetsTable.style.tsx index ae13b2a0f3..06638f5074 100644 --- a/src/pages/Loans/LoansOverview/components/BorrowAssetsTable/BorrowAssetsTable.style.tsx +++ b/src/pages/Loans/LoansOverview/components/BorrowAssetsTable/BorrowAssetsTable.style.tsx @@ -1,9 +1,10 @@ import styled from 'styled-components'; -import { LoansBaseTable } from '../LoansBaseTable'; +import { AssetCell } from '@/components'; -const StyledBorrowAssetsTable = styled(LoansBaseTable)` - grid-area: borrow-assets-table; +const StyledAssetCell = styled(AssetCell)` + // Needs a specific rem so that row size matches on both lending and borrow tables + padding: 0.5625rem 0; `; -export { StyledBorrowAssetsTable }; +export { StyledAssetCell }; diff --git a/src/pages/Loans/LoansOverview/components/BorrowAssetsTable/BorrowAssetsTable.tsx b/src/pages/Loans/LoansOverview/components/BorrowAssetsTable/BorrowAssetsTable.tsx index 43033ce9d6..e81e132de8 100644 --- a/src/pages/Loans/LoansOverview/components/BorrowAssetsTable/BorrowAssetsTable.tsx +++ b/src/pages/Loans/LoansOverview/components/BorrowAssetsTable/BorrowAssetsTable.tsx @@ -1,12 +1,16 @@ import { LoanAsset, TickerToData } from '@interlay/interbtc-api'; +import { useId } from '@react-aria/utils'; import { Key, ReactNode, useMemo } from 'react'; import { useTranslation } from 'react-i18next'; import { convertMonetaryAmountToValueInUSD, formatUSD } from '@/common/utils/utils'; +import { Cell, Table, TableProps } from '@/components'; +import { ApyCell } from '@/components/LoanPositionsTable/ApyCell'; +import { LoanTablePlaceholder } from '@/components/LoanPositionsTable/LoanTablePlaceholder'; +import { getTokenPrice } from '@/utils/helpers/prices'; import { useGetPrices } from '@/utils/hooks/api/use-get-prices'; -import { ApyCell, AssetCell, LoansBaseTable, LoansBaseTableProps } from '../LoansBaseTable'; -import { MonetaryCell } from '../LoansBaseTable/MonetaryCell'; +import { StyledAssetCell } from './BorrowAssetsTable.style'; enum BorrowAssetsColumns { ASSET = 'asset', @@ -31,20 +35,23 @@ const borrowAssetsColumns = [ { name: 'Total Borrowed', uid: BorrowAssetsColumns.TOTAL_BORROWED } ]; -type BorrowAssetsTableProps = { +type Props = { assets: TickerToData; - onRowAction: LoansBaseTableProps['onRowAction']; - disabledKeys: LoansBaseTableProps['disabledKeys']; }; -const BorrowAssetsTable = ({ assets, onRowAction, disabledKeys }: BorrowAssetsTableProps): JSX.Element => { +type InheritAttrs = Omit; + +type BorrowAssetsTableProps = Props & InheritAttrs; + +const BorrowAssetsTable = ({ assets, onRowAction, ...props }: BorrowAssetsTableProps): JSX.Element => { + const titleId = useId(); const { t } = useTranslation(); const prices = useGetPrices(); const rows: BorrowAssetsTableRow[] = useMemo( () => Object.values(assets).map(({ borrowApy, currency, availableCapacity, borrowReward, totalBorrows }) => { - const asset = ; + const asset = ; const apy = ( ; + const capacity = ; const totalBorrowsUSD = convertMonetaryAmountToValueInUSD( totalBorrows, - prices?.[totalBorrows.currency.ticker].usd - ); - const totalBorrowed = ( - + getTokenPrice(prices, totalBorrows.currency.ticker)?.usd ); + const totalBorrowed = ; return { id: currency.ticker, @@ -84,12 +89,14 @@ const BorrowAssetsTable = ({ assets, onRowAction, disabledKeys }: BorrowAssetsTa ); return ( - } + onRowAction={onRowAction} /> ); }; diff --git a/src/pages/Loans/LoansOverview/components/BorrowLimit/BorrowLimit.style.tsx b/src/pages/Loans/LoansOverview/components/BorrowLimit/BorrowLimit.style.tsx index c27d3d8f82..5c01fc65b8 100644 --- a/src/pages/Loans/LoansOverview/components/BorrowLimit/BorrowLimit.style.tsx +++ b/src/pages/Loans/LoansOverview/components/BorrowLimit/BorrowLimit.style.tsx @@ -16,10 +16,20 @@ const StyledDd = styled(Dd)` display: flex; gap: ${theme.spacing.spacing2}; align-items: center; + overflow: hidden; `; const StyledDl = styled(Dl)` font-size: ${theme.text.s}; `; -export { StyledDd, StyledDl }; +const StyledSpan = styled.span` + white-space: nowrap; + + &:last-of-type { + text-overflow: ellipsis; + overflow: hidden; + } +`; + +export { StyledDd, StyledDl, StyledSpan }; diff --git a/src/pages/Loans/LoansOverview/components/BorrowLimit/BorrowLimit.tsx b/src/pages/Loans/LoansOverview/components/BorrowLimit/BorrowLimit.tsx index 8be5118916..ecb727d6ab 100644 --- a/src/pages/Loans/LoansOverview/components/BorrowLimit/BorrowLimit.tsx +++ b/src/pages/Loans/LoansOverview/components/BorrowLimit/BorrowLimit.tsx @@ -10,9 +10,9 @@ import { getTokenPrice } from '@/utils/helpers/prices'; import { Prices } from '@/utils/hooks/api/use-get-prices'; import { useGetLTV } from '../../hooks/use-get-ltv'; -import { isBorrowAsset } from '../../utils/is-loan-asset'; import { LTVMeter } from '../LTVMeter.tsx'; -import { StyledDd, StyledDl } from './BorrowLimit.style'; +import { StyledDd, StyledDl, StyledSpan } from './BorrowLimit.style'; +import { RemainingDebt } from './RemainingDebt'; type BorrowLimitProps = { loanAction: LoanAction; @@ -20,6 +20,7 @@ type BorrowLimitProps = { actionAmount: MonetaryAmount; prices: Prices | undefined; shouldDisplayLiquidationAlert?: boolean; + remainingDebt?: MonetaryAmount; }; const BorrowLimit = ({ @@ -27,6 +28,7 @@ const BorrowLimit = ({ asset, actionAmount, prices, + remainingDebt, shouldDisplayLiquidationAlert }: BorrowLimitProps): JSX.Element | null => { const { t } = useTranslation(); @@ -35,13 +37,18 @@ const BorrowLimit = ({ const { data: currentLTV, getLTV } = useGetLTV(); const newBorrowLimit = getBorrowLimitUSD({ type: loanAction, amount: actionAmount, asset }); - const newLTV = getLTV({ type: loanAction, amount: actionAmount, asset }); + const newLTV = getLTV({ type: loanAction, amount: actionAmount }); if (!currentLTV || !newLTV || !currentBorrowLimit || !newBorrowLimit) { return null; } - const hasLiquidationAlert = shouldDisplayLiquidationAlert && isBorrowAsset(loanAction) && newLTV.status === 'error'; + // Only show on borrow and withdraw because these could + // have negative impact on the loan + const hasLiquidationAlert = + (loanAction === 'borrow' || loanAction === 'withdraw') && + shouldDisplayLiquidationAlert && + newLTV.status === 'error'; const currentLTVLabel = formatPercentage(currentLTV.value); const newLTVLabel = formatPercentage(newLTV.value); @@ -61,16 +68,19 @@ const BorrowLimit = ({ most {displayMonetaryAmount(asset.availableCapacity)} {asset.currency.ticker}. )} + {remainingDebt && ( + + )}
Borrow Limit
{currentBorrowLimit && ( <> - {currentBorrowLimitLabel} - --> + {currentBorrowLimitLabel} + --> )} - {newBorrowLimitLabel} + {newBorrowLimitLabel}
@@ -78,11 +88,11 @@ const BorrowLimit = ({ {currentLTV && ( <> - {currentLTVLabel} - --> + {currentLTVLabel} + --> )} - {newLTVLabel} + {newLTVLabel} diff --git a/src/pages/Loans/LoansOverview/components/BorrowLimit/RemainingDebt.tsx b/src/pages/Loans/LoansOverview/components/BorrowLimit/RemainingDebt.tsx new file mode 100644 index 0000000000..55b5daddae --- /dev/null +++ b/src/pages/Loans/LoansOverview/components/BorrowLimit/RemainingDebt.tsx @@ -0,0 +1,34 @@ +import { CurrencyExt } from '@interlay/interbtc-api'; +import { MonetaryAmount } from '@interlay/monetary-js'; + +import { DlGroup, Dt, Status } from '@/component-library'; + +import { StyledDd, StyledSpan } from './BorrowLimit.style'; + +type RemainingDebtProps = { + status: Status; + remainingDebt: MonetaryAmount; + actionAmount: MonetaryAmount; +}; + +const RemainingDebt = ({ status, actionAmount, remainingDebt }: RemainingDebtProps): JSX.Element | null => { + const newRemainingDebt = actionAmount.gt(remainingDebt) ? 0 : remainingDebt?.sub(actionAmount).toHuman(); + + return ( + +
Remaining debt
+ + + {remainingDebt.toHuman()} {remainingDebt.currency.ticker} + + --> + + {newRemainingDebt} {remainingDebt.currency.ticker} + + +
+ ); +}; + +export { RemainingDebt }; +export type { RemainingDebtProps }; diff --git a/src/pages/Loans/LoansOverview/components/BorrowPositionsTable/BorrowPositionsTable.style.tsx b/src/pages/Loans/LoansOverview/components/BorrowPositionsTable/BorrowPositionsTable.style.tsx deleted file mode 100644 index 12e1c04b7c..0000000000 --- a/src/pages/Loans/LoansOverview/components/BorrowPositionsTable/BorrowPositionsTable.style.tsx +++ /dev/null @@ -1,9 +0,0 @@ -import styled from 'styled-components'; - -import { LoansBaseTable } from '../LoansBaseTable'; - -const StyledBorrowPositionsTable = styled(LoansBaseTable)` - grid-area: borrow-positions-table; -`; - -export { StyledBorrowPositionsTable }; diff --git a/src/pages/Loans/LoansOverview/components/BorrowPositionsTable/BorrowPositionsTable.tsx b/src/pages/Loans/LoansOverview/components/BorrowPositionsTable/BorrowPositionsTable.tsx deleted file mode 100644 index 7abc2e7734..0000000000 --- a/src/pages/Loans/LoansOverview/components/BorrowPositionsTable/BorrowPositionsTable.tsx +++ /dev/null @@ -1,92 +0,0 @@ -import { BorrowPosition, LoanAsset, TickerToData } from '@interlay/interbtc-api'; -import { Key, ReactNode, useMemo } from 'react'; -import { useTranslation } from 'react-i18next'; - -import { useGetPrices } from '@/utils/hooks/api/use-get-prices'; - -import { ApyCell, AssetCell, BalanceCell, LoansBaseTableProps } from '../LoansBaseTable'; -import { StyledBorrowPositionsTable } from './BorrowPositionsTable.style'; - -enum BorrowPositionColumns { - ASSET = 'asset', - APY_ACCRUED = 'apy-accrued', - BORROWED = 'borrowed' -} - -type BorrowPositionTableRow = { - id: string; - [BorrowPositionColumns.ASSET]: ReactNode; - [BorrowPositionColumns.APY_ACCRUED]: ReactNode; - [BorrowPositionColumns.BORROWED]: ReactNode; -}; - -// TODO: translations -const borrowPositionColumns = [ - { name: 'Asset', uid: BorrowPositionColumns.ASSET }, - { name: 'APY / Accrued', uid: BorrowPositionColumns.APY_ACCRUED }, - { name: 'Borrowed', uid: BorrowPositionColumns.BORROWED } -]; - -type BorrowPositionsTableProps = { - assets: TickerToData; - positions: BorrowPosition[]; - onRowAction: LoansBaseTableProps['onRowAction']; - disabledKeys: LoansBaseTableProps['disabledKeys']; -}; - -const BorrowPositionsTable = ({ - assets, - positions, - onRowAction, - disabledKeys -}: BorrowPositionsTableProps): JSX.Element | null => { - const { t } = useTranslation(); - const prices = useGetPrices(); - - const rows: BorrowPositionTableRow[] = useMemo( - () => - positions.map(({ currency, amount, accumulatedDebt }) => { - const asset = ; - - const { borrowApy, borrowReward } = assets[currency.ticker]; - - const apy = ( - onRowAction?.(currency.ticker as Key)} - /> - ); - - const borrowed = ; - - return { - id: currency.ticker, - asset, - 'apy-accrued': apy, - borrowed - }; - }), - [assets, onRowAction, positions, prices] - ); - - return ( - - ); -}; - -export { BorrowPositionsTable }; -export type { BorrowPositionsTableProps }; diff --git a/src/pages/Loans/LoansOverview/components/BorrowPositionsTable/index.tsx b/src/pages/Loans/LoansOverview/components/BorrowPositionsTable/index.tsx deleted file mode 100644 index eb933b0b23..0000000000 --- a/src/pages/Loans/LoansOverview/components/BorrowPositionsTable/index.tsx +++ /dev/null @@ -1,2 +0,0 @@ -export type { BorrowPositionsTableProps } from './BorrowPositionsTable'; -export { BorrowPositionsTable } from './BorrowPositionsTable'; diff --git a/src/pages/Loans/LoansOverview/components/CollateralModal/CollateralModal.tsx b/src/pages/Loans/LoansOverview/components/CollateralModal/CollateralModal.tsx index b0ac9ba3cc..2315a5831c 100644 --- a/src/pages/Loans/LoansOverview/components/CollateralModal/CollateralModal.tsx +++ b/src/pages/Loans/LoansOverview/components/CollateralModal/CollateralModal.tsx @@ -1,10 +1,11 @@ -import { CurrencyExt, LendPosition, LoanAsset } from '@interlay/interbtc-api'; +import { CollateralPosition, CurrencyExt, LoanAsset } from '@interlay/interbtc-api'; import { TFunction, useTranslation } from 'react-i18next'; import { useMutation } from 'react-query'; +import { toast } from 'react-toastify'; -import { CTA, Flex, Modal, ModalBody, ModalHeader, ModalProps, Status } from '@/component-library'; -import ErrorModal from '@/components/ErrorModal'; -import { useGetAccountPositions } from '@/utils/hooks/api/loans/use-get-account-positions'; +import { CTA, Flex, Modal, ModalBody, ModalFooter, ModalHeader, ModalProps, Status } from '@/component-library'; +import ErrorModal from '@/legacy-components/ErrorModal'; +import { useGetAccountLendingStatistics } from '@/utils/hooks/api/loans/use-get-account-lending-statistics'; import { useGetPrices } from '@/utils/hooks/api/use-get-prices'; import { useGetLTV } from '../../hooks/use-get-ltv'; @@ -56,7 +57,7 @@ const getModalVariant = (isCollateralActive: boolean, ltvStatus?: Status): Colla type Props = { asset?: LoanAsset; - position?: LendPosition; + position?: CollateralPosition; }; type InheritAttrs = Omit; @@ -65,11 +66,12 @@ type CollateralModalProps = Props & InheritAttrs; const CollateralModal = ({ asset, position, onClose, ...props }: CollateralModalProps): JSX.Element | null => { const { t } = useTranslation(); - const { refetch } = useGetAccountPositions(); + const { refetch } = useGetAccountLendingStatistics(); const { getLTV } = useGetLTV(); const prices = useGetPrices(); const handleSuccess = () => { + toast.success('Successfully toggled collateral'); onClose?.(); refetch(); }; @@ -85,7 +87,7 @@ const CollateralModal = ({ asset, position, onClose, ...props }: CollateralModal const { isCollateral: isCollateralActive, amount: lendPositionAmount } = position; const loanAction = isCollateralActive ? 'withdraw' : 'lend'; - const currentLTV = getLTV({ type: loanAction, amount: lendPositionAmount, asset }); + const currentLTV = getLTV({ type: loanAction, amount: lendPositionAmount }); const variant = getModalVariant(isCollateralActive, currentLTV?.status); const content = getContentMap(t, variant, asset); @@ -97,7 +99,7 @@ const CollateralModal = ({ asset, position, onClose, ...props }: CollateralModal const isEnabling = variant === 'enable'; - return toggleCollateralMutation.mutate({ isEnabling, underlyingCurrency: position.currency }); + return toggleCollateralMutation.mutate({ isEnabling, underlyingCurrency: position.amount.currency }); }; return ( @@ -109,11 +111,13 @@ const CollateralModal = ({ asset, position, onClose, ...props }: CollateralModal {content.description} {variant !== 'disable-error' && } - - {content.buttonLabel} - + + + {content.buttonLabel} + + {toggleCollateralMutation.isError && ( {label} - + ); diff --git a/src/pages/Loans/LoansOverview/components/LTVMeter.tsx/LTVMeter.style.tsx b/src/pages/Loans/LoansOverview/components/LTVMeter.tsx/LTVMeter.style.tsx index a1f925c6c1..828e0c8f43 100644 --- a/src/pages/Loans/LoansOverview/components/LTVMeter.tsx/LTVMeter.style.tsx +++ b/src/pages/Loans/LoansOverview/components/LTVMeter.tsx/LTVMeter.style.tsx @@ -1,6 +1,5 @@ import styled from 'styled-components'; -import { ReactComponent as InformationCircleIcon } from '@/assets/img/hero-icons/information-circle.svg'; import { Span, Status, theme } from '@/component-library'; type LTVLegendStatus = Exclude | 'info'; @@ -23,10 +22,5 @@ const StyledLabel = styled(Span)` font-weight: ${theme.fontWeight.semibold}; `; -const StyledIcon = styled(InformationCircleIcon)` - width: ${theme.text.s}; - color: ${theme.colors.textPrimary}; -`; - -export { StyledIcon, StyledLabel, StyledLegend }; +export { StyledLabel, StyledLegend }; export type { LTVLegendStatus }; diff --git a/src/pages/Loans/LoansOverview/components/LTVSection/LTVSection.tsx b/src/pages/Loans/LoansOverview/components/LTVSection/LTVSection.tsx index 7c0c7bbacb..00763d357c 100644 --- a/src/pages/Loans/LoansOverview/components/LTVSection/LTVSection.tsx +++ b/src/pages/Loans/LoansOverview/components/LTVSection/LTVSection.tsx @@ -1,6 +1,6 @@ import { formatUSD } from '@/common/utils/utils'; import { Card } from '@/component-library'; -import { AccountPositionsStatisticsData } from '@/utils/hooks/api/loans/use-get-account-positions'; +import { AccountLendingStatistics } from '@/utils/hooks/api/loans/use-get-account-lending-statistics'; import { Prices } from '@/utils/hooks/api/use-get-prices'; import { useGetLTV } from '../../hooks/use-get-ltv'; @@ -22,7 +22,7 @@ const status = { type LTVSectionProps = { prices?: Prices; - statistics: AccountPositionsStatisticsData | undefined; + statistics: AccountLendingStatistics | undefined; }; const LTVSection = ({ statistics }: LTVSectionProps): JSX.Element => { diff --git a/src/pages/Loans/LoansOverview/components/LendAssetsTable/LendAssetsTable.style.tsx b/src/pages/Loans/LoansOverview/components/LendAssetsTable/LendAssetsTable.style.tsx deleted file mode 100644 index ddf6e1003f..0000000000 --- a/src/pages/Loans/LoansOverview/components/LendAssetsTable/LendAssetsTable.style.tsx +++ /dev/null @@ -1,9 +0,0 @@ -import styled from 'styled-components'; - -import { LoansBaseTable } from '../LoansBaseTable'; - -const StyledLendAssetsTable = styled(LoansBaseTable)` - grid-area: lend-assets-table; -`; - -export { StyledLendAssetsTable }; diff --git a/src/pages/Loans/LoansOverview/components/LendAssetsTable/LendAssetsTable.tsx b/src/pages/Loans/LoansOverview/components/LendAssetsTable/LendAssetsTable.tsx index 21451e69bb..c9223ba7f5 100644 --- a/src/pages/Loans/LoansOverview/components/LendAssetsTable/LendAssetsTable.tsx +++ b/src/pages/Loans/LoansOverview/components/LendAssetsTable/LendAssetsTable.tsx @@ -1,15 +1,16 @@ import { LoanAsset, newMonetaryAmount, TickerToData } from '@interlay/interbtc-api'; +import { useId } from '@react-aria/utils'; import { Key, ReactNode, useMemo } from 'react'; import { useTranslation } from 'react-i18next'; import { convertMonetaryAmountToValueInUSD, formatUSD } from '@/common/utils/utils'; +import { AssetCell, BalanceCell, Cell, Table, TableProps } from '@/components'; +import { ApyCell } from '@/components/LoanPositionsTable/ApyCell'; +import { LoanTablePlaceholder } from '@/components/LoanPositionsTable/LoanTablePlaceholder'; +import { getTokenPrice } from '@/utils/helpers/prices'; import { useGetBalances } from '@/utils/hooks/api/tokens/use-get-balances'; import { useGetPrices } from '@/utils/hooks/api/use-get-prices'; -import { ApyCell, AssetCell, BalanceCell, LoansBaseTableProps } from '../LoansBaseTable'; -import { MonetaryCell } from '../LoansBaseTable/MonetaryCell'; -import { StyledLendAssetsTable } from './LendAssetsTable.style'; - enum LendAssetsColumns { ASSET = 'asset', APY = 'apy', @@ -33,13 +34,16 @@ const lendAssetsColumns = [ { name: 'Total Supplied', uid: LendAssetsColumns.TOTAL_SUPPLY } ]; -type LendAssetsTableProps = { +type Props = { assets: TickerToData; - onRowAction: LoansBaseTableProps['onRowAction']; - disabledKeys: LoansBaseTableProps['disabledKeys']; }; -const LendAssetsTable = ({ assets, onRowAction, disabledKeys }: LendAssetsTableProps): JSX.Element => { +type InheritAttrs = Omit; + +type LendAssetsTableProps = Props & InheritAttrs; + +const LendAssetsTable = ({ assets, onRowAction, ...props }: LendAssetsTableProps): JSX.Element => { + const titleId = useId(); const { t } = useTranslation(); const prices = useGetPrices(); const { data: balances } = useGetBalances(); @@ -47,7 +51,7 @@ const LendAssetsTable = ({ assets, onRowAction, disabledKeys }: LendAssetsTableP const rows: LendAssetsTableRow[] = useMemo( () => Object.values(assets).map(({ lendApy, lendReward, currency, totalLiquidity }) => { - const asset = ; + const asset = ; const apy = ( ; + const amountUSD = convertMonetaryAmountToValueInUSD(amount, getTokenPrice(prices, amount.currency.ticker)?.usd); + const wallet = ; const liquidityUSDValue = convertMonetaryAmountToValueInUSD( totalLiquidity, - prices?.[totalLiquidity.currency.ticker].usd + getTokenPrice(prices, totalLiquidity.currency.ticker)?.usd ); const liquidityLabel = liquidityUSDValue || 0; - const totalSupply = ; + const totalSupply = ; return { id: currency.ticker, @@ -82,12 +87,14 @@ const LendAssetsTable = ({ assets, onRowAction, disabledKeys }: LendAssetsTableP ); return ( - } + onRowAction={onRowAction} /> ); }; diff --git a/src/pages/Loans/LoansOverview/components/LendPositionsTable/LendPositionsTable.style.tsx b/src/pages/Loans/LoansOverview/components/LendPositionsTable/LendPositionsTable.style.tsx deleted file mode 100644 index 7e107da92a..0000000000 --- a/src/pages/Loans/LoansOverview/components/LendPositionsTable/LendPositionsTable.style.tsx +++ /dev/null @@ -1,9 +0,0 @@ -import styled from 'styled-components'; - -import { LoansBaseTable } from '../LoansBaseTable'; - -const StyledLendPositionsTable = styled(LoansBaseTable)` - grid-area: lend-positions-table; -`; - -export { StyledLendPositionsTable }; diff --git a/src/pages/Loans/LoansOverview/components/LendPositionsTable/LendPositionsTable.tsx b/src/pages/Loans/LoansOverview/components/LendPositionsTable/LendPositionsTable.tsx deleted file mode 100644 index a594fdd93d..0000000000 --- a/src/pages/Loans/LoansOverview/components/LendPositionsTable/LendPositionsTable.tsx +++ /dev/null @@ -1,106 +0,0 @@ -import { LendPosition, LoanAsset, TickerToData } from '@interlay/interbtc-api'; -import { Key, ReactNode, useMemo } from 'react'; -import { useTranslation } from 'react-i18next'; - -import { Switch } from '@/component-library'; -import { useGetPrices } from '@/utils/hooks/api/use-get-prices'; - -import { ApyCell, AssetCell, BalanceCell, LoansBaseTableProps } from '../LoansBaseTable'; -import { StyledLendPositionsTable } from './LendPositionsTable.style'; - -enum LendPositionColumns { - ASSET = 'asset', - APY_EARNED = 'apy-earned', - SUPPLIED = 'supplied', - COLLATERAL = 'collateral' -} - -type LendPositionTableRow = { - id: string; - [LendPositionColumns.ASSET]: ReactNode; - [LendPositionColumns.APY_EARNED]: ReactNode; - [LendPositionColumns.SUPPLIED]: ReactNode; - [LendPositionColumns.COLLATERAL]: ReactNode; -}; - -// TODO: translations -const lendPositionColumns = [ - { name: 'Asset', uid: LendPositionColumns.ASSET }, - { name: 'APY / Earned', uid: LendPositionColumns.APY_EARNED }, - { name: 'Supplied', uid: LendPositionColumns.SUPPLIED }, - { name: 'Collateral', uid: LendPositionColumns.COLLATERAL } -]; - -type LendPositionsTableProps = { - assets: TickerToData; - positions: LendPosition[]; - onRowAction: LoansBaseTableProps['onRowAction']; - onPressCollateralSwitch: (ticker: string) => void; - disabledKeys: LoansBaseTableProps['disabledKeys']; -}; - -const LendPositionsTable = ({ - assets, - positions, - onRowAction, - onPressCollateralSwitch, - disabledKeys -}: LendPositionsTableProps): JSX.Element | null => { - const { t } = useTranslation(); - const prices = useGetPrices(); - - const rows: LendPositionTableRow[] = useMemo( - () => - positions.map(({ amount, currency, earnedInterest, isCollateral }) => { - const asset = ; - - const { lendApy, lendReward } = assets[currency.ticker]; - - const apy = ( - onRowAction?.(currency.ticker as Key)} - /> - ); - - const supplied = ; - - const collateral = ( - onPressCollateralSwitch(currency.ticker)} - isSelected={isCollateral} - aria-label={`toggle ${currency.ticker} collateral`} - /> - ); - - return { - id: currency.ticker, - asset, - 'apy-earned': apy, - supplied, - collateral - }; - }), - [assets, onPressCollateralSwitch, onRowAction, positions, prices] - ); - - return ( - - ); -}; - -export { LendPositionsTable }; -export type { LendPositionsTableProps }; diff --git a/src/pages/Loans/LoansOverview/components/LendPositionsTable/index.tsx b/src/pages/Loans/LoansOverview/components/LendPositionsTable/index.tsx deleted file mode 100644 index 882ab0d004..0000000000 --- a/src/pages/Loans/LoansOverview/components/LendPositionsTable/index.tsx +++ /dev/null @@ -1,2 +0,0 @@ -export type { LendPositionsTableProps } from './LendPositionsTable'; -export { LendPositionsTable } from './LendPositionsTable'; diff --git a/src/pages/Loans/LoansOverview/components/LoanActionInfo/LoanActionInfo.tsx b/src/pages/Loans/LoansOverview/components/LoanActionInfo/LoanActionInfo.tsx index ede7a7e965..9fde09bd7e 100644 --- a/src/pages/Loans/LoansOverview/components/LoanActionInfo/LoanActionInfo.tsx +++ b/src/pages/Loans/LoansOverview/components/LoanActionInfo/LoanActionInfo.tsx @@ -16,29 +16,21 @@ type LoanActionInfoProps = { prices?: Prices; }; -const LoanActionInfo = ({ variant, asset, prices }: LoanActionInfoProps): JSX.Element => { - const isBorrow = variant === 'borrow'; - const apy = isBorrow ? asset?.borrowApy : asset?.lendApy; - const rewards = isBorrow ? asset?.borrowReward : asset?.lendReward; - - return ( - - {asset && apy && ( - - )} - -
Fees
-
- {displayMonetaryAmount(TRANSACTION_FEE_AMOUNT)} {TRANSACTION_FEE_AMOUNT.currency.ticker} ( - {displayMonetaryAmountInUSDFormat( - TRANSACTION_FEE_AMOUNT, - getTokenPrice(prices, TRANSACTION_FEE_AMOUNT.currency.ticker)?.usd - )} - ) -
-
-
- ); -}; +const LoanActionInfo = ({ variant, asset, prices }: LoanActionInfoProps): JSX.Element => ( + + {(variant === 'borrow' || variant === 'lend') && } + +
Fees
+
+ {displayMonetaryAmount(TRANSACTION_FEE_AMOUNT)} {TRANSACTION_FEE_AMOUNT.currency.ticker} ( + {displayMonetaryAmountInUSDFormat( + TRANSACTION_FEE_AMOUNT, + getTokenPrice(prices, TRANSACTION_FEE_AMOUNT.currency.ticker)?.usd + )} + ) +
+
+
+); export { LoanActionInfo }; export type { LoanActionInfoProps }; diff --git a/src/pages/Loans/LoansOverview/components/LoanActionInfo/LoanGroup.tsx b/src/pages/Loans/LoansOverview/components/LoanActionInfo/LoanGroup.tsx index 42280b7eba..dbd6c7db93 100644 --- a/src/pages/Loans/LoansOverview/components/LoanActionInfo/LoanGroup.tsx +++ b/src/pages/Loans/LoansOverview/components/LoanActionInfo/LoanGroup.tsx @@ -1,34 +1,42 @@ -import { CurrencyExt } from '@interlay/interbtc-api'; -import { MonetaryAmount } from '@interlay/monetary-js'; -import Big from 'big.js'; +import { LoanAsset } from '@interlay/interbtc-api'; import { Dd, DlGroup, Dt } from '@/component-library'; +import { LoanAction } from '@/types/loans'; +import { getApyLabel } from '@/utils/helpers/loans'; import { Prices } from '@/utils/hooks/api/use-get-prices'; -import { getApyLabel } from '../../utils/apy'; import { RewardsGroup } from './RewardsGroup'; type LoanGroupProps = { - apy: Big; - currency: CurrencyExt; - isBorrow: boolean; - rewards?: MonetaryAmount | null; + variant?: Extract; + asset?: LoanAsset; prices?: Prices; }; -const LoanGroup = ({ isBorrow, apy, currency, rewards, prices }: LoanGroupProps): JSX.Element => ( - <> - -
- {isBorrow ? 'Borrow' : 'Lend'} APY {currency.ticker} -
-
{getApyLabel(apy)}
-
- {!!rewards && ( - - )} - -); +const LoanGroup = ({ variant, asset, prices }: LoanGroupProps): JSX.Element | null => { + const isBorrow = variant === 'borrow'; + const apy = isBorrow ? asset?.borrowApy : asset?.lendApy; + + if (!apy || !asset) { + return null; + } + + const rewards = isBorrow ? asset?.borrowReward : asset?.lendReward; + + return ( + <> + +
+ {isBorrow ? 'Borrow' : 'Lend'} APY {asset.currency.ticker} +
+
{getApyLabel(apy)}
+
+ {!!rewards && ( + + )} + + ); +}; export { LoanGroup }; export type { LoanGroupProps }; diff --git a/src/pages/Loans/LoansOverview/components/LoanActionInfo/RewardsGroup.tsx b/src/pages/Loans/LoansOverview/components/LoanActionInfo/RewardsGroup.tsx index ec6f3b4a89..29164ddbc2 100644 --- a/src/pages/Loans/LoansOverview/components/LoanActionInfo/RewardsGroup.tsx +++ b/src/pages/Loans/LoansOverview/components/LoanActionInfo/RewardsGroup.tsx @@ -3,11 +3,9 @@ import { MonetaryAmount } from '@interlay/monetary-js'; import Big from 'big.js'; import { Dd, DlGroup, Dt } from '@/component-library'; +import { getApyLabel, getSubsidyRewardApy } from '@/utils/helpers/loans'; import { Prices } from '@/utils/hooks/api/use-get-prices'; -import { getApyLabel } from '../../utils/apy'; -import { getSubsidyRewardApy } from '../../utils/get-subsidy-rewards-apy'; - type RewardsGroupProps = { apy: Big; rewards: MonetaryAmount; diff --git a/src/pages/Loans/LoansOverview/components/LoanForm/LoanForm.style.tsx b/src/pages/Loans/LoansOverview/components/LoanForm/LoanForm.style.tsx index 6db953caa7..f789a17cd5 100644 --- a/src/pages/Loans/LoansOverview/components/LoanForm/LoanForm.style.tsx +++ b/src/pages/Loans/LoansOverview/components/LoanForm/LoanForm.style.tsx @@ -2,14 +2,8 @@ import styled from 'styled-components'; import { Flex, theme } from '@/component-library'; -type StyledFormWrapperProps = { - $showBorrowLimit: boolean; -}; - -const StyledFormWrapper = styled(Flex)` +const StyledFormWrapper = styled(Flex)` margin-top: ${theme.spacing.spacing8}; - // MEMO: keep the same height across each tab - min-height: ${({ $showBorrowLimit }) => ($showBorrowLimit ? '32rem' : '20rem')}; `; export { StyledFormWrapper }; diff --git a/src/pages/Loans/LoansOverview/components/LoanForm/LoanForm.tsx b/src/pages/Loans/LoansOverview/components/LoanForm/LoanForm.tsx index 22fc4e81d8..bd89f6b57e 100644 --- a/src/pages/Loans/LoansOverview/components/LoanForm/LoanForm.tsx +++ b/src/pages/Loans/LoansOverview/components/LoanForm/LoanForm.tsx @@ -1,22 +1,16 @@ -import { zodResolver } from '@hookform/resolvers/zod'; -import { BorrowPosition, LendPosition, LoanAsset, newMonetaryAmount } from '@interlay/interbtc-api'; -import { useState } from 'react'; -import { useForm } from 'react-hook-form'; +import { BorrowPosition, CollateralPosition, CurrencyExt, LoanAsset, newMonetaryAmount } from '@interlay/interbtc-api'; +import { MonetaryAmount } from '@interlay/monetary-js'; +import { mergeProps } from '@react-aria/utils'; +import { ChangeEventHandler, useState } from 'react'; import { TFunction, useTranslation } from 'react-i18next'; import { toast } from 'react-toastify'; -import * as z from 'zod'; - -import { convertMonetaryAmountToValueInUSD } from '@/common/utils/utils'; -import { CTA, Flex, TokenInput } from '@/component-library'; -import ErrorModal from '@/components/ErrorModal'; -import validate, { - LoanBorrowSchemaParams, - LoanLendSchemaParams, - LoanRepaySchemaParams, - LoanWithdrawSchemaParams -} from '@/lib/form-validation'; +import { useDebounce } from 'react-use'; + +import { convertMonetaryAmountToValueInUSD, newSafeMonetaryAmount } from '@/common/utils/utils'; +import { Flex, TokenInput } from '@/component-library'; +import { AuthCTA } from '@/components'; +import { isFormDisabled, LoanFormData, loanSchema, LoanValidationParams, useForm } from '@/lib/form'; import { LoanAction } from '@/types/loans'; -import { getErrorMessage, isValidForm } from '@/utils/helpers/forms'; import { useGetAccountPositions } from '@/utils/hooks/api/loans/use-get-account-positions'; import { useLoanMutation } from '@/utils/hooks/api/loans/use-loan-mutation'; import { useGetPrices } from '@/utils/hooks/api/use-get-prices'; @@ -34,90 +28,58 @@ import { StyledFormWrapper } from './LoanForm.style'; const shouldShowBorrowLimit = ( variant: LoanAction, hasCollateral: boolean, - position?: LendPosition | BorrowPosition + position?: CollateralPosition | BorrowPosition ) => { const isLendingAsset = isLendAsset(variant); const isBorrowingAsset = !isLendingAsset; - const isCollateralAsset = isLendingAsset && (position as LendPosition)?.isCollateral; + const isCollateralAsset = isLendingAsset && (position as CollateralPosition)?.isCollateral; return isCollateralAsset || (isBorrowingAsset && hasCollateral); }; -type LoanSchemaParams = LoanBorrowSchemaParams & - LoanRepaySchemaParams & - LoanLendSchemaParams & - LoanWithdrawSchemaParams; - -enum FormFields { - BORROW_AMOUNT = 'borrow-amount', - REPAY_AMOUNT = 'repay-amount', - LEND_AMOUNT = 'lend-amount', - WITHDRAW_AMOUNT = 'withdraw-amount' -} - -const getData = (t: TFunction, variant: LoanAction, params: LoanSchemaParams) => +const getData = (t: TFunction, variant: LoanAction) => ({ lend: { content: { title: t('loans.lend'), - label: 'Balance', + label: 'Available', fieldAriaLabel: t('forms.field_amount', { field: t('loans.lend').toLowerCase() }) - }, - schema: z.object({ - [FormFields.LEND_AMOUNT]: validate.loans.lend(t, params) - }), - formField: FormFields.LEND_AMOUNT + } }, withdraw: { content: { title: t('loans.withdraw'), label: 'Limit', fieldAriaLabel: t('forms.field_amount', { field: t('loans.withdraw').toLowerCase() }) - }, - schema: z.object({ - [FormFields.WITHDRAW_AMOUNT]: validate.loans.withdraw(t, params) - }), - formField: FormFields.WITHDRAW_AMOUNT + } }, borrow: { content: { title: t('loans.borrow'), label: 'Limit', fieldAriaLabel: t('forms.field_amount', { field: t('loans.borrow').toLowerCase() }) - }, - schema: z.object({ - [FormFields.BORROW_AMOUNT]: validate.loans.borrow(t, params) - }), - formField: FormFields.BORROW_AMOUNT + } }, repay: { content: { title: t('loans.repay'), - label: t('loans.borrowing'), + label: 'Balance', fieldAriaLabel: t('forms.field_amount', { field: t('loans.repay').toLowerCase() }) - }, - schema: z.object({ - [FormFields.REPAY_AMOUNT]: validate.loans.repay(t, params) - }), - formField: FormFields.REPAY_AMOUNT + } } }[variant]); -type LoanFormData = { - [FormFields.BORROW_AMOUNT]: string; - [FormFields.REPAY_AMOUNT]: string; - [FormFields.LEND_AMOUNT]: string; - [FormFields.WITHDRAW_AMOUNT]: string; -}; - type LoanFormProps = { asset: LoanAsset; variant: LoanAction; - position?: BorrowPosition | LendPosition; + position?: BorrowPosition | CollateralPosition; onChangeLoan?: () => void; }; const LoanForm = ({ asset, variant, position, onChangeLoan }: LoanFormProps): JSX.Element => { + const [inputAmount, setInputAmount] = useState(); + const [isMaxAmount, setMaxAmount] = useState(false); + const { t } = useTranslation(); const { refetch, @@ -125,43 +87,57 @@ const LoanForm = ({ asset, variant, position, onChangeLoan }: LoanFormProps): JS } = useGetAccountPositions(); const prices = useGetPrices(); const { governanceBalance, assetAmount, assetPrice, transactionFee } = useLoanFormData(variant, asset, position); - const [isMaxAmount, setMaxAmount] = useState(false); + + const { content } = getData(t, variant); + + // withdraw has `withdraw` and `withdrawAll` + // repay has `repay` and `repayAll` + // They both are considered a multi action variant + const hasMultiActionVariant = variant === 'withdraw' || variant === 'repay'; + + const handleMaxAmount = (amount: MonetaryAmount) => { + // Comparing if the provided amount is equal to the amount + // available for the action, which is only relevant for + // when the action is `withdraw` or `repay` + const isMaxAmount = variant === 'withdraw' ? !!position?.amount.eq(amount) : assetAmount.max.eq(amount); + + setMaxAmount(isMaxAmount); + }; + + useDebounce( + () => { + if (!inputAmount || !hasMultiActionVariant) return; + + const inputMonetary = newMonetaryAmount(inputAmount, asset.currency, true); + + handleMaxAmount(inputMonetary); + }, + 300, + [inputAmount] + ); const handleSuccess = () => { + toast.success(`Successful ${content.title.toLowerCase()}`); onChangeLoan?.(); refetch(); }; - const loanMutation = useLoanMutation({ onSuccess: handleSuccess }); + const handleError = (error: Error) => { + toast.error(error.message); + }; - const schemaParams: LoanSchemaParams = { + const loanMutation = useLoanMutation({ onSuccess: handleSuccess, onError: handleError }); + + const schemaParams: LoanValidationParams = { governanceBalance, transactionFee, minAmount: assetAmount.min, - maxAmount: assetAmount.max, - availableBalance: assetAmount.available + maxAmount: assetAmount.available }; - const { schema, formField, content } = getData(t, variant, schemaParams); - - const { - register, - handleSubmit: h, - watch, - formState: { errors, isDirty, isValid } - } = useForm({ - mode: 'onChange', - resolver: zodResolver(schema) - }); - - const amount = watch(formField) || 0; - const monetaryAmount = newMonetaryAmount(amount, asset.currency, true); - - const isBtnDisabled = !isValidForm(errors) || !isDirty || !isValid; - const handleSubmit = (data: LoanFormData) => { try { - const submittedAmount = data[formField]; + const submittedAmount = data[variant] || 0; const submittedMonetaryAmount = newMonetaryAmount(submittedAmount, asset.currency, true); loanMutation.mutate({ amount: submittedMonetaryAmount, loanType: variant, isMaxAmount }); } catch (err: any) { @@ -169,61 +145,66 @@ const LoanForm = ({ asset, variant, position, onChangeLoan }: LoanFormProps): JS } }; - const handleClickBalance = () => setMaxAmount(true); + const form = useForm({ + initialValues: { [variant]: '' }, + validationSchema: loanSchema(variant, schemaParams), + onSubmit: handleSubmit + }); + + const monetaryAmount = newSafeMonetaryAmount(form.values[variant] || 0, asset.currency, true); + + const isBtnDisabled = isFormDisabled(form); + + const handleClickBalance = () => { + if (!hasMultiActionVariant) return; - const handleChange = () => setMaxAmount(false); + handleMaxAmount(assetAmount.available); + }; + + const handleChange: ChangeEventHandler = (e) => { + if (!hasMultiActionVariant) return; + + setMaxAmount(false); + setInputAmount(e.target.value); + }; const showBorrowLimit = shouldShowBorrowLimit(variant, hasCollateral, position); return ( - <> - - - - + + + + {showBorrowLimit && ( + - {showBorrowLimit && ( - - )} - - - - - {content.title} - - - - - {loanMutation.isError && ( - loanMutation.reset()} - title='Error' - description={loanMutation.error?.message || ''} - /> - )} - + )} + + + + + {content.title} + + + + ); }; diff --git a/src/pages/Loans/LoansOverview/components/LoanModal/LoanModal.tsx b/src/pages/Loans/LoansOverview/components/LoanModal/LoanModal.tsx index 2d1abb0b6b..43d6ed4254 100644 --- a/src/pages/Loans/LoansOverview/components/LoanModal/LoanModal.tsx +++ b/src/pages/Loans/LoansOverview/components/LoanModal/LoanModal.tsx @@ -1,4 +1,4 @@ -import { BorrowPosition, LendPosition, LoanAsset } from '@interlay/interbtc-api'; +import { BorrowPosition, CollateralPosition, LoanAsset } from '@interlay/interbtc-api'; import { TFunction, useTranslation } from 'react-i18next'; import { Modal, ModalBody, ModalProps, TabsItem } from '@/component-library'; @@ -33,7 +33,7 @@ const getData = (t: TFunction, variant: LoanType): LoanTypeData => { type Props = { variant: LoanType; asset?: LoanAsset; - position?: LendPosition | BorrowPosition; + position?: CollateralPosition | BorrowPosition; }; type InheritAttrs = Omit; @@ -50,7 +50,7 @@ const LoanModal = ({ variant = 'lend', asset, position, onClose, ...props }: Loa const { tabs } = getData(t, variant); return ( - + {tabs.map((tab) => ( diff --git a/src/pages/Loans/LoansOverview/components/LoansBaseTable/AssetCell.tsx b/src/pages/Loans/LoansOverview/components/LoansBaseTable/AssetCell.tsx deleted file mode 100644 index 0999322f0c..0000000000 --- a/src/pages/Loans/LoansOverview/components/LoansBaseTable/AssetCell.tsx +++ /dev/null @@ -1,18 +0,0 @@ -import { CoinIcon } from '@/component-library'; - -import { StyledAssetCellWrapper, StyledCellLabel } from './LoansBaseTable.style'; - -type AssetCellProps = { - currency: string; - hasPadding?: boolean; -}; - -const AssetCell = ({ currency, hasPadding }: AssetCellProps): JSX.Element => ( - - - {currency} - -); - -export { AssetCell }; -export type { AssetCellProps }; diff --git a/src/pages/Loans/LoansOverview/components/LoansBaseTable/BalanceCell.tsx b/src/pages/Loans/LoansOverview/components/LoansBaseTable/BalanceCell.tsx deleted file mode 100644 index 62486697e7..0000000000 --- a/src/pages/Loans/LoansOverview/components/LoansBaseTable/BalanceCell.tsx +++ /dev/null @@ -1,28 +0,0 @@ -import { CurrencyExt } from '@interlay/interbtc-api'; -import { MonetaryAmount } from '@interlay/monetary-js'; - -import { displayMonetaryAmountInUSDFormat, formatNumber } from '@/common/utils/utils'; -import { AlignItems } from '@/component-library'; -import { Prices } from '@/utils/hooks/api/use-get-prices'; - -import { MonetaryCell } from './MonetaryCell'; - -type BalanceCellProps = { - amount: MonetaryAmount; - prices?: Prices; - alignItems?: AlignItems; -}; - -const BalanceCell = ({ amount, prices, alignItems }: BalanceCellProps): JSX.Element => { - const assetBalanceUSD = displayMonetaryAmountInUSDFormat(amount, prices?.[amount.currency.ticker].usd); - const assetBalance = formatNumber(amount.toBig().toNumber(), { - maximumFractionDigits: amount.currency.humanDecimals - }); - - return ( - - ); -}; - -export { BalanceCell }; -export type { BalanceCellProps }; diff --git a/src/pages/Loans/LoansOverview/components/LoansBaseTable/LoanStatusTag.tsx b/src/pages/Loans/LoansOverview/components/LoansBaseTable/LoanStatusTag.tsx deleted file mode 100644 index 5ca21af28d..0000000000 --- a/src/pages/Loans/LoansOverview/components/LoansBaseTable/LoanStatusTag.tsx +++ /dev/null @@ -1,22 +0,0 @@ -import { HTMLAttributes } from 'react'; - -import { Status } from '@/component-library'; - -import { StyledTag } from './LoansBaseTable.style'; - -type Props = { - status: Status; -}; - -type NativeAttrs = Omit, keyof Props>; - -type StatusTagProps = Props & NativeAttrs; - -const StatusTag = ({ status, children, ...props }: StatusTagProps): JSX.Element => ( - - {children} - -); - -export { StatusTag }; -export type { StatusTagProps }; diff --git a/src/pages/Loans/LoansOverview/components/LoansBaseTable/LoansBaseTable.style.tsx b/src/pages/Loans/LoansOverview/components/LoansBaseTable/LoansBaseTable.style.tsx deleted file mode 100644 index 74b78a964c..0000000000 --- a/src/pages/Loans/LoansOverview/components/LoansBaseTable/LoansBaseTable.style.tsx +++ /dev/null @@ -1,49 +0,0 @@ -import styled from 'styled-components'; - -import { Flex, H2, Span, Status, theme } from '@/component-library'; - -type StyledAssetCellWrapperProps = { - $hasPadding?: boolean; -}; - -const StyledTitle = styled(H2)` - font-size: ${theme.text.xl}; - font-weight: ${theme.fontWeight.bold}; -`; - -const StyledCellLabel = styled(Span)` - font-weight: ${theme.fontWeight.bold}; - font-size: ${theme.text.s}; - display: inline-flex; - gap: ${theme.spacing.spacing1}; - align-items: baseline; -`; - -const StyledCellTickerLabel = styled(Span)` - font-size: ${theme.text.xs}; -`; - -const StyledCellSubLabel = styled(Span)` - font-size: ${theme.text.xs}; -`; - -type StyledChipProps = { - $variant: Status; -}; - -const StyledTag = styled.div` - display: inline-flex; - font-size: ${theme.text.xs}; - padding: ${theme.spacing.spacing1} ${theme.spacing.spacing2}; - border-radius: ${theme.rounded.full}; - border: ${theme.border.default}; - border-color: ${(props) => theme.transaction.status.color[props.$variant]}; - background-color: ${(props) => theme.transaction.status.bg[props.$variant]}; -`; - -const StyledAssetCellWrapper = styled(Flex)` - // Needs a specific rem so that row size matches on both lending and borrow tables - padding: ${({ $hasPadding }) => $hasPadding && `0.5625rem 0`}; -`; - -export { StyledAssetCellWrapper, StyledCellLabel, StyledCellSubLabel, StyledCellTickerLabel, StyledTag, StyledTitle }; diff --git a/src/pages/Loans/LoansOverview/components/LoansBaseTable/index.tsx b/src/pages/Loans/LoansOverview/components/LoansBaseTable/index.tsx deleted file mode 100644 index d331777994..0000000000 --- a/src/pages/Loans/LoansOverview/components/LoansBaseTable/index.tsx +++ /dev/null @@ -1,8 +0,0 @@ -export type { ApyCellProps } from './ApyCell'; -export { ApyCell } from './ApyCell'; -export type { AssetCellProps } from './AssetCell'; -export { AssetCell } from './AssetCell'; -export type { BalanceCellProps } from './BalanceCell'; -export { BalanceCell } from './BalanceCell'; -export type { LoansBaseTableProps } from './LoansBaseTable'; -export { LoansBaseTable } from './LoansBaseTable'; diff --git a/src/pages/Loans/LoansOverview/components/LoansInsights/LoansInsights.tsx b/src/pages/Loans/LoansOverview/components/LoansInsights/LoansInsights.tsx index 97a1182c9b..290bb07717 100644 --- a/src/pages/Loans/LoansOverview/components/LoansInsights/LoansInsights.tsx +++ b/src/pages/Loans/LoansOverview/components/LoansInsights/LoansInsights.tsx @@ -1,9 +1,11 @@ +import { useTranslation } from 'react-i18next'; import { useMutation } from 'react-query'; +import { toast } from 'react-toastify'; import { formatNumber, formatPercentage, formatUSD } from '@/common/utils/utils'; import { Card, CTA, Dl, DlGroup } from '@/component-library'; -import ErrorModal from '@/components/ErrorModal'; -import { AccountPositionsStatisticsData } from '@/utils/hooks/api/loans/use-get-account-positions'; +import ErrorModal from '@/legacy-components/ErrorModal'; +import { AccountLendingStatistics } from '@/utils/hooks/api/loans/use-get-account-lending-statistics'; import { useGetAccountSubsidyRewards } from '@/utils/hooks/api/loans/use-get-account-subsidy-rewards'; import { StyledDd, StyledDt } from './LoansInsights.style'; @@ -11,13 +13,15 @@ import { StyledDd, StyledDt } from './LoansInsights.style'; const mutateClaimRewards = () => window.bridge.loans.claimAllSubsidyRewards(); type LoansInsightsProps = { - statistics?: AccountPositionsStatisticsData; + statistics?: AccountLendingStatistics; }; const LoansInsights = ({ statistics }: LoansInsightsProps): JSX.Element => { + const { t } = useTranslation(); const { data: subsidyRewards, refetch } = useGetAccountSubsidyRewards(); const handleSuccess = () => { + toast.success(t('successfully_claimed_rewards')); refetch(); }; @@ -27,10 +31,11 @@ const LoansInsights = ({ statistics }: LoansInsightsProps): JSX.Element => { const handleClickClaimRewards = () => claimRewardsMutation.mutate(); - const { supplyAmountUSD, netAmountUSD, netAPY } = statistics || {}; + const { supplyAmountUSD, netAPY } = statistics || {}; const supplyBalanceLabel = formatUSD(supplyAmountUSD?.toNumber() || 0); - const netBalanceLabel = formatUSD(netAmountUSD?.toNumber() || 0); + // TODO: temporary until squid has earned interest calculation. + // const netBalanceLabel = formatUSD(netAmountUSD?.toNumber() || 0); const netPercentage = formatPercentage(netAPY?.toNumber() || 0); const netPercentageLabel = `${netAPY?.gt(0) ? '+' : ''}${netPercentage}`; @@ -52,9 +57,8 @@ const LoansInsights = ({ statistics }: LoansInsightsProps): JSX.Element => { Net APY - - {netPercentageLabel} ({netBalanceLabel}) - + {/* {netPercentageLabel} ({netBalanceLabel}) */} + {netPercentageLabel} {hasPositions && ( - )} - + ; - positions: LendPosition[]; + positions: CollateralPosition[]; disabledAssets: string[]; hasPositions: boolean; }; @@ -44,7 +43,8 @@ const LendTables = ({ assets, positions, disabledAssets, hasPositions }: LendTab return ( <> {hasPositions && ( - )} - + ` } `; -export { StyledTablesWrapper }; +export { + StyledBorrowAssetsTable, + StyledBorrowPositionsTable, + StyledLendAssetsTable, + StyledLendPositionsTable, + StyledTablesWrapper +}; diff --git a/src/pages/Loans/LoansOverview/components/LoansTables/LoansTables.tsx b/src/pages/Loans/LoansOverview/components/LoansTables/LoansTables.tsx index 3f0beeaf7f..1f20356f09 100644 --- a/src/pages/Loans/LoansOverview/components/LoansTables/LoansTables.tsx +++ b/src/pages/Loans/LoansOverview/components/LoansTables/LoansTables.tsx @@ -1,4 +1,4 @@ -import { BorrowPosition, LendPosition, LoanAsset, TickerToData } from '@interlay/interbtc-api'; +import { BorrowPosition, CollateralPosition, LoanAsset, TickerToData } from '@interlay/interbtc-api'; import { useMemo } from 'react'; import { BorrowTables } from './BorrowTables'; @@ -11,7 +11,7 @@ const getDisabledAssetsTicker = (assets: TickerToData) => .map((activeAsset) => activeAsset.currency.ticker); type LoansTablesProps = { - lendPositions: LendPosition[]; + lendPositions: CollateralPosition[]; borrowPositions: BorrowPosition[]; assets: TickerToData; }; diff --git a/src/pages/Loans/LoansOverview/hooks/use-get-account-borrow-limit.tsx b/src/pages/Loans/LoansOverview/hooks/use-get-account-borrow-limit.tsx index 8775bb3156..1b1b672afd 100644 --- a/src/pages/Loans/LoansOverview/hooks/use-get-account-borrow-limit.tsx +++ b/src/pages/Loans/LoansOverview/hooks/use-get-account-borrow-limit.tsx @@ -3,17 +3,11 @@ import { MonetaryAmount } from '@interlay/monetary-js'; import Big from 'big.js'; import { useCallback } from 'react'; -import { convertMonetaryAmountToValueInUSD } from '@/common/utils/utils'; +import { convertMonetaryBtcToUSD } from '@/common/utils/utils'; import { LoanAction } from '@/types/loans'; -import { getTokenPrice } from '@/utils/helpers/prices'; -import { useGetAccountPositions } from '@/utils/hooks/api/loans/use-get-account-positions'; +import { useGetAccountLendingStatistics } from '@/utils/hooks/api/loans/use-get-account-lending-statistics'; import { useGetPrices } from '@/utils/hooks/api/use-get-prices'; -import { calculateBorrowedAmountUSD, calculateCollateralAmountUSD, calculateThresholdAmountUSD } from '../utils/math'; - -const calculateBorrowLimitUSD = (borrowAmountUSD: Big, collateralAmountUSD: Big): Big => - collateralAmountUSD.sub(borrowAmountUSD); - type LoanActionData = { type: LoanAction; amount: MonetaryAmount; asset: LoanAsset }; interface UseAccountBorrowLimit { @@ -23,10 +17,7 @@ interface UseAccountBorrowLimit { const useAccountBorrowLimit = (): UseAccountBorrowLimit => { const prices = useGetPrices(); - const { - data: { statistics } - } = useGetAccountPositions(); - const { borrowAmountUSD, collateralAmountUSD } = statistics || {}; + const { data: statistics } = useGetAccountLendingStatistics(); /** * This method computes how the borrow limit will change if @@ -36,32 +27,20 @@ const useAccountBorrowLimit = (): UseAccountBorrowLimit => { * @returns New borrow limit in USD after the transaction is done. */ const getBorrowLimitUSD = useCallback( - ({ type, amount, asset }: LoanActionData): Big | undefined => { - if (prices === undefined || borrowAmountUSD === undefined || collateralAmountUSD === undefined) { + ({ type, amount }: LoanActionData): Big | undefined => { + if (prices === undefined || statistics === undefined) { return undefined; } - const { currency, collateralThreshold } = asset; - - const currencyPrice = getTokenPrice(prices, currency.ticker)?.usd; - const actionAmountUSD = Big(convertMonetaryAmountToValueInUSD(amount, currencyPrice) || 0); - - const newTotalBorrowedAmountUSD = calculateBorrowedAmountUSD(type, borrowAmountUSD, actionAmountUSD); - - const collateralThresholdAmountUSD = calculateThresholdAmountUSD(actionAmountUSD, collateralThreshold); - const newCollateralAssetsUSD = calculateCollateralAmountUSD( - type, - collateralAmountUSD, - collateralThresholdAmountUSD - ); - return calculateBorrowLimitUSD(newTotalBorrowedAmountUSD, newCollateralAssetsUSD); + const newBorrowLimitBtc = statistics.calculateBorrowLimitBtcChange(type, amount); + return convertMonetaryBtcToUSD(newBorrowLimitBtc, prices); }, - [borrowAmountUSD, collateralAmountUSD, prices] + [prices, statistics] ); const data = - borrowAmountUSD !== undefined && collateralAmountUSD !== undefined - ? calculateBorrowLimitUSD(borrowAmountUSD, collateralAmountUSD) + prices !== undefined && statistics !== undefined + ? convertMonetaryBtcToUSD(statistics.borrowLimitBtc, prices) : undefined; return { diff --git a/src/pages/Loans/LoansOverview/hooks/use-get-ltv.tsx b/src/pages/Loans/LoansOverview/hooks/use-get-ltv.tsx index 6461dc30c6..585cf3da44 100644 --- a/src/pages/Loans/LoansOverview/hooks/use-get-ltv.tsx +++ b/src/pages/Loans/LoansOverview/hooks/use-get-ltv.tsx @@ -1,16 +1,12 @@ -import { CurrencyExt, LoanAsset } from '@interlay/interbtc-api'; +import { CurrencyExt } from '@interlay/interbtc-api'; import { MonetaryAmount } from '@interlay/monetary-js'; import Big from 'big.js'; import { useCallback } from 'react'; -import { convertMonetaryAmountToValueInUSD, formatNumber } from '@/common/utils/utils'; import { MeterRanges, Status } from '@/component-library'; import { LoanAction } from '@/types/loans'; -import { getTokenPrice } from '@/utils/helpers/prices'; -import { PositionsThresholdsData, useGetAccountPositions } from '@/utils/hooks/api/loans/use-get-account-positions'; -import { useGetPrices } from '@/utils/hooks/api/use-get-prices'; - -import { calculateBorrowedAmountUSD, calculateCollateralAmountUSD, calculateThresholdAmountUSD } from '../utils/math'; +import { useGetAccountLendingStatistics } from '@/utils/hooks/api/loans/use-get-account-lending-statistics'; +import { PositionsThresholdsData } from '@/utils/hooks/api/loans/use-get-account-positions'; type LTVData = { value: number; @@ -18,18 +14,11 @@ type LTVData = { status: Status; }; -type GetDataParams = { - borrowAmountUSD: Big; - collateralAmountUSD: Big; - collateralizedAmountUSD: Big; - liquidationAmountUSD: Big; -}; - const getRanges = (thresholds: PositionsThresholdsData): MeterRanges => { - const collateral = formatNumber(thresholds.collateral.toNumber(), { maximumFractionDigits: 2 }); - const liquidation = formatNumber(thresholds.liquidation.toNumber(), { maximumFractionDigits: 2 }); + const collateral = thresholds.collateral.round(2).toNumber(); + const liquidation = thresholds.liquidation.round(2).toNumber(); - return [0, Number(collateral), Number(liquidation), 100]; + return [0, collateral, liquidation, 100]; }; const getStatus = (value: Big, thresholds: PositionsThresholdsData): Status => { @@ -44,17 +33,20 @@ const getStatus = (value: Big, thresholds: PositionsThresholdsData): Status => { return 'success'; }; -const getData = (data: GetDataParams): LTVData => { - const { borrowAmountUSD, collateralAmountUSD, collateralizedAmountUSD, liquidationAmountUSD } = data; - - // if collateral is 0: +const getData = ( + ltv: Big, + borrowedAmount: MonetaryAmount, + collateralThresholdWeightedAverage: Big, + liquidationThresholdWeightedAverage: Big +): LTVData => { + // if ltv is 0 // 1. and there are still assets being // borrowed then status is error, meaning that the user // should not be able to apply action // 2. and there are no assets being borrowed then // the user should successfuly apply action - if (!collateralAmountUSD.gt(0) || !liquidationAmountUSD.gte(0) || !collateralizedAmountUSD.gt(0)) { - const hasBorrowedAssets = borrowAmountUSD.gt(0); + if (collateralThresholdWeightedAverage.eq(0)) { + const hasBorrowedAssets = borrowedAmount.toBig().gt(0); return { status: hasBorrowedAssets ? 'error' : 'success', @@ -63,21 +55,21 @@ const getData = (data: GetDataParams): LTVData => { }; } - const value = borrowAmountUSD.div(collateralizedAmountUSD).mul(100); + const ltvPercentage = ltv.mul(100); const thresholds = { - collateral: collateralAmountUSD.div(collateralizedAmountUSD).mul(100), - liquidation: liquidationAmountUSD.div(collateralizedAmountUSD).mul(100) + collateral: collateralThresholdWeightedAverage.mul(100), + liquidation: liquidationThresholdWeightedAverage.mul(100) }; return { - value: value.toNumber(), - status: getStatus(value, thresholds), + value: ltvPercentage.toNumber(), + status: getStatus(ltvPercentage, thresholds), ranges: getRanges(thresholds) }; }; -type LoanActionData = { type: LoanAction; amount: MonetaryAmount; asset: LoanAsset }; +type LoanActionData = { type: LoanAction; amount: MonetaryAmount }; interface UserGetLTV { data: LTVData | undefined; @@ -85,11 +77,7 @@ interface UserGetLTV { } const useGetLTV = (): UserGetLTV => { - const prices = useGetPrices(); - const { - data: { statistics } - } = useGetAccountPositions(); - const { borrowAmountUSD, collateralizedAmountUSD, collateralAmountUSD, liquidationAmountUSD } = statistics || {}; + const { data: statistics } = useGetAccountLendingStatistics(); /** * This method computes how the LTV will @@ -99,44 +87,35 @@ const useGetLTV = (): UserGetLTV => { * @returns {number} Health Factor after the transaction is done. */ const getLTV = useCallback( - ({ type, amount, asset }: LoanActionData): LTVData | undefined => { - if ( - prices === undefined || - borrowAmountUSD === undefined || - collateralizedAmountUSD === undefined || - collateralAmountUSD === undefined || - liquidationAmountUSD === undefined - ) { + ({ type, amount }: LoanActionData): LTVData | undefined => { + if (statistics === undefined) { return undefined; } - const { currency, collateralThreshold, liquidationThreshold } = asset; - - const currencyPrice = getTokenPrice(prices, currency.ticker)?.usd; - const actionAmountUSD = Big(convertMonetaryAmountToValueInUSD(amount, currencyPrice) || 0); - - const collateralThresholdAmountUSD = calculateThresholdAmountUSD(actionAmountUSD, collateralThreshold); - const liquidationThresholdAmountUSD = calculateThresholdAmountUSD(actionAmountUSD, liquidationThreshold); - - const data = { - borrowAmountUSD: calculateBorrowedAmountUSD(type, borrowAmountUSD, actionAmountUSD), - collateralAmountUSD: calculateCollateralAmountUSD(type, collateralAmountUSD, collateralThresholdAmountUSD), - collateralizedAmountUSD: calculateCollateralAmountUSD(type, collateralizedAmountUSD, actionAmountUSD), - liquidationAmountUSD: calculateCollateralAmountUSD(type, liquidationAmountUSD, liquidationThresholdAmountUSD) - }; - - return getData(data); + const { + ltv, + collateralThresholdWeightedAverage, + liquidationThresholdWeightedAverage + } = statistics.calculateLtvAndThresholdsChange(type, amount); + + return getData( + ltv, + statistics.totalBorrowedBtc, + collateralThresholdWeightedAverage, + liquidationThresholdWeightedAverage + ); }, - [prices, borrowAmountUSD, collateralizedAmountUSD, collateralAmountUSD, liquidationAmountUSD] + [statistics] ); - const data = - borrowAmountUSD !== undefined && - collateralAmountUSD !== undefined && - collateralizedAmountUSD !== undefined && - liquidationAmountUSD !== undefined - ? getData({ borrowAmountUSD, collateralAmountUSD, collateralizedAmountUSD, liquidationAmountUSD }) - : undefined; + const data = statistics + ? getData( + statistics.ltv, + statistics.totalBorrowedBtc, + statistics.collateralThresholdWeightedAverage, + statistics.liquidationThresholdWeightedAverage + ) + : undefined; return { data, diff --git a/src/pages/Loans/LoansOverview/hooks/use-loan-form-data.tsx b/src/pages/Loans/LoansOverview/hooks/use-loan-form-data.tsx index c8a0e91cd8..8db2cf8e30 100644 --- a/src/pages/Loans/LoansOverview/hooks/use-loan-form-data.tsx +++ b/src/pages/Loans/LoansOverview/hooks/use-loan-form-data.tsx @@ -1,51 +1,49 @@ -import { BorrowPosition, CurrencyExt, LendPosition, LoanAsset, newMonetaryAmount } from '@interlay/interbtc-api'; +import { + BorrowPosition, + CollateralPosition, + CurrencyExt, + LendingStats, + LoanAsset, + newMonetaryAmount +} from '@interlay/interbtc-api'; import { MonetaryAmount } from '@interlay/monetary-js'; -import Big from 'big.js'; import { GOVERNANCE_TOKEN, TRANSACTION_FEE_AMOUNT } from '@/config/relay-chains'; import { BorrowAction, LendAction, LoanAction } from '@/types/loans'; import { getTokenPrice } from '@/utils/helpers/prices'; -import { useGetAccountPositions } from '@/utils/hooks/api/loans/use-get-account-positions'; +import { useGetAccountLendingStatistics } from '@/utils/hooks/api/loans/use-get-account-lending-statistics'; import { useGetBalances } from '@/utils/hooks/api/tokens/use-get-balances'; import { useGetPrices } from '@/utils/hooks/api/use-get-prices'; import { getMaxBorrowableAmount } from '../utils/get-max-borrowable-amount'; +import { getMaxLendableAmount } from '../utils/get-max-lendable-amount'; import { getMaxWithdrawableAmount } from '../utils/get-max-withdrawable-amount'; type GetMaxAmountParams = { loanAction: LoanAction; asset: LoanAsset; - assetPrice?: number; assetBalance?: MonetaryAmount; - position?: LendPosition | BorrowPosition; - totalBorrowedAmountUSD?: Big; - totalCollateralAmountUSD?: Big; + position?: CollateralPosition | BorrowPosition; + lendingStats?: LendingStats; }; -const getMaxAmount = ({ +const getMaxCalculatedAmount = ({ loanAction, asset, - assetPrice, - assetBalance, position, - totalBorrowedAmountUSD, - totalCollateralAmountUSD -}: GetMaxAmountParams): MonetaryAmount | undefined => { + lendingStats +}: GetMaxAmountParams): MonetaryAmount => { switch (loanAction) { - case 'borrow': - return getMaxBorrowableAmount(asset, assetPrice, totalBorrowedAmountUSD, totalCollateralAmountUSD); - case 'withdraw': - return getMaxWithdrawableAmount( - asset, - assetPrice, - position as LendPosition, - totalBorrowedAmountUSD, - totalCollateralAmountUSD - ); case 'lend': - return assetBalance; + return getMaxLendableAmount(asset); + case 'withdraw': + return getMaxWithdrawableAmount(asset, position as CollateralPosition, lendingStats); + case 'borrow': + return getMaxBorrowableAmount(asset, lendingStats); case 'repay': - return position?.amount.add((position as BorrowPosition).accumulatedDebt || 0); + return position + ? position.amount.add((position as BorrowPosition).accumulatedDebt) + : newMonetaryAmount(0, asset.currency); } }; @@ -64,14 +62,11 @@ type UseLoanFormData = { const useLoanFormData = ( loanAction: BorrowAction | LendAction, asset: LoanAsset, - position?: LendPosition | BorrowPosition + position?: CollateralPosition | BorrowPosition ): UseLoanFormData => { const { getBalance, getAvailableBalance } = useGetBalances(); const prices = useGetPrices(); - const { - data: { statistics } - } = useGetAccountPositions(); - const { borrowAmountUSD, collateralAmountUSD } = statistics || {}; + const { data: statistics } = useGetAccountLendingStatistics(); const zeroAssetAmount = newMonetaryAmount(0, asset.currency); @@ -80,28 +75,32 @@ const useLoanFormData = ( const assetBalance = getAvailableBalance(asset.currency.ticker) || zeroAssetAmount; const assetPrice = getTokenPrice(prices, asset.currency.ticker)?.usd || 0; + const minAmount = newMonetaryAmount(1, assetBalance.currency); + const maxAmountParams: GetMaxAmountParams = { loanAction, asset, - assetPrice, assetBalance, position, - totalBorrowedAmountUSD: borrowAmountUSD, - totalCollateralAmountUSD: collateralAmountUSD + lendingStats: statistics }; + const maxAmountData = getMaxCalculatedAmount(maxAmountParams); - const maxAmount = getMaxAmount(maxAmountParams) || zeroAssetAmount; - const minAmount = newMonetaryAmount(1, assetBalance.currency); + const available = + loanAction === 'lend' || loanAction === 'repay' + ? maxAmountData.gt(assetBalance) + ? assetBalance + : maxAmountData + : maxAmountData; return { governanceBalance, transactionFee, assetPrice, assetAmount: { - available: assetBalance, + available, min: minAmount, - // MEMO: checks for negative values - max: maxAmount.gte(zeroAssetAmount) ? maxAmount : zeroAssetAmount + max: maxAmountData } }; }; diff --git a/src/pages/Loans/LoansOverview/utils/apy.ts b/src/pages/Loans/LoansOverview/utils/apy.ts index 8200b624e5..dd990dba84 100644 --- a/src/pages/Loans/LoansOverview/utils/apy.ts +++ b/src/pages/Loans/LoansOverview/utils/apy.ts @@ -4,11 +4,21 @@ import { formatPercentage } from '@/common/utils/utils'; const MIN_DECIMAL_NUMBER = 0.01; +const isTinyApy = (isPositive: boolean, apy: Big) => + isPositive ? apy.lt(MIN_DECIMAL_NUMBER) : apy.gt(-MIN_DECIMAL_NUMBER); + // MEMO: returns formatted apy or better representation of a very small apy const getApyLabel = (apy: Big): string => { - const isTinyApy = apy.lt(MIN_DECIMAL_NUMBER) && apy.gt(0); + const isPositive = apy.gt(0); + + if (!apy.eq(0) && isTinyApy(isPositive, apy)) { + const tinyIndicator = apy.gt(0) ? '<' : '>'; + const minDecimal = isPositive ? MIN_DECIMAL_NUMBER : -MIN_DECIMAL_NUMBER; + + return `${tinyIndicator}${minDecimal}%`; + } - return isTinyApy ? `<${MIN_DECIMAL_NUMBER}%` : formatPercentage(apy.toNumber()); + return formatPercentage(apy.toNumber()); }; export { getApyLabel }; diff --git a/src/pages/Loans/LoansOverview/utils/get-max-borrowable-amount.tsx b/src/pages/Loans/LoansOverview/utils/get-max-borrowable-amount.tsx index e7d9fcbeb1..5a8b3099c0 100644 --- a/src/pages/Loans/LoansOverview/utils/get-max-borrowable-amount.tsx +++ b/src/pages/Loans/LoansOverview/utils/get-max-borrowable-amount.tsx @@ -1,36 +1,33 @@ -import { CurrencyExt, LoanAsset, newMonetaryAmount } from '@interlay/interbtc-api'; +import { CurrencyExt, LendingStats, LoanAsset, newMonetaryAmount } from '@interlay/interbtc-api'; import { MonetaryAmount } from '@interlay/monetary-js'; -import Big from 'big.js'; + +import { pickSmallerAmount } from '@/utils/helpers/currencies'; /** * Get maximum amount of currency that user can borrow * with currently provided collateral and liquidity. - * @param {LoanAsset} asset The asset to be withdrew. - * @param {number} assetPrice The asset price. - * @param {Big} totalBorrowedAmountUSD Total borrowed amount in usd. - * @param {Big} totalCollateralAmountUSD Total provided collateral amount in usd. - * @return {MonetaryAmount | undefined} maximum amount of currency that - * user can withdraw with currently provided collateral. Returns undefined if it is loading + * @param {LoanAsset} asset The asset to be withdrawn.s + * @param {LendingStats} lendingStats Object containing information about account's collateralization. + * @return {MonetaryAmount} maximum amount of currency that + * user can withdraw with currently provided collateral */ -const getMaxBorrowableAmount = ( - asset: LoanAsset, - assetPrice?: number, - totalBorrowedAmountUSD?: Big, - totalCollateralAmountUSD?: Big -): MonetaryAmount | undefined => { - if (assetPrice === undefined || totalCollateralAmountUSD === undefined || totalBorrowedAmountUSD === undefined) { +const getMaxBorrowableAmount = (asset: LoanAsset, lendingStats?: LendingStats): MonetaryAmount => { + if (lendingStats === undefined) { return newMonetaryAmount(0, asset.currency); } + const { exchangeRate } = asset; - const { availableCapacity, currency } = asset; + const { availableCapacity, currency, borrowCap, totalBorrows } = asset; - const availableCollateralUSDValue = totalCollateralAmountUSD.sub(totalBorrowedAmountUSD); - const maxBorrowableCurrencyAmount = availableCollateralUSDValue.div(assetPrice); + const maxBorrowableCurrencyAmount = exchangeRate.toCounter(lendingStats.borrowLimitBtc); + const protocolLimitAmount = pickSmallerAmount(availableCapacity, borrowCap.sub(totalBorrows)); + const maxBorrowableAmount = pickSmallerAmount(protocolLimitAmount, maxBorrowableCurrencyAmount); - const maxBorrowableAmountByCollateral = - new MonetaryAmount(currency, maxBorrowableCurrencyAmount) || new MonetaryAmount(currency, 0); + if (maxBorrowableAmount.toBig().lte(0)) { + return newMonetaryAmount(0, currency); + } - return availableCapacity.gt(maxBorrowableAmountByCollateral) ? maxBorrowableAmountByCollateral : availableCapacity; + return maxBorrowableAmount; }; export { getMaxBorrowableAmount }; diff --git a/src/pages/Loans/LoansOverview/utils/get-max-lendable-amount.ts b/src/pages/Loans/LoansOverview/utils/get-max-lendable-amount.ts new file mode 100644 index 0000000000..b9ffb5b395 --- /dev/null +++ b/src/pages/Loans/LoansOverview/utils/get-max-lendable-amount.ts @@ -0,0 +1,15 @@ +import { CurrencyExt, LoanAsset, newMonetaryAmount } from '@interlay/interbtc-api'; +import { MonetaryAmount } from '@interlay/monetary-js'; + +const getMaxLendableAmount = (asset: LoanAsset): MonetaryAmount => { + const { totalLiquidity, supplyCap, currency, totalBorrows } = asset; + const amountInProtocol = totalLiquidity.sub(totalBorrows); + const maximumAmountToSupply = supplyCap.sub(amountInProtocol); + + if (maximumAmountToSupply.toBig().lte(0)) { + return newMonetaryAmount(0, currency); + } + return maximumAmountToSupply; +}; + +export { getMaxLendableAmount }; diff --git a/src/pages/Loans/LoansOverview/utils/get-max-withdrawable-amount.tsx b/src/pages/Loans/LoansOverview/utils/get-max-withdrawable-amount.tsx index 909cc40302..cc94294016 100644 --- a/src/pages/Loans/LoansOverview/utils/get-max-withdrawable-amount.tsx +++ b/src/pages/Loans/LoansOverview/utils/get-max-withdrawable-amount.tsx @@ -1,52 +1,42 @@ -import { CurrencyExt, LendPosition, LoanAsset, newMonetaryAmount } from '@interlay/interbtc-api'; +import { CollateralPosition, CurrencyExt, LendingStats, LoanAsset, newMonetaryAmount } from '@interlay/interbtc-api'; import { MonetaryAmount } from '@interlay/monetary-js'; -import Big from 'big.js'; /** * Get maximum amount of currency that user can withdraw * with currently provided collateral and liquidity. - * @param {LoanAsset} asset The asset to be withdrew. - * @param {number} assetPrice The asset price. - * @param {LendPosition} position The position to be withdrew. - * @param {Big} totalBorrowedAmountUSD Total borrowed amount in usd. - * @param {Big} totalCollateralAmountUSD Total provided collateral amount in usd. + * @param {LoanAsset} asset The asset to be withdrawn. + * @param {CollateralPosition} position The position to be withdrew. + * @param {LendingStats} lendingStats Object containing information about account's collateralization. * @return {MonetaryAmount | undefined} maximum amount of currency that - * user can withdraw with currently provided collateral. Returns undefined if it is loading + * user can withdraw with currently provided collateral */ const getMaxWithdrawableAmount = ( asset: LoanAsset, - assetPrice?: number, - position?: LendPosition, - totalBorrowedAmountUSD?: Big, - totalCollateralAmountUSD?: Big -): MonetaryAmount | undefined => { + position?: CollateralPosition, + lendingStats?: LendingStats +): MonetaryAmount => { const { currency } = asset; - if ( - assetPrice === undefined || - position === undefined || - totalCollateralAmountUSD === undefined || - totalBorrowedAmountUSD === undefined - ) { + if (position === undefined || lendingStats === undefined) { return newMonetaryAmount(0, currency); } const { amount, isCollateral } = position; - const { collateralThreshold } = asset; + const { collateralThreshold, exchangeRate } = asset; + const { borrowLimitBtc } = lendingStats; if (!isCollateral) { return amount; } - const positionAmountUSD = amount.toBig().mul(assetPrice); - const positionCollateralValueUSD = positionAmountUSD.mul(collateralThreshold); - const borrowLimit = totalCollateralAmountUSD.sub(totalBorrowedAmountUSD); + const positionAmountBtc = exchangeRate.toBase(amount); + const positionCollateralValueBtc = positionAmountBtc.mul(collateralThreshold); - if (positionCollateralValueUSD.lt(borrowLimit)) { + if (positionCollateralValueBtc.lt(borrowLimitBtc)) { return amount; } - const maxWithdrawable = borrowLimit.div(assetPrice).div(collateralThreshold); + const maxWithdrawable = exchangeRate.toCounter(borrowLimitBtc).div(collateralThreshold).toBig(); return newMonetaryAmount(maxWithdrawable, currency, true); }; diff --git a/src/pages/Loans/LoansOverview/utils/get-position.ts b/src/pages/Loans/LoansOverview/utils/get-position.ts index 116441944c..12bd03e521 100644 --- a/src/pages/Loans/LoansOverview/utils/get-position.ts +++ b/src/pages/Loans/LoansOverview/utils/get-position.ts @@ -1,6 +1,6 @@ -import { BorrowPosition, LendPosition } from '@interlay/interbtc-api'; +import { BorrowPosition, CollateralPosition } from '@interlay/interbtc-api'; -const getPosition = (positions: T[], ticker: string): T | undefined => - positions.find((position) => position.currency.ticker === ticker); +const getPosition = (positions: T[], ticker: string): T | undefined => + positions.find((position) => position.amount.currency.ticker === ticker); export { getPosition }; diff --git a/src/pages/Loans/LoansOverview/utils/math.ts b/src/pages/Loans/LoansOverview/utils/math.ts deleted file mode 100644 index 522f5a4bb7..0000000000 --- a/src/pages/Loans/LoansOverview/utils/math.ts +++ /dev/null @@ -1,37 +0,0 @@ -import Big from 'big.js'; - -import { LoanAction } from '@/types/loans'; - -const calculateThresholdAmountUSD = (amountUSD: Big, threshold: Big): Big => amountUSD.mul(threshold); - -const calculateBorrowedAmountUSD = ( - loanAction: LoanAction, - currentBorrowedAmountUSD: Big, - actionAmountUSD: Big -): Big => { - switch (loanAction) { - case 'borrow': - return currentBorrowedAmountUSD.add(actionAmountUSD); - case 'repay': - return currentBorrowedAmountUSD.sub(actionAmountUSD); - default: - return currentBorrowedAmountUSD; - } -}; - -const calculateCollateralAmountUSD = ( - loanAction: LoanAction, - currentCollateralAmountUSD: Big, - actionAmountUSD: Big -): Big => { - switch (loanAction) { - case 'lend': - return currentCollateralAmountUSD.add(actionAmountUSD); - case 'withdraw': - return currentCollateralAmountUSD.sub(actionAmountUSD); - default: - return currentCollateralAmountUSD; - } -}; - -export { calculateBorrowedAmountUSD, calculateCollateralAmountUSD, calculateThresholdAmountUSD }; diff --git a/src/pages/Staking/BalancesUI/index.tsx b/src/pages/Staking/BalancesUI/index.tsx index c36806e623..977e951896 100644 --- a/src/pages/Staking/BalancesUI/index.tsx +++ b/src/pages/Staking/BalancesUI/index.tsx @@ -1,8 +1,8 @@ import clsx from 'clsx'; import { useTranslation } from 'react-i18next'; -import InformationTooltip from '@/components/tooltips/InformationTooltip'; import { GOVERNANCE_TOKEN_SYMBOL, VOTE_GOVERNANCE_TOKEN_SYMBOL } from '@/config/relay-chains'; +import InformationTooltip from '@/legacy-components/tooltips/InformationTooltip'; import { KUSAMA, POLKADOT } from '@/utils/constants/relay-chain-names'; const Label = ({ className, ...rest }: React.ComponentPropsWithRef<'span'>) => ( diff --git a/src/pages/Staking/ClaimRewardsButton/index.tsx b/src/pages/Staking/ClaimRewardsButton/index.tsx index 58db1b0617..f3f3375483 100644 --- a/src/pages/Staking/ClaimRewardsButton/index.tsx +++ b/src/pages/Staking/ClaimRewardsButton/index.tsx @@ -1,11 +1,11 @@ import clsx from 'clsx'; import { useMutation, useQueryClient } from 'react-query'; +import { GOVERNANCE_TOKEN_SYMBOL } from '@/config/relay-chains'; import InterlayDenimOrKintsugiSupernovaContainedButton, { Props as InterlayDenimOrKintsugiMidnightContainedButtonProps -} from '@/components/buttons/InterlayDenimOrKintsugiSupernovaContainedButton'; -import ErrorModal from '@/components/ErrorModal'; -import { GOVERNANCE_TOKEN_SYMBOL } from '@/config/relay-chains'; +} from '@/legacy-components/buttons/InterlayDenimOrKintsugiSupernovaContainedButton'; +import ErrorModal from '@/legacy-components/ErrorModal'; import { useSubstrateSecureState } from '@/lib/substrate'; import { GENERIC_FETCHER } from '@/services/fetchers/generic-fetcher'; diff --git a/src/pages/Staking/InformationUI/index.tsx b/src/pages/Staking/InformationUI/index.tsx index e18dd0116f..f9e081eaa8 100644 --- a/src/pages/Staking/InformationUI/index.tsx +++ b/src/pages/Staking/InformationUI/index.tsx @@ -1,6 +1,6 @@ import clsx from 'clsx'; -import InformationTooltip from '@/components/tooltips/InformationTooltip'; +import InformationTooltip from '@/legacy-components/tooltips/InformationTooltip'; import { KUSAMA, POLKADOT } from '@/utils/constants/relay-chain-names'; interface CustomProps { diff --git a/src/pages/Staking/LockTimeField/index.tsx b/src/pages/Staking/LockTimeField/index.tsx index 6f3566c713..1e55b5f43c 100644 --- a/src/pages/Staking/LockTimeField/index.tsx +++ b/src/pages/Staking/LockTimeField/index.tsx @@ -1,9 +1,9 @@ import clsx from 'clsx'; import * as React from 'react'; -import NumberInput, { Props as NumberInputProps } from '@/components/NumberInput'; -import { TextFieldHelperText, TextFieldLabel } from '@/components/TextField'; import { STAKE_LOCK_TIME } from '@/config/relay-chains'; +import NumberInput, { Props as NumberInputProps } from '@/legacy-components/NumberInput'; +import { TextFieldHelperText, TextFieldLabel } from '@/legacy-components/TextField'; import { KUSAMA, POLKADOT } from '@/utils/constants/relay-chain-names'; // MEMO: inspired by https://medium.com/codex/making-html-5-numeric-inputs-only-accept-integers-d3d117973d56 @@ -50,7 +50,7 @@ const LockTimeField = React.forwardRef( className={clsx('!text-xs', LABEL_TEXT_COLOR_CLASSES)} required={optional === false} > - Max {STAKE_LOCK_TIME.MAX} Weeks + Total {STAKE_LOCK_TIME.MAX} Weeks
{optional === true && ( @@ -65,7 +65,7 @@ const LockTimeField = React.forwardRef( { return ( <> - {process.env.REACT_APP_RELAY_CHAIN_NAME === KUSAMA && ( - -

Block times are currently higher than expected. Lock times may be longer than expected.

-
- )}
diff --git a/src/pages/TX/IssueTX/index.tsx b/src/pages/TX/IssueTX/index.tsx index 88025a3c73..143e055707 100644 --- a/src/pages/TX/IssueTX/index.tsx +++ b/src/pages/TX/IssueTX/index.tsx @@ -2,9 +2,9 @@ import { useErrorHandler, withErrorBoundary } from 'react-error-boundary'; import { useQuery } from 'react-query'; import { useParams } from 'react-router-dom'; -import ErrorFallback from '@/components/ErrorFallback'; -import IssueUI from '@/components/IssueUI'; -import PrimaryColorEllipsisLoader from '@/components/PrimaryColorEllipsisLoader'; +import ErrorFallback from '@/legacy-components/ErrorFallback'; +import IssueUI from '@/legacy-components/IssueUI'; +import PrimaryColorEllipsisLoader from '@/legacy-components/PrimaryColorEllipsisLoader'; import issuesFetcher, { getIssueWithStatus, ISSUES_FETCHER } from '@/services/fetchers/issues-fetcher'; import useCurrentActiveBlockNumber from '@/services/hooks/use-current-active-block-number'; import useStableBitcoinConfirmations from '@/services/hooks/use-stable-bitcoin-confirmations'; diff --git a/src/pages/TX/RedeemTX/index.tsx b/src/pages/TX/RedeemTX/index.tsx index 85b4aa19ec..a05a4ec3a9 100644 --- a/src/pages/TX/RedeemTX/index.tsx +++ b/src/pages/TX/RedeemTX/index.tsx @@ -2,9 +2,9 @@ import { useErrorHandler, withErrorBoundary } from 'react-error-boundary'; import { useQuery } from 'react-query'; import { useParams } from 'react-router-dom'; -import ErrorFallback from '@/components/ErrorFallback'; -import PrimaryColorEllipsisLoader from '@/components/PrimaryColorEllipsisLoader'; -import RedeemUI from '@/components/RedeemUI'; +import ErrorFallback from '@/legacy-components/ErrorFallback'; +import PrimaryColorEllipsisLoader from '@/legacy-components/PrimaryColorEllipsisLoader'; +import RedeemUI from '@/legacy-components/RedeemUI'; import redeemsFetcher, { getRedeemWithStatus, REDEEMS_FETCHER } from '@/services/fetchers/redeems-fetcher'; import useCurrentActiveBlockNumber from '@/services/hooks/use-current-active-block-number'; import useStableBitcoinConfirmations from '@/services/hooks/use-stable-bitcoin-confirmations'; diff --git a/src/pages/TX/ReplaceTX/index.tsx b/src/pages/TX/ReplaceTX/index.tsx index cd5ca14e8f..e368ec8c24 100644 --- a/src/pages/TX/ReplaceTX/index.tsx +++ b/src/pages/TX/ReplaceTX/index.tsx @@ -1,3 +1,4 @@ +// TODO: placeholder for now const ReplaceTX = (): JSX.Element => { return <>ReplaceTX; }; diff --git a/src/pages/TX/index.tsx b/src/pages/TX/index.tsx index dba8274ce0..a80afaa310 100644 --- a/src/pages/TX/index.tsx +++ b/src/pages/TX/index.tsx @@ -2,18 +2,13 @@ import * as React from 'react'; import { Route, Switch, useRouteMatch } from 'react-router-dom'; import MainContainer from '@/parts/MainContainer'; +import { TXType } from '@/types/general.d'; import { URL_PARAMETERS } from '@/utils/constants/links'; const IssueTX = React.lazy(() => import(/* webpackChunkName: 'issue-tx' */ './IssueTX')); const RedeemTX = React.lazy(() => import(/* webpackChunkName: 'redeem-tx' */ './RedeemTX')); const ReplaceTX = React.lazy(() => import(/* webpackChunkName: 'replace-tx' */ './ReplaceTX')); -enum TXType { - Issue = 'issue', - Redeem = 'redeem', - Replace = 'replace' -} - const TX = (): JSX.Element => { const { path } = useRouteMatch(); diff --git a/src/pages/Transactions/IssueRequestsTable/IssueRequestModal/index.tsx b/src/pages/Transactions/IssueRequestsTable/IssueRequestModal/index.tsx index 61a23e1160..c1457d5a8d 100644 --- a/src/pages/Transactions/IssueRequestsTable/IssueRequestModal/index.tsx +++ b/src/pages/Transactions/IssueRequestsTable/IssueRequestModal/index.tsx @@ -2,10 +2,10 @@ import clsx from 'clsx'; import * as React from 'react'; import { useTranslation } from 'react-i18next'; -import CloseIconButton from '@/components/buttons/CloseIconButton'; -import Hr1 from '@/components/hrs/Hr1'; -import IssueUI from '@/components/IssueUI'; -import InterlayModal, { InterlayModalInnerWrapper, Props as ModalProps } from '@/components/UI/InterlayModal'; +import CloseIconButton from '@/legacy-components/buttons/CloseIconButton'; +import Hr1 from '@/legacy-components/hrs/Hr1'; +import IssueUI from '@/legacy-components/IssueUI'; +import InterlayModal, { InterlayModalInnerWrapper, Props as ModalProps } from '@/legacy-components/UI/InterlayModal'; import RequestModalTitle from '../../RequestModalTitle'; diff --git a/src/pages/Transactions/IssueRequestsTable/index.tsx b/src/pages/Transactions/IssueRequestsTable/index.tsx index bfd12b367f..e6f1fd586f 100644 --- a/src/pages/Transactions/IssueRequestsTable/index.tsx +++ b/src/pages/Transactions/IssueRequestsTable/index.tsx @@ -10,10 +10,13 @@ import { useTable } from 'react-table'; import { showAccountModalAction } from '@/common/actions/general.actions'; import { formatDateTimePrecise, formatNumber, shortTxId } from '@/common/utils/utils'; -import ErrorFallback from '@/components/ErrorFallback'; -import ExternalLink from '@/components/ExternalLink'; -import PrimaryColorEllipsisLoader from '@/components/PrimaryColorEllipsisLoader'; -import InterlayPagination from '@/components/UI/InterlayPagination'; +import { BTC_EXPLORER_TRANSACTION_API } from '@/config/blockstream-explorer-links'; +import { ISSUE_REDEEM_REQUEST_REFETCH_INTERVAL } from '@/config/parachain'; +import { WRAPPED_TOKEN_SYMBOL } from '@/config/relay-chains'; +import ErrorFallback from '@/legacy-components/ErrorFallback'; +import ExternalLink from '@/legacy-components/ExternalLink'; +import PrimaryColorEllipsisLoader from '@/legacy-components/PrimaryColorEllipsisLoader'; +import InterlayPagination from '@/legacy-components/UI/InterlayPagination'; import InterlayTable, { InterlayTableContainer, InterlayTbody, @@ -21,10 +24,7 @@ import InterlayTable, { InterlayTh, InterlayThead, InterlayTr -} from '@/components/UI/InterlayTable'; -import { BTC_EXPLORER_TRANSACTION_API } from '@/config/blockstream-explorer-links'; -import { ISSUE_REDEEM_REQUEST_REFETCH_INTERVAL } from '@/config/parachain'; -import { WRAPPED_TOKEN_SYMBOL } from '@/config/relay-chains'; +} from '@/legacy-components/UI/InterlayTable'; import { useSubstrateSecureState } from '@/lib/substrate'; import SectionTitle from '@/parts/SectionTitle'; import graphqlFetcher, { GRAPHQL_FETCHER, GraphqlReturn } from '@/services/fetchers/graphql-fetcher'; diff --git a/src/pages/Transactions/RedeemRequestsTable/RedeemRequestModal/index.tsx b/src/pages/Transactions/RedeemRequestsTable/RedeemRequestModal/index.tsx index d2bb7e0f3b..ccc3fba223 100644 --- a/src/pages/Transactions/RedeemRequestsTable/RedeemRequestModal/index.tsx +++ b/src/pages/Transactions/RedeemRequestsTable/RedeemRequestModal/index.tsx @@ -2,10 +2,10 @@ import clsx from 'clsx'; import * as React from 'react'; import { useTranslation } from 'react-i18next'; -import CloseIconButton from '@/components/buttons/CloseIconButton'; -import Hr1 from '@/components/hrs/Hr1'; -import RedeemUI from '@/components/RedeemUI'; -import InterlayModal, { InterlayModalInnerWrapper, Props as ModalProps } from '@/components/UI/InterlayModal'; +import CloseIconButton from '@/legacy-components/buttons/CloseIconButton'; +import Hr1 from '@/legacy-components/hrs/Hr1'; +import RedeemUI from '@/legacy-components/RedeemUI'; +import InterlayModal, { InterlayModalInnerWrapper, Props as ModalProps } from '@/legacy-components/UI/InterlayModal'; import RequestModalTitle from '../../RequestModalTitle'; diff --git a/src/pages/Transactions/RedeemRequestsTable/index.tsx b/src/pages/Transactions/RedeemRequestsTable/index.tsx index eccd7b82d5..e8da48b299 100644 --- a/src/pages/Transactions/RedeemRequestsTable/index.tsx +++ b/src/pages/Transactions/RedeemRequestsTable/index.tsx @@ -8,10 +8,13 @@ import { useQuery } from 'react-query'; import { useTable } from 'react-table'; import { formatDateTimePrecise, formatNumber, shortTxId } from '@/common/utils/utils'; -import ErrorFallback from '@/components/ErrorFallback'; -import ExternalLink from '@/components/ExternalLink'; -import PrimaryColorEllipsisLoader from '@/components/PrimaryColorEllipsisLoader'; -import InterlayPagination from '@/components/UI/InterlayPagination'; +import { BTC_EXPLORER_TRANSACTION_API } from '@/config/blockstream-explorer-links'; +import { ISSUE_REDEEM_REQUEST_REFETCH_INTERVAL } from '@/config/parachain'; +import { WRAPPED_TOKEN_SYMBOL } from '@/config/relay-chains'; +import ErrorFallback from '@/legacy-components/ErrorFallback'; +import ExternalLink from '@/legacy-components/ExternalLink'; +import PrimaryColorEllipsisLoader from '@/legacy-components/PrimaryColorEllipsisLoader'; +import InterlayPagination from '@/legacy-components/UI/InterlayPagination'; import InterlayTable, { InterlayTableContainer, InterlayTbody, @@ -19,10 +22,7 @@ import InterlayTable, { InterlayTh, InterlayThead, InterlayTr -} from '@/components/UI/InterlayTable'; -import { BTC_EXPLORER_TRANSACTION_API } from '@/config/blockstream-explorer-links'; -import { ISSUE_REDEEM_REQUEST_REFETCH_INTERVAL } from '@/config/parachain'; -import { WRAPPED_TOKEN_SYMBOL } from '@/config/relay-chains'; +} from '@/legacy-components/UI/InterlayTable'; import { useSubstrateSecureState } from '@/lib/substrate'; import SectionTitle from '@/parts/SectionTitle'; import graphqlFetcher, { GRAPHQL_FETCHER, GraphqlReturn } from '@/services/fetchers/graphql-fetcher'; diff --git a/src/pages/Transactions/RequestModalTitle/index.tsx b/src/pages/Transactions/RequestModalTitle/index.tsx index ce1c8d7191..00ca614f63 100644 --- a/src/pages/Transactions/RequestModalTitle/index.tsx +++ b/src/pages/Transactions/RequestModalTitle/index.tsx @@ -1,6 +1,6 @@ import clsx from 'clsx'; -import { InterlayModalTitle, InterlayModalTitleProps } from '@/components/UI/InterlayModal'; +import { InterlayModalTitle, InterlayModalTitleProps } from '@/legacy-components/UI/InterlayModal'; import { KUSAMA, POLKADOT } from '@/utils/constants/relay-chain-names'; const RequestModalTitle = ({ className, ...rest }: InterlayModalTitleProps): JSX.Element => ( diff --git a/src/pages/Transactions/index.tsx b/src/pages/Transactions/index.tsx index 709b531927..b23ba6118f 100644 --- a/src/pages/Transactions/index.tsx +++ b/src/pages/Transactions/index.tsx @@ -1,7 +1,7 @@ import { useTranslation } from 'react-i18next'; -import ExternalLink from '@/components/ExternalLink'; import { SUBSCAN_LINK } from '@/config/relay-chains'; +import ExternalLink from '@/legacy-components/ExternalLink'; import { useSubstrateSecureState } from '@/lib/substrate'; import MainContainer from '@/parts/MainContainer'; diff --git a/src/pages/Transfer/CrossChainTransferForm/components/ChainIcon/ChainIcon.style.tsx b/src/pages/Transfer/CrossChainTransferForm/components/ChainIcon/ChainIcon.style.tsx new file mode 100644 index 0000000000..81f69b8e82 --- /dev/null +++ b/src/pages/Transfer/CrossChainTransferForm/components/ChainIcon/ChainIcon.style.tsx @@ -0,0 +1,11 @@ +import styled from 'styled-components'; + +import { Icon } from '@/component-library/Icon'; +import { theme } from '@/component-library/theme'; + +const StyledFallbackIcon = styled(Icon)` + stroke: ${theme.icon.fallback.stroke}; + color: ${theme.icon.fallback.color}; +`; + +export { StyledFallbackIcon }; diff --git a/src/pages/Transfer/CrossChainTransferForm/components/ChainIcon/ChainIcon.tsx b/src/pages/Transfer/CrossChainTransferForm/components/ChainIcon/ChainIcon.tsx new file mode 100644 index 0000000000..3a6611d6e2 --- /dev/null +++ b/src/pages/Transfer/CrossChainTransferForm/components/ChainIcon/ChainIcon.tsx @@ -0,0 +1,48 @@ +import { forwardRef, ForwardRefExoticComponent, RefAttributes } from 'react'; + +import { IconProps } from '@/component-library/Icon'; + +import { StyledFallbackIcon } from './ChainIcon.style'; +import { INTERLAY, KINTSUGI, KUSAMA, POLKADOT, STATEMINE, STATEMINT } from './icons'; + +type ChainComponent = ForwardRefExoticComponent>; + +const chainsIcon: Record = { + INTERLAY, + KINTSUGI, + KUSAMA, + POLKADOT, + STATEMINE, + STATEMINT +}; + +type Props = { + id: string; +}; + +type NativeAttrs = Omit; + +type ChainIconProps = Props & NativeAttrs; + +const ChainIcon = forwardRef( + ({ id, ...props }, ref): JSX.Element => { + // id is returned from api as lowercase, e.g. 'interlay' + const ChainIcon = chainsIcon[id.toUpperCase()]; + + if (!ChainIcon) { + return ( + + {id} + + + ); + } + + return ; + } +); + +ChainIcon.displayName = 'CoinIcon'; + +export { ChainIcon }; +export type { ChainIconProps }; diff --git a/src/pages/Transfer/CrossChainTransferForm/components/ChainIcon/icons/Interlay.tsx b/src/pages/Transfer/CrossChainTransferForm/components/ChainIcon/icons/Interlay.tsx new file mode 100644 index 0000000000..d7414ff2f3 --- /dev/null +++ b/src/pages/Transfer/CrossChainTransferForm/components/ChainIcon/icons/Interlay.tsx @@ -0,0 +1,17 @@ +import { forwardRef } from 'react'; + +import { Icon, IconProps } from '@/component-library/Icon'; + +const INTERLAY = forwardRef((props, ref) => ( + + INTERLAY + + + + + +)); + +INTERLAY.displayName = 'INTERLAY'; + +export { INTERLAY }; diff --git a/src/pages/Transfer/CrossChainTransferForm/components/ChainIcon/icons/Kintsugi.tsx b/src/pages/Transfer/CrossChainTransferForm/components/ChainIcon/icons/Kintsugi.tsx new file mode 100644 index 0000000000..92e634ee4a --- /dev/null +++ b/src/pages/Transfer/CrossChainTransferForm/components/ChainIcon/icons/Kintsugi.tsx @@ -0,0 +1,44 @@ +import { forwardRef } from 'react'; + +import { Icon, IconProps } from '@/component-library/Icon'; + +const KINTSUGI = forwardRef((props, ref) => ( + + KINTSUGI + + + + + + + + + + + + + + + + + +)); + +KINTSUGI.displayName = 'KINTSUGI'; + +export { KINTSUGI }; diff --git a/src/pages/Transfer/CrossChainTransferForm/components/ChainIcon/icons/Kusama.tsx b/src/pages/Transfer/CrossChainTransferForm/components/ChainIcon/icons/Kusama.tsx new file mode 100644 index 0000000000..d0c62504f9 --- /dev/null +++ b/src/pages/Transfer/CrossChainTransferForm/components/ChainIcon/icons/Kusama.tsx @@ -0,0 +1,29 @@ +import { forwardRef } from 'react'; + +import { Icon, IconProps } from '@/component-library/Icon'; + +const KUSAMA = forwardRef((props, ref) => ( + + KUSAMA + + + + + + + + + + +)); + +KUSAMA.displayName = 'KUSAMA'; + +export { KUSAMA }; diff --git a/src/pages/Transfer/CrossChainTransferForm/components/ChainIcon/icons/Polkadot.tsx b/src/pages/Transfer/CrossChainTransferForm/components/ChainIcon/icons/Polkadot.tsx new file mode 100644 index 0000000000..d3ac6ec114 --- /dev/null +++ b/src/pages/Transfer/CrossChainTransferForm/components/ChainIcon/icons/Polkadot.tsx @@ -0,0 +1,23 @@ +import { forwardRef } from 'react'; + +import { Icon, IconProps } from '@/component-library/Icon'; + +const POLKADOT = forwardRef((props, ref) => ( + + POLKADOT + + + + + + + + + + + +)); + +POLKADOT.displayName = 'POLKADOT'; + +export { POLKADOT }; diff --git a/src/pages/Transfer/CrossChainTransferForm/components/ChainIcon/icons/Statemine.tsx b/src/pages/Transfer/CrossChainTransferForm/components/ChainIcon/icons/Statemine.tsx new file mode 100644 index 0000000000..c27383b6e0 --- /dev/null +++ b/src/pages/Transfer/CrossChainTransferForm/components/ChainIcon/icons/Statemine.tsx @@ -0,0 +1,30 @@ +import { forwardRef } from 'react'; + +import { Icon, IconProps } from '@/component-library/Icon'; + +const STATEMINE = forwardRef((props, ref) => ( + + STATEMINE + + + + + + + + + + + + + + + +)); + +STATEMINE.displayName = 'STATEMINE'; + +export { STATEMINE }; diff --git a/src/pages/Transfer/CrossChainTransferForm/components/ChainIcon/icons/Statemint.tsx b/src/pages/Transfer/CrossChainTransferForm/components/ChainIcon/icons/Statemint.tsx new file mode 100644 index 0000000000..92eeb165d5 --- /dev/null +++ b/src/pages/Transfer/CrossChainTransferForm/components/ChainIcon/icons/Statemint.tsx @@ -0,0 +1,30 @@ +import { forwardRef } from 'react'; + +import { Icon, IconProps } from '@/component-library/Icon'; + +const STATEMINT = forwardRef((props, ref) => ( + + STATEMINT + + + + + + + + + + + + + + + +)); + +STATEMINT.displayName = 'STATEMINT'; + +export { STATEMINT }; diff --git a/src/pages/Transfer/CrossChainTransferForm/components/ChainIcon/icons/index.ts b/src/pages/Transfer/CrossChainTransferForm/components/ChainIcon/icons/index.ts new file mode 100644 index 0000000000..df6ed50da9 --- /dev/null +++ b/src/pages/Transfer/CrossChainTransferForm/components/ChainIcon/icons/index.ts @@ -0,0 +1,6 @@ +export { INTERLAY } from './Interlay'; +export { KINTSUGI } from './Kintsugi'; +export { KUSAMA } from './Kusama'; +export { POLKADOT } from './Polkadot'; +export { STATEMINE } from './Statemine'; +export { STATEMINT } from './Statemint'; diff --git a/src/pages/Transfer/CrossChainTransferForm/components/ChainIcon/index.tsx b/src/pages/Transfer/CrossChainTransferForm/components/ChainIcon/index.tsx new file mode 100644 index 0000000000..10b00ac3ec --- /dev/null +++ b/src/pages/Transfer/CrossChainTransferForm/components/ChainIcon/index.tsx @@ -0,0 +1,2 @@ +export type { ChainIconProps } from './ChainIcon'; +export { ChainIcon } from './ChainIcon'; diff --git a/src/pages/Transfer/CrossChainTransferForm/components/index.tsx b/src/pages/Transfer/CrossChainTransferForm/components/index.tsx new file mode 100644 index 0000000000..98ff9e319b --- /dev/null +++ b/src/pages/Transfer/CrossChainTransferForm/components/index.tsx @@ -0,0 +1,4 @@ +import { ChainIcon, ChainIconProps } from './ChainIcon'; + +export { ChainIcon }; +export type { ChainIconProps }; diff --git a/src/pages/Transfer/CrossChainTransferForm/index.tsx b/src/pages/Transfer/CrossChainTransferForm/index.tsx index 0ac7bba14f..c5f88a6cda 100644 --- a/src/pages/Transfer/CrossChainTransferForm/index.tsx +++ b/src/pages/Transfer/CrossChainTransferForm/index.tsx @@ -18,22 +18,23 @@ import { firstValueFrom } from 'rxjs'; import { showAccountModalAction } from '@/common/actions/general.actions'; import { ParachainStatus, StoreType } from '@/common/types/util.types'; import { displayMonetaryAmountInUSDFormat } from '@/common/utils/utils'; -import { CoinIcon } from '@/component-library'; -import Accounts from '@/components/Accounts'; -import AvailableBalanceUI from '@/components/AvailableBalanceUI'; -import Chains, { ChainOption } from '@/components/Chains'; -import ErrorFallback from '@/components/ErrorFallback'; -import ErrorModal from '@/components/ErrorModal'; -import FormTitle from '@/components/FormTitle'; -import PrimaryColorEllipsisLoader from '@/components/PrimaryColorEllipsisLoader'; -import SubmitButton from '@/components/SubmitButton'; -import TokenField from '@/components/TokenField'; +import Accounts from '@/legacy-components/Accounts'; +import AvailableBalanceUI from '@/legacy-components/AvailableBalanceUI'; +import Chains, { ChainOption } from '@/legacy-components/Chains'; +import ErrorFallback from '@/legacy-components/ErrorFallback'; +import ErrorModal from '@/legacy-components/ErrorModal'; +import FormTitle from '@/legacy-components/FormTitle'; +import PrimaryColorEllipsisLoader from '@/legacy-components/PrimaryColorEllipsisLoader'; +import SubmitButton from '@/legacy-components/SubmitButton'; +import TokenField from '@/legacy-components/TokenField'; import { KeyringPair, useSubstrateSecureState } from '@/lib/substrate'; import STATUSES from '@/utils/constants/statuses'; import { getTokenPrice } from '@/utils/helpers/prices'; import { useGetPrices } from '@/utils/hooks/api/use-get-prices'; import { useXCMBridge } from '@/utils/hooks/api/xcm/use-xcm-bridge'; +import { ChainIcon } from './components'; + const TRANSFER_AMOUNT = 'transfer-amount'; type CrossChainTransferFormData = { @@ -131,7 +132,7 @@ const CrossChainTransferForm = (): JSX.Element => { return { type: adapter.chain.id, name: adapter.chain.id, - icon: + icon: }; }); @@ -146,12 +147,10 @@ const CrossChainTransferForm = (): JSX.Element => { const destinationChains = XCMBridge.router.getDestinationChains({ from: fromChain.type }); const availableToChains = destinationChains.map((chain: any) => { - const adapter = XCMBridge.findAdapter(chain.id) as any; - return { type: chain.id, name: chain.id, - icon: + icon: }; }); diff --git a/src/pages/Transfer/TokenAmountField/index.tsx b/src/pages/Transfer/TokenAmountField/index.tsx index cee2ae873e..b47ff6ae4f 100644 --- a/src/pages/Transfer/TokenAmountField/index.tsx +++ b/src/pages/Transfer/TokenAmountField/index.tsx @@ -1,8 +1,8 @@ import clsx from 'clsx'; import * as React from 'react'; -import NumberInput, { Props as NumberInputProps } from '@/components/NumberInput'; -import { TextFieldContainer, TextFieldHelperText, TextFieldLabel } from '@/components/TextField'; +import NumberInput, { Props as NumberInputProps } from '@/legacy-components/NumberInput'; +import { TextFieldContainer, TextFieldHelperText, TextFieldLabel } from '@/legacy-components/TextField'; import { KUSAMA, POLKADOT } from '@/utils/constants/relay-chain-names'; interface CustomProps { diff --git a/src/pages/Transfer/TransferForm/index.tsx b/src/pages/Transfer/TransferForm/index.tsx index b1db2664c4..bc472332be 100644 --- a/src/pages/Transfer/TransferForm/index.tsx +++ b/src/pages/Transfer/TransferForm/index.tsx @@ -10,13 +10,13 @@ import { toast } from 'react-toastify'; import { showAccountModalAction } from '@/common/actions/general.actions'; import { ParachainStatus, StoreType } from '@/common/types/util.types'; import { formatNumber } from '@/common/utils/utils'; -import ErrorFallback from '@/components/ErrorFallback'; -import ErrorModal from '@/components/ErrorModal'; -import FormTitle from '@/components/FormTitle'; -import SubmitButton from '@/components/SubmitButton'; -import TextField from '@/components/TextField'; -import Tokens, { TokenOption } from '@/components/Tokens'; -import InterlayButtonBase from '@/components/UI/InterlayButtonBase'; +import ErrorFallback from '@/legacy-components/ErrorFallback'; +import ErrorModal from '@/legacy-components/ErrorModal'; +import FormTitle from '@/legacy-components/FormTitle'; +import SubmitButton from '@/legacy-components/SubmitButton'; +import TextField from '@/legacy-components/TextField'; +import Tokens, { TokenOption } from '@/legacy-components/Tokens'; +import InterlayButtonBase from '@/legacy-components/UI/InterlayButtonBase'; import { useSubstrateSecureState } from '@/lib/substrate'; import { KUSAMA, POLKADOT } from '@/utils/constants/relay-chain-names'; import STATUSES from '@/utils/constants/statuses'; diff --git a/src/pages/Transfer/index.tsx b/src/pages/Transfer/index.tsx index beb1d7874c..ed2ea96777 100644 --- a/src/pages/Transfer/index.tsx +++ b/src/pages/Transfer/index.tsx @@ -2,16 +2,16 @@ import clsx from 'clsx'; import * as React from 'react'; import { useTranslation } from 'react-i18next'; -import Hr1 from '@/components/hrs/Hr1'; -import Panel from '@/components/Panel'; -import InterlayRouterLink from '@/components/UI/InterlayRouterLink'; +import Hr1 from '@/legacy-components/hrs/Hr1'; +import Panel from '@/legacy-components/Panel'; +import InterlayRouterLink from '@/legacy-components/UI/InterlayRouterLink'; import InterlayTabGroup, { InterlayTab, InterlayTabList, InterlayTabPanel, InterlayTabPanels -} from '@/components/UI/InterlayTabGroup'; -import WarningBanner from '@/components/WarningBanner'; +} from '@/legacy-components/UI/InterlayTabGroup'; +import WarningBanner from '@/legacy-components/WarningBanner'; import MainContainer from '@/parts/MainContainer'; import { QUERY_PARAMETERS } from '@/utils/constants/links'; import { POLKADOT } from '@/utils/constants/relay-chain-names'; diff --git a/src/pages/Vaults/Vault/ReplaceTable/index.tsx b/src/pages/Vaults/Vault/ReplaceTable/index.tsx index 6af48495f9..6f23497844 100644 --- a/src/pages/Vaults/Vault/ReplaceTable/index.tsx +++ b/src/pages/Vaults/Vault/ReplaceTable/index.tsx @@ -16,9 +16,12 @@ import { useSelector } from 'react-redux'; import { useTable } from 'react-table'; import { StoreType } from '@/common/types/util.types'; -import AddressWithCopyUI from '@/components/AddressWithCopyUI'; -import ErrorFallback from '@/components/ErrorFallback'; -import PrimaryColorEllipsisLoader from '@/components/PrimaryColorEllipsisLoader'; +import { ACCOUNT_ID_TYPE_NAME } from '@/config/general'; +import { ISSUE_REDEEM_REQUEST_REFETCH_INTERVAL } from '@/config/parachain'; +import { WRAPPED_TOKEN_SYMBOL } from '@/config/relay-chains'; +import AddressWithCopyUI from '@/legacy-components/AddressWithCopyUI'; +import ErrorFallback from '@/legacy-components/ErrorFallback'; +import PrimaryColorEllipsisLoader from '@/legacy-components/PrimaryColorEllipsisLoader'; import InterlayTable, { InterlayTableContainer, InterlayTbody, @@ -26,10 +29,7 @@ import InterlayTable, { InterlayTh, InterlayThead, InterlayTr -} from '@/components/UI/InterlayTable'; -import { ACCOUNT_ID_TYPE_NAME } from '@/config/general'; -import { ISSUE_REDEEM_REQUEST_REFETCH_INTERVAL } from '@/config/parachain'; -import { WRAPPED_TOKEN_SYMBOL } from '@/config/relay-chains'; +} from '@/legacy-components/UI/InterlayTable'; import SectionTitle from '@/parts/SectionTitle'; import genericFetcher, { GENERIC_FETCHER } from '@/services/fetchers/generic-fetcher'; diff --git a/src/pages/Vaults/Vault/RequestIssueModal/index.tsx b/src/pages/Vaults/Vault/RequestIssueModal/index.tsx index 6f371258b6..183128e603 100644 --- a/src/pages/Vaults/Vault/RequestIssueModal/index.tsx +++ b/src/pages/Vaults/Vault/RequestIssueModal/index.tsx @@ -11,16 +11,12 @@ import { useSelector } from 'react-redux'; import { ReactComponent as BitcoinLogoIcon } from '@/assets/img/bitcoin-logo.svg'; import { ParachainStatus, StoreType } from '@/common/types/util.types'; import { displayMonetaryAmount, displayMonetaryAmountInUSDFormat } from '@/common/utils/utils'; -import CloseIconButton from '@/components/buttons/CloseIconButton'; -import ErrorModal from '@/components/ErrorModal'; -import Hr2 from '@/components/hrs/Hr2'; -import PriceInfo from '@/components/PriceInfo'; -import SubmitButton from '@/components/SubmitButton'; -import TokenField from '@/components/TokenField'; -import InformationTooltip from '@/components/tooltips/InformationTooltip'; -import InterlayButtonBase from '@/components/UI/InterlayButtonBase'; -import InterlayModal, { InterlayModalInnerWrapper, InterlayModalTitle } from '@/components/UI/InterlayModal'; -import { BLOCKS_BEHIND_LIMIT } from '@/config/parachain'; +import { + BLOCKS_BEHIND_LIMIT, + DEFAULT_ISSUE_BRIDGE_FEE_RATE, + DEFAULT_ISSUE_DUST_AMOUNT, + DEFAULT_ISSUE_GRIEFING_COLLATERAL_RATE +} from '@/config/parachain'; import { GOVERNANCE_TOKEN, GOVERNANCE_TOKEN_SYMBOL, @@ -29,6 +25,15 @@ import { WRAPPED_TOKEN_SYMBOL, WrappedTokenLogoIcon } from '@/config/relay-chains'; +import CloseIconButton from '@/legacy-components/buttons/CloseIconButton'; +import ErrorModal from '@/legacy-components/ErrorModal'; +import Hr2 from '@/legacy-components/hrs/Hr2'; +import PriceInfo from '@/legacy-components/PriceInfo'; +import SubmitButton from '@/legacy-components/SubmitButton'; +import TokenField from '@/legacy-components/TokenField'; +import InformationTooltip from '@/legacy-components/tooltips/InformationTooltip'; +import InterlayButtonBase from '@/legacy-components/UI/InterlayButtonBase'; +import InterlayModal, { InterlayModalInnerWrapper, InterlayModalTitle } from '@/legacy-components/UI/InterlayModal'; import { useSubstrateSecureState } from '@/lib/substrate'; import SubmittedIssueRequestModal from '@/pages/Bridge/IssueForm/SubmittedIssueRequestModal'; import { ForeignAssetIdLiteral } from '@/types/currency'; @@ -72,12 +77,12 @@ const RequestIssueModal = ({ onClose, open, collateralToken, vaultAddress }: Pro const [status, setStatus] = React.useState(STATUSES.IDLE); const [vaultCapacity, setVaultCapacity] = React.useState(BitcoinAmount.zero()); - const [feeRate, setFeeRate] = React.useState(new Big(0.005)); // Set default to 0.5% - const [depositRate, setDepositRate] = React.useState(new Big(0.00005)); // Set default to 0.005% + const [issueFeeRate, setIssueFeeRate] = React.useState(new Big(DEFAULT_ISSUE_BRIDGE_FEE_RATE)); + const [depositRate, setDepositRate] = React.useState(new Big(DEFAULT_ISSUE_GRIEFING_COLLATERAL_RATE)); const [btcToGovernanceTokenRate, setBTCToGovernanceTokenRate] = React.useState( new ExchangeRate(Bitcoin, GOVERNANCE_TOKEN, new Big(0)) ); - const [dustValue, setDustValue] = React.useState(BitcoinAmount.zero()); + const [dustValue, setDustValue] = React.useState(new BitcoinAmount(DEFAULT_ISSUE_DUST_AMOUNT)); const [submitStatus, setSubmitStatus] = React.useState(STATUSES.IDLE); const [submitError, setSubmitError] = React.useState(null); const [submittedRequest, setSubmittedRequest] = React.useState(); @@ -107,11 +112,11 @@ const RequestIssueModal = ({ onClose, open, collateralToken, vaultAddress }: Pro try { setStatus(STATUSES.PENDING); const [ - theFeeRate, - theDepositRate, - theDustValue, - theBtcToGovernanceToken, - issuableAmount + feeRateResult, + depositRateResult, + dustValueResult, + btcToGovernanceTokenResult, + vaultIssuableAmountResult ] = await Promise.allSettled([ // Loading this data is not strictly required as long as the constantly set values did // not change. However, you will not see the correct value for the security deposit. @@ -123,20 +128,20 @@ const RequestIssueModal = ({ onClose, open, collateralToken, vaultAddress }: Pro window.bridge.issue.getVaultIssuableAmount(vaultAccountId, collateralToken) ]); setStatus(STATUSES.RESOLVED); - if (theFeeRate.status === 'fulfilled') { - setFeeRate(theFeeRate.value); + if (feeRateResult.status === 'fulfilled') { + setIssueFeeRate(feeRateResult.value); } - if (theDepositRate.status === 'fulfilled') { - setDepositRate(theDepositRate.value); + if (depositRateResult.status === 'fulfilled') { + setDepositRate(depositRateResult.value); } - if (theDustValue.status === 'fulfilled') { - setDustValue(theDustValue.value); + if (dustValueResult.status === 'fulfilled') { + setDustValue(dustValueResult.value); } - if (issuableAmount.status === 'fulfilled') { - setVaultCapacity(issuableAmount.value); + if (vaultIssuableAmountResult.status === 'fulfilled') { + setVaultCapacity(vaultIssuableAmountResult.value); } - if (theBtcToGovernanceToken.status === 'fulfilled') { - setBTCToGovernanceTokenRate(theBtcToGovernanceToken.value); + if (btcToGovernanceTokenResult.status === 'fulfilled') { + setBTCToGovernanceTokenRate(btcToGovernanceTokenResult.value); } else { setError(WRAPPED_TOKEN_AMOUNT, { type: 'validate', @@ -243,7 +248,7 @@ const RequestIssueModal = ({ onClose, open, collateralToken, vaultAddress }: Pro }; const parsedBTCAmount = new BitcoinAmount(btcAmount); - const bridgeFee = parsedBTCAmount.mul(feeRate); + const bridgeFee = parsedBTCAmount.mul(issueFeeRate); const securityDeposit = btcToGovernanceTokenRate.toCounter(parsedBTCAmount).mul(depositRate); const wrappedTokenAmount = parsedBTCAmount.sub(bridgeFee); diff --git a/src/pages/Vaults/Vault/RequestRedeemModal/index.tsx b/src/pages/Vaults/Vault/RequestRedeemModal/index.tsx index e39e8c4338..07f0684b31 100644 --- a/src/pages/Vaults/Vault/RequestRedeemModal/index.tsx +++ b/src/pages/Vaults/Vault/RequestRedeemModal/index.tsx @@ -8,15 +8,15 @@ import { useQueryClient } from 'react-query'; import { toast } from 'react-toastify'; import { displayMonetaryAmount } from '@/common/utils/utils'; -import CloseIconButton from '@/components/buttons/CloseIconButton'; -import InterlayCinnabarOutlinedButton from '@/components/buttons/InterlayCinnabarOutlinedButton'; -import InterlayMulberryOutlinedButton from '@/components/buttons/InterlayMulberryOutlinedButton'; -import ErrorMessage from '@/components/ErrorMessage'; -import NumberInput from '@/components/NumberInput'; -import TextField from '@/components/TextField'; -import InterlayModal, { InterlayModalInnerWrapper, InterlayModalTitle } from '@/components/UI/InterlayModal'; import { WRAPPED_TOKEN } from '@/config/relay-chains'; import { BTC_ADDRESS_REGEX } from '@/constants'; +import CloseIconButton from '@/legacy-components/buttons/CloseIconButton'; +import InterlayCinnabarOutlinedButton from '@/legacy-components/buttons/InterlayCinnabarOutlinedButton'; +import InterlayMulberryOutlinedButton from '@/legacy-components/buttons/InterlayMulberryOutlinedButton'; +import ErrorMessage from '@/legacy-components/ErrorMessage'; +import NumberInput from '@/legacy-components/NumberInput'; +import TextField from '@/legacy-components/TextField'; +import InterlayModal, { InterlayModalInnerWrapper, InterlayModalTitle } from '@/legacy-components/UI/InterlayModal'; const WRAPPED_TOKEN_AMOUNT = 'amount'; const BTC_ADDRESS = 'btc-address'; diff --git a/src/pages/Vaults/Vault/RequestReplacementModal/index.tsx b/src/pages/Vaults/Vault/RequestReplacementModal/index.tsx index 6597cd5c0e..fc6004d9e4 100644 --- a/src/pages/Vaults/Vault/RequestReplacementModal/index.tsx +++ b/src/pages/Vaults/Vault/RequestReplacementModal/index.tsx @@ -13,15 +13,16 @@ import { toast } from 'react-toastify'; import { StoreType } from '@/common/types/util.types'; import { displayMonetaryAmount } from '@/common/utils/utils'; -import CloseIconButton from '@/components/buttons/CloseIconButton'; -import InterlayCinnabarOutlinedButton from '@/components/buttons/InterlayCinnabarOutlinedButton'; -import InterlayMulberryOutlinedButton from '@/components/buttons/InterlayMulberryOutlinedButton'; -import ErrorMessage from '@/components/ErrorMessage'; -import NumberInput from '@/components/NumberInput'; -import PrimaryColorEllipsisLoader from '@/components/PrimaryColorEllipsisLoader'; -import InterlayModal, { InterlayModalInnerWrapper, InterlayModalTitle } from '@/components/UI/InterlayModal'; import { ACCOUNT_ID_TYPE_NAME } from '@/config/general'; +import { DEFAULT_REDEEM_DUST_AMOUNT } from '@/config/parachain'; import { GOVERNANCE_TOKEN, GOVERNANCE_TOKEN_SYMBOL, TRANSACTION_FEE_AMOUNT } from '@/config/relay-chains'; +import CloseIconButton from '@/legacy-components/buttons/CloseIconButton'; +import InterlayCinnabarOutlinedButton from '@/legacy-components/buttons/InterlayCinnabarOutlinedButton'; +import InterlayMulberryOutlinedButton from '@/legacy-components/buttons/InterlayMulberryOutlinedButton'; +import ErrorMessage from '@/legacy-components/ErrorMessage'; +import NumberInput from '@/legacy-components/NumberInput'; +import PrimaryColorEllipsisLoader from '@/legacy-components/PrimaryColorEllipsisLoader'; +import InterlayModal, { InterlayModalInnerWrapper, InterlayModalTitle } from '@/legacy-components/UI/InterlayModal'; import { GENERIC_FETCHER } from '@/services/fetchers/generic-fetcher'; import STATUSES from '@/utils/constants/statuses'; import { getExchangeRate } from '@/utils/helpers/oracle'; @@ -59,7 +60,6 @@ const RequestReplacementModal = ({ } = useForm(); const amount = watch(AMOUNT) || '0'; - const { t } = useTranslation(); const queryClient = useQueryClient(); const handleError = useErrorHandler(); @@ -71,7 +71,7 @@ const RequestReplacementModal = ({ const [status, setStatus] = React.useState(STATUSES.IDLE); const [griefingRate, setGriefingRate] = React.useState(new Big(10.0)); // Set default to 10% - const [dustValue, setDustValue] = React.useState(BitcoinAmount.zero()); + const [dustValue, setDustValue] = React.useState(new BitcoinAmount(DEFAULT_REDEEM_DUST_AMOUNT)); const [btcToGovernanceTokenRate, setBTCToGovernanceTokenRate] = React.useState( new ExchangeRate(Bitcoin, GOVERNANCE_TOKEN, new Big(0)) ); @@ -84,11 +84,7 @@ const RequestReplacementModal = ({ (async () => { try { setStatus(STATUSES.PENDING); - const [ - theGriefingRate, - theDustValue, - theBtcToGovernanceTokenRate - ] = await Promise.all([ + const [theGriefingRate, theDustValue, theBtcToGovernanceTokenRate] = await Promise.all([ window.bridge.fee.getReplaceGriefingCollateralRate(), window.bridge.redeem.getDustValue(), getExchangeRate(GOVERNANCE_TOKEN) @@ -120,15 +116,10 @@ const RequestReplacementModal = ({ } }); - if ( - status === STATUSES.IDLE || - status === STATUSES.PENDING || - isBalancesLoading - ) { + if (status === STATUSES.IDLE || status === STATUSES.PENDING || isBalancesLoading) { return ; } - if (status === STATUSES.RESOLVED) { const validateAmount = (value: number): string | undefined => { const governanceTokenBalance = balances?.[GOVERNANCE_TOKEN.ticker]; diff --git a/src/pages/Vaults/Vault/UpdateCollateralModal/index.tsx b/src/pages/Vaults/Vault/UpdateCollateralModal/index.tsx index 1e3214c67e..5a22849c3d 100644 --- a/src/pages/Vaults/Vault/UpdateCollateralModal/index.tsx +++ b/src/pages/Vaults/Vault/UpdateCollateralModal/index.tsx @@ -14,11 +14,11 @@ import { toast } from 'react-toastify'; import { updateCollateralAction, updateCollateralizationAction } from '@/common/actions/vault.actions'; import { StoreType } from '@/common/types/util.types'; import { displayMonetaryAmount, displayMonetaryAmountInUSDFormat, formatPercentage } from '@/common/utils/utils'; -import CloseIconButton from '@/components/buttons/CloseIconButton'; -import InterlayDefaultContainedButton from '@/components/buttons/InterlayDefaultContainedButton'; -import TokenField from '@/components/TokenField'; -import InterlayModal, { InterlayModalInnerWrapper, InterlayModalTitle } from '@/components/UI/InterlayModal'; import { ACCOUNT_ID_TYPE_NAME } from '@/config/general'; +import CloseIconButton from '@/legacy-components/buttons/CloseIconButton'; +import InterlayDefaultContainedButton from '@/legacy-components/buttons/InterlayDefaultContainedButton'; +import TokenField from '@/legacy-components/TokenField'; +import InterlayModal, { InterlayModalInnerWrapper, InterlayModalTitle } from '@/legacy-components/UI/InterlayModal'; import genericFetcher, { GENERIC_FETCHER } from '@/services/fetchers/generic-fetcher'; import STATUSES from '@/utils/constants/statuses'; import { getTokenPrice } from '@/utils/helpers/prices'; diff --git a/src/pages/Vaults/Vault/VaultDashboard.tsx b/src/pages/Vaults/Vault/VaultDashboard.tsx index 5b6f24a6b3..974a511b78 100644 --- a/src/pages/Vaults/Vault/VaultDashboard.tsx +++ b/src/pages/Vaults/Vault/VaultDashboard.tsx @@ -6,8 +6,8 @@ import { StoreType } from '@/common/types/util.types'; import { formatNumber, formatPercentage, formatUSD } from '@/common/utils/utils'; import { Stack } from '@/component-library'; import { ProgressCircle } from '@/component-library/ProgressCircle'; -import ErrorFallback from '@/components/ErrorFallback'; -import PrimaryColorEllipsisLoader from '@/components/PrimaryColorEllipsisLoader'; +import ErrorFallback from '@/legacy-components/ErrorFallback'; +import PrimaryColorEllipsisLoader from '@/legacy-components/PrimaryColorEllipsisLoader'; import { useSubstrateSecureState } from '@/lib/substrate'; import MainContainer from '@/parts/MainContainer'; import { URL_PARAMETERS } from '@/utils/constants/links'; diff --git a/src/pages/Vaults/Vault/VaultIssueRequestsTable/index.tsx b/src/pages/Vaults/Vault/VaultIssueRequestsTable/index.tsx index 77ad7c6211..45894efb87 100644 --- a/src/pages/Vaults/Vault/VaultIssueRequestsTable/index.tsx +++ b/src/pages/Vaults/Vault/VaultIssueRequestsTable/index.tsx @@ -7,11 +7,13 @@ import { useQuery } from 'react-query'; import { useTable } from 'react-table'; import { formatDateTimePrecise, shortAddress } from '@/common/utils/utils'; -import AddressWithCopyUI from '@/components/AddressWithCopyUI'; -import ErrorFallback from '@/components/ErrorFallback'; -import ExternalLink from '@/components/ExternalLink'; -import PrimaryColorEllipsisLoader from '@/components/PrimaryColorEllipsisLoader'; -import InterlayPagination from '@/components/UI/InterlayPagination'; +import { BTC_EXPLORER_ADDRESS_API } from '@/config/blockstream-explorer-links'; +import { ISSUE_REDEEM_REQUEST_REFETCH_INTERVAL } from '@/config/parachain'; +import AddressWithCopyUI from '@/legacy-components/AddressWithCopyUI'; +import ErrorFallback from '@/legacy-components/ErrorFallback'; +import ExternalLink from '@/legacy-components/ExternalLink'; +import PrimaryColorEllipsisLoader from '@/legacy-components/PrimaryColorEllipsisLoader'; +import InterlayPagination from '@/legacy-components/UI/InterlayPagination'; import InterlayTable, { InterlayTableContainer, InterlayTbody, @@ -19,14 +21,14 @@ import InterlayTable, { InterlayTh, InterlayThead, InterlayTr -} from '@/components/UI/InterlayTable'; -import StatusCell from '@/components/UI/InterlayTable/StatusCell'; -import { BTC_EXPLORER_ADDRESS_API } from '@/config/blockstream-explorer-links'; -import { ISSUE_REDEEM_REQUEST_REFETCH_INTERVAL } from '@/config/parachain'; +} from '@/legacy-components/UI/InterlayTable'; +import StatusCell from '@/legacy-components/UI/InterlayTable/StatusCell'; +import ViewRequestDetailsLink from '@/legacy-components/ViewRequestDetailsLink'; import SectionTitle from '@/parts/SectionTitle'; import graphqlFetcher, { GRAPHQL_FETCHER, GraphqlReturn } from '@/services/fetchers/graphql-fetcher'; import { useIssueRequests } from '@/services/hooks/issue-requests'; import { issuesCountQuery } from '@/services/queries/issues'; +import { TXType } from '@/types/general.d'; import { TABLE_PAGE_LIMIT } from '@/utils/constants/general'; import { QUERY_PARAMETERS } from '@/utils/constants/links'; import { getCurrencyEqualityCondition } from '@/utils/helpers/currencies'; @@ -93,6 +95,14 @@ const VaultIssueRequestsTable = ({ vaultAddress, collateralToken }: Props): JSX. return <>{formatDateTimePrecise(new Date(issue.request.timestamp))}; } }, + { + Header: t('view_details'), + classNames: ['text-left'], + // TODO: should type properly (`Relay`) + Cell: ({ row: { original: issue } }: any) => { + return ; + } + }, { Header: t('vault.creation_block'), classNames: ['text-right'], diff --git a/src/pages/Vaults/Vault/VaultRedeemRequestsTable/index.tsx b/src/pages/Vaults/Vault/VaultRedeemRequestsTable/index.tsx index dc1eeb2efb..50800e0ea1 100644 --- a/src/pages/Vaults/Vault/VaultRedeemRequestsTable/index.tsx +++ b/src/pages/Vaults/Vault/VaultRedeemRequestsTable/index.tsx @@ -7,11 +7,13 @@ import { useQuery } from 'react-query'; import { useTable } from 'react-table'; import { formatDateTimePrecise, shortAddress, shortTxId } from '@/common/utils/utils'; -import AddressWithCopyUI from '@/components/AddressWithCopyUI'; -import ErrorFallback from '@/components/ErrorFallback'; -import ExternalLink from '@/components/ExternalLink'; -import PrimaryColorEllipsisLoader from '@/components/PrimaryColorEllipsisLoader'; -import InterlayPagination from '@/components/UI/InterlayPagination'; +import { BTC_EXPLORER_ADDRESS_API, BTC_EXPLORER_TRANSACTION_API } from '@/config/blockstream-explorer-links'; +import { ISSUE_REDEEM_REQUEST_REFETCH_INTERVAL } from '@/config/parachain'; +import AddressWithCopyUI from '@/legacy-components/AddressWithCopyUI'; +import ErrorFallback from '@/legacy-components/ErrorFallback'; +import ExternalLink from '@/legacy-components/ExternalLink'; +import PrimaryColorEllipsisLoader from '@/legacy-components/PrimaryColorEllipsisLoader'; +import InterlayPagination from '@/legacy-components/UI/InterlayPagination'; import InterlayTable, { InterlayTableContainer, InterlayTbody, @@ -19,10 +21,9 @@ import InterlayTable, { InterlayTh, InterlayThead, InterlayTr -} from '@/components/UI/InterlayTable'; -import StatusCell from '@/components/UI/InterlayTable/StatusCell'; -import { BTC_EXPLORER_ADDRESS_API, BTC_EXPLORER_TRANSACTION_API } from '@/config/blockstream-explorer-links'; -import { ISSUE_REDEEM_REQUEST_REFETCH_INTERVAL } from '@/config/parachain'; +} from '@/legacy-components/UI/InterlayTable'; +import StatusCell from '@/legacy-components/UI/InterlayTable/StatusCell'; +import ViewRequestDetailsLink from '@/legacy-components/ViewRequestDetailsLink'; import SectionTitle from '@/parts/SectionTitle'; import graphqlFetcher, { GRAPHQL_FETCHER, GraphqlReturn } from '@/services/fetchers/graphql-fetcher'; import redeemsFetcher, { getRedeemWithStatus, REDEEMS_FETCHER } from '@/services/fetchers/redeems-fetcher'; @@ -30,6 +31,7 @@ import useCurrentActiveBlockNumber from '@/services/hooks/use-current-active-blo import useStableBitcoinConfirmations from '@/services/hooks/use-stable-bitcoin-confirmations'; import useStableParachainConfirmations from '@/services/hooks/use-stable-parachain-confirmations'; import redeemCountQuery from '@/services/queries/redeem-count-query'; +import { TXType } from '@/types/general.d'; import { TABLE_PAGE_LIMIT } from '@/utils/constants/general'; import { QUERY_PARAMETERS } from '@/utils/constants/links'; import { getCurrencyEqualityCondition } from '@/utils/helpers/currencies'; @@ -127,6 +129,14 @@ const VaultRedeemRequestsTable = ({ vaultAddress, collateralToken }: Props): JSX return <>{formatDateTimePrecise(new Date(redeem.request.timestamp))}; } }, + { + Header: t('view_details'), + classNames: ['text-left'], + // TODO: should type properly (`Relay`) + Cell: ({ row: { original: redeem } }: any) => { + return ; + } + }, { Header: t('vault.creation_block'), classNames: ['text-right'], diff --git a/src/pages/Vaults/Vault/components/InsightsList/InsightsList.tsx b/src/pages/Vaults/Vault/components/InsightsList/InsightsList.tsx index 4869eec1cc..ef842f9248 100644 --- a/src/pages/Vaults/Vault/components/InsightsList/InsightsList.tsx +++ b/src/pages/Vaults/Vault/components/InsightsList/InsightsList.tsx @@ -1,4 +1,6 @@ -import { HTMLAttributes, ReactNode } from 'react'; +import { ReactNode } from 'react'; + +import { CardProps } from '@/component-library'; import { InsightItemLabel, @@ -17,12 +19,11 @@ type InsightListItem = { }; type Props = { - direction?: 'row' | 'column'; items: InsightListItem[]; title?: ReactNode; }; -type NativeAttrs = Omit, keyof Props>; +type NativeAttrs = Omit; type InsightsListProps = Props & NativeAttrs; diff --git a/src/pages/Vaults/Vault/components/IssueRedeemForm/IssueRedeemForm.tsx b/src/pages/Vaults/Vault/components/IssueRedeemForm/IssueRedeemForm.tsx index 469c04c336..3ff0628290 100644 --- a/src/pages/Vaults/Vault/components/IssueRedeemForm/IssueRedeemForm.tsx +++ b/src/pages/Vaults/Vault/components/IssueRedeemForm/IssueRedeemForm.tsx @@ -16,6 +16,7 @@ import { displayMonetaryAmountInUSDFormat } from '@/common/utils/utils'; import { CTA, Input, Stack, TokenInput } from '@/component-library'; +import { DEFAULT_ISSUE_BRIDGE_FEE_RATE, DEFAULT_ISSUE_GRIEFING_COLLATERAL_RATE } from '@/config/parachain'; import { GOVERNANCE_TOKEN, GOVERNANCE_TOKEN_SYMBOL, @@ -80,12 +81,12 @@ const IssueRedeemForm = ({ // const [status, setStatus] = useState(STATUSES.IDLE); // const [vaultCapacity, setVaultCapacity] = useState(BitcoinAmount.zero()); - const [feeRate, setFeeRate] = useState(new Big(0.005)); // Set default to 0.5% - const [depositRate, setDepositRate] = useState(new Big(0.00005)); // Set default to 0.005% + const [issueFeeRate, setIssueFeeRate] = useState(new Big(DEFAULT_ISSUE_BRIDGE_FEE_RATE)); + const [depositRate, setDepositRate] = useState(new Big(DEFAULT_ISSUE_GRIEFING_COLLATERAL_RATE)); const [btcToGovernanceTokenRate, setBTCToGovernanceTokenRate] = useState( new ExchangeRate(Bitcoin, GOVERNANCE_TOKEN, new Big(0)) ); - // const [dustValue, setDustValue] = useState(BitcoinAmount.zero()); + // const [dustValue, setDustValue] = useState(new BitcoinAmount(DEFAULT_ISSUE_DUST_AMOUNT)); // const [submitStatus, setSubmitStatus] = useState(STATUSES.IDLE); // const [submitError, setSubmitError] = useState(null); // const [submittedRequest, setSubmittedRequest] = useState(); @@ -113,11 +114,11 @@ const IssueRedeemForm = ({ try { // setStatus(STATUSES.PENDING); const [ - theFeeRate, - theDepositRate, - theDustValue, - theBtcToGovernanceToken, - issuableAmount + feeRateResult, + depositRateResult, + dustValueResult, + btcToGovernanceTokenResult, + vaultIssuableAmountResult ] = await Promise.allSettled([ // Loading this data is not strictly required as long as the constantly set values did // not change. However, you will not see the correct value for the security deposit. @@ -129,20 +130,20 @@ const IssueRedeemForm = ({ window.bridge.issue.getVaultIssuableAmount(vaultAccountId, collateralToken) ]); // setStatus(STATUSES.RESOLVED); - if (theFeeRate.status === 'fulfilled') { - setFeeRate(theFeeRate.value); + if (feeRateResult.status === 'fulfilled') { + setIssueFeeRate(feeRateResult.value); } - if (theDepositRate.status === 'fulfilled') { - setDepositRate(theDepositRate.value); + if (depositRateResult.status === 'fulfilled') { + setDepositRate(depositRateResult.value); } - if (theDustValue.status === 'fulfilled') { + if (dustValueResult.status === 'fulfilled') { // setDustValue(theDustValue.value); } - if (issuableAmount.status === 'fulfilled') { - // setVaultCapacity(issuableAmount.value); + if (vaultIssuableAmountResult.status === 'fulfilled') { + // setVaultCapacity(vaultIssuableAmountResult.value); } - if (theBtcToGovernanceToken.status === 'fulfilled') { - setBTCToGovernanceTokenRate(theBtcToGovernanceToken.value); + if (btcToGovernanceTokenResult.status === 'fulfilled') { + setBTCToGovernanceTokenRate(btcToGovernanceTokenResult.value); } else { setError(tokenInputId, { type: 'validate', @@ -170,7 +171,7 @@ const IssueRedeemForm = ({ }; const parsedBTCAmount = new BitcoinAmount(inputBTCAmount); - const bridgeFee = parsedBTCAmount.mul(feeRate); + const bridgeFee = parsedBTCAmount.mul(issueFeeRate); const securityDeposit = btcToGovernanceTokenRate.toCounter(parsedBTCAmount).mul(depositRate); const wrappedTokenAmount = parsedBTCAmount.sub(bridgeFee); diff --git a/src/pages/Vaults/Vault/components/Rewards/Rewards.tsx b/src/pages/Vaults/Vault/components/Rewards/Rewards.tsx index ee6cc3bbfe..a522b8a291 100644 --- a/src/pages/Vaults/Vault/components/Rewards/Rewards.tsx +++ b/src/pages/Vaults/Vault/components/Rewards/Rewards.tsx @@ -1,13 +1,13 @@ import { CollateralCurrencyExt, newVaultId, WrappedCurrency, WrappedIdLiteral } from '@interlay/interbtc-api'; import Big from 'big.js'; -import { HTMLAttributes } from 'react'; import { useMutation, useQueryClient } from 'react-query'; import { toast } from 'react-toastify'; import { formatNumber, formatUSD } from '@/common/utils/utils'; +import { CardProps } from '@/component-library'; import { LoadingSpinner } from '@/component-library/LoadingSpinner'; -import ErrorModal from '@/components/ErrorModal'; import { GOVERNANCE_TOKEN_SYMBOL, WRAPPED_TOKEN } from '@/config/relay-chains'; +import ErrorModal from '@/legacy-components/ErrorModal'; import { ZERO_GOVERNANCE_TOKEN_AMOUNT } from '@/utils/constants/currency'; import { VaultData } from '@/utils/hooks/api/vaults/get-vault-data'; import useAccountId from '@/utils/hooks/use-account-id'; @@ -30,9 +30,9 @@ type Props = { hasWithdrawRewardsBtn: boolean; }; -type NativeAttrs = HTMLAttributes; +type InheritAttrs = Omit; -type RewardsProps = Props & NativeAttrs; +type RewardsProps = Props & InheritAttrs; const Rewards = ({ vaultAddress, diff --git a/src/pages/Vaults/Vault/components/TransactionHistory/TransactionHistory.tsx b/src/pages/Vaults/Vault/components/TransactionHistory/TransactionHistory.tsx index efd15c70aa..d06896ca1e 100644 --- a/src/pages/Vaults/Vault/components/TransactionHistory/TransactionHistory.tsx +++ b/src/pages/Vaults/Vault/components/TransactionHistory/TransactionHistory.tsx @@ -1,8 +1,8 @@ import { useId } from '@react-aria/utils'; -import { HTMLAttributes, useEffect, useState } from 'react'; +import { useEffect, useState } from 'react'; import { useTranslation } from 'react-i18next'; -import { TabsItem } from '@/component-library'; +import { CardProps, TabsItem } from '@/component-library'; import { PAGES } from '@/utils/constants/links'; import { @@ -19,9 +19,9 @@ type Props = { transactions: Array; }; -type NativeAttrs = HTMLAttributes; +type InheritAttrs = Omit; -type TransactionHistoryProps = Props & NativeAttrs; +type TransactionHistoryProps = Props & InheritAttrs; const tabKeys = ['all', 'pending', 'issue', 'redeem', 'replace'] as const; diff --git a/src/pages/Vaults/Vault/components/VaultCollateral/VaultCollateral.tsx b/src/pages/Vaults/Vault/components/VaultCollateral/VaultCollateral.tsx index 02e26cce7c..ec335ec6e5 100644 --- a/src/pages/Vaults/Vault/components/VaultCollateral/VaultCollateral.tsx +++ b/src/pages/Vaults/Vault/components/VaultCollateral/VaultCollateral.tsx @@ -1,9 +1,9 @@ import { CollateralCurrencyExt, CurrencyExt } from '@interlay/interbtc-api'; import { BitcoinAmount, MonetaryAmount } from '@interlay/monetary-js'; import Big from 'big.js'; -import { HTMLAttributes, useMemo, useState } from 'react'; +import { useMemo, useState } from 'react'; -import { CTA, MeterRanges } from '@/component-library'; +import { CardProps, CTA, MeterRanges } from '@/component-library'; import RequestIssueModal from '@/pages/Vaults/Vault/RequestIssueModal'; import RequestRedeemModal from '@/pages/Vaults/Vault/RequestRedeemModal'; import UpdateCollateralModal, { CollateralUpdateStatus } from '@/pages/Vaults/Vault/UpdateCollateralModal'; @@ -52,9 +52,9 @@ type Props = { wrappedId: string; }; -type NativeAttrs = Omit, keyof Props>; +type InheritAttrs = Omit; -type VaultCollateralProps = Props & NativeAttrs; +type VaultCollateralProps = Props & InheritAttrs; // TODO: handling props as a single property bypasses some type errors const VaultCollateral = ({ diff --git a/src/pages/Vaults/Vault/components/VaultInfo/VaultInfo.tsx b/src/pages/Vaults/Vault/components/VaultInfo/VaultInfo.tsx index 860525f141..645a827925 100644 --- a/src/pages/Vaults/Vault/components/VaultInfo/VaultInfo.tsx +++ b/src/pages/Vaults/Vault/components/VaultInfo/VaultInfo.tsx @@ -1,11 +1,11 @@ import { CollateralCurrencyExt, VaultStatusExt } from '@interlay/interbtc-api'; import { BitcoinAmount, MonetaryAmount } from '@interlay/monetary-js'; import Big from 'big.js'; -import React, { HTMLAttributes, useState } from 'react'; +import React, { useState } from 'react'; -import { CTA, Dd, Dt } from '@/component-library'; +import { CardProps, CTA, Dd, Dt } from '@/component-library'; import { Status } from '@/component-library/utils/prop-types'; -import AddressWithCopyUI from '@/components/AddressWithCopyUI'; +import AddressWithCopyUI from '@/legacy-components/AddressWithCopyUI'; import RequestReplacementModal from '@/pages/Vaults/Vault/RequestReplacementModal'; import { StatusTag } from '../StatusTag'; @@ -39,9 +39,9 @@ type Props = { hasManageVaultBtn: boolean; }; -type NativeAttrs = Omit, keyof Props>; +type InheritAttrs = Omit; -type VaultInfoProps = Props & NativeAttrs; +type VaultInfoProps = Props & InheritAttrs; const VaultInfo = ({ vaultStatus, diff --git a/src/pages/Vaults/Vaults.tsx b/src/pages/Vaults/Vaults.tsx index 78d38d3714..c05fc25e24 100644 --- a/src/pages/Vaults/Vaults.tsx +++ b/src/pages/Vaults/Vaults.tsx @@ -1,6 +1,6 @@ import { withErrorBoundary } from 'react-error-boundary'; -import ErrorFallback from '@/components/ErrorFallback'; +import ErrorFallback from '@/legacy-components/ErrorFallback'; import VaultsOverview from './VaultsOverview'; diff --git a/src/pages/Vaults/VaultsOverview/VaultsOverview.tsx b/src/pages/Vaults/VaultsOverview/VaultsOverview.tsx index a2c3e02d70..271a0f3279 100644 --- a/src/pages/Vaults/VaultsOverview/VaultsOverview.tsx +++ b/src/pages/Vaults/VaultsOverview/VaultsOverview.tsx @@ -5,15 +5,15 @@ import { useParams } from 'react-router-dom'; import { StoreType } from '@/common/types/util.types'; import { formatNumber, formatPercentage, formatUSD } from '@/common/utils/utils'; -import { Grid, GridItem, InfoBox, VaultCard } from '@/component-library'; -import ErrorFallback from '@/components/ErrorFallback'; -import PrimaryColorEllipsisLoader from '@/components/PrimaryColorEllipsisLoader'; +import { Grid, GridItem } from '@/component-library'; +import ErrorFallback from '@/legacy-components/ErrorFallback'; +import PrimaryColorEllipsisLoader from '@/legacy-components/PrimaryColorEllipsisLoader'; import { useSubstrateSecureState } from '@/lib/substrate'; import MainContainer from '@/parts/MainContainer'; import { URL_PARAMETERS } from '@/utils/constants/links'; import { useGetVaultData } from '@/utils/hooks/api/vaults/use-get-vault-data'; -import { CreateVaults, VaultsHeader } from './components'; +import { CreateVaults, InfoBox, VaultCard, VaultsHeader } from './components'; const VaultOverview = (): JSX.Element => { const { [URL_PARAMETERS.VAULT.ACCOUNT]: accountAddress } = useParams>(); diff --git a/src/pages/Vaults/VaultsOverview/components/CreateVaultWizard/DespositCollateralStep.tsx b/src/pages/Vaults/VaultsOverview/components/CreateVaultWizard/DespositCollateralStep.tsx index dc5d2007bb..b7df987d1c 100644 --- a/src/pages/Vaults/VaultsOverview/components/CreateVaultWizard/DespositCollateralStep.tsx +++ b/src/pages/Vaults/VaultsOverview/components/CreateVaultWizard/DespositCollateralStep.tsx @@ -1,27 +1,25 @@ -import { zodResolver } from '@hookform/resolvers/zod'; import { CollateralCurrencyExt, newMonetaryAmount } from '@interlay/interbtc-api'; import { MonetaryAmount } from '@interlay/monetary-js'; import { useId } from '@react-aria/utils'; -import { useForm } from 'react-hook-form'; import { useTranslation } from 'react-i18next'; import { useMutation } from 'react-query'; -import * as z from 'zod'; -import { convertMonetaryAmountToValueInUSD } from '@/common/utils/utils'; +import { convertMonetaryAmountToValueInUSD, newSafeMonetaryAmount } from '@/common/utils/utils'; import { CTA, ModalBody, ModalDivider, ModalFooter, ModalHeader, Span, Stack, TokenInput } from '@/component-library'; -import ErrorModal from '@/components/ErrorModal'; import { GOVERNANCE_TOKEN } from '@/config/relay-chains'; -import validate, { VaultDepositSchemaParams } from '@/lib/form-validation'; -import { getErrorMessage, isValidForm } from '@/utils/helpers/forms'; +import ErrorModal from '@/legacy-components/ErrorModal'; +import { + CREATE_VAULT_DEPOSIT_FIELD, + CreateVaultFormData, + createVaultSchema, + isFormDisabled, + useForm +} from '@/lib/form'; import { useDepositCollateral } from '../../utils/use-deposit-collateral'; import { StyledDd, StyledDItem, StyledDl, StyledDt, StyledHr } from './CreateVaultWizard.styles'; import { StepComponentProps, withStep } from './Step'; -const DEPOSIT_COLLATERAL_AMOUNT = 'deposit-collateral-amount'; - -type CollateralFormData = { [DEPOSIT_COLLATERAL_AMOUNT]: string }; - type Props = { collateralCurrency: CollateralCurrencyExt; minCollateralAmount: MonetaryAmount; @@ -39,24 +37,24 @@ const DepositCollateralStep = ({ const { t } = useTranslation(); const { collateral, fee, governance } = useDepositCollateral(collateralCurrency, minCollateralAmount); - const validationParams: VaultDepositSchemaParams = { + const validationParams = { minAmount: collateral.min.raw, - availableBalance: collateral.balance.raw, + maxAmount: collateral.balance.raw, governanceBalance: governance.raw, transactionFee: fee.raw }; - const schema = z.object({ - [DEPOSIT_COLLATERAL_AMOUNT]: validate.vaults.deposit(t, validationParams) - }); - const { - register, - handleSubmit: h, - watch, - formState: { errors, isDirty } - } = useForm({ - mode: 'onChange', - resolver: zodResolver(schema) + const handleSubmit = (data: CreateVaultFormData) => { + if (!data.deposit) return; + + const amount = newMonetaryAmount(data.deposit || 0, collateral.currency, true); + registerNewVaultMutation.mutate(amount); + }; + + const form = useForm({ + initialValues: { deposit: undefined }, + validationSchema: createVaultSchema(validationParams), + onSubmit: handleSubmit }); const registerNewVaultMutation = useMutation>( @@ -66,21 +64,15 @@ const DepositCollateralStep = ({ } ); - const inputCollateral = watch(DEPOSIT_COLLATERAL_AMOUNT) || '0'; - const inputCollateralAmount = newMonetaryAmount(inputCollateral, collateral.currency, true); - - const handleSubmit = async (data: CollateralFormData) => { - const amount = newMonetaryAmount(data[DEPOSIT_COLLATERAL_AMOUNT], collateral.currency, true); - registerNewVaultMutation.mutate(amount); - }; + const inputCollateralAmount = newSafeMonetaryAmount(form.values.deposit || 0, collateral.currency, true); - const isBtnDisabled = !isValidForm(errors) || !isDirty; + const isBtnDisabled = isFormDisabled(form); return ( <> {t('vault.deposit_collateral')} - - + + diff --git a/src/pages/Vaults/VaultsOverview/components/CreateVaultWizard/DisclaimerStep.tsx b/src/pages/Vaults/VaultsOverview/components/CreateVaultWizard/DisclaimerStep.tsx index 1e7a667680..0eea01dcfe 100644 --- a/src/pages/Vaults/VaultsOverview/components/CreateVaultWizard/DisclaimerStep.tsx +++ b/src/pages/Vaults/VaultsOverview/components/CreateVaultWizard/DisclaimerStep.tsx @@ -28,7 +28,7 @@ const DisclaimerStep = ({ onClickAgree }: DisclaimerStepProps): JSX.Element => { return ( <> - + {t('vault.disclaimer.plase_read_before_you_start')} diff --git a/src/component-library/InfoBox/InfoBox.style.tsx b/src/pages/Vaults/VaultsOverview/components/InfoBox/InfoBox.style.tsx similarity index 94% rename from src/component-library/InfoBox/InfoBox.style.tsx rename to src/pages/Vaults/VaultsOverview/components/InfoBox/InfoBox.style.tsx index aea0acc8a2..133df64e8d 100644 --- a/src/component-library/InfoBox/InfoBox.style.tsx +++ b/src/pages/Vaults/VaultsOverview/components/InfoBox/InfoBox.style.tsx @@ -1,6 +1,6 @@ import styled from 'styled-components'; -import { theme } from '../theme'; +import { theme } from '@/component-library'; const InfoBoxWrapper = styled.div` align-items: center; diff --git a/src/component-library/InfoBox/InfoBox.tsx b/src/pages/Vaults/VaultsOverview/components/InfoBox/InfoBox.tsx similarity index 93% rename from src/component-library/InfoBox/InfoBox.tsx rename to src/pages/Vaults/VaultsOverview/components/InfoBox/InfoBox.tsx index 59fa814da0..9d1ba69c7a 100644 --- a/src/component-library/InfoBox/InfoBox.tsx +++ b/src/pages/Vaults/VaultsOverview/components/InfoBox/InfoBox.tsx @@ -1,4 +1,5 @@ -import { CTA } from '../CTA'; +import { CTA } from '@/component-library'; + import { InfoBoxHeader, InfoBoxText, InfoBoxWrapper } from './InfoBox.style'; interface InfoBoxProps { diff --git a/src/component-library/InfoBox/index.tsx b/src/pages/Vaults/VaultsOverview/components/InfoBox/index.tsx similarity index 100% rename from src/component-library/InfoBox/index.tsx rename to src/pages/Vaults/VaultsOverview/components/InfoBox/index.tsx diff --git a/src/component-library/VaultCard/VaultCard.style.tsx b/src/pages/Vaults/VaultsOverview/components/VaultCard/VaultCard.style.tsx similarity index 97% rename from src/component-library/VaultCard/VaultCard.style.tsx rename to src/pages/Vaults/VaultsOverview/components/VaultCard/VaultCard.style.tsx index 8b10db67bf..e0f83f0a57 100644 --- a/src/component-library/VaultCard/VaultCard.style.tsx +++ b/src/pages/Vaults/VaultsOverview/components/VaultCard/VaultCard.style.tsx @@ -1,6 +1,6 @@ import styled from 'styled-components'; -import { theme } from '../theme'; +import { theme } from '@/component-library'; interface CollateralScoreProps { $atRisk: boolean; diff --git a/src/component-library/VaultCard/VaultCard.tsx b/src/pages/Vaults/VaultsOverview/components/VaultCard/VaultCard.tsx similarity index 93% rename from src/component-library/VaultCard/VaultCard.tsx rename to src/pages/Vaults/VaultsOverview/components/VaultCard/VaultCard.tsx index 7d152024fd..5fb55f63d3 100644 --- a/src/component-library/VaultCard/VaultCard.tsx +++ b/src/pages/Vaults/VaultsOverview/components/VaultCard/VaultCard.tsx @@ -1,6 +1,5 @@ -import { CoinPair } from '../CoinPair'; -import { CTALink } from '../CTA'; -import { Tokens } from '../types'; +import { CoinPair, CTALink, Tokens } from '@/component-library'; + import { Card, CardBody, diff --git a/src/component-library/VaultCard/index.tsx b/src/pages/Vaults/VaultsOverview/components/VaultCard/index.tsx similarity index 100% rename from src/component-library/VaultCard/index.tsx rename to src/pages/Vaults/VaultsOverview/components/VaultCard/index.tsx diff --git a/src/pages/Vaults/VaultsOverview/components/VaultsHeader/index.tsx b/src/pages/Vaults/VaultsOverview/components/VaultsHeader/index.tsx index 1d501e1ec2..0e3e198f74 100644 --- a/src/pages/Vaults/VaultsOverview/components/VaultsHeader/index.tsx +++ b/src/pages/Vaults/VaultsOverview/components/VaultsHeader/index.tsx @@ -1,4 +1,4 @@ -import BoldParagraph from '@/components/BoldParagraph'; +import BoldParagraph from '@/legacy-components/BoldParagraph'; import PageTitle from '@/parts/PageTitle'; import TimerIncrement from '@/parts/TimerIncrement'; diff --git a/src/pages/Vaults/VaultsOverview/components/index.tsx b/src/pages/Vaults/VaultsOverview/components/index.tsx index 5495bbb230..ca076b71fe 100644 --- a/src/pages/Vaults/VaultsOverview/components/index.tsx +++ b/src/pages/Vaults/VaultsOverview/components/index.tsx @@ -1,5 +1,7 @@ import { CreateVaults, CreateVaultsProps } from './CreateVaults'; +import { InfoBox, InfoBoxProps } from './InfoBox'; +import { VaultCard, VaultCardProps } from './VaultCard'; import { VaultsHeader } from './VaultsHeader'; -export { CreateVaults, VaultsHeader }; -export type { CreateVaultsProps }; +export { CreateVaults, InfoBox, VaultCard, VaultsHeader }; +export type { CreateVaultsProps, InfoBoxProps, VaultCardProps }; diff --git a/src/pages/Wallet/WalletOverview/WalletOverview.tsx b/src/pages/Wallet/WalletOverview/WalletOverview.tsx new file mode 100644 index 0000000000..5506cf3f7e --- /dev/null +++ b/src/pages/Wallet/WalletOverview/WalletOverview.tsx @@ -0,0 +1,57 @@ +import { LoanPositionsTable, PoolsTable } from '@/components'; +import FullLoadingSpinner from '@/legacy-components/FullLoadingSpinner'; +import MainContainer from '@/parts/MainContainer'; +import { getPooledTickers } from '@/utils/helpers/pools'; +import { useGetAccountPools } from '@/utils/hooks/api/amm/use-get-account-pools'; +import { useGetLiquidityPools } from '@/utils/hooks/api/amm/use-get-liquidity-pools'; +import { useGetAccountStakingData } from '@/utils/hooks/api/escrow/use-get-account-staking-data'; +import { useGetAccountVotingBalance } from '@/utils/hooks/api/escrow/use-get-account-voting-balance'; +import { useGetAccountPositions } from '@/utils/hooks/api/loans/use-get-account-positions'; +import { useGetLoanAssets } from '@/utils/hooks/api/loans/use-get-loan-assets'; +import { useGetBalances } from '@/utils/hooks/api/tokens/use-get-balances'; +import useAccountId from '@/utils/hooks/use-account-id'; + +import { AvailableAssetsTable, StakingTable, WalletInsights } from './components'; + +const WalletOverview = (): JSX.Element => { + const accountId = useAccountId(); + const { data: balances } = useGetBalances(); + const { data: accountPoolsData } = useGetAccountPools(); + const { data: liquidityPools } = useGetLiquidityPools(); + const { data: accountStakingData } = useGetAccountStakingData(); + const { data: accountVotingBalance } = useGetAccountVotingBalance(); + const { + data: { borrowPositions, lendPositions } + } = useGetAccountPositions(); + const { data: assets } = useGetLoanAssets(); + + if (accountId !== undefined && !balances) { + return ; + } + + const hasStakingTable = + accountStakingData && + accountVotingBalance && + (!accountStakingData?.balance.isZero() || !accountVotingBalance?.isZero()); + + const pooledTickers = liquidityPools && getPooledTickers(liquidityPools); + + return ( + + + + {!!lendPositions?.length && assets && ( + + )} + {!!borrowPositions?.length && assets && ( + + )} + {!!accountPoolsData?.positions.length && ( + + )} + {hasStakingTable && } + + ); +}; + +export default WalletOverview; diff --git a/src/pages/Wallet/WalletOverview/components/AvailableAssetsTable/ActionsCell.tsx b/src/pages/Wallet/WalletOverview/components/AvailableAssetsTable/ActionsCell.tsx new file mode 100644 index 0000000000..ca103cb82d --- /dev/null +++ b/src/pages/Wallet/WalletOverview/components/AvailableAssetsTable/ActionsCell.tsx @@ -0,0 +1,131 @@ +import { CurrencyExt } from '@interlay/interbtc-api'; +import { useTranslation } from 'react-i18next'; +import { useMutation } from 'react-query'; +import { useDispatch } from 'react-redux'; +import { toast } from 'react-toastify'; + +import { showBuyModal } from '@/common/actions/general.actions'; +import { CTA, CTALink, CTAProps, Divider, Flex, theme } from '@/component-library'; +import { useMediaQuery } from '@/component-library/utils/use-media-query'; +import { WRAPPED_TOKEN } from '@/config/relay-chains'; +import { PAGES, QUERY_PARAMETERS } from '@/utils/constants/links'; + +const queryString = require('query-string'); + +const claimVesting = async () => { + await window.bridge.api.tx.vesting.claim(); +}; + +type ActionsCellProps = { + currency: CurrencyExt; + isWrappedToken: boolean; + isRedeemable: boolean; + isPooledAsset: boolean; + isGovernanceToken: boolean; + isVestingClaimable: boolean; +}; + +const ActionsCell = ({ + currency, + isGovernanceToken, + isPooledAsset, + isRedeemable, + isVestingClaimable, + isWrappedToken +}: ActionsCellProps): JSX.Element | null => { + const { t } = useTranslation(); + const dispatch = useDispatch(); + + const isMobile = useMediaQuery(theme.breakpoints.down('md')); + const isSmallMobile = useMediaQuery(theme.breakpoints.down('sm')); + + const handleClaimVestingSuccess = () => { + toast.success('Successfully claimed vesting'); + }; + + const handleClaimVestingError = (error: Error) => { + toast.success(error); + }; + + const claimVestingMutation = useMutation(claimVesting, { + onSuccess: handleClaimVestingSuccess, + onError: handleClaimVestingError + }); + + const handlePressClaimVesting = () => claimVestingMutation.mutate(); + + const handlePressBuyGovernance = () => dispatch(showBuyModal(true)); + + const commonCTAProps: CTAProps = { + fullWidth: isMobile, + variant: 'outlined', + size: isMobile && !isSmallMobile ? 'medium' : 'small' + }; + + return ( + + {isMobile && } + + {isWrappedToken && ( + + {t('issue')} + + )} + {isRedeemable && ( + + {t('redeem')} + + )} + {/* TODO: add when xcm re-vamp is added */} + {/* + {t('transfer')} + */} + {isPooledAsset && ( + + {t('amm.swap')} + + )} + {isGovernanceToken && ( + <> + + Buy + + {isVestingClaimable && ( + + Claim vesting + + )} + + )} + + + ); +}; + +export { ActionsCell }; +export type { ActionsCellProps }; diff --git a/src/pages/Wallet/WalletOverview/components/AvailableAssetsTable/AvailableAssetsTable.tsx b/src/pages/Wallet/WalletOverview/components/AvailableAssetsTable/AvailableAssetsTable.tsx new file mode 100644 index 0000000000..923a7eeec2 --- /dev/null +++ b/src/pages/Wallet/WalletOverview/components/AvailableAssetsTable/AvailableAssetsTable.tsx @@ -0,0 +1,130 @@ +import { isCurrencyEqual } from '@interlay/interbtc-api'; +import { ReactNode, useMemo, useState } from 'react'; +import { useTranslation } from 'react-i18next'; + +import { convertMonetaryAmountToValueInUSD, formatUSD } from '@/common/utils/utils'; +import { P, Switch, theme } from '@/component-library'; +import { useMediaQuery } from '@/component-library/utils/use-media-query'; +import { Cell } from '@/components'; +import { AssetCell, DataGrid } from '@/components/DataGrid'; +import { GOVERNANCE_TOKEN, WRAPPED_TOKEN } from '@/config/relay-chains'; +import { getCoinIconProps } from '@/utils/helpers/coin-icon'; +import { getTokenPrice } from '@/utils/helpers/prices'; +import { BalanceData } from '@/utils/hooks/api/tokens/use-get-balances'; +import { useGetPrices } from '@/utils/hooks/api/use-get-prices'; +import { useGetVestingData } from '@/utils/hooks/api/use-get-vesting-data'; + +import { ActionsCell } from './ActionsCell'; + +enum AvailableAssetsColumns { + ASSET = 'asset', + PRICE = 'price', + BALANCE = 'balance', + ACTIONS = 'actions' +} + +type AvailableAssetsRows = { + id: string; + [AvailableAssetsColumns.ASSET]: ReactNode; + [AvailableAssetsColumns.PRICE]: ReactNode; + [AvailableAssetsColumns.BALANCE]: ReactNode; + [AvailableAssetsColumns.ACTIONS]: ReactNode; +}; + +type AvailableAssetsTableProps = { + balances?: BalanceData; + pooledTickers?: Set; +}; + +const AvailableAssetsTable = ({ balances, pooledTickers }: AvailableAssetsTableProps): JSX.Element => { + const { t } = useTranslation(); + const prices = useGetPrices(); + const { data: vestingData } = useGetVestingData(); + const isMobile = useMediaQuery(theme.breakpoints.down('md')); + + const [showZeroBalances, setShowZeroBalances] = useState(false); + + const rows: AvailableAssetsRows[] = useMemo(() => { + const data = balances ? Object.values(balances) : []; + const filteredData = showZeroBalances ? data : data.filter((balance) => !balance.transferable.isZero()); + + return filteredData.map( + ({ currency, transferable }): AvailableAssetsRows => { + const asset = ( + + ); + + const tokenPrice = getTokenPrice(prices, currency.ticker)?.usd || 0; + + const assetPriceLabel = formatUSD(getTokenPrice(prices, currency.ticker)?.usd || 0, { compact: true }); + const price = ; + + const balanceLabel = transferable.toString(); + const balanceSublabel = formatUSD(convertMonetaryAmountToValueInUSD(transferable, tokenPrice) || 0, { + compact: true + }); + const balance = ( + + ); + + const isWrappedToken = isCurrencyEqual(currency, WRAPPED_TOKEN); + const isRedeemable = isWrappedToken && !transferable.isZero(); + const isPooledAsset = !!pooledTickers?.has(currency.ticker); + const isGovernanceToken = isCurrencyEqual(currency, GOVERNANCE_TOKEN); + const isVestingClaimable = isGovernanceToken && !!vestingData?.isClaimable; + + const hasActions = isRedeemable || isPooledAsset || isVestingClaimable; + + const actions = hasActions ? ( + + ) : null; + + return { + id: currency.ticker, + asset, + price, + balance, + actions + }; + } + ); + }, [balances, showZeroBalances, isMobile, prices, pooledTickers, vestingData?.isClaimable]); + + const actions = ( + setShowZeroBalances(e.target.checked)}> + {t('show_zero_balance')} + + ); + + const columns = [ + { name: isMobile ? '' : t('asset'), uid: AvailableAssetsColumns.ASSET }, + { name: t('price'), uid: AvailableAssetsColumns.PRICE }, + { name: t('balance'), uid: AvailableAssetsColumns.BALANCE }, + { name: '', uid: AvailableAssetsColumns.ACTIONS } + ]; + + return ( + {t('wallet.no_assets_available')}

} + /> + ); +}; + +export { AvailableAssetsTable }; +export type { AvailableAssetsTableProps }; diff --git a/src/pages/Wallet/WalletOverview/components/AvailableAssetsTable/index.tsx b/src/pages/Wallet/WalletOverview/components/AvailableAssetsTable/index.tsx new file mode 100644 index 0000000000..1b84d782aa --- /dev/null +++ b/src/pages/Wallet/WalletOverview/components/AvailableAssetsTable/index.tsx @@ -0,0 +1,2 @@ +export type { AvailableAssetsTableProps } from './AvailableAssetsTable'; +export { AvailableAssetsTable } from './AvailableAssetsTable'; diff --git a/src/pages/Wallet/WalletOverview/components/StakingTable/StakingTable.tsx b/src/pages/Wallet/WalletOverview/components/StakingTable/StakingTable.tsx new file mode 100644 index 0000000000..d5fc3e645c --- /dev/null +++ b/src/pages/Wallet/WalletOverview/components/StakingTable/StakingTable.tsx @@ -0,0 +1,94 @@ +import { CurrencyExt } from '@interlay/interbtc-api'; +import { MonetaryAmount } from '@interlay/monetary-js'; +import { useId } from '@react-aria/utils'; +import { differenceInDays, format, formatDistanceToNowStrict } from 'date-fns'; +import { ReactNode, useMemo } from 'react'; +import { useTranslation } from 'react-i18next'; + +import { convertMonetaryAmountToValueInUSD, formatUSD } from '@/common/utils/utils'; +import { CoinIcon, Flex } from '@/component-library'; +import { Cell, Table } from '@/components'; +import { GOVERNANCE_TOKEN, VOTE_GOVERNANCE_TOKEN } from '@/config/relay-chains'; +import { YEAR_MONTH_DAY_PATTERN } from '@/utils/constants/date-time'; +import { getTokenPrice } from '@/utils/helpers/prices'; +import { GetAccountStakingData } from '@/utils/hooks/api/escrow/use-get-account-staking-data'; +import { useGetBalances } from '@/utils/hooks/api/tokens/use-get-balances'; +import { useGetPrices } from '@/utils/hooks/api/use-get-prices'; + +enum StakingTableColumns { + ASSET = 'asset', + UNLOCKS = 'unlocks', + AVAILABLE = 'available', + VOTING_POWER = 'votingPower' +} + +type StakingTableRows = { + id: string; + [StakingTableColumns.ASSET]: ReactNode; + [StakingTableColumns.UNLOCKS]: ReactNode; + [StakingTableColumns.AVAILABLE]: ReactNode; + [StakingTableColumns.VOTING_POWER]: ReactNode; +}; + +type StakingTableProps = { + data: GetAccountStakingData; + votingBalance: MonetaryAmount; +}; + +const StakingTable = ({ data, votingBalance }: StakingTableProps): JSX.Element => { + const { t } = useTranslation(); + const titleId = useId(); + const prices = useGetPrices(); + const { getAvailableBalance } = useGetBalances(); + + const columns = [ + { name: t('wallet.total_governance_locked', { token: GOVERNANCE_TOKEN.ticker }), uid: StakingTableColumns.ASSET }, + { name: t('unlocks'), uid: StakingTableColumns.UNLOCKS }, + { name: t('wallet.available_to_stake'), uid: StakingTableColumns.AVAILABLE }, + { + name: t('wallet.voting_power_governance', { token: VOTE_GOVERNANCE_TOKEN.ticker }), + uid: StakingTableColumns.VOTING_POWER + } + ]; + + const rows = useMemo((): StakingTableRows[] => { + const { balance, unlock } = data; + const stakingBalancePrice = + convertMonetaryAmountToValueInUSD(balance, getTokenPrice(prices, balance.currency.ticker)?.usd) || 0; + + const asset = ( + + + + + ); + + const unlockDateLabel = format(unlock.date, YEAR_MONTH_DAY_PATTERN); + const difference = differenceInDays(unlock.date, new Date()); + const unlockDaysLabel = formatDistanceToNowStrict(unlock.date); + const unlockDaysIndicatorLabel = difference < 0 ? `-${unlockDaysLabel}` : unlockDaysLabel; + const unlocksLabel = `${unlockDateLabel} (${unlockDaysIndicatorLabel})`; + + const unlocks = ; + + const available = ; + + const votingPower = ; + + return [ + { + id: balance.currency.ticker, + asset, + unlocks, + available, + votingPower + } + ]; + // eslint-disable-next-line react-hooks/exhaustive-deps + }, [prices, data, votingBalance]); + + return
; +}; + +export { StakingTable }; +export type { StakingTableProps }; diff --git a/src/pages/Wallet/WalletOverview/components/StakingTable/index.tsx b/src/pages/Wallet/WalletOverview/components/StakingTable/index.tsx new file mode 100644 index 0000000000..67b407a1ac --- /dev/null +++ b/src/pages/Wallet/WalletOverview/components/StakingTable/index.tsx @@ -0,0 +1,2 @@ +export type { StakingTableProps } from './StakingTable'; +export { StakingTable } from './StakingTable'; diff --git a/src/pages/Wallet/WalletOverview/components/WalletInsights/WalletInsights.style.tsx b/src/pages/Wallet/WalletOverview/components/WalletInsights/WalletInsights.style.tsx new file mode 100644 index 0000000000..700a6824d8 --- /dev/null +++ b/src/pages/Wallet/WalletOverview/components/WalletInsights/WalletInsights.style.tsx @@ -0,0 +1,14 @@ +import styled from 'styled-components'; + +import { Dd, Dt, theme } from '@/component-library'; + +const StyledDt = styled(Dt)` + font-weight: ${theme.fontWeight.semibold}; + white-space: nowrap; +`; + +const StyledDd = styled(Dd)` + font-weight: ${theme.fontWeight.bold}; +`; + +export { StyledDd, StyledDt }; diff --git a/src/pages/Wallet/WalletOverview/components/WalletInsights/WalletInsights.tsx b/src/pages/Wallet/WalletOverview/components/WalletInsights/WalletInsights.tsx new file mode 100644 index 0000000000..234b4368db --- /dev/null +++ b/src/pages/Wallet/WalletOverview/components/WalletInsights/WalletInsights.tsx @@ -0,0 +1,85 @@ +import Big from 'big.js'; +import { useTranslation } from 'react-i18next'; + +import { convertMonetaryAmountToValueInUSD, formatUSD } from '@/common/utils/utils'; +import { Card, Dd, Dl, DlGroup, Dt, theme } from '@/component-library'; +import { useMediaQuery } from '@/component-library/utils/use-media-query'; +import { getTokenPrice } from '@/utils/helpers/prices'; +import { BalanceData } from '@/utils/hooks/api/tokens/use-get-balances'; +import { useGetPrices } from '@/utils/hooks/api/use-get-prices'; + +import { WalletMeta } from './WalletMeta'; + +type WalletInsightsProps = { + balances?: BalanceData; +}; + +const WalletInsights = ({ balances }: WalletInsightsProps): JSX.Element => { + const { t } = useTranslation(); + + const prices = useGetPrices(); + const isMobile = useMediaQuery(theme.breakpoints.down('md')); + + const totalBalance = + balances && + Object.values(balances).reduce( + (total, balance) => + total.add( + convertMonetaryAmountToValueInUSD( + balance.free.add(balance.reserved), + getTokenPrice(prices, balance.currency.ticker)?.usd + ) || 0 + ), + new Big(0) + ); + + const totalBalanceLabel = totalBalance ? formatUSD(totalBalance.toNumber(), { compact: true }) : '-'; + + const transfarableBalance = + balances && + Object.values(balances).reduce( + (total, balance) => + total.add( + convertMonetaryAmountToValueInUSD( + balance.transferable, + getTokenPrice(prices, balance.currency.ticker)?.usd + ) || 0 + ), + new Big(0) + ); + + const transfarableBalanceLabel = transfarableBalance + ? formatUSD(transfarableBalance.toNumber(), { compact: true }) + : '-'; + + return ( +
+ + + + + +
+ {t('total_balance')} +
+
+ {totalBalanceLabel} +
+
+
+ + +
+ {t('transferable_balance')} +
+
+ {transfarableBalanceLabel} +
+
+
+
+ ); +}; + +export { WalletInsights }; +export type { WalletInsightsProps }; diff --git a/src/pages/Wallet/WalletOverview/components/WalletInsights/WalletMeta.tsx b/src/pages/Wallet/WalletOverview/components/WalletInsights/WalletMeta.tsx new file mode 100644 index 0000000000..3b79462324 --- /dev/null +++ b/src/pages/Wallet/WalletOverview/components/WalletInsights/WalletMeta.tsx @@ -0,0 +1,85 @@ +import { mergeProps } from '@react-aria/utils'; +import { useDispatch } from 'react-redux'; +import { useCopyToClipboard } from 'react-use'; + +import { ArrowTopRightOnSquare, DocumentDuplicate, PencilSquare } from '@/assets/icons'; +import { showAccountModalAction } from '@/common/actions/general.actions'; +import { shortAddress } from '@/common/utils/utils'; +import { CTA, CTALink, Dd, DlGroup, Dt, Flex, FlexProps, P, Tooltip, WalletIcon } from '@/component-library'; +import { AuthCTA } from '@/components'; +import { SUBSCAN_LINK } from '@/config/relay-chains'; +import { useSubstrateState } from '@/lib/substrate'; +import { useCopyTooltip } from '@/utils/hooks/use-copy-tooltip'; + +type WalletMetaProps = FlexProps; + +const WalletMeta = (props: WalletMetaProps): JSX.Element => { + const { selectedAccount } = useSubstrateState(); + const dispatch = useDispatch(); + + const [, copy] = useCopyToClipboard(); + const { buttonProps, tooltipProps } = useCopyTooltip(); + + if (!selectedAccount) { + return ( + +

No Account Connected

+ Connect Account +
+ ); + } + + const handleCopy = () => copy(selectedAccount.address); + + return ( + + + + + +
+ {selectedAccount.meta.name} +
+
{shortAddress(selectedAccount.address)}
+
+
+ + + + + + + dispatch(showAccountModalAction(true))} + variant='text' + size='x-small' + > + + + + + + +
+ {/* TODO: add back when banxa on-ramp is added */} + {/* + {t('fund_wallet')} + View History + */} +
+ ); +}; + +export { WalletMeta }; diff --git a/src/pages/Wallet/WalletOverview/components/WalletInsights/index.tsx b/src/pages/Wallet/WalletOverview/components/WalletInsights/index.tsx new file mode 100644 index 0000000000..ebc0b4243a --- /dev/null +++ b/src/pages/Wallet/WalletOverview/components/WalletInsights/index.tsx @@ -0,0 +1,2 @@ +export type { WalletInsightsProps } from './WalletInsights'; +export { WalletInsights } from './WalletInsights'; diff --git a/src/pages/Wallet/WalletOverview/components/WalletInsights/utils.ts b/src/pages/Wallet/WalletOverview/components/WalletInsights/utils.ts new file mode 100644 index 0000000000..f5d171d2a3 --- /dev/null +++ b/src/pages/Wallet/WalletOverview/components/WalletInsights/utils.ts @@ -0,0 +1,24 @@ +import { CurrencyExt, LpCurrency } from '@interlay/interbtc-api'; +import { MonetaryAmount } from '@interlay/monetary-js'; + +import { calculateTotalLiquidityUSD } from '@/pages/AMM/shared/utils'; +import { Prices } from '@/utils/hooks/api/use-get-prices'; + +const calculateClaimableFarmingRewardUSD = ( + claimableRewards: Map[]> | undefined, + prices: Prices | undefined +): number => { + if (claimableRewards === undefined) { + return 0; + } + const flattenedRewardAmounts: Array> = []; + for (const [, rewardAmounts] of claimableRewards.entries()) { + flattenedRewardAmounts.push(...rewardAmounts); + } + + const totalRewardUSD = calculateTotalLiquidityUSD(flattenedRewardAmounts, prices); + + return totalRewardUSD; +}; + +export { calculateClaimableFarmingRewardUSD }; diff --git a/src/pages/Wallet/WalletOverview/components/index.tsx b/src/pages/Wallet/WalletOverview/components/index.tsx new file mode 100644 index 0000000000..e4670e5c97 --- /dev/null +++ b/src/pages/Wallet/WalletOverview/components/index.tsx @@ -0,0 +1,6 @@ +import { AvailableAssetsTable, AvailableAssetsTableProps } from './AvailableAssetsTable'; +import { StakingTable, StakingTableProps } from './StakingTable'; +import { WalletInsights, WalletInsightsProps } from './WalletInsights'; + +export { AvailableAssetsTable, StakingTable, WalletInsights }; +export type { AvailableAssetsTableProps, StakingTableProps, WalletInsightsProps }; diff --git a/src/pages/Wallet/WalletOverview/index.tsx b/src/pages/Wallet/WalletOverview/index.tsx new file mode 100644 index 0000000000..ab4e95ed6f --- /dev/null +++ b/src/pages/Wallet/WalletOverview/index.tsx @@ -0,0 +1,3 @@ +import WalletOverview from './WalletOverview'; + +export default WalletOverview; diff --git a/src/pages/Wallet/index.tsx b/src/pages/Wallet/index.tsx new file mode 100644 index 0000000000..ab4e95ed6f --- /dev/null +++ b/src/pages/Wallet/index.tsx @@ -0,0 +1,3 @@ +import WalletOverview from './WalletOverview'; + +export default WalletOverview; diff --git a/src/parts/AccountModal/ModalContent/AccountModalContentWrapper.tsx b/src/parts/AccountModal/ModalContent/AccountModalContentWrapper.tsx index 299b8d4b5e..24f8d34336 100644 --- a/src/parts/AccountModal/ModalContent/AccountModalContentWrapper.tsx +++ b/src/parts/AccountModal/ModalContent/AccountModalContentWrapper.tsx @@ -2,10 +2,10 @@ import clsx from 'clsx'; import * as React from 'react'; import { Trans } from 'react-i18next'; -import CloseIconButton from '@/components/buttons/CloseIconButton'; -import InterlayLink from '@/components/UI/InterlayLink'; -import { InterlayModalTitle } from '@/components/UI/InterlayModal'; import { TERMS_AND_CONDITIONS_LINK } from '@/config/relay-chains'; +import CloseIconButton from '@/legacy-components/buttons/CloseIconButton'; +import InterlayLink from '@/legacy-components/UI/InterlayLink'; +import { InterlayModalTitle } from '@/legacy-components/UI/InterlayModal'; interface AccountContentWrapperProps { title: string; diff --git a/src/parts/AccountModal/ModalContent/ModalContentNoAccountFound.tsx b/src/parts/AccountModal/ModalContent/ModalContentNoAccountFound.tsx index 47a5fc9fff..d067dd4152 100644 --- a/src/parts/AccountModal/ModalContent/ModalContentNoAccountFound.tsx +++ b/src/parts/AccountModal/ModalContent/ModalContentNoAccountFound.tsx @@ -1,7 +1,7 @@ import { useTranslation } from 'react-i18next'; -import ExternalLink from '@/components/ExternalLink'; import { WALLETS, WalletSourceName } from '@/config/wallets'; +import ExternalLink from '@/legacy-components/ExternalLink'; interface ModalContentNoAccountFoundProps { selectedWallet: WalletSourceName; diff --git a/src/parts/AccountModal/ModalContent/ModalContentNoWalletFound.tsx b/src/parts/AccountModal/ModalContent/ModalContentNoWalletFound.tsx index 693a21205f..4532dc3071 100644 --- a/src/parts/AccountModal/ModalContent/ModalContentNoWalletFound.tsx +++ b/src/parts/AccountModal/ModalContent/ModalContentNoWalletFound.tsx @@ -1,9 +1,9 @@ import clsx from 'clsx'; import { useTranslation } from 'react-i18next'; -import ExternalLink from '@/components/ExternalLink'; import { TERMS_AND_CONDITIONS_LINK } from '@/config/relay-chains'; import { WALLETS } from '@/config/wallets'; +import ExternalLink from '@/legacy-components/ExternalLink'; const ModalContentNoWalletFound = (): JSX.Element => { const { t } = useTranslation(); diff --git a/src/parts/AccountModal/ModalContent/ModalContentSelectAccount.tsx b/src/parts/AccountModal/ModalContent/ModalContentSelectAccount.tsx index 908290f97c..e0ca751cc1 100644 --- a/src/parts/AccountModal/ModalContent/ModalContentSelectAccount.tsx +++ b/src/parts/AccountModal/ModalContent/ModalContentSelectAccount.tsx @@ -3,9 +3,9 @@ import clsx from 'clsx'; import { useTranslation } from 'react-i18next'; import { shortAddress } from '@/common/utils/utils'; -import CopyToClipboardButton from '@/components/CopyToClipboardButton'; -import InterlayButtonBase from '@/components/UI/InterlayButtonBase'; import { WALLETS, WalletSourceName } from '@/config/wallets'; +import CopyToClipboardButton from '@/legacy-components/CopyToClipboardButton'; +import InterlayButtonBase from '@/legacy-components/UI/InterlayButtonBase'; import { KUSAMA, POLKADOT } from '@/utils/constants/relay-chain-names'; import { ACCOUNT_MODAL_BUTTON_CLASSES, ACCOUNT_MODAL_BUTTON_SELECTED_CLASSES } from '..'; diff --git a/src/parts/AccountModal/ModalContent/ModalContentSelectWallet.tsx b/src/parts/AccountModal/ModalContent/ModalContentSelectWallet.tsx index aa3da2a5bc..31dc21e7ac 100644 --- a/src/parts/AccountModal/ModalContent/ModalContentSelectWallet.tsx +++ b/src/parts/AccountModal/ModalContent/ModalContentSelectWallet.tsx @@ -1,8 +1,8 @@ import { InjectedExtension } from '@polkadot/extension-inject/types'; import clsx from 'clsx'; -import InterlayButtonBase from '@/components/UI/InterlayButtonBase'; import { WALLETS, WalletSourceName } from '@/config/wallets'; +import InterlayButtonBase from '@/legacy-components/UI/InterlayButtonBase'; import { ACCOUNT_MODAL_BUTTON_CLASSES } from '..'; diff --git a/src/parts/AccountModal/index.tsx b/src/parts/AccountModal/index.tsx index 6320b90c16..d78f9f8e84 100644 --- a/src/parts/AccountModal/index.tsx +++ b/src/parts/AccountModal/index.tsx @@ -4,9 +4,9 @@ import clsx from 'clsx'; import * as React from 'react'; import { useTranslation } from 'react-i18next'; -import InterlayModal, { InterlayModalInnerWrapper } from '@/components/UI/InterlayModal'; import { WalletSourceName } from '@/config/wallets'; import { SS58_FORMAT } from '@/constants'; +import InterlayModal, { InterlayModalInnerWrapper } from '@/legacy-components/UI/InterlayModal'; import { useSubstrate, useSubstrateSecureState } from '@/lib/substrate'; import { KUSAMA, POLKADOT } from '@/utils/constants/relay-chain-names'; diff --git a/src/parts/MaintenanceBanner/index.tsx b/src/parts/MaintenanceBanner/index.tsx index 457fcf1405..b713ef3fdd 100644 --- a/src/parts/MaintenanceBanner/index.tsx +++ b/src/parts/MaintenanceBanner/index.tsx @@ -2,7 +2,7 @@ import clsx from 'clsx'; import { useTranslation } from 'react-i18next'; import { formatDateTime } from '@/common/utils/utils'; -import InterlayLink from '@/components/UI/InterlayLink'; +import InterlayLink from '@/legacy-components/UI/InterlayLink'; interface Message { id: number; diff --git a/src/parts/Sidebar/OpenButton/index.tsx b/src/parts/Sidebar/OpenButton/index.tsx index 74c7ebf74e..50777b0bd0 100644 --- a/src/parts/Sidebar/OpenButton/index.tsx +++ b/src/parts/Sidebar/OpenButton/index.tsx @@ -1,7 +1,7 @@ import { Bars3Icon } from '@heroicons/react/24/outline'; import clsx from 'clsx'; -import InterlayButtonBase, { Props as InterlayButtonBaseProps } from '@/components/UI/InterlayButtonBase'; +import InterlayButtonBase, { Props as InterlayButtonBaseProps } from '@/legacy-components/UI/InterlayButtonBase'; import { KUSAMA, POLKADOT } from '@/utils/constants/relay-chain-names'; const OpenButton = ({ onClick }: InterlayButtonBaseProps): JSX.Element => ( diff --git a/src/parts/Sidebar/SidebarContent/CloseButton/index.tsx b/src/parts/Sidebar/SidebarContent/CloseButton/index.tsx index 6bb227a73f..1249a37e5e 100644 --- a/src/parts/Sidebar/SidebarContent/CloseButton/index.tsx +++ b/src/parts/Sidebar/SidebarContent/CloseButton/index.tsx @@ -3,7 +3,7 @@ import { XCircleIcon } from '@heroicons/react/24/outline'; import clsx from 'clsx'; import * as React from 'react'; -import InterlayButtonBase, { Props as InterlayButtonBaseProps } from '@/components/UI/InterlayButtonBase'; +import InterlayButtonBase, { Props as InterlayButtonBaseProps } from '@/legacy-components/UI/InterlayButtonBase'; type Ref = HTMLButtonElement; const CloseButton = React.forwardRef( diff --git a/src/parts/Sidebar/SidebarContent/Navigation/SidebarNavLink/index.tsx b/src/parts/Sidebar/SidebarContent/Navigation/SidebarNavLink/index.tsx index 7c8fb0f708..0eebbc7bf4 100644 --- a/src/parts/Sidebar/SidebarContent/Navigation/SidebarNavLink/index.tsx +++ b/src/parts/Sidebar/SidebarContent/Navigation/SidebarNavLink/index.tsx @@ -1,8 +1,10 @@ import { ArrowTopRightOnSquareIcon } from '@heroicons/react/24/outline'; import clsx from 'clsx'; -import InterlayLink, { Props as InterlayLinkProps } from '@/components/UI/InterlayLink'; -import InterlayRouterNavLink, { Props as InterlayRouterNavLinkProps } from '@/components/UI/InterlayRouterNavLink'; +import InterlayLink, { Props as InterlayLinkProps } from '@/legacy-components/UI/InterlayLink'; +import InterlayRouterNavLink, { + Props as InterlayRouterNavLinkProps +} from '@/legacy-components/UI/InterlayRouterNavLink'; interface CustomProps { external: boolean; diff --git a/src/parts/Sidebar/SidebarContent/Navigation/index.tsx b/src/parts/Sidebar/SidebarContent/Navigation/index.tsx index dd73b84cd2..83581e5880 100644 --- a/src/parts/Sidebar/SidebarContent/Navigation/index.tsx +++ b/src/parts/Sidebar/SidebarContent/Navigation/index.tsx @@ -11,7 +11,9 @@ import { DocumentTextIcon, HandRaisedIcon, PresentationChartBarIcon, - ScaleIcon + ScaleIcon, + Square3Stack3DIcon, + UserIcon } from '@heroicons/react/24/outline'; import clsx from 'clsx'; import * as React from 'react'; @@ -21,7 +23,6 @@ import { matchPath } from 'react-router'; import { useLocation } from 'react-router-dom'; import { StoreType } from '@/common/types/util.types'; -import Hr2 from '@/components/hrs/Hr2'; import { INTERLAY_DOCS_LINK } from '@/config/links'; import { CROWDLOAN_LINK, @@ -31,6 +32,7 @@ import { USE_WRAPPED_CURRENCY_LINK, WRAPPED_TOKEN_SYMBOL } from '@/config/relay-chains'; +import Hr2 from '@/legacy-components/hrs/Hr2'; import { useSubstrateSecureState } from '@/lib/substrate'; import { PAGES, URL_PARAMETERS } from '@/utils/constants/links'; import { KUSAMA, POLKADOT } from '@/utils/constants/relay-chain-names'; @@ -66,9 +68,17 @@ const Navigation = ({ const { selectedAccount } = useSubstrateSecureState(); const { vaultClientLoaded } = useSelector((state: StoreType) => state.general); const isLendingEnabled = useFeatureFlag(FeatureFlags.LENDING); + const isAMMEnabled = useFeatureFlag(FeatureFlags.AMM); + const isWalletEnabled = useFeatureFlag(FeatureFlags.WALLET); const NAVIGATION_ITEMS = React.useMemo( () => [ + { + name: 'nav_wallet', + link: PAGES.WALLET, + icon: UserIcon, + disabled: !isWalletEnabled + }, { name: 'nav_bridge', link: PAGES.BRIDGE, @@ -88,9 +98,15 @@ const Navigation = ({ }, { name: 'nav_swap', - link: '#', + link: PAGES.SWAP, icon: ArrowPathRoundedSquareIcon, - disabled: true + disabled: !isAMMEnabled + }, + { + name: 'nav_pools', + link: PAGES.POOLS, + icon: Square3Stack3DIcon, + disabled: !isAMMEnabled }, { name: 'nav_transactions', @@ -177,7 +193,7 @@ const Navigation = ({ } } ], - [isLendingEnabled, selectedAccount?.address, vaultClientLoaded] + [isWalletEnabled, isLendingEnabled, isAMMEnabled, selectedAccount?.address, vaultClientLoaded] ); return ( diff --git a/src/parts/Sidebar/SidebarContent/SocialMediaContainer/index.tsx b/src/parts/Sidebar/SidebarContent/SocialMediaContainer/index.tsx index 1fc051ffae..877ccb3438 100644 --- a/src/parts/Sidebar/SidebarContent/SocialMediaContainer/index.tsx +++ b/src/parts/Sidebar/SidebarContent/SocialMediaContainer/index.tsx @@ -1,13 +1,13 @@ import clsx from 'clsx'; import { FaDiscord, FaGithub, FaMailBulk, FaTwitter } from 'react-icons/fa'; -import InterlayLink from '@/components/UI/InterlayLink'; import { INTERLAY_DISCORD_LINK, INTERLAY_EMAIL_LINK, INTERLAY_GITHUB_LINK, INTERLAY_TWITTER_LINK } from '@/config/links'; +import InterlayLink from '@/legacy-components/UI/InterlayLink'; import { KUSAMA, POLKADOT } from '@/utils/constants/relay-chain-names'; const SOCIAL_MEDIA_ITEMS = [ diff --git a/src/parts/Sidebar/SidebarContent/TestnetBadge/index.tsx b/src/parts/Sidebar/SidebarContent/TestnetBadge/index.tsx index 70d8641567..30593427ee 100644 --- a/src/parts/Sidebar/SidebarContent/TestnetBadge/index.tsx +++ b/src/parts/Sidebar/SidebarContent/TestnetBadge/index.tsx @@ -1,8 +1,10 @@ import clsx from 'clsx'; import { useTranslation } from 'react-i18next'; -import InterlayCinnabarBadge, { Props as InterlayCinnabarBadgeProps } from '@/components/badges/InterlayCinnabarBadge'; -import InterlayTooltip from '@/components/UI/InterlayTooltip'; +import InterlayCinnabarBadge, { + Props as InterlayCinnabarBadgeProps +} from '@/legacy-components/badges/InterlayCinnabarBadge'; +import InterlayTooltip from '@/legacy-components/UI/InterlayTooltip'; const TestnetBadge = ({ className, ...rest }: InterlayCinnabarBadgeProps): JSX.Element => { const { t } = useTranslation(); diff --git a/src/parts/Sidebar/SidebarContent/index.tsx b/src/parts/Sidebar/SidebarContent/index.tsx index 724c6dd6cd..738f021d3c 100644 --- a/src/parts/Sidebar/SidebarContent/index.tsx +++ b/src/parts/Sidebar/SidebarContent/index.tsx @@ -2,8 +2,8 @@ import clsx from 'clsx'; import * as React from 'react'; import { useTranslation } from 'react-i18next'; -import InterlayRouterLink from '@/components/UI/InterlayRouterLink'; import { GovernanceTokenLogoWithTextIcon } from '@/config/relay-chains'; +import InterlayRouterLink from '@/legacy-components/UI/InterlayRouterLink'; import { BitcoinNetwork } from '@/types/bitcoin'; import { PAGES } from '@/utils/constants/links'; import { KUSAMA, POLKADOT } from '@/utils/constants/relay-chain-names'; diff --git a/src/parts/Topbar/GetGovernanceTokenUI/index.tsx b/src/parts/Topbar/GetGovernanceTokenUI/index.tsx index 56620285d0..474238bc04 100644 --- a/src/parts/Topbar/GetGovernanceTokenUI/index.tsx +++ b/src/parts/Topbar/GetGovernanceTokenUI/index.tsx @@ -1,6 +1,7 @@ import clsx from 'clsx'; import * as React from 'react'; import { useTranslation } from 'react-i18next'; +import { useDispatch, useSelector } from 'react-redux'; import { ReactComponent as AcalaLogoIcon } from '@/assets/img/exchanges/acala-logo.svg'; import { ReactComponent as GateLogoIcon } from '@/assets/img/exchanges/gate-logo.svg'; @@ -10,13 +11,15 @@ import { ReactComponent as MexcLogoForInterlayIcon } from '@/assets/img/exchange import { ReactComponent as MexcLogoForKintsugiIcon } from '@/assets/img/exchanges/mexc-logo-for-kintsugi.svg'; import { ReactComponent as StellaSwapLogoIcon } from '@/assets/img/exchanges/stellaswap-logo.svg'; import { ReactComponent as ZenlinkLogoIcon } from '@/assets/img/exchanges/zenlink-logo.svg'; +import { showBuyModal } from '@/common/actions/general.actions'; +import { StoreType } from '@/common/types/util.types'; +import { GOVERNANCE_TOKEN_SYMBOL } from '@/config/relay-chains'; import InterlayDefaultOutlinedButton, { Props as InterlayDefaultOutlinedButtonProps -} from '@/components/buttons/InterlayDefaultOutlinedButton'; -import TitleWithUnderline from '@/components/TitleWithUnderline'; -import InterlayLink from '@/components/UI/InterlayLink'; -import InterlayModal, { InterlayModalInnerWrapper } from '@/components/UI/InterlayModal'; -import { GOVERNANCE_TOKEN_SYMBOL } from '@/config/relay-chains'; +} from '@/legacy-components/buttons/InterlayDefaultOutlinedButton'; +import TitleWithUnderline from '@/legacy-components/TitleWithUnderline'; +import InterlayLink from '@/legacy-components/UI/InterlayLink'; +import InterlayModal, { InterlayModalInnerWrapper } from '@/legacy-components/UI/InterlayModal'; import { KUSAMA, POLKADOT } from '@/utils/constants/relay-chain-names'; import { BORDER_CLASSES } from '@/utils/constants/styles'; @@ -94,15 +97,16 @@ const ExchangeLink = ({ href, icon }: ExchangeLinkProps) => { }; const GetGovernanceTokenUI = (props: InterlayDefaultOutlinedButtonProps): JSX.Element => { - const [modalOpen, setModalOpen] = React.useState(false); + const { isBuyModalOpen } = useSelector((state: StoreType) => state.general); const focusRef = React.useRef(null); const { t } = useTranslation(); + const dispatch = useDispatch(); const handleModalOpen = () => { - setModalOpen(true); + dispatch(showBuyModal(true)); }; const handleModalClose = () => { - setModalOpen(false); + dispatch(showBuyModal(false)); }; const getGovernanceTokenLabel = t('get_governance_token', { @@ -118,7 +122,7 @@ const GetGovernanceTokenUI = (props: InterlayDefaultOutlinedButtonProps): JSX.El {getGovernanceTokenLabel} - +
diff --git a/src/parts/Topbar/ManualIssueExecutionActionsBadge/index.tsx b/src/parts/Topbar/ManualIssueExecutionActionsBadge/index.tsx index 31cef59214..c4498ff404 100644 --- a/src/parts/Topbar/ManualIssueExecutionActionsBadge/index.tsx +++ b/src/parts/Topbar/ManualIssueExecutionActionsBadge/index.tsx @@ -1,7 +1,7 @@ import { useErrorHandler, withErrorBoundary } from 'react-error-boundary'; import { CTALink } from '@/component-library'; -import ErrorFallback from '@/components/ErrorFallback'; +import ErrorFallback from '@/legacy-components/ErrorFallback'; import { useManualIssueRequests } from '@/services/hooks/issue-requests'; import { PAGES } from '@/utils/constants/links'; diff --git a/src/parts/Topbar/index.tsx b/src/parts/Topbar/index.tsx index cb7b0bae93..2e11eca46a 100644 --- a/src/parts/Topbar/index.tsx +++ b/src/parts/Topbar/index.tsx @@ -7,16 +7,17 @@ import { toast } from 'react-toastify'; import { showAccountModalAction } from '@/common/actions/general.actions'; import { StoreType } from '@/common/types/util.types'; -import InterlayCaliforniaOutlinedButton from '@/components/buttons/InterlayCaliforniaOutlinedButton'; -import InterlayDefaultContainedButton from '@/components/buttons/InterlayDefaultContainedButton'; -import InterlayDenimOrKintsugiMidnightOutlinedButton from '@/components/buttons/InterlayDenimOrKintsugiMidnightOutlinedButton'; -import Tokens from '@/components/Tokens'; -import InterlayLink from '@/components/UI/InterlayLink'; import { ACCOUNT_ID_TYPE_NAME } from '@/config/general'; import { GOVERNANCE_TOKEN } from '@/config/relay-chains'; +import InterlayCaliforniaOutlinedButton from '@/legacy-components/buttons/InterlayCaliforniaOutlinedButton'; +import InterlayDefaultContainedButton from '@/legacy-components/buttons/InterlayDefaultContainedButton'; +import InterlayDenimOrKintsugiMidnightOutlinedButton from '@/legacy-components/buttons/InterlayDenimOrKintsugiMidnightOutlinedButton'; +import Tokens from '@/legacy-components/Tokens'; +import InterlayLink from '@/legacy-components/UI/InterlayLink'; import { useSubstrateSecureState } from '@/lib/substrate'; import AccountModal from '@/parts/AccountModal'; import { BitcoinNetwork } from '@/types/bitcoin'; +import { useGetBalances } from '@/utils/hooks/api/tokens/use-get-balances'; import GetGovernanceTokenUI from './GetGovernanceTokenUI'; import ManualIssueExecutionActionsBadge from './ManualIssueExecutionActionsBadge'; @@ -27,6 +28,8 @@ const Topbar = (): JSX.Element => { const { bridgeLoaded, showAccountModal } = useSelector((state: StoreType) => state.general); const dispatch = useDispatch(); const { t } = useTranslation(); + const { getAvailableBalance } = useGetBalances(); + const kintBalanceIsZero = getAvailableBalance("KINT")?.isZero(); const handleRequestFromFaucet = async (): Promise => { if (!selectedAccount) return; @@ -79,6 +82,17 @@ const Topbar = (): JSX.Element => { {selectedAccount !== undefined && ( <> + {process.env.REACT_APP_FAUCET_URL && kintBalanceIsZero && ( + <> + + {t('request_funds')} + + + )} {process.env.REACT_APP_BITCOIN_NETWORK !== BitcoinNetwork.Mainnet && ( <> { {t('request_btc')} - - {t('request_funds')} - )} diff --git a/src/store.ts b/src/store.ts index a6e702a45b..68a0f763a2 100644 --- a/src/store.ts +++ b/src/store.ts @@ -1,6 +1,5 @@ import { FaucetClient, InterBtcApi } from '@interlay/interbtc-api'; -import { applyMiddleware, createStore } from 'redux'; -import { createLogger } from 'redux-logger'; +import { createStore } from 'redux'; import { rootReducer } from './common/reducers/index'; @@ -12,7 +11,6 @@ declare global { } } -const storeLogger = createLogger(); -const store = createStore(rootReducer, undefined, applyMiddleware(storeLogger)); +const store = createStore(rootReducer, undefined); export { store }; diff --git a/src/test/mocks/@interlay/interbtc-api/faucet.ts b/src/test/mocks/@interlay/interbtc-api/faucet.ts new file mode 100644 index 0000000000..c68e79a49c --- /dev/null +++ b/src/test/mocks/@interlay/interbtc-api/faucet.ts @@ -0,0 +1,5 @@ +const mockFundAccount = jest.fn(); + +const mockFaucet = jest.fn().mockImplementation(() => ({ fundAccount: mockFundAccount })); + +export { mockFaucet }; diff --git a/src/test/mocks/@interlay/interbtc-api/index.ts b/src/test/mocks/@interlay/interbtc-api/index.ts index 49f922d384..592472d6ce 100644 --- a/src/test/mocks/@interlay/interbtc-api/index.ts +++ b/src/test/mocks/@interlay/interbtc-api/index.ts @@ -5,6 +5,7 @@ import { Interlay, Polkadot } from '@interlay/monetary-js'; import { AddressOrPair } from '@polkadot/api/types'; import { Signer } from '@polkadot/types/types'; +import { mockFaucet } from './faucet'; import { mockApiCreateType, mockBtcRelayGetLatestBlockHeight, @@ -12,6 +13,11 @@ import { mockElectrsAPIGetLatestBlockHeight, mockFeeGetIssueFee, mockFeeGetIssueGriefingCollateralRate, + mockGetCurrentActiveBlockNumber, + mockGetCurrentBlockNumber, + mockGetFutureBlockNumber, + mockGetStableBitcoinConfirmations, + mockGetStableParachainConfirmations, mockIssueGetDustValue, mockIssueGetRequestLimits, mockIssueRequest, @@ -34,8 +40,39 @@ import { mockVaultsGetVaultsWithIssuableTokens, mockVaultsGetVaultsWithRedeemableTokens } from './parachain'; +import { + mockAddLiquidity, + mockClaimFarmingRewards, + mockGetClaimableFarmingRewards, + mockGetLiquidityPools, + mockGetLiquidityProvidedByAccount, + mockGetLpTokens, + mockGetOptimalTrade, + mockRemoveLiquidity, + mockSwap +} from './parachain/amm'; import { mockGetForeignAssets } from './parachain/assetRegistry'; -import { mockGetLendTokens } from './parachain/loans'; +import { mockGetStakedBalance, mockVotingBalance } from './parachain/escrow'; +import { + mockBorrow, + mockClaimAllSubsidyRewards, + mockDisableAsCollateral, + mockEnableAsCollateral, + mockGetAccountSubsidyRewards, + mockGetBorrowPositionsOfAccount, + mockGetLendingStats, + mockGetLendPositionsOfAccount, + mockGetLendTokens, + mockGetLoanAssets, + mockLend, + mockRepay, + mockRepayAll, + mockWithdraw, + mockWithdrawAll +} from './parachain/loans'; +import { mockClaimVesting, mockVestingSchedules } from './parachain/vesting'; + +const DEFAULT_ACCOUNT_ADDRESS = 'a3aTRC4zs1djutYS9QuZSB3XmfRgNzFfyRtbZKaoQyv67Yzcc'; type RecursivePartial = { [P in keyof T]?: RecursivePartial; @@ -57,12 +94,26 @@ const mockInterBtcApi: RecursivePartial = { chainType: mockChainType } }, - on: jest.fn() + on: jest.fn(), + query: { + vesting: { + vestingSchedules: mockVestingSchedules as any + } + }, + tx: { + vesting: { + claim: mockClaimVesting + } + } }, assetRegistry: { getForeignAssets: mockGetForeignAssets }, - btcRelay: { getLatestBlockHeight: mockBtcRelayGetLatestBlockHeight }, + btcRelay: { + getLatestBlockHeight: mockBtcRelayGetLatestBlockHeight, + getStableParachainConfirmations: mockGetStableParachainConfirmations, + getStableBitcoinConfirmations: mockGetStableBitcoinConfirmations + }, electrsAPI: { getLatestBlockHeight: mockElectrsAPIGetLatestBlockHeight }, fee: { getIssueFee: mockFeeGetIssueFee, @@ -74,7 +125,21 @@ const mockInterBtcApi: RecursivePartial = { request: mockIssueRequest }, loans: { - getLendTokens: mockGetLendTokens + getLendTokens: mockGetLendTokens, + getLendPositionsOfAccount: mockGetLendPositionsOfAccount, + getBorrowPositionsOfAccount: mockGetBorrowPositionsOfAccount, + getLoanAssets: mockGetLoanAssets, + getAccruedRewardsOfAccount: mockGetAccountSubsidyRewards, + lend: mockLend, + withdraw: mockWithdraw, + withdrawAll: mockWithdrawAll, + borrow: mockBorrow, + repay: mockRepay, + repayAll: mockRepayAll, + enableAsCollateral: mockEnableAsCollateral, + disableAsCollateral: mockDisableAsCollateral, + claimAllSubsidyRewards: mockClaimAllSubsidyRewards, + getLendingStats: mockGetLendingStats }, oracle: { getExchangeRate: mockOracleGetExchangeRate @@ -90,7 +155,10 @@ const mockInterBtcApi: RecursivePartial = { request: mockRedeemRequest }, system: { - getStatusCode: mockSystemGetStatusCode + getStatusCode: mockSystemGetStatusCode, + getFutureBlockNumber: mockGetFutureBlockNumber, + getCurrentActiveBlockNumber: mockGetCurrentActiveBlockNumber, + getCurrentBlockNumber: mockGetCurrentBlockNumber }, tokens: { balance: mockTokensBalance, @@ -102,6 +170,21 @@ const mockInterBtcApi: RecursivePartial = { getVaultsWithIssuableTokens: mockVaultsGetVaultsWithIssuableTokens, getPremiumRedeemVaults: mockVaultsGetPremiumRedeemVaults, getVaultsWithRedeemableTokens: mockVaultsGetVaultsWithRedeemableTokens + }, + amm: { + getLiquidityPools: mockGetLiquidityPools, + getLiquidityProvidedByAccount: mockGetLiquidityProvidedByAccount, + getClaimableFarmingRewards: mockGetClaimableFarmingRewards, + addLiquidity: mockAddLiquidity, + removeLiquidity: mockRemoveLiquidity, + getLpTokens: mockGetLpTokens, + getOptimalTrade: mockGetOptimalTrade, + swap: mockSwap, + claimFarmingRewards: mockClaimFarmingRewards + }, + escrow: { + getStakedBalance: mockGetStakedBalance, + votingBalance: mockVotingBalance } }; @@ -111,11 +194,12 @@ jest.mock('@interlay/interbtc-api', () => { return { ...actualInterBtcApi, currencyIdToMonetaryCurrency: jest.fn(), - newAccountId: jest.fn().mockReturnValue('a3bS5ufTQYaWkWtiKH9urgnC81QWFArJz4TJCFXiBCj8C1oUm'), + newAccountId: jest.fn().mockReturnValue(DEFAULT_ACCOUNT_ADDRESS), getCollateralCurrencies: jest.fn(() => mockCollateralCurrencies), - createInterBtcApi: jest.fn((..._argv) => mockInterBtcApi as InterBtcApi) + createInterBtcApi: jest.fn((..._argv) => mockInterBtcApi as InterBtcApi), + FaucetClient: mockFaucet }; }); -export { mockInterBtcApi, mockSetAccount }; export * from './parachain'; +export { mockInterBtcApi, mockSetAccount }; diff --git a/src/test/mocks/@interlay/interbtc-api/parachain/amm.ts b/src/test/mocks/@interlay/interbtc-api/parachain/amm.ts new file mode 100644 index 0000000000..8d08541ec5 --- /dev/null +++ b/src/test/mocks/@interlay/interbtc-api/parachain/amm.ts @@ -0,0 +1,153 @@ +import { + MultiPathElementStandard, + MultiPathElementType, + newMonetaryAmount, + StandardLiquidityPool, + StandardLpToken, + Trade +} from '@interlay/interbtc-api'; +import Big from 'big.js'; + +import { GOVERNANCE_TOKEN, RELAY_CHAIN_NATIVE_TOKEN, WRAPPED_TOKEN } from '@/config/relay-chains'; + +const DEFAULT_LP_TOKEN_1_NAME = `LP ${GOVERNANCE_TOKEN.ticker}-${RELAY_CHAIN_NATIVE_TOKEN.ticker}`; + +const DEFAULT_LP_TOKEN_1: StandardLpToken = { + name: DEFAULT_LP_TOKEN_1_NAME, + ticker: DEFAULT_LP_TOKEN_1_NAME, + decimals: 12, + lpToken: { + token0: GOVERNANCE_TOKEN, + token1: RELAY_CHAIN_NATIVE_TOKEN + } +}; + +const DEFAULT_LP_TOKEN_2_NAME = `LP ${WRAPPED_TOKEN.ticker}-${RELAY_CHAIN_NATIVE_TOKEN.ticker}`; + +const DEFAULT_LP_TOKEN_2: StandardLpToken = { + name: DEFAULT_LP_TOKEN_2_NAME, + ticker: DEFAULT_LP_TOKEN_2_NAME, + decimals: 12, + lpToken: { + token0: WRAPPED_TOKEN, + token1: RELAY_CHAIN_NATIVE_TOKEN + } +}; + +const DEFAULT_POOLED_CURRENCIES_1 = [ + newMonetaryAmount(1, GOVERNANCE_TOKEN, true), + newMonetaryAmount(5, RELAY_CHAIN_NATIVE_TOKEN, true) +]; + +const DEFAULT_POOLED_CURRENCIES_2 = [ + newMonetaryAmount(1, WRAPPED_TOKEN, true), + newMonetaryAmount(5, RELAY_CHAIN_NATIVE_TOKEN, true) +]; + +const DEFAULT_LIQUIDITY_POOL_1 = new StandardLiquidityPool( + DEFAULT_LP_TOKEN_1, + DEFAULT_POOLED_CURRENCIES_1, + [newMonetaryAmount(5, GOVERNANCE_TOKEN, true)], + new Big('0.003'), + true, + newMonetaryAmount(1, DEFAULT_LP_TOKEN_1, true) +); + +const DEFAULT_LIQUIDITY_POOL_2 = new StandardLiquidityPool( + DEFAULT_LP_TOKEN_2, + DEFAULT_POOLED_CURRENCIES_2, + [newMonetaryAmount(5, GOVERNANCE_TOKEN, true)], + new Big('0.003'), + true, + newMonetaryAmount(1, DEFAULT_LP_TOKEN_2, true) +); + +const DEFAULT_LIQUIDITY_POOLS = [DEFAULT_LIQUIDITY_POOL_1, DEFAULT_LIQUIDITY_POOL_2]; + +const DEFAULT_ACCOUNT_LIQUIDITY = [newMonetaryAmount(0, DEFAULT_LP_TOKEN_1), newMonetaryAmount(0, DEFAULT_LP_TOKEN_2)]; + +const ACCOUNT_WITH_SOME_LIQUIDITY = [ + newMonetaryAmount(0, DEFAULT_LP_TOKEN_1), + newMonetaryAmount(1, DEFAULT_LP_TOKEN_2) +]; + +const ACCOUNT_WITH_FULL_LIQUIDITY = [ + newMonetaryAmount(2, DEFAULT_LP_TOKEN_1), + newMonetaryAmount(1, DEFAULT_LP_TOKEN_2) +]; + +const mockGetLiquidityPools = jest.fn().mockResolvedValue(DEFAULT_LIQUIDITY_POOLS); + +const mockGetLiquidityProvidedByAccount = jest.fn().mockResolvedValue(DEFAULT_ACCOUNT_LIQUIDITY); + +const DEFAULT_CLAIMABLE_REWARDS = new Map(); + +DEFAULT_CLAIMABLE_REWARDS.set(DEFAULT_LP_TOKEN_1, [newMonetaryAmount(1, WRAPPED_TOKEN, true)]); + +const mockGetClaimableFarmingRewards = jest.fn().mockResolvedValue(DEFAULT_CLAIMABLE_REWARDS); + +const mockClaimFarmingRewards = jest.fn(); + +const mockAddLiquidity = jest.fn(); + +const mockRemoveLiquidity = jest.fn(); + +const DEFAULT_LP_TOKENS = [DEFAULT_LP_TOKEN_1, DEFAULT_LP_TOKEN_2]; + +const mockGetLpTokens = jest.fn().mockResolvedValue(DEFAULT_LP_TOKENS); + +const DEFAULT_TRADE_AMOUNT = { + INPUT: newMonetaryAmount(1, RELAY_CHAIN_NATIVE_TOKEN, true), + OUTPUT: newMonetaryAmount(0.1, WRAPPED_TOKEN, true) +}; + +const mockGetOutputAmount = jest.fn().mockReturnValue(DEFAULT_TRADE_AMOUNT.OUTPUT); + +const DEFAULT_MULTI_PATH_ELEMENT: MultiPathElementStandard = { + pair: { + getOutputAmount: mockGetOutputAmount, + pathOf: jest.fn(), + token0: RELAY_CHAIN_NATIVE_TOKEN, + token1: WRAPPED_TOKEN, + reserve0: newMonetaryAmount(5, RELAY_CHAIN_NATIVE_TOKEN, true), + reserve1: newMonetaryAmount(5, WRAPPED_TOKEN, true) + }, + input: RELAY_CHAIN_NATIVE_TOKEN, + output: WRAPPED_TOKEN, + type: MultiPathElementType.STANDARD, + pool: DEFAULT_LIQUIDITY_POOL_2 +}; + +const DEFAULT_TRADE = new Trade([DEFAULT_MULTI_PATH_ELEMENT], DEFAULT_TRADE_AMOUNT.INPUT, DEFAULT_TRADE_AMOUNT.OUTPUT); +jest.spyOn(DEFAULT_TRADE, 'getMinimumOutputAmount').mockReturnValue(DEFAULT_TRADE_AMOUNT.OUTPUT); + +const mockGetOptimalTrade = jest.fn().mockReturnValue(DEFAULT_TRADE); + +const mockSwap = jest.fn(); + +export { + ACCOUNT_WITH_FULL_LIQUIDITY, + ACCOUNT_WITH_SOME_LIQUIDITY, + DEFAULT_ACCOUNT_LIQUIDITY, + DEFAULT_CLAIMABLE_REWARDS, + DEFAULT_LIQUIDITY_POOL_1, + DEFAULT_LIQUIDITY_POOL_2, + DEFAULT_LIQUIDITY_POOLS, + DEFAULT_LP_TOKEN_1, + DEFAULT_LP_TOKEN_2, + DEFAULT_LP_TOKENS, + DEFAULT_MULTI_PATH_ELEMENT, + DEFAULT_POOLED_CURRENCIES_1, + DEFAULT_POOLED_CURRENCIES_2, + DEFAULT_TRADE, + DEFAULT_TRADE_AMOUNT, + mockAddLiquidity, + mockClaimFarmingRewards, + mockGetClaimableFarmingRewards, + mockGetLiquidityPools, + mockGetLiquidityProvidedByAccount, + mockGetLpTokens, + mockGetOptimalTrade, + mockRemoveLiquidity, + mockSwap +}; diff --git a/src/test/mocks/@interlay/interbtc-api/parachain/btcRelay.ts b/src/test/mocks/@interlay/interbtc-api/parachain/btcRelay.ts index d4f886aabc..bc88c46cca 100644 --- a/src/test/mocks/@interlay/interbtc-api/parachain/btcRelay.ts +++ b/src/test/mocks/@interlay/interbtc-api/parachain/btcRelay.ts @@ -1,5 +1,20 @@ -const DEFAULT_BTC_BLOCK_HEIGHT = 1000; +const MOCK_BTC_RELAY_HEIGHT = 1000; -const mockBtcRelayGetLatestBlockHeight = jest.fn(() => DEFAULT_BTC_BLOCK_HEIGHT); +const mockBtcRelayGetLatestBlockHeight = jest.fn(() => MOCK_BTC_RELAY_HEIGHT); -export { mockBtcRelayGetLatestBlockHeight }; +const DEFAULT_STABLE_PARACHAIN_CONFIRMATION = 1; + +const DEFAULT_STABLE_BITCOIN_CONFIRMATION = 1; + +const mockGetStableParachainConfirmations = jest.fn().mockReturnValue(DEFAULT_STABLE_PARACHAIN_CONFIRMATION); + +const mockGetStableBitcoinConfirmations = jest.fn().mockReturnValue(DEFAULT_STABLE_BITCOIN_CONFIRMATION); + +export { + DEFAULT_STABLE_BITCOIN_CONFIRMATION, + DEFAULT_STABLE_PARACHAIN_CONFIRMATION, + MOCK_BTC_RELAY_HEIGHT, + mockBtcRelayGetLatestBlockHeight, + mockGetStableBitcoinConfirmations, + mockGetStableParachainConfirmations +}; diff --git a/src/test/mocks/@interlay/interbtc-api/parachain/electrsAPI.ts b/src/test/mocks/@interlay/interbtc-api/parachain/electrsAPI.ts index df3c623f47..b28fd0f445 100644 --- a/src/test/mocks/@interlay/interbtc-api/parachain/electrsAPI.ts +++ b/src/test/mocks/@interlay/interbtc-api/parachain/electrsAPI.ts @@ -1,5 +1,5 @@ -const DEFAULT_BTC_BLOCK_HEIGHT = 1000; +const MOCK_BITCOIN_HEIGHT = 1000; -const mockElectrsAPIGetLatestBlockHeight = jest.fn(() => DEFAULT_BTC_BLOCK_HEIGHT); +const mockElectrsAPIGetLatestBlockHeight = jest.fn(() => MOCK_BITCOIN_HEIGHT); -export { mockElectrsAPIGetLatestBlockHeight }; +export { MOCK_BITCOIN_HEIGHT, mockElectrsAPIGetLatestBlockHeight }; diff --git a/src/test/mocks/@interlay/interbtc-api/parachain/escrow.ts b/src/test/mocks/@interlay/interbtc-api/parachain/escrow.ts new file mode 100644 index 0000000000..8d27cc7621 --- /dev/null +++ b/src/test/mocks/@interlay/interbtc-api/parachain/escrow.ts @@ -0,0 +1,25 @@ +import '@testing-library/jest-dom'; + +import { newMonetaryAmount } from '@interlay/interbtc-api'; + +import { GOVERNANCE_TOKEN } from '@/config/relay-chains'; + +const DEFAULT_STAKED_AMOUNT = newMonetaryAmount(10, GOVERNANCE_TOKEN, true); + +const DEFAULT_STAKED_BALANCE = { amount: DEFAULT_STAKED_AMOUNT, endBlock: 0 }; + +const EMPTY_STAKED_BALANCE = { amount: newMonetaryAmount(0, GOVERNANCE_TOKEN), endBlock: 0 }; + +const mockGetStakedBalance = jest.fn().mockResolvedValue(DEFAULT_STAKED_BALANCE); + +const DEFAULT_VOTING_BALANCE = newMonetaryAmount(10, GOVERNANCE_TOKEN, true); + +const mockVotingBalance = jest.fn().mockResolvedValue(DEFAULT_VOTING_BALANCE); + +export { + DEFAULT_STAKED_BALANCE, + DEFAULT_VOTING_BALANCE, + EMPTY_STAKED_BALANCE, + mockGetStakedBalance, + mockVotingBalance +}; diff --git a/src/test/mocks/@interlay/interbtc-api/parachain/fee.ts b/src/test/mocks/@interlay/interbtc-api/parachain/fee.ts index d34beea49e..b15f6417b7 100644 --- a/src/test/mocks/@interlay/interbtc-api/parachain/fee.ts +++ b/src/test/mocks/@interlay/interbtc-api/parachain/fee.ts @@ -1,9 +1,26 @@ +import { BitcoinAmount } from '@interlay/monetary-js'; import Big from 'big.js'; -const DEFAULT_ISSUE_FEE = new Big(0.005); -const DEFAULT_ISSUE_GRIEFING_COLLATERAL_RATE = new Big(0.00005); +import { + DEFAULT_ISSUE_BRIDGE_FEE_RATE, + DEFAULT_ISSUE_GRIEFING_COLLATERAL_RATE, + DEFAULT_REDEEM_BRIDGE_FEE_RATE +} from '@/config/parachain'; -const mockFeeGetIssueFee = jest.fn(() => DEFAULT_ISSUE_FEE); -const mockFeeGetIssueGriefingCollateralRate = jest.fn(() => DEFAULT_ISSUE_GRIEFING_COLLATERAL_RATE); +const mockFeeGetIssueFee = jest.fn(() => new Big(DEFAULT_ISSUE_BRIDGE_FEE_RATE)); -export { mockFeeGetIssueFee, mockFeeGetIssueGriefingCollateralRate }; +const mockFeeGetIssueGriefingCollateralRate = jest.fn(() => new Big(DEFAULT_ISSUE_GRIEFING_COLLATERAL_RATE)); + +const mockRedeemGetPremiumRedeemFeeRate = jest.fn(() => Big(0)); + +const mockRedeemGetFeeRate = jest.fn(() => new Big(DEFAULT_REDEEM_BRIDGE_FEE_RATE)); + +const mockRedeemGetCurrentInclusionFee = jest.fn(() => new BitcoinAmount(0.0000038)); + +export { + mockFeeGetIssueFee, + mockFeeGetIssueGriefingCollateralRate, + mockRedeemGetCurrentInclusionFee, + mockRedeemGetFeeRate, + mockRedeemGetPremiumRedeemFeeRate +}; diff --git a/src/test/mocks/@interlay/interbtc-api/parachain/issue.ts b/src/test/mocks/@interlay/interbtc-api/parachain/issue.ts index f20a7cc28a..9eca8d8c19 100644 --- a/src/test/mocks/@interlay/interbtc-api/parachain/issue.ts +++ b/src/test/mocks/@interlay/interbtc-api/parachain/issue.ts @@ -1,16 +1,16 @@ import { newMonetaryAmount } from '@interlay/interbtc-api'; import { BitcoinAmount } from '@interlay/monetary-js'; +import { DEFAULT_ISSUE_DUST_AMOUNT } from '@/config/parachain'; import { WRAPPED_TOKEN } from '@/config/relay-chains'; -const DEFAULT_REQUEST_LIMITS = { - singleVaultMaxIssuable: newMonetaryAmount('56527153', WRAPPED_TOKEN), - totalMaxIssuable: newMonetaryAmount('493817337', WRAPPED_TOKEN) -}; +const mockIssueGetDustValue = jest.fn(() => new BitcoinAmount(DEFAULT_ISSUE_DUST_AMOUNT)); -const mockIssueGetDustValue = jest.fn(() => BitcoinAmount.zero()); +const mockIssueGetRequestLimits = jest.fn(() => ({ + singleVaultMaxIssuable: newMonetaryAmount(0.56527153, WRAPPED_TOKEN, true), + totalMaxIssuable: newMonetaryAmount(4.93817337, WRAPPED_TOKEN, true) +})); -const mockIssueGetRequestLimits = jest.fn(() => DEFAULT_REQUEST_LIMITS); const mockIssueRequest = jest.fn(); export { mockIssueGetDustValue, mockIssueGetRequestLimits, mockIssueRequest }; diff --git a/src/test/mocks/@interlay/interbtc-api/parachain/loans.ts b/src/test/mocks/@interlay/interbtc-api/parachain/loans.ts index 7c9c951f05..e29119298d 100644 --- a/src/test/mocks/@interlay/interbtc-api/parachain/loans.ts +++ b/src/test/mocks/@interlay/interbtc-api/parachain/loans.ts @@ -1,7 +1,206 @@ -import { CurrencyExt } from '@interlay/interbtc-api'; +import { + BorrowPosition, + CollateralPosition, + CurrencyExt, + LendingStats, + LoanAsset, + newMonetaryAmount, + TickerToData +} from '@interlay/interbtc-api'; +import { Bitcoin, ExchangeRate } from '@interlay/monetary-js'; +import Big from 'big.js'; + +import { GOVERNANCE_TOKEN, WRAPPED_TOKEN } from '@/config/relay-chains'; const DEFAULT_LEND_TOKENS: CurrencyExt[] = []; +const DEFAULT_IBTC = { + AMOUNT: { VERY_SMALL: '0.01', SMALL: '0.1', MEDIUM: '1', LARGE: '10', VERY_LARGE: '100' }, + MONETARY: { + EMPTY: newMonetaryAmount(0, WRAPPED_TOKEN, true), + VERY_SMALL: newMonetaryAmount(0.01, WRAPPED_TOKEN, true), + SMALL: newMonetaryAmount(0.1, WRAPPED_TOKEN, true), + MEDIUM: newMonetaryAmount(1, WRAPPED_TOKEN, true), + LARGE: newMonetaryAmount(10, WRAPPED_TOKEN, true), + VERY_LARGE: newMonetaryAmount(100, WRAPPED_TOKEN, true) + } +}; + +const DEFAULT_INTR = { + AMOUNT: { VERY_SMALL: '10', SMALL: '100', MEDIUM: '1000', LARGE: '10000', VERY_LARGE: '100000' }, + MONETARY: { + EMPTY: newMonetaryAmount(0, GOVERNANCE_TOKEN, true), + VERY_SMALL: newMonetaryAmount(10, GOVERNANCE_TOKEN, true), + SMALL: newMonetaryAmount(100, GOVERNANCE_TOKEN, true), + MEDIUM: newMonetaryAmount(1000, GOVERNANCE_TOKEN, true), + LARGE: newMonetaryAmount(10000, GOVERNANCE_TOKEN, true), + VERY_LARGE: newMonetaryAmount(100000, GOVERNANCE_TOKEN, true) + } +}; + +const DEFAULT_APY = { + IBTC: { + BASE: '10.20', + LEND: '10.48', + BORROW: '9.92' + }, + INTR: { + BASE: '10.20', + LEND: '10.20', + BORROW: '10.20' + } +}; + +const DEFAULT_POSITIONS = { + LEND: { + IBTC: { + currency: WRAPPED_TOKEN, + amount: DEFAULT_IBTC.MONETARY.MEDIUM, + isCollateral: true, + earnedInterest: DEFAULT_IBTC.MONETARY.VERY_SMALL + } as CollateralPosition, + INTR: { + currency: GOVERNANCE_TOKEN, + amount: DEFAULT_INTR.MONETARY.MEDIUM, + isCollateral: true, + earnedInterest: DEFAULT_INTR.MONETARY.SMALL + } as CollateralPosition + }, + BORROW: { + IBTC: { + currency: WRAPPED_TOKEN, + amount: DEFAULT_IBTC.MONETARY.SMALL, + accumulatedDebt: DEFAULT_IBTC.MONETARY.VERY_SMALL + }, + INTR: { + currency: GOVERNANCE_TOKEN, + amount: DEFAULT_INTR.MONETARY.MEDIUM, + accumulatedDebt: DEFAULT_INTR.MONETARY.SMALL + } + } +}; + +const DEFAULT_LEND_POSITIONS: CollateralPosition[] = [DEFAULT_POSITIONS.LEND.IBTC]; + +const DEFAULT_BORROW_POSITIONS: BorrowPosition[] = [DEFAULT_POSITIONS.BORROW.IBTC]; + +const DEFAULT_IBTC_LOAN_ASSET: LoanAsset = { + currency: WRAPPED_TOKEN, + lendApy: new Big(DEFAULT_APY.IBTC.BASE), + borrowApy: new Big(DEFAULT_APY.IBTC.BASE), + totalLiquidity: DEFAULT_IBTC.MONETARY.VERY_LARGE, + lendReward: DEFAULT_INTR.MONETARY.VERY_LARGE, + borrowReward: DEFAULT_INTR.MONETARY.VERY_LARGE, + availableCapacity: DEFAULT_IBTC.MONETARY.VERY_LARGE, + collateralThreshold: new Big(0.6), + liquidationThreshold: new Big(0.8), + isActive: true, + totalBorrows: DEFAULT_IBTC.MONETARY.MEDIUM, + borrowCap: DEFAULT_IBTC.MONETARY.VERY_LARGE, + supplyCap: DEFAULT_IBTC.MONETARY.VERY_LARGE, + exchangeRate: new ExchangeRate(Bitcoin, WRAPPED_TOKEN, DEFAULT_IBTC.MONETARY.MEDIUM.toBig()) +}; + +const DEFAULT_INTR_LOAN_ASSET: LoanAsset = { + currency: GOVERNANCE_TOKEN, + lendApy: new Big(DEFAULT_APY.INTR.BASE), + borrowApy: new Big(DEFAULT_APY.INTR.BASE), + totalLiquidity: DEFAULT_INTR.MONETARY.VERY_SMALL, + lendReward: null, + borrowReward: null, + availableCapacity: DEFAULT_INTR.MONETARY.VERY_SMALL, + collateralThreshold: new Big(0.6), + liquidationThreshold: new Big(0.8), + isActive: true, + totalBorrows: DEFAULT_INTR.MONETARY.MEDIUM, + borrowCap: DEFAULT_INTR.MONETARY.VERY_LARGE, + supplyCap: DEFAULT_INTR.MONETARY.VERY_LARGE, + exchangeRate: new ExchangeRate(Bitcoin, GOVERNANCE_TOKEN, DEFAULT_IBTC.MONETARY.MEDIUM.toBig()) +}; + +const DEFAULT_ASSETS: TickerToData = { + IBTC: DEFAULT_IBTC_LOAN_ASSET, + INTR: DEFAULT_INTR_LOAN_ASSET +}; + +const mockGetLendPositionsOfAccount = jest.fn().mockReturnValue(DEFAULT_LEND_POSITIONS); +const mockGetBorrowPositionsOfAccount = jest.fn().mockReturnValue(DEFAULT_BORROW_POSITIONS); +const mockGetLoanAssets = jest.fn().mockReturnValue(DEFAULT_ASSETS); +const mockGetAccountSubsidyRewards = jest.fn().mockReturnValue(DEFAULT_INTR.MONETARY.MEDIUM); + +const mockLend = jest.fn(); +const mockWithdraw = jest.fn(); +const mockWithdrawAll = jest.fn(); +const mockBorrow = jest.fn(); +const mockRepay = jest.fn(); +const mockRepayAll = jest.fn(); + +const mockEnableAsCollateral = jest.fn(); +const mockDisableAsCollateral = jest.fn(); + +const mockClaimAllSubsidyRewards = jest.fn(); + const mockGetLendTokens = jest.fn(() => DEFAULT_LEND_TOKENS); +const DEFAULT_THRESOLD = { + MIN: new Big(0), + LOW: new Big(0.25), + MEDIUM: new Big(0.5), + HIGH: new Big(0.75), + MAX: new Big(1) +}; + +const DEFAULT_CALCULATE_BORROW_LIMIT = { + ltv: DEFAULT_THRESOLD.LOW, + collateralThresholdWeightedAverage: DEFAULT_THRESOLD.MEDIUM, + liquidationThresholdWeightedAverage: DEFAULT_THRESOLD.HIGH +}; + +const mockCalculateLtvAndThresholdsChange = jest.fn().mockReturnValue(DEFAULT_CALCULATE_BORROW_LIMIT); + +const mockCalculateBorrowLimitBtcChange = jest.fn().mockReturnValue(DEFAULT_IBTC.MONETARY.LARGE); + +const DEFAULT_LENDING_STATS: LendingStats = { + borrowLimitBtc: DEFAULT_IBTC.MONETARY.LARGE, + calculateBorrowLimitBtcChange: mockCalculateBorrowLimitBtcChange, + calculateLtvAndThresholdsChange: mockCalculateLtvAndThresholdsChange, + collateralThresholdWeightedAverage: new Big(0.5), + liquidationThresholdWeightedAverage: new Big(0.75), + ltv: new Big(0.2), + totalBorrowedBtc: DEFAULT_IBTC.MONETARY.VERY_SMALL, + totalCollateralBtc: DEFAULT_IBTC.MONETARY.LARGE, + totalLentBtc: DEFAULT_IBTC.MONETARY.LARGE +}; + +const mockGetLendingStats = jest.fn().mockReturnValue(DEFAULT_LENDING_STATS); + +export { + DEFAULT_APY, + DEFAULT_ASSETS, + DEFAULT_BORROW_POSITIONS, + DEFAULT_CALCULATE_BORROW_LIMIT, + DEFAULT_IBTC, + DEFAULT_IBTC_LOAN_ASSET, + DEFAULT_INTR_LOAN_ASSET, + DEFAULT_LEND_POSITIONS, + DEFAULT_LENDING_STATS, + DEFAULT_POSITIONS, + DEFAULT_THRESOLD, + mockBorrow, + mockCalculateBorrowLimitBtcChange, + mockCalculateLtvAndThresholdsChange, + mockClaimAllSubsidyRewards, + mockDisableAsCollateral, + mockEnableAsCollateral, + mockGetAccountSubsidyRewards, + mockGetBorrowPositionsOfAccount, + mockGetLendingStats, + mockGetLendPositionsOfAccount, + mockGetLoanAssets, + mockLend, + mockRepay, + mockRepayAll, + mockWithdraw, + mockWithdrawAll +}; export { mockGetLendTokens }; diff --git a/src/test/mocks/@interlay/interbtc-api/parachain/oracle.ts b/src/test/mocks/@interlay/interbtc-api/parachain/oracle.ts index e5255e2a9f..9584099db1 100644 --- a/src/test/mocks/@interlay/interbtc-api/parachain/oracle.ts +++ b/src/test/mocks/@interlay/interbtc-api/parachain/oracle.ts @@ -2,10 +2,10 @@ import { CurrencyExt } from '@interlay/interbtc-api'; import { Bitcoin, ExchangeRate } from '@interlay/monetary-js'; import Big from 'big.js'; -const DEFAULT_EXCHANGE_RATE = '1000000'; +const MOCK_EXCHANGE_RATE = new Big(1000000); const mockOracleGetExchangeRate = jest.fn( - (currency: CurrencyExt) => new ExchangeRate(Bitcoin, currency, Big(DEFAULT_EXCHANGE_RATE)) + (currency: CurrencyExt) => new ExchangeRate(Bitcoin, currency, MOCK_EXCHANGE_RATE) ); -export { mockOracleGetExchangeRate }; +export { MOCK_EXCHANGE_RATE, mockOracleGetExchangeRate }; diff --git a/src/test/mocks/@interlay/interbtc-api/parachain/redeem.ts b/src/test/mocks/@interlay/interbtc-api/parachain/redeem.ts index 68202fcb0e..23956e2c63 100644 --- a/src/test/mocks/@interlay/interbtc-api/parachain/redeem.ts +++ b/src/test/mocks/@interlay/interbtc-api/parachain/redeem.ts @@ -2,39 +2,30 @@ import { CollateralCurrencyExt, CurrencyExt, newMonetaryAmount } from '@interlay import { Bitcoin, ExchangeRate } from '@interlay/monetary-js'; import Big from 'big.js'; +import { DEFAULT_REDEEM_DUST_AMOUNT } from '@/config/parachain'; import { WRAPPED_TOKEN } from '@/config/relay-chains'; -const DEFAULT_MAX_BURNABLE_TOKENS = '100000000'; // 1 BTC -const DEFAULT_BURN_EXCHANGE_RATE = '150000'; - -const zeroWrappedTokenAmount = newMonetaryAmount(0, WRAPPED_TOKEN); +const MOCK_MAX_BURNABLE_TOKENS = '100000000'; // 1 BTC +const MOCK_BURN_EXCHANGE_RATE = '150000'; const mockRedeemGetMaxBurnableTokens = jest.fn((_currency: CurrencyExt) => - newMonetaryAmount(DEFAULT_MAX_BURNABLE_TOKENS, WRAPPED_TOKEN) + newMonetaryAmount(MOCK_MAX_BURNABLE_TOKENS, WRAPPED_TOKEN) ); + const mockRedeemGetBurnExchangeRate = jest.fn( (collateralCurrency: CollateralCurrencyExt) => - new ExchangeRate(Bitcoin, collateralCurrency, Big(DEFAULT_BURN_EXCHANGE_RATE)) + new ExchangeRate(Bitcoin, collateralCurrency, Big(MOCK_BURN_EXCHANGE_RATE)) ); const mockRedeemBurn = jest.fn(); -const mockRedeemGetDustValue = jest.fn(() => zeroWrappedTokenAmount); - -const mockRedeemGetPremiumRedeemFeeRate = jest.fn(() => Big(0)); - -const mockRedeemGetFeeRate = jest.fn(() => Big(0.005)); - -const mockRedeemGetCurrentInclusionFee = jest.fn(() => zeroWrappedTokenAmount); +const mockRedeemGetDustValue = jest.fn(() => newMonetaryAmount(DEFAULT_REDEEM_DUST_AMOUNT, WRAPPED_TOKEN)); const mockRedeemRequest = jest.fn(); export { mockRedeemBurn, mockRedeemGetBurnExchangeRate, - mockRedeemGetCurrentInclusionFee, mockRedeemGetDustValue, - mockRedeemGetFeeRate, mockRedeemGetMaxBurnableTokens, - mockRedeemGetPremiumRedeemFeeRate, mockRedeemRequest }; diff --git a/src/test/mocks/@interlay/interbtc-api/parachain/system.ts b/src/test/mocks/@interlay/interbtc-api/parachain/system.ts index 477641628b..5c1622cbcb 100644 --- a/src/test/mocks/@interlay/interbtc-api/parachain/system.ts +++ b/src/test/mocks/@interlay/interbtc-api/parachain/system.ts @@ -3,4 +3,24 @@ const SYSTEM_STATUS_RUNNING = { isRunning: true }; const mockSystemGetStatusCode = jest.fn(() => SYSTEM_STATUS_RUNNING); -export { mockSystemGetStatusCode }; +const DEFAULT_DEADLINE_BLOCK_NUMBER = 0; + +const mockGetFutureBlockNumber = jest.fn().mockResolvedValue(DEFAULT_DEADLINE_BLOCK_NUMBER); + +const DEFAULT_CURRENT_ACTIVE_BLOCK_NUMBER = 1; + +const mockGetCurrentActiveBlockNumber = jest.fn().mockResolvedValue(DEFAULT_CURRENT_ACTIVE_BLOCK_NUMBER); + +const DEFAULT_CURRENT_BLOCK_NUMBER = 0; + +const mockGetCurrentBlockNumber = jest.fn().mockReturnValue(DEFAULT_CURRENT_BLOCK_NUMBER); + +export { + DEFAULT_CURRENT_ACTIVE_BLOCK_NUMBER, + DEFAULT_CURRENT_BLOCK_NUMBER, + DEFAULT_DEADLINE_BLOCK_NUMBER, + mockGetCurrentActiveBlockNumber, + mockGetCurrentBlockNumber, + mockGetFutureBlockNumber, + mockSystemGetStatusCode +}; diff --git a/src/test/mocks/@interlay/interbtc-api/parachain/tokens.ts b/src/test/mocks/@interlay/interbtc-api/parachain/tokens.ts index 454670f9be..48b990a67a 100644 --- a/src/test/mocks/@interlay/interbtc-api/parachain/tokens.ts +++ b/src/test/mocks/@interlay/interbtc-api/parachain/tokens.ts @@ -1,20 +1,33 @@ import { ChainBalance, CurrencyExt, newMonetaryAmount } from '@interlay/interbtc-api'; import { AccountId } from '@polkadot/types/interfaces'; +import Big from 'big.js'; -const DEFAULT_TOKEN_BALANCE = '1000000000000'; -const DEFAULT_TOKEN_TOTAL_AMOUNT = '10000000000000000000000'; +const MOCK_TOKEN_BALANCE = '1000000000000'; +const MOCK_TOKEN_TOTAL_AMOUNT = '10000000000000000000000'; + +const DEFAULT_TOKENS_BALANCE_FN = (currency: CurrencyExt, _id: AccountId): ChainBalance => + new ChainBalance(currency, MOCK_TOKEN_BALANCE, MOCK_TOKEN_BALANCE, MOCK_TOKEN_BALANCE); + +const EMPTY_TOKENS_BALANCE_FN = (currency: CurrencyExt, _id: AccountId): ChainBalance => + new ChainBalance(currency, new Big(0), new Big(0)); + +const mockTokensBalance = jest.fn().mockImplementation(DEFAULT_TOKENS_BALANCE_FN); + +const mockTokensTotal = jest.fn(async (currency: CurrencyExt) => newMonetaryAmount(MOCK_TOKEN_TOTAL_AMOUNT, currency)); -const mockTokensBalance = jest.fn( - (currency: CurrencyExt, _id: AccountId) => new ChainBalance(currency, DEFAULT_TOKEN_BALANCE, DEFAULT_TOKEN_BALANCE) -); -const mockTokensTotal = jest.fn(async (currency: CurrencyExt) => - newMonetaryAmount(DEFAULT_TOKEN_TOTAL_AMOUNT, currency) -); const mockTokensSubscribeToBalance = jest.fn((currency: CurrencyExt, account, callback) => { - const balance = new ChainBalance(currency, DEFAULT_TOKEN_BALANCE, DEFAULT_TOKEN_BALANCE); + const balance = new ChainBalance(currency, MOCK_TOKEN_BALANCE, MOCK_TOKEN_BALANCE); callback(account, balance); return () => undefined; }); -export { mockTokensBalance, mockTokensSubscribeToBalance, mockTokensTotal }; +export { + DEFAULT_TOKENS_BALANCE_FN, + EMPTY_TOKENS_BALANCE_FN, + MOCK_TOKEN_BALANCE, + MOCK_TOKEN_TOTAL_AMOUNT, + mockTokensBalance, + mockTokensSubscribeToBalance, + mockTokensTotal +}; diff --git a/src/test/mocks/@interlay/interbtc-api/parachain/vaults.ts b/src/test/mocks/@interlay/interbtc-api/parachain/vaults.ts index 6a2e14f4ba..920878dea1 100644 --- a/src/test/mocks/@interlay/interbtc-api/parachain/vaults.ts +++ b/src/test/mocks/@interlay/interbtc-api/parachain/vaults.ts @@ -6,13 +6,11 @@ import { AccountId } from '@polkadot/types/interfaces'; import { RELAY_CHAIN_NATIVE_TOKEN, WRAPPED_TOKEN } from '@/config/relay-chains'; -const DEFAULT_COLLATERAL_AMOUNT = '1000000000000'; -// TODO: Extend with all the data needed for Vaults page. -const DEFAULT_VAULT_WITH_ISSUABLE_TOKENS = (_accountId: AccountId, collateralCurrency: CollateralCurrencyExt) => ({ - backingCollateral: newMonetaryAmount(DEFAULT_COLLATERAL_AMOUNT, collateralCurrency) -}); +const MOCK_COLLATERAL_AMOUNT = '1000000000000'; -const mockVaultsGet = jest.fn((accountId, currency) => DEFAULT_VAULT_WITH_ISSUABLE_TOKENS(accountId, currency)); +const mockVaultsGet = jest.fn((_accountId: AccountId, collateralCurrency: CollateralCurrencyExt) => ({ + backingCollateral: newMonetaryAmount(MOCK_COLLATERAL_AMOUNT, collateralCurrency) +})); const mockNewVaultId = (vaultAddress: string, collateralToken: CurrencyExt) => ({ accountId: vaultAddress, @@ -22,21 +20,23 @@ const mockNewVaultId = (vaultAddress: string, collateralToken: CurrencyExt) => ( } }); -const DEFAULT_VAULT_ADDRESS = '5GQoBrhX3mfnmKnw2qz2vGvHG8yvf6xT15gGM54865g6qEfE'; +const MOCK_VAULT_ADDRESS = '5GQoBrhX3mfnmKnw2qz2vGvHG8yvf6xT15gGM54865g6qEfE'; -const DEFAULT_COLLATERAL_TOKEN = RELAY_CHAIN_NATIVE_TOKEN; +const MOCK_COLLATERAL_TOKEN = RELAY_CHAIN_NATIVE_TOKEN; -const DEFAULT_BITCOIN_AMOUNT = 100; +const MOCK_BITCOIN_AMOUNT = 100; -const mockVaults = new Map().set( - mockNewVaultId(DEFAULT_VAULT_ADDRESS, DEFAULT_COLLATERAL_TOKEN), - new BitcoinAmount(DEFAULT_BITCOIN_AMOUNT) +const mockVaultsGetVaultsWithIssuableTokens = jest.fn(() => + new Map().set(mockNewVaultId(MOCK_VAULT_ADDRESS, MOCK_COLLATERAL_TOKEN), new BitcoinAmount(MOCK_BITCOIN_AMOUNT)) ); -const mockVaultsGetVaultsWithIssuableTokens = jest.fn(() => mockVaults); -const mockVaultsGetPremiumRedeemVaults = jest.fn(() => mockVaults); +const mockVaultsGetPremiumRedeemVaults = jest.fn(() => + new Map().set(mockNewVaultId(MOCK_VAULT_ADDRESS, MOCK_COLLATERAL_TOKEN), new BitcoinAmount(MOCK_BITCOIN_AMOUNT)) +); -const mockVaultsGetVaultsWithRedeemableTokens = jest.fn(() => mockVaults); +const mockVaultsGetVaultsWithRedeemableTokens = jest.fn(() => + new Map().set(mockNewVaultId(MOCK_VAULT_ADDRESS, MOCK_COLLATERAL_TOKEN), new BitcoinAmount(MOCK_BITCOIN_AMOUNT)) +); export { mockVaultsGet, diff --git a/src/test/mocks/@interlay/interbtc-api/parachain/vesting.ts b/src/test/mocks/@interlay/interbtc-api/parachain/vesting.ts new file mode 100644 index 0000000000..8725706d9d --- /dev/null +++ b/src/test/mocks/@interlay/interbtc-api/parachain/vesting.ts @@ -0,0 +1,9 @@ +const SOME_VESTING_SCHEDULES = [{ start: 0, period: 0, periodCount: 1, perPeriod: 1 }]; + +const EMPTY_VESTING_SCHEDULES: any[] = []; + +const mockVestingSchedules = jest.fn().mockReturnValue(EMPTY_VESTING_SCHEDULES); + +const mockClaimVesting = jest.fn(); + +export { EMPTY_VESTING_SCHEDULES, mockClaimVesting, mockVestingSchedules, SOME_VESTING_SCHEDULES }; diff --git a/src/test/mocks/fetch/index.ts b/src/test/mocks/fetch/index.ts index 42a44019c0..9aa0c1ef7f 100644 --- a/src/test/mocks/fetch/index.ts +++ b/src/test/mocks/fetch/index.ts @@ -1,12 +1,29 @@ +import { newMonetaryAmount } from '@interlay/interbtc-api'; + +import { RELAY_CHAIN_NATIVE_TOKEN, WRAPPED_TOKEN } from '@/config/relay-chains'; +import { issuesQuery } from '@/services/queries/issues'; +import vaultsByAccountIdQuery from '@/services/queries/vaults-by-accountId-query'; import { PRICES_API } from '@/utils/constants/api'; +import { KUSAMA, POLKADOT } from '@/utils/constants/relay-chain-names'; + +import { DEFAULT_ACCOUNT_ADDRESS } from '../substrate/mocks'; -const DEFAULT_PRICES = { +let mockGovernanceTokenPriceInUsd: number; +if (process.env.REACT_APP_RELAY_CHAIN_NAME === POLKADOT) { + mockGovernanceTokenPriceInUsd = 0.057282; +} else if (process.env.REACT_APP_RELAY_CHAIN_NAME === KUSAMA) { + mockGovernanceTokenPriceInUsd = 1.84; +} else { + throw new Error('Something went wrong!'); +} + +const MOCK_TOKEN_PRICES = { bitcoin: { usd: 20306 }, polkadot: { usd: 7.19 }, 'kintsugi-btc': { usd: 20128 }, kusama: { usd: 48.74 }, - interlay: { usd: 0.057282 }, - kintsugi: { usd: 1.84 } + interlay: { usd: mockGovernanceTokenPriceInUsd }, + kintsugi: { usd: mockGovernanceTokenPriceInUsd } }; // Can mock all fetch calls here based on URL and input. @@ -15,8 +32,72 @@ const mockFetch = jest.fn((input, _init?) => { let result: unknown; switch (true) { case input.includes(PRICES_API.URL): - result = DEFAULT_PRICES; + result = MOCK_TOKEN_PRICES; + break; + case input.includes('http://localhost:4000/graphql'): { + const { query } = JSON.parse(_init.body); + + switch (query) { + case vaultsByAccountIdQuery(DEFAULT_ACCOUNT_ADDRESS): + result = { + vaults: [] + }; + break; + case issuesQuery(`userParachainAddress_eq: "a3aTRC4zs1djutYS9QuZSB3XmfRgNzFfyRtbZKaoQyv67Yzcc"`): + result = { + data: { + issues: [ + { + id: '0x9b0bc5abb87bca076718953b362634646fbbccc76368d2f78104b4be04ff0693', + request: { + amountWrapped: newMonetaryAmount(100000, WRAPPED_TOKEN, true), + bridgeFeeWrapped: newMonetaryAmount(1500, WRAPPED_TOKEN, true), + timestamp: new Date('2023-03-17'), + height: { + absolute: 243298, + active: 241957 + } + }, + userParachainAddress: 'a3czP8uH8BKyS1NpqXkjzEGe5iKNhuTbXmYSNdC47iM9sNmMB', + vault: { + accountId: 'a3baoFq36qeR3q21sb5hh7m4uJ2rhmmUXxM4uy8ShZj9sFAiX', + collateralToken: { + token: 'KSM' + }, + wrappedToken: { + token: 'KBTC' + } + }, + vaultBackingAddress: 'tb1q8ylml0gtdv73s6vzm08gder2l4jf4upfg36zte', + vaultWalletPubkey: '0x02a8f111af401369e0ceefc5330a800b5aa3bfe2759dbd2eef5e53b611a4e36e86', + griefingCollateral: newMonetaryAmount(100000, RELAY_CHAIN_NATIVE_TOKEN, true), + status: 'Expired', + refund: null, + execution: null, + cancellation: null, + period: { + id: 'initial-1675864050243', + value: 14400, + height: { + absolute: 499, + id: '499' + }, + timestamp: new Date('2023-02-08') + }, + backingPayment: {} + } + ] + } + }; + break; + + default: { + throw new Error(`mockFetch: provided input [${input}] is not mocked`); + } + } + break; + } default: { throw new Error(`mockFetch: provided input [${input}] is not mocked`); } @@ -26,6 +107,7 @@ const mockFetch = jest.fn((input, _init?) => { }); }); +// TODO: need to mock with `msw` global.fetch = mockFetch as any; -export { mockFetch }; +export { MOCK_TOKEN_PRICES, mockFetch, mockGovernanceTokenPriceInUsd }; diff --git a/src/test/mocks/setup.ts b/src/test/mocks/setup.tsx similarity index 52% rename from src/test/mocks/setup.ts rename to src/test/mocks/setup.tsx index 115f9d4b98..c190c9a418 100644 --- a/src/test/mocks/setup.ts +++ b/src/test/mocks/setup.tsx @@ -7,6 +7,29 @@ import './fetch'; import './substrate'; import { createInterBtcApi } from '@interlay/interbtc-api'; +import { FocusScope } from '@react-aria/focus'; +import ReactDOM from 'react-dom'; + +// Enforce garbage collection +afterAll(() => { + if (global.gc) global.gc(); +}); + +// MEMO: mocking @react/aria overlay component because +// of a error around `createTreeWalker` +const mockOverlay: React.FC = ({ children, isOpen }: any) => + isOpen + ? ReactDOM.createPortal( + + {children} + , + document.body + ) + : null; +jest.mock('@/component-library/Overlay', () => { + mockOverlay.displayName = 'mockOverlay'; + return { Overlay: mockOverlay }; +}); // Pre-create API and assign to window to avoid waiting for substrate provider to be loaded before. // Does not need any parameters since lib instance is mocked. diff --git a/src/test/mocks/substrate/mocks.ts b/src/test/mocks/substrate/mocks.ts index 402203db00..e260d1ab81 100644 --- a/src/test/mocks/substrate/mocks.ts +++ b/src/test/mocks/substrate/mocks.ts @@ -1,6 +1,8 @@ import { mockInterBtcApi } from '../@interlay/interbtc-api'; import mockJsonRpc from './jsonrpc'; +const DEFAULT_ACCOUNT_ADDRESS = 'a3aTRC4zs1djutYS9QuZSB3XmfRgNzFfyRtbZKaoQyv67Yzcc'; + const DEFAULT_KEYRING_ACCOUNTS = [ { key: 'header-accounts', @@ -45,7 +47,7 @@ const DEFAULT_KEYRING = { const DEFAULT_ACCOUNTS = [ { - address: 'a3aTRC4zs1djutYS9QuZSB3XmfRgNzFfyRtbZKaoQyv67Yzcc', + address: DEFAULT_ACCOUNT_ADDRESS, meta: { genesisHash: '', name: 'Wallet 1', @@ -75,7 +77,7 @@ const DEFAULT_EXTENSIONS = [ ]; const DEFAULT_SELECTED_ACCOUNT = { - address: 'a3aTRC4zs1djutYS9QuZSB3XmfRgNzFfyRtbZKaoQyv67Yzcc', + address: DEFAULT_ACCOUNT_ADDRESS, addressRaw: { '0': 10, '1': 224, @@ -166,4 +168,4 @@ const DEFAULT_SUBSTRATE = { extensions: DEFAULT_EXTENSIONS }; -export { DEFAULT_KEYRING, DEFAULT_SUBSTRATE }; +export { DEFAULT_ACCOUNT_ADDRESS, DEFAULT_KEYRING, DEFAULT_SUBSTRATE }; diff --git a/src/test/pages/Burn.test.tsx b/src/test/pages/Burn.test.tsx index 8294c0aef3..64cacffe5e 100644 --- a/src/test/pages/Burn.test.tsx +++ b/src/test/pages/Burn.test.tsx @@ -9,24 +9,14 @@ import { mockRedeemBurn, mockRedeemGetMaxBurnableTokens } from '../mocks/@interl import { act, render, screen, userEvent, waitFor } from '../test-utils'; describe('Burn page', () => { - it('should display burn tab when there is liquidated vault', async () => { + it('the burn tab is displayed when there is a liquidated vault', async () => { await render(, { path: '/bridge?tab=burn' }); const burnTab = screen.getByRole('tab', { name: /burn/i }); expect(burnTab).toBeVisible(); }); - it('should not display burn tab when there is no liquidated vault', async () => { - mockRedeemGetMaxBurnableTokens.mockImplementationOnce(() => newMonetaryAmount('0', WRAPPED_TOKEN)); - - await render(, { path: '/bridge?tab=burn' }); - - await waitFor(() => { - expect(screen.queryByText(/Burn/i)).not.toBeInTheDocument(); - }); - }); - - it('should burn the IBTC', async () => { + it('the burn method is called', async () => { await render(, { path: '/bridge?tab=burn' }); const burnTab = screen.getByRole('tab', { name: /burn/i }); @@ -49,4 +39,14 @@ describe('Burn page', () => { // Check that burn method was called. await waitFor(() => expect(mockRedeemBurn).toHaveBeenCalledTimes(1)); }); + + it('the burn tab is not displayed when there is no liquidated vault', async () => { + mockRedeemGetMaxBurnableTokens.mockImplementation(() => newMonetaryAmount('0', WRAPPED_TOKEN)); + + await render(, { path: '/bridge?tab=burn' }); + + await waitFor(() => { + expect(screen.queryByText(/Burn/i)).not.toBeInTheDocument(); + }); + }); }); diff --git a/src/test/pages/Issue.test.tsx b/src/test/pages/Issue.test.tsx index ab9bf5940d..3d4b3ba59d 100644 --- a/src/test/pages/Issue.test.tsx +++ b/src/test/pages/Issue.test.tsx @@ -1,31 +1,283 @@ import '@testing-library/jest-dom'; +import { ChainBalance, CurrencyExt, newMonetaryAmount } from '@interlay/interbtc-api'; +import { Bitcoin, BitcoinAmount, ExchangeRate } from '@interlay/monetary-js'; +import { AccountId } from '@polkadot/types/interfaces'; +import Big from 'big.js'; + import App from '@/App'; +import { displayMonetaryAmount, displayMonetaryAmountInUSDFormat } from '@/common/utils/utils'; +import { BLOCKS_BEHIND_LIMIT } from '@/config/parachain'; +import { GOVERNANCE_TOKEN, TRANSACTION_FEE_AMOUNT, WRAPPED_TOKEN } from '@/config/relay-chains'; -import { mockIssueRequest } from '../mocks/@interlay/interbtc-api'; -import { act, render, screen, userEvent, waitFor } from '../test-utils'; +import { + MOCK_BITCOIN_HEIGHT, + MOCK_BTC_RELAY_HEIGHT, + MOCK_EXCHANGE_RATE, + MOCK_TOKEN_BALANCE, + mockBtcRelayGetLatestBlockHeight, + mockElectrsAPIGetLatestBlockHeight, + mockFeeGetIssueFee, + mockFeeGetIssueGriefingCollateralRate, + mockIssueGetDustValue, + mockIssueGetRequestLimits, + mockIssueRequest, + mockOracleGetExchangeRate, + mockTokensBalance +} from '../mocks/@interlay/interbtc-api'; +import { MOCK_TOKEN_PRICES, mockGovernanceTokenPriceInUsd } from '../mocks/fetch'; +import { act, render, screen, userEvent, waitFor, within } from '../test-utils'; -describe('Issue page', () => { - it('should issue the IBTC', async () => { - await render(, { path: '/bridge?tab=issue' }); +const getBridgeFee = (inputAmount: number) => { + return new BitcoinAmount(inputAmount).mul(mockFeeGetIssueFee()); +}; - const issueTab = screen.getByRole('tab', { name: /issue/i }); - userEvent.click(issueTab); +const ISSUE_TAB_PATH = '/bridge?tab=issue'; - const amountToIssueInput = screen.getByRole('textbox'); - // Input 0.0001 IBTC - await act(async () => { - userEvent.type(amountToIssueInput, '0.0001'); - }); +// TODO: type `props` properly +const renderIssueForm = async (props?: any) => { + await render(, { path: ISSUE_TAB_PATH }); - const submitButton = screen.getByRole('button', { name: /confirm/i }); + const issueTab = screen.getByRole('tab', { name: /issue/i }); - // Issue IBTC - await act(async () => { - userEvent.click(submitButton); - }); + const issueTabPanel = screen.getByRole('tabpanel', { + name: /issue/i + }); + + userEvent.click(issueTab); + + const amountToIssueInput = screen.getByRole('textbox'); + + const submitButton = screen.getByRole('button', { name: /confirm/i }); + + const errorElement = within(issueTabPanel).getByRole('alert'); + + return { + tab: issueTab, + errorElement, + amountToIssueInput, + submitButton, + changeAmountToIssue: async (value: string) => await act(async () => userEvent.type(amountToIssueInput, value)), + submitForm: async () => await act(async () => userEvent.click(submitButton)) + }; +}; + +describe('issue form', () => { + it('if the issue method is called', async () => { + const { changeAmountToIssue, submitForm } = await renderIssueForm(); + + const inputAmount = 0.0001; + + await changeAmountToIssue(inputAmount.toString()); + + await submitForm(); - // Check that the issue method was called await waitFor(() => expect(mockIssueRequest).toHaveBeenCalledTimes(1)); }); + + it('if the bridge fee is correctly displayed', async () => { + const { changeAmountToIssue } = await renderIssueForm(); + + const inputAmount = 0.0001; + + await changeAmountToIssue(inputAmount.toString()); + + const bridgeFee = getBridgeFee(inputAmount); + + const bridgeFeeElement = screen.getByTestId(/issue-bridge-fee/i); + + const bridgeFeeInBTC = bridgeFee.toHuman(8); + + expect(bridgeFeeElement).toHaveTextContent(bridgeFeeInBTC); + + const bridgeFeeInUSD = displayMonetaryAmountInUSDFormat(bridgeFee, MOCK_TOKEN_PRICES.bitcoin.usd); + + expect(bridgeFeeElement).toHaveTextContent(bridgeFeeInUSD.toString()); + }); + + it('if the security deposit is correctly displayed', async () => { + const { changeAmountToIssue } = await renderIssueForm(); + + const inputAmount = 0.0001; + + await changeAmountToIssue(inputAmount.toString()); + + const btcToGovernanceTokenRate = mockOracleGetExchangeRate(GOVERNANCE_TOKEN); + + const monetaryBtcAmount = new BitcoinAmount(inputAmount); + + const securityDeposit = btcToGovernanceTokenRate + .toCounter(monetaryBtcAmount) + .mul(mockFeeGetIssueGriefingCollateralRate()); + + const securityDepositElement = screen.getByTestId(/security-deposit/i); + + const securityDepositInGovernanceToken = displayMonetaryAmount(securityDeposit); + + expect(securityDepositElement).toHaveTextContent(securityDepositInGovernanceToken); + + const securityDepositInUSD = displayMonetaryAmountInUSDFormat(securityDeposit, mockGovernanceTokenPriceInUsd); + + expect(securityDepositElement).toHaveTextContent(securityDepositInUSD); + }); + + it('if the transaction fee is correctly displayed', async () => { + const { changeAmountToIssue } = await renderIssueForm(); + + const inputAmount = 0.0001; + + await changeAmountToIssue(inputAmount.toString()); + + const transactionFeeElement = screen.getByTestId(/transaction-fee/i); + + const txFeeInGovernanceToken = displayMonetaryAmount(TRANSACTION_FEE_AMOUNT); + + expect(transactionFeeElement).toHaveTextContent(txFeeInGovernanceToken); + + const txFeeInUSD = displayMonetaryAmountInUSDFormat(TRANSACTION_FEE_AMOUNT, mockGovernanceTokenPriceInUsd); + + expect(transactionFeeElement).toHaveTextContent(txFeeInUSD); + }); + + it('if the total receiving amount is correctly displayed', async () => { + const { changeAmountToIssue } = await renderIssueForm(); + + const inputAmount = 0.0001; + + await changeAmountToIssue(inputAmount.toString()); + + const totalElement = screen.getByTestId(/total-receiving-amount/i); + + const bridgeFee = getBridgeFee(inputAmount); + + const monetaryBtcAmount = new BitcoinAmount(inputAmount); + + const total = monetaryBtcAmount.sub(bridgeFee); + + const totalInBTC = total.toHuman(8); + + expect(totalElement).toHaveTextContent(totalInBTC); + + const totalInUSD = displayMonetaryAmountInUSDFormat(total, MOCK_TOKEN_PRICES.bitcoin.usd); + + expect(totalElement).toHaveTextContent(totalInUSD.toString()); + }); + + it('if the max issuable amounts are correctly displayed', async () => { + await renderIssueForm(); + + const singleMaxIssuableAmountElement = screen.getByTestId(/single-max-issuable/i); + + const singleMaxIssuableAmount = displayMonetaryAmount(mockIssueGetRequestLimits().singleVaultMaxIssuable); + + expect(singleMaxIssuableAmountElement).toHaveTextContent(singleMaxIssuableAmount); + + const totalMaxIssuableAmountElement = screen.getByTestId(/total-max-issuable/i); + + const totalMaxIssuableAmount = displayMonetaryAmount(mockIssueGetRequestLimits().totalMaxIssuable); + + expect(totalMaxIssuableAmountElement).toHaveTextContent(totalMaxIssuableAmount); + }); + + it('when the governance token balance is less than required', async () => { + mockTokensBalance.mockImplementation((currency: CurrencyExt, _id: AccountId) => { + if (currency.ticker === GOVERNANCE_TOKEN.ticker) { + return new ChainBalance(currency, 0, 0); + } else { + return new ChainBalance(currency, MOCK_TOKEN_BALANCE, MOCK_TOKEN_BALANCE); + } + }); + + const { changeAmountToIssue, submitForm, errorElement } = await renderIssueForm(); + + const inputAmount = 0.0001; + + await changeAmountToIssue(inputAmount.toString()); + + expect(errorElement.textContent).toMatchInlineSnapshot(`"Insufficient free INTR for security deposit and fees"`); + + await submitForm(); + + await waitFor(() => expect(mockIssueRequest).not.toHaveBeenCalled()); + + mockTokensBalance.mockImplementation( + (currency: CurrencyExt, _id: AccountId) => new ChainBalance(currency, MOCK_TOKEN_BALANCE, MOCK_TOKEN_BALANCE) + ); + }); + + it('when the input amount is greater than the single vault max issuable amount', async () => { + const { changeAmountToIssue, submitForm, errorElement } = await renderIssueForm(); + + const inputAmount = mockIssueGetRequestLimits().singleVaultMaxIssuable.add(newMonetaryAmount('1', WRAPPED_TOKEN)); + + await changeAmountToIssue(inputAmount.toString()); + + expect(errorElement.textContent).toMatchInlineSnapshot(`"Please enter less than 0.565 IBTC."`); + + await submitForm(); + + await waitFor(() => expect(mockIssueRequest).not.toHaveBeenCalled()); + }); + + it('when the input amount is less than the Bitcoin dust amount', async () => { + const { changeAmountToIssue, submitForm, errorElement } = await renderIssueForm(); + + const inputAmount = mockIssueGetDustValue().sub(newMonetaryAmount(1, Bitcoin)); + + await changeAmountToIssue(inputAmount.toString()); + + expect(errorElement.textContent).toMatchInlineSnapshot( + `"Please enter an amount greater than Bitcoin dust limit (0 BTC)."` + ); + + await submitForm(); + + await waitFor(() => expect(mockIssueRequest).not.toHaveBeenCalled()); + }); + + it('when the parachain is more than 6 blocks behind', async () => { + mockBtcRelayGetLatestBlockHeight.mockImplementation(() => MOCK_BTC_RELAY_HEIGHT); + mockElectrsAPIGetLatestBlockHeight.mockImplementation(() => BLOCKS_BEHIND_LIMIT + MOCK_BTC_RELAY_HEIGHT + 1); + + const { changeAmountToIssue, submitForm, errorElement } = await renderIssueForm(); + + const inputAmount = 0.0001; + + await changeAmountToIssue(inputAmount.toString()); + + expect(errorElement.textContent).toMatchInlineSnapshot( + `"You can't issue IBTC at the moment because IBTC parachain is more than 6 blocks behind."` + ); + + await submitForm(); + + await waitFor(() => expect(mockIssueRequest).not.toHaveBeenCalled()); + + mockBtcRelayGetLatestBlockHeight.mockImplementation(() => MOCK_BTC_RELAY_HEIGHT); + mockElectrsAPIGetLatestBlockHeight.mockImplementation(() => MOCK_BITCOIN_HEIGHT); + }); + + it('when the oracle is offline', async () => { + mockOracleGetExchangeRate.mockImplementation( + (currency: CurrencyExt) => new ExchangeRate(Bitcoin, currency, new Big(0)) + ); + + const { changeAmountToIssue, submitForm, errorElement } = await renderIssueForm(); + + const inputAmount = 0.0001; + + await changeAmountToIssue(inputAmount.toString()); + + expect(errorElement.textContent).toMatchInlineSnapshot( + `"You can't issue IBTC at the moment because oracle is offline."` + ); + + await submitForm(); + + await waitFor(() => expect(mockIssueRequest).not.toHaveBeenCalled()); + + mockOracleGetExchangeRate.mockImplementation( + (currency: CurrencyExt) => new ExchangeRate(Bitcoin, currency, MOCK_EXCHANGE_RATE) + ); + }); }); diff --git a/src/test/pages/Loans/borrow.test.tsx b/src/test/pages/Loans/borrow.test.tsx new file mode 100644 index 0000000000..d8b2d22ab3 --- /dev/null +++ b/src/test/pages/Loans/borrow.test.tsx @@ -0,0 +1,146 @@ +import '@testing-library/jest-dom'; + +import Big from 'big.js'; + +import App from '@/App'; +import { WRAPPED_TOKEN } from '@/config/relay-chains'; +import { + DEFAULT_ASSETS, + DEFAULT_BORROW_POSITIONS, + DEFAULT_IBTC, + DEFAULT_IBTC_LOAN_ASSET, + DEFAULT_LEND_POSITIONS, + DEFAULT_LENDING_STATS, + mockBorrow, + mockCalculateBorrowLimitBtcChange, + mockCalculateLtvAndThresholdsChange, + mockGetBorrowPositionsOfAccount, + mockGetLendingStats, + mockGetLendPositionsOfAccount, + mockGetLoanAssets +} from '@/test/mocks/@interlay/interbtc-api/parachain/loans'; + +import { render, userEvent, waitFor } from '../../test-utils'; +import { submitForm, withinModalTabPanel } from '../utils/table'; +import { TABLES } from './constants'; + +const path = '/lending'; +const tab = 'borrow'; + +jest.mock('../../../parts/Layout', () => { + const MockedLayout: React.FC = ({ children }: any) => children; + MockedLayout.displayName = 'MockedLayout'; + return MockedLayout; +}); + +describe('Borrow Flow', () => { + beforeEach(() => { + mockGetBorrowPositionsOfAccount.mockReturnValue(DEFAULT_BORROW_POSITIONS); + mockGetLendPositionsOfAccount.mockReturnValue(DEFAULT_LEND_POSITIONS); + mockGetLendingStats.mockReturnValue(DEFAULT_LENDING_STATS); + }); + + it('should be able to borrow', async () => { + await render(, { path }); + + const tabPanel = withinModalTabPanel(TABLES.BORROW.POSITION, 'IBTC', tab); + + userEvent.type(tabPanel.getByRole('textbox', { name: 'borrow amount' }), DEFAULT_IBTC.AMOUNT.SMALL); + + await submitForm(tabPanel, 'borrow'); + + expect(mockBorrow).toHaveBeenCalledWith(WRAPPED_TOKEN, DEFAULT_IBTC.MONETARY.SMALL); + }); + + it('should not be able to borrow due to borrow limit', async () => { + mockCalculateBorrowLimitBtcChange.mockReturnValue(DEFAULT_IBTC.MONETARY.VERY_SMALL); + mockGetLendingStats.mockReturnValue({ ...DEFAULT_LENDING_STATS, borrowLimitBtc: DEFAULT_IBTC.MONETARY.VERY_SMALL }); + + await render(, { path }); + + const tabPanel = withinModalTabPanel(TABLES.BORROW.POSITION, 'IBTC', tab, true); + + // If there is collateral, modal LTV meter should be rendered + expect(tabPanel.getByRole('meter', { name: /ltv meter/i })).toBeInTheDocument(); + + userEvent.type(tabPanel.getByRole('textbox', { name: 'borrow amount' }), DEFAULT_IBTC.AMOUNT.MEDIUM); + + await waitFor(() => { + expect(tabPanel.getByRole('textbox', { name: 'borrow amount' })).toHaveErrorMessage(''); + }); + + userEvent.click(tabPanel.getByRole('button', { name: /borrow/i })); + + await waitFor(() => { + expect(mockBorrow).not.toHaveBeenCalled(); + }); + }); + + it('should not be able to borrow due lack of available capacity', async () => { + mockGetLoanAssets.mockReturnValue({ + ...DEFAULT_ASSETS, + IBTC: { ...DEFAULT_IBTC_LOAN_ASSET, availableCapacity: DEFAULT_IBTC.MONETARY.VERY_SMALL } + }); + + await render(, { path }); + + const tabPanel = withinModalTabPanel(TABLES.BORROW.POSITION, 'IBTC', tab, true); + + userEvent.type(tabPanel.getByRole('textbox', { name: 'borrow amount' }), DEFAULT_IBTC.AMOUNT.MEDIUM); + + await waitFor(() => { + expect(tabPanel.getByRole('textbox', { name: 'borrow amount' })).toHaveErrorMessage(''); + }); + + userEvent.click(tabPanel.getByRole('button', { name: /borrow/i })); + + await waitFor(() => { + expect(mockBorrow).not.toHaveBeenCalled(); + }); + }); + + it('should not be able to borrow due too many borrows', async () => { + mockGetLoanAssets.mockReturnValue({ + ...DEFAULT_ASSETS, + IBTC: { + ...DEFAULT_IBTC_LOAN_ASSET, + borrowCap: DEFAULT_IBTC.MONETARY.MEDIUM, + totalBorrows: DEFAULT_IBTC.MONETARY.MEDIUM + } + }); + + await render(, { path }); + + const tabPanel = withinModalTabPanel(TABLES.BORROW.POSITION, 'IBTC', tab, true); + + userEvent.type(tabPanel.getByRole('textbox', { name: 'borrow amount' }), DEFAULT_IBTC.AMOUNT.MEDIUM); + + await waitFor(() => { + expect(tabPanel.getByRole('textbox', { name: 'borrow amount' })).toHaveErrorMessage(''); + }); + + userEvent.click(tabPanel.getByRole('button', { name: /borrow/i })); + + await waitFor(() => { + expect(mockBorrow).not.toHaveBeenCalled(); + }); + }); + + it('should display liquidation alert', async () => { + mockCalculateLtvAndThresholdsChange.mockReturnValue({ + collateralThresholdWeightedAverage: new Big(0.5), + liquidationThresholdWeightedAverage: new Big(0.75), + ltv: new Big(0.75) + }); + + await render(, { path }); + + const tabPanel = withinModalTabPanel(TABLES.BORROW.POSITION, 'IBTC', tab, true); + + userEvent.type(tabPanel.getByRole('textbox', { name: 'borrow amount' }), DEFAULT_IBTC.AMOUNT.MEDIUM); + + await waitFor(() => { + expect(tabPanel.getByRole('alert')).toBeInTheDocument(); + }); + }); +}); diff --git a/src/test/pages/Loans/collateral.test.tsx b/src/test/pages/Loans/collateral.test.tsx new file mode 100644 index 0000000000..40534ca358 --- /dev/null +++ b/src/test/pages/Loans/collateral.test.tsx @@ -0,0 +1,122 @@ +import '@testing-library/jest-dom'; + +import App from '@/App'; +import { GOVERNANCE_TOKEN, WRAPPED_TOKEN } from '@/config/relay-chains'; +import { + DEFAULT_BORROW_POSITIONS, + DEFAULT_CALCULATE_BORROW_LIMIT, + DEFAULT_LEND_POSITIONS, + DEFAULT_POSITIONS, + DEFAULT_THRESOLD, + mockCalculateLtvAndThresholdsChange, + mockDisableAsCollateral, + mockEnableAsCollateral, + mockGetBorrowPositionsOfAccount, + mockGetLendPositionsOfAccount +} from '@/test/mocks/@interlay/interbtc-api/parachain/loans'; + +import { render, screen, userEvent, waitForElementToBeRemoved, within } from '../../test-utils'; +import { withinTableRow } from '../utils/table'; +import { TABLES } from './constants'; + +const path = '/lending'; + +const withinCollateralModal = (asset = 'IBTC') => { + const row = withinTableRow(TABLES.LEND.POSITION, asset); + + userEvent.click(row.getByRole('switch', { name: `toggle ${asset} collateral` })); + + return within(screen.getByRole('dialog')); +}; + +describe('Collateral Flow', () => { + beforeEach(() => { + mockGetBorrowPositionsOfAccount.mockReturnValue(DEFAULT_BORROW_POSITIONS); + mockGetLendPositionsOfAccount.mockReturnValue(DEFAULT_LEND_POSITIONS); + }); + + it('should be able to enable collateral when there is no collateral', async () => { + mockGetLendPositionsOfAccount.mockReturnValue([{ ...DEFAULT_POSITIONS.LEND.IBTC, isCollateral: false }]); + + await render(, { path }); + + const modal = withinCollateralModal(); + + userEvent.click(modal.getByRole('button', { name: /use IBTC as collateral/i })); + + await waitForElementToBeRemoved(screen.getByRole('dialog')); + + expect(mockEnableAsCollateral).toHaveBeenCalledTimes(1); + expect(mockEnableAsCollateral).toHaveBeenCalledWith(WRAPPED_TOKEN); + }); + + it('should be able to enable collateral when there is already collateral', async () => { + mockGetBorrowPositionsOfAccount.mockReturnValue(DEFAULT_BORROW_POSITIONS); + mockGetLendPositionsOfAccount.mockReturnValue([ + DEFAULT_POSITIONS.LEND.IBTC, + { ...DEFAULT_POSITIONS.LEND.INTR, isCollateral: false } + ]); + + await render(, { path }); + + const modal2 = withinCollateralModal('INTR'); + + userEvent.click(modal2.getByRole('button', { name: /use INTR as collateral/i })); + + await waitForElementToBeRemoved(screen.getByRole('dialog')); + + expect(mockEnableAsCollateral).toHaveBeenCalledTimes(1); + expect(mockEnableAsCollateral).toHaveBeenCalledWith(GOVERNANCE_TOKEN); + }); + + it('should be able to disable collateral when there are no borrow positions', async () => { + mockGetBorrowPositionsOfAccount.mockReturnValue([]); + + await render(, { path }); + + const modal = withinCollateralModal(); + + userEvent.click(modal.getByRole('button', { name: /disable IBTC/i })); + + await waitForElementToBeRemoved(screen.getByRole('dialog')); + + expect(mockDisableAsCollateral).toHaveBeenCalledTimes(1); + expect(mockDisableAsCollateral).toHaveBeenCalledWith(WRAPPED_TOKEN); + }); + + it('should be able to disable collateral when there are open borrow positions', async () => { + mockGetBorrowPositionsOfAccount.mockReturnValue([DEFAULT_POSITIONS.BORROW.INTR]); + mockGetLendPositionsOfAccount.mockReturnValue([DEFAULT_POSITIONS.LEND.IBTC, DEFAULT_POSITIONS.LEND.INTR]); + + await render(, { path }); + + const modal2 = withinCollateralModal('INTR'); + + userEvent.click(modal2.getByRole('button', { name: /disable INTR/i })); + + await waitForElementToBeRemoved(screen.getByRole('dialog')); + + expect(mockDisableAsCollateral).toHaveBeenCalledTimes(1); + expect(mockDisableAsCollateral).toHaveBeenCalledWith(GOVERNANCE_TOKEN); + }); + + it('should not be able to disable collateral due to low collateral while having only one asset as collateral', async () => { + mockCalculateLtvAndThresholdsChange.mockReturnValue({ + ltv: DEFAULT_THRESOLD.HIGH, + collateralThresholdWeightedAverage: DEFAULT_THRESOLD.MEDIUM, + liquidationThresholdWeightedAverage: DEFAULT_THRESOLD.HIGH + }); + + await render(, { path }); + + const modal = withinCollateralModal(); + + userEvent.click(modal.getAllByRole('button', { name: /dismiss/i })[1]); + + expect(screen.queryByRole('dialog')).not.toBeInTheDocument(); + expect(mockEnableAsCollateral).not.toHaveBeenCalled(); + expect(mockDisableAsCollateral).not.toHaveBeenCalled(); + + mockCalculateLtvAndThresholdsChange.mockReturnValue(DEFAULT_CALCULATE_BORROW_LIMIT); + }); +}); diff --git a/src/test/pages/Loans/constants.ts b/src/test/pages/Loans/constants.ts new file mode 100644 index 0000000000..edc4b0efef --- /dev/null +++ b/src/test/pages/Loans/constants.ts @@ -0,0 +1,12 @@ +const TABLES = { + LEND: { + MARKET: 'lend market', + POSITION: 'my lend positions' + }, + BORROW: { + MARKET: 'borrow market', + POSITION: 'my borrow positions' + } +}; + +export { TABLES }; diff --git a/src/test/pages/Loans/index.test.tsx b/src/test/pages/Loans/index.test.tsx new file mode 100644 index 0000000000..5d71f63017 --- /dev/null +++ b/src/test/pages/Loans/index.test.tsx @@ -0,0 +1,154 @@ +import '@testing-library/jest-dom'; + +import { newMonetaryAmount } from '@interlay/interbtc-api'; +import Big from 'big.js'; + +import App from '@/App'; +import { GOVERNANCE_TOKEN } from '@/config/relay-chains'; +import { + DEFAULT_ASSETS, + DEFAULT_BORROW_POSITIONS, + DEFAULT_LEND_POSITIONS, + DEFAULT_LENDING_STATS, + DEFAULT_POSITIONS, + mockClaimAllSubsidyRewards, + mockGetAccountSubsidyRewards, + mockGetBorrowPositionsOfAccount, + mockGetLendingStats, + mockGetLendPositionsOfAccount, + mockGetLoanAssets +} from '@/test/mocks/@interlay/interbtc-api/parachain/loans'; + +import { render, screen, userEvent, waitFor } from '../../test-utils'; +import { getTableRow, withinTable } from '../utils/table'; +import { TABLES } from './constants'; + +const path = '/lending'; + +describe('Loans page', () => { + beforeEach(() => { + mockGetBorrowPositionsOfAccount.mockReturnValue(DEFAULT_BORROW_POSITIONS); + mockGetLendPositionsOfAccount.mockReturnValue(DEFAULT_LEND_POSITIONS); + mockGetLoanAssets.mockReturnValue(DEFAULT_ASSETS); + mockGetLendingStats.mockReturnValue(DEFAULT_LENDING_STATS); + }); + + describe('Tables Section', () => { + it.each([TABLES.LEND.MARKET, TABLES.BORROW.MARKET])( + 'should not be able to open inactive market on %s table', + async (tableName) => { + mockGetLoanAssets.mockReturnValue({ IBTC: { ...DEFAULT_ASSETS.IBTC, isActive: false } }); + + await render(, { path }); + + const row = getTableRow(tableName, 'IBTC'); + + userEvent.click(row); + + expect(screen.queryByRole('dialog')).not.toBeInTheDocument(); + } + ); + + it.each([TABLES.LEND.POSITION, TABLES.BORROW.POSITION])('should not display %s table', async (tableName) => { + mockGetBorrowPositionsOfAccount.mockReturnValue([]); + mockGetLendPositionsOfAccount.mockReturnValue([]); + + await render(, { path }); + + expect(screen.queryByRole('grid', { name: new RegExp(tableName, 'i') })).not.toBeInTheDocument(); + }); + + it('should not display my borrow positions table but instead a placeholder', async () => { + mockGetBorrowPositionsOfAccount.mockReturnValue([]); + + await render(, { path }); + + const table = withinTable(TABLES.BORROW.POSITION); + + expect(table.queryAllByRole('row')).toHaveLength(0); + expect(screen.getByText(/no borrow positions/i)).toBeInTheDocument(); + }); + }); + + describe('LTV Section', () => { + it('should not render when there no positions open', async () => { + mockGetBorrowPositionsOfAccount.mockReturnValue([]); + mockGetLendPositionsOfAccount.mockReturnValue([]); + + await render(, { path }); + + expect(screen.queryByRole('meter', { name: /ltv meter/i })).not.toBeInTheDocument(); + }); + + it('should not render when there is a non-collateral lend position', async () => { + mockGetBorrowPositionsOfAccount.mockReturnValue([]); + mockGetLendPositionsOfAccount.mockReturnValue([{ ...DEFAULT_POSITIONS.LEND.IBTC, isCollateral: false }]); + + await render(, { path }); + + expect(screen.queryByRole('meter', { name: /ltv meter/i })).not.toBeInTheDocument(); + }); + + it('should render when there is a lend position as collateral', async () => { + mockGetBorrowPositionsOfAccount.mockReturnValue([]); + mockGetLendPositionsOfAccount.mockReturnValue([DEFAULT_POSITIONS.LEND.IBTC]); + + await render(, { path }); + + expect(screen.getByRole('meter', { name: /ltv meter/i })).toBeInTheDocument(); + expect(screen.getByText(/borrow balance/i)).toBeInTheDocument(); + expect(screen.getByText(/collateral balance/i)).toBeInTheDocument(); + expect(screen.getByText(/loan status/i)).toBeInTheDocument(); + }); + + it('should display low risk', async () => { + await render(, { path }); + + expect(screen.getByText(/low risk/i)).toBeInTheDocument(); + }); + + it('should display medium risk', async () => { + mockGetLendingStats.mockReturnValue({ + ...DEFAULT_LENDING_STATS, + collateralThresholdWeightedAverage: new Big(0.5), + liquidationThresholdWeightedAverage: new Big(0.75), + ltv: new Big(0.5) + }); + + await render(, { path }); + + expect(screen.getByText(/medium risk/i)).toBeInTheDocument(); + }); + + it('should display liquidation risk', async () => { + mockGetLendingStats.mockReturnValue({ + ...DEFAULT_LENDING_STATS, + collateralThresholdWeightedAverage: new Big(0.5), + liquidationThresholdWeightedAverage: new Big(0.75), + ltv: new Big(0.75) + }); + + await render(, { path }); + + expect(screen.getByText(/liquidation risk/i)).toBeInTheDocument(); + }); + }); + + describe('Rewards', () => { + it('should be able to claim', async () => { + await render(, { path }); + + userEvent.click(screen.getByRole('button', { name: /claim/i })); + + await waitFor(() => expect(mockClaimAllSubsidyRewards).toHaveBeenCalledTimes(1)); + }); + + it('should not be able to claim', async () => { + mockGetAccountSubsidyRewards.mockReturnValue(newMonetaryAmount(0, GOVERNANCE_TOKEN)); + + await render(, { path }); + + expect(screen.queryByRole('button', { name: /claim/i })).not.toBeInTheDocument(); + }); + }); +}); diff --git a/src/test/pages/Loans/lend.test.tsx b/src/test/pages/Loans/lend.test.tsx new file mode 100644 index 0000000000..408769b274 --- /dev/null +++ b/src/test/pages/Loans/lend.test.tsx @@ -0,0 +1,99 @@ +import '@testing-library/jest-dom'; + +import App from '@/App'; +import { WRAPPED_TOKEN } from '@/config/relay-chains'; +import { + DEFAULT_TOKENS_BALANCE_FN, + EMPTY_TOKENS_BALANCE_FN, + mockTokensBalance +} from '@/test/mocks/@interlay/interbtc-api'; +import { + DEFAULT_ASSETS, + DEFAULT_BORROW_POSITIONS, + DEFAULT_IBTC, + DEFAULT_IBTC_LOAN_ASSET, + DEFAULT_LEND_POSITIONS, + mockGetBorrowPositionsOfAccount, + mockGetLendPositionsOfAccount, + mockGetLoanAssets, + mockLend +} from '@/test/mocks/@interlay/interbtc-api/parachain/loans'; + +import { render, screen, userEvent, waitFor } from '../../test-utils'; +import { submitForm, withinModalTabPanel } from '../utils/table'; +import { TABLES } from './constants'; + +const path = '/lending'; +const tab = 'lend'; + +describe('Lending Flow', () => { + beforeEach(() => { + mockGetLoanAssets.mockReturnValue(DEFAULT_ASSETS); + mockGetBorrowPositionsOfAccount.mockReturnValue(DEFAULT_BORROW_POSITIONS); + mockGetLendPositionsOfAccount.mockReturnValue(DEFAULT_LEND_POSITIONS); + mockLend.mockRestore(); + mockTokensBalance.mockImplementation(DEFAULT_TOKENS_BALANCE_FN); + }); + + it('should be able to lend', async () => { + await render(, { path }); + + const tabPanel = withinModalTabPanel(TABLES.LEND.POSITION, 'IBTC', tab); + + expect(tabPanel.getByRole('meter', { name: /ltv meter/i })).toBeInTheDocument(); + + userEvent.type(tabPanel.getByRole('textbox', { name: 'lend amount' }), DEFAULT_IBTC.AMOUNT.MEDIUM); + + await submitForm(tabPanel, 'lend'); + + expect(mockLend).toHaveBeenCalledWith(WRAPPED_TOKEN, DEFAULT_IBTC.MONETARY.MEDIUM); + }); + + it('should not be able to lend over available balance', async () => { + mockTokensBalance.mockImplementation(EMPTY_TOKENS_BALANCE_FN); + + await render(, { path }); + + const tabPanel = withinModalTabPanel(TABLES.LEND.POSITION, 'IBTC', tab); + + userEvent.type(tabPanel.getByRole('textbox', { name: 'lend amount' }), DEFAULT_IBTC.AMOUNT.MEDIUM); + + await waitFor(() => { + expect(tabPanel.getByRole('textbox', { name: 'lend amount' })).toHaveErrorMessage(''); + }); + + userEvent.click(tabPanel.getByRole('button', { name: /lend/i })); + + await waitFor(() => { + expect(screen.getByRole('dialog')).toBeInTheDocument(); + expect(mockLend).not.toHaveBeenCalled(); + }); + }); + + it('should not be able to lend due to lack of borrows and supply cap', async () => { + mockGetLoanAssets.mockReturnValue({ + IBTC: { + ...DEFAULT_IBTC_LOAN_ASSET, + totalBorrows: DEFAULT_IBTC.MONETARY.VERY_LARGE, + supplyCap: DEFAULT_IBTC.MONETARY.EMPTY + } + }); + + await render(, { path }); + + const tabPanel = withinModalTabPanel(TABLES.LEND.POSITION, 'IBTC', tab); + + userEvent.type(tabPanel.getByRole('textbox', { name: 'lend amount' }), DEFAULT_IBTC.AMOUNT.MEDIUM); + + await waitFor(() => { + expect(tabPanel.getByRole('textbox', { name: 'lend amount' })).toHaveErrorMessage(''); + }); + + userEvent.click(tabPanel.getByRole('button', { name: /lend/i })); + + await waitFor(() => { + expect(screen.getByRole('dialog')).toBeInTheDocument(); + expect(mockLend).not.toHaveBeenCalled(); + }); + }); +}); diff --git a/src/test/pages/Loans/repay.test.tsx b/src/test/pages/Loans/repay.test.tsx new file mode 100644 index 0000000000..67b4677915 --- /dev/null +++ b/src/test/pages/Loans/repay.test.tsx @@ -0,0 +1,138 @@ +import '@testing-library/jest-dom'; + +import { ChainBalance, CurrencyExt, newMonetaryAmount } from '@interlay/interbtc-api'; +import { AccountId } from '@polkadot/types/interfaces'; + +import App from '@/App'; +import { WRAPPED_TOKEN } from '@/config/relay-chains'; +import { + DEFAULT_TOKENS_BALANCE_FN, + EMPTY_TOKENS_BALANCE_FN, + MOCK_TOKEN_BALANCE, + mockTokensBalance +} from '@/test/mocks/@interlay/interbtc-api'; +import { + DEFAULT_BORROW_POSITIONS, + DEFAULT_IBTC, + DEFAULT_LEND_POSITIONS, + DEFAULT_POSITIONS, + mockGetBorrowPositionsOfAccount, + mockGetLendPositionsOfAccount, + mockRepay, + mockRepayAll +} from '@/test/mocks/@interlay/interbtc-api/parachain/loans'; + +import { act, render, screen, userEvent, waitFor } from '../../test-utils'; +import { submitForm, withinModalTabPanel } from '../utils/table'; +import { TABLES } from './constants'; + +const path = '/lending'; +const tab = 'repay'; + +describe('Repay Flow', () => { + beforeEach(() => { + mockGetBorrowPositionsOfAccount.mockReturnValue(DEFAULT_BORROW_POSITIONS); + mockGetLendPositionsOfAccount.mockReturnValue(DEFAULT_LEND_POSITIONS); + mockTokensBalance.mockImplementation(DEFAULT_TOKENS_BALANCE_FN); + }); + + it('should be able to repay', async () => { + // SCENARIO: user is partially repaying loan + await render(, { path }); + + const tabPanel = withinModalTabPanel(TABLES.BORROW.POSITION, 'IBTC', tab, true); + + // should render modal with ltv meter + expect(tabPanel.getByRole('meter', { name: /ltv meter/i })).toBeInTheDocument(); + + userEvent.type(tabPanel.getByRole('textbox', { name: 'repay amount' }), DEFAULT_IBTC.AMOUNT.SMALL); + + await submitForm(tabPanel, 'repay'); + + expect(mockRepay).toHaveBeenCalledWith(WRAPPED_TOKEN, DEFAULT_IBTC.MONETARY.SMALL); + }); + + it('should be able repay all by using max button', async () => { + await render(, { path }); + + const tabPanel = withinModalTabPanel(TABLES.BORROW.POSITION, 'IBTC', tab, true); + + userEvent.click( + tabPanel.getByRole('button', { + name: /max/i + }) + ); + + await submitForm(tabPanel, 'repay'); + + expect(mockRepayAll).toHaveBeenCalledWith(WRAPPED_TOKEN); + }); + + it('should be able repay all by typing max amount', async () => { + await render(, { path }); + + const tabPanel = withinModalTabPanel(TABLES.BORROW.POSITION, 'IBTC', tab, true); + + const replayAllAmount = DEFAULT_POSITIONS.BORROW.IBTC.amount.add(DEFAULT_POSITIONS.BORROW.IBTC.accumulatedDebt); + + userEvent.type(tabPanel.getByRole('textbox', { name: 'repay amount' }), replayAllAmount.toString()); + + // Wait for debounce + await act(async () => { + await new Promise((resolve) => setTimeout(resolve, 500)); + }); + + await submitForm(tabPanel, 'repay'); + + expect(mockRepayAll).toHaveBeenCalledWith(WRAPPED_TOKEN); + }); + + it('should not be able to repay over available balance', async () => { + mockTokensBalance.mockImplementation(EMPTY_TOKENS_BALANCE_FN); + + await render(, { path }); + + const tabPanel = withinModalTabPanel(TABLES.BORROW.POSITION, 'IBTC', tab, true); + + userEvent.type(tabPanel.getByRole('textbox', { name: 'repay amount' }), DEFAULT_IBTC.AMOUNT.VERY_LARGE); + + await waitFor(() => { + expect(tabPanel.getByRole('textbox', { name: 'repay amount' })).toHaveErrorMessage(''); + }); + + userEvent.click(tabPanel.getByRole('button', { name: /repay/i })); + + await waitFor(() => { + expect(screen.getByRole('dialog')).toBeInTheDocument(); + expect(mockRepay).not.toHaveBeenCalled(); + expect(mockRepayAll).not.toHaveBeenCalled(); + }); + }); + + it('should partially repay loan while applying max balance when there are not enough funds to pay the entire loan', async () => { + const mockWrappedTokenBalance = 10000000; + + mockTokensBalance.mockImplementation((currency: CurrencyExt, _id: AccountId) => { + if (currency.ticker === WRAPPED_TOKEN.ticker) { + return new ChainBalance(currency, mockWrappedTokenBalance, mockWrappedTokenBalance, mockWrappedTokenBalance); + } + + return new ChainBalance(currency, MOCK_TOKEN_BALANCE, MOCK_TOKEN_BALANCE, MOCK_TOKEN_BALANCE); + }); + + await render(, { path }); + + const tabPanel = withinModalTabPanel(TABLES.BORROW.POSITION, 'IBTC', tab, true); + + userEvent.click( + tabPanel.getByRole('button', { + name: /max/i + }) + ); + + await submitForm(tabPanel, 'repay'); + + expect(mockRepay).toHaveBeenCalledWith(WRAPPED_TOKEN, newMonetaryAmount(mockWrappedTokenBalance, WRAPPED_TOKEN)); + expect(mockRepayAll).not.toHaveBeenCalled(); + }); +}); diff --git a/src/test/pages/Loans/withdraw.test.tsx b/src/test/pages/Loans/withdraw.test.tsx new file mode 100644 index 0000000000..ec750efc30 --- /dev/null +++ b/src/test/pages/Loans/withdraw.test.tsx @@ -0,0 +1,148 @@ +import '@testing-library/jest-dom'; + +import Big from 'big.js'; + +import App from '@/App'; +import { WRAPPED_TOKEN } from '@/config/relay-chains'; +import { + DEFAULT_BORROW_POSITIONS, + DEFAULT_IBTC, + DEFAULT_LEND_POSITIONS, + DEFAULT_LENDING_STATS, + DEFAULT_POSITIONS, + mockCalculateLtvAndThresholdsChange, + mockGetBorrowPositionsOfAccount, + mockGetLendingStats, + mockGetLendPositionsOfAccount, + mockWithdraw, + mockWithdrawAll +} from '@/test/mocks/@interlay/interbtc-api/parachain/loans'; + +import { act, render, screen, userEvent, waitFor } from '../../test-utils'; +import { submitForm, withinModalTabPanel } from '../utils/table'; +import { TABLES } from './constants'; + +const path = '/lending'; +const tab = 'withdraw'; + +describe('Withdraw Flow', () => { + beforeEach(() => { + mockGetBorrowPositionsOfAccount.mockReturnValue(DEFAULT_BORROW_POSITIONS); + mockGetLendPositionsOfAccount.mockReturnValue(DEFAULT_LEND_POSITIONS); + mockGetLendingStats.mockReturnValue(DEFAULT_LENDING_STATS); + }); + + it('should be able to partially withdraw when there are no borrow positions', async () => { + await render(, { path }); + + const tabPanel = withinModalTabPanel(TABLES.LEND.POSITION, 'IBTC', tab, true); + + // should render modal with ltv meter + expect(tabPanel.getByRole('meter', { name: /ltv meter/i })).toBeInTheDocument(); + + userEvent.type(tabPanel.getByRole('textbox', { name: 'withdraw amount' }), DEFAULT_IBTC.AMOUNT.SMALL); + + await submitForm(tabPanel, 'withdraw'); + + expect(mockWithdraw).toHaveBeenCalledWith(WRAPPED_TOKEN, DEFAULT_IBTC.MONETARY.SMALL); + }); + + it('should be able to withdraw all when there are no borrow positions by using max button', async () => { + mockGetBorrowPositionsOfAccount.mockReturnValue([]); + + await render(, { path }); + + const tabPanel = withinModalTabPanel(TABLES.LEND.POSITION, 'IBTC', tab, true); + + userEvent.click( + tabPanel.getByRole('button', { + name: /max/i + }) + ); + + await submitForm(tabPanel, 'withdraw'); + + expect(mockWithdrawAll).toHaveBeenCalledWith(WRAPPED_TOKEN); + }); + + it('should be able to withdraw all when there are no borrow positions by typing max amount', async () => { + mockGetBorrowPositionsOfAccount.mockReturnValue([]); + + await render(, { path }); + + const tabPanel = withinModalTabPanel(TABLES.LEND.POSITION, 'IBTC', tab, true); + + userEvent.type( + tabPanel.getByRole('textbox', { name: 'withdraw amount' }), + DEFAULT_POSITIONS.LEND.IBTC.amount.toString() + ); + + // Wait for debounce + await act(async () => { + await new Promise((resolve) => setTimeout(resolve, 500)); + }); + + await submitForm(tabPanel, 'withdraw'); + + expect(mockWithdrawAll).toHaveBeenCalledWith(WRAPPED_TOKEN); + }); + + it('should partially withdraw while applying max withdraw when there is low borrow limit', async () => { + mockGetLendingStats.mockReturnValue({ ...DEFAULT_LENDING_STATS, borrowLimitBtc: DEFAULT_IBTC.MONETARY.VERY_SMALL }); + + await render(, { path }); + + const tabPanel = withinModalTabPanel(TABLES.LEND.POSITION, 'IBTC', tab, true); + + userEvent.click( + tabPanel.getByRole('button', { + name: /max/i + }) + ); + + await submitForm(tabPanel, 'withdraw'); + + expect(mockWithdraw).toHaveBeenCalled(); + expect(mockWithdrawAll).not.toHaveBeenCalled(); + }); + + it('should not be able to withdraw due low borrow limit', async () => { + mockGetLendingStats.mockReturnValue({ ...DEFAULT_LENDING_STATS, borrowLimitBtc: DEFAULT_IBTC.MONETARY.VERY_SMALL }); + + await render(, { path }); + + const tabPanel = withinModalTabPanel(TABLES.LEND.POSITION, 'IBTC', tab, true); + + userEvent.type(tabPanel.getByRole('textbox', { name: 'withdraw amount' }), DEFAULT_IBTC.AMOUNT.MEDIUM); + + await waitFor(() => { + expect(tabPanel.getByRole('textbox', { name: 'withdraw amount' })).toHaveErrorMessage(''); + }); + + userEvent.click(tabPanel.getByRole('button', { name: /withdraw/i })); + + await waitFor(() => { + expect(screen.getByRole('dialog')).toBeInTheDocument(); + expect(mockWithdraw).not.toHaveBeenCalled(); + expect(mockWithdrawAll).not.toHaveBeenCalled(); + }); + }); + + it('should display liquidation alert', async () => { + mockCalculateLtvAndThresholdsChange.mockReturnValue({ + collateralThresholdWeightedAverage: new Big(0.5), + liquidationThresholdWeightedAverage: new Big(0.75), + ltv: new Big(0.75) + }); + + await render(, { path }); + + const tabPanel = withinModalTabPanel(TABLES.LEND.POSITION, 'IBTC', tab, true); + + userEvent.type(tabPanel.getByRole('textbox', { name: 'withdraw amount' }), DEFAULT_IBTC.AMOUNT.MEDIUM); + + await waitFor(() => { + expect(tabPanel.getByRole('alert')).toBeInTheDocument(); + }); + }); +}); diff --git a/src/test/pages/Pools.test.tsx b/src/test/pages/Pools.test.tsx new file mode 100644 index 0000000000..ef5c3034cb --- /dev/null +++ b/src/test/pages/Pools.test.tsx @@ -0,0 +1,230 @@ +import { newMonetaryAmount } from '@interlay/interbtc-api'; + +import App from '@/App'; + +import { DEFAULT_DEADLINE_BLOCK_NUMBER, mockGetFutureBlockNumber } from '../mocks/@interlay/interbtc-api'; +import { + ACCOUNT_WITH_FULL_LIQUIDITY, + ACCOUNT_WITH_SOME_LIQUIDITY, + DEFAULT_ACCOUNT_LIQUIDITY, + DEFAULT_CLAIMABLE_REWARDS, + DEFAULT_LIQUIDITY_POOL_1, + DEFAULT_LIQUIDITY_POOL_2, + DEFAULT_LP_TOKEN_1, + DEFAULT_LP_TOKEN_2, + DEFAULT_POOLED_CURRENCIES_1, + mockAddLiquidity, + mockClaimFarmingRewards, + mockGetClaimableFarmingRewards, + mockGetLiquidityProvidedByAccount, + mockRemoveLiquidity +} from '../mocks/@interlay/interbtc-api/parachain/amm'; +import { DEFAULT_ACCOUNT_ADDRESS } from '../mocks/substrate/mocks'; +import { render, screen, userEvent, waitFor, waitForElementToBeRemoved } from '../test-utils'; +import { withinModalTabPanel, withinTable } from './utils/table'; + +const path = '/pools'; + +const TABLES = { + ACCOUNT_POOLS: 'my pools', + AVAILABLE_POOLS: 'other pools' +}; + +const TABS = { + DEPOSIT: 'deposit', + WITHDRAW: 'withdraw' +}; + +// MEMO: skipped including testing slippage +describe('Pools Page', () => { + beforeEach(() => { + mockGetLiquidityProvidedByAccount.mockResolvedValue(DEFAULT_ACCOUNT_LIQUIDITY); + }); + + it('should only render available pools', async () => { + await render(, { path }); + + const otherPoolsTable = withinTable(TABLES.AVAILABLE_POOLS); + + expect(otherPoolsTable.getAllByRole('row')).toHaveLength(2); + expect(otherPoolsTable.getByRole('row', { name: DEFAULT_LP_TOKEN_1.ticker })).toBeInTheDocument(); + expect(otherPoolsTable.getByRole('row', { name: DEFAULT_LP_TOKEN_2.ticker })).toBeInTheDocument(); + + expect(screen.queryByRole('grid', { name: new RegExp(TABLES.ACCOUNT_POOLS, 'i') })).not.toBeInTheDocument(); + }); + + it('should render both available and account pools', async () => { + mockGetLiquidityProvidedByAccount.mockResolvedValue(ACCOUNT_WITH_SOME_LIQUIDITY); + + await render(, { path }); + + const otherPoolsTable = withinTable(TABLES.AVAILABLE_POOLS); + + expect(otherPoolsTable.getAllByRole('row')).toHaveLength(1); + expect(otherPoolsTable.getByRole('row', { name: DEFAULT_LP_TOKEN_1.ticker })).toBeInTheDocument(); + + const myPoolsTable = withinTable(TABLES.ACCOUNT_POOLS); + + expect(myPoolsTable.getAllByRole('row')).toHaveLength(1); + expect(myPoolsTable.getByRole('row', { name: DEFAULT_LP_TOKEN_2.ticker })).toBeInTheDocument(); + }); + + it('should render account pools', async () => { + mockGetLiquidityProvidedByAccount.mockResolvedValue(ACCOUNT_WITH_FULL_LIQUIDITY); + + await render(, { path }); + + expect(screen.queryByRole('grid', { name: new RegExp(TABLES.AVAILABLE_POOLS, 'i') })).not.toBeInTheDocument(); + + const myPoolsTable = withinTable(TABLES.ACCOUNT_POOLS); + + expect(myPoolsTable.getAllByRole('row')).toHaveLength(2); + }); + + it('should be able to deposit', async () => { + jest + .spyOn(DEFAULT_LIQUIDITY_POOL_1, 'getLiquidityDepositInputAmounts') + .mockReturnValue(DEFAULT_POOLED_CURRENCIES_1); + + const [DEFAULT_CURRENCY_1, DEFAULT_CURRENCY_2] = DEFAULT_POOLED_CURRENCIES_1; + + await render(, { path }); + + const tabPanel = withinModalTabPanel(TABLES.AVAILABLE_POOLS, DEFAULT_LP_TOKEN_1.ticker, TABS.DEPOSIT); + + await userEvent.type( + tabPanel.getByRole('textbox', { + name: new RegExp(`${DEFAULT_CURRENCY_1.currency.ticker} deposit amount`, 'i') + }), + DEFAULT_CURRENCY_1.toString(), + { delay: 1 } + ); + + expect(DEFAULT_LIQUIDITY_POOL_1.getLiquidityDepositInputAmounts).toHaveBeenCalledWith(DEFAULT_CURRENCY_1); + + await waitFor(() => { + expect( + tabPanel.getByRole('textbox', { + name: new RegExp(`${DEFAULT_CURRENCY_2.currency.ticker} deposit amount`, 'i') + }) + ).toHaveValue(DEFAULT_CURRENCY_2.toString()); + }); + + userEvent.click(tabPanel.getByRole('button', { name: /add liquidity/i })); + + await waitForElementToBeRemoved(screen.getByRole('dialog')); + + expect(mockGetFutureBlockNumber).toHaveBeenCalledTimes(1); + expect(mockAddLiquidity).toHaveBeenCalledWith( + DEFAULT_POOLED_CURRENCIES_1, + DEFAULT_LIQUIDITY_POOL_1, + 0.1, + DEFAULT_DEADLINE_BLOCK_NUMBER, + DEFAULT_ACCOUNT_ADDRESS + ); + }); + + it('should be able to withdraw', async () => { + mockGetLiquidityProvidedByAccount.mockResolvedValue(ACCOUNT_WITH_SOME_LIQUIDITY); + + const LP_TOKEN_INPUT = newMonetaryAmount(0.1, DEFAULT_LP_TOKEN_2, true); + + jest.spyOn(DEFAULT_LIQUIDITY_POOL_2, 'getLiquidityWithdrawalPooledCurrencyAmounts').mockReturnValue([]); + + await render(, { path }); + + const tabPanel = await withinModalTabPanel(TABLES.ACCOUNT_POOLS, DEFAULT_LP_TOKEN_2.ticker, TABS.WITHDRAW, true); + + await userEvent.type( + tabPanel.getByRole('textbox', { + name: /withdraw amount/i + }), + LP_TOKEN_INPUT.toString(), + { delay: 1 } + ); + + await waitFor(() => { + expect(tabPanel.getByRole('button', { name: /remove liquidity/i })).not.toBeDisabled(); + }); + + userEvent.click(tabPanel.getByRole('button', { name: /remove liquidity/i })); + + await waitForElementToBeRemoved(screen.getByRole('dialog')); + + expect(mockGetFutureBlockNumber).toHaveBeenCalledTimes(1); + expect(mockRemoveLiquidity).toHaveBeenCalledWith( + LP_TOKEN_INPUT, + DEFAULT_LIQUIDITY_POOL_2, + 0.1, + DEFAULT_DEADLINE_BLOCK_NUMBER, + DEFAULT_ACCOUNT_ADDRESS + ); + }); + + it('should be able to claim rewards', async () => { + let app = await render(, { path }); + + userEvent.click(screen.getByRole('button', { name: /claim/i })); + + await waitFor(() => { + expect(mockClaimFarmingRewards).toHaveBeenCalledWith(DEFAULT_CLAIMABLE_REWARDS); + expect(mockClaimFarmingRewards).toHaveBeenCalledTimes(1); + }); + + app.unmount(); + + mockGetClaimableFarmingRewards.mockReturnValue(new Map()); + + app = await render(, { path }); + + expect(screen.queryByRole('button', { name: /claim/i })).not.toBeInTheDocument(); + + mockGetClaimableFarmingRewards.mockReturnValue(DEFAULT_CLAIMABLE_REWARDS); + }); + + it('should be able to enter customisable input amounts mode', async () => { + jest + .spyOn(DEFAULT_LIQUIDITY_POOL_1, 'getLiquidityDepositInputAmounts') + .mockReturnValue(DEFAULT_POOLED_CURRENCIES_1); + + const [DEFAULT_CURRENCY_1, DEFAULT_CURRENCY_2] = DEFAULT_POOLED_CURRENCIES_1; + + await render(, { path }); + + const tabPanel = withinModalTabPanel(TABLES.AVAILABLE_POOLS, DEFAULT_LP_TOKEN_1.ticker, TABS.DEPOSIT); + + await userEvent.type( + tabPanel.getByRole('textbox', { + name: new RegExp(`${DEFAULT_CURRENCY_1.currency.ticker} deposit amount`, 'i') + }), + DEFAULT_CURRENCY_1.toString(), + { delay: 1 } + ); + + expect(DEFAULT_LIQUIDITY_POOL_1.getLiquidityDepositInputAmounts).toHaveBeenCalledWith(DEFAULT_CURRENCY_1); + + await waitFor(() => { + expect( + tabPanel.getByRole('textbox', { + name: new RegExp(`${DEFAULT_CURRENCY_2.currency.ticker} deposit amount`, 'i') + }) + ).toHaveValue(DEFAULT_CURRENCY_2.toString()); + }); + + await userEvent.type( + tabPanel.getByRole('textbox', { + name: new RegExp(`${DEFAULT_CURRENCY_2.currency.ticker} deposit amount`, 'i') + }), + '10', + { delay: 1 } + ); + + await waitFor(() => { + expect( + tabPanel.getByRole('textbox', { + name: new RegExp(`${DEFAULT_CURRENCY_1.currency.ticker} deposit amount`, 'i') + }) + ).toHaveValue(DEFAULT_CURRENCY_1.toString()); + }); + }); +}); diff --git a/src/test/pages/Redeem.test.tsx b/src/test/pages/Redeem.test.tsx index bcec0acb19..8451988d3f 100644 --- a/src/test/pages/Redeem.test.tsx +++ b/src/test/pages/Redeem.test.tsx @@ -1,40 +1,275 @@ import '@testing-library/jest-dom'; +import { ChainBalance, CurrencyExt, newMonetaryAmount } from '@interlay/interbtc-api'; +import { Bitcoin, BitcoinAmount, ExchangeRate } from '@interlay/monetary-js'; +import { AccountId } from '@polkadot/types/interfaces'; +import Big from 'big.js'; + import App from '@/App'; +import { displayMonetaryAmount, displayMonetaryAmountInUSDFormat } from '@/common/utils/utils'; +import { BLOCKS_BEHIND_LIMIT } from '@/config/parachain'; +import { WRAPPED_TOKEN, WRAPPED_TOKEN_SYMBOL } from '@/config/relay-chains'; +import { BTC_ADDRESS_LABEL } from '@/pages/Bridge/RedeemForm'; -import { mockRedeemRequest } from '../mocks/@interlay/interbtc-api'; -import { act, render, screen, userEvent, waitFor } from '../test-utils'; +import { + MOCK_BITCOIN_HEIGHT, + MOCK_BTC_RELAY_HEIGHT, + MOCK_EXCHANGE_RATE, + MOCK_TOKEN_BALANCE, + mockBtcRelayGetLatestBlockHeight, + mockElectrsAPIGetLatestBlockHeight, + mockOracleGetExchangeRate, + mockRedeemGetCurrentInclusionFee, + mockRedeemGetDustValue, + mockRedeemGetFeeRate, + mockRedeemRequest, + mockTokensBalance, + mockVaultsGetVaultsWithRedeemableTokens +} from '../mocks/@interlay/interbtc-api'; +import { MOCK_TOKEN_PRICES } from '../mocks/fetch'; +import { act, render, screen, userEvent, waitFor, within } from '../test-utils'; -describe('redeemTab page', () => { - it('should redeem the IBTC', async () => { - await render(, { path: '/bridge?tab=redeem' }); +const getBridgeFee = (inputAmount: number) => { + return new BitcoinAmount(inputAmount).mul(mockRedeemGetFeeRate()); +}; - const redeemTab = screen.getByRole('tab', { name: /redeem/i }); - userEvent.click(redeemTab); +const REDEEM_TAB_PATH = '/bridge?tab=redeem'; - // Input 0.0001 IBTC - const textboxElements = screen.getAllByRole('textbox'); +// TODO: type `props` properly +const renderRedeemForm = async (props?: any) => { + await render(, { path: REDEEM_TAB_PATH }); - const amountToRedeemInput = textboxElements[0]; + const redeemTab = screen.getByRole('tab', { name: /redeem/i }); - await act(async () => { - userEvent.type(amountToRedeemInput, '0.0001'); - }); + const redeemTabPanel = screen.getByRole('tabpanel', { + name: /redeem/i + }); - const btcAddressToSendInput = textboxElements[1]; + userEvent.click(redeemTab); - await act(async () => { - userEvent.type(btcAddressToSendInput, 'tb1q3f6lu0g92q0d5jdng6m367uwpw7lnt7x3n0nqf'); - }); + const amountToRedeemInput = screen.getByRole('textbox', { name: WRAPPED_TOKEN_SYMBOL }); - const submitButton = screen.getByRole('button', { name: /confirm/i }); + const btcAddressToSendInput = screen.getByRole('textbox', { name: BTC_ADDRESS_LABEL }); - // Redeem IBTC - await act(async () => { - userEvent.click(submitButton); - }); + const submitButton = screen.getByRole('button', { name: /confirm/i }); + + const errorElement = within(redeemTabPanel).getByRole('alert', { name: WRAPPED_TOKEN_SYMBOL }); + + return { + tab: redeemTab, + errorElement, + amountToRedeemInput, + btcAddressToSendInput, + submitButton, + changeAmountToRedeem: async (value: string) => await act(async () => userEvent.type(amountToRedeemInput, value)), + changeBtcAddressToSend: async (value: string) => + await act(async () => userEvent.type(btcAddressToSendInput, value)), + submitForm: async () => await act(async () => userEvent.click(submitButton)) + }; +}; + +describe('redeem form', () => { + it('if the redeem method is called', async () => { + const { changeAmountToRedeem, changeBtcAddressToSend, submitForm } = await renderRedeemForm(); + + const inputAmount = 0.0001; + + await changeAmountToRedeem(inputAmount.toString()); + + await changeBtcAddressToSend('tb1q3f6lu0g92q0d5jdng6m367uwpw7lnt7x3n0nqf'); + + await submitForm(); - // Check that the redeem method was called await waitFor(() => expect(mockRedeemRequest).toHaveBeenCalledTimes(1)); }); + + it('if the bridge fee is correctly displayed', async () => { + const { changeAmountToRedeem } = await renderRedeemForm(); + + const inputAmount = 0.0001; + + await changeAmountToRedeem(inputAmount.toString()); + + const bridgeFee = getBridgeFee(inputAmount); + + const bridgeFeeElement = screen.getByTestId(/redeem-bridge-fee/i); + + const bridgeFeeInBTC = bridgeFee.toHuman(8); + + expect(bridgeFeeElement).toHaveTextContent(bridgeFeeInBTC); + + const bridgeFeeInUSD = displayMonetaryAmountInUSDFormat(bridgeFee, MOCK_TOKEN_PRICES.bitcoin.usd); + + expect(bridgeFeeElement).toHaveTextContent(bridgeFeeInUSD.toString()); + }); + + it('if the Bitcoin network fee is correctly displayed', async () => { + const { changeAmountToRedeem } = await renderRedeemForm(); + + const inputAmount = 0.0001; + + await changeAmountToRedeem(inputAmount.toString()); + + const bitcoinNetworkFeeElement = screen.getByTestId(/redeem-bitcoin-network-fee/i); + + const bitcoinNetworkFeeInBTC = mockRedeemGetCurrentInclusionFee().toHuman(8); + + expect(bitcoinNetworkFeeElement).toHaveTextContent(bitcoinNetworkFeeInBTC); + + const bitcoinNetworkFeeInUSD = displayMonetaryAmountInUSDFormat( + mockRedeemGetCurrentInclusionFee(), + MOCK_TOKEN_PRICES.bitcoin.usd + ); + + expect(bitcoinNetworkFeeElement).toHaveTextContent(bitcoinNetworkFeeInUSD.toString()); + }); + + it('if the total receiving amount is correctly displayed', async () => { + const { changeAmountToRedeem } = await renderRedeemForm(); + + const inputAmount = 0.0001; + + await changeAmountToRedeem(inputAmount.toString()); + + const totalElement = screen.getByTestId(/total-receiving-amount/i); + + const bridgeFee = getBridgeFee(inputAmount); + + const monetaryWrappedTokenAmount = new BitcoinAmount(inputAmount); + + const total = monetaryWrappedTokenAmount.gt(bridgeFee.add(mockRedeemGetCurrentInclusionFee())) + ? monetaryWrappedTokenAmount.sub(bridgeFee).sub(mockRedeemGetCurrentInclusionFee()) + : BitcoinAmount.zero(); + + const totalInBTC = total.toHuman(8); + + expect(totalElement).toHaveTextContent(totalInBTC); + + const totalInUSD = displayMonetaryAmountInUSDFormat(total, MOCK_TOKEN_PRICES.bitcoin.usd); + + expect(totalElement).toHaveTextContent(totalInUSD.toString()); + }); + + it('if the max redeemable amount is correctly displayed', async () => { + await renderRedeemForm(); + + const singleMaxIssuableAmountElement = screen.getByTestId(/single-max-redeemable/i); + + const singleMaxRedeemableAmount = displayMonetaryAmount( + mockVaultsGetVaultsWithRedeemableTokens().values().next().value + ); + + expect(singleMaxIssuableAmountElement).toHaveTextContent(singleMaxRedeemableAmount); + }); + + it('when the wrapped token balance is less than required', async () => { + mockTokensBalance.mockImplementation((currency: CurrencyExt, _id: AccountId) => { + if (currency.ticker === WRAPPED_TOKEN.ticker) { + return new ChainBalance(currency, 0, 0); + } else { + return new ChainBalance(currency, MOCK_TOKEN_BALANCE, MOCK_TOKEN_BALANCE); + } + }); + + const { changeAmountToRedeem, submitForm, errorElement } = await renderRedeemForm(); + + const inputAmount = 0.0001; + + await changeAmountToRedeem(inputAmount.toString()); + + expect(errorElement.textContent).toMatchInlineSnapshot( + `"Please enter an amount smaller than your current balance: 0"` + ); + + await submitForm(); + + await waitFor(() => expect(mockRedeemRequest).not.toHaveBeenCalled()); + + mockTokensBalance.mockImplementation( + (currency: CurrencyExt, _id: AccountId) => new ChainBalance(currency, MOCK_TOKEN_BALANCE, MOCK_TOKEN_BALANCE) + ); + }); + + it('when the input amount is greater than the single vault max redeemable amount', async () => { + const { changeAmountToRedeem, submitForm, errorElement } = await renderRedeemForm(); + + const inputAmount = mockVaultsGetVaultsWithRedeemableTokens() + .values() + .next() + .value.add(newMonetaryAmount('1', Bitcoin)); + + await changeAmountToRedeem(inputAmount.toString()); + + expect(errorElement.textContent).toMatchInlineSnapshot( + `" The Vault with the highest amount of locked BTC has 100 BTC which is the maximum you can redeem in a single request. You can request to redeem from multiple Vaults to get your whole amount of BTC."` + ); + + await submitForm(); + + await waitFor(() => expect(mockRedeemRequest).not.toHaveBeenCalled()); + }); + + it('when the input amount is less than the combined', async () => { + const { changeAmountToRedeem, submitForm, errorElement } = await renderRedeemForm(); + + const inputAmount = mockRedeemGetDustValue() + .add(mockRedeemGetCurrentInclusionFee()) + .sub(newMonetaryAmount(1, Bitcoin)); + + await changeAmountToRedeem(inputAmount.toString()); + + expect(errorElement.textContent).toMatchInlineSnapshot( + `"Please enter an amount above the combined Bridge Fee, Bitcoin Network Fee, and Bitcoin Dust limit (0 BTC)."` + ); + + await submitForm(); + + await waitFor(() => expect(mockRedeemRequest).not.toHaveBeenCalled()); + }); + + it('when the parachain is more than 6 blocks behind', async () => { + mockBtcRelayGetLatestBlockHeight.mockImplementation(() => MOCK_BTC_RELAY_HEIGHT); + mockElectrsAPIGetLatestBlockHeight.mockImplementation(() => BLOCKS_BEHIND_LIMIT + MOCK_BTC_RELAY_HEIGHT + 1); + + const { changeAmountToRedeem, submitForm, errorElement } = await renderRedeemForm(); + + const inputAmount = 0.0001; + + await changeAmountToRedeem(inputAmount.toString()); + + expect(errorElement.textContent).toMatchInlineSnapshot( + `"You can't redeem IBTC at the moment because IBTC parachain is more than 6 blocks behind."` + ); + + await submitForm(); + + await waitFor(() => expect(mockRedeemRequest).not.toHaveBeenCalled()); + + mockBtcRelayGetLatestBlockHeight.mockImplementation(() => MOCK_BTC_RELAY_HEIGHT); + mockElectrsAPIGetLatestBlockHeight.mockImplementation(() => MOCK_BITCOIN_HEIGHT); + }); + + it('when the oracle is offline', async () => { + mockOracleGetExchangeRate.mockImplementation( + (currency: CurrencyExt) => new ExchangeRate(Bitcoin, currency, new Big(0)) + ); + + const { changeAmountToRedeem, submitForm, errorElement } = await renderRedeemForm(); + + const inputAmount = 0.0001; + + await changeAmountToRedeem(inputAmount.toString()); + + expect(errorElement.textContent).toMatchInlineSnapshot( + `"You can't redeem IBTC at the moment because oracle is offline."` + ); + + await submitForm(); + + await waitFor(() => expect(mockRedeemRequest).not.toHaveBeenCalled()); + + mockOracleGetExchangeRate.mockImplementation( + (currency: CurrencyExt) => new ExchangeRate(Bitcoin, currency, MOCK_EXCHANGE_RATE) + ); + }); }); diff --git a/src/test/pages/Swap.test.tsx b/src/test/pages/Swap.test.tsx new file mode 100644 index 0000000000..80897a7014 --- /dev/null +++ b/src/test/pages/Swap.test.tsx @@ -0,0 +1,285 @@ +import App from '@/App'; +import { RELAY_CHAIN_NATIVE_TOKEN, WRAPPED_TOKEN } from '@/config/relay-chains'; + +import { DEFAULT_DEADLINE_BLOCK_NUMBER, mockGetFutureBlockNumber } from '../mocks/@interlay/interbtc-api'; +import { + DEFAULT_ACCOUNT_LIQUIDITY, + DEFAULT_TRADE, + DEFAULT_TRADE_AMOUNT, + mockGetLiquidityProvidedByAccount, + mockSwap +} from '../mocks/@interlay/interbtc-api/parachain/amm'; +import { DEFAULT_ACCOUNT_ADDRESS } from '../mocks/substrate/mocks'; +import { render, screen, userEvent, waitFor, within } from '../test-utils'; + +const path = '/swap'; + +jest.mock('../../parts/Layout', () => { + const MockedLayout: React.FC = ({ children }: any) => children; + MockedLayout.displayName = 'MockedLayout'; + return MockedLayout; +}); + +describe('Swap Page', () => { + beforeEach(() => { + mockGetLiquidityProvidedByAccount.mockResolvedValue(DEFAULT_ACCOUNT_LIQUIDITY); + }); + + // There is only a single test case that tests the whole flow of a swap + it('should be able to swap', async () => { + await render(, { path }); + + // Submit button should be disabled + expect(screen.getByRole('button', { name: /select token/i })).toBeDisabled(); + + // Output field should be disabled and his token field empty + expect( + screen.getByRole('textbox', { + name: 'To', + exact: true + }) + ).toBeDisabled(); + expect(screen.getByRole('textbox', { name: /choose token for to field/i })).toHaveValue(''); + + /* START - Select Output token */ + + userEvent.click(screen.getByRole('button', { name: /choose token for to field/i })); + + let dialog = within(screen.getByRole('dialog', { name: /select token/i })); + + userEvent.click(dialog.getByRole('row', { name: RELAY_CHAIN_NATIVE_TOKEN.ticker })); + + await waitFor(() => { + // Fields should switch tokens + expect(screen.getByRole('textbox', { name: /choose token for from field/i })).toHaveValue(''); + expect(screen.getByRole('textbox', { name: /choose token for to field/i })).toHaveValue( + RELAY_CHAIN_NATIVE_TOKEN.ticker + ); + // Submit button should be disabled + expect(screen.getByRole('button', { name: /select token/i })).toBeDisabled(); + }); + + /* END - Select Output token */ + + /* START - Select Input token */ + + userEvent.click(screen.getByRole('button', { name: /choose token for from field/i })); + + dialog = within(screen.getByRole('dialog', { name: /select token/i })); + + userEvent.click(dialog.getByRole('row', { name: WRAPPED_TOKEN.ticker })); + + await waitFor(() => { + expect(screen.getByRole('textbox', { name: /choose token for from field/i })).toHaveValue(WRAPPED_TOKEN.ticker); + }); + + /* END - Select Input token */ + + /* START - Switch tokens */ + + userEvent.click(screen.getByRole('button', { name: /switch tokens/i })); + + await waitFor(() => { + expect(screen.getByRole('textbox', { name: /choose token for from field/i })).toHaveValue( + RELAY_CHAIN_NATIVE_TOKEN.ticker + ); + expect(screen.getByRole('textbox', { name: /choose token for to field/i })).toHaveValue(WRAPPED_TOKEN.ticker); + + // Submit button should be disabled + expect( + screen.getByRole('button', { name: new RegExp(`enter ${RELAY_CHAIN_NATIVE_TOKEN.ticker} amount`, 'i') }) + ).toBeDisabled(); + }); + + /* END - Switch tokens */ + + /* START - Create a trade setup */ + + userEvent.type(screen.getByRole('textbox', { name: 'From' }), DEFAULT_TRADE_AMOUNT.INPUT.toString()); + + await waitFor(() => { + expect(screen.getByRole('button', { name: /loading.../i })).toBeDisabled(); + }); + + await waitFor(() => { + expect( + screen.getByRole('textbox', { + name: 'To' + }) + ).toHaveValue(DEFAULT_TRADE_AMOUNT.OUTPUT.toString()); + }); + + /* END - Create a trade setup */ + + /* START - Trade setup info */ + const swapInfoTitle = new RegExp( + `1 ${DEFAULT_TRADE_AMOUNT.INPUT.currency.ticker} = ${DEFAULT_TRADE.executionPrice.toHuman()}` + ); + + expect(screen.queryByRole('region', { name: swapInfoTitle })).not.toBeVisible(); + + userEvent.click( + screen.getByRole('button', { + name: swapInfoTitle + }) + ); + + await waitFor(() => { + expect(screen.getByRole('region', { name: swapInfoTitle })).toBeVisible(); + }); + + const swapInfoRegion = within(screen.getByRole('region', { name: swapInfoTitle })); + + expect(swapInfoRegion.getByText(/expected output/i)).toBeInTheDocument(); + expect(swapInfoRegion.getByText(/minimum received/i)).toBeInTheDocument(); + expect(swapInfoRegion.getByText(/price impact/i)).toBeInTheDocument(); + expect(swapInfoRegion.getByText(/fees/i)).toBeInTheDocument(); + + /* END - Trade setup info */ + + /* START - Trade setup liquidity */ + + expect( + screen.getByRole('heading', { + name: new RegExp( + `${DEFAULT_TRADE_AMOUNT.INPUT.currency.ticker} - ${DEFAULT_TRADE_AMOUNT.OUTPUT.currency.ticker}`, + 'i' + ) + }) + ).toBeInTheDocument(); + + expect(screen.getByText(/volume/i, { exact: false })).toBeInTheDocument(); + expect(screen.getByText(/liquidity/i)).toBeInTheDocument(); + + /* END - Trade setup liquidity */ + + /* START - Execute trade setup */ + + userEvent.click(screen.getByRole('button', { name: /swap/i })); + + await waitFor(() => { + expect(screen.getByRole('button', { name: /loading.../i, exact: false })).toBeDisabled(); + }); + + await waitFor(() => { + expect( + screen.getByRole('button', { name: new RegExp(`enter ${RELAY_CHAIN_NATIVE_TOKEN.ticker} amount`, 'i') }) + ).toBeInTheDocument(); + + expect(screen.getByRole('textbox', { name: 'From' })).toHaveValue(''); + expect(screen.getByRole('textbox', { name: 'To' })).toHaveValue(''); + }); + + expect(DEFAULT_TRADE.getMinimumOutputAmount).toHaveBeenCalledWith(0.1); + expect(mockGetFutureBlockNumber).toHaveBeenCalledTimes(1); + expect(mockSwap).toHaveBeenCalledWith( + DEFAULT_TRADE, + DEFAULT_TRADE_AMOUNT.OUTPUT, + DEFAULT_ACCOUNT_ADDRESS, + DEFAULT_DEADLINE_BLOCK_NUMBER + ); + + /* END - Execute trade setup */ + + /* START - Execute trade setup with different slippage */ + + userEvent.click(screen.getByRole('button', { name: /slippage settings/i })); + + const slippageDialog = within(screen.getByRole('dialog', { name: /set slippage tolerance/i })); + + userEvent.click(slippageDialog.getByRole('gridcell', { name: /0.5%/ })); + + userEvent.type(screen.getByRole('textbox', { name: 'From' }), DEFAULT_TRADE_AMOUNT.INPUT.toString()); + + await waitFor(() => { + expect( + screen.getByRole('textbox', { + name: 'To' + }) + ).toHaveValue(DEFAULT_TRADE_AMOUNT.OUTPUT.toString()); + }); + + userEvent.click(screen.getByRole('button', { name: /swap/i })); + + await waitFor(() => { + expect(DEFAULT_TRADE.getMinimumOutputAmount).toHaveBeenCalledWith(0.5); + }); + + /* END - Execute trade setup with different slippage */ + }); + + it('should show price impact warning', async () => { + await render(, { path }); + + /* START - Create a trade setup */ + + userEvent.click(screen.getByRole('button', { name: /choose token for to field/i })); + + const dialog = within(screen.getByRole('dialog', { name: /select token/i })); + + userEvent.click(dialog.getByRole('row', { name: WRAPPED_TOKEN.ticker })); + + await waitFor(() => { + expect(screen.getByRole('textbox', { name: /choose token for to field/i })).toHaveValue(WRAPPED_TOKEN.ticker); + }); + + userEvent.click(screen.getByRole('button', { name: /switch tokens/i })); + + await waitFor(() => { + expect(screen.getByRole('textbox', { name: /choose token for from field/i })).toHaveValue(WRAPPED_TOKEN.ticker); + }); + + userEvent.type(screen.getByRole('textbox', { name: 'From' }), '100'); + + await waitFor(() => { + expect(screen.getByRole('button', { name: /loading.../i })).toBeDisabled(); + }); + + await waitFor(() => { + expect(screen.getByRole('button', { name: /swap/i })).toBeInTheDocument(); + }); + /* END - Create a trade setup */ + + userEvent.click(screen.getByRole('button', { name: /swap/i })); + + await waitFor(() => { + expect(screen.getByRole('dialog', { name: /price impact warning/i })).toBeInTheDocument(); + }); + + const withinPriceImpactDialog = within(screen.getByRole('dialog', { name: /price impact warning/i })); + + userEvent.click(withinPriceImpactDialog.getByRole('button', { name: /confirm swap/i })); + + await waitFor(() => { + expect(screen.getByRole('button', { name: /loading.../i, exact: false })).toBeDisabled(); + }); + + await waitFor(() => { + expect(mockSwap).toHaveBeenCalledTimes(1); + }); + }); + + it('should setup input and output currencies from search query', async () => { + await render(, { path: `${path}?from=${WRAPPED_TOKEN.ticker}&to=${RELAY_CHAIN_NATIVE_TOKEN.ticker}` }); + + await waitFor(() => { + expect(screen.getByRole('textbox', { name: /choose token for from field/i })).toHaveValue(WRAPPED_TOKEN.ticker); + + expect(screen.getByRole('textbox', { name: /choose token for to field/i })).toHaveValue( + RELAY_CHAIN_NATIVE_TOKEN.ticker + ); + }); + }); + + it('should setup input and output currencies to default pair when query is invalid', async () => { + await render(, { path: `${path}?from=&to=` }); + + await waitFor(() => { + expect(screen.getByRole('textbox', { name: /choose token for from field/i })).toHaveValue( + RELAY_CHAIN_NATIVE_TOKEN.ticker + ); + + expect(screen.getByRole('textbox', { name: /choose token for to field/i })).not.toHaveValue(); + }); + }); +}); diff --git a/src/test/pages/Wallet.test.tsx b/src/test/pages/Wallet.test.tsx new file mode 100644 index 0000000000..c2e1d5bcc0 --- /dev/null +++ b/src/test/pages/Wallet.test.tsx @@ -0,0 +1,250 @@ +import MatchMediaMock from 'jest-matchmedia-mock'; + +import App from '@/App'; +import { theme } from '@/component-library'; +import { GOVERNANCE_TOKEN, RELAY_CHAIN_NATIVE_TOKEN, WRAPPED_TOKEN } from '@/config/relay-chains'; +import { NATIVE_CURRENCIES } from '@/utils/constants/currency'; +import { PAGES, QUERY_PARAMETERS } from '@/utils/constants/links'; + +import { + DEFAULT_CURRENT_BLOCK_NUMBER, + DEFAULT_TOKENS_BALANCE_FN, + EMPTY_TOKENS_BALANCE_FN, + mockGetCurrentBlockNumber, + mockTokensBalance +} from '../mocks/@interlay/interbtc-api'; +import { + ACCOUNT_WITH_FULL_LIQUIDITY, + DEFAULT_ACCOUNT_LIQUIDITY, + mockGetLiquidityProvidedByAccount, + mockGetLpTokens +} from '../mocks/@interlay/interbtc-api/parachain/amm'; +import { + DEFAULT_STAKED_BALANCE, + EMPTY_STAKED_BALANCE, + mockGetStakedBalance +} from '../mocks/@interlay/interbtc-api/parachain/escrow'; +import { + DEFAULT_BORROW_POSITIONS, + DEFAULT_LEND_POSITIONS, + mockGetBorrowPositionsOfAccount, + mockGetLendPositionsOfAccount +} from '../mocks/@interlay/interbtc-api/parachain/loans'; +import { + EMPTY_VESTING_SCHEDULES, + mockClaimVesting, + mockVestingSchedules, + SOME_VESTING_SCHEDULES +} from '../mocks/@interlay/interbtc-api/parachain/vesting'; +import { render, screen, userEvent, waitFor } from '../test-utils'; +import { withinList } from './utils/list'; +import { queryTable, withinTable, withinTableRow } from './utils/table'; + +jest.mock('@/pages/AMM', () => ({ __esModule: true, default: () =>
Swap page
})); + +const path = '/wallet'; + +const TABLES = { + AVAILABLE_ASSETS: 'available assets', + LEND_POSITIONS: 'lend positions', + BORROW_POSITIONS: 'borrow positions', + LIQUIDITY_POOLS: 'liquidity pools', + STAKED: 'staked' +}; + +describe('Wallet Page', () => { + let matchMedia: MatchMediaMock; + + beforeEach(() => { + matchMedia = new MatchMediaMock(); + + // ignoring lp-tokens + mockGetLpTokens.mockResolvedValue([]); + mockTokensBalance.mockImplementation(DEFAULT_TOKENS_BALANCE_FN); + mockGetLendPositionsOfAccount.mockReturnValue(DEFAULT_LEND_POSITIONS); + mockGetBorrowPositionsOfAccount.mockReturnValue(DEFAULT_BORROW_POSITIONS); + mockGetLiquidityProvidedByAccount.mockReturnValue(DEFAULT_ACCOUNT_LIQUIDITY); + mockGetStakedBalance.mockReturnValue(DEFAULT_STAKED_BALANCE); + mockGetCurrentBlockNumber.mockReturnValue(DEFAULT_CURRENT_BLOCK_NUMBER); + mockVestingSchedules.mockReturnValue(EMPTY_VESTING_SCHEDULES); + }); + + afterEach(() => { + matchMedia.clear(); + }); + + // TODO: add tests for Buy and Transfer CTALinks + describe('Available Assets', () => { + it('should render table (desktop)', async () => { + await render(, { path }); + + const table = withinTable(TABLES.AVAILABLE_ASSETS); + + expect(table.getAllByRole('row')).toHaveLength(NATIVE_CURRENCIES.length); + }); + + it('should render list (mobile)', async () => { + matchMedia.useMediaQuery(theme.breakpoints.down('md')); + + await render(, { path }); + + const list = withinList(TABLES.AVAILABLE_ASSETS); + + expect(list.getAllByRole('row')).toHaveLength(NATIVE_CURRENCIES.length); + }); + + it('should be able to navigate to issue page', async () => { + const { history } = await render(, { path }); + + const row = withinTableRow(TABLES.AVAILABLE_ASSETS, WRAPPED_TOKEN.ticker); + + userEvent.click(row.getByRole('link', { name: /issue/i })); + + expect(history.location.pathname).toBe(PAGES.BRIDGE); + expect(history.location.search).toMatch(`${QUERY_PARAMETERS.TAB}=issue`); + }); + + // TODO: enable when banxa e merged + it.skip('should be able to open buy dialog', async () => { + await render(, { path }); + + const row = withinTableRow(TABLES.AVAILABLE_ASSETS, GOVERNANCE_TOKEN.ticker); + + userEvent.click(row.getByRole('button', { name: /buy/i })); + + await waitFor(() => { + expect(screen.getByRole('dialog')).toBeInTheDocument(); + }); + }); + + it('should be able to claim vesting', async () => { + mockGetCurrentBlockNumber.mockReturnValue(10); + mockVestingSchedules.mockReturnValue(SOME_VESTING_SCHEDULES); + + await render(, { path }); + + const row = withinTableRow(TABLES.AVAILABLE_ASSETS, GOVERNANCE_TOKEN.ticker); + + userEvent.click(row.getByRole('button', { name: /claim vesting/i })); + + await waitFor(() => { + expect(mockClaimVesting).toHaveBeenCalledTimes(1); + }); + }); + + it(`should be able to navigate to swap page using ${WRAPPED_TOKEN.ticker}`, async () => { + const { history } = await render(, { path }); + + const row = withinTableRow(TABLES.AVAILABLE_ASSETS, WRAPPED_TOKEN.ticker); + + userEvent.click(row.getByRole('link', { name: /swap/i })); + + expect(history.location.pathname).toBe(PAGES.SWAP); + expect(history.location.search).toMatch(`${QUERY_PARAMETERS.SWAP.FROM}=${WRAPPED_TOKEN.ticker}`); + }); + + it(`should be able to navigate to swap page using ${RELAY_CHAIN_NATIVE_TOKEN.ticker}`, async () => { + const { history } = await render(, { path }); + + const row = withinTableRow(TABLES.AVAILABLE_ASSETS, RELAY_CHAIN_NATIVE_TOKEN.ticker); + + userEvent.click(row.getByRole('link', { name: /swap/i })); + + expect(history.location.pathname).toBe(PAGES.SWAP); + expect(history.location.search).toMatch( + `${QUERY_PARAMETERS.SWAP.FROM}=${RELAY_CHAIN_NATIVE_TOKEN.ticker}&${QUERY_PARAMETERS.SWAP.TO}=${WRAPPED_TOKEN.ticker}` + ); + }); + + it('should display zero balance assets', async () => { + mockTokensBalance.mockImplementation(EMPTY_TOKENS_BALANCE_FN); + + await render(, { path }); + + const table = withinTable(TABLES.AVAILABLE_ASSETS); + + expect(table.queryAllByRole('row')).toHaveLength(0); + expect(screen.getByText(/no assets available/i)).toBeInTheDocument(); + + userEvent.click(screen.getByRole('switch', { name: /show zero balance/i })); + + await waitFor(() => { + expect(table.queryAllByRole('row')).toHaveLength(NATIVE_CURRENCIES.length); + }); + }); + }); + + describe('Lending Positions', () => { + it('should display table', async () => { + await render(, { path }); + + const table = withinTable(TABLES.LEND_POSITIONS); + + expect(table.getAllByRole('row')).toHaveLength(DEFAULT_LEND_POSITIONS.length); + }); + + it('should not display table', async () => { + mockGetLendPositionsOfAccount.mockReturnValue([]); + + await render(, { path }); + + expect(queryTable(TABLES.LEND_POSITIONS)).not.toBeInTheDocument(); + }); + }); + + describe('Borrow Positions', () => { + it('should display table', async () => { + await render(, { path }); + + const table = withinTable(TABLES.BORROW_POSITIONS); + + expect(table.getAllByRole('row')).toHaveLength(DEFAULT_BORROW_POSITIONS.length); + }); + + it('should not display table', async () => { + mockGetBorrowPositionsOfAccount.mockReturnValue([]); + + await render(, { path }); + + expect(queryTable(TABLES.BORROW_POSITIONS)).not.toBeInTheDocument(); + }); + }); + + describe('Liquidity Pools', () => { + it('should display table', async () => { + mockGetLiquidityProvidedByAccount.mockResolvedValue(ACCOUNT_WITH_FULL_LIQUIDITY); + + await render(, { path }); + + const table = withinTable(TABLES.LIQUIDITY_POOLS); + + expect(table.getAllByRole('row')).toHaveLength(ACCOUNT_WITH_FULL_LIQUIDITY.length); + }); + + it('should not display table', async () => { + mockGetLiquidityProvidedByAccount.mockReturnValue(DEFAULT_ACCOUNT_LIQUIDITY); + + await render(, { path }); + + expect(queryTable(TABLES.LIQUIDITY_POOLS)).not.toBeInTheDocument(); + }); + }); + + describe('Staking', () => { + it('should display table', async () => { + await render(, { path }); + + const table = withinTable(TABLES.STAKED); + + expect(table.getAllByRole('row')).toHaveLength(1); + }); + + it('should not display table', async () => { + mockGetStakedBalance.mockReturnValue(EMPTY_STAKED_BALANCE); + + await render(, { path }); + + expect(queryTable(TABLES.LIQUIDITY_POOLS)).not.toBeInTheDocument(); + }); + }); +}); diff --git a/src/test/pages/utils/common.ts b/src/test/pages/utils/common.ts new file mode 100644 index 0000000000..223d6c77f2 --- /dev/null +++ b/src/test/pages/utils/common.ts @@ -0,0 +1,6 @@ +type ElementName = string | RegExp; + +const composeName = (name: ElementName): RegExp => (typeof name === 'string' ? new RegExp(name, 'i') : name); + +export { composeName }; +export type { ElementName }; diff --git a/src/test/pages/utils/list.ts b/src/test/pages/utils/list.ts new file mode 100644 index 0000000000..2818e63523 --- /dev/null +++ b/src/test/pages/utils/list.ts @@ -0,0 +1,17 @@ +/* eslint-disable @typescript-eslint/explicit-module-boundary-types */ +import { screen, within } from '../../test-utils'; +import { composeName, ElementName } from './common'; + +const getList = (name: ElementName) => screen.getByRole('grid', { name: composeName(name) }); + +const withinList = (name: ElementName) => within(getList(name)); + +const getListRow = (listName: ElementName, rowName: ElementName) => { + const table = withinList(listName); + + return table.getByRole('row', { name: composeName(rowName) }); +}; + +const withinListRow = (listName: string, asset: string) => within(getListRow(listName, asset)); + +export { getList, getListRow, withinList, withinListRow }; diff --git a/src/test/pages/utils/table.ts b/src/test/pages/utils/table.ts new file mode 100644 index 0000000000..bad822e8a3 --- /dev/null +++ b/src/test/pages/utils/table.ts @@ -0,0 +1,70 @@ +/* eslint-disable @typescript-eslint/explicit-module-boundary-types */ +import { screen, userEvent, waitFor, waitForElementToBeRemoved, within } from '../../test-utils'; +import { composeName, ElementName } from './common'; + +const getTable = (name: ElementName) => screen.getByRole('grid', { name: composeName(name) }); + +const queryTable = (name: ElementName) => screen.queryByRole('grid', { name: composeName(name) }); + +const withinTable = (name: ElementName) => { + const table = within(getTable(name)); + return within(table.getAllByRole('rowgroup')[1]); +}; + +const getTableRow = (tableName: ElementName, rowName: ElementName) => { + const table = withinTable(tableName); + + return table.getByRole('row', { name: composeName(rowName) }); +}; + +const withinTableRow = (tableName: string, asset: string) => within(getTableRow(tableName, asset)); + +const getTableModal = (tableName: ElementName, rowName: ElementName) => { + const row = getTableRow(tableName, rowName); + + userEvent.click(row); + + return screen.getByRole('dialog'); +}; + +const withinTableModal = (tableName: ElementName, rowName: ElementName) => within(getTableModal(tableName, rowName)); + +const getModalTabPanel = ( + tableName: ElementName, + rowName: ElementName, + tabName: ElementName, + shouldClickTab?: boolean +) => { + const modal = withinTableModal(tableName, rowName); + + if (shouldClickTab) { + userEvent.click( + modal.getByRole('tab', { + name: new RegExp(tabName, 'i') + }) + ); + } + + return modal.getByRole('tabpanel', { + name: new RegExp(tabName, 'i') + }); +}; + +const withinModalTabPanel = ( + tableName: ElementName, + rowName: ElementName, + tabName: ElementName, + shouldClickTab?: boolean +) => within(getModalTabPanel(tableName, rowName, tabName, shouldClickTab)); + +const submitForm = async (tabPanel: ReturnType, buttonLabel: string) => { + await waitFor(() => { + expect(tabPanel.getByRole('button', { name: new RegExp(buttonLabel, 'i') })).not.toBeDisabled(); + }); + + userEvent.click(tabPanel.getByRole('button', { name: new RegExp(buttonLabel, 'i') })); + + await waitForElementToBeRemoved(screen.getByRole('dialog')); +}; + +export { getTable, getTableRow, queryTable, submitForm, withinModalTabPanel, withinTable, withinTableRow }; diff --git a/src/test/test-utils.tsx b/src/test/test-utils.tsx index f7455561a9..3dc04831db 100644 --- a/src/test/test-utils.tsx +++ b/src/test/test-utils.tsx @@ -16,7 +16,6 @@ const queryClient = new QueryClient(); interface CustomRenderOptions extends Omit { path?: `/${string}`; - hasLayout?: boolean; } const testStore = createStore(rootReducer); @@ -41,26 +40,23 @@ const ProvidersWrapper: (history: MemoryHistory) => FC<{ children?: React.ReactN ); }; -const customRender = (ui: ReactElement, options?: CustomRenderOptions): Promise => { +const customRender = async ( + ui: ReactElement, + options?: CustomRenderOptions +): Promise & { history: MemoryHistory }> => { const history = createMemoryHistory(); if (options?.path) { history.push(options.path); } - if (!options?.hasLayout) { - jest.mock('../parts/Layout', () => { - const MockedLayout: React.FC = ({ children }: any) => children; - MockedLayout.displayName = 'MockedLayout'; - return MockedLayout; - }); - - console.warn('Rendering with mocked out `Layout` component'); - } + let _render; // Wrapped in act so async updates are awaited. - return act(async () => { - render(ui, { wrapper: ProvidersWrapper(history), ...options }); + await act(async () => { + _render = render(ui, { wrapper: ProvidersWrapper(history), ...options }); }); + + return { ...(_render as any), history }; }; export * from '@testing-library/react'; diff --git a/src/types/general.d.ts b/src/types/general.d.ts index 216beda3db..9af5a51069 100644 --- a/src/types/general.d.ts +++ b/src/types/general.d.ts @@ -1,3 +1,11 @@ type TreasuryAction = 'issue' | 'redeem'; +enum TXType { + Issue = 'issue', + Redeem = 'redeem', + Replace = 'replace' +} + export type { TreasuryAction }; + +export { TXType }; diff --git a/src/types/pools.ts b/src/types/pools.ts new file mode 100644 index 0000000000..ac6e185598 --- /dev/null +++ b/src/types/pools.ts @@ -0,0 +1,3 @@ +type PoolAction = 'deposit' | 'withdraw'; + +export type { PoolAction }; diff --git a/src/types/swap.ts b/src/types/swap.ts new file mode 100644 index 0000000000..6aee8f0d5b --- /dev/null +++ b/src/types/swap.ts @@ -0,0 +1,6 @@ +import { CurrencyExt } from '@interlay/interbtc-api'; + +export type SwapPair = { + input?: CurrencyExt; + output?: CurrencyExt; +}; diff --git a/src/utils/constants/api.ts b/src/utils/constants/api.ts index 0279c68bed..6a135bb8aa 100644 --- a/src/utils/constants/api.ts +++ b/src/utils/constants/api.ts @@ -11,4 +11,12 @@ const COINGECKO_IDS = ['bitcoin', 'kintsugi', 'kusama', 'polkadot', 'interlay'] const BLOCKTIME_REFETCH_INTERVAL = BLOCK_TIME * 1000; -export { BLOCKTIME_REFETCH_INTERVAL, COINGECKO_IDS, PRICES_API }; +// TODO: start using this instead of `BLOCKTIME_REFETCH_INTERVAL` +const REFETCH_INTERVAL = { + MINUTE: 60000, + BLOCK: BLOCK_TIME * 1000 +}; + +const AMM_DEADLINE_INTERVAL = 30 * 60; + +export { AMM_DEADLINE_INTERVAL, BLOCKTIME_REFETCH_INTERVAL, COINGECKO_IDS, PRICES_API, REFETCH_INTERVAL }; diff --git a/src/utils/constants/currency.ts b/src/utils/constants/currency.ts index df66c4f5b2..1e8413d614 100644 --- a/src/utils/constants/currency.ts +++ b/src/utils/constants/currency.ts @@ -12,11 +12,14 @@ import { import { GOVERNANCE_TOKEN, VOTE_GOVERNANCE_TOKEN } from '@/config/relay-chains'; import { COINGECKO_IDS } from './api'; +import { POLKADOT } from './relay-chain-names'; const ZERO_VOTE_GOVERNANCE_TOKEN_AMOUNT = newMonetaryAmount(0, VOTE_GOVERNANCE_TOKEN, true); const ZERO_GOVERNANCE_TOKEN_AMOUNT = newMonetaryAmount(0, GOVERNANCE_TOKEN, true); -const NATIVE_CURRENCIES: Array = [Polkadot, InterBtc, Interlay, KBtc, Kintsugi, Kusama]; +// TODO: Pull values in from lib, as we do with vault collaterals +const NATIVE_CURRENCIES: Array = + process.env.REACT_APP_RELAY_CHAIN_NAME === POLKADOT ? [Polkadot, InterBtc, Interlay] : [KBtc, Kintsugi, Kusama]; const COINGECKO_ID_BY_CURRENCY_TICKER: Record = Object.freeze({ [Bitcoin.ticker]: 'bitcoin', diff --git a/src/utils/constants/links.ts b/src/utils/constants/links.ts index c5cf92f865..852612b127 100644 --- a/src/utils/constants/links.ts +++ b/src/utils/constants/links.ts @@ -26,7 +26,10 @@ const PAGES = Object.freeze({ VAULT: `/vaults/:${URL_PARAMETERS.VAULT.ACCOUNT}/:${URL_PARAMETERS.VAULT.COLLATERAL}/:${URL_PARAMETERS.VAULT.WRAPPED}`, FEEDBACK: '/feedback', ACTIONS: '/actions', - LOANS: '/lending' + LOANS: '/lending', + SWAP: '/swap', + POOLS: '/pools', + WALLET: '/wallet' }); const QUERY_PARAMETERS = Object.freeze({ @@ -35,7 +38,11 @@ const QUERY_PARAMETERS = Object.freeze({ ISSUE_REQUESTS_PAGE: 'issueRequestPage', REDEEM_REQUESTS_PAGE: 'redeemRequestPage', ISSUE_REQUEST_ID: 'issueRequestId', - REDEEM_REQUEST_ID: 'redeemRequestId' + REDEEM_REQUEST_ID: 'redeemRequestId', + SWAP: { + FROM: 'from', + TO: 'to' + } }); export { PAGES, QUERY_PARAMETERS, URL_PARAMETERS }; diff --git a/src/utils/constants/swap.ts b/src/utils/constants/swap.ts new file mode 100644 index 0000000000..726d1dc815 --- /dev/null +++ b/src/utils/constants/swap.ts @@ -0,0 +1,5 @@ +import Big from 'big.js'; + +const SWAP_PRICE_IMPACT_LIMIT = new Big(5); + +export { SWAP_PRICE_IMPACT_LIMIT }; diff --git a/src/utils/helpers/coin-icon.ts b/src/utils/helpers/coin-icon.ts new file mode 100644 index 0000000000..766d60c95a --- /dev/null +++ b/src/utils/helpers/coin-icon.ts @@ -0,0 +1,18 @@ +import { CurrencyExt, StandardLpToken } from '@interlay/interbtc-api'; + +import { CoinIconProps } from '@/component-library'; + +const getCoinIconProps = (currency: CurrencyExt): Pick => { + if ((currency as StandardLpToken)?.lpToken) { + return { + ticker: currency.ticker, + tickers: Object.values((currency as StandardLpToken).lpToken).map((token) => token.ticker) + }; + } + + return { + ticker: currency.ticker + }; +}; + +export { getCoinIconProps }; diff --git a/src/utils/helpers/currencies.ts b/src/utils/helpers/currencies.ts index 89352b7231..1a6d916615 100644 --- a/src/utils/helpers/currencies.ts +++ b/src/utils/helpers/currencies.ts @@ -1,4 +1,5 @@ import { CurrencyExt } from '@interlay/interbtc-api'; +import { MonetaryAmount } from '@interlay/monetary-js'; // Squid query by currency ticker for native assets and by id for foreign assets. // We need to differentiate because those are handled differently on squid side. @@ -6,4 +7,14 @@ import { CurrencyExt } from '@interlay/interbtc-api'; const getCurrencyEqualityCondition = (currency: CurrencyExt): string => 'foreignAsset' in currency ? `asset_eq: ${currency.foreignAsset.id}` : `token_eq: ${currency.ticker}`; -export { getCurrencyEqualityCondition }; +const pickSmallerAmount = ( + amount0: MonetaryAmount, + amount1: MonetaryAmount +): MonetaryAmount => { + if (amount0.lte(amount1)) { + return amount0; + } + return amount1; +}; + +export { getCurrencyEqualityCondition, pickSmallerAmount }; diff --git a/src/pages/Loans/LoansOverview/utils/get-subsidy-rewards-apy.ts b/src/utils/helpers/loans.ts similarity index 54% rename from src/pages/Loans/LoansOverview/utils/get-subsidy-rewards-apy.ts rename to src/utils/helpers/loans.ts index 13061b9ef3..c7241abb5e 100644 --- a/src/pages/Loans/LoansOverview/utils/get-subsidy-rewards-apy.ts +++ b/src/utils/helpers/loans.ts @@ -2,8 +2,27 @@ import { CurrencyExt } from '@interlay/interbtc-api'; import { MonetaryAmount } from '@interlay/monetary-js'; import Big from 'big.js'; -import { getTokenPrice } from '../../../../utils/helpers/prices'; -import { Prices } from '../../../../utils/hooks/api/use-get-prices'; +import { formatPercentage } from '@/common/utils/utils'; + +import { Prices } from '../hooks/api/use-get-prices'; +import { getTokenPrice } from './prices'; + +const MIN_DECIMAL_NUMBER = 0.01; + +// MEMO: returns formatted apy or better representation of a very small apy +const getApyLabel = (apy: Big): string => { + const isPositive = apy.gt(0); + const isTinyApy = isPositive ? apy.lt(MIN_DECIMAL_NUMBER) : apy.gt(-MIN_DECIMAL_NUMBER); + + if (isTinyApy) { + const tinyIndicator = apy.gt(0) ? '<' : '>'; + const minDecimal = isPositive ? MIN_DECIMAL_NUMBER : -MIN_DECIMAL_NUMBER; + + return `${tinyIndicator}${minDecimal}%`; + } + + return formatPercentage(apy.toNumber()); +}; const getSubsidyRewardApy = ( positionCurrency: CurrencyExt | undefined, @@ -27,4 +46,4 @@ const getSubsidyRewardApy = ( return apy; }; -export { getSubsidyRewardApy }; +export { getApyLabel, getSubsidyRewardApy }; diff --git a/src/utils/helpers/pools.ts b/src/utils/helpers/pools.ts new file mode 100644 index 0000000000..e75c766884 --- /dev/null +++ b/src/utils/helpers/pools.ts @@ -0,0 +1,32 @@ +import { CurrencyExt, LiquidityPool, LpCurrency } from '@interlay/interbtc-api'; +import { MonetaryAmount } from '@interlay/monetary-js'; +import Big from 'big.js'; + +import { calculateTotalLiquidityUSD } from '@/pages/AMM/shared/utils'; + +import { Prices } from '../hooks/api/use-get-prices'; + +const getPooledTickers = (liquidityPools: LiquidityPool[]): Set => + liquidityPools.reduce((acc, pool) => { + pool.pooledCurrencies.forEach((curr) => acc.add(curr.currency.ticker)); + + return acc; + }, new Set()); + +const getFarmingApr = ( + rewardAmountsYearly: Array>, + lpTotalSupply: MonetaryAmount, + totalLiquidityUSD: number, + prices: Prices | undefined +): Big => { + if (prices === undefined || lpTotalSupply.toBig().eq(0) || totalLiquidityUSD === 0) { + return new Big(0); + } + const totalRewardsPerTokenUSD = calculateTotalLiquidityUSD(rewardAmountsYearly, prices); + + const farmingApr = new Big(totalRewardsPerTokenUSD).div(totalLiquidityUSD).mul(100); + + return farmingApr; +}; + +export { getFarmingApr, getPooledTickers }; diff --git a/src/utils/hooks/api/amm/use-get-account-pools.tsx b/src/utils/hooks/api/amm/use-get-account-pools.tsx new file mode 100644 index 0000000000..4c38e495db --- /dev/null +++ b/src/utils/hooks/api/amm/use-get-account-pools.tsx @@ -0,0 +1,66 @@ +import { CurrencyExt, isCurrencyEqual, LiquidityPool, LpCurrency } from '@interlay/interbtc-api'; +import { MonetaryAmount } from '@interlay/monetary-js'; +import { AccountId } from '@polkadot/types/interfaces'; +import { useErrorHandler } from 'react-error-boundary'; +import { useQuery } from 'react-query'; + +import { BLOCKTIME_REFETCH_INTERVAL } from '@/utils/constants/api'; + +import useAccountId from '../../use-account-id'; +import { useGetLiquidityPools } from './use-get-liquidity-pools'; + +type AccountLiquidityPool = { data: LiquidityPool; amount: MonetaryAmount }; + +interface AccountPoolsData { + positions: AccountLiquidityPool[]; + claimableRewards: Map[]>; +} + +const getAccountLiqudityPools = async (accountId: AccountId, pools: LiquidityPool[]): Promise => { + const accountLiquidityPools = await window.bridge.amm.getLiquidityProvidedByAccount(accountId); + const claimableRewards = await window.bridge.amm.getClaimableFarmingRewards(accountId, accountLiquidityPools, pools); + const filteredPools = accountLiquidityPools.filter((lpToken) => !lpToken.isZero()); + + const positions = filteredPools.reduce((acc: AccountLiquidityPool[], amount) => { + const pool = pools.find((pool) => isCurrencyEqual(pool.lpToken, amount.currency)); + + if (!pool) return acc; + + const data = { data: pool, amount }; + + return [...acc, data]; + }, []); + + return { positions, claimableRewards }; +}; + +interface UseGetAccountProvidedLiquidity { + data: AccountPoolsData | undefined; + refetch: () => void; +} + +// Mixes current pools with liquidity provided by the account +const useGetAccountPools = (): UseGetAccountProvidedLiquidity => { + const accountId = useAccountId(); + + const { data: liquidityPools, refetch: refetchLiquidityPools } = useGetLiquidityPools(); + const queryKey = ['account-pools', accountId]; + const { data, error, refetch: refetchQuery } = useQuery({ + queryKey: ['account-pools', accountId], + queryFn: () => accountId && liquidityPools && getAccountLiqudityPools(accountId, liquidityPools), + enabled: !!liquidityPools, + refetchInterval: BLOCKTIME_REFETCH_INTERVAL + }); + + useErrorHandler(error); + + const refetch = () => { + refetchQuery({ queryKey }); + refetchLiquidityPools(); + }; + + return { data, refetch }; +}; + +export { useGetAccountPools }; +export type { AccountLiquidityPool, AccountPoolsData, UseGetAccountProvidedLiquidity }; diff --git a/src/utils/hooks/api/amm/use-get-liquidity-pools.tsx b/src/utils/hooks/api/amm/use-get-liquidity-pools.tsx new file mode 100644 index 0000000000..7244c4c453 --- /dev/null +++ b/src/utils/hooks/api/amm/use-get-liquidity-pools.tsx @@ -0,0 +1,29 @@ +import { LiquidityPool } from '@interlay/interbtc-api'; +import { useErrorHandler } from 'react-error-boundary'; +import { useQuery } from 'react-query'; + +import { BLOCKTIME_REFETCH_INTERVAL } from '@/utils/constants/api'; + +interface UseGetLiquidityPools { + data: Array | undefined; + refetch: () => void; +} + +const getLiquidityPools = async (): Promise => window.bridge.amm.getLiquidityPools(); + +const useGetLiquidityPools = (): UseGetLiquidityPools => { + const queryKey = ['liquidity-pools']; + + const { data, error, refetch } = useQuery({ + queryKey, + queryFn: getLiquidityPools, + refetchInterval: BLOCKTIME_REFETCH_INTERVAL + }); + + useErrorHandler(error); + + return { data, refetch }; +}; + +export { useGetLiquidityPools }; +export type { UseGetLiquidityPools }; diff --git a/src/utils/hooks/api/escrow/use-get-account-staking-data.tsx b/src/utils/hooks/api/escrow/use-get-account-staking-data.tsx new file mode 100644 index 0000000000..31aebac5d7 --- /dev/null +++ b/src/utils/hooks/api/escrow/use-get-account-staking-data.tsx @@ -0,0 +1,71 @@ +import { CurrencyExt } from '@interlay/interbtc-api'; +import { MonetaryAmount } from '@interlay/monetary-js'; +import { AccountId } from '@polkadot/types/interfaces'; +import { add } from 'date-fns'; +import { useErrorHandler } from 'react-error-boundary'; +import { useQuery } from 'react-query'; + +import { BLOCK_TIME } from '@/config/parachain'; +import { BLOCKTIME_REFETCH_INTERVAL } from '@/utils/constants/api'; + +import useAccountId from '../../use-account-id'; + +type AccountUnlockStakingData = { + date: Date; + block: number; +}; + +type GetAccountStakingData = { + unlock: AccountUnlockStakingData; + balance: MonetaryAmount; +}; + +const getUnlockData = (stakeEndBlock: number, currentBlockNumber: number): AccountUnlockStakingData => { + const blocksUntilUnlockDate = stakeEndBlock - currentBlockNumber; + + const unlockDate = add(new Date(), { seconds: blocksUntilUnlockDate * BLOCK_TIME }); + + return { + date: unlockDate, + block: stakeEndBlock + }; +}; + +const getAccountStakingData = async (accountId: AccountId): Promise => { + const stakedBalancePromise = window.bridge.escrow.getStakedBalance(accountId); + const currentBlockNumberPromise = window.bridge.system.getCurrentBlockNumber(); + + const [stakedBalance, currentBlockNumber] = await Promise.all([stakedBalancePromise, currentBlockNumberPromise]); + + const unlock = getUnlockData(stakedBalance.endBlock, currentBlockNumber); + + return { + unlock, + balance: stakedBalance.amount + }; +}; + +interface GetAccountStakingDataResult { + data: GetAccountStakingData | undefined; + refetch: () => void; +} + +const useGetAccountStakingData = (): GetAccountStakingDataResult => { + const accountId = useAccountId(); + + const queryKey = ['staking', accountId]; + + const { data, error, refetch } = useQuery({ + queryKey, + queryFn: () => accountId && getAccountStakingData(accountId), + refetchInterval: BLOCKTIME_REFETCH_INTERVAL, + enabled: !!accountId + }); + + useErrorHandler(error); + + return { data, refetch }; +}; + +export { useGetAccountStakingData }; +export type { AccountUnlockStakingData, GetAccountStakingData, GetAccountStakingDataResult }; diff --git a/src/utils/hooks/api/escrow/use-get-account-voting-balance.tsx b/src/utils/hooks/api/escrow/use-get-account-voting-balance.tsx new file mode 100644 index 0000000000..649e79e61c --- /dev/null +++ b/src/utils/hooks/api/escrow/use-get-account-voting-balance.tsx @@ -0,0 +1,37 @@ +import { CurrencyExt } from '@interlay/interbtc-api'; +import { MonetaryAmount } from '@interlay/monetary-js'; +import { AccountId } from '@polkadot/types/interfaces'; +import { useErrorHandler } from 'react-error-boundary'; +import { useQuery } from 'react-query'; + +import { BLOCKTIME_REFETCH_INTERVAL } from '@/utils/constants/api'; + +import useAccountId from '../../use-account-id'; + +const getAccountVotingBalance = async (accountId: AccountId): Promise> => + window.bridge.escrow.votingBalance(accountId); + +interface GetAccountVotingBalanceResult { + data: MonetaryAmount | undefined; + refetch: () => void; +} + +const useGetAccountVotingBalance = (): GetAccountVotingBalanceResult => { + const accountId = useAccountId(); + + const queryKey = ['voting', accountId]; + + const { data, error, refetch } = useQuery({ + queryKey, + queryFn: () => accountId && getAccountVotingBalance(accountId), + refetchInterval: BLOCKTIME_REFETCH_INTERVAL, + enabled: !!accountId + }); + + useErrorHandler(error); + + return { data, refetch }; +}; + +export { useGetAccountVotingBalance }; +export type { GetAccountVotingBalanceResult }; diff --git a/src/utils/hooks/api/loans/use-get-account-lending-statistics.tsx b/src/utils/hooks/api/loans/use-get-account-lending-statistics.tsx new file mode 100644 index 0000000000..b9211bba93 --- /dev/null +++ b/src/utils/hooks/api/loans/use-get-account-lending-statistics.tsx @@ -0,0 +1,140 @@ +import { + BorrowPosition, + CollateralPosition, + CurrencyExt, + LendingStats, + LoanAsset, + TickerToData +} from '@interlay/interbtc-api'; +import { MonetaryAmount } from '@interlay/monetary-js'; +import Big from 'big.js'; +import { useMemo } from 'react'; + +import { convertMonetaryAmountToValueInUSD, convertMonetaryBtcToUSD } from '@/common/utils/utils'; +import { getSubsidyRewardApy } from '@/utils/helpers/loans'; +import { getTokenPrice } from '@/utils/helpers/prices'; +import { useGetLoanAssets } from '@/utils/hooks/api/loans/use-get-loan-assets'; + +import { Prices, useGetPrices } from '../use-get-prices'; +import { useGetAccountPositions } from './use-get-account-positions'; +import { useGetAccountSubsidyRewards } from './use-get-account-subsidy-rewards'; + +interface AccountLendingStatistics extends LendingStats { + supplyAmountUSD: Big; + borrowAmountUSD: Big; + collateralizedAmountUSD: Big; + earnedInterestAmountUSD: Big; + netAmountUSD: Big; + netAPY: Big; +} + +interface UseGetAccountLendingStatistics { + data: AccountLendingStatistics | undefined; + refetch: () => void; +} + +const getNetAPY = ( + lendPositions: CollateralPosition[], + borrowPositions: BorrowPosition[], + assets: TickerToData, + supplyAmountUSD: Big, + prices: Prices +): Big => { + if (!supplyAmountUSD.gt(0)) { + return new Big(0); + } + + const totalLendApy = lendPositions.reduce((total, position) => { + const { currency } = position.amount; + const { lendApy, lendReward } = assets[currency.ticker]; + const rewardsApy = getSubsidyRewardApy(currency, lendReward, prices); + const positionApy = lendApy.add(rewardsApy || 0); + const positionUSDValue = convertMonetaryAmountToValueInUSD( + position.amount, + getTokenPrice(prices, currency.ticker)?.usd + ); + + return positionUSDValue ? total.add(positionApy.mul(positionUSDValue)) : total; + }, new Big(0)); + + const totalBorrowApy = borrowPositions.reduce((total, position) => { + const { currency } = position.amount; + const { borrowApy, borrowReward } = assets[currency.ticker]; + const rewardsApy = getSubsidyRewardApy(currency, borrowReward, prices); + const positionApy = borrowApy.sub(rewardsApy || 0); + const positionUSDValue = convertMonetaryAmountToValueInUSD( + position.amount, + getTokenPrice(prices, currency.ticker)?.usd + ); + + return positionUSDValue ? total.add(positionApy.mul(positionUSDValue)) : total; + }, new Big(0)); + + return totalLendApy.sub(totalBorrowApy).div(supplyAmountUSD); +}; + +const getAccountPositionsStats = ( + assets: TickerToData, + lendPositions: CollateralPosition[], + borrowPositions: BorrowPosition[], + subsidyRewards: MonetaryAmount, + prices: Prices, + lendingStats: LendingStats +): AccountLendingStatistics => { + const { totalLentBtc, totalBorrowedBtc, totalCollateralBtc } = lendingStats; + // Convert from BTC to USD values. + const supplyAmountUSD = convertMonetaryBtcToUSD(totalLentBtc, prices); + const borrowAmountUSD = convertMonetaryBtcToUSD(totalBorrowedBtc, prices); + const collateralizedAmountUSD = convertMonetaryBtcToUSD(totalCollateralBtc, prices); + const netAPY = getNetAPY(lendPositions, borrowPositions, assets, supplyAmountUSD, prices); + + return { + ...lendingStats, + supplyAmountUSD, + borrowAmountUSD, + earnedInterestAmountUSD: new Big(0), + collateralizedAmountUSD, + netAmountUSD: new Big(0), + netAPY + }; +}; + +const useGetAccountLendingStatistics = (): UseGetAccountLendingStatistics => { + const { + data: { lendPositions, borrowPositions }, + refetch: positionsRefetch + } = useGetAccountPositions(); + const { data: loanAssets, refetch: loanAssetsRefetch } = useGetLoanAssets(); + + const prices = useGetPrices(); + + const { data: subsidyRewards, refetch: subsidyRewardsRefetch } = useGetAccountSubsidyRewards(); + + const lendingStats = useMemo(() => { + if (!lendPositions || !borrowPositions || !loanAssets) { + return undefined; + } + return window.bridge.loans.getLendingStats(lendPositions, borrowPositions, loanAssets); + }, [lendPositions, borrowPositions, loanAssets]); + + const statistics = useMemo(() => { + if (!loanAssets || !lendPositions || !borrowPositions || !subsidyRewards || !prices || !lendingStats) { + return undefined; + } + + return getAccountPositionsStats(loanAssets, lendPositions, borrowPositions, subsidyRewards, prices, lendingStats); + }, [lendPositions, borrowPositions, prices, subsidyRewards, loanAssets, lendingStats]); + + return { + data: statistics, + refetch: () => { + positionsRefetch(); + loanAssetsRefetch(); + subsidyRewardsRefetch(); + } + }; +}; + +export { useGetAccountLendingStatistics }; + +export type { AccountLendingStatistics }; diff --git a/src/utils/hooks/api/loans/use-get-account-positions.tsx b/src/utils/hooks/api/loans/use-get-account-positions.tsx index 163c8a89d1..d1d17f735a 100644 --- a/src/utils/hooks/api/loans/use-get-account-positions.tsx +++ b/src/utils/hooks/api/loans/use-get-account-positions.tsx @@ -1,212 +1,98 @@ -import { BorrowPosition, CurrencyExt, LendPosition, LoanAsset, TickerToData } from '@interlay/interbtc-api'; -import { MonetaryAmount } from '@interlay/monetary-js'; -import { AccountId } from '@polkadot/types/interfaces'; +import { BorrowPosition, CollateralPosition } from '@interlay/interbtc-api'; import Big from 'big.js'; -import { useMemo } from 'react'; import { useErrorHandler } from 'react-error-boundary'; import { useQuery } from 'react-query'; -import { convertMonetaryAmountToValueInUSD } from '@/common/utils/utils'; -import { getSubsidyRewardApy } from '@/pages/Loans/LoansOverview/utils/get-subsidy-rewards-apy'; import { BLOCKTIME_REFETCH_INTERVAL } from '@/utils/constants/api'; -import { getTokenPrice } from '@/utils/helpers/prices'; import useAccountId from '../../use-account-id'; -import { Prices, useGetPrices } from '../use-get-prices'; -import { useGetAccountSubsidyRewards } from './use-get-account-subsidy-rewards'; -import { useGetLoanAssets } from './use-get-loan-assets'; -import { getPositionsSumOfFieldsInUSD } from './utils'; interface AccountPositionsData { - lendPositions: LendPosition[]; + lendPositions: CollateralPosition[]; borrowPositions: BorrowPosition[]; } -const getAccountLendPositions = (accountId: AccountId): Promise> => - window.bridge.loans.getLendPositionsOfAccount(accountId); - -const getAccountBorrowPositions = (accountId: AccountId): Promise> => - window.bridge.loans.getBorrowPositionsOfAccount(accountId); - -const getAccountPositionsQuery = async (accountId: AccountId): Promise => { - const [lendPositions, borrowPositions] = await Promise.all([ - getAccountLendPositions(accountId), - getAccountBorrowPositions(accountId) - ]); +interface PositionsThresholdsData { + collateral: Big; + liquidation: Big; +} - return { - borrowPositions, - lendPositions +type UseGetAccountPositions = { + data: Partial & { + hasCollateral: boolean; }; + refetch: () => void; }; -interface PositionsThresholdsData { - collateral: Big; - liquidation: Big; +interface UseGetLendPositionsOfAccount { + data: Array | undefined; + refetch: () => void; } -interface AccountPositionsStatisticsData { - supplyAmountUSD: Big; - borrowAmountUSD: Big; - collateralAmountUSD: Big; - liquidationAmountUSD: Big; - collateralizedAmountUSD: Big; - earnedInterestAmountUSD: Big; - earnedDeptAmountUSD: Big; - netAmountUSD: Big; - netAPY: Big; - thresholds?: PositionsThresholdsData; +interface UseGetBorrowPositionsOfAccount { + data: Array | undefined; + refetch: () => void; } -const getNetAPY = ( - lendPositions: LendPosition[], - borrowPositions: BorrowPosition[], - assets: TickerToData, - supplyAmountUSD: Big, - prices: Prices -): Big => { - if (!supplyAmountUSD.gt(0)) { - return new Big(0); - } - - const totalLendApy = lendPositions.reduce((total, position) => { - const { lendApy, lendReward } = assets[position.currency.ticker]; - const rewardsApy = getSubsidyRewardApy(position.currency, lendReward, prices); - const positionApy = lendApy.add(rewardsApy || 0); - const positionUSDValue = convertMonetaryAmountToValueInUSD( - position.amount, - getTokenPrice(prices, position.currency.ticker)?.usd - ); - - return positionUSDValue ? total.add(positionApy.mul(positionUSDValue)) : total; - }, new Big(0)); - - const totalBorrowApy = borrowPositions.reduce((total, position) => { - const { borrowApy, borrowReward } = assets[position.currency.ticker]; - const rewardsApy = getSubsidyRewardApy(position.currency, borrowReward, prices); - const positionApy = borrowApy.sub(rewardsApy || 0); - const positionUSDValue = convertMonetaryAmountToValueInUSD( - position.amount, - getTokenPrice(prices, position.currency.ticker)?.usd - ); - - return positionUSDValue ? total.add(positionApy.mul(positionUSDValue)) : total; - }, new Big(0)); - - return totalLendApy.sub(totalBorrowApy).div(supplyAmountUSD); -}; +const useGetLendPositionsOfAccount = (): UseGetLendPositionsOfAccount => { + const accountId = useAccountId(); -const getAccountPositionsStats = ( - assets: TickerToData, - lendPositions: LendPosition[], - borrowPositions: BorrowPosition[], - subsidyRewards: MonetaryAmount, - prices: Prices -): AccountPositionsStatisticsData => { - const supplyAmountUSD = getPositionsSumOfFieldsInUSD('amount', lendPositions, prices); - - const borrowAmountUSD = getPositionsSumOfFieldsInUSD('amount', borrowPositions, prices); - - const collateralLendPositions = lendPositions.filter(({ isCollateral }) => isCollateral); - - const collateralPositionsWithAppliedCollateralThreshold = collateralLendPositions.map( - ({ amount, currency, ...rest }) => ({ - // MEMO: compute total value based on collateral threshold (not full lend amount value) - amount: amount.mul(assets[currency.ticker].collateralThreshold), - currency, - ...rest - }) - ); - - const collateralPositionsWithAppliedLiquidationThreshold = collateralLendPositions.map( - ({ amount, currency, ...rest }) => ({ - // MEMO: compute total value based on collateral threshold (not full lend amount value) - amount: amount.mul(assets[currency.ticker].liquidationThreshold), - currency, - ...rest - }) - ); - - const collateralAmountUSD = getPositionsSumOfFieldsInUSD( - 'amount', - collateralPositionsWithAppliedCollateralThreshold, - prices - ); - const liquidationAmountUSD = getPositionsSumOfFieldsInUSD( - 'amount', - collateralPositionsWithAppliedLiquidationThreshold, - prices - ); - - const collateralizedAmountUSD = getPositionsSumOfFieldsInUSD('amount', collateralLendPositions, prices); - const earnedInterestAmountUSD = getPositionsSumOfFieldsInUSD('earnedInterest', lendPositions, prices); - const earnedDeptAmountUSD = getPositionsSumOfFieldsInUSD('accumulatedDebt', borrowPositions, prices); - - const totalEarnedRewardsUSDValue = - convertMonetaryAmountToValueInUSD(subsidyRewards, getTokenPrice(prices, subsidyRewards.currency.ticker)?.usd) || 0; - const netAmountUSD = earnedInterestAmountUSD.add(totalEarnedRewardsUSDValue).sub(earnedDeptAmountUSD); - - const netAPY = getNetAPY(lendPositions, borrowPositions, assets, supplyAmountUSD, prices); + const { data, error, refetch } = useQuery({ + queryKey: ['getLendPositionsOfAccount', accountId], + queryFn: async () => { + if (!accountId) { + throw new Error('Something went wrong!'); + } - return { - supplyAmountUSD, - borrowAmountUSD, - earnedInterestAmountUSD, - collateralAmountUSD, - liquidationAmountUSD, - collateralizedAmountUSD, - earnedDeptAmountUSD, - netAmountUSD, - netAPY - }; -}; + return await window.bridge.loans.getLendPositionsOfAccount(accountId); + }, + enabled: !!accountId, + refetchInterval: BLOCKTIME_REFETCH_INTERVAL + }); -type UseGetAccountPositions = { - data: Partial & { - statistics: AccountPositionsStatisticsData | undefined; - hasCollateral: boolean; - }; - refetch: () => void; + useErrorHandler(error); + + return { data, refetch }; }; -const useGetAccountPositions = (): UseGetAccountPositions => { +const useGetBorrowPositionsOfAccount = (): UseGetBorrowPositionsOfAccount => { const accountId = useAccountId(); - const prices = useGetPrices(); - const { data: assets } = useGetLoanAssets(); + const { data, error, refetch } = useQuery({ + queryKey: ['getBorrowPositionsOfAccount', accountId], + queryFn: async () => { + if (!accountId) { + throw new Error('Something went wrong!'); + } - const { data: positions, error: positionsError, refetch: refetchPositions } = useQuery({ - queryKey: ['positions', accountId], - queryFn: () => accountId && getAccountPositionsQuery(accountId), + return await window.bridge.loans.getBorrowPositionsOfAccount(accountId); + }, enabled: !!accountId, refetchInterval: BLOCKTIME_REFETCH_INTERVAL }); - const { data: subsidyRewards } = useGetAccountSubsidyRewards(); + useErrorHandler(error); - // MEMO: we dont need assets as a dependency, since we only use the collateral threshold and - // it's value is very unlikely to change - const statistics = useMemo(() => { - if (!assets || !positions || !subsidyRewards || !prices) { - return undefined; - } + return { data, refetch }; +}; - return getAccountPositionsStats(assets, positions.lendPositions, positions.borrowPositions, subsidyRewards, prices); - // eslint-disable-next-line react-hooks/exhaustive-deps - }, [positions, prices, subsidyRewards]); +const useGetAccountPositions = (): UseGetAccountPositions => { + const { data: lendPositions, refetch: lendPositionsRefetch } = useGetLendPositionsOfAccount(); - useErrorHandler(positionsError); + const { data: borrowPositions, refetch: borrowPositionsRefetch } = useGetBorrowPositionsOfAccount(); return { data: { - borrowPositions: positions?.borrowPositions, - lendPositions: positions?.lendPositions, - hasCollateral: !!positions?.lendPositions.find((position) => position.isCollateral), - statistics + borrowPositions: borrowPositions, + lendPositions: lendPositions, + hasCollateral: !!lendPositions?.find((position) => position.isCollateral) }, - refetch: refetchPositions + refetch: () => { + lendPositionsRefetch(); + borrowPositionsRefetch(); + } }; }; export { useGetAccountPositions }; -export type { AccountPositionsData, AccountPositionsStatisticsData, PositionsThresholdsData }; +export type { AccountPositionsData, PositionsThresholdsData }; diff --git a/src/utils/hooks/api/loans/use-get-loan-assets.tsx b/src/utils/hooks/api/loans/use-get-loan-assets.tsx index 74490b2a34..d94d3d6559 100644 --- a/src/utils/hooks/api/loans/use-get-loan-assets.tsx +++ b/src/utils/hooks/api/loans/use-get-loan-assets.tsx @@ -1,31 +1,23 @@ import { LoanAsset, TickerToData } from '@interlay/interbtc-api'; import { useErrorHandler } from 'react-error-boundary'; -import { useQuery, useQueryClient } from 'react-query'; +import { useQuery } from 'react-query'; import { BLOCKTIME_REFETCH_INTERVAL } from '@/utils/constants/api'; -interface LoanAssetsData { +interface UseGetLoansAssets { data: TickerToData | undefined; refetch: () => void; } -const getLoanAssets = (): Promise> => window.bridge.loans.getLoanAssets(); - -const useGetLoanAssets = (): LoanAssetsData => { - const queryKey = ['loan-assets']; - - const { data, error } = useQuery({ - queryKey, - queryFn: getLoanAssets, +const useGetLoanAssets = (): UseGetLoansAssets => { + const { data, error, refetch } = useQuery({ + queryKey: ['loan-assets'], + queryFn: (): Promise> => window.bridge.loans.getLoanAssets(), refetchInterval: BLOCKTIME_REFETCH_INTERVAL }); useErrorHandler(error); - const queryClient = useQueryClient(); - - const refetch = () => queryClient.invalidateQueries(queryKey); - return { data, refetch }; }; diff --git a/src/utils/hooks/api/loans/use-loan-mutation.tsx b/src/utils/hooks/api/loans/use-loan-mutation.tsx index 5af5963531..ac43ef35a9 100644 --- a/src/utils/hooks/api/loans/use-loan-mutation.tsx +++ b/src/utils/hooks/api/loans/use-loan-mutation.tsx @@ -27,11 +27,15 @@ const mutateLoan = ({ loanType, amount, isMaxAmount }: CreateLoanVariables) => { } }; -type UseLoanMutation = { onSuccess: () => void }; +type UseLoanMutation = { onSuccess: () => void; onError: (error: Error) => void }; -const useLoanMutation = ({ onSuccess }: UseLoanMutation): UseMutationResult => { +const useLoanMutation = ({ + onSuccess, + onError +}: UseLoanMutation): UseMutationResult => { return useMutation(mutateLoan, { - onSuccess + onSuccess, + onError }); }; diff --git a/src/utils/hooks/api/loans/utils.ts b/src/utils/hooks/api/loans/utils.ts deleted file mode 100644 index 71b0346b27..0000000000 --- a/src/utils/hooks/api/loans/utils.ts +++ /dev/null @@ -1,37 +0,0 @@ -import { BorrowPosition, LendPosition } from '@interlay/interbtc-api'; -import Big from 'big.js'; - -import { convertMonetaryAmountToValueInUSD } from '@/common/utils/utils'; -import { getTokenPrice } from '@/utils/helpers/prices'; - -import { Prices } from '../use-get-prices'; - -type Field = T extends LendPosition - ? Extract - : Extract; - -/** - * This function uses the value specified in the object key `field` param - * to calculate total USD amount - * @param {Field} field The object key to be summed. - * @param {T[]} positions The data to be used in the sum. - * @param {Prices} prices The prices data needed to calculate the total USD value - * @return {Big} Summed USD amount - */ -const getPositionsSumOfFieldsInUSD = ( - field: Field, - positions: T[], - prices: Prices -): Big => - positions.reduce((totalValue: Big, position: T) => { - const price = getTokenPrice(prices, position.currency.ticker)?.usd; - - if (price === undefined) { - console.error(`useGetAccountCollateralization: No exchange rate found for currency: ${position.currency.name}`); - } - - const positionUSDValue = convertMonetaryAmountToValueInUSD(position[field as 'amount'], price); - return positionUSDValue === null ? totalValue : totalValue.add(positionUSDValue); - }, Big(0)); - -export { getPositionsSumOfFieldsInUSD }; diff --git a/src/utils/hooks/api/tokens/use-get-balances.tsx b/src/utils/hooks/api/tokens/use-get-balances.tsx index c1b8849d83..27ba5f300c 100644 --- a/src/utils/hooks/api/tokens/use-get-balances.tsx +++ b/src/utils/hooks/api/tokens/use-get-balances.tsx @@ -8,6 +8,7 @@ import { useSelector } from 'react-redux'; import { StoreType } from '@/common/types/util.types'; import { GOVERNANCE_TOKEN, TRANSACTION_FEE_AMOUNT } from '@/config/relay-chains'; import { useSubstrateSecureState } from '@/lib/substrate'; +import { REFETCH_INTERVAL } from '@/utils/constants/api'; import { useGetCurrencies } from '@/utils/hooks/api/use-get-currencies'; import useAccountId from '@/utils/hooks/use-account-id'; @@ -44,7 +45,8 @@ const useGetBalances = (): UseGetBalances => { const queryResult = useQuery({ queryKey: getBalancesQueryKey(selectedAccount?.address), queryFn: () => (accountId && currencies ? getBalances(currencies, accountId) : undefined), - enabled: selectedAccount && accountId && isCurrenciesSuccess && bridgeLoaded + enabled: selectedAccount && accountId && isCurrenciesSuccess && bridgeLoaded, + refetchInterval: REFETCH_INTERVAL.BLOCK }); const { data, error } = queryResult; @@ -58,17 +60,17 @@ const useGetBalances = (): UseGetBalances => { // from the return value const getAvailableBalance = useCallback( (ticker: string) => { - const { free } = getBalance(ticker) || {}; + const { transferable } = getBalance(ticker) || {}; if (ticker === GOVERNANCE_TOKEN.ticker) { - if (!free) return undefined; + if (!transferable) return undefined; - const governanceBalance = free.sub(TRANSACTION_FEE_AMOUNT); + const governanceBalance = transferable.sub(TRANSACTION_FEE_AMOUNT); return governanceBalance.toBig().gte(0) ? governanceBalance : newMonetaryAmount(0, governanceBalance.currency); } - return free; + return transferable; }, [getBalance] ); diff --git a/src/utils/hooks/api/use-get-collateral-currencies.tsx b/src/utils/hooks/api/use-get-collateral-currencies.tsx index 518cca1cfa..2e88864af9 100644 --- a/src/utils/hooks/api/use-get-collateral-currencies.tsx +++ b/src/utils/hooks/api/use-get-collateral-currencies.tsx @@ -1,8 +1,7 @@ import { CollateralCurrencyExt, getCollateralCurrencies } from '@interlay/interbtc-api'; import { useQuery, UseQueryResult } from 'react-query'; -const getCurrencies = async (): Promise> => - getCollateralCurrencies(window.bridge.api, window.bridge.assetRegistry, window.bridge.loans); +const getCurrencies = async (): Promise> => getCollateralCurrencies(window.bridge.api); const useGetCollateralCurrencies = (bridgeLoaded: boolean): UseQueryResult> => { return useQuery({ queryKey: 'getCollateralCurrencies', queryFn: getCurrencies, enabled: bridgeLoaded }); diff --git a/src/utils/hooks/api/use-get-currencies.tsx b/src/utils/hooks/api/use-get-currencies.tsx index 45cb37789a..1442f6a14c 100644 --- a/src/utils/hooks/api/use-get-currencies.tsx +++ b/src/utils/hooks/api/use-get-currencies.tsx @@ -4,24 +4,33 @@ import { useQuery, UseQueryResult } from 'react-query'; import { NATIVE_CURRENCIES } from '@/utils/constants/currency'; +import { FeatureFlags, useFeatureFlag } from '../use-feature-flag'; + type UseGetCurrenciesResult = UseQueryResult> & { getCurrencyFromTicker: (ticker: string) => CurrencyExt; getForeignCurrencyFromId: (id: number) => CurrencyExt; getCurrencyFromIdPrimitive: (currencyPrimitive: InterbtcPrimitivesCurrencyId) => CurrencyExt; }; -const getCurrencies = async (): Promise> => { - const isLendingEnabled = process.env.REACT_APP_FEATURE_FLAG_LENDING === 'enabled'; - const [foreignCurrencies, lendCurrencies] = await Promise.all([ +const getCurrencies = async (featureFlags: { lending: boolean; amm: boolean }): Promise> => { + const [foreignCurrencies, lendCurrencies, lpTokens] = await Promise.all([ window.bridge.assetRegistry.getForeignAssets(), - isLendingEnabled ? window.bridge.loans.getLendTokens() : [] + featureFlags.lending ? window.bridge.loans.getLendTokens() : [], + featureFlags.amm ? window.bridge.amm.getLpTokens() : [] ]); - return [...NATIVE_CURRENCIES, ...foreignCurrencies, ...lendCurrencies]; + return [...NATIVE_CURRENCIES, ...foreignCurrencies, ...lendCurrencies, ...lpTokens]; }; // Returns all currencies, both native and foreign and helping utils to get CurrencyExt object. const useGetCurrencies = (bridgeLoaded: boolean): UseGetCurrenciesResult => { - const queryResult = useQuery({ queryKey: 'getCurrencies', queryFn: getCurrencies, enabled: bridgeLoaded }); + const isLendingEnabled = useFeatureFlag(FeatureFlags.LENDING); + const isAMMEnabled = useFeatureFlag(FeatureFlags.AMM); + + const queryResult = useQuery({ + queryKey: 'getCurrencies', + queryFn: () => getCurrencies({ lending: isLendingEnabled, amm: isAMMEnabled }), + enabled: bridgeLoaded + }); // Throws when passed parameter is not ticker of any currency or currencies are not loaded yet. const getCurrencyFromTicker = useCallback( @@ -66,18 +75,18 @@ const useGetCurrencies = (bridgeLoaded: boolean): UseGetCurrenciesResult => { throw new Error('useGetCurrencies: Cannot call `getLendCurrencyFromId` before currencies are loaded.'); } - const foreignCurrency = queryResult.data.find( - (currency) => 'lendToken' in currency && currency.lendToken.id === id - ); - if (foreignCurrency === undefined) { + const lendCurrency = queryResult.data.find((currency) => 'lendToken' in currency && currency.lendToken.id === id); + if (lendCurrency === undefined) { throw new Error(`useGetCurrencies: getLendCurrencyFromId: Lend token with id ${id} not found.`); } - return foreignCurrency; + return lendCurrency; }, [queryResult] ); + // TODO: add getter according to existing LP Tokens identifiers + // Throws when passed parameter is not primitiveId of any currency or currencies are not loaded yet. // Synchronous function which has the same functionality as async `currencyIdToMonetaryCurrency` in lib. const getCurrencyFromIdPrimitive = useCallback( diff --git a/src/utils/hooks/api/use-get-prices.tsx b/src/utils/hooks/api/use-get-prices.tsx index 23d25f2f4e..73fd8df062 100644 --- a/src/utils/hooks/api/use-get-prices.tsx +++ b/src/utils/hooks/api/use-get-prices.tsx @@ -5,7 +5,7 @@ import { useQuery } from 'react-query'; import { useSelector } from 'react-redux'; import { StoreType } from '@/common/types/util.types'; -import { BLOCKTIME_REFETCH_INTERVAL, PRICES_API } from '@/utils/constants/api'; +import { PRICES_API, REFETCH_INTERVAL } from '@/utils/constants/api'; import { COINGECKO_ID_BY_CURRENCY_TICKER } from '@/utils/constants/currency'; import { useGetCurrencies } from './use-get-currencies'; @@ -64,9 +64,11 @@ type Prices = { const useGetPrices = (): Prices | undefined => { const { bridgeLoaded } = useSelector((state: StoreType) => state.general); const { data: currencies, isSuccess: isGetCurrenciesSuccess } = useGetCurrencies(bridgeLoaded); + + // TODO: error prone because the key computation is not complete const { data, error } = useQuery(['prices'], () => getPrices(currencies), { enabled: isGetCurrenciesSuccess, - refetchInterval: BLOCKTIME_REFETCH_INTERVAL + refetchInterval: REFETCH_INTERVAL.MINUTE }); useEffect(() => { diff --git a/src/utils/hooks/api/use-get-vesting-data.tsx b/src/utils/hooks/api/use-get-vesting-data.tsx new file mode 100644 index 0000000000..48972a88f1 --- /dev/null +++ b/src/utils/hooks/api/use-get-vesting-data.tsx @@ -0,0 +1,50 @@ +import { AccountId } from '@polkadot/types/interfaces'; +import { Codec } from '@polkadot/types/types'; +import { useErrorHandler } from 'react-error-boundary'; +import { useQuery } from 'react-query'; + +import { BLOCKTIME_REFETCH_INTERVAL } from '@/utils/constants/api'; + +import useAccountId from '../use-account-id'; + +type VestingData = { + schedules: Codec; + isClaimable: boolean; +}; + +interface UseGetVestingResult { + data: VestingData | undefined; + refetch: () => void; +} + +const getVestingData = async (accountId: AccountId): Promise => { + const currentBlockNumber = await window.bridge.system.getCurrentBlockNumber(); + + const schedules = await window.bridge.api.query.vesting.vestingSchedules(accountId); + + const schedule = schedules[0]; + const isClaimable = !!schedule && currentBlockNumber > schedule.start + schedule.period; + + return { + schedules, + isClaimable + }; +}; + +const useGetVestingData = (): UseGetVestingResult => { + const accountId = useAccountId(); + + const queryKey = ['vesting-schedule', accountId]; + const { data, error, refetch } = useQuery({ + queryKey, + queryFn: () => accountId && getVestingData(accountId), + refetchInterval: BLOCKTIME_REFETCH_INTERVAL + }); + + useErrorHandler(error); + + return { data, refetch }; +}; + +export { useGetVestingData }; +export type { UseGetVestingResult }; diff --git a/src/utils/hooks/api/xcm/use-xcm-bridge.ts b/src/utils/hooks/api/xcm/use-xcm-bridge.ts index 390d10958f..4b086c55c8 100644 --- a/src/utils/hooks/api/xcm/use-xcm-bridge.ts +++ b/src/utils/hooks/api/xcm/use-xcm-bridge.ts @@ -5,13 +5,13 @@ import { firstValueFrom } from 'rxjs'; import { XCM_ADAPTERS } from '@/config/relay-chains'; import { BITCOIN_NETWORK } from '@/constants'; +// MEMO: BitcoinNetwork type is not available on XCM bridge +const XCMNetwork = BITCOIN_NETWORK === 'mainnet' ? 'mainnet' : 'testnet'; + const XCMBridge = new Bridge({ adapters: Object.values(XCM_ADAPTERS) }); -// MEMO: BitcoinNetwork type is not available on XCM bridge -const XCMNetwork = BITCOIN_NETWORK === 'mainnet' ? 'mainnet' : 'testnet'; - // TODO: This config needs to be pushed higher up the app. // Not sure how this will look: something to decide when // adding USDT support. diff --git a/src/utils/hooks/use-copy-to-clipboard.tsx b/src/utils/hooks/use-copy-to-clipboard.tsx index eaa3d955f9..e580a23b64 100644 --- a/src/utils/hooks/use-copy-to-clipboard.tsx +++ b/src/utils/hooks/use-copy-to-clipboard.tsx @@ -9,7 +9,7 @@ interface CopyToClipboardUIProps { const useCopyToClipboard = ( text: string ): { - handleCopyToClipboard: () => void; + handleCopyToClipboard: (event: React.PointerEvent) => void; CopyToClipboardUI: React.ElementType; } => { const [state, copyToClipboard] = useLibCopyToClipboard(); @@ -19,11 +19,15 @@ const useCopyToClipboard = ( setShowCopied(false); }, 1000); - const handleCopyToClipboard = React.useCallback(() => { - copyToClipboard(text); - setShowCopied(true); - reset(); - }, [copyToClipboard, reset, text]); + const handleCopyToClipboard = React.useCallback( + (event: React.PointerEvent) => { + event.stopPropagation(); + copyToClipboard(text); + setShowCopied(true); + reset(); + }, + [copyToClipboard, reset, text] + ); const CopyToClipboardUI = React.useMemo(() => { // eslint-disable-next-line react/display-name diff --git a/src/utils/hooks/use-copy-tooltip.tsx b/src/utils/hooks/use-copy-tooltip.tsx new file mode 100644 index 0000000000..36ec13ccd4 --- /dev/null +++ b/src/utils/hooks/use-copy-tooltip.tsx @@ -0,0 +1,63 @@ +import { HoverProps, useHover } from '@react-aria/interactions'; +import { mergeProps } from '@react-aria/utils'; +import { DOMAttributes, FocusableElement } from '@react-types/shared'; +import { useMemo, useState } from 'react'; +import { useTranslation } from 'react-i18next'; +import { useTimeoutFn } from 'react-use'; + +type CopyTooltipProp = Pick; + +type CopyTooltipResult = { + buttonProps: DOMAttributes; + tooltipProps: { + isOpen: boolean; + label: string; + }; +}; + +const useCopyTooltip = (props?: CopyTooltipProp): CopyTooltipResult => { + const { t } = useTranslation(); + + const defaultTooltipProps = useMemo(() => ({ isOpen: false, label: t('click_to_copy') }), [t]); + + const [tooltipProps, setTooltipProps] = useState(defaultTooltipProps); + const [isReadyToDefaultLabel, cancelLabelReset, setToDefaultLabel] = useTimeoutFn( + () => setTooltipProps((s) => ({ ...s, label: t('click_to_copy') })), + 1000 + ); + + const [isReadyToExit, cancelExit, delayExit] = useTimeoutFn(() => { + setTooltipProps((s) => ({ ...s, isOpen: false })); + }, 500); + + const defaultHoverProps = { + onHoverStart: () => { + // Cancel exit if is still ongoing + if (!isReadyToExit()) { + cancelExit(); + } + + setTooltipProps({ label: t('click_to_copy'), isOpen: true }); + }, + onHoverEnd: delayExit + }; + + const { hoverProps } = useHover({ ...props, ...defaultHoverProps }); + + const handlePress = () => { + // Cancel setting label to default if is still ongoing + if (!isReadyToDefaultLabel()) { + cancelLabelReset(); + } + setTooltipProps({ isOpen: true, label: t('copied') }); + setToDefaultLabel(); + }; + + return { + buttonProps: mergeProps(hoverProps, { onPress: handlePress }), + tooltipProps + }; +}; + +export { useCopyTooltip }; +export type { CopyTooltipResult }; diff --git a/src/utils/hooks/use-feature-flag.ts b/src/utils/hooks/use-feature-flag.ts index 7c25394d53..58ae2cb1f7 100644 --- a/src/utils/hooks/use-feature-flag.ts +++ b/src/utils/hooks/use-feature-flag.ts @@ -1,9 +1,13 @@ enum FeatureFlags { - LENDING = 'lending' + LENDING = 'lending', + AMM = 'amm', + WALLET = 'wallet' } const featureFlags: Record = { - [FeatureFlags.LENDING]: process.env.REACT_APP_FEATURE_FLAG_LENDING + [FeatureFlags.LENDING]: process.env.REACT_APP_FEATURE_FLAG_LENDING, + [FeatureFlags.AMM]: process.env.REACT_APP_FEATURE_FLAG_AMM, + [FeatureFlags.WALLET]: process.env.REACT_APP_FEATURE_FLAG_WALLET }; const useFeatureFlag = (feature: FeatureFlags): boolean => featureFlags[feature] === 'enabled'; diff --git a/src/utils/hooks/use-wallet.ts b/src/utils/hooks/use-wallet.ts new file mode 100644 index 0000000000..3dbbb9e282 --- /dev/null +++ b/src/utils/hooks/use-wallet.ts @@ -0,0 +1,44 @@ +import { newAccountId } from '@interlay/interbtc-api'; +import { InjectedAccountWithMeta } from '@polkadot/extension-inject/types'; +import { AccountId } from '@polkadot/types/interfaces'; +import { useMemo } from 'react'; +import { useSelector } from 'react-redux'; + +import { StoreType } from '@/common/types/util.types'; +import { useSubstrateSecureState } from '@/lib/substrate'; + +type UseWalletResult = { + isAuth: boolean; + account?: AccountId; + accounts: InjectedAccountWithMeta[]; +}; + +const useWallet = (): UseWalletResult => { + const { selectedAccount, accounts } = useSubstrateSecureState(); + const { bridgeLoaded } = useSelector((state: StoreType) => state.general); + const substrate = useSubstrateSecureState(); + + const account = useMemo( + () => (selectedAccount && bridgeLoaded ? newAccountId(window.bridge.api, selectedAccount.address) : undefined), + [bridgeLoaded, selectedAccount] + ); + + const isAuth = !!substrate.selectedAccount; + + if (!bridgeLoaded || !selectedAccount) { + return { + isAuth: false, + account: undefined, + accounts: [] + }; + } + + return { + isAuth, + account, + accounts: isAuth ? accounts : [] + }; +}; + +export { useWallet }; +export type { UseWalletResult }; diff --git a/yarn.lock b/yarn.lock index fd3f9fbe97..e1db99f1ba 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2,185 +2,106 @@ # yarn lockfile v1 -"@acala-network/api-derive@4.1.4-3": - version "4.1.4-3" - resolved "https://registry.yarnpkg.com/@acala-network/api-derive/-/api-derive-4.1.4-3.tgz#f4eeeb22c3335f889391bf998f6bc9a014c2b6d9" - integrity sha512-M1H09WHBJgQARJY/Z6g4NY8Ll+DJdoBsvF+kZqcewVtoF3bfoAAEfCNZTAAHRtTrS6MXK30hdRuMW3DOoiGMRA== +"@acala-network/api-derive@4.1.8-9": + version "4.1.8-9" + resolved "https://registry.yarnpkg.com/@acala-network/api-derive/-/api-derive-4.1.8-9.tgz#f4d3969665fe2e92d2fca73d2c403e4f26b519bd" + integrity sha512-GN/9rXRGy5zJ0eEeBSoO2lC3MgT2D2A4O94DMtONNQsD30RMBhtSj5PTd8Mo+RnaFVzwLrbsCNs1UxkmAg4Rlg== dependencies: - "@acala-network/types" "4.1.4-3" + "@acala-network/types" "4.1.8-9" "@babel/runtime" "^7.10.2" "@open-web3/orml-types" "^1.1.4" "@polkadot/api-derive" "^8.5.1" -"@acala-network/api-derive@4.1.6-23": - version "4.1.6-23" - resolved "https://registry.yarnpkg.com/@acala-network/api-derive/-/api-derive-4.1.6-23.tgz#365f6d76d5286a848f6382739c7c8bdd8983f807" - integrity sha512-6Y2zpCdM0IQmUILZplNjk0mOPeVm7WTiTVfx6/5IhOZ5w1HTFTXY7IYd1e/7EyGAeTfuhPepT8mQSlm5lQr+xw== +"@acala-network/api@4.1.8-9", "@acala-network/api@~4.1.8-9": + version "4.1.8-9" + resolved "https://registry.yarnpkg.com/@acala-network/api/-/api-4.1.8-9.tgz#9213e09b7c43b3df95eaf47fe78c989ddfe4207e" + integrity sha512-9kpYQYe5vBCKWlyABh+Q2sjONDdtNfdv0PL0Tek3bpt00a3VjNIZvQro5ZSwzdpGJs5YcsiWPRMBq3iMgJNtGQ== dependencies: - "@acala-network/types" "4.1.6-23" - "@babel/runtime" "^7.10.2" - "@open-web3/orml-types" "^1.1.4" - "@polkadot/api-derive" "^8.5.1" - -"@acala-network/api@4.1.4-3": - version "4.1.4-3" - resolved "https://registry.yarnpkg.com/@acala-network/api/-/api-4.1.4-3.tgz#4d581fe2a627af36e6cccc22b2cb61264a6eaacc" - integrity sha512-BFFBxcZnRDWkKQzd78rlznvLQLXoI8yjEdQKP0dABTaPPguSnlNZ4SxEeUdlTjLF78FqfzoIGBdMPDiv1XPGeg== - dependencies: - "@acala-network/api-derive" "4.1.4-3" - "@acala-network/types" "4.1.4-3" - "@babel/runtime" "^7.10.2" - "@open-web3/orml-api-derive" "^1.1.4" - "@polkadot/api" "^8.5.1" - "@polkadot/rpc-core" "^8.5.1" - -"@acala-network/api@4.1.6-23": - version "4.1.6-23" - resolved "https://registry.yarnpkg.com/@acala-network/api/-/api-4.1.6-23.tgz#132d72c2209587eb0c46a1847d0215f38c187800" - integrity sha512-ZyGPofMH9+gLmrhxNC70kw+4f9Ztt483uH1d3wuRn1eVrNrjrZjy24jbnLBwXNVPzuk9jqDegywJObzQqnYfbg== - dependencies: - "@acala-network/api-derive" "4.1.6-23" - "@acala-network/types" "4.1.6-23" + "@acala-network/api-derive" "4.1.8-9" + "@acala-network/types" "4.1.8-9" "@babel/runtime" "^7.10.2" "@open-web3/orml-api-derive" "^1.1.4" - "@polkadot/api" "^8.5.1" - "@polkadot/rpc-core" "^8.5.1" - -"@acala-network/bodhi@^2.4.0", "@acala-network/bodhi@^2.4.20": - version "2.5.3" - resolved "https://registry.yarnpkg.com/@acala-network/bodhi/-/bodhi-2.5.3.tgz#04b646bc7e70346b6d8d26d7a0d8aefe6f9741eb" - integrity sha512-bAue5KvXvUTKj29PpdB5suxCYwGARDhCEcox1Qduj+KdzjyQO91hGGO4PVIL5nPj6Pm5PPW5LTVAXZdTZFZERw== - dependencies: - "@acala-network/eth-providers" "2.5.3" - "@ethersproject/abstract-provider" "~5.5.0" - "@ethersproject/abstract-signer" "~5.5.0" - "@ethersproject/address" "~5.5.0" - "@ethersproject/bignumber" "~5.5.0" - "@ethersproject/bytes" "~5.5.0" - "@ethersproject/logger" "~5.5.0" - "@ethersproject/properties" "~5.5.0" - "@ethersproject/strings" "~5.5.0" - "@polkadot/api" "8.1.1" - "@polkadot/keyring" "^9.0.1" - "@polkadot/types" "8.1.1" - "@polkadot/util" "^9.0.1" - "@polkadot/util-crypto" "^9.0.1" - "@types/bn.js" "~5.1.0" - bn.js "~5.2.0" - ethers "~5.5.0" + "@polkadot/api" "^9.9.1" + "@polkadot/rpc-core" "^9.9.1" "@acala-network/contracts@~4.3.4": version "4.3.4" resolved "https://registry.yarnpkg.com/@acala-network/contracts/-/contracts-4.3.4.tgz#f37cf54894c72b762df539042a61f90b10b68600" integrity sha512-oBgXGUjRW+lRo9TWGtCB1+OpEOFfhxW//wReb7V/YdbEElVvYuKw3lmfly/eZ/mdBgqxA3eXxNW0AgXiyOn2NQ== -"@acala-network/eth-providers@2.5.3": - version "2.5.3" - resolved "https://registry.yarnpkg.com/@acala-network/eth-providers/-/eth-providers-2.5.3.tgz#ae7286e241728feab94a74675ffe8702afed6145" - integrity sha512-ZT0a7Web76xDiJtKqWye8fpIs2O7RKzXoMfqnwfWwXiByt5ZvExseWmNLu9DuZrMCdb4N0+f87X0B23lzsQxuQ== +"@acala-network/eth-providers@^2.5.4": + version "2.6.5" + resolved "https://registry.yarnpkg.com/@acala-network/eth-providers/-/eth-providers-2.6.5.tgz#9087abe44a0686de5188ea962961519ecff20e66" + integrity sha512-Y0hi0LRN8pJ144dv9WcSi9nPn5Wez0h745EGa1/6NFtU7jsua0jg25WYJ53s17rXIMz8GUKdln9SAIeShQiEtw== dependencies: + "@acala-network/api" "~4.1.8-9" "@acala-network/contracts" "~4.3.4" - "@acala-network/eth-transactions" "2.5.3" - "@acala-network/types" "~4.1.5" - "@ethersproject/abstract-provider" "~5.5.0" - "@ethersproject/address" "~5.5.0" - "@ethersproject/bignumber" "~5.5.0" - "@ethersproject/bytes" "~5.5.0" - "@ethersproject/contracts" "~5.5.0" - "@ethersproject/keccak256" "~5.5.0" - "@ethersproject/logger" "~5.5.0" - "@ethersproject/networks" "~5.5.0" - "@ethersproject/properties" "~5.5.0" - "@ethersproject/providers" "~5.5.0" - "@ethersproject/transactions" "~5.5.0" - "@ethersproject/wallet" "~5.5.0" - "@polkadot/api" "8.1.1" - "@polkadot/api-augment" "8.1.1" - "@polkadot/api-derive" "8.1.1" - "@polkadot/keyring" "^9.0.1" - "@polkadot/types" "8.1.1" - "@polkadot/util" "^9.0.1" - "@polkadot/util-crypto" "^9.0.1" - ethers "~5.5.0" + "@acala-network/eth-transactions" "2.6.5" + "@acala-network/types" "~4.1.8-9" + "@ethersproject/abstract-provider" "~5.7.0" + "@ethersproject/address" "~5.7.0" + "@ethersproject/bignumber" "~5.7.0" + "@ethersproject/bytes" "~5.7.0" + "@ethersproject/contracts" "~5.7.0" + "@ethersproject/keccak256" "~5.7.0" + "@ethersproject/logger" "~5.7.0" + "@ethersproject/networks" "~5.7.0" + "@ethersproject/properties" "~5.7.0" + "@ethersproject/providers" "~5.7.0" + "@ethersproject/transactions" "~5.7.0" + "@ethersproject/wallet" "~5.7.0" + "@polkadot/api" "9.10.3" + "@polkadot/api-augment" "9.10.3" + "@polkadot/api-derive" "9.10.3" + "@polkadot/keyring" "^10.2.1" + "@polkadot/types" "9.10.3" + "@polkadot/util" "^10.2.1" + "@polkadot/util-crypto" "^10.2.1" + bn.js "~5.2.0" + ethers "~5.7.0" graphql "~16.0.1" graphql-request "~3.6.1" lru-cache "~7.8.2" -"@acala-network/eth-transactions@2.5.3": - version "2.5.3" - resolved "https://registry.yarnpkg.com/@acala-network/eth-transactions/-/eth-transactions-2.5.3.tgz#b8efcabe9b35edaf5ea8c027c4855425e94178fb" - integrity sha512-LomyjEgEUd0WQcyFpbT3WWHSxwPVEyPFVi2K9tQBuTgp04hxq89zPgyVOd0a7ReoKV1wReISNNgLWxIsTTldOw== - dependencies: - "@ethersproject/address" "~5.5.0" - "@ethersproject/bignumber" "~5.5.0" - "@ethersproject/bytes" "~5.5.0" - "@ethersproject/constants" "~5.5.0" - "@ethersproject/hash" "~5.5.0" - "@ethersproject/logger" "~5.5.0" - "@ethersproject/rlp" "~5.5.0" - "@ethersproject/transactions" "~5.5.0" - "@ethersproject/wallet" "~5.5.0" - "@polkadot/util-crypto" "^9.0.1" - -"@acala-network/sdk-core@4.1.4-3": - version "4.1.4-3" - resolved "https://registry.yarnpkg.com/@acala-network/sdk-core/-/sdk-core-4.1.4-3.tgz#d48b7d5602ed73bf0f3e2feb065cc55c7080b0f8" - integrity sha512-TGPsY5Dn6hKEGBzsTRKdYPWHM4LlBuPrsar+njh7A49gz9pyJBMdI/bs04O/i+i6Yf9ialVwjEF1GHC/DIZcJw== - dependencies: - "@polkadot/api" "^8.5.1" - "@polkadot/types" "^8.5.1" - "@types/events" "^3.0.0" - bignumber.js "^9.0.0" - events "^3.2.0" - lodash "^4.17.20" +"@acala-network/eth-transactions@2.6.5": + version "2.6.5" + resolved "https://registry.yarnpkg.com/@acala-network/eth-transactions/-/eth-transactions-2.6.5.tgz#ddc93a3cd766c89aa81acdcd4aa9d00854b2116d" + integrity sha512-r+1i3AWHpg+vWZbiTldDSztZAPh+lJl4d9NKh7DCRgEd5/yOXgK5D05j1tTISut3ckENHBE2m0MKDp/4xX+3Eg== + dependencies: + "@ethersproject/address" "~5.7.0" + "@ethersproject/bignumber" "~5.7.0" + "@ethersproject/bytes" "~5.7.0" + "@ethersproject/constants" "~5.7.0" + "@ethersproject/hash" "~5.7.0" + "@ethersproject/logger" "~5.7.0" + "@ethersproject/rlp" "~5.7.0" + "@ethersproject/transactions" "~5.7.0" + "@ethersproject/wallet" "~5.7.0" + "@polkadot/util-crypto" "^10.2.1" -"@acala-network/sdk-core@4.1.6-23": - version "4.1.6-23" - resolved "https://registry.yarnpkg.com/@acala-network/sdk-core/-/sdk-core-4.1.6-23.tgz#c8af958e52869bfd6eb067603b5746932855a3a1" - integrity sha512-6tk/z27N9huU3pbDI/1zmgd2lID/SPB6KN/z/Lzj0n2TcDN66RJG07zqCDYfWsZZwWOPIQ9rpXlrwEhwcxoWTQ== +"@acala-network/sdk-core@4.1.8-9": + version "4.1.8-9" + resolved "https://registry.yarnpkg.com/@acala-network/sdk-core/-/sdk-core-4.1.8-9.tgz#47de650483f74aa9320d9ff9a8cdcf0e48b4a192" + integrity sha512-hjJ4Qs20aacg9vUnt2xZne3nN+c73zS7sBklVwtzXLlW87QWKDHdvkRkGZyeeKujaGRnqODhYIPtGtPqd0t+ag== dependencies: - "@polkadot/api" "^8.5.1" - "@polkadot/types" "^8.5.1" + "@polkadot/api" "^9.9.1" + "@polkadot/types" "^9.9.1" "@types/events" "^3.0.0" bignumber.js "^9.0.0" events "^3.2.0" lodash "^4.17.20" -"@acala-network/sdk@4.1.4-3": - version "4.1.4-3" - resolved "https://registry.yarnpkg.com/@acala-network/sdk/-/sdk-4.1.4-3.tgz#d1a8dd461ced0cbb814e2822493462125c38760c" - integrity sha512-I/tGhlYtlMx69TaqSTzkMr2SUr53/W4lt/iH27/TI4sEo3/fMVMvCHKXJraAiXmQPag+eHN1X0EbLYE3MVw1Kw== +"@acala-network/sdk@4.1.8-9": + version "4.1.8-9" + resolved "https://registry.yarnpkg.com/@acala-network/sdk/-/sdk-4.1.8-9.tgz#3501296ec663346e2118dcfcc72e31afcdf63fbb" + integrity sha512-/e624PRyzwUJUEW4g7y4kVjs4WsDU2S6KPvn2Nbojl0bz0wrm05ghjD3lW98m8CcLLLv4wa4hldegFzx79LYgw== dependencies: - "@acala-network/api" "4.1.4-3" - "@acala-network/bodhi" "^2.4.0" - "@acala-network/type-definitions" "4.1.4-3" - "@polkadot/api" "^8.5.1" - "@polkadot/types" "^8.5.1" - "@types/events" "^3.0.0" - axios "^0.24.0" - bignumber.js "^9.0.0" - cross-fetch "^3.1.4" - ethers "^5.6.2" - events "^3.2.0" - graphql "^16.3.0" - graphql-request "^4.1.0" - lodash "^4.17.20" - rxjs "^7.4.0" - -"@acala-network/sdk@4.1.6-23": - version "4.1.6-23" - resolved "https://registry.yarnpkg.com/@acala-network/sdk/-/sdk-4.1.6-23.tgz#d8b5811ae2dce93b4401650251771848ae110436" - integrity sha512-nGxNx7ORJ7e4GWVeU4YmZ3HFYrHOZc62nAFBldMF+iZNzSPbNUgdO3cKvPtNGZ3nrP2R6s2cjpK6cgz/kaEAqQ== - dependencies: - "@acala-network/api" "4.1.6-23" - "@acala-network/bodhi" "^2.4.20" - "@acala-network/type-definitions" "4.1.6-23" - "@certusone/wormhole-sdk" "^0.5.0" - "@grpc/grpc-js" "^1.6.8" - "@improbable-eng/grpc-web-node-http-transport" "^0.15.0" - "@nuts-finance/sdk-stable-asset" "^1.1.16" - "@polkadot/api" "^8.5.1" - "@polkadot/types" "^8.5.1" + "@acala-network/api" "4.1.8-9" + "@acala-network/eth-providers" "^2.5.4" + "@acala-network/type-definitions" "4.1.8-9" + "@ethersproject/bignumber" "^5.7.0" + "@polkadot/api" "^9.9.1" + "@polkadot/types" "^9.9.1" "@types/events" "^3.0.0" axios "^0.24.0" bignumber.js "^9.0.0" @@ -189,57 +110,30 @@ events "^3.2.0" graphql "^16.3.0" graphql-request "^4.1.0" - grpc "^1.24.11" lodash "^4.17.20" - rxjs "^7.4.0" + lru-cache "^7.14.1" + rxjs "^7.5.7" -"@acala-network/type-definitions@4.1.4-3": - version "4.1.4-3" - resolved "https://registry.yarnpkg.com/@acala-network/type-definitions/-/type-definitions-4.1.4-3.tgz#cd79f13496ec9087ca44b35889efd4d702b66031" - integrity sha512-B9BSPC0Ultas21lQbmlzOcCTkzbo2mv6RtpYycG/ibcmzk58z4AoqCEVKFwevw1CLMdfwCmb97gnfC55J0s3GQ== +"@acala-network/type-definitions@4.1.8-9": + version "4.1.8-9" + resolved "https://registry.yarnpkg.com/@acala-network/type-definitions/-/type-definitions-4.1.8-9.tgz#be238e2e269cd701b79b0af5f9ed4d9c168d94c0" + integrity sha512-z0pDbVwxXutpnt2CRkfamaZel/OHF65hzwyolk+Vn+4wFFGY5QMh+nDiw8oEYdZENHEPUEAVa4XOQWRsE329jQ== dependencies: "@open-web3/orml-type-definitions" "^1.1.4" -"@acala-network/type-definitions@4.1.5", "@acala-network/type-definitions@^4.1.5": +"@acala-network/type-definitions@^4.1.5": version "4.1.5" resolved "https://registry.yarnpkg.com/@acala-network/type-definitions/-/type-definitions-4.1.5.tgz#c02624ba9bb637588ddd184a4ce35ab7d9de2bf6" integrity sha512-XwXtKf5ESfzGk32N1sE3MlBtnamz2JZYtjB6KcKe9eOyv+3lowQvRn4Z347rNSEp+tpenZWnLwBXk4XWhdiSoQ== dependencies: "@open-web3/orml-type-definitions" "^1.1.4" -"@acala-network/type-definitions@4.1.6-23": - version "4.1.6-23" - resolved "https://registry.yarnpkg.com/@acala-network/type-definitions/-/type-definitions-4.1.6-23.tgz#b41224512ea52974f71367d45e911cd9cba64a9e" - integrity sha512-3QKjevSvAEQgyJEfj51dNAZlRjRhYJHmtowgCbkS2Jr1D4YwdggzgzQaO5cVi4hgyV3LGav+NiYyuNpGewRHNg== - dependencies: - "@open-web3/orml-type-definitions" "^1.1.4" - -"@acala-network/types@4.1.4-3": - version "4.1.4-3" - resolved "https://registry.yarnpkg.com/@acala-network/types/-/types-4.1.4-3.tgz#b88f8ec14b9e6e17f879cec194d1a85f22deea59" - integrity sha512-Vu4RwQWF3fYbC+H3WxfFkVOHSkrSJG8r+2bEc8uKEDpyVLtbxZV6EJJymjzi94Jzscc8eXLxO3cQ1zWivCZxRw== - dependencies: - "@acala-network/type-definitions" "4.1.4-3" - "@babel/runtime" "^7.10.2" - "@open-web3/api-mobx" "^1.1.4" - "@open-web3/orml-types" "^1.1.4" - -"@acala-network/types@4.1.6-23": - version "4.1.6-23" - resolved "https://registry.yarnpkg.com/@acala-network/types/-/types-4.1.6-23.tgz#c0b67a55f70677dad437f81e5afeb44830980665" - integrity sha512-moMkPzyG3pqzbAwnwb8ekc5LDtpB0dPBN9eyQSDN78+wHSAQLyiP6uYhtd3gWPvvs5XsSw/cJEyh9vcc6+PgOw== - dependencies: - "@acala-network/type-definitions" "4.1.6-23" - "@babel/runtime" "^7.10.2" - "@open-web3/api-mobx" "^1.1.4" - "@open-web3/orml-types" "^1.1.4" - -"@acala-network/types@~4.1.5": - version "4.1.5" - resolved "https://registry.yarnpkg.com/@acala-network/types/-/types-4.1.5.tgz#ae3ced6dcac911d7f36416881b95fcc4e1ebe6f5" - integrity sha512-MTT0d+AFCMrm/ngNDoPJDAS8XSayFgLRUH9wT5Nvks/6n3WCzgNyGGZsgQI0PWGer/fdvAryJmNCX2xbKq4ghA== +"@acala-network/types@4.1.8-9", "@acala-network/types@~4.1.8-9": + version "4.1.8-9" + resolved "https://registry.yarnpkg.com/@acala-network/types/-/types-4.1.8-9.tgz#afc11f555dc900149eff132857f456500dcfb892" + integrity sha512-sILIcHyndYYde7xHbjwbLHfuWo3GmC3ZYTe515GrgC6hoBAzADqyA92FyZscSEQKpd7olnoPiMqq7ClmbGkdpA== dependencies: - "@acala-network/type-definitions" "4.1.5" + "@acala-network/type-definitions" "4.1.8-9" "@babel/runtime" "^7.10.2" "@open-web3/api-mobx" "^1.1.4" "@open-web3/orml-types" "^1.1.4" @@ -1642,12 +1536,12 @@ dependencies: regenerator-runtime "^0.13.4" -"@babel/runtime@^7.10.5", "@babel/runtime@^7.14.6", "@babel/runtime@^7.17.2", "@babel/runtime@^7.18.6", "@babel/runtime@^7.20.1": - version "7.20.1" - resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.20.1.tgz#1148bb33ab252b165a06698fde7576092a78b4a9" - integrity sha512-mrzLkl6U9YLF8qpqI7TB82PESyEGjm/0Ly91jG575eVxMMlb8fYfOXFZIJ8XfLrJZQbm7dlKry2bJmXBUEkdFg== +"@babel/runtime@^7.14.6", "@babel/runtime@^7.18.6", "@babel/runtime@^7.20.1", "@babel/runtime@^7.20.13", "@babel/runtime@^7.20.6": + version "7.21.0" + resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.21.0.tgz#5b55c9d394e5fcf304909a8b00c07dc217b56673" + integrity sha512-xwII0//EObnq89Ji5AKYQaRYiW/nZ3llSv29d49IuxPhKbtJoLP+9QUUZ4nVragQVtaVGeZrpB+ZtG/Pdy/POw== dependencies: - regenerator-runtime "^0.13.10" + regenerator-runtime "^0.13.11" "@babel/runtime@^7.17.8": version "7.18.0" @@ -1656,27 +1550,20 @@ dependencies: regenerator-runtime "^0.13.4" -"@babel/runtime@^7.17.9", "@babel/runtime@^7.18.3": +"@babel/runtime@^7.18.3": version "7.18.3" resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.18.3.tgz#c7b654b57f6f63cf7f8b418ac9ca04408c4579f4" integrity sha512-38Y8f7YUhce/K7RMwTp7m0uCumpv9hZkitCbBClqQIow1qSbCvGkcegKOXpEWCQLfWmevgRiWokZ1GkpfhbZug== dependencies: regenerator-runtime "^0.13.4" -"@babel/runtime@^7.18.9", "@babel/runtime@^7.19.0": +"@babel/runtime@^7.18.9": version "7.19.0" resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.19.0.tgz#22b11c037b094d27a8a2504ea4dcff00f50e2259" integrity sha512-eR8Lo9hnDS7tqkO7NsV+mKvCmv5boaXFSZ70DnfhcgiEne8hv9oCEd36Klw74EtizEqLsy4YnW8UWwpBVolHZA== dependencies: regenerator-runtime "^0.13.4" -"@babel/runtime@^7.20.6": - version "7.20.6" - resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.20.6.tgz#facf4879bfed9b5326326273a64220f099b0fce3" - integrity sha512-Q+8MqP7TiHMWzSfwiJwXCjyf4GYA4Dgw3emg/7xmwsdLJOZUp+nMqcOwOzzYheuM1rhDu8FSj2l0aoMygEuXuA== - dependencies: - regenerator-runtime "^0.13.11" - "@babel/template@^7.10.4", "@babel/template@^7.12.7", "@babel/template@^7.16.7", "@babel/template@^7.3.3": version "7.16.7" resolved "https://registry.yarnpkg.com/@babel/template/-/template-7.16.7.tgz#8d126c8701fde4d66b264b3eba3d96f07666d155" @@ -1801,38 +1688,6 @@ dependencies: "@open-web3/orml-type-definitions" "^0.9.4-38" -"@certusone/wormhole-sdk-proto-web@^0.0.1": - version "0.0.1" - resolved "https://registry.yarnpkg.com/@certusone/wormhole-sdk-proto-web/-/wormhole-sdk-proto-web-0.0.1.tgz#257bf20e2871e0345efc8f4bbf4531e7e0acef06" - integrity sha512-v6D+vCPqzTmrRuN0ZHpOdA1XnF3nmaD1wlJf025SXb7JFhVSmKyFXzLajkt50rk6SCkEvXtRlxNTJtnuCxg94Q== - dependencies: - "@improbable-eng/grpc-web" "^0.15.0" - protobufjs "^7.0.0" - rxjs "^7.5.6" - -"@certusone/wormhole-sdk-wasm@^0.0.1": - version "0.0.1" - resolved "https://registry.yarnpkg.com/@certusone/wormhole-sdk-wasm/-/wormhole-sdk-wasm-0.0.1.tgz#05f0c05acba3df5bd025eaa9c7a3d55e017a771f" - integrity sha512-LdIwLhOyr4pPs2jqYubqC7d4UkqYBX0EG/ppspQlW3qlVE0LZRMrH6oVzzLMyHtV0Rw7O9sIKzORW/T3mrJv2w== - dependencies: - "@types/long" "^4.0.2" - "@types/node" "^18.0.3" - -"@certusone/wormhole-sdk@^0.5.0": - version "0.5.0" - resolved "https://registry.yarnpkg.com/@certusone/wormhole-sdk/-/wormhole-sdk-0.5.0.tgz#fa7416039477af9c925ce6d921b03f516af71bf1" - integrity sha512-Z8Cj2yZ41if842jSSLzKLomwkq9PgXdjVq3r3VNzkSM3aZavU8vZqNT33LU9IabmW7hiWe1uI9j2Z1JZe7SIEg== - dependencies: - "@certusone/wormhole-sdk-proto-web" "^0.0.1" - "@certusone/wormhole-sdk-wasm" "^0.0.1" - "@solana/spl-token" "^0.1.8" - "@solana/web3.js" "^1.24.0" - "@terra-money/terra.js" "^3.1.3" - algosdk "^1.15.0" - axios "^0.24.0" - bech32 "^2.0.0" - js-base64 "^3.6.1" - "@cnakazawa/watch@^1.0.3": version "1.0.4" resolved "https://registry.yarnpkg.com/@cnakazawa/watch/-/watch-1.0.4.tgz#f864ae85004d0fcab6f50be9141c4da368d1656a" @@ -2201,22 +2056,7 @@ minimatch "^3.0.4" strip-json-comments "^3.1.1" -"@ethersproject/abi@5.5.0": - version "5.5.0" - resolved "https://registry.yarnpkg.com/@ethersproject/abi/-/abi-5.5.0.tgz#fb52820e22e50b854ff15ce1647cc508d6660613" - integrity sha512-loW7I4AohP5KycATvc0MgujU6JyCHPqHdeoo9z3Nr9xEiNioxa65ccdm1+fsoJhkuhdRtfcL8cfyGamz2AxZ5w== - dependencies: - "@ethersproject/address" "^5.5.0" - "@ethersproject/bignumber" "^5.5.0" - "@ethersproject/bytes" "^5.5.0" - "@ethersproject/constants" "^5.5.0" - "@ethersproject/hash" "^5.5.0" - "@ethersproject/keccak256" "^5.5.0" - "@ethersproject/logger" "^5.5.0" - "@ethersproject/properties" "^5.5.0" - "@ethersproject/strings" "^5.5.0" - -"@ethersproject/abi@5.7.0", "@ethersproject/abi@^5.5.0", "@ethersproject/abi@^5.7.0": +"@ethersproject/abi@5.7.0", "@ethersproject/abi@^5.7.0": version "5.7.0" resolved "https://registry.yarnpkg.com/@ethersproject/abi/-/abi-5.7.0.tgz#b3f3e045bbbeed1af3947335c247ad625a44e449" integrity sha512-351ktp42TiRcYB3H1OP8yajPeAQstMW/yCFokj/AthP9bLHzQFPlOrxOcwYEDkUAICmOHljvN4K39OMTMUa9RA== @@ -2231,20 +2071,7 @@ "@ethersproject/properties" "^5.7.0" "@ethersproject/strings" "^5.7.0" -"@ethersproject/abstract-provider@5.5.1", "@ethersproject/abstract-provider@~5.5.0": - version "5.5.1" - resolved "https://registry.yarnpkg.com/@ethersproject/abstract-provider/-/abstract-provider-5.5.1.tgz#2f1f6e8a3ab7d378d8ad0b5718460f85649710c5" - integrity sha512-m+MA/ful6eKbxpr99xUYeRvLkfnlqzrF8SZ46d/xFB1A7ZVknYc/sXJG0RcufF52Qn2jeFj1hhcoQ7IXjNKUqg== - dependencies: - "@ethersproject/bignumber" "^5.5.0" - "@ethersproject/bytes" "^5.5.0" - "@ethersproject/logger" "^5.5.0" - "@ethersproject/networks" "^5.5.0" - "@ethersproject/properties" "^5.5.0" - "@ethersproject/transactions" "^5.5.0" - "@ethersproject/web" "^5.5.0" - -"@ethersproject/abstract-provider@5.7.0", "@ethersproject/abstract-provider@^5.5.0", "@ethersproject/abstract-provider@^5.7.0": +"@ethersproject/abstract-provider@5.7.0", "@ethersproject/abstract-provider@^5.7.0", "@ethersproject/abstract-provider@~5.7.0": version "5.7.0" resolved "https://registry.yarnpkg.com/@ethersproject/abstract-provider/-/abstract-provider-5.7.0.tgz#b0a8550f88b6bf9d51f90e4795d48294630cb9ef" integrity sha512-R41c9UkchKCpAqStMYUpdunjo3pkEvZC3FAwZn5S5MGbXoMQOHIdHItezTETxAO5bevtMApSyEhn9+CHcDsWBw== @@ -2257,18 +2084,7 @@ "@ethersproject/transactions" "^5.7.0" "@ethersproject/web" "^5.7.0" -"@ethersproject/abstract-signer@5.5.0", "@ethersproject/abstract-signer@~5.5.0": - version "5.5.0" - resolved "https://registry.yarnpkg.com/@ethersproject/abstract-signer/-/abstract-signer-5.5.0.tgz#590ff6693370c60ae376bf1c7ada59eb2a8dd08d" - integrity sha512-lj//7r250MXVLKI7sVarXAbZXbv9P50lgmJQGr2/is82EwEb8r7HrxsmMqAjTsztMYy7ohrIhGMIml+Gx4D3mA== - dependencies: - "@ethersproject/abstract-provider" "^5.5.0" - "@ethersproject/bignumber" "^5.5.0" - "@ethersproject/bytes" "^5.5.0" - "@ethersproject/logger" "^5.5.0" - "@ethersproject/properties" "^5.5.0" - -"@ethersproject/abstract-signer@5.7.0", "@ethersproject/abstract-signer@^5.5.0", "@ethersproject/abstract-signer@^5.7.0": +"@ethersproject/abstract-signer@5.7.0", "@ethersproject/abstract-signer@^5.7.0": version "5.7.0" resolved "https://registry.yarnpkg.com/@ethersproject/abstract-signer/-/abstract-signer-5.7.0.tgz#13f4f32117868452191a4649723cb086d2b596b2" integrity sha512-a16V8bq1/Cz+TGCkE2OPMTOUDLS3grCpdjoJCYNnVBbdYEMSgKrU0+B90s8b6H+ByYTBZN7a3g76jdIJi7UfKQ== @@ -2279,18 +2095,7 @@ "@ethersproject/logger" "^5.7.0" "@ethersproject/properties" "^5.7.0" -"@ethersproject/address@5.5.0", "@ethersproject/address@~5.5.0": - version "5.5.0" - resolved "https://registry.yarnpkg.com/@ethersproject/address/-/address-5.5.0.tgz#bcc6f576a553f21f3dd7ba17248f81b473c9c78f" - integrity sha512-l4Nj0eWlTUh6ro5IbPTgbpT4wRbdH5l8CQf7icF7sb/SI3Nhd9Y9HzhonTSTi6CefI0necIw7LJqQPopPLZyWw== - dependencies: - "@ethersproject/bignumber" "^5.5.0" - "@ethersproject/bytes" "^5.5.0" - "@ethersproject/keccak256" "^5.5.0" - "@ethersproject/logger" "^5.5.0" - "@ethersproject/rlp" "^5.5.0" - -"@ethersproject/address@5.7.0", "@ethersproject/address@^5.5.0", "@ethersproject/address@^5.7.0": +"@ethersproject/address@5.7.0", "@ethersproject/address@^5.7.0", "@ethersproject/address@~5.7.0": version "5.7.0" resolved "https://registry.yarnpkg.com/@ethersproject/address/-/address-5.7.0.tgz#19b56c4d74a3b0a46bfdbb6cfcc0a153fc697f37" integrity sha512-9wYhYt7aghVGo758POM5nqcOMaE168Q6aRLJZwUmiqSrAungkG74gSSeKEIR7ukixesdRZGPgVqme6vmxs1fkA== @@ -2301,29 +2106,14 @@ "@ethersproject/logger" "^5.7.0" "@ethersproject/rlp" "^5.7.0" -"@ethersproject/base64@5.5.0": - version "5.5.0" - resolved "https://registry.yarnpkg.com/@ethersproject/base64/-/base64-5.5.0.tgz#881e8544e47ed976930836986e5eb8fab259c090" - integrity sha512-tdayUKhU1ljrlHzEWbStXazDpsx4eg1dBXUSI6+mHlYklOXoXF6lZvw8tnD6oVaWfnMxAgRSKROg3cVKtCcppA== - dependencies: - "@ethersproject/bytes" "^5.5.0" - -"@ethersproject/base64@5.7.0", "@ethersproject/base64@^5.5.0", "@ethersproject/base64@^5.7.0": +"@ethersproject/base64@5.7.0", "@ethersproject/base64@^5.7.0": version "5.7.0" resolved "https://registry.yarnpkg.com/@ethersproject/base64/-/base64-5.7.0.tgz#ac4ee92aa36c1628173e221d0d01f53692059e1c" integrity sha512-Dr8tcHt2mEbsZr/mwTPIQAf3Ai0Bks/7gTw9dSqk1mQvhW3XvRlmDJr/4n+wg1JmCl16NZue17CDh8xb/vZ0sQ== dependencies: "@ethersproject/bytes" "^5.7.0" -"@ethersproject/basex@5.5.0": - version "5.5.0" - resolved "https://registry.yarnpkg.com/@ethersproject/basex/-/basex-5.5.0.tgz#e40a53ae6d6b09ab4d977bd037010d4bed21b4d3" - integrity sha512-ZIodwhHpVJ0Y3hUCfUucmxKsWQA5TMnavp5j/UOuDdzZWzJlRmuOjcTMIGgHCYuZmHt36BfiSyQPSRskPxbfaQ== - dependencies: - "@ethersproject/bytes" "^5.5.0" - "@ethersproject/properties" "^5.5.0" - -"@ethersproject/basex@5.7.0", "@ethersproject/basex@^5.5.0", "@ethersproject/basex@^5.7.0": +"@ethersproject/basex@5.7.0", "@ethersproject/basex@^5.7.0": version "5.7.0" resolved "https://registry.yarnpkg.com/@ethersproject/basex/-/basex-5.7.0.tgz#97034dc7e8938a8ca943ab20f8a5e492ece4020b" integrity sha512-ywlh43GwZLv2Voc2gQVTKBoVQ1mti3d8HK5aMxsfu/nRDnMmNqaSJ3r3n85HBByT8OpoY96SXM1FogC533T4zw== @@ -2331,16 +2121,7 @@ "@ethersproject/bytes" "^5.7.0" "@ethersproject/properties" "^5.7.0" -"@ethersproject/bignumber@5.5.0", "@ethersproject/bignumber@~5.5.0": - version "5.5.0" - resolved "https://registry.yarnpkg.com/@ethersproject/bignumber/-/bignumber-5.5.0.tgz#875b143f04a216f4f8b96245bde942d42d279527" - integrity sha512-6Xytlwvy6Rn3U3gKEc1vP7nR92frHkv6wtVr95LFR3jREXiCPzdWxKQ1cx4JGQBXxcguAwjA8murlYN2TSiEbg== - dependencies: - "@ethersproject/bytes" "^5.5.0" - "@ethersproject/logger" "^5.5.0" - bn.js "^4.11.9" - -"@ethersproject/bignumber@5.7.0", "@ethersproject/bignumber@^5.5.0", "@ethersproject/bignumber@^5.7.0": +"@ethersproject/bignumber@5.7.0", "@ethersproject/bignumber@^5.7.0", "@ethersproject/bignumber@~5.7.0": version "5.7.0" resolved "https://registry.yarnpkg.com/@ethersproject/bignumber/-/bignumber-5.7.0.tgz#e2f03837f268ba655ffba03a57853e18a18dc9c2" integrity sha512-n1CAdIHRWjSucQO3MC1zPSVgV/6dy/fjL9pMrPP9peL+QxEg9wOsVqwD4+818B6LUEtaXzVHQiuivzRoxPxUGw== @@ -2349,51 +2130,21 @@ "@ethersproject/logger" "^5.7.0" bn.js "^5.2.1" -"@ethersproject/bytes@5.5.0", "@ethersproject/bytes@~5.5.0": - version "5.5.0" - resolved "https://registry.yarnpkg.com/@ethersproject/bytes/-/bytes-5.5.0.tgz#cb11c526de657e7b45d2e0f0246fb3b9d29a601c" - integrity sha512-ABvc7BHWhZU9PNM/tANm/Qx4ostPGadAuQzWTr3doklZOhDlmcBqclrQe/ZXUIj3K8wC28oYeuRa+A37tX9kog== - dependencies: - "@ethersproject/logger" "^5.5.0" - -"@ethersproject/bytes@5.7.0", "@ethersproject/bytes@^5.5.0", "@ethersproject/bytes@^5.7.0": +"@ethersproject/bytes@5.7.0", "@ethersproject/bytes@^5.7.0", "@ethersproject/bytes@~5.7.0": version "5.7.0" resolved "https://registry.yarnpkg.com/@ethersproject/bytes/-/bytes-5.7.0.tgz#a00f6ea8d7e7534d6d87f47188af1148d71f155d" integrity sha512-nsbxwgFXWh9NyYWo+U8atvmMsSdKJprTcICAkvbBffT75qDocbuggBU0SJiVK2MuTrp0q+xvLkTnGMPK1+uA9A== dependencies: "@ethersproject/logger" "^5.7.0" -"@ethersproject/constants@5.5.0", "@ethersproject/constants@~5.5.0": - version "5.5.0" - resolved "https://registry.yarnpkg.com/@ethersproject/constants/-/constants-5.5.0.tgz#d2a2cd7d94bd1d58377d1d66c4f53c9be4d0a45e" - integrity sha512-2MsRRVChkvMWR+GyMGY4N1sAX9Mt3J9KykCsgUFd/1mwS0UH1qw+Bv9k1UJb3X3YJYFco9H20pjSlOIfCG5HYQ== - dependencies: - "@ethersproject/bignumber" "^5.5.0" - -"@ethersproject/constants@5.7.0", "@ethersproject/constants@^5.5.0", "@ethersproject/constants@^5.7.0": +"@ethersproject/constants@5.7.0", "@ethersproject/constants@^5.7.0", "@ethersproject/constants@~5.7.0": version "5.7.0" resolved "https://registry.yarnpkg.com/@ethersproject/constants/-/constants-5.7.0.tgz#df80a9705a7e08984161f09014ea012d1c75295e" integrity sha512-DHI+y5dBNvkpYUMiRQyxRBYBefZkJfo70VUkUAsRjcPs47muV9evftfZ0PJVCXYbAiCgght0DtcF9srFQmIgWA== dependencies: "@ethersproject/bignumber" "^5.7.0" -"@ethersproject/contracts@5.5.0", "@ethersproject/contracts@~5.5.0": - version "5.5.0" - resolved "https://registry.yarnpkg.com/@ethersproject/contracts/-/contracts-5.5.0.tgz#b735260d4bd61283a670a82d5275e2a38892c197" - integrity sha512-2viY7NzyvJkh+Ug17v7g3/IJC8HqZBDcOjYARZLdzRxrfGlRgmYgl6xPRKVbEzy1dWKw/iv7chDcS83pg6cLxg== - dependencies: - "@ethersproject/abi" "^5.5.0" - "@ethersproject/abstract-provider" "^5.5.0" - "@ethersproject/abstract-signer" "^5.5.0" - "@ethersproject/address" "^5.5.0" - "@ethersproject/bignumber" "^5.5.0" - "@ethersproject/bytes" "^5.5.0" - "@ethersproject/constants" "^5.5.0" - "@ethersproject/logger" "^5.5.0" - "@ethersproject/properties" "^5.5.0" - "@ethersproject/transactions" "^5.5.0" - -"@ethersproject/contracts@5.7.0": +"@ethersproject/contracts@5.7.0", "@ethersproject/contracts@~5.7.0": version "5.7.0" resolved "https://registry.yarnpkg.com/@ethersproject/contracts/-/contracts-5.7.0.tgz#c305e775abd07e48aa590e1a877ed5c316f8bd1e" integrity sha512-5GJbzEU3X+d33CdfPhcyS+z8MzsTrBGk/sc+G+59+tPa9yFkl6HQ9D6L0QMgNTA9q8dT0XKxxkyp883XsQvbbg== @@ -2409,21 +2160,7 @@ "@ethersproject/properties" "^5.7.0" "@ethersproject/transactions" "^5.7.0" -"@ethersproject/hash@5.5.0", "@ethersproject/hash@~5.5.0": - version "5.5.0" - resolved "https://registry.yarnpkg.com/@ethersproject/hash/-/hash-5.5.0.tgz#7cee76d08f88d1873574c849e0207dcb32380cc9" - integrity sha512-dnGVpK1WtBjmnp3mUT0PlU2MpapnwWI0PibldQEq1408tQBAbZpPidkWoVVuNMOl/lISO3+4hXZWCL3YV7qzfg== - dependencies: - "@ethersproject/abstract-signer" "^5.5.0" - "@ethersproject/address" "^5.5.0" - "@ethersproject/bignumber" "^5.5.0" - "@ethersproject/bytes" "^5.5.0" - "@ethersproject/keccak256" "^5.5.0" - "@ethersproject/logger" "^5.5.0" - "@ethersproject/properties" "^5.5.0" - "@ethersproject/strings" "^5.5.0" - -"@ethersproject/hash@5.7.0", "@ethersproject/hash@^5.5.0", "@ethersproject/hash@^5.7.0": +"@ethersproject/hash@5.7.0", "@ethersproject/hash@^5.7.0", "@ethersproject/hash@~5.7.0": version "5.7.0" resolved "https://registry.yarnpkg.com/@ethersproject/hash/-/hash-5.7.0.tgz#eb7aca84a588508369562e16e514b539ba5240a7" integrity sha512-qX5WrQfnah1EFnO5zJv1v46a8HW0+E5xuBBDTwMFZLuVTx0tbU2kkx15NqdjxecrLGatQN9FGQKpb1FKdHCt+g== @@ -2438,25 +2175,7 @@ "@ethersproject/properties" "^5.7.0" "@ethersproject/strings" "^5.7.0" -"@ethersproject/hdnode@5.5.0": - version "5.5.0" - resolved "https://registry.yarnpkg.com/@ethersproject/hdnode/-/hdnode-5.5.0.tgz#4a04e28f41c546f7c978528ea1575206a200ddf6" - integrity sha512-mcSOo9zeUg1L0CoJH7zmxwUG5ggQHU1UrRf8jyTYy6HxdZV+r0PBoL1bxr+JHIPXRzS6u/UW4mEn43y0tmyF8Q== - dependencies: - "@ethersproject/abstract-signer" "^5.5.0" - "@ethersproject/basex" "^5.5.0" - "@ethersproject/bignumber" "^5.5.0" - "@ethersproject/bytes" "^5.5.0" - "@ethersproject/logger" "^5.5.0" - "@ethersproject/pbkdf2" "^5.5.0" - "@ethersproject/properties" "^5.5.0" - "@ethersproject/sha2" "^5.5.0" - "@ethersproject/signing-key" "^5.5.0" - "@ethersproject/strings" "^5.5.0" - "@ethersproject/transactions" "^5.5.0" - "@ethersproject/wordlists" "^5.5.0" - -"@ethersproject/hdnode@5.7.0", "@ethersproject/hdnode@^5.5.0", "@ethersproject/hdnode@^5.7.0": +"@ethersproject/hdnode@5.7.0", "@ethersproject/hdnode@^5.7.0": version "5.7.0" resolved "https://registry.yarnpkg.com/@ethersproject/hdnode/-/hdnode-5.7.0.tgz#e627ddc6b466bc77aebf1a6b9e47405ca5aef9cf" integrity sha512-OmyYo9EENBPPf4ERhR7oj6uAtUAhYGqOnIS+jE5pTXvdKBS99ikzq1E7Iv0ZQZ5V36Lqx1qZLeak0Ra16qpeOg== @@ -2474,26 +2193,7 @@ "@ethersproject/transactions" "^5.7.0" "@ethersproject/wordlists" "^5.7.0" -"@ethersproject/json-wallets@5.5.0": - version "5.5.0" - resolved "https://registry.yarnpkg.com/@ethersproject/json-wallets/-/json-wallets-5.5.0.tgz#dd522d4297e15bccc8e1427d247ec8376b60e325" - integrity sha512-9lA21XQnCdcS72xlBn1jfQdj2A1VUxZzOzi9UkNdnokNKke/9Ya2xA9aIK1SC3PQyBDLt4C+dfps7ULpkvKikQ== - dependencies: - "@ethersproject/abstract-signer" "^5.5.0" - "@ethersproject/address" "^5.5.0" - "@ethersproject/bytes" "^5.5.0" - "@ethersproject/hdnode" "^5.5.0" - "@ethersproject/keccak256" "^5.5.0" - "@ethersproject/logger" "^5.5.0" - "@ethersproject/pbkdf2" "^5.5.0" - "@ethersproject/properties" "^5.5.0" - "@ethersproject/random" "^5.5.0" - "@ethersproject/strings" "^5.5.0" - "@ethersproject/transactions" "^5.5.0" - aes-js "3.0.0" - scrypt-js "3.0.1" - -"@ethersproject/json-wallets@5.7.0", "@ethersproject/json-wallets@^5.5.0", "@ethersproject/json-wallets@^5.7.0": +"@ethersproject/json-wallets@5.7.0", "@ethersproject/json-wallets@^5.7.0": version "5.7.0" resolved "https://registry.yarnpkg.com/@ethersproject/json-wallets/-/json-wallets-5.7.0.tgz#5e3355287b548c32b368d91014919ebebddd5360" integrity sha512-8oee5Xgu6+RKgJTkvEMl2wDgSPSAQ9MB/3JYjFV9jlKvcYHUXZC+cQp0njgmxdHkYWn8s6/IqIZYm0YWCjO/0g== @@ -2512,15 +2212,7 @@ aes-js "3.0.0" scrypt-js "3.0.1" -"@ethersproject/keccak256@5.5.0", "@ethersproject/keccak256@~5.5.0": - version "5.5.0" - resolved "https://registry.yarnpkg.com/@ethersproject/keccak256/-/keccak256-5.5.0.tgz#e4b1f9d7701da87c564ffe336f86dcee82983492" - integrity sha512-5VoFCTjo2rYbBe1l2f4mccaRFN/4VQEYFwwn04aJV2h7qf4ZvI2wFxUE1XOX+snbwCLRzIeikOqtAoPwMza9kg== - dependencies: - "@ethersproject/bytes" "^5.5.0" - js-sha3 "0.8.0" - -"@ethersproject/keccak256@5.7.0", "@ethersproject/keccak256@^5.5.0", "@ethersproject/keccak256@^5.7.0": +"@ethersproject/keccak256@5.7.0", "@ethersproject/keccak256@^5.7.0", "@ethersproject/keccak256@~5.7.0": version "5.7.0" resolved "https://registry.yarnpkg.com/@ethersproject/keccak256/-/keccak256-5.7.0.tgz#3186350c6e1cd6aba7940384ec7d6d9db01f335a" integrity sha512-2UcPboeL/iW+pSg6vZ6ydF8tCnv3Iu/8tUmLLzWWGzxWKFFqOBQFLo6uLUv6BDrLgCDfN28RJ/wtByx+jZ4KBg== @@ -2528,39 +2220,19 @@ "@ethersproject/bytes" "^5.7.0" js-sha3 "0.8.0" -"@ethersproject/logger@5.5.0", "@ethersproject/logger@~5.5.0": - version "5.5.0" - resolved "https://registry.yarnpkg.com/@ethersproject/logger/-/logger-5.5.0.tgz#0c2caebeff98e10aefa5aef27d7441c7fd18cf5d" - integrity sha512-rIY/6WPm7T8n3qS2vuHTUBPdXHl+rGxWxW5okDfo9J4Z0+gRRZT0msvUdIJkE4/HS29GUMziwGaaKO2bWONBrg== - -"@ethersproject/logger@5.7.0", "@ethersproject/logger@^5.5.0", "@ethersproject/logger@^5.7.0": +"@ethersproject/logger@5.7.0", "@ethersproject/logger@^5.7.0", "@ethersproject/logger@~5.7.0": version "5.7.0" resolved "https://registry.yarnpkg.com/@ethersproject/logger/-/logger-5.7.0.tgz#6ce9ae168e74fecf287be17062b590852c311892" integrity sha512-0odtFdXu/XHtjQXJYA3u9G0G8btm0ND5Cu8M7i5vhEcE8/HmF4Lbdqanwyv4uQTr2tx6b7fQRmgLrsnpQlmnig== -"@ethersproject/networks@5.5.2", "@ethersproject/networks@~5.5.0": - version "5.5.2" - resolved "https://registry.yarnpkg.com/@ethersproject/networks/-/networks-5.5.2.tgz#784c8b1283cd2a931114ab428dae1bd00c07630b" - integrity sha512-NEqPxbGBfy6O3x4ZTISb90SjEDkWYDUbEeIFhJly0F7sZjoQMnj5KYzMSkMkLKZ+1fGpx00EDpHQCy6PrDupkQ== - dependencies: - "@ethersproject/logger" "^5.5.0" - -"@ethersproject/networks@5.7.1", "@ethersproject/networks@^5.5.0", "@ethersproject/networks@^5.7.0": +"@ethersproject/networks@5.7.1", "@ethersproject/networks@^5.7.0", "@ethersproject/networks@~5.7.0": version "5.7.1" resolved "https://registry.yarnpkg.com/@ethersproject/networks/-/networks-5.7.1.tgz#118e1a981d757d45ccea6bb58d9fd3d9db14ead6" integrity sha512-n/MufjFYv3yFcUyfhnXotyDlNdFb7onmkSy8aQERi2PjNcnWQ66xXxa3XlS8nCcA8aJKJjIIMNJTC7tu80GwpQ== dependencies: "@ethersproject/logger" "^5.7.0" -"@ethersproject/pbkdf2@5.5.0": - version "5.5.0" - resolved "https://registry.yarnpkg.com/@ethersproject/pbkdf2/-/pbkdf2-5.5.0.tgz#e25032cdf02f31505d47afbf9c3e000d95c4a050" - integrity sha512-SaDvQFvXPnz1QGpzr6/HToLifftSXGoXrbpZ6BvoZhmx4bNLHrxDe8MZisuecyOziP1aVEwzC2Hasj+86TgWVg== - dependencies: - "@ethersproject/bytes" "^5.5.0" - "@ethersproject/sha2" "^5.5.0" - -"@ethersproject/pbkdf2@5.7.0", "@ethersproject/pbkdf2@^5.5.0", "@ethersproject/pbkdf2@^5.7.0": +"@ethersproject/pbkdf2@5.7.0", "@ethersproject/pbkdf2@^5.7.0": version "5.7.0" resolved "https://registry.yarnpkg.com/@ethersproject/pbkdf2/-/pbkdf2-5.7.0.tgz#d2267d0a1f6e123f3771007338c47cccd83d3102" integrity sha512-oR/dBRZR6GTyaofd86DehG72hY6NpAjhabkhxgr3X2FpJtJuodEl2auADWBZfhDHgVCbu3/H/Ocq2uC6dpNjjw== @@ -2568,46 +2240,14 @@ "@ethersproject/bytes" "^5.7.0" "@ethersproject/sha2" "^5.7.0" -"@ethersproject/properties@5.5.0", "@ethersproject/properties@~5.5.0": - version "5.5.0" - resolved "https://registry.yarnpkg.com/@ethersproject/properties/-/properties-5.5.0.tgz#61f00f2bb83376d2071baab02245f92070c59995" - integrity sha512-l3zRQg3JkD8EL3CPjNK5g7kMx4qSwiR60/uk5IVjd3oq1MZR5qUg40CNOoEJoX5wc3DyY5bt9EbMk86C7x0DNA== - dependencies: - "@ethersproject/logger" "^5.5.0" - -"@ethersproject/properties@5.7.0", "@ethersproject/properties@^5.5.0", "@ethersproject/properties@^5.7.0": +"@ethersproject/properties@5.7.0", "@ethersproject/properties@^5.7.0", "@ethersproject/properties@~5.7.0": version "5.7.0" resolved "https://registry.yarnpkg.com/@ethersproject/properties/-/properties-5.7.0.tgz#a6e12cb0439b878aaf470f1902a176033067ed30" integrity sha512-J87jy8suntrAkIZtecpxEPxY//szqr1mlBaYlQ0r4RCaiD2hjheqF9s1LVE8vVuJCXisjIP+JgtK/Do54ej4Sw== dependencies: "@ethersproject/logger" "^5.7.0" -"@ethersproject/providers@5.5.3", "@ethersproject/providers@~5.5.0": - version "5.5.3" - resolved "https://registry.yarnpkg.com/@ethersproject/providers/-/providers-5.5.3.tgz#56c2b070542ac44eb5de2ed3cf6784acd60a3130" - integrity sha512-ZHXxXXXWHuwCQKrgdpIkbzMNJMvs+9YWemanwp1fA7XZEv7QlilseysPvQe0D7Q7DlkJX/w/bGA1MdgK2TbGvA== - dependencies: - "@ethersproject/abstract-provider" "^5.5.0" - "@ethersproject/abstract-signer" "^5.5.0" - "@ethersproject/address" "^5.5.0" - "@ethersproject/basex" "^5.5.0" - "@ethersproject/bignumber" "^5.5.0" - "@ethersproject/bytes" "^5.5.0" - "@ethersproject/constants" "^5.5.0" - "@ethersproject/hash" "^5.5.0" - "@ethersproject/logger" "^5.5.0" - "@ethersproject/networks" "^5.5.0" - "@ethersproject/properties" "^5.5.0" - "@ethersproject/random" "^5.5.0" - "@ethersproject/rlp" "^5.5.0" - "@ethersproject/sha2" "^5.5.0" - "@ethersproject/strings" "^5.5.0" - "@ethersproject/transactions" "^5.5.0" - "@ethersproject/web" "^5.5.0" - bech32 "1.1.4" - ws "7.4.6" - -"@ethersproject/providers@5.7.2": +"@ethersproject/providers@5.7.2", "@ethersproject/providers@~5.7.0": version "5.7.2" resolved "https://registry.yarnpkg.com/@ethersproject/providers/-/providers-5.7.2.tgz#f8b1a4f275d7ce58cf0a2eec222269a08beb18cb" integrity sha512-g34EWZ1WWAVgr4aptGlVBF8mhl3VWjv+8hoAnzStu8Ah22VHBsuGzP17eb6xDVRzw895G4W7vvx60lFFur/1Rg== @@ -2633,15 +2273,7 @@ bech32 "1.1.4" ws "7.4.6" -"@ethersproject/random@5.5.1": - version "5.5.1" - resolved "https://registry.yarnpkg.com/@ethersproject/random/-/random-5.5.1.tgz#7cdf38ea93dc0b1ed1d8e480ccdaf3535c555415" - integrity sha512-YaU2dQ7DuhL5Au7KbcQLHxcRHfgyNgvFV4sQOo0HrtW3Zkrc9ctWNz8wXQ4uCSfSDsqX2vcjhroxU5RQRV0nqA== - dependencies: - "@ethersproject/bytes" "^5.5.0" - "@ethersproject/logger" "^5.5.0" - -"@ethersproject/random@5.7.0", "@ethersproject/random@^5.5.0", "@ethersproject/random@^5.7.0": +"@ethersproject/random@5.7.0", "@ethersproject/random@^5.7.0": version "5.7.0" resolved "https://registry.yarnpkg.com/@ethersproject/random/-/random-5.7.0.tgz#af19dcbc2484aae078bb03656ec05df66253280c" integrity sha512-19WjScqRA8IIeWclFme75VMXSBvi4e6InrUNuaR4s5pTF2qNhcGdCUwdxUVGtDDqC00sDLCO93jPQoDUH4HVmQ== @@ -2649,15 +2281,7 @@ "@ethersproject/bytes" "^5.7.0" "@ethersproject/logger" "^5.7.0" -"@ethersproject/rlp@5.5.0", "@ethersproject/rlp@~5.5.0": - version "5.5.0" - resolved "https://registry.yarnpkg.com/@ethersproject/rlp/-/rlp-5.5.0.tgz#530f4f608f9ca9d4f89c24ab95db58ab56ab99a0" - integrity sha512-hLv8XaQ8PTI9g2RHoQGf/WSxBfTB/NudRacbzdxmst5VHAqd1sMibWG7SENzT5Dj3yZ3kJYx+WiRYEcQTAkcYA== - dependencies: - "@ethersproject/bytes" "^5.5.0" - "@ethersproject/logger" "^5.5.0" - -"@ethersproject/rlp@5.7.0", "@ethersproject/rlp@^5.5.0", "@ethersproject/rlp@^5.7.0": +"@ethersproject/rlp@5.7.0", "@ethersproject/rlp@^5.7.0", "@ethersproject/rlp@~5.7.0": version "5.7.0" resolved "https://registry.yarnpkg.com/@ethersproject/rlp/-/rlp-5.7.0.tgz#de39e4d5918b9d74d46de93af80b7685a9c21304" integrity sha512-rBxzX2vK8mVF7b0Tol44t5Tb8gomOHkj5guL+HhzQ1yBh/ydjGnpw6at+X6Iw0Kp3OzzzkcKp8N9r0W4kYSs9w== @@ -2665,16 +2289,7 @@ "@ethersproject/bytes" "^5.7.0" "@ethersproject/logger" "^5.7.0" -"@ethersproject/sha2@5.5.0": - version "5.5.0" - resolved "https://registry.yarnpkg.com/@ethersproject/sha2/-/sha2-5.5.0.tgz#a40a054c61f98fd9eee99af2c3cc6ff57ec24db7" - integrity sha512-B5UBoglbCiHamRVPLA110J+2uqsifpZaTmid2/7W5rbtYVz6gus6/hSDieIU/6gaKIDcOj12WnOdiymEUHIAOA== - dependencies: - "@ethersproject/bytes" "^5.5.0" - "@ethersproject/logger" "^5.5.0" - hash.js "1.1.7" - -"@ethersproject/sha2@5.7.0", "@ethersproject/sha2@^5.5.0", "@ethersproject/sha2@^5.7.0": +"@ethersproject/sha2@5.7.0", "@ethersproject/sha2@^5.7.0": version "5.7.0" resolved "https://registry.yarnpkg.com/@ethersproject/sha2/-/sha2-5.7.0.tgz#9a5f7a7824ef784f7f7680984e593a800480c9fb" integrity sha512-gKlH42riwb3KYp0reLsFTokByAKoJdgFCwI+CCiX/k+Jm2mbNs6oOaCjYQSlI1+XBVejwH2KrmCbMAT/GnRDQw== @@ -2683,19 +2298,7 @@ "@ethersproject/logger" "^5.7.0" hash.js "1.1.7" -"@ethersproject/signing-key@5.5.0": - version "5.5.0" - resolved "https://registry.yarnpkg.com/@ethersproject/signing-key/-/signing-key-5.5.0.tgz#2aa37169ce7e01e3e80f2c14325f624c29cedbe0" - integrity sha512-5VmseH7qjtNmDdZBswavhotYbWB0bOwKIlOTSlX14rKn5c11QmJwGt4GHeo7NrL/Ycl7uo9AHvEqs5xZgFBTng== - dependencies: - "@ethersproject/bytes" "^5.5.0" - "@ethersproject/logger" "^5.5.0" - "@ethersproject/properties" "^5.5.0" - bn.js "^4.11.9" - elliptic "6.5.4" - hash.js "1.1.7" - -"@ethersproject/signing-key@5.7.0", "@ethersproject/signing-key@^5.5.0", "@ethersproject/signing-key@^5.7.0": +"@ethersproject/signing-key@5.7.0", "@ethersproject/signing-key@^5.7.0": version "5.7.0" resolved "https://registry.yarnpkg.com/@ethersproject/signing-key/-/signing-key-5.7.0.tgz#06b2df39411b00bc57c7c09b01d1e41cf1b16ab3" integrity sha512-MZdy2nL3wO0u7gkB4nA/pEf8lu1TlFswPNmy8AiYkfKTdO6eXBJyUdmHO/ehm/htHw9K/qF8ujnTyUAD+Ry54Q== @@ -2707,18 +2310,6 @@ elliptic "6.5.4" hash.js "1.1.7" -"@ethersproject/solidity@5.5.0": - version "5.5.0" - resolved "https://registry.yarnpkg.com/@ethersproject/solidity/-/solidity-5.5.0.tgz#2662eb3e5da471b85a20531e420054278362f93f" - integrity sha512-9NgZs9LhGMj6aCtHXhtmFQ4AN4sth5HuFXVvAQtzmm0jpSCNOTGtrHZJAeYTh7MBjRR8brylWZxBZR9zDStXbw== - dependencies: - "@ethersproject/bignumber" "^5.5.0" - "@ethersproject/bytes" "^5.5.0" - "@ethersproject/keccak256" "^5.5.0" - "@ethersproject/logger" "^5.5.0" - "@ethersproject/sha2" "^5.5.0" - "@ethersproject/strings" "^5.5.0" - "@ethersproject/solidity@5.7.0": version "5.7.0" resolved "https://registry.yarnpkg.com/@ethersproject/solidity/-/solidity-5.7.0.tgz#5e9c911d8a2acce2a5ebb48a5e2e0af20b631cb8" @@ -2731,16 +2322,7 @@ "@ethersproject/sha2" "^5.7.0" "@ethersproject/strings" "^5.7.0" -"@ethersproject/strings@5.5.0", "@ethersproject/strings@~5.5.0": - version "5.5.0" - resolved "https://registry.yarnpkg.com/@ethersproject/strings/-/strings-5.5.0.tgz#e6784d00ec6c57710755699003bc747e98c5d549" - integrity sha512-9fy3TtF5LrX/wTrBaT8FGE6TDJyVjOvXynXJz5MT5azq+E6D92zuKNx7i29sWW2FjVOaWjAsiZ1ZWznuduTIIQ== - dependencies: - "@ethersproject/bytes" "^5.5.0" - "@ethersproject/constants" "^5.5.0" - "@ethersproject/logger" "^5.5.0" - -"@ethersproject/strings@5.7.0", "@ethersproject/strings@^5.5.0", "@ethersproject/strings@^5.7.0": +"@ethersproject/strings@5.7.0", "@ethersproject/strings@^5.7.0": version "5.7.0" resolved "https://registry.yarnpkg.com/@ethersproject/strings/-/strings-5.7.0.tgz#54c9d2a7c57ae8f1205c88a9d3a56471e14d5ed2" integrity sha512-/9nu+lj0YswRNSH0NXYqrh8775XNyEdUQAuf3f+SmOrnVewcJ5SBNAjF7lpgehKi4abvNNXyf+HX86czCdJ8Mg== @@ -2749,22 +2331,7 @@ "@ethersproject/constants" "^5.7.0" "@ethersproject/logger" "^5.7.0" -"@ethersproject/transactions@5.5.0", "@ethersproject/transactions@~5.5.0": - version "5.5.0" - resolved "https://registry.yarnpkg.com/@ethersproject/transactions/-/transactions-5.5.0.tgz#7e9bf72e97bcdf69db34fe0d59e2f4203c7a2908" - integrity sha512-9RZYSKX26KfzEd/1eqvv8pLauCKzDTub0Ko4LfIgaERvRuwyaNV78mJs7cpIgZaDl6RJui4o49lHwwCM0526zA== - dependencies: - "@ethersproject/address" "^5.5.0" - "@ethersproject/bignumber" "^5.5.0" - "@ethersproject/bytes" "^5.5.0" - "@ethersproject/constants" "^5.5.0" - "@ethersproject/keccak256" "^5.5.0" - "@ethersproject/logger" "^5.5.0" - "@ethersproject/properties" "^5.5.0" - "@ethersproject/rlp" "^5.5.0" - "@ethersproject/signing-key" "^5.5.0" - -"@ethersproject/transactions@5.7.0", "@ethersproject/transactions@^5.5.0", "@ethersproject/transactions@^5.7.0": +"@ethersproject/transactions@5.7.0", "@ethersproject/transactions@^5.7.0", "@ethersproject/transactions@~5.7.0": version "5.7.0" resolved "https://registry.yarnpkg.com/@ethersproject/transactions/-/transactions-5.7.0.tgz#91318fc24063e057885a6af13fdb703e1f993d3b" integrity sha512-kmcNicCp1lp8qanMTC3RIikGgoJ80ztTyvtsFvCYpSCfkjhD0jZ2LOrnbcuxuToLIUYYf+4XwD1rP+B/erDIhQ== @@ -2779,15 +2346,6 @@ "@ethersproject/rlp" "^5.7.0" "@ethersproject/signing-key" "^5.7.0" -"@ethersproject/units@5.5.0": - version "5.5.0" - resolved "https://registry.yarnpkg.com/@ethersproject/units/-/units-5.5.0.tgz#104d02db5b5dc42cc672cc4587bafb87a95ee45e" - integrity sha512-7+DpjiZk4v6wrikj+TCyWWa9dXLNU73tSTa7n0TSJDxkYbV3Yf1eRh9ToMLlZtuctNYu9RDNNy2USq3AdqSbag== - dependencies: - "@ethersproject/bignumber" "^5.5.0" - "@ethersproject/constants" "^5.5.0" - "@ethersproject/logger" "^5.5.0" - "@ethersproject/units@5.7.0": version "5.7.0" resolved "https://registry.yarnpkg.com/@ethersproject/units/-/units-5.7.0.tgz#637b563d7e14f42deeee39245275d477aae1d8b1" @@ -2797,28 +2355,7 @@ "@ethersproject/constants" "^5.7.0" "@ethersproject/logger" "^5.7.0" -"@ethersproject/wallet@5.5.0", "@ethersproject/wallet@~5.5.0": - version "5.5.0" - resolved "https://registry.yarnpkg.com/@ethersproject/wallet/-/wallet-5.5.0.tgz#322a10527a440ece593980dca6182f17d54eae75" - integrity sha512-Mlu13hIctSYaZmUOo7r2PhNSd8eaMPVXe1wxrz4w4FCE4tDYBywDH+bAR1Xz2ADyXGwqYMwstzTrtUVIsKDO0Q== - dependencies: - "@ethersproject/abstract-provider" "^5.5.0" - "@ethersproject/abstract-signer" "^5.5.0" - "@ethersproject/address" "^5.5.0" - "@ethersproject/bignumber" "^5.5.0" - "@ethersproject/bytes" "^5.5.0" - "@ethersproject/hash" "^5.5.0" - "@ethersproject/hdnode" "^5.5.0" - "@ethersproject/json-wallets" "^5.5.0" - "@ethersproject/keccak256" "^5.5.0" - "@ethersproject/logger" "^5.5.0" - "@ethersproject/properties" "^5.5.0" - "@ethersproject/random" "^5.5.0" - "@ethersproject/signing-key" "^5.5.0" - "@ethersproject/transactions" "^5.5.0" - "@ethersproject/wordlists" "^5.5.0" - -"@ethersproject/wallet@5.7.0": +"@ethersproject/wallet@5.7.0", "@ethersproject/wallet@~5.7.0": version "5.7.0" resolved "https://registry.yarnpkg.com/@ethersproject/wallet/-/wallet-5.7.0.tgz#4e5d0790d96fe21d61d38fb40324e6c7ef350b2d" integrity sha512-MhmXlJXEJFBFVKrDLB4ZdDzxcBxQ3rLyCkhNqVu3CDYvR97E+8r01UgrI+TI99Le+aYm/in/0vp86guJuM7FCA== @@ -2839,18 +2376,7 @@ "@ethersproject/transactions" "^5.7.0" "@ethersproject/wordlists" "^5.7.0" -"@ethersproject/web@5.5.1": - version "5.5.1" - resolved "https://registry.yarnpkg.com/@ethersproject/web/-/web-5.5.1.tgz#cfcc4a074a6936c657878ac58917a61341681316" - integrity sha512-olvLvc1CB12sREc1ROPSHTdFCdvMh0J5GSJYiQg2D0hdD4QmJDy8QYDb1CvoqD/bF1c++aeKv2sR5uduuG9dQg== - dependencies: - "@ethersproject/base64" "^5.5.0" - "@ethersproject/bytes" "^5.5.0" - "@ethersproject/logger" "^5.5.0" - "@ethersproject/properties" "^5.5.0" - "@ethersproject/strings" "^5.5.0" - -"@ethersproject/web@5.7.1", "@ethersproject/web@^5.5.0", "@ethersproject/web@^5.7.0": +"@ethersproject/web@5.7.1", "@ethersproject/web@^5.7.0": version "5.7.1" resolved "https://registry.yarnpkg.com/@ethersproject/web/-/web-5.7.1.tgz#de1f285b373149bee5928f4eb7bcb87ee5fbb4ae" integrity sha512-Gueu8lSvyjBWL4cYsWsjh6MtMwM0+H4HvqFPZfB6dV8ctbP9zFAO73VG1cMWae0FLPCtz0peKPpZY8/ugJJX2w== @@ -2861,18 +2387,7 @@ "@ethersproject/properties" "^5.7.0" "@ethersproject/strings" "^5.7.0" -"@ethersproject/wordlists@5.5.0": - version "5.5.0" - resolved "https://registry.yarnpkg.com/@ethersproject/wordlists/-/wordlists-5.5.0.tgz#aac74963aa43e643638e5172353d931b347d584f" - integrity sha512-bL0UTReWDiaQJJYOC9sh/XcRu/9i2jMrzf8VLRmPKx58ckSlOJiohODkECCO50dtLZHcGU6MLXQ4OOrgBwP77Q== - dependencies: - "@ethersproject/bytes" "^5.5.0" - "@ethersproject/hash" "^5.5.0" - "@ethersproject/logger" "^5.5.0" - "@ethersproject/properties" "^5.5.0" - "@ethersproject/strings" "^5.5.0" - -"@ethersproject/wordlists@5.7.0", "@ethersproject/wordlists@^5.5.0", "@ethersproject/wordlists@^5.7.0": +"@ethersproject/wordlists@5.7.0", "@ethersproject/wordlists@^5.7.0": version "5.7.0" resolved "https://registry.yarnpkg.com/@ethersproject/wordlists/-/wordlists-5.7.0.tgz#8fb2c07185d68c3e09eb3bfd6e779ba2774627f5" integrity sha512-S2TFNJNfHWVHNE6cNDjbVlZ6MgE17MIxMbMg2zv3wn+3XSJGosL1m9ZVv3GXCf/2ymSsQ+hRI5IzoMJTG6aoVA== @@ -2966,25 +2481,6 @@ resolved "https://registry.yarnpkg.com/@gar/promisify/-/promisify-1.1.3.tgz#555193ab2e3bb3b6adc3d551c9c030d9e860daf6" integrity sha512-k2Ty1JcVojjJFwrg/ThKi2ujJ7XNLYaFGNB/bWT9wGR+oSMJHMa5w+CUq6p/pVrKeNNgA7pCqEcjSnHVoqJQFw== -"@grpc/grpc-js@^1.6.8": - version "1.7.3" - resolved "https://registry.yarnpkg.com/@grpc/grpc-js/-/grpc-js-1.7.3.tgz#f2ea79f65e31622d7f86d4b4c9ae38f13ccab99a" - integrity sha512-H9l79u4kJ2PVSxUNA08HMYAnUBLj9v6KjYQ7SQ71hOZcEXhShE/y5iQCesP8+6/Ik/7i2O0a10bPquIcYfufog== - dependencies: - "@grpc/proto-loader" "^0.7.0" - "@types/node" ">=12.12.47" - -"@grpc/proto-loader@^0.7.0": - version "0.7.3" - resolved "https://registry.yarnpkg.com/@grpc/proto-loader/-/proto-loader-0.7.3.tgz#75a6f95b51b85c5078ac7394da93850c32d36bb8" - integrity sha512-5dAvoZwna2Py3Ef96Ux9jIkp3iZ62TUsV00p3wVBPNX5K178UbNi8Q7gQVqwXT1Yq9RejIGG9G2IPEo93T6RcA== - dependencies: - "@types/long" "^4.0.1" - lodash.camelcase "^4.3.0" - long "^4.0.0" - protobufjs "^7.0.0" - yargs "^16.2.0" - "@hapi/address@2.x.x": version "2.1.4" resolved "https://registry.yarnpkg.com/@hapi/address/-/address-2.1.4.tgz#5d67ed43f3fd41a69d4b9ff7b56e7c0d1d0a81e5" @@ -3027,11 +2523,6 @@ resolved "https://registry.yarnpkg.com/@heroicons/react/-/react-2.0.12.tgz#7e5a16c82512f89a30266dd36f8b8465b3e3e216" integrity sha512-FZxKh3i9aKIDxyALTgIpSF2t6V6/eZfF5mRu41QlwkX3Oxzecdm1u6dpft6PQGxIBwO7TKYWaMAYYL8mp/EaOg== -"@hookform/resolvers@^2.9.7": - version "2.9.7" - resolved "https://registry.yarnpkg.com/@hookform/resolvers/-/resolvers-2.9.7.tgz#8b257ae67234ce0270e6b044c1a61fb98ec02b4b" - integrity sha512-BloehX3MOLwuFEwT4yZnmolPjVmqyn8VsSuodLfazbCIqxBHsQ4qUZsi+bvNNCduRli1AGWFrkDLGD5QoNzsoA== - "@humanwhocodes/config-array@^0.5.0": version "0.5.0" resolved "https://registry.yarnpkg.com/@humanwhocodes/config-array/-/config-array-0.5.0.tgz#1407967d4c6eecd7388f83acf1eaf4d0c6e58ef9" @@ -3051,37 +2542,18 @@ resolved "https://registry.yarnpkg.com/@iarna/toml/-/toml-2.2.5.tgz#b32366c89b43c6f8cefbdefac778b9c828e3ba8c" integrity sha512-trnsAYxU3xnS1gPHPyU961coFyLkh4gAD/0zQ5mymY4yOZ+CYvsPqUbOFSw0aDM4y0tV7tiFxL/1XfXPNC6IPg== -"@improbable-eng/grpc-web-node-http-transport@^0.15.0": - version "0.15.0" - resolved "https://registry.yarnpkg.com/@improbable-eng/grpc-web-node-http-transport/-/grpc-web-node-http-transport-0.15.0.tgz#5a064472ef43489cbd075a91fb831c2abeb09d68" - integrity sha512-HLgJfVolGGpjc9DWPhmMmXJx8YGzkek7jcCFO1YYkSOoO81MWRZentPOd/JiKiZuU08wtc4BG+WNuGzsQB5jZA== - -"@improbable-eng/grpc-web@^0.14.1": - version "0.14.1" - resolved "https://registry.yarnpkg.com/@improbable-eng/grpc-web/-/grpc-web-0.14.1.tgz#f4662f64dc89c0f956a94bb8a3b576556c74589c" - integrity sha512-XaIYuunepPxoiGVLLHmlnVminUGzBTnXr8Wv7khzmLWbNw4TCwJKX09GSMJlKhu/TRk6gms0ySFxewaETSBqgw== - dependencies: - browser-headers "^0.4.1" - -"@improbable-eng/grpc-web@^0.15.0": - version "0.15.0" - resolved "https://registry.yarnpkg.com/@improbable-eng/grpc-web/-/grpc-web-0.15.0.tgz#3e47e9fdd90381a74abd4b7d26e67422a2a04bef" - integrity sha512-ERft9/0/8CmYalqOVnJnpdDry28q+j+nAlFFARdjyxXDJ+Mhgv9+F600QC8BR9ygOfrXRlAk6CvST2j+JCpQPg== +"@interlay/bridge@^0.2.4": + version "0.2.4" + resolved "https://registry.yarnpkg.com/@interlay/bridge/-/bridge-0.2.4.tgz#83f446575d1b66cac7601bc4b771c3b19b9137b5" + integrity sha512-XYgLhd4anvoaLL9C+Su/BDATd0K6rQipZXjQW3wuqTYyy+Pr7ItNGu4FbSGLqid1osn7b7No4sXQ5WwFJsZSQA== dependencies: - browser-headers "^0.4.1" - -"@interlay/bridge@^0.1.14": - version "0.1.14" - resolved "https://registry.yarnpkg.com/@interlay/bridge/-/bridge-0.1.14.tgz#5b46aa15fbe430a4e99a5b135745d8d6b9b62553" - integrity sha512-FgfkfWjRooyEAONgK6rutmUanFVYfyLL3poWeGI/dDfbr0JhLcQBRRQXg7HY37hemJXnl2+BBMyeTDxoRK0D/w== - dependencies: - "@acala-network/api" "4.1.6-23" - "@acala-network/sdk" "4.1.6-23" - "@acala-network/sdk-core" "4.1.6-23" - "@polkadot/api" "^9.8.1" + "@acala-network/api" "4.1.8-9" + "@acala-network/sdk" "4.1.8-9" + "@acala-network/sdk-core" "4.1.8-9" + "@polkadot/api" "^9.11.1" "@polkadot/apps-config" "^0.122.2" - "@polkadot/types" "^9.8.1" - "@polkadot/types-augment" "^9.8.1" + "@polkadot/types" "^9.11.1" + "@polkadot/types-augment" "^9.11.1" axios "^0.27.2" lodash "^4.17.20" @@ -3092,15 +2564,15 @@ dependencies: axios "^0.21.1" -"@interlay/interbtc-api@1.19.0": - version "1.19.0" - resolved "https://registry.yarnpkg.com/@interlay/interbtc-api/-/interbtc-api-1.19.0.tgz#27b7907f595c3986c9b4f172b347e023f4056e09" - integrity sha512-4G9krBf5lxvS+oOvwFIrM9rZ7cneOZQTEbl7xLtDqKvztSm8S7K2ebcacBu2CHCD6X0asSyRmXmE9OxOJjKbcw== +"@interlay/interbtc-api@2.0.3": + version "2.0.3" + resolved "https://registry.yarnpkg.com/@interlay/interbtc-api/-/interbtc-api-2.0.3.tgz#21dd39e5539cf6967c5bc5e641501ed3d4cddf68" + integrity sha512-holtYk240R+MBERc5iZ48FoorwezNSgAt3rVUksqa61tguofc6agNK5sP/MDjKUZi8JLImlDYt7VmhOs9O88og== dependencies: "@interlay/esplora-btc-api" "0.4.0" - "@interlay/interbtc-types" "1.10.0" - "@interlay/monetary-js" "0.7.0" - "@polkadot/api" "9.8.1" + "@interlay/interbtc-types" "1.12.0" + "@interlay/monetary-js" "0.7.2" + "@polkadot/api" "9.14.2" big.js "6.1.1" bitcoin-core "^3.0.0" bitcoinjs-lib "^5.2.0" @@ -3109,20 +2581,20 @@ isomorphic-fetch "^3.0.0" regtest-client "^0.2.0" -"@interlay/interbtc-types@1.10.0": - version "1.10.0" - resolved "https://registry.yarnpkg.com/@interlay/interbtc-types/-/interbtc-types-1.10.0.tgz#d1a4b2895d99b0b711737126b3b8c6872b2b9a94" - integrity sha512-lg1CfFsBOlQbjVPhTP5GLFyZ6OCC7ixwX+Th8uQcQToGUhgRjz9evK6qBydqvo8/muTaMVEEUYclBhCgXVQdwQ== +"@interlay/interbtc-types@1.12.0": + version "1.12.0" + resolved "https://registry.yarnpkg.com/@interlay/interbtc-types/-/interbtc-types-1.12.0.tgz#07dc8e15690292387124dbc2bbb7bf5bc8b68001" + integrity sha512-ELJa2ftIbe8Ds2ejS7kO5HumN9EB5l2OBi3Qsy5iHJsHKq2HtXfFoKnW38HarM6hADrWG+e/yNGHSKJIJzEZuA== "@interlay/interbtc-types@1.9.0": version "1.9.0" resolved "https://registry.yarnpkg.com/@interlay/interbtc-types/-/interbtc-types-1.9.0.tgz#beffd3b04bc1d9dba49f3ddc338b5867b81dec3d" integrity sha512-G/jOHXM6lqoFAPquESAxsjt5ETrmcPTDC36WFRWYpoRUfFxcjq6TkWGxUC5/RS6jIoWMQ6lEJZupmlm/QNdbAg== -"@interlay/monetary-js@0.7.0": - version "0.7.0" - resolved "https://registry.yarnpkg.com/@interlay/monetary-js/-/monetary-js-0.7.0.tgz#7142f7d2b86acf77d8524cee22b4ae2c5ba2d3a6" - integrity sha512-vxUcrl2Kum4W2bPt9Lz0WM/pPZEjOrwBtMEgNBAqBtBROXWeOi5jdwQeUwNSr2jqxv0g3V0mVyCVqOxy00iiQg== +"@interlay/monetary-js@0.7.2": + version "0.7.2" + resolved "https://registry.yarnpkg.com/@interlay/monetary-js/-/monetary-js-0.7.2.tgz#a54a315b60be12f5b1a9c31f0d71d5e8ee7ba174" + integrity sha512-SqNKJKBEstXuLnzqWi+ON+9ImrNVlKqZpXfiTtT3bcvxqh4jnVsGbYVe2I0FqDWtilCWq6/1RjKkpaSG2dvJhA== dependencies: "@types/big.js" "6.1.2" big.js "6.1.1" @@ -3473,17 +2945,6 @@ resolved "https://registry.yarnpkg.com/@kiltprotocol/type-definitions/-/type-definitions-0.2.1.tgz#0469b0bcc58063be0b02ffbf6f779176c6b0a00d" integrity sha512-By09MH20P+rXadiZDnw2XeAw7bQLgNazOyNS3gPdU1L4Jx+lU9OtvIgZEA+T/TY/KM5nTv32s3c4wZ7v1s2znw== -"@koa/router@^9.0.1": - version "9.4.0" - resolved "https://registry.yarnpkg.com/@koa/router/-/router-9.4.0.tgz#734b64c0ae566eb5af752df71e4143edc4748e48" - integrity sha512-dOOXgzqaDoHu5qqMEPLKEgLz5CeIA7q8+1W62mCvFVCOqeC71UoTGJ4u1xUSOpIl2J1x2pqrNULkFteUeZW3/A== - dependencies: - debug "^4.1.1" - http-errors "^1.7.3" - koa-compose "^4.1.0" - methods "^1.1.2" - path-to-regexp "^6.1.0" - "@laminar/type-definitions@0.3.1": version "0.3.1" resolved "https://registry.yarnpkg.com/@laminar/type-definitions/-/type-definitions-0.3.1.tgz#e1b62ab353245f9b3454cb5d909a329f66aaac8f" @@ -3508,26 +2969,6 @@ resolved "https://registry.yarnpkg.com/@mangata-finance/types/-/types-0.9.0.tgz#8272a01d87243f693d0e0889070afb0d00b4f91f" integrity sha512-37yIP9xh+6kTt+UQJsbPt0OCrDIZsqGRh3f7sEtK7zX4G8uWrg/GHcHtGZQRruNIOAO8+1PLXuMfLokSzojVBw== -"@mapbox/node-pre-gyp@^1.0.4": - version "1.0.10" - resolved "https://registry.yarnpkg.com/@mapbox/node-pre-gyp/-/node-pre-gyp-1.0.10.tgz#8e6735ccebbb1581e5a7e652244cadc8a844d03c" - integrity sha512-4ySo4CjzStuprMwk35H5pPbkymjv1SF3jGLj6rAHp/xT/RF7TL7bd9CTm1xDY49K2qF7jmR/g7k+SkLETP6opA== - dependencies: - detect-libc "^2.0.0" - https-proxy-agent "^5.0.0" - make-dir "^3.1.0" - node-fetch "^2.6.7" - nopt "^5.0.0" - npmlog "^5.0.1" - rimraf "^3.0.2" - semver "^7.3.5" - tar "^6.1.11" - -"@material-icons/svg@^1.0.28": - version "1.0.28" - resolved "https://registry.yarnpkg.com/@material-icons/svg/-/svg-1.0.28.tgz#ac111059a7e597c7b53e27b8be22e17839ba5ecc" - integrity sha512-qil+dMa6oN2fxrmmBPH0oKBheIehOrlu6rIrcE5wvceo6cong6x7qWN/7fOUaDOVDoAxSnUKFy9Q3xXXJuPYkQ== - "@mdx-js/mdx@^1.6.22": version "1.6.22" resolved "https://registry.yarnpkg.com/@mdx-js/mdx/-/mdx-1.6.22.tgz#8a723157bf90e78f17dc0f27995398e6c731f1ba" @@ -3578,45 +3019,15 @@ call-me-maybe "^1.0.1" glob-to-regexp "^0.3.0" -"@noble/ed25519@^1.7.0": - version "1.7.1" - resolved "https://registry.yarnpkg.com/@noble/ed25519/-/ed25519-1.7.1.tgz#6899660f6fbb97798a6fbd227227c4589a454724" - integrity sha512-Rk4SkJFaXZiznFyC/t77Q0NKS4FL7TLJJsVG2V2oiEq3kJVeTdxysEe/yRWSpnWMe808XRDJ+VFh5pt/FN5plw== - -"@noble/hashes@1.0.0": - version "1.0.0" - resolved "https://registry.yarnpkg.com/@noble/hashes/-/hashes-1.0.0.tgz#d5e38bfbdaba174805a4e649f13be9a9ed3351ae" - integrity sha512-DZVbtY62kc3kkBtMHqwCOfXrT/hnoORy5BJ4+HU1IR59X0KWAOqsfzQPcUl/lQLlG7qXbe/fZ3r/emxtAl+sqg== - -"@noble/hashes@1.1.2": - version "1.1.2" - resolved "https://registry.yarnpkg.com/@noble/hashes/-/hashes-1.1.2.tgz#e9e035b9b166ca0af657a7848eb2718f0f22f183" - integrity sha512-KYRCASVTv6aeUi1tsF8/vpyR7zpfs3FUzy2Jqm+MU+LmUKhQ0y2FpfwqkCcxSg2ua4GALJd8k2R76WxwZGbQpA== - -"@noble/hashes@1.1.3", "@noble/hashes@^1.1.2": - version "1.1.3" - resolved "https://registry.yarnpkg.com/@noble/hashes/-/hashes-1.1.3.tgz#360afc77610e0a61f3417e497dcf36862e4f8111" - integrity sha512-CE0FCR57H2acVI5UOzIGSSIYxZ6v/HOhDR0Ro9VLyhnzLwx0o8W1mmgaqlEUx4049qJDlIBRztv5k+MM8vbO3A== - -"@noble/secp256k1@1.5.5": - version "1.5.5" - resolved "https://registry.yarnpkg.com/@noble/secp256k1/-/secp256k1-1.5.5.tgz#315ab5745509d1a8c8e90d0bdf59823ccf9bcfc3" - integrity sha512-sZ1W6gQzYnu45wPrWx8D3kwI2/U29VYTx9OjbDAd7jwRItJ0cSTMPRL/C8AWZFn9kWFLQGqEXVEE86w4Z8LpIQ== - -"@noble/secp256k1@1.6.0": - version "1.6.0" - resolved "https://registry.yarnpkg.com/@noble/secp256k1/-/secp256k1-1.6.0.tgz#602afbbfcfb7e169210469b697365ef740d7e930" - integrity sha512-DWSsg8zMHOYMYBqIQi96BQuthZrp98LCeMNcUOaffCIVYQ5yxDbNikLF+H7jEnmNNmXbtVic46iCuVWzar+MgA== - -"@noble/secp256k1@1.6.3": - version "1.6.3" - resolved "https://registry.yarnpkg.com/@noble/secp256k1/-/secp256k1-1.6.3.tgz#7eed12d9f4404b416999d0c87686836c4c5c9b94" - integrity sha512-T04e4iTurVy7I8Sw4+c5OSN9/RkPlo1uKxAomtxQNLq8j1uPAqnsqG1bqvY3Jv7c13gyr6dui0zmh/I3+f/JaQ== +"@noble/hashes@1.2.0": + version "1.2.0" + resolved "https://registry.yarnpkg.com/@noble/hashes/-/hashes-1.2.0.tgz#a3150eeb09cc7ab207ebf6d7b9ad311a9bdbed12" + integrity sha512-FZfhjEDbT5GRswV3C6uvLPHMiVD6lQBmpoX5+eSiPaMTXte/IKqI5dykDxzZB/WBeK/CDuQRBWarPdi3FNY2zQ== -"@noble/secp256k1@1.7.0", "@noble/secp256k1@^1.6.3": - version "1.7.0" - resolved "https://registry.yarnpkg.com/@noble/secp256k1/-/secp256k1-1.7.0.tgz#d15357f7c227e751d90aa06b05a0e5cf993ba8c1" - integrity sha512-kbacwGSsH/CTout0ZnZWxnW1B+jH/7r/WAAKLBtrRJ/+CUH7lgmQzl3GTrQua3SGKWNSDsS6lmjnDpIJ5Dxyaw== +"@noble/secp256k1@1.7.1": + version "1.7.1" + resolved "https://registry.yarnpkg.com/@noble/secp256k1/-/secp256k1-1.7.1.tgz#b251c70f824ce3ca7f8dc3df08d58f005cc0507c" + integrity sha512-hOUk6AyBFmqVrv7k5WAw/LpszxVbj9gGN4JRkIX52fdFAj1UA61KXmZDvqVEm+pOyec3+fIeZB02LYa/pWOArw== "@nodelib/fs.scandir@2.1.5": version "2.1.5" @@ -3660,21 +3071,6 @@ mkdirp "^1.0.4" rimraf "^3.0.2" -"@nuts-finance/sdk-stable-asset@^1.1.16": - version "1.2.7" - resolved "https://registry.yarnpkg.com/@nuts-finance/sdk-stable-asset/-/sdk-stable-asset-1.2.7.tgz#121babbe0dd0a221381c99c4aa322adaaa18552f" - integrity sha512-Z/THmfMLZdTdKRz7+WHAh3kgXpNhbHtqLHzXzavIrD5drTxBK1SMuaF3k5qmeW6P8Gvs2FDMYTzkZ6X6sVYxpw== - dependencies: - "@acala-network/sdk" "4.1.4-3" - "@acala-network/sdk-core" "4.1.4-3" - "@acala-network/types" "4.1.4-3" - "@polkadot/api" "^8.5.1" - "@polkadot/types" "^8.5.1" - axios "^0.27.2" - bignumber.js "^9.0.0" - lodash "^4.17.20" - rxjs "^7.4.0" - "@octokit/auth-token@^3.0.0": version "3.0.2" resolved "https://registry.yarnpkg.com/@octokit/auth-token/-/auth-token-3.0.2.tgz#a0fc8de149fd15876e1ac78f6525c1c5ab48435f" @@ -3901,1340 +3297,297 @@ resolved "https://registry.yarnpkg.com/@polka/url/-/url-1.0.0-next.21.tgz#5de5a2385a35309427f6011992b544514d559aa1" integrity sha512-a5Sab1C4/icpTZVzZc5Ghpz88yQtGOyNqYXcZgOssB2uuAr+wF/MvN6bgtW32q7HHrvBki+BsZ0OuNv6EV3K9g== -"@polkadot/api-augment@7.15.1": - version "7.15.1" - resolved "https://registry.yarnpkg.com/@polkadot/api-augment/-/api-augment-7.15.1.tgz#120b766feeaa96996f1c6717a5261c2e0845c1e0" - integrity sha512-7csQLS6zuYuGq7W1EkTBz1ZmxyRvx/Qpz7E7zPSwxmY8Whb7Yn2effU9XF0eCcRpyfSW8LodF8wMmLxGYs1OaQ== - dependencies: - "@babel/runtime" "^7.17.8" - "@polkadot/api-base" "7.15.1" - "@polkadot/rpc-augment" "7.15.1" - "@polkadot/types" "7.15.1" - "@polkadot/types-augment" "7.15.1" - "@polkadot/types-codec" "7.15.1" - "@polkadot/util" "^8.7.1" - -"@polkadot/api-augment@8.1.1": - version "8.1.1" - resolved "https://registry.yarnpkg.com/@polkadot/api-augment/-/api-augment-8.1.1.tgz#5e49193b322456add7114ebdcc739ab3583f5ffb" - integrity sha512-Entu5JP94mxLlaWduASECr+etw73CSBhpuaNY+/G4nXeZ34E6KkG1jURvGNvnNA8GXig42TXWT1Xrn3db6GoTg== - dependencies: - "@babel/runtime" "^7.17.9" - "@polkadot/api-base" "8.1.1" - "@polkadot/rpc-augment" "8.1.1" - "@polkadot/types" "8.1.1" - "@polkadot/types-augment" "8.1.1" - "@polkadot/types-codec" "8.1.1" - "@polkadot/util" "^9.0.1" - -"@polkadot/api-augment@8.14.1": - version "8.14.1" - resolved "https://registry.yarnpkg.com/@polkadot/api-augment/-/api-augment-8.14.1.tgz#f44a2e1952cb158bce55db687be9e3ac7113c87e" - integrity sha512-65GMlgVnZd08Ifh8uAj+p/+MlXxvsAfBcCHjQhOmbCE0dki+rzTPUR31LsWyDKtuw+nUBj0iZN4PelO+wU4r0g== - dependencies: - "@babel/runtime" "^7.18.9" - "@polkadot/api-base" "8.14.1" - "@polkadot/rpc-augment" "8.14.1" - "@polkadot/types" "8.14.1" - "@polkadot/types-augment" "8.14.1" - "@polkadot/types-codec" "8.14.1" - "@polkadot/util" "^10.1.1" - -"@polkadot/api-augment@9.10.2": - version "9.10.2" - resolved "https://registry.yarnpkg.com/@polkadot/api-augment/-/api-augment-9.10.2.tgz#9d1875bffe9d8677a4f03d53ca6df3d0d7e7f53d" - integrity sha512-B0xC7yvPAZqPZpKJzrlFSDfHBawCJISwdV4/nBSs1/AaqQIXVu2ZqPUaSdq7eisZL/EZziptK0SpCtDcb6LpAg== - dependencies: - "@babel/runtime" "^7.20.6" - "@polkadot/api-base" "9.10.2" - "@polkadot/rpc-augment" "9.10.2" - "@polkadot/types" "9.10.2" - "@polkadot/types-augment" "9.10.2" - "@polkadot/types-codec" "9.10.2" - "@polkadot/util" "^10.2.1" - -"@polkadot/api-augment@9.8.1": - version "9.8.1" - resolved "https://registry.yarnpkg.com/@polkadot/api-augment/-/api-augment-9.8.1.tgz#53cfc2acb1b1a1ae03a180918d523f102471696f" - integrity sha512-uFtznBzeMysT6CTaGM+Ssa6cC6FOUc3jFa1qGd8ENBJwc0LOhqEQukGwdeIWWzXgwqUbc4158rEg4ifMj64BoQ== - dependencies: - "@babel/runtime" "^7.20.1" - "@polkadot/api-base" "9.8.1" - "@polkadot/rpc-augment" "9.8.1" - "@polkadot/types" "9.8.1" - "@polkadot/types-augment" "9.8.1" - "@polkadot/types-codec" "9.8.1" - "@polkadot/util" "^10.1.12" +"@polkadot/api-augment@9.10.3", "@polkadot/api-augment@9.14.2", "@polkadot/api-augment@^9.14.2": + version "9.14.2" + resolved "https://registry.yarnpkg.com/@polkadot/api-augment/-/api-augment-9.14.2.tgz#2c49cdcfdf7057523db1dc8d7b0801391a8a2e69" + integrity sha512-19MmW8AHEcLkdcUIo3LLk0eCQgREWqNSxkUyOeWn7UiNMY1AhDOOwMStUBNCvrIDK6VL6GGc1sY7rkPCLMuKSw== + dependencies: + "@babel/runtime" "^7.20.13" + "@polkadot/api-base" "9.14.2" + "@polkadot/rpc-augment" "9.14.2" + "@polkadot/types" "9.14.2" + "@polkadot/types-augment" "9.14.2" + "@polkadot/types-codec" "9.14.2" + "@polkadot/util" "^10.4.2" + +"@polkadot/api-base@9.14.2", "@polkadot/api-base@^9.14.2": + version "9.14.2" + resolved "https://registry.yarnpkg.com/@polkadot/api-base/-/api-base-9.14.2.tgz#605b44e3692a125bd8d97eed9cea8af9d0c454e2" + integrity sha512-ky9fmzG1Tnrjr/SBZ0aBB21l0TFr+CIyQenQczoUyVgiuxVaI/2Bp6R2SFrHhG28P+PW2/RcYhn2oIAR2Z2fZQ== + dependencies: + "@babel/runtime" "^7.20.13" + "@polkadot/rpc-core" "9.14.2" + "@polkadot/types" "9.14.2" + "@polkadot/util" "^10.4.2" + rxjs "^7.8.0" + +"@polkadot/api-contract@^9.14.2": + version "9.14.2" + resolved "https://registry.yarnpkg.com/@polkadot/api-contract/-/api-contract-9.14.2.tgz#42ca46eecd6cef64b6c453b452bdb5d22a29b6a3" + integrity sha512-gAAHEh+tOIKuAJWxbAuB8Imo+Z8s0FHdICN6/q4JOxBhONJNA9beHB4wmqWSKvYqYmWrJvtv3HensLaITzAcrQ== + dependencies: + "@babel/runtime" "^7.20.13" + "@polkadot/api" "9.14.2" + "@polkadot/types" "9.14.2" + "@polkadot/types-codec" "9.14.2" + "@polkadot/types-create" "9.14.2" + "@polkadot/util" "^10.4.2" + "@polkadot/util-crypto" "^10.4.2" + rxjs "^7.8.0" + +"@polkadot/api-derive@9.10.3", "@polkadot/api-derive@9.14.2", "@polkadot/api-derive@^8.5.1", "@polkadot/api-derive@^9.14.2", "@polkadot/api-derive@^9.7.1": + version "9.14.2" + resolved "https://registry.yarnpkg.com/@polkadot/api-derive/-/api-derive-9.14.2.tgz#e8fcd4ee3f2b80b9fe34d4dec96169c3bdb4214d" + integrity sha512-yw9OXucmeggmFqBTMgza0uZwhNjPxS7MaT7lSCUIRKckl1GejdV+qMhL3XFxPFeYzXwzFpdPG11zWf+qJlalqw== + dependencies: + "@babel/runtime" "^7.20.13" + "@polkadot/api" "9.14.2" + "@polkadot/api-augment" "9.14.2" + "@polkadot/api-base" "9.14.2" + "@polkadot/rpc-core" "9.14.2" + "@polkadot/types" "9.14.2" + "@polkadot/types-codec" "9.14.2" + "@polkadot/util" "^10.4.2" + "@polkadot/util-crypto" "^10.4.2" + rxjs "^7.8.0" + +"@polkadot/api@9.10.3", "@polkadot/api@9.14.2", "@polkadot/api@^7.2.1", "@polkadot/api@^9.11.1", "@polkadot/api@^9.14.2", "@polkadot/api@^9.4.2", "@polkadot/api@^9.7.1", "@polkadot/api@^9.8.1", "@polkadot/api@^9.9.1", "@polkadot/api@latest": + version "9.14.2" + resolved "https://registry.yarnpkg.com/@polkadot/api/-/api-9.14.2.tgz#d5cee02236654c6063d7c4b70c78c290db5aba8d" + integrity sha512-R3eYFj2JgY1zRb+OCYQxNlJXCs2FA+AU4uIEiVcXnVLmR3M55tkRNEwYAZmiFxx0pQmegGgPMc33q7TWGdw24A== + dependencies: + "@babel/runtime" "^7.20.13" + "@polkadot/api-augment" "9.14.2" + "@polkadot/api-base" "9.14.2" + "@polkadot/api-derive" "9.14.2" + "@polkadot/keyring" "^10.4.2" + "@polkadot/rpc-augment" "9.14.2" + "@polkadot/rpc-core" "9.14.2" + "@polkadot/rpc-provider" "9.14.2" + "@polkadot/types" "9.14.2" + "@polkadot/types-augment" "9.14.2" + "@polkadot/types-codec" "9.14.2" + "@polkadot/types-create" "9.14.2" + "@polkadot/types-known" "9.14.2" + "@polkadot/util" "^10.4.2" + "@polkadot/util-crypto" "^10.4.2" + eventemitter3 "^5.0.0" + rxjs "^7.8.0" -"@polkadot/api-augment@9.8.2": - version "9.8.2" - resolved "https://registry.yarnpkg.com/@polkadot/api-augment/-/api-augment-9.8.2.tgz#6af994618fccf367985d18cf5054eebf34af7c71" - integrity sha512-qAkbQXEDnXWopQAieBT60OtkpcTB6zML0oPZuccH6rwogZ5wgVZEn+XmE7LyyBipYF/Htzt9D5Bq03pTrHrlVA== +"@polkadot/apps-config@^0.122.2": + version "0.122.2" + resolved "https://registry.yarnpkg.com/@polkadot/apps-config/-/apps-config-0.122.2.tgz#b15d2dbfc43b0e8bc32fc14bd56b97de3abb83e3" + integrity sha512-EBINVhOe4w5gzjOJ/lIzYJDP1DiZY8SWBf8Jp25DOwdSvUsyV1AYyrGAgmz+kE+jtakEMKOZpXdRt3OwGYLPqw== dependencies: + "@acala-network/type-definitions" "^4.1.5" "@babel/runtime" "^7.20.1" - "@polkadot/api-base" "9.8.2" - "@polkadot/rpc-augment" "9.8.2" - "@polkadot/types" "9.8.2" - "@polkadot/types-augment" "9.8.2" - "@polkadot/types-codec" "9.8.2" - "@polkadot/util" "^10.1.12" + "@bifrost-finance/type-definitions" "1.7.1" + "@crustio/type-definitions" "1.3.0" + "@darwinia/types" "2.8.10" + "@darwinia/types-known" "2.8.10" + "@digitalnative/type-definitions" "1.1.27" + "@docknetwork/node-types" "0.13.0" + "@edgeware/node-types" "3.6.2-wako" + "@equilab/definitions" "1.4.14" + "@interlay/interbtc-types" "1.9.0" + "@kiltprotocol/type-definitions" "^0.2.1" + "@laminar/type-definitions" "0.3.1" + "@logion/node-api" "^0.7.0" + "@mangata-finance/types" "^0.9.0" + "@metaverse-network-sdk/type-definitions" "^0.0.1-13" + "@parallel-finance/type-definitions" "1.7.13" + "@phala/typedefs" "0.2.32" + "@polkadot/api" "^9.7.1" + "@polkadot/api-derive" "^9.7.1" + "@polkadot/networks" "^10.1.11" + "@polkadot/types" "^9.7.1" + "@polkadot/util" "^10.1.11" + "@polkadot/x-fetch" "^10.1.11" + "@polymathnetwork/polymesh-types" "0.0.2" + "@snowfork/snowbridge-types" "0.2.7" + "@sora-substrate/type-definitions" "1.10.21" + "@subsocial/definitions" "^0.7.8-dev.0" + "@unique-nft/opal-testnet-types" "930.31.0" + "@unique-nft/quartz-mainnet-types" "930.31.0" + "@unique-nft/unique-mainnet-types" "930.31.0" + "@zeitgeistpm/type-defs" "0.9.0" + "@zeroio/type-definitions" "0.0.14" + lodash "^4.17.21" + moonbeam-types-bundle "2.0.9" + pontem-types-bundle "1.0.15" + rxjs "^7.5.7" -"@polkadot/api-base@7.15.1": - version "7.15.1" - resolved "https://registry.yarnpkg.com/@polkadot/api-base/-/api-base-7.15.1.tgz#7567595be68431cc4085c48b18ba66933ff7b4d9" - integrity sha512-UlhLdljJPDwGpm5FxOjvJNFTxXMRFaMuVNx6EklbuetbBEJ/Amihhtj0EJRodxQwtZ4ZtPKYKt+g+Dn7OJJh4g== +"@polkadot/extension-dapp@0.44.1": + version "0.44.1" + resolved "https://registry.yarnpkg.com/@polkadot/extension-dapp/-/extension-dapp-0.44.1.tgz#bbde17799efba9bb889f97575e69814263a7ed59" + integrity sha512-NPMiww/JxUra8OPYfYlw45r5bjl/mh1kvuDqdmDdYCLUCM00g/oynEEL8GraKUCRkZ/0K6CepldDAmIzgv45hw== dependencies: - "@babel/runtime" "^7.17.8" - "@polkadot/rpc-core" "7.15.1" - "@polkadot/types" "7.15.1" - "@polkadot/util" "^8.7.1" - rxjs "^7.5.5" + "@babel/runtime" "^7.18.3" + "@polkadot/extension-inject" "^0.44.1" + "@polkadot/util" "^9.4.1" + "@polkadot/util-crypto" "^9.4.1" -"@polkadot/api-base@8.1.1": - version "8.1.1" - resolved "https://registry.yarnpkg.com/@polkadot/api-base/-/api-base-8.1.1.tgz#3ec4d2f94182e2bae2a72edabb46bc2bb280f48a" - integrity sha512-1+4DYF3kdVE0dxpQPPznahQgDjOYokAWTTivYkR+ARj0du0b2JYzJLlnQV6vmmu7U+njn3mBAFPMByWa+scFIg== - dependencies: - "@babel/runtime" "^7.17.9" - "@polkadot/rpc-core" "8.1.1" - "@polkadot/types" "8.1.1" - "@polkadot/util" "^9.0.1" - rxjs "^7.5.5" - -"@polkadot/api-base@8.14.1": - version "8.14.1" - resolved "https://registry.yarnpkg.com/@polkadot/api-base/-/api-base-8.14.1.tgz#a9380b11b74f2bc60dbf62b562b78c779e1d5b24" - integrity sha512-EXFhNXIfpirf18IsqcG2pGQW1/Xn+bfjqVYQMMJ4ZONtYH4baZZlXk7SoXCCHonN2x1ixs4DOcRx5oVxjabdIQ== - dependencies: - "@babel/runtime" "^7.18.9" - "@polkadot/rpc-core" "8.14.1" - "@polkadot/types" "8.14.1" - "@polkadot/util" "^10.1.1" - rxjs "^7.5.6" - -"@polkadot/api-base@9.10.2": - version "9.10.2" - resolved "https://registry.yarnpkg.com/@polkadot/api-base/-/api-base-9.10.2.tgz#39248e966b468ecff7c0ed00bb61dfca14ca99d4" - integrity sha512-M/Yushqk6eEAfbkF90vy3GCVg+a2uVeSXyTBKbmkjZtcE7x39GiXs7LOJuYkIim51hlwcvVSeInX8HufwnTUMw== - dependencies: - "@babel/runtime" "^7.20.6" - "@polkadot/rpc-core" "9.10.2" - "@polkadot/types" "9.10.2" - "@polkadot/util" "^10.2.1" - rxjs "^7.6.0" - -"@polkadot/api-base@9.8.1": - version "9.8.1" - resolved "https://registry.yarnpkg.com/@polkadot/api-base/-/api-base-9.8.1.tgz#90b0404b0636f738e09ca12c2263affbf67d17ad" - integrity sha512-ofWYB34/+6kDr2XpKgG1oTuOC9C4XAy+7KNW5OkvpKNmNiRX7svk1T4u2hGxnHoEbCEHGUb8nIlz90twxwBreg== - dependencies: - "@babel/runtime" "^7.20.1" - "@polkadot/rpc-core" "9.8.1" - "@polkadot/types" "9.8.1" - "@polkadot/util" "^10.1.12" - rxjs "^7.5.7" - -"@polkadot/api-base@9.8.2": - version "9.8.2" - resolved "https://registry.yarnpkg.com/@polkadot/api-base/-/api-base-9.8.2.tgz#4c906451d2d6f41a49af6122cc8396c0d3cb7818" - integrity sha512-WjlwaTKQx/+1FdEh2s8BOgS4bK04sYCNH5pky23OPwzKzFbFtGa4RpBCoZfq8aFB2mBQjb8tAGMYpSNUOX92aw== - dependencies: - "@babel/runtime" "^7.20.1" - "@polkadot/rpc-core" "9.8.2" - "@polkadot/types" "9.8.2" - "@polkadot/util" "^10.1.12" - rxjs "^7.5.7" - -"@polkadot/api-derive@7.15.1": - version "7.15.1" - resolved "https://registry.yarnpkg.com/@polkadot/api-derive/-/api-derive-7.15.1.tgz#450542bb7d848013225d6c8480648340e5ee6a61" - integrity sha512-CsOQppksQBaa34L1fWRzmfQQpoEBwfH0yTTQxgj3h7rFYGVPxEKGeFjo1+IgI2vXXvOO73Z8E4H/MnbxvKrs1Q== - dependencies: - "@babel/runtime" "^7.17.8" - "@polkadot/api" "7.15.1" - "@polkadot/api-augment" "7.15.1" - "@polkadot/api-base" "7.15.1" - "@polkadot/rpc-core" "7.15.1" - "@polkadot/types" "7.15.1" - "@polkadot/types-codec" "7.15.1" - "@polkadot/util" "^8.7.1" - "@polkadot/util-crypto" "^8.7.1" - rxjs "^7.5.5" - -"@polkadot/api-derive@8.1.1": - version "8.1.1" - resolved "https://registry.yarnpkg.com/@polkadot/api-derive/-/api-derive-8.1.1.tgz#4e945738bf9374943aa86bb643e3ae0ec020e8ba" - integrity sha512-pwGls3OtPGbbR0uszBzWH3zqvsLlbB4bgvTYOYWzITY/WpwI0EkhINIRW1osf7foyRD6AzeX8FKumfkv3d8ItQ== - dependencies: - "@babel/runtime" "^7.17.9" - "@polkadot/api" "8.1.1" - "@polkadot/api-augment" "8.1.1" - "@polkadot/api-base" "8.1.1" - "@polkadot/rpc-core" "8.1.1" - "@polkadot/types" "8.1.1" - "@polkadot/types-codec" "8.1.1" - "@polkadot/util" "^9.0.1" - "@polkadot/util-crypto" "^9.0.1" - rxjs "^7.5.5" - -"@polkadot/api-derive@8.14.1", "@polkadot/api-derive@^8.5.1": - version "8.14.1" - resolved "https://registry.yarnpkg.com/@polkadot/api-derive/-/api-derive-8.14.1.tgz#9079ad58f66e6a2d45d57947e3d8a40135eba9b9" - integrity sha512-eWG1MrQhHMUjt9gDHN9/9/ZMATu1MolqcalPFhNoGtdON3+I0J3ntjQ4y5X7+p2OGwQplpYRKqbK4k7tKzu8tA== - dependencies: - "@babel/runtime" "^7.18.9" - "@polkadot/api" "8.14.1" - "@polkadot/api-augment" "8.14.1" - "@polkadot/api-base" "8.14.1" - "@polkadot/rpc-core" "8.14.1" - "@polkadot/types" "8.14.1" - "@polkadot/types-codec" "8.14.1" - "@polkadot/util" "^10.1.1" - "@polkadot/util-crypto" "^10.1.1" - rxjs "^7.5.6" - -"@polkadot/api-derive@9.10.2", "@polkadot/api-derive@^9.7.1": - version "9.10.2" - resolved "https://registry.yarnpkg.com/@polkadot/api-derive/-/api-derive-9.10.2.tgz#d6b0eb558ee057416b87a304ca2790b19afa4be6" - integrity sha512-Ut1aqbGvqAkxXq7M4HgJ7BVhUyfbQigqt5LISmnjWdGkhroBxtIJ24saOUPYNr0O/c3jocJpoWqGK2CuucL81w== - dependencies: - "@babel/runtime" "^7.20.6" - "@polkadot/api" "9.10.2" - "@polkadot/api-augment" "9.10.2" - "@polkadot/api-base" "9.10.2" - "@polkadot/rpc-core" "9.10.2" - "@polkadot/types" "9.10.2" - "@polkadot/types-codec" "9.10.2" - "@polkadot/util" "^10.2.1" - "@polkadot/util-crypto" "^10.2.1" - rxjs "^7.6.0" - -"@polkadot/api-derive@9.8.1": - version "9.8.1" - resolved "https://registry.yarnpkg.com/@polkadot/api-derive/-/api-derive-9.8.1.tgz#ed368c40f6a92aae38513462b8ffc0572f491f3d" - integrity sha512-z7CaJXgvar1v0+vHlUIko4AB+apocavknQZKbDHtJzPAZB8iMmmtQj1hCVSdIdD7yDvn24f25KUZTMmZ5OHsDw== - dependencies: - "@babel/runtime" "^7.20.1" - "@polkadot/api" "9.8.1" - "@polkadot/api-augment" "9.8.1" - "@polkadot/api-base" "9.8.1" - "@polkadot/rpc-core" "9.8.1" - "@polkadot/types" "9.8.1" - "@polkadot/types-codec" "9.8.1" - "@polkadot/util" "^10.1.12" - "@polkadot/util-crypto" "^10.1.12" - rxjs "^7.5.7" - -"@polkadot/api-derive@9.8.2": - version "9.8.2" - resolved "https://registry.yarnpkg.com/@polkadot/api-derive/-/api-derive-9.8.2.tgz#aaa1d3435640b2168192c7d1439fd0a4ac631367" - integrity sha512-6BNk4+o1rsMO8JmxLKmEB8vADMx48e0Vn2PMvAC/KsE5CPSchwPvsSPh2VXV7PPjdAzaKicK5SlSPJ3XkNTMUA== - dependencies: - "@babel/runtime" "^7.20.1" - "@polkadot/api" "9.8.2" - "@polkadot/api-augment" "9.8.2" - "@polkadot/api-base" "9.8.2" - "@polkadot/rpc-core" "9.8.2" - "@polkadot/types" "9.8.2" - "@polkadot/types-codec" "9.8.2" - "@polkadot/util" "^10.1.12" - "@polkadot/util-crypto" "^10.1.12" - rxjs "^7.5.7" - -"@polkadot/api@7.15.1", "@polkadot/api@^7.2.1": - version "7.15.1" - resolved "https://registry.yarnpkg.com/@polkadot/api/-/api-7.15.1.tgz#24eaeaa8ffbc6f30ff3d9846a816a18a688ceb8b" - integrity sha512-z0z6+k8+R9ixRMWzfsYrNDnqSV5zHKmyhTCL0I7+1I081V18MJTCFUKubrh0t1gD0/FCt3U9Ibvr4IbtukYLrQ== - dependencies: - "@babel/runtime" "^7.17.8" - "@polkadot/api-augment" "7.15.1" - "@polkadot/api-base" "7.15.1" - "@polkadot/api-derive" "7.15.1" - "@polkadot/keyring" "^8.7.1" - "@polkadot/rpc-augment" "7.15.1" - "@polkadot/rpc-core" "7.15.1" - "@polkadot/rpc-provider" "7.15.1" - "@polkadot/types" "7.15.1" - "@polkadot/types-augment" "7.15.1" - "@polkadot/types-codec" "7.15.1" - "@polkadot/types-create" "7.15.1" - "@polkadot/types-known" "7.15.1" - "@polkadot/util" "^8.7.1" - "@polkadot/util-crypto" "^8.7.1" - eventemitter3 "^4.0.7" - rxjs "^7.5.5" - -"@polkadot/api@8.1.1": - version "8.1.1" - resolved "https://registry.yarnpkg.com/@polkadot/api/-/api-8.1.1.tgz#dab093beb1258848dcd164e73b50642264f5d040" - integrity sha512-XAprWhVFdy23YDuGqMc6rTW6CemsbhngQ1e5wMcQKNWxHgCf1mF0Hhvbv/Z4+x6bLG70OELoX7Ssz1mk0tptVQ== - dependencies: - "@babel/runtime" "^7.17.9" - "@polkadot/api-augment" "8.1.1" - "@polkadot/api-base" "8.1.1" - "@polkadot/api-derive" "8.1.1" - "@polkadot/keyring" "^9.0.1" - "@polkadot/rpc-augment" "8.1.1" - "@polkadot/rpc-core" "8.1.1" - "@polkadot/rpc-provider" "8.1.1" - "@polkadot/types" "8.1.1" - "@polkadot/types-augment" "8.1.1" - "@polkadot/types-codec" "8.1.1" - "@polkadot/types-create" "8.1.1" - "@polkadot/types-known" "8.1.1" - "@polkadot/util" "^9.0.1" - "@polkadot/util-crypto" "^9.0.1" - eventemitter3 "^4.0.7" - rxjs "^7.5.5" - -"@polkadot/api@8.14.1", "@polkadot/api@^8.5.1": - version "8.14.1" - resolved "https://registry.yarnpkg.com/@polkadot/api/-/api-8.14.1.tgz#e2f543700db84f89e873c4e1f8eb78d9e648797e" - integrity sha512-jg26eIKFYqVfDBTAopHL3aDaNw9j6TdUkXuvYJOnynpecU4xwbTVKcOtSOjJ2eRX4MgMQ4zlyMHJx3iKw0uUTA== - dependencies: - "@babel/runtime" "^7.18.9" - "@polkadot/api-augment" "8.14.1" - "@polkadot/api-base" "8.14.1" - "@polkadot/api-derive" "8.14.1" - "@polkadot/keyring" "^10.1.1" - "@polkadot/rpc-augment" "8.14.1" - "@polkadot/rpc-core" "8.14.1" - "@polkadot/rpc-provider" "8.14.1" - "@polkadot/types" "8.14.1" - "@polkadot/types-augment" "8.14.1" - "@polkadot/types-codec" "8.14.1" - "@polkadot/types-create" "8.14.1" - "@polkadot/types-known" "8.14.1" - "@polkadot/util" "^10.1.1" - "@polkadot/util-crypto" "^10.1.1" - eventemitter3 "^4.0.7" - rxjs "^7.5.6" - -"@polkadot/api@9.10.2", "@polkadot/api@^9.4.2", "@polkadot/api@^9.7.1": - version "9.10.2" - resolved "https://registry.yarnpkg.com/@polkadot/api/-/api-9.10.2.tgz#9a3132f0c8a5de6c2b7d56f9d9e9c9c5ed2bc77e" - integrity sha512-5leF7rxwRkkd/g11tGPho/CcbInVX7ZiuyMsLMTwn+2PDX+Ggv/gmxUboa34eyeLp8/AMui5YbqRD4QExLTxqw== - dependencies: - "@babel/runtime" "^7.20.6" - "@polkadot/api-augment" "9.10.2" - "@polkadot/api-base" "9.10.2" - "@polkadot/api-derive" "9.10.2" - "@polkadot/keyring" "^10.2.1" - "@polkadot/rpc-augment" "9.10.2" - "@polkadot/rpc-core" "9.10.2" - "@polkadot/rpc-provider" "9.10.2" - "@polkadot/types" "9.10.2" - "@polkadot/types-augment" "9.10.2" - "@polkadot/types-codec" "9.10.2" - "@polkadot/types-create" "9.10.2" - "@polkadot/types-known" "9.10.2" - "@polkadot/util" "^10.2.1" - "@polkadot/util-crypto" "^10.2.1" - eventemitter3 "^4.0.7" - rxjs "^7.6.0" - -"@polkadot/api@9.8.1": - version "9.8.1" - resolved "https://registry.yarnpkg.com/@polkadot/api/-/api-9.8.1.tgz#4ed20a82f0b804488da6e94129f71c3d3684ea9f" - integrity sha512-voxZJQNzqDa4ztSKGFneJ84q4Hc/mhl2beIWNVUUG0LFI7/QUnu3f4FXGdw6WSF+wDu6e88/o0W3LbO+gswbMA== - dependencies: - "@babel/runtime" "^7.20.1" - "@polkadot/api-augment" "9.8.1" - "@polkadot/api-base" "9.8.1" - "@polkadot/api-derive" "9.8.1" - "@polkadot/keyring" "^10.1.12" - "@polkadot/rpc-augment" "9.8.1" - "@polkadot/rpc-core" "9.8.1" - "@polkadot/rpc-provider" "9.8.1" - "@polkadot/types" "9.8.1" - "@polkadot/types-augment" "9.8.1" - "@polkadot/types-codec" "9.8.1" - "@polkadot/types-create" "9.8.1" - "@polkadot/types-known" "9.8.1" - "@polkadot/util" "^10.1.12" - "@polkadot/util-crypto" "^10.1.12" - eventemitter3 "^4.0.7" - rxjs "^7.5.7" - -"@polkadot/api@9.8.2", "@polkadot/api@^9.8.1", "@polkadot/api@latest": - version "9.8.2" - resolved "https://registry.yarnpkg.com/@polkadot/api/-/api-9.8.2.tgz#c95f4058dea05deeec60eb21075840962968dfb4" - integrity sha512-bUPOWcJwhz/MJuxRJ/rRsNZIzjCREmHPNPKO5YpWw3dnQjKTlJxz+X6tG6ZxtfnIuTdoP6I3DdNCsFf6f2hEvQ== - dependencies: - "@babel/runtime" "^7.20.1" - "@polkadot/api-augment" "9.8.2" - "@polkadot/api-base" "9.8.2" - "@polkadot/api-derive" "9.8.2" - "@polkadot/keyring" "^10.1.12" - "@polkadot/rpc-augment" "9.8.2" - "@polkadot/rpc-core" "9.8.2" - "@polkadot/rpc-provider" "9.8.2" - "@polkadot/types" "9.8.2" - "@polkadot/types-augment" "9.8.2" - "@polkadot/types-codec" "9.8.2" - "@polkadot/types-create" "9.8.2" - "@polkadot/types-known" "9.8.2" - "@polkadot/util" "^10.1.12" - "@polkadot/util-crypto" "^10.1.12" - eventemitter3 "^4.0.7" - rxjs "^7.5.7" - -"@polkadot/apps-config@^0.122.2": - version "0.122.2" - resolved "https://registry.yarnpkg.com/@polkadot/apps-config/-/apps-config-0.122.2.tgz#b15d2dbfc43b0e8bc32fc14bd56b97de3abb83e3" - integrity sha512-EBINVhOe4w5gzjOJ/lIzYJDP1DiZY8SWBf8Jp25DOwdSvUsyV1AYyrGAgmz+kE+jtakEMKOZpXdRt3OwGYLPqw== - dependencies: - "@acala-network/type-definitions" "^4.1.5" - "@babel/runtime" "^7.20.1" - "@bifrost-finance/type-definitions" "1.7.1" - "@crustio/type-definitions" "1.3.0" - "@darwinia/types" "2.8.10" - "@darwinia/types-known" "2.8.10" - "@digitalnative/type-definitions" "1.1.27" - "@docknetwork/node-types" "0.13.0" - "@edgeware/node-types" "3.6.2-wako" - "@equilab/definitions" "1.4.14" - "@interlay/interbtc-types" "1.9.0" - "@kiltprotocol/type-definitions" "^0.2.1" - "@laminar/type-definitions" "0.3.1" - "@logion/node-api" "^0.7.0" - "@mangata-finance/types" "^0.9.0" - "@metaverse-network-sdk/type-definitions" "^0.0.1-13" - "@parallel-finance/type-definitions" "1.7.13" - "@phala/typedefs" "0.2.32" - "@polkadot/api" "^9.7.1" - "@polkadot/api-derive" "^9.7.1" - "@polkadot/networks" "^10.1.11" - "@polkadot/types" "^9.7.1" - "@polkadot/util" "^10.1.11" - "@polkadot/x-fetch" "^10.1.11" - "@polymathnetwork/polymesh-types" "0.0.2" - "@snowfork/snowbridge-types" "0.2.7" - "@sora-substrate/type-definitions" "1.10.21" - "@subsocial/definitions" "^0.7.8-dev.0" - "@unique-nft/opal-testnet-types" "930.31.0" - "@unique-nft/quartz-mainnet-types" "930.31.0" - "@unique-nft/unique-mainnet-types" "930.31.0" - "@zeitgeistpm/type-defs" "0.9.0" - "@zeroio/type-definitions" "0.0.14" - lodash "^4.17.21" - moonbeam-types-bundle "2.0.9" - pontem-types-bundle "1.0.15" - rxjs "^7.5.7" - -"@polkadot/extension-dapp@0.44.1": - version "0.44.1" - resolved "https://registry.yarnpkg.com/@polkadot/extension-dapp/-/extension-dapp-0.44.1.tgz#bbde17799efba9bb889f97575e69814263a7ed59" - integrity sha512-NPMiww/JxUra8OPYfYlw45r5bjl/mh1kvuDqdmDdYCLUCM00g/oynEEL8GraKUCRkZ/0K6CepldDAmIzgv45hw== - dependencies: - "@babel/runtime" "^7.18.3" - "@polkadot/extension-inject" "^0.44.1" - "@polkadot/util" "^9.4.1" - "@polkadot/util-crypto" "^9.4.1" - -"@polkadot/extension-inject@^0.44.1": - version "0.44.1" - resolved "https://registry.yarnpkg.com/@polkadot/extension-inject/-/extension-inject-0.44.1.tgz#1bd5239507f308390b1e28d6c971264cee4b9ca2" - integrity sha512-CA12DdJXtNSaYMvwZLN3iz4qCRBQrIe+KtUFO/Mh5pbbRCn0xOl4qP3JlgT2xysM1u4PLv+RC5KMktx1gnGIUQ== - dependencies: - "@babel/runtime" "^7.18.3" - "@polkadot/rpc-provider" "^8.7.1" - "@polkadot/types" "^8.7.1" - "@polkadot/util" "^9.4.1" - "@polkadot/util-crypto" "^9.4.1" - "@polkadot/x-global" "^9.4.1" - -"@polkadot/keyring@^10.1.1", "@polkadot/keyring@^10.1.12": - version "10.1.12" - resolved "https://registry.yarnpkg.com/@polkadot/keyring/-/keyring-10.1.12.tgz#01480b10c825d2db7e180968baeb51950ea879e4" - integrity sha512-zwTWOa+Glg0NT+EoOOjg9U6dotGPxDpr8xdpkg9EXcyKMa5Pw7alrUg+V3tC+5Ttu7dsZRCJKW8jEshCekulWA== - dependencies: - "@babel/runtime" "^7.20.1" - "@polkadot/util" "10.1.12" - "@polkadot/util-crypto" "10.1.12" - -"@polkadot/keyring@^10.1.6": - version "10.1.9" - resolved "https://registry.yarnpkg.com/@polkadot/keyring/-/keyring-10.1.9.tgz#56c4e31e8cdb9ebcdf992c19603ee53a4c90451c" - integrity sha512-oUYhvfOCzyMA+SJ+O5BasQYYalmMkVBRHADASFNAxVBMbOl1DKNQx3tgpUZlbA95UzRPjKbV0RplhAIzb5CzVQ== - dependencies: - "@babel/runtime" "^7.19.0" - "@polkadot/util" "10.1.9" - "@polkadot/util-crypto" "10.1.9" - -"@polkadot/keyring@^10.2.1": - version "10.2.1" - resolved "https://registry.yarnpkg.com/@polkadot/keyring/-/keyring-10.2.1.tgz#692d4e24dcbbe294b6945640802fc924ea20348e" - integrity sha512-84/zzxDZANQ4AfsCT1vrjX3I23/mj9WUWl1F7q9ruK6UBFyGsl46Y3ABOopFHij9UXhppndhB65IeDnqoOKqxQ== - dependencies: - "@babel/runtime" "^7.20.6" - "@polkadot/util" "10.2.1" - "@polkadot/util-crypto" "10.2.1" - -"@polkadot/keyring@^6.9.1": - version "6.11.1" - resolved "https://registry.yarnpkg.com/@polkadot/keyring/-/keyring-6.11.1.tgz#2510c349c965c74cc2f108f114f1048856940604" - integrity sha512-rW8INl7pO6Dmaffd6Df1yAYCRWa2RmWQ0LGfJeA/M6seVIkI6J3opZqAd4q2Op+h9a7z4TESQGk8yggOEL+Csg== - dependencies: - "@babel/runtime" "^7.14.6" - "@polkadot/util" "6.11.1" - "@polkadot/util-crypto" "6.11.1" - -"@polkadot/keyring@^7.4.1": - version "7.9.2" - resolved "https://registry.yarnpkg.com/@polkadot/keyring/-/keyring-7.9.2.tgz#1f5bf6b7bdb5942d275aebf72d4ed98abe874fa8" - integrity sha512-6UGoIxhiTyISkYEZhUbCPpgVxaneIfb/DBVlHtbvaABc8Mqh1KuqcTIq19Mh9wXlBuijl25rw4lUASrE/9sBqg== - dependencies: - "@babel/runtime" "^7.16.3" - "@polkadot/util" "7.9.2" - "@polkadot/util-crypto" "7.9.2" - -"@polkadot/keyring@^8.2.2", "@polkadot/keyring@^8.7.1": - version "8.7.1" - resolved "https://registry.yarnpkg.com/@polkadot/keyring/-/keyring-8.7.1.tgz#07cf6d6ee351dcf70fbf965b1d6d96c5025ae1b8" - integrity sha512-t6ZgQVC+nQT7XwbWtEhkDpiAzxKVJw8Xd/gWdww6xIrawHu7jo3SGB4QNdPgkf8TvDHYAAJiupzVQYAlOIq3GA== - dependencies: - "@babel/runtime" "^7.17.8" - "@polkadot/util" "8.7.1" - "@polkadot/util-crypto" "8.7.1" - -"@polkadot/keyring@^9.0.1": - version "9.7.2" - resolved "https://registry.yarnpkg.com/@polkadot/keyring/-/keyring-9.7.2.tgz#3e252bdcabce4f4e74b8fbcd98d77bb0205af21e" - integrity sha512-qY5baU1qduwTE04Cyrqtf2pCpsIk7Z5vi45CD9U3cbkKXaJoNUqIpfKoL8Vh/yVJBwhclMdxV9E2rEJs8Iv4bg== - dependencies: - "@babel/runtime" "^7.18.6" - "@polkadot/util" "9.7.2" - "@polkadot/util-crypto" "9.7.2" - -"@polkadot/keyring@^9.4.1": - version "9.4.1" - resolved "https://registry.yarnpkg.com/@polkadot/keyring/-/keyring-9.4.1.tgz#4bc8d1c1962756841742abac0d7e4ef233d9c2a9" - integrity sha512-op6Tj8E9GHeZYvEss38FRUrX+GlBj6qiwF4BlFrAvPqjPnRn8TT9NhRLroiCwvxeNg3uMtEF/5xB+vvdI0I6qw== - dependencies: - "@babel/runtime" "^7.18.3" - "@polkadot/util" "9.4.1" - "@polkadot/util-crypto" "9.4.1" - -"@polkadot/metadata@4.17.1": - version "4.17.1" - resolved "https://registry.yarnpkg.com/@polkadot/metadata/-/metadata-4.17.1.tgz#4da9ee5b2b816493910abfd302a50b58141ceca2" - integrity sha512-219isiCWVfbu5JxZnOPj+cV4T+S0XHS4+Jal3t3xz9y4nbgr+25Pa4KInEsJPx0u8EZAxMeiUCX3vd5U7oe72g== - dependencies: - "@babel/runtime" "^7.14.6" - "@polkadot/types" "4.17.1" - "@polkadot/types-known" "4.17.1" - "@polkadot/util" "^6.11.1" - "@polkadot/util-crypto" "^6.11.1" - -"@polkadot/networks@10.1.12", "@polkadot/networks@^10.1.1", "@polkadot/networks@^10.1.12": - version "10.1.12" - resolved "https://registry.yarnpkg.com/@polkadot/networks/-/networks-10.1.12.tgz#19384b39de8369667312977120916b0bbf379688" - integrity sha512-asobCUdRSsnSIZKoqFsaPPtUPvbaNPh3r1XOvNqgrE65wsoNx15AiuqAigW2RKOoqVrUW7zthRJ5Xmk0MKS9GA== - dependencies: - "@babel/runtime" "^7.20.1" - "@polkadot/util" "10.1.12" - "@substrate/ss58-registry" "^1.34.0" - -"@polkadot/networks@10.1.6": - version "10.1.6" - resolved "https://registry.yarnpkg.com/@polkadot/networks/-/networks-10.1.6.tgz#32749dc6d39a9dd0cbd3af7fea11b1da4a4a63a0" - integrity sha512-NINGTVkvAnrBDXbIdcSJ7gCmtXUB6ybI4TLHY2Tf/57hak+hlyQUoHZdaTzpRYrxZ9xoUUS1K83Lr3wfwMblHA== - dependencies: - "@babel/runtime" "^7.18.9" - "@polkadot/util" "10.1.6" - "@substrate/ss58-registry" "^1.28.0" - -"@polkadot/networks@10.1.9", "@polkadot/networks@^10.1.6": - version "10.1.9" - resolved "https://registry.yarnpkg.com/@polkadot/networks/-/networks-10.1.9.tgz#37e789dd13c683b3e8cdc16edff4983d5f94e28f" - integrity sha512-38bYoXYszJMTRSvhJOkfKXlIyTiFjJ1ZG7cyMYBOi1YtO9USd4lH0gknVfqXa1dyPyNvsOwI5RCKGFjbx2BEAw== - dependencies: - "@babel/runtime" "^7.19.0" - "@polkadot/util" "10.1.9" - "@substrate/ss58-registry" "^1.29.1" - -"@polkadot/networks@10.2.1", "@polkadot/networks@^10.1.11", "@polkadot/networks@^10.2.1": - version "10.2.1" - resolved "https://registry.yarnpkg.com/@polkadot/networks/-/networks-10.2.1.tgz#5095011795afa20291ef3e34a2ad38ed2c63fe09" - integrity sha512-cDZIY4jBo2tlDdSXNbECpuWer0NWlPcJNhHHveTiu2idje2QyIBNxBlAPViNGpz+ScAR0EknEzmQKuHOcSKxzg== - dependencies: - "@babel/runtime" "^7.20.6" - "@polkadot/util" "10.2.1" - "@substrate/ss58-registry" "^1.35.0" - -"@polkadot/networks@6.11.1", "@polkadot/networks@^6.11.1": - version "6.11.1" - resolved "https://registry.yarnpkg.com/@polkadot/networks/-/networks-6.11.1.tgz#8fd189593f6ee4f8bf64378d0aaae09e39a37d35" - integrity sha512-0C6Ha2kvr42se3Gevx6UhHzv3KnPHML0N73Amjwvdr4y0HLZ1Nfw+vcm5yqpz5gpiehqz97XqFrsPRauYdcksQ== - dependencies: - "@babel/runtime" "^7.14.6" - -"@polkadot/networks@7.9.2": - version "7.9.2" - resolved "https://registry.yarnpkg.com/@polkadot/networks/-/networks-7.9.2.tgz#03e3f3ac6bdea177517436537826055df60bcb9a" - integrity sha512-4obI1RdW5/7TFwbwKA9oqw8aggVZ65JAUvIFMd2YmMC2T4+NiZLnok0WhRkhZkUnqjLIHXYNwq7Ho1i39dte0g== - dependencies: - "@babel/runtime" "^7.16.3" - -"@polkadot/networks@8.7.1", "@polkadot/networks@^8.1.2", "@polkadot/networks@^8.7.1": - version "8.7.1" - resolved "https://registry.yarnpkg.com/@polkadot/networks/-/networks-8.7.1.tgz#26c2ec6158c985bb77c510d98a3ab1c7e049f89c" - integrity sha512-8xAmhDW0ry5EKcEjp6VTuwoTm0DdDo/zHsmx88P6sVL87gupuFsL+B6TrsYLl8GcaqxujwrOlKB+CKTUg7qFKg== - dependencies: - "@babel/runtime" "^7.17.8" - "@polkadot/util" "8.7.1" - "@substrate/ss58-registry" "^1.17.0" - -"@polkadot/networks@9.4.1": - version "9.4.1" - resolved "https://registry.yarnpkg.com/@polkadot/networks/-/networks-9.4.1.tgz#acdf3d64421ce0e3d3ba68797fc29a28ee40c185" - integrity sha512-ibH8bZ2/XMXv0XEsP1fGOqNnm2mg1rHo5kHXSJ3QBcZJFh1+xkI4Ovl2xrFfZ+SYATA3Wsl5R6knqimk2EqyJQ== - dependencies: - "@babel/runtime" "^7.18.3" - "@polkadot/util" "9.4.1" - "@substrate/ss58-registry" "^1.22.0" - -"@polkadot/networks@9.7.2", "@polkadot/networks@^9.0.1": - version "9.7.2" - resolved "https://registry.yarnpkg.com/@polkadot/networks/-/networks-9.7.2.tgz#9064f0578b293245bee263367d6f1674eb06e506" - integrity sha512-oMAdF8Y9CLBI0EUZBcycHcvbQQdbkJHevPJ/lwnZXJTaueXuav/Xm2yiFj5J3V8meIjLocURlMawgsAVItXOBQ== - dependencies: - "@babel/runtime" "^7.18.6" - "@polkadot/util" "9.7.2" - "@substrate/ss58-registry" "^1.23.0" - -"@polkadot/rpc-augment@7.15.1": - version "7.15.1" - resolved "https://registry.yarnpkg.com/@polkadot/rpc-augment/-/rpc-augment-7.15.1.tgz#391a42a9c3e553335a2a544598a717b47654ad6e" - integrity sha512-sK0+mphN7nGz/eNPsshVi0qd0+N0Pqxuebwc1YkUGP0f9EkDxzSGp6UjGcSwWVaAtk9WZZ1MpK1Jwb/2GrKV7Q== - dependencies: - "@babel/runtime" "^7.17.8" - "@polkadot/rpc-core" "7.15.1" - "@polkadot/types" "7.15.1" - "@polkadot/types-codec" "7.15.1" - "@polkadot/util" "^8.7.1" - -"@polkadot/rpc-augment@8.1.1": - version "8.1.1" - resolved "https://registry.yarnpkg.com/@polkadot/rpc-augment/-/rpc-augment-8.1.1.tgz#e23df5014fc16958ffd1b7a95659ef8993da5004" - integrity sha512-4bgtO6myo74AXXwOvH1BvgVd7LTyiu/KqiknxexYBDF1w8HfDfbVGkTWTan/WRfQhCOSfbidxbY5Y+Ef4AHYDA== - dependencies: - "@babel/runtime" "^7.17.9" - "@polkadot/rpc-core" "8.1.1" - "@polkadot/types" "8.1.1" - "@polkadot/types-codec" "8.1.1" - "@polkadot/util" "^9.0.1" - -"@polkadot/rpc-augment@8.14.1": - version "8.14.1" - resolved "https://registry.yarnpkg.com/@polkadot/rpc-augment/-/rpc-augment-8.14.1.tgz#1b55c9e66a8aaafb76e6ed6a37b0682a4331b2a7" - integrity sha512-0dIsNVIMeCp0kV7+Obz0Odt6K32Ka2ygwhiV5jhhJthy8GJBPo94mKDed5gzln3Dgl2LEdJJt1h/pgCx4a2i4A== - dependencies: - "@babel/runtime" "^7.18.9" - "@polkadot/rpc-core" "8.14.1" - "@polkadot/types" "8.14.1" - "@polkadot/types-codec" "8.14.1" - "@polkadot/util" "^10.1.1" - -"@polkadot/rpc-augment@9.10.2": - version "9.10.2" - resolved "https://registry.yarnpkg.com/@polkadot/rpc-augment/-/rpc-augment-9.10.2.tgz#5650aa118d39d0c4b17425a9b327354f7bbf99e5" - integrity sha512-LrGzpSdkqXltZDwuBeBBMev68eVVN1GpgV4auEAytgDYYcjI9XDaeLZm7vUVx9aBO8OYz9hQZeHrWrab/FaKmg== - dependencies: - "@babel/runtime" "^7.20.6" - "@polkadot/rpc-core" "9.10.2" - "@polkadot/types" "9.10.2" - "@polkadot/types-codec" "9.10.2" - "@polkadot/util" "^10.2.1" - -"@polkadot/rpc-augment@9.8.1": - version "9.8.1" - resolved "https://registry.yarnpkg.com/@polkadot/rpc-augment/-/rpc-augment-9.8.1.tgz#dbec6c6c959d785e1e40543ccc63d30f6f51baa8" - integrity sha512-ykBtSvfbET3yTs/kSyAj2g1fn7x4fqBrmsTyzPp9Lq6U86Iwg37UGKpbN3mzDeZRDQE/ujOktMgjoXRL/NAwXw== - dependencies: - "@babel/runtime" "^7.20.1" - "@polkadot/rpc-core" "9.8.1" - "@polkadot/types" "9.8.1" - "@polkadot/types-codec" "9.8.1" - "@polkadot/util" "^10.1.12" - -"@polkadot/rpc-augment@9.8.2": - version "9.8.2" - resolved "https://registry.yarnpkg.com/@polkadot/rpc-augment/-/rpc-augment-9.8.2.tgz#ce22e2126cdc51ee746e32805954cb75bc7c0fa2" - integrity sha512-UkoE2g8RHS5F8RvwDLAcDLapffx9phsyd574YP1wGRfzQ3nGFCVWcvSP5mwQH/uaa8fKKWF6a8X3EXDEWRXr7g== - dependencies: - "@babel/runtime" "^7.20.1" - "@polkadot/rpc-core" "9.8.2" - "@polkadot/types" "9.8.2" - "@polkadot/types-codec" "9.8.2" - "@polkadot/util" "^10.1.12" - -"@polkadot/rpc-core@7.15.1": - version "7.15.1" - resolved "https://registry.yarnpkg.com/@polkadot/rpc-core/-/rpc-core-7.15.1.tgz#47855cf05bd2f8dbf87d9f1f77343114e61c664a" - integrity sha512-4Sb0e0PWmarCOizzxQAE1NQSr5z0n+hdkrq3+aPohGu9Rh4PodG+OWeIBy7Ov/3GgdhNQyBLG+RiVtliXecM3g== - dependencies: - "@babel/runtime" "^7.17.8" - "@polkadot/rpc-augment" "7.15.1" - "@polkadot/rpc-provider" "7.15.1" - "@polkadot/types" "7.15.1" - "@polkadot/util" "^8.7.1" - rxjs "^7.5.5" - -"@polkadot/rpc-core@8.1.1": - version "8.1.1" - resolved "https://registry.yarnpkg.com/@polkadot/rpc-core/-/rpc-core-8.1.1.tgz#aa941ddb0ddf79aa8cd42205700fda0aaf203708" - integrity sha512-7CL3eQnm1V0TLSpRFBHPFLdbjSg4nYePxaI4Ko9Mytvo5l7vNcPi2/qb3aaoGr+SbGRdPj0e8FstbBEYc10lSQ== - dependencies: - "@babel/runtime" "^7.17.9" - "@polkadot/rpc-augment" "8.1.1" - "@polkadot/rpc-provider" "8.1.1" - "@polkadot/types" "8.1.1" - "@polkadot/util" "^9.0.1" - rxjs "^7.5.5" - -"@polkadot/rpc-core@8.14.1", "@polkadot/rpc-core@^8.5.1": - version "8.14.1" - resolved "https://registry.yarnpkg.com/@polkadot/rpc-core/-/rpc-core-8.14.1.tgz#0b9a408a03ecde820d0d55287efbfb4cb95b537a" - integrity sha512-deQ8Ob59ao/1fZQdaVtFjYR/HCBdxSYvQGt7/alBu1Uig9Sahx9oKcMkU5rWY36XqGZYos4zLay98W2hDlf+6Q== - dependencies: - "@babel/runtime" "^7.18.9" - "@polkadot/rpc-augment" "8.14.1" - "@polkadot/rpc-provider" "8.14.1" - "@polkadot/types" "8.14.1" - "@polkadot/util" "^10.1.1" - rxjs "^7.5.6" - -"@polkadot/rpc-core@9.10.2": - version "9.10.2" - resolved "https://registry.yarnpkg.com/@polkadot/rpc-core/-/rpc-core-9.10.2.tgz#72362d26012c53397c1079912d5d4aacf910a650" - integrity sha512-qr+q2R3YeRBC++bYxK292jb6t9/KXeLoRheW5z7LbYyre3J60vZPN7WxPxbwm+iCGk1VtvH80Dv1OSCoVC+7hA== - dependencies: - "@babel/runtime" "^7.20.6" - "@polkadot/rpc-augment" "9.10.2" - "@polkadot/rpc-provider" "9.10.2" - "@polkadot/types" "9.10.2" - "@polkadot/util" "^10.2.1" - rxjs "^7.6.0" - -"@polkadot/rpc-core@9.8.1": - version "9.8.1" - resolved "https://registry.yarnpkg.com/@polkadot/rpc-core/-/rpc-core-9.8.1.tgz#bf9be52adf708a3ed7050e5c148c1d1a585e50d5" - integrity sha512-68ETNY32/kpFWOJOk7e8SP8Ng1aLKSw7uCKQFeeJI85XKntFhS1H9LUNZesTRpCOC51O/DlERqUyE6VIhyUC4A== - dependencies: - "@babel/runtime" "^7.20.1" - "@polkadot/rpc-augment" "9.8.1" - "@polkadot/rpc-provider" "9.8.1" - "@polkadot/types" "9.8.1" - "@polkadot/util" "^10.1.12" - rxjs "^7.5.7" - -"@polkadot/rpc-core@9.8.2": - version "9.8.2" - resolved "https://registry.yarnpkg.com/@polkadot/rpc-core/-/rpc-core-9.8.2.tgz#4347cda0dd57850a23a35155198e0d2233b3d433" - integrity sha512-hgJ2QQfG+WL7CeSqSWMmfsvEQ/avt7aue+06oW8YOvLufQJHL1Kol8kKZJvcz0Kyu3Of8VQQPJrBdU9ykgVFgg== - dependencies: - "@babel/runtime" "^7.20.1" - "@polkadot/rpc-augment" "9.8.2" - "@polkadot/rpc-provider" "9.8.2" - "@polkadot/types" "9.8.2" - "@polkadot/util" "^10.1.12" - rxjs "^7.5.7" - -"@polkadot/rpc-provider@7.15.1": - version "7.15.1" - resolved "https://registry.yarnpkg.com/@polkadot/rpc-provider/-/rpc-provider-7.15.1.tgz#a213de907a6f4f480c3c819aa95e4e60d4247f84" - integrity sha512-n0RWfSaD/r90JXeJkKry1aGZwJeBUUiMpXUQ9Uvp6DYBbYEDs0fKtWLpdT3PdFrMbe5y3kwQmNLxwe6iF4+mzg== - dependencies: - "@babel/runtime" "^7.17.8" - "@polkadot/keyring" "^8.7.1" - "@polkadot/types" "7.15.1" - "@polkadot/types-support" "7.15.1" - "@polkadot/util" "^8.7.1" - "@polkadot/util-crypto" "^8.7.1" - "@polkadot/x-fetch" "^8.7.1" - "@polkadot/x-global" "^8.7.1" - "@polkadot/x-ws" "^8.7.1" - "@substrate/connect" "0.7.0-alpha.0" - eventemitter3 "^4.0.7" - mock-socket "^9.1.2" - nock "^13.2.4" - -"@polkadot/rpc-provider@8.1.1": - version "8.1.1" - resolved "https://registry.yarnpkg.com/@polkadot/rpc-provider/-/rpc-provider-8.1.1.tgz#bd0e57e8fd15e9bac87bbf667fb484036cf7595d" - integrity sha512-PFQYhVDxayQ8BP6xk2ZFvrfG/wWZ3BIEUesdCiZU8Nb1UbTXMQQbLvfI+14Q46Z426MR9UNHxrzi4AlKbPWqCQ== - dependencies: - "@babel/runtime" "^7.17.9" - "@polkadot/keyring" "^9.0.1" - "@polkadot/types" "8.1.1" - "@polkadot/types-support" "8.1.1" - "@polkadot/util" "^9.0.1" - "@polkadot/util-crypto" "^9.0.1" - "@polkadot/x-fetch" "^9.0.1" - "@polkadot/x-global" "^9.0.1" - "@polkadot/x-ws" "^9.0.1" - "@substrate/connect" "0.7.2" - eventemitter3 "^4.0.7" - mock-socket "^9.1.2" - nock "^13.2.4" - -"@polkadot/rpc-provider@8.14.1": - version "8.14.1" - resolved "https://registry.yarnpkg.com/@polkadot/rpc-provider/-/rpc-provider-8.14.1.tgz#d318a3cb3c71410cea6a9c2c609d39e3fc62d8bb" - integrity sha512-pAUSHZiSWLhBSYf4LmLc8iCaeqTu7Ajn8AzyqxvZDHGnIrzV5M7eTjpNDP84qno6jWRHKQ/IILr62hausEmS5w== - dependencies: - "@babel/runtime" "^7.18.9" - "@polkadot/keyring" "^10.1.1" - "@polkadot/types" "8.14.1" - "@polkadot/types-support" "8.14.1" - "@polkadot/util" "^10.1.1" - "@polkadot/util-crypto" "^10.1.1" - "@polkadot/x-fetch" "^10.1.1" - "@polkadot/x-global" "^10.1.1" - "@polkadot/x-ws" "^10.1.1" - "@substrate/connect" "0.7.9" - eventemitter3 "^4.0.7" - mock-socket "^9.1.5" - nock "^13.2.9" - -"@polkadot/rpc-provider@9.10.2": - version "9.10.2" - resolved "https://registry.yarnpkg.com/@polkadot/rpc-provider/-/rpc-provider-9.10.2.tgz#83c8e114b3aad75eedaf98a374bc77a2b8cc1dbc" - integrity sha512-mm8l1uZ7DOrsMUN+DELS8apyZVVNIy/SrqEBjHZeZ0AA9noAEbH4ubxR375lG/T32+T97mFudv1rxRnEwXqByg== - dependencies: - "@babel/runtime" "^7.20.6" - "@polkadot/keyring" "^10.2.1" - "@polkadot/types" "9.10.2" - "@polkadot/types-support" "9.10.2" - "@polkadot/util" "^10.2.1" - "@polkadot/util-crypto" "^10.2.1" - "@polkadot/x-fetch" "^10.2.1" - "@polkadot/x-global" "^10.2.1" - "@polkadot/x-ws" "^10.2.1" - "@substrate/connect" "0.7.17" - eventemitter3 "^4.0.7" - mock-socket "^9.1.5" - nock "^13.2.9" - -"@polkadot/rpc-provider@9.8.1": - version "9.8.1" - resolved "https://registry.yarnpkg.com/@polkadot/rpc-provider/-/rpc-provider-9.8.1.tgz#4c557544201d5964576ee15a8775d1126865d3d2" - integrity sha512-QEPRrvhELZDfX0RNFHUefoik0v2pXpRnBNIR1Uj1SJ1ny9OYk1wHGY1dw3UrKABnBqFDs320IZ61TBy6ZTFsnA== - dependencies: - "@babel/runtime" "^7.20.1" - "@polkadot/keyring" "^10.1.12" - "@polkadot/types" "9.8.1" - "@polkadot/types-support" "9.8.1" - "@polkadot/util" "^10.1.12" - "@polkadot/util-crypto" "^10.1.12" - "@polkadot/x-fetch" "^10.1.12" - "@polkadot/x-global" "^10.1.12" - "@polkadot/x-ws" "^10.1.12" - "@substrate/connect" "0.7.16" - eventemitter3 "^4.0.7" - mock-socket "^9.1.5" - nock "^13.2.9" - -"@polkadot/rpc-provider@9.8.2": - version "9.8.2" - resolved "https://registry.yarnpkg.com/@polkadot/rpc-provider/-/rpc-provider-9.8.2.tgz#86caba499e414e0daed83c20262692f6d87c957c" - integrity sha512-vyc118s/d3Xs0JNLr29d5NeqvMDspWzQ5KNvFibnKKGst1BOzIHb0BZLpMw3PCXqlCdDtUK86CTx9aW2kzYOCg== - dependencies: - "@babel/runtime" "^7.20.1" - "@polkadot/keyring" "^10.1.12" - "@polkadot/types" "9.8.2" - "@polkadot/types-support" "9.8.2" - "@polkadot/util" "^10.1.12" - "@polkadot/util-crypto" "^10.1.12" - "@polkadot/x-fetch" "^10.1.12" - "@polkadot/x-global" "^10.1.12" - "@polkadot/x-ws" "^10.1.12" - "@substrate/connect" "0.7.16" - eventemitter3 "^4.0.7" - mock-socket "^9.1.5" - nock "^13.2.9" - -"@polkadot/rpc-provider@^8.7.1": - version "8.7.1" - resolved "https://registry.yarnpkg.com/@polkadot/rpc-provider/-/rpc-provider-8.7.1.tgz#f7278276e1ff5487d26c11499b1ef90101641740" - integrity sha512-yvmv4OS/TWQvD022Hpx6/FohKSxthML20FD0scJ5sYcz5WHExANUDXi2cfF8oVCECg5jcVZeATIov64oFEjLTw== - dependencies: - "@babel/runtime" "^7.18.3" - "@polkadot/keyring" "^9.4.1" - "@polkadot/types" "8.7.1" - "@polkadot/types-support" "8.7.1" - "@polkadot/util" "^9.4.1" - "@polkadot/util-crypto" "^9.4.1" - "@polkadot/x-fetch" "^9.4.1" - "@polkadot/x-global" "^9.4.1" - "@polkadot/x-ws" "^9.4.1" - "@substrate/connect" "0.7.5" - eventemitter3 "^4.0.7" - mock-socket "^9.1.4" - nock "^13.2.6" - -"@polkadot/types-augment@7.15.1": - version "7.15.1" - resolved "https://registry.yarnpkg.com/@polkadot/types-augment/-/types-augment-7.15.1.tgz#437047f961b8d29e5ffd4fd59cd35f0e6374750b" - integrity sha512-aqm7xT/66TCna0I2utpIekoquKo0K5pnkA/7WDzZ6gyD8he2h0IXfe8xWjVmuyhjxrT/C/7X1aUF2Z0xlOCwzQ== - dependencies: - "@babel/runtime" "^7.17.8" - "@polkadot/types" "7.15.1" - "@polkadot/types-codec" "7.15.1" - "@polkadot/util" "^8.7.1" - -"@polkadot/types-augment@8.1.1": - version "8.1.1" - resolved "https://registry.yarnpkg.com/@polkadot/types-augment/-/types-augment-8.1.1.tgz#f62107ca46080b9ddfc55f4acda30265dcf033ff" - integrity sha512-JyJigD/rH33uDKPRF8u2rMRmxkh/brM/AkD+pOH5ZO6AfcQ3mNsFEvM5OZ+Wx2vq6+vX3oH922wjK3d3/ILkpQ== - dependencies: - "@babel/runtime" "^7.17.9" - "@polkadot/types" "8.1.1" - "@polkadot/types-codec" "8.1.1" - "@polkadot/util" "^9.0.1" - -"@polkadot/types-augment@8.14.1": - version "8.14.1" - resolved "https://registry.yarnpkg.com/@polkadot/types-augment/-/types-augment-8.14.1.tgz#6868d7f1321f6cd2b5028374bc496e5174fcf15e" - integrity sha512-Xa4TUFqyZT+IJ6pBSwDjWcF42u/E34OyC+gbs5Z2vWQ4EzSDkq4xNoUKjJlEEgTemsD9lhPOIc4jvqTCefwxEw== - dependencies: - "@babel/runtime" "^7.18.9" - "@polkadot/types" "8.14.1" - "@polkadot/types-codec" "8.14.1" - "@polkadot/util" "^10.1.1" - -"@polkadot/types-augment@8.7.1": - version "8.7.1" - resolved "https://registry.yarnpkg.com/@polkadot/types-augment/-/types-augment-8.7.1.tgz#64b31c8799413a35a7d4ebbfdd9ee76ef1199209" - integrity sha512-68civ9XiRjAk4sGUwuM6h3Bj1tLqorM8AcKaQer6a5RwIF2j3mh16b/b4omTDXoM1hhwCR3THIGQ25FxulD8WQ== - dependencies: - "@babel/runtime" "^7.18.3" - "@polkadot/types" "8.7.1" - "@polkadot/types-codec" "8.7.1" - "@polkadot/util" "^9.4.1" - -"@polkadot/types-augment@9.10.2": - version "9.10.2" - resolved "https://registry.yarnpkg.com/@polkadot/types-augment/-/types-augment-9.10.2.tgz#2dce4ea8a2879d248339ad377ff5479fae884cd5" - integrity sha512-z0M3bAwGi0pGS3ieXyiJZLzDEc5yBvlqaZvaAbf2r+vto83SylhbjjG1wX8ARI5hqptBUWqS9BssUFH0q6l4sg== - dependencies: - "@babel/runtime" "^7.20.6" - "@polkadot/types" "9.10.2" - "@polkadot/types-codec" "9.10.2" - "@polkadot/util" "^10.2.1" - -"@polkadot/types-augment@9.8.1": - version "9.8.1" - resolved "https://registry.yarnpkg.com/@polkadot/types-augment/-/types-augment-9.8.1.tgz#7f2ee7b772854db5d13e0d766d24d7620c7ec13a" - integrity sha512-EvCQvIYFAQzXLZn5mzT6JOsynYMcDgvZqZ9itWmxnHdnQ0LfwaM6nS0fPVMjDfWmcrMjCKRJJe37eO0sNXl8MQ== - dependencies: - "@babel/runtime" "^7.20.1" - "@polkadot/types" "9.8.1" - "@polkadot/types-codec" "9.8.1" - "@polkadot/util" "^10.1.12" - -"@polkadot/types-augment@9.8.2", "@polkadot/types-augment@^9.8.1": - version "9.8.2" - resolved "https://registry.yarnpkg.com/@polkadot/types-augment/-/types-augment-9.8.2.tgz#fc69968fafe2f28954f95aacca820c9c99ae1a2b" - integrity sha512-x2bzcDIdGL5GYA5XkzE/HiVKf/Rfw19N4BBTt0kFMZOn11oPluD1XU+dijg5vDFf1p45935E4JRMeGEV4S1MLQ== - dependencies: - "@babel/runtime" "^7.20.1" - "@polkadot/types" "9.8.2" - "@polkadot/types-codec" "9.8.2" - "@polkadot/util" "^10.1.12" - -"@polkadot/types-codec@7.15.1": - version "7.15.1" - resolved "https://registry.yarnpkg.com/@polkadot/types-codec/-/types-codec-7.15.1.tgz#c0155867efd3ae35e15fea6a8aab49c2c63988fa" - integrity sha512-nI11dT7FGaeDd/fKPD8iJRFGhosOJoyjhZ0gLFFDlKCaD3AcGBRTTY8HFJpP/5QXXhZzfZsD93fVKrosnegU0Q== - dependencies: - "@babel/runtime" "^7.17.8" - "@polkadot/util" "^8.7.1" - -"@polkadot/types-codec@8.1.1": - version "8.1.1" - resolved "https://registry.yarnpkg.com/@polkadot/types-codec/-/types-codec-8.1.1.tgz#f45c40953169c28e406fbdb0b7306f90b858861a" - integrity sha512-JJkSYJrkSjNZYIWAqpihgtMKbTfk2r9J6eHeESiWFYhce61o2x1ylyzedaZkvoxD9hVhb7l94ulrHZKtlJKBFQ== - dependencies: - "@babel/runtime" "^7.17.9" - "@polkadot/util" "^9.0.1" - -"@polkadot/types-codec@8.14.1": - version "8.14.1" - resolved "https://registry.yarnpkg.com/@polkadot/types-codec/-/types-codec-8.14.1.tgz#b5342fa38e17eb1183434981e23334d5bd1ecd2e" - integrity sha512-y6YDN4HwvEgSWlgrEV04QBBxDxES1cTuUQFzZJzOTuZCWpA371Mdj3M9wYxGXMnj0wa+rCQGECHPZZaNxBMiKg== - dependencies: - "@babel/runtime" "^7.18.9" - "@polkadot/util" "^10.1.1" - "@polkadot/x-bigint" "^10.1.1" - -"@polkadot/types-codec@8.7.1": - version "8.7.1" - resolved "https://registry.yarnpkg.com/@polkadot/types-codec/-/types-codec-8.7.1.tgz#c734255ad5486c0507ffa311e0146007e6504f33" - integrity sha512-gnDNYKmrGQvWpPjjk9Q4EUNMvUNGvBwCO1+TfUz0c7SHljFPi2Y5Ktt4eMXdMw62v/EBjvmcbzmwso+tucDm6g== - dependencies: - "@babel/runtime" "^7.18.3" - "@polkadot/util" "^9.4.1" - -"@polkadot/types-codec@9.10.2": - version "9.10.2" - resolved "https://registry.yarnpkg.com/@polkadot/types-codec/-/types-codec-9.10.2.tgz#7f0e33c33292bdfcd959945b2427742b941df712" - integrity sha512-zQOPzxq2N6PUP6Gkxc3OVT7Ub8AD3qC0PBeCnc/fhKjgX3CoKQK4TC6tDL8pEaaIVFh4LOHlHvhWJhqaUNe95A== - dependencies: - "@babel/runtime" "^7.20.6" - "@polkadot/util" "^10.2.1" - "@polkadot/x-bigint" "^10.2.1" - -"@polkadot/types-codec@9.8.1": - version "9.8.1" - resolved "https://registry.yarnpkg.com/@polkadot/types-codec/-/types-codec-9.8.1.tgz#08ff93aa23424a3fba4e4764f6e75180970b97ad" - integrity sha512-ZN6zyRWzvfD6KrOjieVODDlJ+yMFRNPhX/PC2OFY1mpuXgETs5PM9YqIxkc3pmVi+UI4KfL7T516C2NC63LEsA== - dependencies: - "@babel/runtime" "^7.20.1" - "@polkadot/util" "^10.1.12" - "@polkadot/x-bigint" "^10.1.12" - -"@polkadot/types-codec@9.8.2": - version "9.8.2" - resolved "https://registry.yarnpkg.com/@polkadot/types-codec/-/types-codec-9.8.2.tgz#c95e165140e90a03695433f175a90805915ea7e9" - integrity sha512-3ZtPE4gneR0OMR91dnBhTM6NKUHhvdldXcHdl5lT6kMI1gbfsekioJFKgNoM1bcucdno8sFtaGIeknlAney+CA== - dependencies: - "@babel/runtime" "^7.20.1" - "@polkadot/util" "^10.1.12" - "@polkadot/x-bigint" "^10.1.12" - -"@polkadot/types-create@7.15.1": - version "7.15.1" - resolved "https://registry.yarnpkg.com/@polkadot/types-create/-/types-create-7.15.1.tgz#ec4cafa314a82a25a109f0f52207e9169fc9b003" - integrity sha512-+HiaHn7XOwP0kv/rVdORlVkNuMoxuvt+jd67A/CeEreJiXqRLu+S61Mdk7wi6719PTaOal1hTDFfyGrtUd8FSQ== - dependencies: - "@babel/runtime" "^7.17.8" - "@polkadot/types-codec" "7.15.1" - "@polkadot/util" "^8.7.1" - -"@polkadot/types-create@8.1.1": - version "8.1.1" - resolved "https://registry.yarnpkg.com/@polkadot/types-create/-/types-create-8.1.1.tgz#7e9663b1d8abf8caedb71482c1370e4438eee858" - integrity sha512-cL+CpLkHiTxRH67oHiCeunant9JpVvmtJZh+t/NZZypjRkH7YVOpKj643vkiP2m02259N2BzYTR6CEQP8QZGGQ== - dependencies: - "@babel/runtime" "^7.17.9" - "@polkadot/types-codec" "8.1.1" - "@polkadot/util" "^9.0.1" - -"@polkadot/types-create@8.14.1": - version "8.14.1" - resolved "https://registry.yarnpkg.com/@polkadot/types-create/-/types-create-8.14.1.tgz#46af719c33581576eed03658baf3aaa932b3d89c" - integrity sha512-fb9yyblj5AYAPzeCIq0kYSfzDxRDi/0ud9gN2UzB3H7M/O4n2mPC1vD4UOLF+B7l9QzCrt4e+k+/riGp7GfvyA== - dependencies: - "@babel/runtime" "^7.18.9" - "@polkadot/types-codec" "8.14.1" - "@polkadot/util" "^10.1.1" - -"@polkadot/types-create@8.7.1": - version "8.7.1" - resolved "https://registry.yarnpkg.com/@polkadot/types-create/-/types-create-8.7.1.tgz#c04ce1b008e9cfa7954e583ac9689ad964c892d1" - integrity sha512-PIkIPf9jzrEqiAs64uP+rHYec/4jLcNGG0rIM9bE+flbN5h4RzgiTnsnnfx6lGrtqM6lTzggpwP1C+wPaLNj1g== +"@polkadot/extension-inject@^0.44.1": + version "0.44.1" + resolved "https://registry.yarnpkg.com/@polkadot/extension-inject/-/extension-inject-0.44.1.tgz#1bd5239507f308390b1e28d6c971264cee4b9ca2" + integrity sha512-CA12DdJXtNSaYMvwZLN3iz4qCRBQrIe+KtUFO/Mh5pbbRCn0xOl4qP3JlgT2xysM1u4PLv+RC5KMktx1gnGIUQ== dependencies: "@babel/runtime" "^7.18.3" - "@polkadot/types-codec" "8.7.1" + "@polkadot/rpc-provider" "^8.7.1" + "@polkadot/types" "^8.7.1" "@polkadot/util" "^9.4.1" + "@polkadot/util-crypto" "^9.4.1" + "@polkadot/x-global" "^9.4.1" -"@polkadot/types-create@9.10.2": - version "9.10.2" - resolved "https://registry.yarnpkg.com/@polkadot/types-create/-/types-create-9.10.2.tgz#eb7dbf5f50eb4d01a965347d324de26a679a25e3" - integrity sha512-U6wDaJe8tZmt0WibxWeDFYVKfvOYa2su8xOwg8HTRraijF6k0/OMugb15bpjEkG6RZ1qg1L7oKrKghugVbRDGQ== - dependencies: - "@babel/runtime" "^7.20.6" - "@polkadot/types-codec" "9.10.2" - "@polkadot/util" "^10.2.1" - -"@polkadot/types-create@9.8.1": - version "9.8.1" - resolved "https://registry.yarnpkg.com/@polkadot/types-create/-/types-create-9.8.1.tgz#d85318f30ff88f6f1055cd0296ca01db5b8063f3" - integrity sha512-275aFbTaK4Hpx3sflSS7i0jYfzwu3GfI41w49XF94UlevY6XtJr7pW3asMDVgOMkchbHAQ1DJqGXPm00GOLc6A== - dependencies: - "@babel/runtime" "^7.20.1" - "@polkadot/types-codec" "9.8.1" - "@polkadot/util" "^10.1.12" - -"@polkadot/types-create@9.8.2": - version "9.8.2" - resolved "https://registry.yarnpkg.com/@polkadot/types-create/-/types-create-9.8.2.tgz#d9cdd5225714c70b8bd686443f3553c023e55eed" - integrity sha512-4mn+uu0pjh29+XR6K3bMZN6rZLWIW+R5wCpoJVOkp2p4WTVWfVTwI23mQhOpPfPDwx4N7bQfihlm4wRwSXsRYQ== +"@polkadot/keyring@^10.1.6", "@polkadot/keyring@^10.2.1", "@polkadot/keyring@^10.4.2": + version "10.4.2" + resolved "https://registry.yarnpkg.com/@polkadot/keyring/-/keyring-10.4.2.tgz#793377fdb9076df0af771df11388faa6be03c70d" + integrity sha512-7iHhJuXaHrRTG6cJDbZE9G+c1ts1dujp0qbO4RfAPmT7YUvphHvAtCKueN9UKPz5+TYDL+rP/jDEaSKU8jl/qQ== dependencies: - "@babel/runtime" "^7.20.1" - "@polkadot/types-codec" "9.8.2" - "@polkadot/util" "^10.1.12" + "@babel/runtime" "^7.20.13" + "@polkadot/util" "10.4.2" + "@polkadot/util-crypto" "10.4.2" -"@polkadot/types-known@4.17.1": - version "4.17.1" - resolved "https://registry.yarnpkg.com/@polkadot/types-known/-/types-known-4.17.1.tgz#71c18dda4967a13ec34fbbf0c4ef264e882c2688" - integrity sha512-YkOwGrO+k9aVrBR8FgYHnfJKhOfpdgC5ZRYNL/xJ9oa7lBYqPts9ENAxeBmJS/5IGeDF9f32MNyrCP2umeCXWg== +"@polkadot/keyring@^6.9.1": + version "6.11.1" + resolved "https://registry.yarnpkg.com/@polkadot/keyring/-/keyring-6.11.1.tgz#2510c349c965c74cc2f108f114f1048856940604" + integrity sha512-rW8INl7pO6Dmaffd6Df1yAYCRWa2RmWQ0LGfJeA/M6seVIkI6J3opZqAd4q2Op+h9a7z4TESQGk8yggOEL+Csg== dependencies: "@babel/runtime" "^7.14.6" - "@polkadot/networks" "^6.11.1" - "@polkadot/types" "4.17.1" - "@polkadot/util" "^6.11.1" + "@polkadot/util" "6.11.1" + "@polkadot/util-crypto" "6.11.1" -"@polkadot/types-known@6.12.1": - version "6.12.1" - resolved "https://registry.yarnpkg.com/@polkadot/types-known/-/types-known-6.12.1.tgz#2dd3ca4e4aa20b86ef182eb75672690f8c14a84e" - integrity sha512-Z8bHpPQy+mqUm0uR1tai6ra0bQIoPmgRcGFYUM+rJtW1kx/6kZLh10HAICjLpPeA1cwLRzaxHRDqH5MCU6OgXw== +"@polkadot/keyring@^7.4.1": + version "7.9.2" + resolved "https://registry.yarnpkg.com/@polkadot/keyring/-/keyring-7.9.2.tgz#1f5bf6b7bdb5942d275aebf72d4ed98abe874fa8" + integrity sha512-6UGoIxhiTyISkYEZhUbCPpgVxaneIfb/DBVlHtbvaABc8Mqh1KuqcTIq19Mh9wXlBuijl25rw4lUASrE/9sBqg== dependencies: "@babel/runtime" "^7.16.3" - "@polkadot/networks" "^8.1.2" - "@polkadot/types" "6.12.1" - "@polkadot/util" "^8.1.2" - -"@polkadot/types-known@7.15.1": - version "7.15.1" - resolved "https://registry.yarnpkg.com/@polkadot/types-known/-/types-known-7.15.1.tgz#71dbf0835a48cdc97d0f50b0000298687e29818d" - integrity sha512-LMcNP0CxT84DqAKV62/qDeeIVIJCR5yzE9b+9AsYhyfhE4apwxjrThqZA7K0CF56bOdQJSexAerYB/jwk2IijA== - dependencies: - "@babel/runtime" "^7.17.8" - "@polkadot/networks" "^8.7.1" - "@polkadot/types" "7.15.1" - "@polkadot/types-codec" "7.15.1" - "@polkadot/types-create" "7.15.1" - "@polkadot/util" "^8.7.1" - -"@polkadot/types-known@8.1.1": - version "8.1.1" - resolved "https://registry.yarnpkg.com/@polkadot/types-known/-/types-known-8.1.1.tgz#f956b5e0f282cabc32416c81e1a47f6dcda92e06" - integrity sha512-aOuHf/vTFrScipGx9DOcD83ki1jBLHg3549SAkMwyz0K+RnIlt2nat32/M60eUWJgyHHITl4G0QCZrtFY2D2OA== - dependencies: - "@babel/runtime" "^7.17.9" - "@polkadot/networks" "^9.0.1" - "@polkadot/types" "8.1.1" - "@polkadot/types-codec" "8.1.1" - "@polkadot/types-create" "8.1.1" - "@polkadot/util" "^9.0.1" - -"@polkadot/types-known@8.14.1": - version "8.14.1" - resolved "https://registry.yarnpkg.com/@polkadot/types-known/-/types-known-8.14.1.tgz#86103e2b58da15cf74d2babc0cba544a2b12702a" - integrity sha512-GP7gRo9nmitykkrRnoLF61Qm19UFdTwMsOnJkdm7AOeWDmZGxutacgO6k1tBsHr38hsiCCGsB/JiseUgywvGIw== - dependencies: - "@babel/runtime" "^7.18.9" - "@polkadot/networks" "^10.1.1" - "@polkadot/types" "8.14.1" - "@polkadot/types-codec" "8.14.1" - "@polkadot/types-create" "8.14.1" - "@polkadot/util" "^10.1.1" - -"@polkadot/types-known@9.10.2": - version "9.10.2" - resolved "https://registry.yarnpkg.com/@polkadot/types-known/-/types-known-9.10.2.tgz#d37d984eed6aa17b33603aca9f9d006d6eb468cb" - integrity sha512-Kwxoo+xvAAE1w0jZdGqmNoEJHdfJzncO1xrBJ7WjeCuEFoDsWmjP63u/o8VaC1ZNnfrhjRK0vyvquslJ6NQOUA== - dependencies: - "@babel/runtime" "^7.20.6" - "@polkadot/networks" "^10.2.1" - "@polkadot/types" "9.10.2" - "@polkadot/types-codec" "9.10.2" - "@polkadot/types-create" "9.10.2" - "@polkadot/util" "^10.2.1" - -"@polkadot/types-known@9.8.1": - version "9.8.1" - resolved "https://registry.yarnpkg.com/@polkadot/types-known/-/types-known-9.8.1.tgz#4955d6197851983868b266021c5917681a173ac1" - integrity sha512-R0pUPn9KOVdHWoeZy9vXvZNCzF9sM6thgrfAVA9mD4HSdhIhxBjc4FluR5/KwfQ9NXSSqZGGv1btASW1bYVhpA== - dependencies: - "@babel/runtime" "^7.20.1" - "@polkadot/networks" "^10.1.12" - "@polkadot/types" "9.8.1" - "@polkadot/types-codec" "9.8.1" - "@polkadot/types-create" "9.8.1" - "@polkadot/util" "^10.1.12" - -"@polkadot/types-known@9.8.2": - version "9.8.2" - resolved "https://registry.yarnpkg.com/@polkadot/types-known/-/types-known-9.8.2.tgz#e09314fbbda1705e4e1e00509842e75ac9b451af" - integrity sha512-OO7j1n+i0agk56omcJ29aNtcFUMHKSkeASeNnVK6sy9V7ywOJdx7qfEreeWf1fhWKPwwLb9J5o5ReyvSN4dVKA== - dependencies: - "@babel/runtime" "^7.20.1" - "@polkadot/networks" "^10.1.12" - "@polkadot/types" "9.8.2" - "@polkadot/types-codec" "9.8.2" - "@polkadot/types-create" "9.8.2" - "@polkadot/util" "^10.1.12" - -"@polkadot/types-support@7.15.1": - version "7.15.1" - resolved "https://registry.yarnpkg.com/@polkadot/types-support/-/types-support-7.15.1.tgz#9c274759647dd89d46ea9cf74d593bcedcd85527" - integrity sha512-FIK251ffVo+NaUXLlaJeB5OvT7idDd3uxaoBM6IwsS87rzt2CcWMyCbu0uX89AHZUhSviVx7xaBxfkGEqMePWA== - dependencies: - "@babel/runtime" "^7.17.8" - "@polkadot/util" "^8.7.1" - -"@polkadot/types-support@8.1.1": - version "8.1.1" - resolved "https://registry.yarnpkg.com/@polkadot/types-support/-/types-support-8.1.1.tgz#7f4df53766a343cf1b1df7eb55e33ed62de3e153" - integrity sha512-M3rsWvpHlQawhc4CTLgeFxT6nIeYU9JZIlubJ5je1NSorIaX/TdUEgluyPZ52kN5PNDzJNVo84/g6zwKXF5MfQ== - dependencies: - "@babel/runtime" "^7.17.9" - "@polkadot/util" "^9.0.1" - -"@polkadot/types-support@8.14.1": - version "8.14.1" - resolved "https://registry.yarnpkg.com/@polkadot/types-support/-/types-support-8.14.1.tgz#a227266aa296847c43f6d51426c22ce68357bd2c" - integrity sha512-XqR4qq6pCZyNBuFVod8nFSNUmLssrjoU9bOIn4Ua2cqNlI9xsuKaI1X5ySEn/oWOtKQ2L5hbCm9vkXrEtXBl1w== - dependencies: - "@babel/runtime" "^7.18.9" - "@polkadot/util" "^10.1.1" + "@polkadot/util" "7.9.2" + "@polkadot/util-crypto" "7.9.2" -"@polkadot/types-support@8.7.1": +"@polkadot/keyring@^8.2.2": version "8.7.1" - resolved "https://registry.yarnpkg.com/@polkadot/types-support/-/types-support-8.7.1.tgz#a256c2e5abb20bad43751e7f0c1d0de4bf2a9cd0" - integrity sha512-xD2tpY0Hhhl0paPeRF89SQiSzk/iYwjORknuP49cHH4GHMgQpsQBLVi9dG/Xr7dUWerHiOkonOWntIJtVGzijw== - dependencies: - "@babel/runtime" "^7.18.3" - "@polkadot/util" "^9.4.1" - -"@polkadot/types-support@9.10.2": - version "9.10.2" - resolved "https://registry.yarnpkg.com/@polkadot/types-support/-/types-support-9.10.2.tgz#eff0ef399a3373421a543059f62ca96b85645f0b" - integrity sha512-RQSCNNBH8+mzXbErB/LUDU9oMQScv0GZ4UmM2MPDPKBcqXNCdJ4dK+ajNfVbgGTUucYUEebpp2m5Az1usjE4Ew== - dependencies: - "@babel/runtime" "^7.20.6" - "@polkadot/util" "^10.2.1" - -"@polkadot/types-support@9.8.1": - version "9.8.1" - resolved "https://registry.yarnpkg.com/@polkadot/types-support/-/types-support-9.8.1.tgz#a8f27e727f35745854fcf10f310e47e7bb481a58" - integrity sha512-4AYALCP6HD7GeyITauT1ezZU7x+tj1h0dhrTO+ZLL5EXVmCXw5zpadLn19LfPlP4qTsiLeBXJiVTzp5pzHcNtw== - dependencies: - "@babel/runtime" "^7.20.1" - "@polkadot/util" "^10.1.12" - -"@polkadot/types-support@9.8.2": - version "9.8.2" - resolved "https://registry.yarnpkg.com/@polkadot/types-support/-/types-support-9.8.2.tgz#20c3ba10c007790f38d4bf415a93c74f1039ba90" - integrity sha512-67POc2CfW2yIJW3wqVzcY6fXPsqYC5dxzJBruwZa4yUIZc2H4CdCcsQ2tDh8IsSuEloFvp/biYQ5NPSNZK5YJQ== - dependencies: - "@babel/runtime" "^7.20.1" - "@polkadot/util" "^10.1.12" - -"@polkadot/types@4.17.1", "@polkadot/types@^4.13.1": - version "4.17.1" - resolved "https://registry.yarnpkg.com/@polkadot/types/-/types-4.17.1.tgz#41d43621d53820ee930ba4036bfa8b16cf98ca6f" - integrity sha512-rjW4OFdwvFekzN3ATLibC2JPSd8AWt5YepJhmuCPdwH26r3zB8bEC6dM7YQExLVUmygVPvgXk5ffHI6RAdXBMg== - dependencies: - "@babel/runtime" "^7.14.6" - "@polkadot/metadata" "4.17.1" - "@polkadot/util" "^6.11.1" - "@polkadot/util-crypto" "^6.11.1" - "@polkadot/x-rxjs" "^6.11.1" - -"@polkadot/types@6.12.1", "@polkadot/types@^6.0.5": - version "6.12.1" - resolved "https://registry.yarnpkg.com/@polkadot/types/-/types-6.12.1.tgz#e5d6dff997740c3da947fa67abe2e1ec144c4757" - integrity sha512-O37cAGUL0xiXTuO3ySweVh0OuFUD6asrd0TfuzGsEp3jAISWdElEHV5QDiftWq8J9Vf8BMgTcP2QLFbmSusxqA== - dependencies: - "@babel/runtime" "^7.16.3" - "@polkadot/types-known" "6.12.1" - "@polkadot/util" "^8.1.2" - "@polkadot/util-crypto" "^8.1.2" - rxjs "^7.4.0" - -"@polkadot/types@7.15.1", "@polkadot/types@^7.2.1": - version "7.15.1" - resolved "https://registry.yarnpkg.com/@polkadot/types/-/types-7.15.1.tgz#fb78886f4437fbc472e01019846fb1f229d2a177" - integrity sha512-KawZVS+eLR1D6O7c/P5cSUwr6biM9Qd2KwKtJIO8l1Mrxp7r+y2tQnXSSXVAd6XPdb3wVMhnIID+NW3W99TAnQ== + resolved "https://registry.yarnpkg.com/@polkadot/keyring/-/keyring-8.7.1.tgz#07cf6d6ee351dcf70fbf965b1d6d96c5025ae1b8" + integrity sha512-t6ZgQVC+nQT7XwbWtEhkDpiAzxKVJw8Xd/gWdww6xIrawHu7jo3SGB4QNdPgkf8TvDHYAAJiupzVQYAlOIq3GA== dependencies: "@babel/runtime" "^7.17.8" - "@polkadot/keyring" "^8.7.1" - "@polkadot/types-augment" "7.15.1" - "@polkadot/types-codec" "7.15.1" - "@polkadot/types-create" "7.15.1" - "@polkadot/util" "^8.7.1" - "@polkadot/util-crypto" "^8.7.1" - rxjs "^7.5.5" - -"@polkadot/types@8.1.1": - version "8.1.1" - resolved "https://registry.yarnpkg.com/@polkadot/types/-/types-8.1.1.tgz#43e5fb78e6214e6af7c5edbdb6ac69d5b919421d" - integrity sha512-x9WDx9XcaSkQGlnk2MNu+49oK80s8Js7lr0mmCinV12m8+3si+GvIOvnuV3ydmWgWtpTt2ERfN+T8a/6f50EpA== - dependencies: - "@babel/runtime" "^7.17.9" - "@polkadot/keyring" "^9.0.1" - "@polkadot/types-augment" "8.1.1" - "@polkadot/types-codec" "8.1.1" - "@polkadot/types-create" "8.1.1" - "@polkadot/util" "^9.0.1" - "@polkadot/util-crypto" "^9.0.1" - rxjs "^7.5.5" - -"@polkadot/types@8.14.1", "@polkadot/types@^8.5.1": - version "8.14.1" - resolved "https://registry.yarnpkg.com/@polkadot/types/-/types-8.14.1.tgz#a0149680d06b02bc23b842865b84840a5bc8b870" - integrity sha512-Xza16ejKrSd4XhTOlbfISyxZ2sRmbMAZk5pX7VEMHVZHqV98o+bJ2f9Kk7F8YJijkHHGosCLDestP9R5nLoOoA== - dependencies: - "@babel/runtime" "^7.18.9" - "@polkadot/keyring" "^10.1.1" - "@polkadot/types-augment" "8.14.1" - "@polkadot/types-codec" "8.14.1" - "@polkadot/types-create" "8.14.1" - "@polkadot/util" "^10.1.1" - "@polkadot/util-crypto" "^10.1.1" - rxjs "^7.5.6" - -"@polkadot/types@8.7.1", "@polkadot/types@^8.7.1": - version "8.7.1" - resolved "https://registry.yarnpkg.com/@polkadot/types/-/types-8.7.1.tgz#2cf60a3cb2f3fcd36cd6e8358f5e22cc50f1e770" - integrity sha512-/6+UgK1fbjNHMvFcfqWxeCt1ujYZDPWIO7KC5eZ2TtAmKzJbmpo7/kUv1lSVFzNgNYKRW34JDpf5HlwIdbeQPg== - dependencies: - "@babel/runtime" "^7.18.3" - "@polkadot/keyring" "^9.4.1" - "@polkadot/types-augment" "8.7.1" - "@polkadot/types-codec" "8.7.1" - "@polkadot/types-create" "8.7.1" - "@polkadot/util" "^9.4.1" - "@polkadot/util-crypto" "^9.4.1" - rxjs "^7.5.5" - -"@polkadot/types@9.10.2", "@polkadot/types@^9.7.1": - version "9.10.2" - resolved "https://registry.yarnpkg.com/@polkadot/types/-/types-9.10.2.tgz#1f6647445b055856bdbd949106f698c89a125386" - integrity sha512-B5Bg/IaAMJEwdWzGp3pil5WBukr5fm9x9NFIMuoCS9TyIqpm9rSHrz2n/408R3B4rwqqtx8RQAxiIETFI+m6Rw== - dependencies: - "@babel/runtime" "^7.20.6" - "@polkadot/keyring" "^10.2.1" - "@polkadot/types-augment" "9.10.2" - "@polkadot/types-codec" "9.10.2" - "@polkadot/types-create" "9.10.2" - "@polkadot/util" "^10.2.1" - "@polkadot/util-crypto" "^10.2.1" - rxjs "^7.6.0" - -"@polkadot/types@9.8.1": - version "9.8.1" - resolved "https://registry.yarnpkg.com/@polkadot/types/-/types-9.8.1.tgz#2df753390cab8c4863cf72c03b7ec4d0fed1047f" - integrity sha512-u1pMk2uQX2Q+9DwdXLUmXRG16l8p1ZGZSYv735FeSUEYQfr07C+gE0bLlYFH3I9k/EJJw8nyp30KUOS2rW/qHw== - dependencies: - "@babel/runtime" "^7.20.1" - "@polkadot/keyring" "^10.1.12" - "@polkadot/types-augment" "9.8.1" - "@polkadot/types-codec" "9.8.1" - "@polkadot/types-create" "9.8.1" - "@polkadot/util" "^10.1.12" - "@polkadot/util-crypto" "^10.1.12" - rxjs "^7.5.7" + "@polkadot/util" "8.7.1" + "@polkadot/util-crypto" "8.7.1" -"@polkadot/types@9.8.2", "@polkadot/types@^9.8.1": - version "9.8.2" - resolved "https://registry.yarnpkg.com/@polkadot/types/-/types-9.8.2.tgz#317a5ac4132083d7ede8c8694847df5cae8c07af" - integrity sha512-klbqJZTobNSZ8zXhPWV0ICrYlSAcooyclTRv3fYSdVUYwGF8K1zkPUImDHlnlQQ2mGQFi+8JiBUOcbAp/JgLWw== - dependencies: - "@babel/runtime" "^7.20.1" - "@polkadot/keyring" "^10.1.12" - "@polkadot/types-augment" "9.8.2" - "@polkadot/types-codec" "9.8.2" - "@polkadot/types-create" "9.8.2" - "@polkadot/util" "^10.1.12" - "@polkadot/util-crypto" "^10.1.12" - rxjs "^7.5.7" +"@polkadot/networks@10.4.2", "@polkadot/networks@^10.1.11", "@polkadot/networks@^10.1.6", "@polkadot/networks@^10.4.2": + version "10.4.2" + resolved "https://registry.yarnpkg.com/@polkadot/networks/-/networks-10.4.2.tgz#d7878c6aad8173c800a21140bfe5459261724456" + integrity sha512-FAh/znrEvWBiA/LbcT5GXHsCFUl//y9KqxLghSr/CreAmAergiJNT0MVUezC7Y36nkATgmsr4ylFwIxhVtuuCw== + dependencies: + "@babel/runtime" "^7.20.13" + "@polkadot/util" "10.4.2" + "@substrate/ss58-registry" "^1.38.0" + +"@polkadot/rpc-augment@9.14.2", "@polkadot/rpc-augment@^9.14.2": + version "9.14.2" + resolved "https://registry.yarnpkg.com/@polkadot/rpc-augment/-/rpc-augment-9.14.2.tgz#eb70d5511463dab8d995faeb77d4edfe4952fe26" + integrity sha512-mOubRm3qbKZTbP9H01XRrfTk7k5it9WyzaWAg72DJBQBYdgPUUkGSgpPD/Srkk5/5GAQTWVWL1I2UIBKJ4TJjQ== + dependencies: + "@babel/runtime" "^7.20.13" + "@polkadot/rpc-core" "9.14.2" + "@polkadot/types" "9.14.2" + "@polkadot/types-codec" "9.14.2" + "@polkadot/util" "^10.4.2" + +"@polkadot/rpc-core@9.14.2", "@polkadot/rpc-core@^9.14.2", "@polkadot/rpc-core@^9.9.1": + version "9.14.2" + resolved "https://registry.yarnpkg.com/@polkadot/rpc-core/-/rpc-core-9.14.2.tgz#26d4f00ac7abbf880f8280e9b96235880d35794b" + integrity sha512-krA/mtQ5t9nUQEsEVC1sjkttLuzN6z6gyJxK2IlpMS3S5ncy/R6w4FOpy+Q0H18Dn83JBo0p7ZtY7Y6XkK48Kw== + dependencies: + "@babel/runtime" "^7.20.13" + "@polkadot/rpc-augment" "9.14.2" + "@polkadot/rpc-provider" "9.14.2" + "@polkadot/types" "9.14.2" + "@polkadot/util" "^10.4.2" + rxjs "^7.8.0" + +"@polkadot/rpc-provider@9.14.2", "@polkadot/rpc-provider@^8.7.1", "@polkadot/rpc-provider@^9.14.2": + version "9.14.2" + resolved "https://registry.yarnpkg.com/@polkadot/rpc-provider/-/rpc-provider-9.14.2.tgz#0dea667f3a03bf530f202cba5cd360df19b32e30" + integrity sha512-YTSywjD5PF01V47Ru5tln2LlpUwJiSOdz6rlJXPpMaY53hUp7+xMU01FVAQ1bllSBNisSD1Msv/mYHq84Oai2g== + dependencies: + "@babel/runtime" "^7.20.13" + "@polkadot/keyring" "^10.4.2" + "@polkadot/types" "9.14.2" + "@polkadot/types-support" "9.14.2" + "@polkadot/util" "^10.4.2" + "@polkadot/util-crypto" "^10.4.2" + "@polkadot/x-fetch" "^10.4.2" + "@polkadot/x-global" "^10.4.2" + "@polkadot/x-ws" "^10.4.2" + eventemitter3 "^5.0.0" + mock-socket "^9.2.1" + nock "^13.3.0" + optionalDependencies: + "@substrate/connect" "0.7.19" + +"@polkadot/types-augment@9.14.2", "@polkadot/types-augment@^9.11.1", "@polkadot/types-augment@^9.14.2": + version "9.14.2" + resolved "https://registry.yarnpkg.com/@polkadot/types-augment/-/types-augment-9.14.2.tgz#1a478e18e713b04038f3e171287ee5abe908f0aa" + integrity sha512-WO9d7RJufUeY3iFgt2Wz762kOu1tjEiGBR5TT4AHtpEchVHUeosVTrN9eycC+BhleqYu52CocKz6u3qCT/jKLg== + dependencies: + "@babel/runtime" "^7.20.13" + "@polkadot/types" "9.14.2" + "@polkadot/types-codec" "9.14.2" + "@polkadot/util" "^10.4.2" + +"@polkadot/types-codec@9.14.2", "@polkadot/types-codec@^9.14.2": + version "9.14.2" + resolved "https://registry.yarnpkg.com/@polkadot/types-codec/-/types-codec-9.14.2.tgz#d625c80495d7a68237b3d5c0a60ff10d206131fa" + integrity sha512-AJ4XF7W1no4PENLBRU955V6gDxJw0h++EN3YoDgThozZ0sj3OxyFupKgNBZcZb2V23H8JxQozzIad8k+nJbO1w== + dependencies: + "@babel/runtime" "^7.20.13" + "@polkadot/util" "^10.4.2" + "@polkadot/x-bigint" "^10.4.2" + +"@polkadot/types-create@9.14.2", "@polkadot/types-create@^9.14.2": + version "9.14.2" + resolved "https://registry.yarnpkg.com/@polkadot/types-create/-/types-create-9.14.2.tgz#aec515a2d3bc7e7b7802095cdd35ece48dc442bc" + integrity sha512-nSnKpBierlmGBQT8r6/SHf6uamBIzk4WmdMsAsR4uJKJF1PtbIqx2W5PY91xWSiMSNMzjkbCppHkwaDAMwLGaw== + dependencies: + "@babel/runtime" "^7.20.13" + "@polkadot/types-codec" "9.14.2" + "@polkadot/util" "^10.4.2" + +"@polkadot/types-known@9.14.2", "@polkadot/types-known@^9.14.2": + version "9.14.2" + resolved "https://registry.yarnpkg.com/@polkadot/types-known/-/types-known-9.14.2.tgz#17fe5034a5b907bd006093a687f112b07edadf10" + integrity sha512-iM8WOCgguzJ3TLMqlm4K1gKQEwWm2zxEKT1HZZ1irs/lAbBk9MquDWDvebryiw3XsLB8xgrp3RTIBn2Q4FjB2A== + dependencies: + "@babel/runtime" "^7.20.13" + "@polkadot/networks" "^10.4.2" + "@polkadot/types" "9.14.2" + "@polkadot/types-codec" "9.14.2" + "@polkadot/types-create" "9.14.2" + "@polkadot/util" "^10.4.2" + +"@polkadot/types-support@9.14.2", "@polkadot/types-support@^9.14.2": + version "9.14.2" + resolved "https://registry.yarnpkg.com/@polkadot/types-support/-/types-support-9.14.2.tgz#d25e8d4e5ec3deef914cb55fc8bc5448431ddd18" + integrity sha512-VWCOPgXDK3XtXT7wMLyIWeNDZxUbNcw/8Pn6n6vMogs7o/n4h6WGbGMeTIQhPWyn831/RmkVs5+2DUC+2LlOhw== + dependencies: + "@babel/runtime" "^7.20.13" + "@polkadot/util" "^10.4.2" + +"@polkadot/types@9.10.3", "@polkadot/types@9.14.2", "@polkadot/types@^4.13.1", "@polkadot/types@^6.0.5", "@polkadot/types@^7.2.1", "@polkadot/types@^8.7.1", "@polkadot/types@^9.11.1", "@polkadot/types@^9.14.2", "@polkadot/types@^9.7.1", "@polkadot/types@^9.9.1": + version "9.14.2" + resolved "https://registry.yarnpkg.com/@polkadot/types/-/types-9.14.2.tgz#5105f41eb9e8ea29938188d21497cbf1753268b8" + integrity sha512-hGLddTiJbvowhhUZJ3k+olmmBc1KAjWIQxujIUIYASih8FQ3/YJDKxaofGOzh0VygOKW3jxQBN2VZPofyDP9KQ== + dependencies: + "@babel/runtime" "^7.20.13" + "@polkadot/keyring" "^10.4.2" + "@polkadot/types-augment" "9.14.2" + "@polkadot/types-codec" "9.14.2" + "@polkadot/types-create" "9.14.2" + "@polkadot/util" "^10.4.2" + "@polkadot/util-crypto" "^10.4.2" + rxjs "^7.8.0" "@polkadot/ui-keyring@^2.9.7": version "2.9.7" @@ -5261,303 +3614,36 @@ eventemitter3 "^4.0.7" store "^2.0.12" -"@polkadot/util-crypto@10.1.12", "@polkadot/util-crypto@^10.1.1", "@polkadot/util-crypto@^10.1.12": - version "10.1.12" - resolved "https://registry.yarnpkg.com/@polkadot/util-crypto/-/util-crypto-10.1.12.tgz#cdf5a7fbd85d2fa5137fd3fd3865f8cedb0edd53" - integrity sha512-X/IxeMMYMVWhByU2be5bzPFy8acPTa2oLcvzSKLjhxqtCEIqprs/fshhfT3qVTa/lAN+WeNE8oTPCPLKVpQE4w== - dependencies: - "@babel/runtime" "^7.20.1" - "@noble/hashes" "1.1.3" - "@noble/secp256k1" "1.7.0" - "@polkadot/networks" "10.1.12" - "@polkadot/util" "10.1.12" - "@polkadot/wasm-crypto" "^6.3.1" - "@polkadot/x-bigint" "10.1.12" - "@polkadot/x-randomvalues" "10.1.12" - "@scure/base" "1.1.1" - ed2curve "^0.3.0" - tweetnacl "^1.0.3" - -"@polkadot/util-crypto@10.1.9": - version "10.1.9" - resolved "https://registry.yarnpkg.com/@polkadot/util-crypto/-/util-crypto-10.1.9.tgz#807ffd4c8bb20ffe6eb446a74a3a8725c845acf1" - integrity sha512-wmrT5M8dOaSklnKwk7Wg6TjJzLYm5YCVJW97ziz5ulfN7LFbOwtPQ3fpk6uzBmnrRNXpI5hjYzzTKEyHrZZqhA== - dependencies: - "@babel/runtime" "^7.19.0" - "@noble/hashes" "1.1.2" - "@noble/secp256k1" "1.7.0" - "@polkadot/networks" "10.1.9" - "@polkadot/util" "10.1.9" - "@polkadot/wasm-crypto" "^6.3.1" - "@polkadot/x-bigint" "10.1.9" - "@polkadot/x-randomvalues" "10.1.9" - "@scure/base" "1.1.1" - ed2curve "^0.3.0" - tweetnacl "^1.0.3" - -"@polkadot/util-crypto@10.2.1", "@polkadot/util-crypto@^10.2.1": - version "10.2.1" - resolved "https://registry.yarnpkg.com/@polkadot/util-crypto/-/util-crypto-10.2.1.tgz#f6ce1c81496336ca50c2ca84975bcde79aa16634" - integrity sha512-UH1J4oD92gkLXMfVTLee3Y2vYadNyp1lmS4P2nZwQ0SOzGZ4rN7khD2CrB1cXS9WPq196Zb5oZdGLnPYnXHtjw== +"@polkadot/util-crypto@10.4.2", "@polkadot/util-crypto@6.11.1", "@polkadot/util-crypto@7.9.2", "@polkadot/util-crypto@8.7.1", "@polkadot/util-crypto@^10.1.12", "@polkadot/util-crypto@^10.1.6", "@polkadot/util-crypto@^10.2.1", "@polkadot/util-crypto@^10.2.4", "@polkadot/util-crypto@^10.4.2", "@polkadot/util-crypto@^9.4.1": + version "10.4.2" + resolved "https://registry.yarnpkg.com/@polkadot/util-crypto/-/util-crypto-10.4.2.tgz#871fb69c65768bd48c57bb5c1f76a85d979fb8b5" + integrity sha512-RxZvF7C4+EF3fzQv8hZOLrYCBq5+wA+2LWv98nECkroChY3C2ZZvyWDqn8+aonNULt4dCVTWDZM0QIY6y4LUAQ== dependencies: - "@babel/runtime" "^7.20.6" - "@noble/hashes" "1.1.3" - "@noble/secp256k1" "1.7.0" - "@polkadot/networks" "10.2.1" - "@polkadot/util" "10.2.1" + "@babel/runtime" "^7.20.13" + "@noble/hashes" "1.2.0" + "@noble/secp256k1" "1.7.1" + "@polkadot/networks" "10.4.2" + "@polkadot/util" "10.4.2" "@polkadot/wasm-crypto" "^6.4.1" - "@polkadot/x-bigint" "10.2.1" - "@polkadot/x-randomvalues" "10.2.1" - "@scure/base" "1.1.1" - ed2curve "^0.3.0" - tweetnacl "^1.0.3" - -"@polkadot/util-crypto@6.11.1", "@polkadot/util-crypto@^6.11.1": - version "6.11.1" - resolved "https://registry.yarnpkg.com/@polkadot/util-crypto/-/util-crypto-6.11.1.tgz#7a36acf5c8bf52541609ec0b0b2a69af295d652e" - integrity sha512-fWA1Nz17FxWJslweZS4l0Uo30WXb5mYV1KEACVzM+BSZAvG5eoiOAYX6VYZjyw6/7u53XKrWQlD83iPsg3KvZw== - dependencies: - "@babel/runtime" "^7.14.6" - "@polkadot/networks" "6.11.1" - "@polkadot/util" "6.11.1" - "@polkadot/wasm-crypto" "^4.0.2" - "@polkadot/x-randomvalues" "6.11.1" - base-x "^3.0.8" - base64-js "^1.5.1" - blakejs "^1.1.1" - bn.js "^4.11.9" - create-hash "^1.2.0" - elliptic "^6.5.4" - hash.js "^1.1.7" - js-sha3 "^0.8.0" - scryptsy "^2.1.0" - tweetnacl "^1.0.3" - xxhashjs "^0.2.2" - -"@polkadot/util-crypto@7.9.2": - version "7.9.2" - resolved "https://registry.yarnpkg.com/@polkadot/util-crypto/-/util-crypto-7.9.2.tgz#cdc336f92a6bc3d40c5a23734e1974fb777817f0" - integrity sha512-nNwqUwP44eCH9jKKcPie+IHLKkg9LMe6H7hXo91hy3AtoslnNrT51tP3uAm5yllhLvswJfnAgnlHq7ybCgqeFw== - dependencies: - "@babel/runtime" "^7.16.3" - "@polkadot/networks" "7.9.2" - "@polkadot/util" "7.9.2" - "@polkadot/wasm-crypto" "^4.4.1" - "@polkadot/x-randomvalues" "7.9.2" - blakejs "^1.1.1" - bn.js "^4.12.0" - create-hash "^1.2.0" - ed2curve "^0.3.0" - elliptic "^6.5.4" - hash.js "^1.1.7" - js-sha3 "^0.8.0" - micro-base "^0.9.0" - scryptsy "^2.1.0" - tweetnacl "^1.0.3" - xxhashjs "^0.2.2" - -"@polkadot/util-crypto@8.7.1", "@polkadot/util-crypto@^8.1.2", "@polkadot/util-crypto@^8.7.1": - version "8.7.1" - resolved "https://registry.yarnpkg.com/@polkadot/util-crypto/-/util-crypto-8.7.1.tgz#f9fcca2895b5f160ce1c2faa0aa3054cc7aa4655" - integrity sha512-TaSuJ2aNrB5sYK7YXszkEv24nYJKRFqjF2OrggoMg6uYxUAECvTkldFnhtgeizMweRMxJIBu6bMHlSIutbWgjw== - dependencies: - "@babel/runtime" "^7.17.8" - "@noble/hashes" "1.0.0" - "@noble/secp256k1" "1.5.5" - "@polkadot/networks" "8.7.1" - "@polkadot/util" "8.7.1" - "@polkadot/wasm-crypto" "^5.1.1" - "@polkadot/x-bigint" "8.7.1" - "@polkadot/x-randomvalues" "8.7.1" - "@scure/base" "1.0.0" - ed2curve "^0.3.0" - tweetnacl "^1.0.3" - -"@polkadot/util-crypto@9.4.1", "@polkadot/util-crypto@^9.4.1": - version "9.4.1" - resolved "https://registry.yarnpkg.com/@polkadot/util-crypto/-/util-crypto-9.4.1.tgz#af50d9b3e3fcf9760ee8eb262b1cc61614c21d98" - integrity sha512-V6xMOjdd8Kt/QmXlcDYM4WJDAmKuH4vWSlIcMmkFHnwH/NtYVdYIDZswLQHKL8gjLijPfVTHpWaJqNFhGpZJEg== - dependencies: - "@babel/runtime" "^7.18.3" - "@noble/hashes" "1.0.0" - "@noble/secp256k1" "1.5.5" - "@polkadot/networks" "9.4.1" - "@polkadot/util" "9.4.1" - "@polkadot/wasm-crypto" "^6.1.1" - "@polkadot/x-bigint" "9.4.1" - "@polkadot/x-randomvalues" "9.4.1" - "@scure/base" "1.0.0" - ed2curve "^0.3.0" - tweetnacl "^1.0.3" - -"@polkadot/util-crypto@9.7.2", "@polkadot/util-crypto@^9.0.1": - version "9.7.2" - resolved "https://registry.yarnpkg.com/@polkadot/util-crypto/-/util-crypto-9.7.2.tgz#0a097f4e197cd344d101ab748a740c2d99a4c5b9" - integrity sha512-tfz6mJtPwoNteivKCmR+QklC4mr1/hGZRsDJLWKaFhanDinYZ3V2pJM1EbCI6WONLuuzlTxsDXjAffWzzRqlPA== - dependencies: - "@babel/runtime" "^7.18.6" - "@noble/hashes" "1.1.2" - "@noble/secp256k1" "1.6.0" - "@polkadot/networks" "9.7.2" - "@polkadot/util" "9.7.2" - "@polkadot/wasm-crypto" "^6.2.2" - "@polkadot/x-bigint" "9.7.2" - "@polkadot/x-randomvalues" "9.7.2" - "@scure/base" "1.1.1" - ed2curve "^0.3.0" - tweetnacl "^1.0.3" - -"@polkadot/util-crypto@^10.1.6": - version "10.1.6" - resolved "https://registry.yarnpkg.com/@polkadot/util-crypto/-/util-crypto-10.1.6.tgz#f28bdcea6c5f732fef272928a4ba1cfc7c08e45e" - integrity sha512-r3XWcCERomcGyB5PT7Qa1LYtCHfspVbehPGvraRlX5xhZBihpU4zMRWTSBNMPNaeIjUJmmSQHeG0ZnQVvnwzkg== - dependencies: - "@babel/runtime" "^7.18.9" - "@noble/hashes" "1.1.2" - "@noble/secp256k1" "1.6.3" - "@polkadot/networks" "10.1.6" - "@polkadot/util" "10.1.6" - "@polkadot/wasm-crypto" "^6.3.1" - "@polkadot/x-bigint" "10.1.6" - "@polkadot/x-randomvalues" "10.1.6" + "@polkadot/x-bigint" "10.4.2" + "@polkadot/x-randomvalues" "10.4.2" "@scure/base" "1.1.1" ed2curve "^0.3.0" tweetnacl "^1.0.3" -"@polkadot/util@10.1.12", "@polkadot/util@^10.1.1", "@polkadot/util@^10.1.12": - version "10.1.12" - resolved "https://registry.yarnpkg.com/@polkadot/util/-/util-10.1.12.tgz#9652de84c87b0b5b9b50767cd4d93f5f2d70a074" - integrity sha512-bOz1WqDFzIgkTpT6oRhAdXKqETr2GffZdRlYqyOvP1ATAEa48/sRgzIvg7WTiI68D8By3fJmKuA+ggX3YrNF/w== +"@polkadot/util@10.4.2", "@polkadot/util@6.11.1", "@polkadot/util@7.9.2", "@polkadot/util@8.7.1", "@polkadot/util@^10.1.11", "@polkadot/util@^10.1.12", "@polkadot/util@^10.1.6", "@polkadot/util@^10.2.1", "@polkadot/util@^10.2.4", "@polkadot/util@^10.4.2", "@polkadot/util@^9.4.1": + version "10.4.2" + resolved "https://registry.yarnpkg.com/@polkadot/util/-/util-10.4.2.tgz#df41805cb27f46b2b4dad24c371fa2a68761baa1" + integrity sha512-0r5MGICYiaCdWnx+7Axlpvzisy/bi1wZGXgCSw5+ZTyPTOqvsYRqM2X879yxvMsGfibxzWqNzaiVjToz1jvUaA== dependencies: - "@babel/runtime" "^7.20.1" - "@polkadot/x-bigint" "10.1.12" - "@polkadot/x-global" "10.1.12" - "@polkadot/x-textdecoder" "10.1.12" - "@polkadot/x-textencoder" "10.1.12" - "@types/bn.js" "^5.1.1" - bn.js "^5.2.1" - -"@polkadot/util@10.1.6": - version "10.1.6" - resolved "https://registry.yarnpkg.com/@polkadot/util/-/util-10.1.6.tgz#fe828533902b6983de833524c1f5884569a2ac2b" - integrity sha512-k+gCKmgwxp0smmLIR7SfiEYEToayWXjrC7pQ0PqAGxpBNOdVMSCzLMnOHf9AI5cjs/lx6ULr1fHn721wLVonkw== - dependencies: - "@babel/runtime" "^7.18.9" - "@polkadot/x-bigint" "10.1.6" - "@polkadot/x-global" "10.1.6" - "@polkadot/x-textdecoder" "10.1.6" - "@polkadot/x-textencoder" "10.1.6" - "@types/bn.js" "^5.1.0" - bn.js "^5.2.1" - -"@polkadot/util@10.1.9", "@polkadot/util@^10.1.6": - version "10.1.9" - resolved "https://registry.yarnpkg.com/@polkadot/util/-/util-10.1.9.tgz#0280a830881ccb4c7c428ef8b5669db87c16d71b" - integrity sha512-nbXE7dqfsP38uHwMXBoL5s2x+5PXsGZIfWa0bjCdy6RwF6btqFTo7ryi0kzkco/kil0EZ3QaB+EJxVVaAwX2bA== - dependencies: - "@babel/runtime" "^7.19.0" - "@polkadot/x-bigint" "10.1.9" - "@polkadot/x-global" "10.1.9" - "@polkadot/x-textdecoder" "10.1.9" - "@polkadot/x-textencoder" "10.1.9" - "@types/bn.js" "^5.1.1" - bn.js "^5.2.1" - -"@polkadot/util@10.2.1", "@polkadot/util@^10.1.11", "@polkadot/util@^10.2.1": - version "10.2.1" - resolved "https://registry.yarnpkg.com/@polkadot/util/-/util-10.2.1.tgz#a8c3a4fe87091197448bec70f7ea079b60d5abf6" - integrity sha512-ewGKSOp+VXKEeCvpCCP2Qqi/FVkewBF9vb/N8pRwuNQ2XE9k1lnsOZZeQemVBDhKsZz+h3IeNcWejaF6K3vYHQ== - dependencies: - "@babel/runtime" "^7.20.6" - "@polkadot/x-bigint" "10.2.1" - "@polkadot/x-global" "10.2.1" - "@polkadot/x-textdecoder" "10.2.1" - "@polkadot/x-textencoder" "10.2.1" + "@babel/runtime" "^7.20.13" + "@polkadot/x-bigint" "10.4.2" + "@polkadot/x-global" "10.4.2" + "@polkadot/x-textdecoder" "10.4.2" + "@polkadot/x-textencoder" "10.4.2" "@types/bn.js" "^5.1.1" bn.js "^5.2.1" -"@polkadot/util@6.11.1", "@polkadot/util@^6.11.1": - version "6.11.1" - resolved "https://registry.yarnpkg.com/@polkadot/util/-/util-6.11.1.tgz#8950b038ba3e6ebfc0a7ff47feeb972e81b2626c" - integrity sha512-TEdCetr9rsdUfJZqQgX/vxLuV4XU8KMoKBMJdx+JuQ5EWemIdQkEtMBdL8k8udNGbgSNiYFA6rPppATeIxAScg== - dependencies: - "@babel/runtime" "^7.14.6" - "@polkadot/x-textdecoder" "6.11.1" - "@polkadot/x-textencoder" "6.11.1" - "@types/bn.js" "^4.11.6" - bn.js "^4.11.9" - camelcase "^5.3.1" - ip-regex "^4.3.0" - -"@polkadot/util@7.9.2": - version "7.9.2" - resolved "https://registry.yarnpkg.com/@polkadot/util/-/util-7.9.2.tgz#567ac659516d6b685ed7e796919901d92e5cbe6b" - integrity sha512-6ABY6ErgkCsM4C6+X+AJSY4pBGwbKlHZmUtHftaiTvbaj4XuA4nTo3GU28jw8wY0Jh2cJZJvt6/BJ5GVkm5tBA== - dependencies: - "@babel/runtime" "^7.16.3" - "@polkadot/x-textdecoder" "7.9.2" - "@polkadot/x-textencoder" "7.9.2" - "@types/bn.js" "^4.11.6" - bn.js "^4.12.0" - camelcase "^6.2.1" - ip-regex "^4.3.0" - -"@polkadot/util@8.7.1", "@polkadot/util@^8.1.2", "@polkadot/util@^8.7.1": - version "8.7.1" - resolved "https://registry.yarnpkg.com/@polkadot/util/-/util-8.7.1.tgz#27fe93bf7b8345276f10cfe9c0380510cd4584f6" - integrity sha512-XjY1bTo7V6OvOCe4yn8H2vifeuBciCy0gq0k5P1tlGUQLI/Yt0hvDmxcA0FEPtqg8CL+rYRG7WXGPVNjkrNvyQ== - dependencies: - "@babel/runtime" "^7.17.8" - "@polkadot/x-bigint" "8.7.1" - "@polkadot/x-global" "8.7.1" - "@polkadot/x-textdecoder" "8.7.1" - "@polkadot/x-textencoder" "8.7.1" - "@types/bn.js" "^5.1.0" - bn.js "^5.2.0" - ip-regex "^4.3.0" - -"@polkadot/util@9.4.1", "@polkadot/util@^9.4.1": - version "9.4.1" - resolved "https://registry.yarnpkg.com/@polkadot/util/-/util-9.4.1.tgz#49446e88b1231b0716bf6b4eb4818145f08a1294" - integrity sha512-z0HcnIe3zMWyK1s09wQIwc1M8gDKygSF9tDAbC8H9KDeIRZB2ldhwWEFx/1DJGOgFFrmRfkxeC6dcDpfzQhFow== - dependencies: - "@babel/runtime" "^7.18.3" - "@polkadot/x-bigint" "9.4.1" - "@polkadot/x-global" "9.4.1" - "@polkadot/x-textdecoder" "9.4.1" - "@polkadot/x-textencoder" "9.4.1" - "@types/bn.js" "^5.1.0" - bn.js "^5.2.1" - ip-regex "^4.3.0" - -"@polkadot/util@9.7.2", "@polkadot/util@^9.0.1": - version "9.7.2" - resolved "https://registry.yarnpkg.com/@polkadot/util/-/util-9.7.2.tgz#0f97fa92b273e6ce4b53fe869a957ac99342007d" - integrity sha512-ivTmA+KkPCq5i3O0Gk+dTds/hwdwlYCh89aKfeaG9ni3XHUbbuBgTqHneo648HqxwAwSAyiDiwE9EdXrzAdO4Q== - dependencies: - "@babel/runtime" "^7.18.6" - "@polkadot/x-bigint" "9.7.2" - "@polkadot/x-global" "9.7.2" - "@polkadot/x-textdecoder" "9.7.2" - "@polkadot/x-textencoder" "9.7.2" - "@types/bn.js" "^5.1.0" - bn.js "^5.2.1" - ip-regex "^4.3.0" - -"@polkadot/wasm-bridge@6.1.1": - version "6.1.1" - resolved "https://registry.yarnpkg.com/@polkadot/wasm-bridge/-/wasm-bridge-6.1.1.tgz#9342f2b3c139df72fa45c8491b348f8ebbfa57fa" - integrity sha512-Cy0k00VCu+HWxie+nn9GWPlSPdiZl8Id8ulSGA2FKET0jIbffmOo4e1E2FXNucfR1UPEpqov5BCF9T5YxEXZDg== - dependencies: - "@babel/runtime" "^7.17.9" - -"@polkadot/wasm-bridge@6.3.1": - version "6.3.1" - resolved "https://registry.yarnpkg.com/@polkadot/wasm-bridge/-/wasm-bridge-6.3.1.tgz#439fa78e80947a7cb695443e1f64b25c30bb1487" - integrity sha512-1TYkHsb9AEFhU9uZj3biEnN2yKQNzdrwSjiTvfCYnt97pnEkKsZI6cku+YPZQv5w/x9CQa5Yua9e2DVVZSivGA== - dependencies: - "@babel/runtime" "^7.18.9" - "@polkadot/wasm-bridge@6.4.1": version "6.4.1" resolved "https://registry.yarnpkg.com/@polkadot/wasm-bridge/-/wasm-bridge-6.4.1.tgz#e97915dd67ba543ec3381299c2a5b9330686e27e" @@ -5565,60 +3651,12 @@ dependencies: "@babel/runtime" "^7.20.6" -"@polkadot/wasm-crypto-asmjs@6.1.1": - version "6.1.1" - resolved "https://registry.yarnpkg.com/@polkadot/wasm-crypto-asmjs/-/wasm-crypto-asmjs-6.1.1.tgz#6d09045679120b43fbfa435b29c3690d1f788ebb" - integrity sha512-gG4FStVumkyRNH7WcTB+hn3EEwCssJhQyi4B1BOUt+eYYmw9xJdzIhqjzSd9b/yF2e5sRaAzfnMj2srGufsE6A== - dependencies: - "@babel/runtime" "^7.17.9" - -"@polkadot/wasm-crypto-asmjs@6.3.1": - version "6.3.1" - resolved "https://registry.yarnpkg.com/@polkadot/wasm-crypto-asmjs/-/wasm-crypto-asmjs-6.3.1.tgz#e8f469c9cf4a7709c8131a96f857291953f3e30a" - integrity sha512-zbombRfA5v/mUWQQhgg2YwaxhRmxRIrvskw65x+lruax3b6xPBFDs7yplopiJU3r8h2pTgQvX/DUksvqz2TCRQ== - dependencies: - "@babel/runtime" "^7.18.9" - "@polkadot/wasm-crypto-asmjs@6.4.1": version "6.4.1" resolved "https://registry.yarnpkg.com/@polkadot/wasm-crypto-asmjs/-/wasm-crypto-asmjs-6.4.1.tgz#3cc76bbda5ea4a7a860982c64f9565907b312253" integrity sha512-UxZTwuBZlnODGIQdCsE2Sn/jU0O2xrNQ/TkhRFELfkZXEXTNu4lw6NpaKq7Iey4L+wKd8h4lT3VPVkMcPBLOvA== dependencies: - "@babel/runtime" "^7.20.6" - -"@polkadot/wasm-crypto-asmjs@^4.6.1": - version "4.6.1" - resolved "https://registry.yarnpkg.com/@polkadot/wasm-crypto-asmjs/-/wasm-crypto-asmjs-4.6.1.tgz#4f4a5adcf8dce65666eaa0fb16b6ff7b0243aead" - integrity sha512-1oHQjz2oEO1kCIcQniOP+dZ9N2YXf2yCLHLsKaKSvfXiWaetVCaBNB8oIHIVYvuLnVc8qlMi66O6xc1UublHsw== - dependencies: - "@babel/runtime" "^7.17.2" - -"@polkadot/wasm-crypto-asmjs@^5.1.1": - version "5.1.1" - resolved "https://registry.yarnpkg.com/@polkadot/wasm-crypto-asmjs/-/wasm-crypto-asmjs-5.1.1.tgz#6648e9c6f627501f61aef570e110022f2be1eff2" - integrity sha512-1WBwc2G3pZMKW1T01uXzKE30Sg22MXmF3RbbZiWWk3H2d/Er4jZQRpjumxO5YGWan+xOb7HQQdwnrUnrPgbDhg== - dependencies: - "@babel/runtime" "^7.17.8" - -"@polkadot/wasm-crypto-init@6.1.1": - version "6.1.1" - resolved "https://registry.yarnpkg.com/@polkadot/wasm-crypto-init/-/wasm-crypto-init-6.1.1.tgz#73731071bea9b4e22b380d75099da9dc683fadf5" - integrity sha512-rbBm/9FOOUjISL4gGNokjcKy2X+Af6Chaet4zlabatpImtPIAK26B2UUBGoaRUnvl/w6K3+GwBL4LuBC+CvzFw== - dependencies: - "@babel/runtime" "^7.17.9" - "@polkadot/wasm-bridge" "6.1.1" - "@polkadot/wasm-crypto-asmjs" "6.1.1" - "@polkadot/wasm-crypto-wasm" "6.1.1" - -"@polkadot/wasm-crypto-init@6.3.1": - version "6.3.1" - resolved "https://registry.yarnpkg.com/@polkadot/wasm-crypto-init/-/wasm-crypto-init-6.3.1.tgz#b590220c53c94b9a54d5dc236d0cbe943db76706" - integrity sha512-9yaUBcu+snwjJLmPPGl3cyGRQ1afyFGm16qzTM0sgG/ZCfUlK4uk8KWZe+sBUKgoxb2oXY7Y4WklKgQI1YBdfw== - dependencies: - "@babel/runtime" "^7.18.9" - "@polkadot/wasm-bridge" "6.3.1" - "@polkadot/wasm-crypto-asmjs" "6.3.1" - "@polkadot/wasm-crypto-wasm" "6.3.1" + "@babel/runtime" "^7.20.6" "@polkadot/wasm-crypto-init@6.4.1": version "6.4.1" @@ -5630,22 +3668,6 @@ "@polkadot/wasm-crypto-asmjs" "6.4.1" "@polkadot/wasm-crypto-wasm" "6.4.1" -"@polkadot/wasm-crypto-wasm@6.1.1": - version "6.1.1" - resolved "https://registry.yarnpkg.com/@polkadot/wasm-crypto-wasm/-/wasm-crypto-wasm-6.1.1.tgz#3fdc8f1280710e4d68112544b2473e811c389a2a" - integrity sha512-zkz5Ct4KfTBT+YNEA5qbsHhTV58/FAxDave8wYIOaW4TrBnFPPs+J0WBWlGFertgIhPkvjFnQC/xzRyhet9prg== - dependencies: - "@babel/runtime" "^7.17.9" - "@polkadot/wasm-util" "6.1.1" - -"@polkadot/wasm-crypto-wasm@6.3.1": - version "6.3.1" - resolved "https://registry.yarnpkg.com/@polkadot/wasm-crypto-wasm/-/wasm-crypto-wasm-6.3.1.tgz#67f720e7f9694fef096abe9d60abbac02e032383" - integrity sha512-idSlzKGVzCfeCMRHsacRvqwojSaTadFxL/Dbls4z1thvfa3U9Ku0d2qVtlwg7Hj+tYWDiuP8Kygs+6bQwfs0XA== - dependencies: - "@babel/runtime" "^7.18.9" - "@polkadot/wasm-util" "6.3.1" - "@polkadot/wasm-crypto-wasm@6.4.1": version "6.4.1" resolved "https://registry.yarnpkg.com/@polkadot/wasm-crypto-wasm/-/wasm-crypto-wasm-6.4.1.tgz#97180f80583b18f6a13c1054fa5f7e8da40b1028" @@ -5654,62 +3676,6 @@ "@babel/runtime" "^7.20.6" "@polkadot/wasm-util" "6.4.1" -"@polkadot/wasm-crypto-wasm@^4.6.1": - version "4.6.1" - resolved "https://registry.yarnpkg.com/@polkadot/wasm-crypto-wasm/-/wasm-crypto-wasm-4.6.1.tgz#882d8199e216966c612f56a18e31f6aaae77e7eb" - integrity sha512-NI3JVwmLjrSYpSVuhu0yeQYSlsZrdpK41UC48sY3kyxXC71pi6OVePbtHS1K3xh3FFmDd9srSchExi3IwzKzMw== - dependencies: - "@babel/runtime" "^7.17.2" - -"@polkadot/wasm-crypto-wasm@^5.1.1": - version "5.1.1" - resolved "https://registry.yarnpkg.com/@polkadot/wasm-crypto-wasm/-/wasm-crypto-wasm-5.1.1.tgz#dc371755a05fe93f87a2754a2bcf1ff42e4bb541" - integrity sha512-F9PZ30J2S8vUNl2oY7Myow5Xsx5z5uNVpnNlJwlmY8IXBvyucvyQ4HSdhJsrbs4W1BfFc0mHghxgp0FbBCnf/Q== - dependencies: - "@babel/runtime" "^7.17.8" - -"@polkadot/wasm-crypto@^4.0.2", "@polkadot/wasm-crypto@^4.4.1": - version "4.6.1" - resolved "https://registry.yarnpkg.com/@polkadot/wasm-crypto/-/wasm-crypto-4.6.1.tgz#12f8481e6f9021928435168beb0697d57ff573e9" - integrity sha512-2wEftBDxDG+TN8Ah6ogtvzjdZdcF0mAjU4UNNOfpmkBCxQYZOrAHB8HXhzo3noSsKkLX7PDX57NxvJ9OhoTAjw== - dependencies: - "@babel/runtime" "^7.17.2" - "@polkadot/wasm-crypto-asmjs" "^4.6.1" - "@polkadot/wasm-crypto-wasm" "^4.6.1" - -"@polkadot/wasm-crypto@^5.1.1": - version "5.1.1" - resolved "https://registry.yarnpkg.com/@polkadot/wasm-crypto/-/wasm-crypto-5.1.1.tgz#d1f8a0da631028ba904c374c1e8496ab3ba4636b" - integrity sha512-JCcAVfH8DhYuEyd4oX1ouByxhou0TvpErKn8kHjtzt7+tRoFi0nzWlmK4z49vszsV3JJgXxV81i10C0BYlwTcQ== - dependencies: - "@babel/runtime" "^7.17.8" - "@polkadot/wasm-crypto-asmjs" "^5.1.1" - "@polkadot/wasm-crypto-wasm" "^5.1.1" - -"@polkadot/wasm-crypto@^6.1.1": - version "6.1.1" - resolved "https://registry.yarnpkg.com/@polkadot/wasm-crypto/-/wasm-crypto-6.1.1.tgz#8e2c2d64d24eeaa78eb0b74ea1c438b7bc704176" - integrity sha512-hv9RCbMYtgjCy7+FKZFnO2Afu/whax9sk6udnZqGRBRiwaNagtyliWZGrKNGvaXMIO0VyaY4jWUwSzUgPrLu1A== - dependencies: - "@babel/runtime" "^7.17.9" - "@polkadot/wasm-bridge" "6.1.1" - "@polkadot/wasm-crypto-asmjs" "6.1.1" - "@polkadot/wasm-crypto-init" "6.1.1" - "@polkadot/wasm-crypto-wasm" "6.1.1" - "@polkadot/wasm-util" "6.1.1" - -"@polkadot/wasm-crypto@^6.2.2", "@polkadot/wasm-crypto@^6.3.1": - version "6.3.1" - resolved "https://registry.yarnpkg.com/@polkadot/wasm-crypto/-/wasm-crypto-6.3.1.tgz#63f5798aca2b2ff0696f190e6862d9781d8f280c" - integrity sha512-OO8h0qeVkqp4xYZaRVl4iuWOEtq282pNBHDKb6SOJuI2g59eWGcKh4EQU9Me2VP6qzojIqptrkrVt7KQXC68gA== - dependencies: - "@babel/runtime" "^7.18.9" - "@polkadot/wasm-bridge" "6.3.1" - "@polkadot/wasm-crypto-asmjs" "6.3.1" - "@polkadot/wasm-crypto-init" "6.3.1" - "@polkadot/wasm-crypto-wasm" "6.3.1" - "@polkadot/wasm-util" "6.3.1" - "@polkadot/wasm-crypto@^6.4.1": version "6.4.1" resolved "https://registry.yarnpkg.com/@polkadot/wasm-crypto/-/wasm-crypto-6.4.1.tgz#79310e23ad1ca62362ba893db6a8567154c2536a" @@ -5722,20 +3688,6 @@ "@polkadot/wasm-crypto-wasm" "6.4.1" "@polkadot/wasm-util" "6.4.1" -"@polkadot/wasm-util@6.1.1": - version "6.1.1" - resolved "https://registry.yarnpkg.com/@polkadot/wasm-util/-/wasm-util-6.1.1.tgz#58a566aba68f90d2a701c78ad49a1a9521b17f5b" - integrity sha512-DgpLoFXMT53UKcfZ8eT2GkJlJAOh89AWO+TP6a6qeZQpvXVe5f1yR45WQpkZlgZyUP+/19+kY56GK0pQxfslqg== - dependencies: - "@babel/runtime" "^7.17.9" - -"@polkadot/wasm-util@6.3.1": - version "6.3.1" - resolved "https://registry.yarnpkg.com/@polkadot/wasm-util/-/wasm-util-6.3.1.tgz#439ebb68a436317af388ed6438b8f879df3afcda" - integrity sha512-12oAv5J7Yoc9m6jixrSaQCxpOkWOyzHx3DMC8qmLjRiwdBWxqLmImOVRVnFsbaxqSbhBIHRuJphVxWE+GZETDg== - dependencies: - "@babel/runtime" "^7.18.9" - "@polkadot/wasm-util@6.4.1": version "6.4.1" resolved "https://registry.yarnpkg.com/@polkadot/wasm-util/-/wasm-util-6.4.1.tgz#74aecc85bec427a9225d9874685944ea3dc3ab76" @@ -5743,446 +3695,69 @@ dependencies: "@babel/runtime" "^7.20.6" -"@polkadot/x-bigint@10.1.12", "@polkadot/x-bigint@^10.1.1", "@polkadot/x-bigint@^10.1.12": - version "10.1.12" - resolved "https://registry.yarnpkg.com/@polkadot/x-bigint/-/x-bigint-10.1.12.tgz#0aa4412851d2b8a2d18511549488ddf149fe4c7b" - integrity sha512-n9cRXhdPvA9qO9/dgb32Ej/5t4mI4KnBYoPqlAcGviOnn7lc9yEPlYSMfgt+4p7F2bX8o6nbmbvBXqZL457Yhw== - dependencies: - "@babel/runtime" "^7.20.1" - "@polkadot/x-global" "10.1.12" - -"@polkadot/x-bigint@10.1.6": - version "10.1.6" - resolved "https://registry.yarnpkg.com/@polkadot/x-bigint/-/x-bigint-10.1.6.tgz#25ec3af7217862808e20ccf0ecd1dfafb1bc31bd" - integrity sha512-yeBZQ9+u49KqDBaeSw+ytshqzyaScKrDjAxpWCfOGxJaB+5Nv1W7fqi3OJ4S/HN5DYItr0a6UC14e1hiZUtZCg== - dependencies: - "@babel/runtime" "^7.18.9" - "@polkadot/x-global" "10.1.6" - -"@polkadot/x-bigint@10.1.9": - version "10.1.9" - resolved "https://registry.yarnpkg.com/@polkadot/x-bigint/-/x-bigint-10.1.9.tgz#d8b450df90f3cb0e2b1f2583d35a184cc3d6ad50" - integrity sha512-1iA30V8+FdVK0I1kRSShTV/XgjBtC5Gl2EQl2Z9rrkK27mtjgIr14hD4nFfM176UgKvERinfDXzzRFU/p5w/Mg== - dependencies: - "@babel/runtime" "^7.19.0" - "@polkadot/x-global" "10.1.9" - -"@polkadot/x-bigint@10.2.1", "@polkadot/x-bigint@^10.2.1": - version "10.2.1" - resolved "https://registry.yarnpkg.com/@polkadot/x-bigint/-/x-bigint-10.2.1.tgz#aa2d4384bb4ae6b5a3f333aa25bf6fd64d9006c5" - integrity sha512-asFroI2skC4gYv0oIqqb84DqCCxhNUTSCKobEg57WdXoT4TKrN9Uetg2AMSIHRiX/9lP3EPMhUjM1VVGobTQRQ== - dependencies: - "@babel/runtime" "^7.20.6" - "@polkadot/x-global" "10.2.1" - -"@polkadot/x-bigint@8.7.1": - version "8.7.1" - resolved "https://registry.yarnpkg.com/@polkadot/x-bigint/-/x-bigint-8.7.1.tgz#a496225def32e98c430c76b91c1579f48acf501a" - integrity sha512-ClkhgdB/KqcAKk3zA6Qw8wBL6Wz67pYTPkrAtImpvoPJmR+l4RARauv+MH34JXMUNlNb3aUwqN6lq2Z1zN+mJg== - dependencies: - "@babel/runtime" "^7.17.8" - "@polkadot/x-global" "8.7.1" - -"@polkadot/x-bigint@9.4.1": - version "9.4.1" - resolved "https://registry.yarnpkg.com/@polkadot/x-bigint/-/x-bigint-9.4.1.tgz#0a7c6b5743a6fb81ab6a1c3a48a584e774c37910" - integrity sha512-KlbXboegENoyrpjj+eXfY13vsqrXgk4620zCAUhKNH622ogdvAepHbY/DpV6w0FLEC6MwN9zd5cRuDBEXVeWiw== - dependencies: - "@babel/runtime" "^7.18.3" - "@polkadot/x-global" "9.4.1" - -"@polkadot/x-bigint@9.7.2": - version "9.7.2" - resolved "https://registry.yarnpkg.com/@polkadot/x-bigint/-/x-bigint-9.7.2.tgz#ec79977335dce173a81e45247bdfd46f3b301702" - integrity sha512-qi8/DTGypFSt5vvNOsYcEaqH72lymfyidGlsHlZ6e7nNASnEhk/NaOcINiTr1ds+fpu4dtKXWAIPZufujf2JeQ== - dependencies: - "@babel/runtime" "^7.18.6" - "@polkadot/x-global" "9.7.2" - -"@polkadot/x-fetch@^10.1.1", "@polkadot/x-fetch@^10.1.12": - version "10.1.12" - resolved "https://registry.yarnpkg.com/@polkadot/x-fetch/-/x-fetch-10.1.12.tgz#734e22cd131d6f2e3e695b7c8c953c1f71fc859a" - integrity sha512-t40R3Vi2itsqT9bDAGxNYSV1+D8JQX4gCQoA8jQSjQe5xfSF9WidbcvDONsPhsunYe8gOb0FhdNm7ruuAdPNNw== +"@polkadot/x-bigint@10.4.2", "@polkadot/x-bigint@^10.4.2": + version "10.4.2" + resolved "https://registry.yarnpkg.com/@polkadot/x-bigint/-/x-bigint-10.4.2.tgz#7eb2ec732259df48b5a00f07879a1331e05606ec" + integrity sha512-awRiox+/XSReLzimAU94fPldowiwnnMUkQJe8AebYhNocAj6SJU00GNoj6j6tAho6yleOwrTJXZaWFBaQVJQNg== dependencies: - "@babel/runtime" "^7.20.1" - "@polkadot/x-global" "10.1.12" - "@types/node-fetch" "^2.6.2" - node-fetch "^3.3.0" + "@babel/runtime" "^7.20.13" + "@polkadot/x-global" "10.4.2" -"@polkadot/x-fetch@^10.1.11", "@polkadot/x-fetch@^10.2.1": - version "10.2.1" - resolved "https://registry.yarnpkg.com/@polkadot/x-fetch/-/x-fetch-10.2.1.tgz#cb5b33da1d91787eb2e5207ef62806a75ef3c62f" - integrity sha512-6ASJUZIrbLaKW+AOW7E5CuktwJwa2LHhxxRyJe398HxZUjJRjO2VJPdqoSwwCYvfFa1TcIr3FDWS63ooDfvGMA== +"@polkadot/x-fetch@^10.1.11", "@polkadot/x-fetch@^10.4.2": + version "10.4.2" + resolved "https://registry.yarnpkg.com/@polkadot/x-fetch/-/x-fetch-10.4.2.tgz#bc6ba70de71a252472fbe36180511ed920e05f05" + integrity sha512-Ubb64yaM4qwhogNP+4mZ3ibRghEg5UuCYRMNaCFoPgNAY8tQXuDKrHzeks3+frlmeH9YRd89o8wXLtWouwZIcw== dependencies: - "@babel/runtime" "^7.20.6" - "@polkadot/x-global" "10.2.1" + "@babel/runtime" "^7.20.13" + "@polkadot/x-global" "10.4.2" "@types/node-fetch" "^2.6.2" node-fetch "^3.3.0" -"@polkadot/x-fetch@^8.7.1": - version "8.7.1" - resolved "https://registry.yarnpkg.com/@polkadot/x-fetch/-/x-fetch-8.7.1.tgz#dc866e7aa87c39b2e64c87f734b8fbaf1b9190e1" - integrity sha512-ygNparcalYFGbspXtdtZOHvNXZBkNgmNO+um9C0JYq74K5OY9/be93uyfJKJ8JcRJtOqBfVDsJpbiRkuJ1PRfg== - dependencies: - "@babel/runtime" "^7.17.8" - "@polkadot/x-global" "8.7.1" - "@types/node-fetch" "^2.6.1" - node-fetch "^2.6.7" - -"@polkadot/x-fetch@^9.0.1": - version "9.7.2" - resolved "https://registry.yarnpkg.com/@polkadot/x-fetch/-/x-fetch-9.7.2.tgz#bc4091603114a4182ab06b8d7d127a3dd730cf48" - integrity sha512-ysXpPNq2S+L98hKow3d59prU4QFRg5N86pMkJdONc4VxtKITVY2MfdLVCqfEOOFuuwCzE7Sfmx53I4XpDgbP7A== - dependencies: - "@babel/runtime" "^7.18.6" - "@polkadot/x-global" "9.7.2" - "@types/node-fetch" "^2.6.2" - node-fetch "^2.6.7" - -"@polkadot/x-fetch@^9.4.1": - version "9.4.1" - resolved "https://registry.yarnpkg.com/@polkadot/x-fetch/-/x-fetch-9.4.1.tgz#92802d3880db826a90bf1be90174a9fc73fc044a" - integrity sha512-CZFPZKgy09TOF5pOFRVVhGrAaAPdSMyrUSKwdO2I8DzdIE1tmjnol50dlnZja5t8zTD0n1uIY1H4CEWwc5NF/g== - dependencies: - "@babel/runtime" "^7.18.3" - "@polkadot/x-global" "9.4.1" - "@types/node-fetch" "^2.6.1" - node-fetch "^2.6.7" - -"@polkadot/x-global@10.1.12", "@polkadot/x-global@^10.1.1", "@polkadot/x-global@^10.1.12": - version "10.1.12" - resolved "https://registry.yarnpkg.com/@polkadot/x-global/-/x-global-10.1.12.tgz#4af9eeca9f3d371fbe4f348681a46bb1dc82ed35" - integrity sha512-APFCIVoyaB9rhgcg2j5ayW0WVTSDO4yUriyrOPgX4A4ZJ6DFV+w30h9uWtfKzNzAV6dDs6pDNlB0K4Mpi5CmcA== - dependencies: - "@babel/runtime" "^7.20.1" - -"@polkadot/x-global@10.1.6": - version "10.1.6" - resolved "https://registry.yarnpkg.com/@polkadot/x-global/-/x-global-10.1.6.tgz#af6e3c38384d64c69e66607b7a6e794822c65e40" - integrity sha512-/nraYZg0hdSjbczhDBAsHlEqeZLs0u0xa8HJrfH2lq8+HOIYkQpJPHOqiQIvEe/VFRq7Xnbij+4uffV+otzB/w== - dependencies: - "@babel/runtime" "^7.18.9" - -"@polkadot/x-global@10.1.9": - version "10.1.9" - resolved "https://registry.yarnpkg.com/@polkadot/x-global/-/x-global-10.1.9.tgz#a2a380ed7816cfc553ca9b73dc401c36fbafaaef" - integrity sha512-arsQEUbUccEI8pt0Bngk66vpJlMC/sZ38xivwrR8MVi2u8FdWFwb7udvwGRbXujHmPgGfRqxQujheKSR3d2ToA== - dependencies: - "@babel/runtime" "^7.19.0" - -"@polkadot/x-global@10.2.1", "@polkadot/x-global@^10.2.1": - version "10.2.1" - resolved "https://registry.yarnpkg.com/@polkadot/x-global/-/x-global-10.2.1.tgz#6fbaab05653e680adc8c69c07947eee49afc1238" - integrity sha512-kWmPku2lCcoYKU16+lWGOb95+6Lu9zo1trvzTWmAt7z0DXw2GlD9+qmDTt5iYGtguJsGXoRZDGilDTo3MeFrkA== - dependencies: - "@babel/runtime" "^7.20.6" - -"@polkadot/x-global@6.11.1": - version "6.11.1" - resolved "https://registry.yarnpkg.com/@polkadot/x-global/-/x-global-6.11.1.tgz#c292b3825fea60e9b33fff1790323fc57de1ca5d" - integrity sha512-lsBK/e4KbjfieyRmnPs7bTiGbP/6EoCZz7rqD/voNS5qsJAaXgB9LR+ilubun9gK/TDpebyxgO+J19OBiQPIRw== - dependencies: - "@babel/runtime" "^7.14.6" - -"@polkadot/x-global@7.9.2": - version "7.9.2" - resolved "https://registry.yarnpkg.com/@polkadot/x-global/-/x-global-7.9.2.tgz#b272b0a3bedaad3bcbf075ec4682abe68cf2a850" - integrity sha512-JX5CrGWckHf1P9xKXq4vQCAuMUbL81l2hOWX7xeP8nv4caHEpmf5T1wD1iMdQBL5PFifo6Pg0V6/oZBB+bts7A== - dependencies: - "@babel/runtime" "^7.16.3" - -"@polkadot/x-global@8.7.1", "@polkadot/x-global@^8.7.1": - version "8.7.1" - resolved "https://registry.yarnpkg.com/@polkadot/x-global/-/x-global-8.7.1.tgz#b972044125a4fe059f4aef7c15a4e22d18179095" - integrity sha512-WOgUor16IihgNVdiTVGAWksYLUAlqjmODmIK1cuWrLOZtV1VBomWcb3obkO9sh5P6iWziAvCB/i+L0vnTN9ZCA== - dependencies: - "@babel/runtime" "^7.17.8" - -"@polkadot/x-global@9.4.1", "@polkadot/x-global@^9.4.1": - version "9.4.1" - resolved "https://registry.yarnpkg.com/@polkadot/x-global/-/x-global-9.4.1.tgz#3bd44862ea2b7e0fb2de766dfa4d56bb46d19e17" - integrity sha512-eN4oZeRdIKQeUPNN7OtH5XeYp349d8V9+gW6W0BmCfB2lTg8TDlG1Nj+Cyxpjl9DNF5CiKudTq72zr0dDSRbwA== +"@polkadot/x-global@10.4.2", "@polkadot/x-global@^10.4.2": + version "10.4.2" + resolved "https://registry.yarnpkg.com/@polkadot/x-global/-/x-global-10.4.2.tgz#5662366e3deda0b4c8f024b2d902fa838f9e60a4" + integrity sha512-g6GXHD/ykZvHap3M6wh19dO70Zm43l4jEhlxf5LtTo5/0/UporFCXr2YJYZqfbn9JbQwl1AU+NroYio+vtJdiA== dependencies: - "@babel/runtime" "^7.18.3" + "@babel/runtime" "^7.20.13" -"@polkadot/x-global@9.7.2", "@polkadot/x-global@^9.0.1": +"@polkadot/x-global@^9.4.1": version "9.7.2" resolved "https://registry.yarnpkg.com/@polkadot/x-global/-/x-global-9.7.2.tgz#9847fd1da13989f321ca621e85477ba70fd8d55a" integrity sha512-3NN5JhjosaelaFWBJSlv9mb/gDAlt7RuZ8NKlOjB+LQHd9g6ZbnYi5wwjW+i/x/3E4IVbBx66uvWgNaw7IBrkg== dependencies: "@babel/runtime" "^7.18.6" -"@polkadot/x-randomvalues@10.1.12": - version "10.1.12" - resolved "https://registry.yarnpkg.com/@polkadot/x-randomvalues/-/x-randomvalues-10.1.12.tgz#484b1d400aa64a6f51d060b8d84ef8cbc184db05" - integrity sha512-oldth/zJAwysb+1uSxB7GIgCnuJmK33ZbxRl8vOwHrIKgKJGxJufRxLtZg1Pq530DW6V3z5TwOWacx58ggfN3Q== - dependencies: - "@babel/runtime" "^7.20.1" - "@polkadot/x-global" "10.1.12" - -"@polkadot/x-randomvalues@10.1.6": - version "10.1.6" - resolved "https://registry.yarnpkg.com/@polkadot/x-randomvalues/-/x-randomvalues-10.1.6.tgz#9dfb0e403d60139b5ac4ba7ebeecf395a8f193c6" - integrity sha512-NDOr7Zvv3lN0Z3+gt/RfmZbeOXg21402ggiBdL8ni9rf0ZMUzFLqRNsbm0334L0lApfhfvxm98MGfK+gq6uVZg== - dependencies: - "@babel/runtime" "^7.18.9" - "@polkadot/x-global" "10.1.6" - -"@polkadot/x-randomvalues@10.1.9": - version "10.1.9" - resolved "https://registry.yarnpkg.com/@polkadot/x-randomvalues/-/x-randomvalues-10.1.9.tgz#a2c769b6090d768590bed5d32d8c1b4415e695c9" - integrity sha512-owSk4vVuy18kZI+L8pLV8Zt8Blsv0BM7j0VcNu4q6gdsduU4a5entGr3t2a3P4dj3hU65B4KgbSy516y//jMng== - dependencies: - "@babel/runtime" "^7.19.0" - "@polkadot/x-global" "10.1.9" - -"@polkadot/x-randomvalues@10.2.1": - version "10.2.1" - resolved "https://registry.yarnpkg.com/@polkadot/x-randomvalues/-/x-randomvalues-10.2.1.tgz#1c463625c0b7cf775e94594f522eb21a5229b42e" - integrity sha512-bEwG6j/+HMZ5LIkyzRbTB0N1Wz2lHyxP25pPFgHFqGqon/KZoRN5kxOwEJ1DpPJIv+9PVn5tt7bc4R3qsaZ93g== - dependencies: - "@babel/runtime" "^7.20.6" - "@polkadot/x-global" "10.2.1" - -"@polkadot/x-randomvalues@6.11.1": - version "6.11.1" - resolved "https://registry.yarnpkg.com/@polkadot/x-randomvalues/-/x-randomvalues-6.11.1.tgz#f006fa250c8e82c92ccb769976a45a8e7f3df28b" - integrity sha512-2MfUfGZSOkuPt7GF5OJkPDbl4yORI64SUuKM25EGrJ22o1UyoBnPOClm9eYujLMD6BfDZRM/7bQqqoLW+NuHVw== - dependencies: - "@babel/runtime" "^7.14.6" - "@polkadot/x-global" "6.11.1" - -"@polkadot/x-randomvalues@7.9.2": - version "7.9.2" - resolved "https://registry.yarnpkg.com/@polkadot/x-randomvalues/-/x-randomvalues-7.9.2.tgz#0c9bb7b48a0791c2a32e9605a31a5ce56fee621d" - integrity sha512-svQfG31yCXf6yVyIgP0NgCzEy7oc3Lw054ZspkaqjOivxYdrXaf5w3JSSUyM/MRjI2+nk+B/EyJoMYcfSwTfsQ== - dependencies: - "@babel/runtime" "^7.16.3" - "@polkadot/x-global" "7.9.2" - -"@polkadot/x-randomvalues@8.7.1": - version "8.7.1" - resolved "https://registry.yarnpkg.com/@polkadot/x-randomvalues/-/x-randomvalues-8.7.1.tgz#b7cc358c2a6d20f7e7798d45d1d5c7ac8c9ab4f2" - integrity sha512-njt17MlfN6yNyNEti7fL12lr5qM6A1aSGkWKVuqzc7XwSBesifJuW4km5u6r2gwhXjH2eHDv9SoQ7WXu8vrrkg== - dependencies: - "@babel/runtime" "^7.17.8" - "@polkadot/x-global" "8.7.1" - -"@polkadot/x-randomvalues@9.4.1": - version "9.4.1" - resolved "https://registry.yarnpkg.com/@polkadot/x-randomvalues/-/x-randomvalues-9.4.1.tgz#ab995b3a22aee6bffc18490e636e1a7409f36a15" - integrity sha512-TLOQw3JNPgCrcq9WO2ipdeG8scsSreu3m9hwj3n7nX/QKlVzSf4G5bxJo5TW1dwcUdHwBuVox+3zgCmo+NPh+Q== - dependencies: - "@babel/runtime" "^7.18.3" - "@polkadot/x-global" "9.4.1" - -"@polkadot/x-randomvalues@9.7.2": - version "9.7.2" - resolved "https://registry.yarnpkg.com/@polkadot/x-randomvalues/-/x-randomvalues-9.7.2.tgz#d580b0e9149ea22b2afebba5d7b1368371f7086d" - integrity sha512-819slnXNpoVtqdhjI19ao7w5m+Zwx11VfwCZkFQypVv3b/1UEoKG/baJA9dVI6yMvhnBN//i8mLgNy3IXWbVVw== - dependencies: - "@babel/runtime" "^7.18.6" - "@polkadot/x-global" "9.7.2" - -"@polkadot/x-rxjs@^6.11.1": - version "6.11.1" - resolved "https://registry.yarnpkg.com/@polkadot/x-rxjs/-/x-rxjs-6.11.1.tgz#5454708b61da70eea05708611d9148fce9372498" - integrity sha512-zIciEmij7SUuXXg9g/683Irx6GogxivrQS2pgBir2DI/YZq+um52+Dqg1mqsEZt74N4KMTMnzAZAP6LJOBOMww== - dependencies: - "@babel/runtime" "^7.14.6" - rxjs "^6.6.7" - -"@polkadot/x-textdecoder@10.1.12": - version "10.1.12" - resolved "https://registry.yarnpkg.com/@polkadot/x-textdecoder/-/x-textdecoder-10.1.12.tgz#1873750006c7f075cb928f3bb1f8a173c2d7ef54" - integrity sha512-YKEr3QiTu8zxosVx9WWFhMmFw4hBmAKWkgd7dhpMU+wIaUlSBriV/7vL+HnvJMIKf1jz3iAZ7i0oDGfvj1cZ6w== - dependencies: - "@babel/runtime" "^7.20.1" - "@polkadot/x-global" "10.1.12" - -"@polkadot/x-textdecoder@10.1.6": - version "10.1.6" - resolved "https://registry.yarnpkg.com/@polkadot/x-textdecoder/-/x-textdecoder-10.1.6.tgz#3eaceb8f9b9e6872232b63e9c7a0ac84bdcc7eba" - integrity sha512-T+jvyv6OvUgGfOlMDLkPKEmQnZGP1CNohdEDeRr93AmeYikIfbC20qYuAc0bEBXR7/rPXgnqiEfrpkL2W3r/Ig== - dependencies: - "@babel/runtime" "^7.18.9" - "@polkadot/x-global" "10.1.6" - -"@polkadot/x-textdecoder@10.1.9": - version "10.1.9" - resolved "https://registry.yarnpkg.com/@polkadot/x-textdecoder/-/x-textdecoder-10.1.9.tgz#25cf4947626a96373b18a29a37f8761cc1de3b04" - integrity sha512-RSe1qgbaJ6+dnmKR+yIYt9uKfZreJQns1sSQBJra49xBk2ImQmon7wwu19TjycR6kCEdI2WGRdZaa8kkrgIsYQ== - dependencies: - "@babel/runtime" "^7.19.0" - "@polkadot/x-global" "10.1.9" - -"@polkadot/x-textdecoder@10.2.1": - version "10.2.1" - resolved "https://registry.yarnpkg.com/@polkadot/x-textdecoder/-/x-textdecoder-10.2.1.tgz#c1778ef35e2aa8db8f11bbe31a5bbf5e46017d7d" - integrity sha512-hpFmrdv/rrSM4UNaV8TJBgMtwXsYlNgBTSUmnKWwJIN3PhOUeYxl1qIbPchxGbJBc35WviJCZe7rlLja9JvFcw== - dependencies: - "@babel/runtime" "^7.20.6" - "@polkadot/x-global" "10.2.1" - -"@polkadot/x-textdecoder@6.11.1": - version "6.11.1" - resolved "https://registry.yarnpkg.com/@polkadot/x-textdecoder/-/x-textdecoder-6.11.1.tgz#6cc314645681cc4639085c03b65328671c7f182c" - integrity sha512-DI1Ym2lyDSS/UhnTT2e9WutukevFZ0WGpzj4eotuG2BTHN3e21uYtYTt24SlyRNMrWJf5+TkZItmZeqs1nwAfQ== - dependencies: - "@babel/runtime" "^7.14.6" - "@polkadot/x-global" "6.11.1" - -"@polkadot/x-textdecoder@7.9.2": - version "7.9.2" - resolved "https://registry.yarnpkg.com/@polkadot/x-textdecoder/-/x-textdecoder-7.9.2.tgz#a78548e33efeb3a25f761fec9787b2bcae7f0608" - integrity sha512-wfwbSHXPhrOAl12QvlIOGNkMH/N/h8PId2ytIjvM/8zPPFB5Il6DWSFLtVapOGEpIFjEWbd5t8Td4pHBVXIEbg== - dependencies: - "@babel/runtime" "^7.16.3" - "@polkadot/x-global" "7.9.2" - -"@polkadot/x-textdecoder@8.7.1": - version "8.7.1" - resolved "https://registry.yarnpkg.com/@polkadot/x-textdecoder/-/x-textdecoder-8.7.1.tgz#b706ef98d5a033d02c633009fb8dab4a4f9d7d55" - integrity sha512-ia0Ie2zi4VdQdNVD2GE2FZzBMfX//hEL4w546RMJfZM2LqDS674LofHmcyrsv5zscLnnRyCxZC1+J2dt+6MDIA== - dependencies: - "@babel/runtime" "^7.17.8" - "@polkadot/x-global" "8.7.1" - -"@polkadot/x-textdecoder@9.4.1": - version "9.4.1" - resolved "https://registry.yarnpkg.com/@polkadot/x-textdecoder/-/x-textdecoder-9.4.1.tgz#1d891b82f4192d92dd373d14ea4b5654d0130484" - integrity sha512-yLulcgVASFUBJqrvS6Ssy0ko9teAfbu1ajH0r3Jjnqkpmmz2DJ1CS7tAktVa7THd4GHPGeKAVfxl+BbV/LZl+w== - dependencies: - "@babel/runtime" "^7.18.3" - "@polkadot/x-global" "9.4.1" - -"@polkadot/x-textdecoder@9.7.2": - version "9.7.2" - resolved "https://registry.yarnpkg.com/@polkadot/x-textdecoder/-/x-textdecoder-9.7.2.tgz#c94ea6c8f510fdf579659248ede9421854e32b42" - integrity sha512-hhrMNZwJBmusdpqjDRpOHZoMB4hpyJt9Gu9Bi9is7/D/vq/hpxq8z7s6NxrbRyXJf1SIk6NMK0jf5XjRLdKdbw== - dependencies: - "@babel/runtime" "^7.18.6" - "@polkadot/x-global" "9.7.2" - -"@polkadot/x-textencoder@10.1.12": - version "10.1.12" - resolved "https://registry.yarnpkg.com/@polkadot/x-textencoder/-/x-textencoder-10.1.12.tgz#5db7bcb9682717c8b8c4c70fed8c5d01cbe61d8d" - integrity sha512-WgHAUhiepWBAcOMOAJnBl2mZNu5KPmTndg4f1Z1CwNdg/AhAhJL/yh98f5KH1aajNkC+5xutlOzdmEySg+e21g== - dependencies: - "@babel/runtime" "^7.20.1" - "@polkadot/x-global" "10.1.12" - -"@polkadot/x-textencoder@10.1.6": - version "10.1.6" - resolved "https://registry.yarnpkg.com/@polkadot/x-textencoder/-/x-textencoder-10.1.6.tgz#a7d2d9bcd7d9c6bc663d524a278aa4a6679ecf28" - integrity sha512-e+iHdR1P/8xAc54l3gHfqozH6ZfxPkKlVVaz3vOMnzfc8cA3Zw93mLYkGtLDqv+825LkSrWSmb/bDZb6YyEEXg== - dependencies: - "@babel/runtime" "^7.18.9" - "@polkadot/x-global" "10.1.6" - -"@polkadot/x-textencoder@10.1.9": - version "10.1.9" - resolved "https://registry.yarnpkg.com/@polkadot/x-textencoder/-/x-textencoder-10.1.9.tgz#81f169b783e72185c5f907afc2b918b1c80d2b21" - integrity sha512-IXW4+G2r6wTpcxCGi2PeGd1aQRv0+FsgD9L7FDVjCejdgk6W87knIAaFTTYEmIF/x1clUqhw3c5Gxb0lUOchUw== - dependencies: - "@babel/runtime" "^7.19.0" - "@polkadot/x-global" "10.1.9" - -"@polkadot/x-textencoder@10.2.1": - version "10.2.1" - resolved "https://registry.yarnpkg.com/@polkadot/x-textencoder/-/x-textencoder-10.2.1.tgz#c09562c73a44659243075d43b007b5c1b39c57a8" - integrity sha512-4gMyY6DCH34KA++bawu/zlUJ0/8+aZJsurwjRBbkdfOS2uLo0K+vJ5GBevAhl0VSznM36ptfh/MpkIBKK/6R0g== - dependencies: - "@babel/runtime" "^7.20.6" - "@polkadot/x-global" "10.2.1" - -"@polkadot/x-textencoder@6.11.1": - version "6.11.1" - resolved "https://registry.yarnpkg.com/@polkadot/x-textencoder/-/x-textencoder-6.11.1.tgz#73e89da5b91954ae380042c19314c90472f59d9e" - integrity sha512-8ipjWdEuqFo+R4Nxsc3/WW9CSEiprX4XU91a37ZyRVC4e9R1bmvClrpXmRQLVcAQyhRvG8DKOOtWbz8xM+oXKg== - dependencies: - "@babel/runtime" "^7.14.6" - "@polkadot/x-global" "6.11.1" - -"@polkadot/x-textencoder@7.9.2": - version "7.9.2" - resolved "https://registry.yarnpkg.com/@polkadot/x-textencoder/-/x-textencoder-7.9.2.tgz#b32bfd6fbff8587c56452f58252a52d62bbcd5b9" - integrity sha512-A19wwYINuZwU2dUyQ/mMzB0ISjyfc4cISfL4zCMUAVgj7xVoXMYV2GfjNdMpA8Wsjch3su6pxLbtJ2wU03sRTQ== - dependencies: - "@babel/runtime" "^7.16.3" - "@polkadot/x-global" "7.9.2" - -"@polkadot/x-textencoder@8.7.1": - version "8.7.1" - resolved "https://registry.yarnpkg.com/@polkadot/x-textencoder/-/x-textencoder-8.7.1.tgz#7820e30081e8e0a607c1c27b9e3486d82d1a723e" - integrity sha512-XDO0A27Xy+eJCKSxENroB8Dcnl+UclGG4ZBei+P/BqZ9rsjskUyd2Vsl6peMXAcsxwOE7g0uTvujoGM8jpKOXw== - dependencies: - "@babel/runtime" "^7.17.8" - "@polkadot/x-global" "8.7.1" - -"@polkadot/x-textencoder@9.4.1": - version "9.4.1" - resolved "https://registry.yarnpkg.com/@polkadot/x-textencoder/-/x-textencoder-9.4.1.tgz#09c47727d7713884cf82fd773e478487fe39d479" - integrity sha512-/47wa31jBa43ULqMO60vzcJigTG+ZAGNcyT5r6hFLrQzRzc8nIBjIOD8YWtnKM92r9NvlNv2wJhdamqyU0mntg== - dependencies: - "@babel/runtime" "^7.18.3" - "@polkadot/x-global" "9.4.1" - -"@polkadot/x-textencoder@9.7.2": - version "9.7.2" - resolved "https://registry.yarnpkg.com/@polkadot/x-textencoder/-/x-textencoder-9.7.2.tgz#2ae29fa5ca2c0353e7a1913eef710b2d45bdf0b2" - integrity sha512-GHbSdbMPixDAOnJ9cvL/x9sPNeHegPoDSqCAzY5H6/zHc/fNn0vUu0To9VpPgPhp/Jb9dbc0h8YqEyvOcOlphw== - dependencies: - "@babel/runtime" "^7.18.6" - "@polkadot/x-global" "9.7.2" - -"@polkadot/x-ws@^10.1.1", "@polkadot/x-ws@^10.1.12": - version "10.1.12" - resolved "https://registry.yarnpkg.com/@polkadot/x-ws/-/x-ws-10.1.12.tgz#a093d27c6698c0433efb1802358c97ee35416b4a" - integrity sha512-MmPNHAVBhCkd9Cv6i/ctcVC5Fo8fEeIlBk/GTOZSaLQZAP9jqFPrZZQE7n4oFLSw645z+RjlBodd3Y9Q9acfMg== - dependencies: - "@babel/runtime" "^7.20.1" - "@polkadot/x-global" "10.1.12" - "@types/websocket" "^1.0.5" - websocket "^1.0.34" - -"@polkadot/x-ws@^10.2.1": - version "10.2.1" - resolved "https://registry.yarnpkg.com/@polkadot/x-ws/-/x-ws-10.2.1.tgz#ec119c22a8cb7b9cde00e9909e37b6ba2845efd1" - integrity sha512-oS/WEHc1JSJ+xMArzFXbg1yEeaRrp6GsJLBvObj4DgTyqoWTR5fYkq1G1nHbyqdR729yAnR6755PdaWecIg98g== +"@polkadot/x-randomvalues@10.4.2": + version "10.4.2" + resolved "https://registry.yarnpkg.com/@polkadot/x-randomvalues/-/x-randomvalues-10.4.2.tgz#895f1220d5a4522a83d8d5014e3c1e03b129893e" + integrity sha512-mf1Wbpe7pRZHO0V3V89isPLqZOy5XGX2bCqsfUWHgb1NvV1MMx5TjVjdaYyNlGTiOkAmJKlOHshcfPU2sYWpNg== dependencies: - "@babel/runtime" "^7.20.6" - "@polkadot/x-global" "10.2.1" - "@types/websocket" "^1.0.5" - websocket "^1.0.34" + "@babel/runtime" "^7.20.13" + "@polkadot/x-global" "10.4.2" -"@polkadot/x-ws@^8.7.1": - version "8.7.1" - resolved "https://registry.yarnpkg.com/@polkadot/x-ws/-/x-ws-8.7.1.tgz#501c63c575e04bba68bdc32448e2c9b692f0411e" - integrity sha512-Mt0tcNzGXyKnN3DQ06alkv+JLtTfXWu6zSypFrrKHSQe3u79xMQ1nSicmpT3gWLhIa8YF+8CYJXMrqaXgCnDhw== +"@polkadot/x-textdecoder@10.4.2": + version "10.4.2" + resolved "https://registry.yarnpkg.com/@polkadot/x-textdecoder/-/x-textdecoder-10.4.2.tgz#93202f3e5ad0e7f75a3fa02d2b8a3343091b341b" + integrity sha512-d3ADduOKUTU+cliz839+KCFmi23pxTlabH7qh7Vs1GZQvXOELWdqFOqakdiAjtMn68n1KVF4O14Y+OUm7gp/zA== dependencies: - "@babel/runtime" "^7.17.8" - "@polkadot/x-global" "8.7.1" - "@types/websocket" "^1.0.5" - websocket "^1.0.34" + "@babel/runtime" "^7.20.13" + "@polkadot/x-global" "10.4.2" -"@polkadot/x-ws@^9.0.1": - version "9.7.2" - resolved "https://registry.yarnpkg.com/@polkadot/x-ws/-/x-ws-9.7.2.tgz#b35470b487d19be48c1fe39e1c73c623bf548b7e" - integrity sha512-yF2qKL00SGivbima22jxoBNYCZFI8Ph7dmnVm7fDztVtO8Fc2x30Lj3a8+qsSOrynLyJHAh2bjjQxpPmDCB9tw== +"@polkadot/x-textencoder@10.4.2": + version "10.4.2" + resolved "https://registry.yarnpkg.com/@polkadot/x-textencoder/-/x-textencoder-10.4.2.tgz#cd2e6c8a66b0b400a73f0164e99c510fb5c83501" + integrity sha512-mxcQuA1exnyv74Kasl5vxBq01QwckG088lYjc3KwmND6+pPrW2OWagbxFX5VFoDLDAE+UJtnUHsjdWyOTDhpQA== dependencies: - "@babel/runtime" "^7.18.6" - "@polkadot/x-global" "9.7.2" - "@types/websocket" "^1.0.5" - websocket "^1.0.34" + "@babel/runtime" "^7.20.13" + "@polkadot/x-global" "10.4.2" -"@polkadot/x-ws@^9.4.1": - version "9.4.1" - resolved "https://registry.yarnpkg.com/@polkadot/x-ws/-/x-ws-9.4.1.tgz#c48f2ef3e80532f4b366b57b6661429b46a16155" - integrity sha512-zQjVxXgHsBVn27u4bjY01cFO6XWxgv2b3MMOpNHTKTAs8SLEmFf0LcT7fBShimyyudyTeJld5pHApJ4qp1OXxA== +"@polkadot/x-ws@^10.4.2": + version "10.4.2" + resolved "https://registry.yarnpkg.com/@polkadot/x-ws/-/x-ws-10.4.2.tgz#4e9d88f37717570ccf942c6f4f63b06260f45025" + integrity sha512-3gHSTXAWQu1EMcMVTF5QDKHhEHzKxhAArweEyDXE7VsgKUP/ixxw4hVZBrkX122iI5l5mjSiooRSnp/Zl3xqDQ== dependencies: - "@babel/runtime" "^7.18.3" - "@polkadot/x-global" "9.4.1" + "@babel/runtime" "^7.20.13" + "@polkadot/x-global" "10.4.2" "@types/websocket" "^1.0.5" websocket "^1.0.34" @@ -6196,59 +3771,6 @@ resolved "https://registry.yarnpkg.com/@popperjs/core/-/core-2.11.4.tgz#d8c7b8db9226d2d7664553a0741ad7d0397ee503" integrity sha512-q/ytXxO5NKvyT37pmisQAItCFqA7FD/vNb8dgaJy3/630Fsc+Mz9/9f2SziBoIZ30TJooXyTwZmhi1zjXmObYg== -"@protobufjs/aspromise@^1.1.1", "@protobufjs/aspromise@^1.1.2": - version "1.1.2" - resolved "https://registry.yarnpkg.com/@protobufjs/aspromise/-/aspromise-1.1.2.tgz#9b8b0cc663d669a7d8f6f5d0893a14d348f30fbf" - integrity sha512-j+gKExEuLmKwvz3OgROXtrJ2UG2x8Ch2YZUxahh+s1F2HZ+wAceUNLkvy6zKCPVRkU++ZWQrdxsUeQXmcg4uoQ== - -"@protobufjs/base64@^1.1.2": - version "1.1.2" - resolved "https://registry.yarnpkg.com/@protobufjs/base64/-/base64-1.1.2.tgz#4c85730e59b9a1f1f349047dbf24296034bb2735" - integrity sha512-AZkcAA5vnN/v4PDqKyMR5lx7hZttPDgClv83E//FMNhR2TMcLUhfRUBHCmSl0oi9zMgDDqRUJkSxO3wm85+XLg== - -"@protobufjs/codegen@^2.0.4": - version "2.0.4" - resolved "https://registry.yarnpkg.com/@protobufjs/codegen/-/codegen-2.0.4.tgz#7ef37f0d010fb028ad1ad59722e506d9262815cb" - integrity sha512-YyFaikqM5sH0ziFZCN3xDC7zeGaB/d0IUb9CATugHWbd1FRFwWwt4ld4OYMPWu5a3Xe01mGAULCdqhMlPl29Jg== - -"@protobufjs/eventemitter@^1.1.0": - version "1.1.0" - resolved "https://registry.yarnpkg.com/@protobufjs/eventemitter/-/eventemitter-1.1.0.tgz#355cbc98bafad5978f9ed095f397621f1d066b70" - integrity sha512-j9ednRT81vYJ9OfVuXG6ERSTdEL1xVsNgqpkxMsbIabzSo3goCjDIveeGv5d03om39ML71RdmrGNjG5SReBP/Q== - -"@protobufjs/fetch@^1.1.0": - version "1.1.0" - resolved "https://registry.yarnpkg.com/@protobufjs/fetch/-/fetch-1.1.0.tgz#ba99fb598614af65700c1619ff06d454b0d84c45" - integrity sha512-lljVXpqXebpsijW71PZaCYeIcE5on1w5DlQy5WH6GLbFryLUrBD4932W/E2BSpfRJWseIL4v/KPgBFxDOIdKpQ== - dependencies: - "@protobufjs/aspromise" "^1.1.1" - "@protobufjs/inquire" "^1.1.0" - -"@protobufjs/float@^1.0.2": - version "1.0.2" - resolved "https://registry.yarnpkg.com/@protobufjs/float/-/float-1.0.2.tgz#5e9e1abdcb73fc0a7cb8b291df78c8cbd97b87d1" - integrity sha512-Ddb+kVXlXst9d+R9PfTIxh1EdNkgoRe5tOX6t01f1lYWOvJnSPDBlG241QLzcyPdoNTsblLUdujGSE4RzrTZGQ== - -"@protobufjs/inquire@^1.1.0": - version "1.1.0" - resolved "https://registry.yarnpkg.com/@protobufjs/inquire/-/inquire-1.1.0.tgz#ff200e3e7cf2429e2dcafc1140828e8cc638f089" - integrity sha512-kdSefcPdruJiFMVSbn801t4vFK7KB/5gd2fYvrxhuJYg8ILrmn9SKSX2tZdV6V+ksulWqS7aXjBcRXl3wHoD9Q== - -"@protobufjs/path@^1.1.2": - version "1.1.2" - resolved "https://registry.yarnpkg.com/@protobufjs/path/-/path-1.1.2.tgz#6cc2b20c5c9ad6ad0dccfd21ca7673d8d7fbf68d" - integrity sha512-6JOcJ5Tm08dOHAbdR3GrvP+yUUfkjG5ePsHYczMFLq3ZmMkAD98cDgcT2iA1lJ9NVwFd4tH/iSSoe44YWkltEA== - -"@protobufjs/pool@^1.1.0": - version "1.1.0" - resolved "https://registry.yarnpkg.com/@protobufjs/pool/-/pool-1.1.0.tgz#09fd15f2d6d3abfa9b65bc366506d6ad7846ff54" - integrity sha512-0kELaGSIDBKvcgS4zkjz1PeddatrjYcmMWOlAuAPwAeccUrPHdUqo/J6LiymHHEiJT5NrF1UVwxY14f+fy4WQw== - -"@protobufjs/utf8@^1.1.0": - version "1.1.0" - resolved "https://registry.yarnpkg.com/@protobufjs/utf8/-/utf8-1.1.0.tgz#a777360b5b39a1a2e5106f8e858f2fd2d060c570" - integrity sha512-Vvn3zZrhQZkkBE8LSuW3em98c0FwgO4nxzv6OdSxPKJIEKY2bGbHn+mhGIPerzI4twdxaP8/0+06HBpwf345Lw== - "@reach/auto-id@0.16.0": version "0.16.0" resolved "https://registry.yarnpkg.com/@reach/auto-id/-/auto-id-0.16.0.tgz#dfabc3227844e8c04f8e6e45203a8e14a8edbaed" @@ -6312,6 +3834,21 @@ prop-types "^15.7.2" tslib "^2.3.0" +"@react-aria/accordion@^3.0.0-alpha.14": + version "3.0.0-alpha.14" + resolved "https://registry.yarnpkg.com/@react-aria/accordion/-/accordion-3.0.0-alpha.14.tgz#8606b8c8858370ba7985a1cbf45f2d353de1d8fd" + integrity sha512-B06lLcpgib7MMkHmCg5qXkbEEfDSSabPa8Fu2U/ZNeZ9NKi2aYMZRwGum3l+cl4q8+5P+5wgcy/HJaOPJT6odg== + dependencies: + "@react-aria/button" "^3.6.4" + "@react-aria/interactions" "^3.13.1" + "@react-aria/selection" "^3.12.1" + "@react-aria/utils" "^3.14.2" + "@react-stately/tree" "^3.4.1" + "@react-types/accordion" "3.0.0-alpha.11" + "@react-types/button" "^3.7.0" + "@react-types/shared" "^3.16.0" + "@swc/helpers" "^0.4.14" + "@react-aria/button@^3.6.4": version "3.6.4" resolved "https://registry.yarnpkg.com/@react-aria/button/-/button-3.6.4.tgz#51927c9d968d0c1f741ee2081ca7f2e244abbc12" @@ -6360,6 +3897,17 @@ "@swc/helpers" "^0.4.14" clsx "^1.1.1" +"@react-aria/focus@^3.11.0": + version "3.11.0" + resolved "https://registry.yarnpkg.com/@react-aria/focus/-/focus-3.11.0.tgz#8124b2341e8d43af72f3da85b08567bda45421b7" + integrity sha512-yPuWs9bAR9CMfIwyOPm2oXLPF19gNkUqW+ozSPhWbLMTEa8Ma09eHW1br4xbN+4ONOm/dCJsIkxTNPUkiLdQoA== + dependencies: + "@react-aria/interactions" "^3.14.0" + "@react-aria/utils" "^3.15.0" + "@react-types/shared" "^3.17.0" + "@swc/helpers" "^0.4.14" + clsx "^1.1.1" + "@react-aria/focus@^3.6.1": version "3.6.1" resolved "https://registry.yarnpkg.com/@react-aria/focus/-/focus-3.6.1.tgz#46478d0919bdc4fedfa1ea115b36f93c055ce8d8" @@ -6554,8 +4102,17 @@ resolved "https://registry.yarnpkg.com/@react-aria/interactions/-/interactions-3.13.1.tgz#75e102c50a5c1d002cad4ee8d59677aee226186b" integrity sha512-WCvfZOi1hhussVTHxVq76OR48ry13Zvp9U5hmuQufyxIUlf4hOvDk4/cbK4o4JiCs8X7C7SRzcwFM34M4NHzmg== dependencies: - "@react-aria/utils" "^3.14.2" - "@react-types/shared" "^3.16.0" + "@react-aria/utils" "^3.14.2" + "@react-types/shared" "^3.16.0" + "@swc/helpers" "^0.4.14" + +"@react-aria/interactions@^3.14.0": + version "3.14.0" + resolved "https://registry.yarnpkg.com/@react-aria/interactions/-/interactions-3.14.0.tgz#d6480985048c009d58b8572428010dc61093cfcc" + integrity sha512-e1Tkr0UTuYFpV21PJrXy7jEY542Vl+C2Fo70oukZ1fN+wtfQkzodsTIzyepXb7kVMGmC34wDisMJUrksVkfY2w== + dependencies: + "@react-aria/utils" "^3.15.0" + "@react-types/shared" "^3.17.0" "@swc/helpers" "^0.4.14" "@react-aria/interactions@^3.9.1": @@ -6567,16 +4124,6 @@ "@react-aria/utils" "^3.13.1" "@react-types/shared" "^3.13.1" -"@react-aria/label@^3.3.1": - version "3.3.1" - resolved "https://registry.yarnpkg.com/@react-aria/label/-/label-3.3.1.tgz#bdee0004dd4208276b07af5d0e24e0295a5546c3" - integrity sha512-Md4AEYBNByD8e6okbM/GR320++N/i4/xZZXdWtDVIfwpMVnLZ0v/ZPEi4DgLTc4Rwhm021s2qpYhZHQYThOd4A== - dependencies: - "@babel/runtime" "^7.6.2" - "@react-aria/utils" "^3.13.1" - "@react-types/label" "^3.6.1" - "@react-types/shared" "^3.13.1" - "@react-aria/label@^3.4.0": version "3.4.0" resolved "https://registry.yarnpkg.com/@react-aria/label/-/label-3.4.0.tgz#81bf096bd28990d54e6db1ca9294391b7ed0d064" @@ -6607,12 +4154,17 @@ "@react-types/label" "^3.7.1" "@react-types/shared" "^3.16.0" -"@react-aria/live-announcer@^3.1.0": - version "3.1.0" - resolved "https://registry.yarnpkg.com/@react-aria/live-announcer/-/live-announcer-3.1.0.tgz#06cfce35f6a9bcff266ab0ceab97f2fe6e487975" - integrity sha512-YEaGJh1ELho3G9zvUZGOsKsSNEqHsm0fb3Ngvj9z0tjZCXa0867h8YWKuiyTA9BG7WhH8eeJq07WN4nDvYU7fg== - dependencies: - "@babel/runtime" "^7.6.2" +"@react-aria/link@^3.4.0": + version "3.4.0" + resolved "https://registry.yarnpkg.com/@react-aria/link/-/link-3.4.0.tgz#f3f7c5277ab9fc0b8c55c76503e1fbe764e02ca6" + integrity sha512-d/h4y7SFO+KweMX5IRU99L1jz9AAwp6mNStkBjYGxCD29QYTVWClpZHjRlO1s6a9e2QTpk/LzsvjiytowzfHyA== + dependencies: + "@react-aria/focus" "^3.11.0" + "@react-aria/interactions" "^3.14.0" + "@react-aria/utils" "^3.15.0" + "@react-types/link" "^3.4.0" + "@react-types/shared" "^3.17.0" + "@swc/helpers" "^0.4.14" "@react-aria/live-announcer@^3.1.1": version "3.1.1" @@ -6638,24 +4190,6 @@ "@react-types/meter" "^3.2.1" "@react-types/shared" "^3.13.1" -"@react-aria/numberfield@^3.2.1": - version "3.2.1" - resolved "https://registry.yarnpkg.com/@react-aria/numberfield/-/numberfield-3.2.1.tgz#7b7cb32c0c57c73e6d8391192521de83c9d4a55e" - integrity sha512-UJLb87INfOPKKFfxHmllPFfjA25rzKj3L51o/miRYPKbVn+qEEy3PT1UhvT6Lf65BHGDtgS86+LevYTYl5BXgA== - dependencies: - "@babel/runtime" "^7.6.2" - "@react-aria/i18n" "^3.4.1" - "@react-aria/interactions" "^3.9.1" - "@react-aria/live-announcer" "^3.1.0" - "@react-aria/spinbutton" "^3.1.1" - "@react-aria/textfield" "^3.6.1" - "@react-aria/utils" "^3.13.1" - "@react-stately/numberfield" "^3.1.1" - "@react-types/button" "^3.5.1" - "@react-types/numberfield" "^3.3.1" - "@react-types/shared" "^3.13.1" - "@react-types/textfield" "^3.5.1" - "@react-aria/overlays@^3.12.0": version "3.12.0" resolved "https://registry.yarnpkg.com/@react-aria/overlays/-/overlays-3.12.0.tgz#66fc930a39771888123c6fd1b246fedd5e76ad89" @@ -6736,18 +4270,6 @@ "@react-aria/utils" "^3.14.1" "@react-types/shared" "^3.16.0" -"@react-aria/spinbutton@^3.1.1": - version "3.1.1" - resolved "https://registry.yarnpkg.com/@react-aria/spinbutton/-/spinbutton-3.1.1.tgz#17a06c4254e51b4ccc0cedc6137375124abdacc1" - integrity sha512-Fsiu8Is2TN3mRojdJ+cvNb/daW550scjV53g+OMyRZnryu0wLWgxFPKlODyAfuMzoTz/+3ULD8+0jQbp51nlDA== - dependencies: - "@babel/runtime" "^7.6.2" - "@react-aria/i18n" "^3.4.1" - "@react-aria/live-announcer" "^3.1.0" - "@react-aria/utils" "^3.13.1" - "@react-types/button" "^3.5.1" - "@react-types/shared" "^3.13.1" - "@react-aria/ssr@^3.2.0": version "3.2.0" resolved "https://registry.yarnpkg.com/@react-aria/ssr/-/ssr-3.2.0.tgz#88460384b43204f91c972d5b0de24ee44d6a2984" @@ -6776,6 +4298,13 @@ dependencies: "@swc/helpers" "^0.4.14" +"@react-aria/ssr@^3.5.0": + version "3.5.0" + resolved "https://registry.yarnpkg.com/@react-aria/ssr/-/ssr-3.5.0.tgz#40c1270a75868185f72a88cafe37bd1392f690cb" + integrity sha512-h0MJdSWOd1qObLnJ8mprU31wI8tmKFJMuwT22MpWq6psisOOZaga6Ml4u6Ee6M6duWWISjXvqO4Sb/J0PBA+nQ== + dependencies: + "@swc/helpers" "^0.4.14" + "@react-aria/switch@^3.2.4": version "3.2.4" resolved "https://registry.yarnpkg.com/@react-aria/switch/-/switch-3.2.4.tgz#6a4f95d89347b77d215b5b6d3640baba0360880b" @@ -6822,18 +4351,6 @@ "@react-types/shared" "^3.13.1" "@react-types/tabs" "^3.1.1" -"@react-aria/textfield@^3.6.1": - version "3.6.1" - resolved "https://registry.yarnpkg.com/@react-aria/textfield/-/textfield-3.6.1.tgz#542dd22babe10ce5aca4023e5b7371e7d582b19b" - integrity sha512-MX0xDIin1rZweEVKqEAqLjw7BsNQjjj8FVopNFzRl2tLxf7C+n/cHLdgwf8uVhHOcl9Vcqx/zeKuuZeB//Dvrg== - dependencies: - "@babel/runtime" "^7.6.2" - "@react-aria/focus" "^3.6.1" - "@react-aria/label" "^3.3.1" - "@react-aria/utils" "^3.13.1" - "@react-types/shared" "^3.13.1" - "@react-types/textfield" "^3.5.1" - "@react-aria/textfield@^3.7.1": version "3.7.1" resolved "https://registry.yarnpkg.com/@react-aria/textfield/-/textfield-3.7.1.tgz#5dac1631120f4ff6dd303f6c48225af16b2d0d6b" @@ -6939,6 +4456,17 @@ "@swc/helpers" "^0.4.14" clsx "^1.1.1" +"@react-aria/utils@^3.15.0": + version "3.15.0" + resolved "https://registry.yarnpkg.com/@react-aria/utils/-/utils-3.15.0.tgz#a836674dd40ec7f15e9f7a69b1a14f19b1ee42e6" + integrity sha512-aJZBG++iz1UwTW5gXFaHicKju4p0MPhAyBTcf2awHYWeTUUslDjJcEnNg7kjBYZBOrOSlA2rAt7/7C5CCURQPg== + dependencies: + "@react-aria/ssr" "^3.5.0" + "@react-stately/utils" "^3.6.0" + "@react-types/shared" "^3.17.0" + "@swc/helpers" "^0.4.14" + clsx "^1.1.1" + "@react-aria/visually-hidden@^3.6.0": version "3.6.0" resolved "https://registry.yarnpkg.com/@react-aria/visually-hidden/-/visually-hidden-3.6.0.tgz#cc4dd9e648a5c8b6d8dfbd1f70d8672b36d3f1bc" @@ -7027,17 +4555,6 @@ "@react-types/shared" "^3.16.0" "@swc/helpers" "^0.4.14" -"@react-stately/numberfield@^3.1.1": - version "3.1.1" - resolved "https://registry.yarnpkg.com/@react-stately/numberfield/-/numberfield-3.1.1.tgz#cbdeac91ba9f522dab71ab56fd2386d85a972a3b" - integrity sha512-0vCPxzKpkrJcPDDFG3WTstkUFr1jBHjZBtDuyMCyNTKfSvXy2+tl+LqgFTTJZGJwBAu4ezKMFYxjm9i6DygVoA== - dependencies: - "@babel/runtime" "^7.6.2" - "@internationalized/number" "^3.1.1" - "@react-stately/utils" "^3.5.0" - "@react-types/numberfield" "^3.3.1" - "@react-types/shared" "^3.13.1" - "@react-stately/overlays@^3.4.3": version "3.4.3" resolved "https://registry.yarnpkg.com/@react-stately/overlays/-/overlays-3.4.3.tgz#2e935c404c0845ee7a7c6f001ff057d315161a16" @@ -7130,6 +4647,17 @@ "@react-stately/utils" "^3.5.1" "@react-types/tooltip" "^3.2.5" +"@react-stately/tree@^3.4.1": + version "3.4.1" + resolved "https://registry.yarnpkg.com/@react-stately/tree/-/tree-3.4.1.tgz#bb267784000b22c7c1aa6415103ad1b9f3566677" + integrity sha512-kIXeJOHgGGaUFnAD2wyRIiOwOw/+PN1OXo46n8+dPTFIYwR4+IWFNG8OMjVlIiSLPYWMCzzxZBE9a5grmbmNWQ== + dependencies: + "@react-stately/collections" "^3.5.1" + "@react-stately/selection" "^3.11.2" + "@react-stately/utils" "^3.5.2" + "@react-types/shared" "^3.16.0" + "@swc/helpers" "^0.4.14" + "@react-stately/utils@^3.5.0": version "3.5.0" resolved "https://registry.yarnpkg.com/@react-stately/utils/-/utils-3.5.0.tgz#e76231cfc93042814dcc5d96436e50c807c1d905" @@ -7151,6 +4679,13 @@ dependencies: "@swc/helpers" "^0.4.14" +"@react-stately/utils@^3.6.0": + version "3.6.0" + resolved "https://registry.yarnpkg.com/@react-stately/utils/-/utils-3.6.0.tgz#f273e7fcb348254347d2e88c8f0c45571060c207" + integrity sha512-rptF7iUWDrquaYvBAS4QQhOBQyLBncDeHF03WnHXAxnuPJXNcr9cXJtjJPGCs036ZB8Q2hc9BGG5wNyMkF5v+Q== + dependencies: + "@swc/helpers" "^0.4.14" + "@react-stately/virtualizer@^3.2.2": version "3.2.2" resolved "https://registry.yarnpkg.com/@react-stately/virtualizer/-/virtualizer-3.2.2.tgz#3fca1acc0622d24b1acd3df8efd7795f546f3d1c" @@ -7169,12 +4704,12 @@ "@react-types/shared" "^3.16.0" "@swc/helpers" "^0.4.14" -"@react-types/button@^3.5.1": - version "3.5.1" - resolved "https://registry.yarnpkg.com/@react-types/button/-/button-3.5.1.tgz#152113ea1ca0f4dadf16130dcb7043385e8a29c9" - integrity sha512-GxTikDKfuztu1r0LWHmqG1AD5yM9F6WFzwAYgLltn8B7RshzpJcYSfpYsh1e64z84YJpEoj+DD8XGGSc0p/rvw== +"@react-types/accordion@3.0.0-alpha.11": + version "3.0.0-alpha.11" + resolved "https://registry.yarnpkg.com/@react-types/accordion/-/accordion-3.0.0-alpha.11.tgz#b09ccf8afc411b6fce54da70680426f56a5858f1" + integrity sha512-7TAvvMnfSWtX4gkmRg3pE31y/E/oTWfUAIi9WT4hPJBbE/8zItjD2P5cQ5tLizXT87fnm+4s8jvci7gZcN2/pw== dependencies: - "@react-types/shared" "^3.13.1" + "@react-types/shared" "^3.16.0" "@react-types/button@^3.7.0": version "3.7.0" @@ -7226,13 +4761,6 @@ dependencies: "@react-types/shared" "^3.16.0" -"@react-types/label@^3.6.1": - version "3.6.1" - resolved "https://registry.yarnpkg.com/@react-types/label/-/label-3.6.1.tgz#d6eb4a52155a61ff644ccd62ea3abafcab1eb929" - integrity sha512-Yjz1qWJ3uAdMYMOKDUiB28Wdc/3kqeuE9BtrLbvqjX/VZcnFgbVEQ6Yy8G9v6pcjEF/EJRl+hcA0deUews/G0w== - dependencies: - "@react-types/shared" "^3.13.1" - "@react-types/label@^3.6.2": version "3.6.2" resolved "https://registry.yarnpkg.com/@react-types/label/-/label-3.6.2.tgz#55726a796c0329e836e887241b9d0fefcdb5ae34" @@ -7254,6 +4782,14 @@ dependencies: "@react-types/shared" "^3.16.0" +"@react-types/link@^3.4.0": + version "3.4.0" + resolved "https://registry.yarnpkg.com/@react-types/link/-/link-3.4.0.tgz#1c549ffbc594e49726a4c4e912bea36718feb4cf" + integrity sha512-eImWLzxwzSmjOLa0Ow3HJaguyDCz98191v2pb7nT/zPzGDnhHhDjxB023hrXVUoCbsWrCb5QLp91Ts+VjiCyTA== + dependencies: + "@react-aria/interactions" "^3.14.0" + "@react-types/shared" "^3.17.0" + "@react-types/meter@^3.2.1": version "3.2.2" resolved "https://registry.yarnpkg.com/@react-types/meter/-/meter-3.2.2.tgz#029f593e405cf3aebccd9c0f54c588c399689559" @@ -7262,13 +4798,6 @@ "@react-types/progress" "^3.2.2" "@react-types/shared" "^3.14.0" -"@react-types/numberfield@^3.3.1": - version "3.3.1" - resolved "https://registry.yarnpkg.com/@react-types/numberfield/-/numberfield-3.3.1.tgz#7108c77ed5ed466723e476fc2bebd36e5b1d289e" - integrity sha512-xeP/oo9QXTAtzJukahGcR2r26AqPSYlshWPw8wHBKLDqFu2PVIMJmiNblhSUyOxTQ8/OfoyCCkerojjh6uZCAw== - dependencies: - "@react-types/shared" "^3.13.1" - "@react-types/overlays@^3.6.5": version "3.6.5" resolved "https://registry.yarnpkg.com/@react-types/overlays/-/overlays-3.6.5.tgz#466b325d9be51f67beb98b7bec3fd9295c72efac" @@ -7308,6 +4837,11 @@ resolved "https://registry.yarnpkg.com/@react-types/shared/-/shared-3.16.0.tgz#cab7bf0376969d1773480ecb2d6da5aa91391db5" integrity sha512-IQgU4oAEvMwylEvaTsr2XB1G/mAoMe1JFYLD6G78v++oAR9l8o9MQxZ0YSeANDkqTamb2gKezGoT1RxvSKjVxw== +"@react-types/shared@^3.17.0": + version "3.17.0" + resolved "https://registry.yarnpkg.com/@react-types/shared/-/shared-3.17.0.tgz#b7c5e318664aadb315d305a27dd2a209d1837d95" + integrity sha512-1SNZ/RhVrrQ1e6yE0bPV7d5Sfp+Uv0dfUEhwF9MAu2v5msu7AMewnsiojKNA0QA6Ing1gpDLjHCxtayQfuxqcg== + "@react-types/switch@^3.2.4": version "3.2.4" resolved "https://registry.yarnpkg.com/@react-types/switch/-/switch-3.2.4.tgz#6853793032da50415be1abbac1374fca08ea5e44" @@ -7331,13 +4865,6 @@ dependencies: "@react-types/shared" "^3.13.1" -"@react-types/textfield@^3.5.1": - version "3.5.1" - resolved "https://registry.yarnpkg.com/@react-types/textfield/-/textfield-3.5.1.tgz#5096ce03a13f44135a379b288d5088cc2d2aa369" - integrity sha512-EYsljXfGJJJ6B/Buu6BaBm0Kyrf67so6skBCP3odECKIvmBwTKbikHULpe/Od8DBFIvjd7x2ZEzUlMH2hp+eRQ== - dependencies: - "@react-types/shared" "^3.13.1" - "@react-types/textfield@^3.5.3": version "3.5.3" resolved "https://registry.yarnpkg.com/@react-types/textfield/-/textfield-3.5.3.tgz#6c47b3d3f03c528099ab89c347634df712a04df8" @@ -7381,11 +4908,6 @@ estree-walker "^1.0.1" picomatch "^2.2.2" -"@scure/base@1.0.0": - version "1.0.0" - resolved "https://registry.yarnpkg.com/@scure/base/-/base-1.0.0.tgz#109fb595021de285f05a7db6806f2f48296fcee7" - integrity sha512-gIVaYhUsy+9s58m/ETjSJVKHhKTBMmcRb9cEV5/5dwvfDlfORjKrFsDeDHWRrm6RjcPvCLZFwGJjAjLj1gg4HA== - "@scure/base@1.1.1": version "1.1.1" resolved "https://registry.yarnpkg.com/@scure/base/-/base-1.1.1.tgz#ebb651ee52ff84f420097055f4bf46cfba403938" @@ -7424,46 +4946,6 @@ "@polkadot/keyring" "^8.2.2" "@polkadot/types" "^7.2.1" -"@solana/buffer-layout@^4.0.0": - version "4.0.0" - resolved "https://registry.yarnpkg.com/@solana/buffer-layout/-/buffer-layout-4.0.0.tgz#75b1b11adc487234821c81dfae3119b73a5fd734" - integrity sha512-lR0EMP2HC3+Mxwd4YcnZb0smnaDw7Bl2IQWZiTevRH5ZZBZn6VRWn3/92E3qdU4SSImJkA6IDHawOHAnx/qUvQ== - dependencies: - buffer "~6.0.3" - -"@solana/spl-token@^0.1.8": - version "0.1.8" - resolved "https://registry.yarnpkg.com/@solana/spl-token/-/spl-token-0.1.8.tgz#f06e746341ef8d04165e21fc7f555492a2a0faa6" - integrity sha512-LZmYCKcPQDtJgecvWOgT/cnoIQPWjdH+QVyzPcFvyDUiT0DiRjZaam4aqNUyvchLFhzgunv3d9xOoyE34ofdoQ== - dependencies: - "@babel/runtime" "^7.10.5" - "@solana/web3.js" "^1.21.0" - bn.js "^5.1.0" - buffer "6.0.3" - buffer-layout "^1.2.0" - dotenv "10.0.0" - -"@solana/web3.js@^1.21.0", "@solana/web3.js@^1.24.0": - version "1.66.2" - resolved "https://registry.yarnpkg.com/@solana/web3.js/-/web3.js-1.66.2.tgz#80b43c5868b846124fe3ebac7d3943930c3fa60c" - integrity sha512-RyaHMR2jGmaesnYP045VLeBGfR/gAW3cvZHzMFGg7bkO+WOYOYp1nEllf0/la4U4qsYGKCsO9eEevR5fhHiVHg== - dependencies: - "@babel/runtime" "^7.12.5" - "@noble/ed25519" "^1.7.0" - "@noble/hashes" "^1.1.2" - "@noble/secp256k1" "^1.6.3" - "@solana/buffer-layout" "^4.0.0" - bigint-buffer "^1.1.5" - bn.js "^5.0.0" - borsh "^0.7.0" - bs58 "^4.0.1" - buffer "6.0.1" - fast-stable-stringify "^1.0.0" - jayson "^3.4.4" - node-fetch "2" - rpc-websockets "^7.5.0" - superstruct "^0.14.2" - "@sora-substrate/type-definitions@1.10.21": version "1.10.21" resolved "https://registry.yarnpkg.com/@sora-substrate/type-definitions/-/type-definitions-1.10.21.tgz#1fb225cc8036729cdfb8fd2fcdc72bfa18251781" @@ -8189,7 +5671,7 @@ prettier ">=2.2.1 <=2.3.0" ts-dedent "^2.0.0" -"@storybook/node-logger@6.5.9", "@storybook/node-logger@^6.5.9": +"@storybook/node-logger@6.5.9": version "6.5.9" resolved "https://registry.yarnpkg.com/@storybook/node-logger/-/node-logger-6.5.9.tgz#129cfe0d0f79cab4f6a2ba194d39516680b1626f" integrity sha512-nZZNZG2Wtwv6Trxi3FrnIqUmB55xO+X/WQGPT5iKlqNjdRIu/T72mE7addcp4rbuWCQfZUhcDDGpBOwKtBxaGg== @@ -8436,151 +5918,39 @@ resolve-from "^5.0.0" "@subsocial/definitions@^0.7.8-dev.0": - version "0.7.9" - resolved "https://registry.yarnpkg.com/@subsocial/definitions/-/definitions-0.7.9.tgz#5bc0d94c9ad269d00d10f9b47fdf4abc447bb183" - integrity sha512-HBYvftT+ixPgPttCRUIpcpKJ9UlvFHWhhUgaLTxfFNQ4qaVMCPT7ZdoEkWYeErefnUVag1j5NzS9TfjTyx9gLg== + version "0.7.14" + resolved "https://registry.yarnpkg.com/@subsocial/definitions/-/definitions-0.7.14.tgz#1397f1ec806d60d9deb112b9f36d530400b711fe" + integrity sha512-dor5S6/tbY09n40e/dh7qFcqF9slMihOMDTXWBM5hTe8nS/Pf5Zp4/r9WiZxxYLoY2v5MlSqyJxjiSCjTxxjUw== dependencies: "@polkadot/api" latest lodash.camelcase "^4.3.0" -"@substrate/connect-extension-protocol@^1.0.0": - version "1.0.0" - resolved "https://registry.yarnpkg.com/@substrate/connect-extension-protocol/-/connect-extension-protocol-1.0.0.tgz#d452beda84b3ebfcf0e88592a4695e729a91e858" - integrity sha512-nFVuKdp71hMd/MGlllAOh+a2hAqt8m6J2G0aSsS/RcALZexxF9jodbFc62ni8RDtJboeOfXAHhenYOANvJKPIg== - "@substrate/connect-extension-protocol@^1.0.1": version "1.0.1" resolved "https://registry.yarnpkg.com/@substrate/connect-extension-protocol/-/connect-extension-protocol-1.0.1.tgz#fa5738039586c648013caa6a0c95c43265dbe77d" integrity sha512-161JhCC1csjH3GE5mPLEd7HbWtwNSPJBg3p1Ksz9SFlTzj/bgEwudiRN2y5i0MoLGCIJRYKyKGMxVnd29PzNjg== -"@substrate/connect@0.7.0-alpha.0": - version "0.7.0-alpha.0" - resolved "https://registry.yarnpkg.com/@substrate/connect/-/connect-0.7.0-alpha.0.tgz#df605f4e808b58d146ad0272a79b2c4b870459a5" - integrity sha512-fvO7w++M8R95R/pGJFW9+cWOt8OYnnTfgswxtlPqSgzqX4tta8xcNQ51crC72FcL5agwSGkA1gc2/+eyTj7O8A== - dependencies: - "@substrate/connect-extension-protocol" "^1.0.0" - "@substrate/smoldot-light" "0.6.8" - eventemitter3 "^4.0.7" - -"@substrate/connect@0.7.16": - version "0.7.16" - resolved "https://registry.yarnpkg.com/@substrate/connect/-/connect-0.7.16.tgz#789b867051bc7ce1714d3c80970d715ddac6e4d4" - integrity sha512-068ZOR5ohb3qC2UegHe73RphnIa4F8fRjOIoap69K2qAdgT2yiegjK4nsxTnnqzlGrQTzgN3yG6SY+Va/a3+mw== - dependencies: - "@substrate/connect-extension-protocol" "^1.0.1" - "@substrate/smoldot-light" "0.7.5" - eventemitter3 "^4.0.7" - -"@substrate/connect@0.7.17": - version "0.7.17" - resolved "https://registry.yarnpkg.com/@substrate/connect/-/connect-0.7.17.tgz#b76ce23d24255e89028db81b3cb280c7f86db72e" - integrity sha512-s0XBmGpUCFWZFa+TS0TEvOKtWjJP2uT4xKmvzApH8INB5xbz79wqWFX6WWh3AlK/X1P0Smt+RVEH7HQiLJAYAw== +"@substrate/connect@0.7.19": + version "0.7.19" + resolved "https://registry.yarnpkg.com/@substrate/connect/-/connect-0.7.19.tgz#7c879cb275bc7ac2fe9edbf797572d4ff8d8b86a" + integrity sha512-+DDRadc466gCmDU71sHrYOt1HcI2Cbhm7zdCFjZfFVHXhC/E8tOdrVSglAH2HDEHR0x2SiHRxtxOGC7ak2Zjog== dependencies: "@substrate/connect-extension-protocol" "^1.0.1" - "@substrate/smoldot-light" "0.7.7" - eventemitter3 "^4.0.7" - -"@substrate/connect@0.7.2": - version "0.7.2" - resolved "https://registry.yarnpkg.com/@substrate/connect/-/connect-0.7.2.tgz#a2440a7a85a75acbc839745b301d5b8b81cbac5d" - integrity sha512-8GWdrN7qbClYLa9LmETJnywT5fknEQeMw+QKvkUMvsHKegHD0Zkhi0K484mKxRN9RGwcPsKHPj1gMk8xlZuJ9g== - dependencies: - "@substrate/connect-extension-protocol" "^1.0.0" - "@substrate/smoldot-light" "0.6.15" - eventemitter3 "^4.0.7" - -"@substrate/connect@0.7.5": - version "0.7.5" - resolved "https://registry.yarnpkg.com/@substrate/connect/-/connect-0.7.5.tgz#8d868ed905df25c87ff9bad9fa8db6d4137012c9" - integrity sha512-sdAZ6IGuTNxRGlH/O+6IaXvkYzZFwMK03VbQMgxUzry9dz1+JzyaNf8iOTVHxhMIUZc0h0E90JQz/hNiUYPlUw== - dependencies: - "@substrate/connect-extension-protocol" "^1.0.0" - "@substrate/smoldot-light" "0.6.16" + "@substrate/smoldot-light" "0.7.9" eventemitter3 "^4.0.7" -"@substrate/connect@0.7.9": +"@substrate/smoldot-light@0.7.9": version "0.7.9" - resolved "https://registry.yarnpkg.com/@substrate/connect/-/connect-0.7.9.tgz#0bb65fef28c70051e6158e10f633005e899fbb5b" - integrity sha512-E6bdBhzsfHNAKlmQSvbTW1jyb0WcIvgbrEBfJ4B6FZ3t1wpGjldL6GrYtegVtKr9/ySQ/pFNn0uVbugukpMDjQ== - dependencies: - "@substrate/connect-extension-protocol" "^1.0.1" - "@substrate/smoldot-light" "0.6.25" - eventemitter3 "^4.0.7" - -"@substrate/smoldot-light@0.6.15": - version "0.6.15" - resolved "https://registry.yarnpkg.com/@substrate/smoldot-light/-/smoldot-light-0.6.15.tgz#f3fd2a9fa2e3a579d2bf0c13590fb48db4935f9f" - integrity sha512-c2tJCSp9Litsn/p8wY1FfEqIkJI8Peh89BU7T43bruWRO2SSgLVh0cIVbOCY4en90tIOX4W0CueRWFBRQz7BjQ== - dependencies: - buffer "^6.0.1" - pako "^2.0.4" - websocket "^1.0.32" - -"@substrate/smoldot-light@0.6.16": - version "0.6.16" - resolved "https://registry.yarnpkg.com/@substrate/smoldot-light/-/smoldot-light-0.6.16.tgz#04ec70cf1df285431309fe5704d3b2dd701faa0b" - integrity sha512-Ej0ZdNPTW0EXbp45gv/5Kt/JV+c9cmRZRYAXg+EALxXPm0hW9h2QdVLm61A2PAskOGptW4wnJ1WzzruaenwAXQ== - dependencies: - buffer "^6.0.1" - pako "^2.0.4" - websocket "^1.0.32" - -"@substrate/smoldot-light@0.6.25": - version "0.6.25" - resolved "https://registry.yarnpkg.com/@substrate/smoldot-light/-/smoldot-light-0.6.25.tgz#3025ba5134b1be470855f901ffeb028a0f93460c" - integrity sha512-OQ9/bnJJy90xSRg5Vp9MIvrgbrVt/r/FwXYSmyLeBBNbJt6o1gSeshVo8icD+2VWwd/TJ2oHl5CVQWe89MyByA== - dependencies: - websocket "^1.0.32" - -"@substrate/smoldot-light@0.6.8": - version "0.6.8" - resolved "https://registry.yarnpkg.com/@substrate/smoldot-light/-/smoldot-light-0.6.8.tgz#e626e25cd2386824f1164e7d7dda7258580c36e4" - integrity sha512-9lVwbG6wrtss0sd6013BJGe4WN4taujsGG49pwyt1Lj36USeL2Sb164TTUxmZF/g2NQEqDPwPROBdekQ2gFmgg== - dependencies: - buffer "^6.0.1" - pako "^2.0.4" - websocket "^1.0.32" - -"@substrate/smoldot-light@0.7.5": - version "0.7.5" - resolved "https://registry.yarnpkg.com/@substrate/smoldot-light/-/smoldot-light-0.7.5.tgz#e380d50e748959d50c70c9489fda91e2d618cf7c" - integrity sha512-rODqhQx0C6zRN7HzdZrjzcg2dNbl3IUtM7GWEHitPzLS/0c3ikO/RkCCD1uleVqCwKXlZzjVNBeYW4/47vxKSA== - dependencies: - pako "^2.0.4" - ws "^8.8.1" - -"@substrate/smoldot-light@0.7.7": - version "0.7.7" - resolved "https://registry.yarnpkg.com/@substrate/smoldot-light/-/smoldot-light-0.7.7.tgz#ee5f89bb25af64d2014d97548b959b7da4c67f08" - integrity sha512-ksxeAed6dIUtYSl0f8ehgWQjwXnpDGTIJt+WVRIGt3OObZkA96ZdBWx0xP7GrXZtj37u4n/Y1z7TyTm4bwQvrw== + resolved "https://registry.yarnpkg.com/@substrate/smoldot-light/-/smoldot-light-0.7.9.tgz#68449873a25558e547e9468289686ee228a9930f" + integrity sha512-HP8iP7sFYlpSgjjbo0lqHyU+gu9lL2hbDNce6dWk5/10mFFF9jKIFGfui4zCecUY808o/Go9pan/31kMJoLbug== dependencies: pako "^2.0.4" ws "^8.8.1" -"@substrate/ss58-registry@^1.17.0", "@substrate/ss58-registry@^1.23.0", "@substrate/ss58-registry@^1.34.0": - version "1.34.0" - resolved "https://registry.yarnpkg.com/@substrate/ss58-registry/-/ss58-registry-1.34.0.tgz#b6faed02343da7a8956444f5db23bc7246dd5fb5" - integrity sha512-8Df5usnWvjnw/WRAmKOqHXRPPRfiCd1kIN8ttH4YmBrRTERjVInsdu0xvLdbyUYKyvgK6zKhHWQfYohXqllHhg== - -"@substrate/ss58-registry@^1.22.0": - version "1.22.0" - resolved "https://registry.yarnpkg.com/@substrate/ss58-registry/-/ss58-registry-1.22.0.tgz#d115bc5dcab8c0f5800e05e4ef265949042b13ec" - integrity sha512-IKqrPY0B3AeIXEc5/JGgEhPZLy+SmVyQf+k0SIGcNSTqt1GLI3gQFEOFwSScJdem+iYZQUrn6YPPxC3TpdSC3A== - -"@substrate/ss58-registry@^1.28.0": - version "1.28.0" - resolved "https://registry.yarnpkg.com/@substrate/ss58-registry/-/ss58-registry-1.28.0.tgz#39b7fa355d9b97bcb30ef1eedb47b10c3fddcf03" - integrity sha512-XPSwSq4CThLyg+OnZ5/LHh3SPDQjRdGS3Ux5ClgWhRCQamlU86FCT1LBwQ/i+ximbdBfqKRRzVhm1ql3AJ9FKQ== - -"@substrate/ss58-registry@^1.29.1": - version "1.29.1" - resolved "https://registry.yarnpkg.com/@substrate/ss58-registry/-/ss58-registry-1.29.1.tgz#6070fc3cdf24ad8b3eba1eb7bdf4d96804615775" - integrity sha512-7+DeSVpUS2m+7HAeYvvypbnrYVat1b7ze2V9SV3d+pTbUHkovZACaHYm33FBHc9F+RpXgYgf+pikJXK5ux4o1g== - -"@substrate/ss58-registry@^1.35.0": - version "1.36.0" - resolved "https://registry.yarnpkg.com/@substrate/ss58-registry/-/ss58-registry-1.36.0.tgz#22b59fa85cacc0bdf40aa5d8131a377c1b5a8dd8" - integrity sha512-YfQIpe2bIvGg/XWNByycznbOiAknMvpYaUpQJ2sLmNT/OwPx7XjEXk7dLShccuiQDoOQt3trTtF3Frz/Tjv6Fg== +"@substrate/ss58-registry@^1.38.0": + version "1.39.0" + resolved "https://registry.yarnpkg.com/@substrate/ss58-registry/-/ss58-registry-1.39.0.tgz#eb916ff5fea7fa02e77745823fde21af979273d2" + integrity sha512-qZYpuE6n+mwew+X71dOur/CbMXj6rNW27o63JeJwdQH/GvcSKm3JLNhd+bGzwUKg0D/zD30Qc6p4JykArzM+tA== "@surma/rollup-plugin-off-main-thread@^1.1.1": version "1.4.2" @@ -8755,58 +6125,19 @@ resolve "^1.20.0" tmp "^0.2.1" -"@terra-money/legacy.proto@npm:@terra-money/terra.proto@^0.1.7": - version "0.1.7" - resolved "https://registry.yarnpkg.com/@terra-money/terra.proto/-/terra.proto-0.1.7.tgz#59c18f30da10d43200bab3ba8feb5b17e43a365f" - integrity sha512-NXD7f6pQCulvo6+mv6MAPzhOkUzRjgYVuHZE/apih+lVnPG5hDBU0rRYnOGGofwvKT5/jQoOENnFn/gioWWnyQ== - dependencies: - google-protobuf "^3.17.3" - long "^4.0.0" - protobufjs "~6.11.2" - -"@terra-money/terra.js@^3.1.3": - version "3.1.7" - resolved "https://registry.yarnpkg.com/@terra-money/terra.js/-/terra.js-3.1.7.tgz#76fcb655ca106ff683beb05d6ce305f5d05de7d4" - integrity sha512-z7NwVI1gh0846pgQJaPHya6SRKLd/dHWR5UwWG6T2Pf24I2EjCo8YY5Fay30pCvHTYA2NBFgnWfXEZ/31TfB1Q== - dependencies: - "@terra-money/legacy.proto" "npm:@terra-money/terra.proto@^0.1.7" - "@terra-money/terra.proto" "^2.1.0" - axios "^0.27.2" - bech32 "^2.0.0" - bip32 "^2.0.6" - bip39 "^3.0.3" - bufferutil "^4.0.3" - decimal.js "^10.2.1" - jscrypto "^1.0.1" - readable-stream "^3.6.0" - secp256k1 "^4.0.2" - tmp "^0.2.1" - utf-8-validate "^5.0.5" - ws "^7.5.9" - -"@terra-money/terra.proto@^2.1.0": - version "2.1.0" - resolved "https://registry.yarnpkg.com/@terra-money/terra.proto/-/terra.proto-2.1.0.tgz#5a2ed85fc8146a346d6095adfc5d205b6fb6d387" - integrity sha512-rhaMslv3Rkr+QsTQEZs64FKA4QlfO0DfQHaR6yct/EovenMkibDEQ63dEL6yJA6LCaEQGYhyVB9JO9pTUA8ybw== - dependencies: - "@improbable-eng/grpc-web" "^0.14.1" - google-protobuf "^3.17.3" - long "^4.0.0" - protobufjs "~6.11.2" - -"@testing-library/dom@^7.28.1": - version "7.31.2" - resolved "https://registry.yarnpkg.com/@testing-library/dom/-/dom-7.31.2.tgz#df361db38f5212b88555068ab8119f5d841a8c4a" - integrity sha512-3UqjCpey6HiTZT92vODYLPxTBWlM8ZOOjr3LX5F37/VRipW2M1kX6I/Cm4VXzteZqfGfagg8yXywpcOgQBlNsQ== +"@testing-library/dom@^8.0.0": + version "8.20.0" + resolved "https://registry.yarnpkg.com/@testing-library/dom/-/dom-8.20.0.tgz#914aa862cef0f5e89b98cc48e3445c4c921010f6" + integrity sha512-d9ULIT+a4EXLX3UU8FBjauG9NnsZHkHztXoIcTsOKoOw030fyjheN9svkTULjJxtYag9DZz5Jz5qkWZDPxTFwA== dependencies: "@babel/code-frame" "^7.10.4" "@babel/runtime" "^7.12.5" - "@types/aria-query" "^4.2.0" - aria-query "^4.2.2" + "@types/aria-query" "^5.0.1" + aria-query "^5.0.0" chalk "^4.1.0" - dom-accessibility-api "^0.5.6" + dom-accessibility-api "^0.5.9" lz-string "^1.4.4" - pretty-format "^26.6.2" + pretty-format "^27.0.2" "@testing-library/jest-dom@^5.11.4": version "5.16.5" @@ -8823,13 +6154,14 @@ lodash "^4.17.15" redent "^3.0.0" -"@testing-library/react@^11.1.0": - version "11.2.7" - resolved "https://registry.yarnpkg.com/@testing-library/react/-/react-11.2.7.tgz#b29e2e95c6765c815786c0bc1d5aed9cb2bf7818" - integrity sha512-tzRNp7pzd5QmbtXNG/mhdcl7Awfu/Iz1RaVHY75zTdOkmHCuzMhRL83gWHSgOAcjS3CCbyfwUHMZgRJb4kAfpA== +"@testing-library/react@12.1.5": + version "12.1.5" + resolved "https://registry.yarnpkg.com/@testing-library/react/-/react-12.1.5.tgz#bb248f72f02a5ac9d949dea07279095fa577963b" + integrity sha512-OfTXCJUFgjd/digLUuPxa0+/3ZxsQmE7ub9kcbW/wi96Bh3o/p5vrETcBGfP17NWPGqeYYl5LTRpwyGoMC4ysg== dependencies: "@babel/runtime" "^7.12.5" - "@testing-library/dom" "^7.28.1" + "@testing-library/dom" "^8.0.0" + "@types/react-dom" "<18.0.0" "@testing-library/user-event@^12.1.10": version "12.8.3" @@ -8863,10 +6195,10 @@ resolved "https://registry.yarnpkg.com/@tsconfig/node16/-/node16-1.0.2.tgz#423c77877d0569db20e1fc80885ac4118314010e" integrity sha512-eZxlbI8GZscaGS7kkc/trHTT5xgrjH3/1n2JDwusC9iahPKWMRvRjJSAN5mCXviuTGQ/lHnhvv8Q1YTpnfz9gA== -"@types/aria-query@^4.2.0": - version "4.2.2" - resolved "https://registry.yarnpkg.com/@types/aria-query/-/aria-query-4.2.2.tgz#ed4e0ad92306a704f9fb132a0cfcf77486dbe2bc" - integrity sha512-HnYpAE1Y6kRyKM/XkEuiRQhTHvkzMBurTHnpFLYLBGPIylZNPs9jJcuOOYWxPLJCSEtmZT0Y8rHDokKN7rRTig== +"@types/aria-query@^5.0.1": + version "5.0.1" + resolved "https://registry.yarnpkg.com/@types/aria-query/-/aria-query-5.0.1.tgz#3286741fb8f1e1580ac28784add4c7a1d49bdfbc" + integrity sha512-XTIieEY+gvJ39ChLcB4If5zHtPxt3Syj5rgZR+e1ctpmK8NjPf0zFqsz4JpLJT0xla9GFDKjy8Cpu331nrmE1Q== "@types/babel__core@^7.0.0", "@types/babel__core@^7.1.7": version "7.1.18" @@ -8906,35 +6238,13 @@ resolved "https://registry.yarnpkg.com/@types/big.js/-/big.js-6.1.2.tgz#68a952b629a6aaa2b5855a2f63363d1e77f6dd91" integrity sha512-h24JIZ52rvSvi2jkpYDk2yLH99VzZoCJiSfDWwjst7TwJVuXN61XVCUlPCzRl7mxKEMsGf8z42Q+J4TZwU3z2w== -"@types/bn.js@^4.11.6": - version "4.11.6" - resolved "https://registry.yarnpkg.com/@types/bn.js/-/bn.js-4.11.6.tgz#c306c70d9358aaea33cd4eda092a742b9505967c" - integrity sha512-pqr857jrp2kPuO9uRjZ3PwnJTjoQy+fcdxvBTvHm6dkmEL9q+hDD/2j/0ELOBPtPnS8LjCX0gI9nbl8lVkadpg== - dependencies: - "@types/node" "*" - -"@types/bn.js@^5.1.0": - version "5.1.0" - resolved "https://registry.yarnpkg.com/@types/bn.js/-/bn.js-5.1.0.tgz#32c5d271503a12653c62cf4d2b45e6eab8cebc68" - integrity sha512-QSSVYj7pYFN49kW77o2s9xTCwZ8F2xLbjLLSEVh8D2F4JUhZtPAGOFLTD+ffqksBx/u4cE/KImFjyhqCjn/LIA== - dependencies: - "@types/node" "*" - -"@types/bn.js@^5.1.1", "@types/bn.js@~5.1.0": +"@types/bn.js@^5.1.1": version "5.1.1" resolved "https://registry.yarnpkg.com/@types/bn.js/-/bn.js-5.1.1.tgz#b51e1b55920a4ca26e9285ff79936bbdec910682" integrity sha512-qNrYbZqMx0uJAfKnKclPh+dTwK33KfLHYqtyODwd5HnXOjnkhc4qgn3BrK6RWyGZm5+sIFE7Q7Vz6QQtJB7w7g== dependencies: "@types/node" "*" -"@types/bytebuffer@^5.0.40": - version "5.0.44" - resolved "https://registry.yarnpkg.com/@types/bytebuffer/-/bytebuffer-5.0.44.tgz#553015fb34db1fc3eb3f7b232bff91c006c251a1" - integrity sha512-k1qonHga/SfQT02NF633i+7tIfKd+cfC/8pjnedcfuXJNMWooss/FkCgRMSnLf2WorLjbuH4bfgAZEbtyHBDoQ== - dependencies: - "@types/long" "^3.0.0" - "@types/node" "*" - "@types/color-convert@^2.0.0": version "2.0.0" resolved "https://registry.yarnpkg.com/@types/color-convert/-/color-convert-2.0.0.tgz#8f5ee6b9e863dcbee5703f5a517ffb13d3ea4e22" @@ -8947,13 +6257,6 @@ resolved "https://registry.yarnpkg.com/@types/color-name/-/color-name-1.1.1.tgz#1c1261bbeaa10a8055bbc5d8ab84b7b2afc846a0" integrity sha512-rr+OQyAjxze7GgWrSaJwydHStIhHq2lvY3BOC2Mj7KnzI7XK0Uw1TOOdI9lDoajEbSWLiYgoo4f1R51erQfhPQ== -"@types/connect@^3.4.33": - version "3.4.35" - resolved "https://registry.yarnpkg.com/@types/connect/-/connect-3.4.35.tgz#5fcf6ae445e4021d1fc2219a4873cc73a3bb2ad1" - integrity sha512-cdeYyv4KWoEgpBISTxWvqYsVy444DOqehiF3fM3ne10AmJ62RSyNkUnxMJXHQWRQQX2eR94m5y1IZyDwBjV9FQ== - dependencies: - "@types/node" "*" - "@types/eslint-scope@^3.7.3": version "3.7.3" resolved "https://registry.yarnpkg.com/@types/eslint-scope/-/eslint-scope-3.7.3.tgz#125b88504b61e3c8bc6f870882003253005c3224" @@ -9098,15 +6401,10 @@ resolved "https://registry.yarnpkg.com/@types/lodash/-/lodash-4.14.182.tgz#05301a4d5e62963227eaafe0ce04dd77c54ea5c2" integrity sha512-/THyiqyQAP9AfARo4pF+aCGcyiQ94tX/Is2I7HofNRqoYLgN1PBoOWu2/zTA5zMxzP5EFutMtWtGAFRKUe961Q== -"@types/long@^3.0.0": - version "3.0.32" - resolved "https://registry.yarnpkg.com/@types/long/-/long-3.0.32.tgz#f4e5af31e9e9b196d8e5fca8a5e2e20aa3d60b69" - integrity sha512-ZXyOOm83p7X8p3s0IYM3VeueNmHpkk/yMlP8CLeOnEcu6hIwPH7YjZBvhQkR0ZFS2DqZAxKtJ/M5fcuv3OU5BA== - -"@types/long@^4.0.1", "@types/long@^4.0.2": - version "4.0.2" - resolved "https://registry.yarnpkg.com/@types/long/-/long-4.0.2.tgz#b74129719fc8d11c01868010082d483b7545591a" - integrity sha512-MqTGEo5bj5t157U6fA/BiDynNkn0YknVdh48CMPkTSpFTVmvao5UQmm7uEF6xBEo7qIMAlY/JSleYaE6VOdpaA== +"@types/lodash@^4.14.175": + version "4.14.191" + resolved "https://registry.yarnpkg.com/@types/lodash/-/lodash-4.14.191.tgz#09511e7f7cba275acd8b419ddac8da9a6a79e2fa" + integrity sha512-BdZ5BCCvho3EIXw6wUCXHe7rS53AIDPLE+JzwgT+OsJk53oBfbSmZZ7CX4VaRoN78N+TJpFi9QPlfIVNmJYWxQ== "@types/mdast@^3.0.0": version "3.0.10" @@ -9125,7 +6423,7 @@ resolved "https://registry.yarnpkg.com/@types/minimist/-/minimist-1.2.2.tgz#ee771e2ba4b3dc5b372935d549fd9617bf345b8c" integrity sha512-jhuKLIRrhvCPLqwPcx6INqmKeiA5EWrsCOPhrlFSrbrmU4ZMPjj5Ul/oLCMDO98XRUIwVm78xICz4EPCektzeQ== -"@types/node-fetch@^2.5.7", "@types/node-fetch@^2.6.1": +"@types/node-fetch@^2.5.7": version "2.6.1" resolved "https://registry.yarnpkg.com/@types/node-fetch/-/node-fetch-2.6.1.tgz#8f127c50481db65886800ef496f20bbf15518975" integrity sha512-oMqjURCaxoSIsHSr1E47QHzbmzNR5rK8McHuNb11BOM9cHcIK3Avy0s/b2JlXHoQGTYS3NsvWzV1M0iK7l0wbA== @@ -9151,31 +6449,16 @@ resolved "https://registry.yarnpkg.com/@types/node/-/node-10.12.18.tgz#1d3ca764718915584fcd9f6344621b7672665c67" integrity sha512-fh+pAqt4xRzPfqA6eh3Z2y6fyZavRIumvjhaCL753+TVkGKGhpPeyrJG2JftD0T9q4GF00KjefsQ+PQNDdWQaQ== -"@types/node@11.11.6": - version "11.11.6" - resolved "https://registry.yarnpkg.com/@types/node/-/node-11.11.6.tgz#df929d1bb2eee5afdda598a41930fe50b43eaa6a" - integrity sha512-Exw4yUWMBXM3X+8oqzJNRqZSwUAaS4+7NdvHqQuFi/d+synz++xmX3QIf+BFqneW8N31R8Ky+sikfZUXq07ggQ== - "@types/node@>=12": version "17.0.32" resolved "https://registry.yarnpkg.com/@types/node/-/node-17.0.32.tgz#51d59d7a90ef2d0ae961791e0900cad2393a0149" integrity sha512-eAIcfAvhf/BkHcf4pkLJ7ECpBAhh9kcxRBpip9cTiO+hf+aJrsxYxBeS6OXvOd9WqNAJmavXVpZvY1rBjNsXmw== -"@types/node@>=12.12.47", "@types/node@>=13.7.0", "@types/node@^18.0.3": - version "18.11.9" - resolved "https://registry.yarnpkg.com/@types/node/-/node-18.11.9.tgz#02d013de7058cea16d36168ef2fc653464cfbad4" - integrity sha512-CRpX21/kGdzjOpFsZSkcrXMGIBWMGNIHXXBVFSH+ggkftxg+XYP20TESbh+zFvFj3EQOl5byk0HTRn1IL6hbqg== - "@types/node@^12.0.0": version "12.20.47" resolved "https://registry.yarnpkg.com/@types/node/-/node-12.20.47.tgz#ca9237d51f2a2557419688511dab1c8daf475188" integrity sha512-BzcaRsnFuznzOItW1WpQrDHM7plAa7GIDMZ6b5pnMbkqEtM/6WCOhvZar39oeMQP79gwvFUWjjptE7/KGcNqFg== -"@types/node@^12.12.54": - version "12.20.55" - resolved "https://registry.yarnpkg.com/@types/node/-/node-12.20.55.tgz#c329cbd434c42164f846b909bd6f85b5537f6240" - integrity sha512-J8xLz7q2OFulZ2cyGTLE1TbbZcjpno7FaN6zdJNrgAdrJ+DZzh/uFR6YrTb4C+nXakvud8Q4+rbhoIWlYQbUFQ== - "@types/node@^14.0.10 || ^16.0.0", "@types/node@^14.14.20 || ^16.0.0": version "16.11.36" resolved "https://registry.yarnpkg.com/@types/node/-/node-16.11.36.tgz#9ab9f8276987132ed2b225cace2218ba794fc751" @@ -9245,6 +6528,13 @@ dependencies: "@types/react" "*" +"@types/react-dom@<18.0.0": + version "17.0.19" + resolved "https://registry.yarnpkg.com/@types/react-dom/-/react-dom-17.0.19.tgz#36feef3aa35d045cacd5ed60fe0eef5272f19492" + integrity sha512-PiYG40pnQRdPHnlf7tZnp0aQ6q9tspYr72vD61saO6zFCybLfMqwUCN0va1/P+86DXn18ZWeW30Bk7xlC5eEAQ== + dependencies: + "@types/react" "^17" + "@types/react-paginate@7.1.1": version "7.1.1" resolved "https://registry.yarnpkg.com/@types/react-paginate/-/react-paginate-7.1.1.tgz#81cbe7bfcf2af60b9d784a56cd0e5d870675c68c" @@ -9296,6 +6586,13 @@ dependencies: "@types/react" "*" +"@types/react-transition-group@^4.4.5": + version "4.4.5" + resolved "https://registry.yarnpkg.com/@types/react-transition-group/-/react-transition-group-4.4.5.tgz#aae20dcf773c5aa275d5b9f7cdbca638abc5e416" + integrity sha512-juKD/eiSM3/xZYzjuzH6ZwpP+/lejltmiS3QEzV/vmb/Q8+HfDmxu+Baga8UEMGBqV88Nbg4l2hY/K2DkyaLLA== + dependencies: + "@types/react" "*" + "@types/react@*", "@types/react@17.0.0": version "17.0.0" resolved "https://registry.yarnpkg.com/@types/react/-/react-17.0.0.tgz#5af3eb7fad2807092f0046a1302b7823e27919b8" @@ -9304,12 +6601,14 @@ "@types/prop-types" "*" csstype "^3.0.2" -"@types/redux-logger@^3.0.8": - version "3.0.9" - resolved "https://registry.yarnpkg.com/@types/redux-logger/-/redux-logger-3.0.9.tgz#9193b3d51bb6ab98d25514ba7764e4f98a64d3ec" - integrity sha512-cwYhVbYNgH01aepeMwhd0ABX6fhVB2rcQ9m80u8Fl50ZODhsZ8RhQArnLTkE7/Zrfq4Sz/taNoF7DQy9pCZSKg== +"@types/react@^17": + version "17.0.53" + resolved "https://registry.yarnpkg.com/@types/react/-/react-17.0.53.tgz#10d4d5999b8af3d6bc6a9369d7eb953da82442ab" + integrity sha512-1yIpQR2zdYu1Z/dc1OxC+MA6GR240u3gcnP4l6mvj/PJiVaqHsQPmWttsvHsfnhfPbU2FuGmo0wSITPygjBmsw== dependencies: - redux "^4.0.0" + "@types/prop-types" "*" + "@types/scheduler" "*" + csstype "^3.0.2" "@types/resolve@0.0.8": version "0.0.8" @@ -9318,6 +6617,11 @@ dependencies: "@types/node" "*" +"@types/scheduler@*": + version "0.16.2" + resolved "https://registry.yarnpkg.com/@types/scheduler/-/scheduler-0.16.2.tgz#1a62f89525723dde24ba1b01b092bf5df8ad4d39" + integrity sha512-hppQEBDmlwhFAXKJX2KnWLYu5yMfi91yazPb2l+lbJiwW+wdo1gNeRA+3RgNSO39WYX2euey41KEwnqesU2Jew== + "@types/source-list-map@*": version "0.1.2" resolved "https://registry.yarnpkg.com/@types/source-list-map/-/source-list-map-0.1.2.tgz#0078836063ffaf17412349bba364087e0ac02ec9" @@ -9404,13 +6708,6 @@ dependencies: "@types/node" "*" -"@types/ws@^7.4.4": - version "7.4.7" - resolved "https://registry.yarnpkg.com/@types/ws/-/ws-7.4.7.tgz#f7c390a36f7a0679aa69de2d501319f4f8d9b702" - integrity sha512-JQbbmxZTZehdc2iszGKs5oC3NFnjeay7mtAWrdt7qNtAVK0g19muApzAy4bm9byz79xa2ZnO/BOBC2R8RC5Lww== - dependencies: - "@types/node" "*" - "@types/yargs-parser@*": version "21.0.0" resolved "https://registry.yarnpkg.com/@types/yargs-parser/-/yargs-parser-21.0.0.tgz#0c60e537fa790f5f9472ed2776c2b71ec117351b" @@ -9559,11 +6856,6 @@ dependencies: uuid "^3.0.1" -"@use-it/event-listener@^0.1.2": - version "0.1.7" - resolved "https://registry.yarnpkg.com/@use-it/event-listener/-/event-listener-0.1.7.tgz#443a9b6df87f2f2961b74d42997ce723a7078623" - integrity sha512-hgfExDzUU9uTRTPDCpw2s9jWTxcxmpJya3fK5ADpf5VDpSy8WYwY/kh28XE0tUcbsljeP8wfan48QvAQTSSa3Q== - "@webassemblyjs/ast@1.11.1": version "1.11.1" resolved "https://registry.yarnpkg.com/@webassemblyjs/ast/-/ast-1.11.1.tgz#2bfd767eae1a6996f432ff7e8d7fc75679c0b6a7" @@ -9855,7 +7147,7 @@ resolved "https://registry.yarnpkg.com/@zeroio/type-definitions/-/type-definitions-0.0.14.tgz#9331e7bbbd96fa47cf62a4a0a3fa1caaaafcd7aa" integrity sha512-OkqtOLPkR7oqWLrsgRKhzyLZVFLnNLfEF3DMXH+Rpn1fMNMDq/fOY9pXt55B+flBc62saN73CfOTy3hMSVZFTA== -JSONStream@^1.0.4, JSONStream@^1.3.5: +JSONStream@^1.0.4: version "1.3.5" resolved "https://registry.yarnpkg.com/JSONStream/-/JSONStream-1.3.5.tgz#3208c1f08d3a4d99261ab64f92302bc15e111ca0" integrity sha512-E+iruNOY8VV9s4JEbe1aNEm6MiszPRr/UfcHMz0TQh1BXSxHK+ASV1R6W4HpjBhSeS+54PIsAMCBmwD06LLsqQ== @@ -9868,12 +7160,7 @@ abab@^2.0.3, abab@^2.0.5: resolved "https://registry.yarnpkg.com/abab/-/abab-2.0.5.tgz#c0b678fb32d60fc1219c784d6a826fe385aeb79a" integrity sha512-9IK9EadsbHo6jLWIpxpR6pL0sazTXV6+SQv25ZB+F7Bj9mJNaOc4nCRabwd5M/JwmUa8idz6Eci6eKfJryPs6Q== -abbrev@1: - version "1.1.1" - resolved "https://registry.yarnpkg.com/abbrev/-/abbrev-1.1.1.tgz#f8f2c887ad10bf67f634f005b6987fed3179aac8" - integrity sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q== - -accepts@^1.3.5, accepts@~1.3.4, accepts@~1.3.5, accepts@~1.3.8: +accepts@~1.3.4, accepts@~1.3.5, accepts@~1.3.8: version "1.3.8" resolved "https://registry.yarnpkg.com/accepts/-/accepts-1.3.8.tgz#0bf0be125b67014adcb0b0921e62db7bffe16b2e" integrity sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw== @@ -10024,27 +7311,6 @@ ajv@^8.0.1: require-from-string "^2.0.2" uri-js "^4.2.2" -algo-msgpack-with-bigint@^2.1.1: - version "2.1.1" - resolved "https://registry.yarnpkg.com/algo-msgpack-with-bigint/-/algo-msgpack-with-bigint-2.1.1.tgz#38bb717220525b3ff42232eefdcd9efb9ad405d6" - integrity sha512-F1tGh056XczEaEAqu7s+hlZUDWwOBT70Eq0lfMpBP2YguSQVyxRbprLq5rELXKQOyOaixTWYhMeMQMzP0U5FoQ== - -algosdk@^1.15.0: - version "1.23.2" - resolved "https://registry.yarnpkg.com/algosdk/-/algosdk-1.23.2.tgz#1fda2b537d832687da64bf708f10a69086f02b43" - integrity sha512-ZDq71Kq+e3bvHxnTi/hWqom2/YHeTrbkxenOOCvPidRQuwkaOQAs92c4fqImdn7+Y+MfLhdtQspBLFR1hQTE4A== - dependencies: - algo-msgpack-with-bigint "^2.1.1" - buffer "^6.0.2" - cross-fetch "^3.1.5" - hi-base32 "^0.5.1" - js-sha256 "^0.9.0" - js-sha3 "^0.8.0" - js-sha512 "^0.8.0" - json-bigint "^1.0.0" - tweetnacl "^1.0.3" - vlq "^2.0.4" - alphanum-sort@^1.0.0: version "1.0.2" resolved "https://registry.yarnpkg.com/alphanum-sort/-/alphanum-sort-1.0.2.tgz#97a1119649b211ad33691d9f9f486a8ec9fbe0a3" @@ -10328,14 +7594,6 @@ asap@~2.0.6: resolved "https://registry.yarnpkg.com/asap/-/asap-2.0.6.tgz#e50347611d7e690943208bbdafebcbc2fb866d46" integrity sha1-5QNHYR1+aQlDIIu9r+vLwvuGbUY= -ascli@~1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/ascli/-/ascli-1.0.1.tgz#bcfa5974a62f18e81cabaeb49732ab4a88f906bc" - integrity sha512-JGQaNxpaCJz9Bd1JvVaFIHuWn9S+l3xhN17R0V/vmUDiGE0QngNMXhjlqpwqV+91plWz9Fg+Lt28Lj7p5vjs8A== - dependencies: - colour "~0.7.1" - optjs "~3.2.2" - asn1.js@^5.2.0: version "5.4.1" resolved "https://registry.yarnpkg.com/asn1.js/-/asn1.js-5.4.1.tgz#11a980b84ebb91781ce35b0fdc2ee294e3783f07" @@ -10766,14 +8024,14 @@ balanced-match@^1.0.0: resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-1.0.2.tgz#e83e3a7e3f300b34cb9d87f615fa0cbf357690ee" integrity sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw== -base-x@^3.0.2, base-x@^3.0.8: +base-x@^3.0.2: version "3.0.9" resolved "https://registry.yarnpkg.com/base-x/-/base-x-3.0.9.tgz#6349aaabb58526332de9f60995e548a53fe21320" integrity sha512-H7JU6iBHTal1gp56aKoaa//YUxEaAOUiydvrV/pILqIHXTtqxSkATOnDA2u+jZ/61sD+L/412+7kzXRtWukhpQ== dependencies: safe-buffer "^5.0.1" -base64-js@^1.0.2, base64-js@^1.3.1, base64-js@^1.5.1: +base64-js@^1.0.2, base64-js@^1.3.1: version "1.5.1" resolved "https://registry.yarnpkg.com/base64-js/-/base64-js-1.5.1.tgz#1b1b440160a5bf7ad40b650f095963481903930a" integrity sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA== @@ -10808,11 +8066,6 @@ bech32@1.1.4, bech32@^1.1.2: resolved "https://registry.yarnpkg.com/bech32/-/bech32-1.1.4.tgz#e38c9f37bf179b8eb16ae3a772b40c356d4832e9" integrity sha512-s0IrSOzLlbvX7yp4WBfPITzpAU8sqQcpsmwXDiKwrG4r491vwCO/XpejasRNl0piBMe/DvP4Tz0mIS/X1DPJBQ== -bech32@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/bech32/-/bech32-2.0.0.tgz#078d3686535075c8c79709f054b1b226a133b355" - integrity sha512-LcknSilhIGatDAsY1ak2I8VtGaHNhgMSYVxFrGLXv+xLHytaKZKcaUJJUE7qmBr7h33o5YQwP55pMI0xmkpJwg== - before-after-hook@^2.2.0: version "2.2.3" resolved "https://registry.yarnpkg.com/before-after-hook/-/before-after-hook-2.2.3.tgz#c51e809c81a4e354084422b9b26bad88249c517c" @@ -10850,13 +8103,6 @@ big.js@^5.2.2: resolved "https://registry.yarnpkg.com/big.js/-/big.js-5.2.2.tgz#65f0af382f578bcdc742bd9c281e9cb2d7768328" integrity sha512-vyL2OymJxmarO8gxMr0mhChsO9QGwhynfuu4+MHTAW6czfq9humCB7rKpUjDd9YUiDPU4mzpyupFSvOClAwbmQ== -bigint-buffer@^1.1.5: - version "1.1.5" - resolved "https://registry.yarnpkg.com/bigint-buffer/-/bigint-buffer-1.1.5.tgz#d038f31c8e4534c1f8d0015209bf34b4fa6dd442" - integrity sha512-trfYco6AoZ+rKhKnxA0hgX0HAbVP/s808/EuDSe2JDzUnCp/xAsli35Orvk67UrTEcwuxZqYZDmfA2RXJgxVvA== - dependencies: - bindings "^1.3.0" - bignumber.js@^4.0.0: version "4.1.0" resolved "https://registry.yarnpkg.com/bignumber.js/-/bignumber.js-4.1.0.tgz#db6f14067c140bd46624815a7916c92d9b6c24b1" @@ -10889,7 +8135,7 @@ bip174@^2.0.1: resolved "https://registry.yarnpkg.com/bip174/-/bip174-2.0.1.tgz#39cf8ca99e50ce538fb762589832f4481d07c254" integrity sha512-i3X26uKJOkDTAalYAp0Er+qGMDhrbbh2o93/xiPyAN2s25KrClSpe3VXo/7mNJoqA5qfko8rLS2l3RWZgYmjKQ== -bip32@^2.0.4, bip32@^2.0.6: +bip32@^2.0.4: version "2.0.6" resolved "https://registry.yarnpkg.com/bip32/-/bip32-2.0.6.tgz#6a81d9f98c4cd57d05150c60d8f9e75121635134" integrity sha512-HpV5OMLLGTjSVblmrtYRfFFKuQB+GArM0+XP8HGWfJ5vxYBqo+DesvJwOdC2WJ3bCkZShGf0QIfoIpeomVzVdA== @@ -10902,16 +8148,6 @@ bip32@^2.0.4, bip32@^2.0.6: typeforce "^1.11.5" wif "^2.0.6" -bip39@^3.0.3: - version "3.0.4" - resolved "https://registry.yarnpkg.com/bip39/-/bip39-3.0.4.tgz#5b11fed966840b5e1b8539f0f54ab6392969b2a0" - integrity sha512-YZKQlb752TrUWqHWj7XAwCSjYEgGAk+/Aas3V7NyjQeZYsztO8JnQUaCWhcnL4T+jL8nvB8typ2jRPzTlgugNw== - dependencies: - "@types/node" "11.11.6" - create-hash "^1.1.0" - pbkdf2 "^3.0.9" - randombytes "^2.0.1" - bip66@^1.1.0: version "1.1.5" resolved "https://registry.yarnpkg.com/bip66/-/bip66-1.1.5.tgz#01fa8748785ca70955d5011217d1b3139969ca22" @@ -10967,17 +8203,12 @@ bl@^5.0.0: inherits "^2.0.4" readable-stream "^3.4.0" -blakejs@^1.1.1: - version "1.2.1" - resolved "https://registry.yarnpkg.com/blakejs/-/blakejs-1.2.1.tgz#5057e4206eadb4a97f7c0b6e197a505042fc3814" - integrity sha512-QXUSXI3QVc/gJME0dBpXrag1kbzOqCjCX8/b54ntNyW6sjtoqxqRk3LTmXzaJoh71zMsDCjM+47jS7XiwN/+fQ== - bluebird@^3.3.5, bluebird@^3.5.5: version "3.7.2" resolved "https://registry.yarnpkg.com/bluebird/-/bluebird-3.7.2.tgz#9f229c15be272454ffa973ace0dbee79a1b0c36f" integrity sha512-XpNj6GDQzdfW+r2Wnn7xiSAd7TM3jzkxGXBGTtWKuSXv1xUV+azxAm8jdWZN06QTQk+2N2XB9jRDkvbmQmcRtg== -bn.js@4.12.0, bn.js@^4.0.0, bn.js@^4.1.0, bn.js@^4.11.8, bn.js@^4.11.9, bn.js@^4.12.0, bn.js@^5.0.0, bn.js@^5.1.0, bn.js@^5.1.1, bn.js@^5.2.0, bn.js@^5.2.1, bn.js@~5.2.0: +bn.js@4.12.0, bn.js@^4.0.0, bn.js@^4.1.0, bn.js@^4.11.8, bn.js@^4.11.9, bn.js@^5.0.0, bn.js@^5.1.1, bn.js@^5.2.1, bn.js@~5.2.0: version "4.12.0" resolved "https://registry.yarnpkg.com/bn.js/-/bn.js-4.12.0.tgz#775b3f278efbb9718eec7361f483fb36fbbfea88" integrity sha512-c98Bf3tPniI+scsdk237ku1Dc3ujXQTSgyiPUDEOe7tRkhrqridvh8klBv0HCEso1OLOYcHuCv/cS6DNxKH+ZA== @@ -11015,15 +8246,6 @@ boolbase@^1.0.0, boolbase@~1.0.0: resolved "https://registry.yarnpkg.com/boolbase/-/boolbase-1.0.0.tgz#68dff5fbe60c51eb37725ea9e3ed310dcc1e776e" integrity sha1-aN/1++YMUes3cl6p4+0xDcwed24= -borsh@^0.7.0: - version "0.7.0" - resolved "https://registry.yarnpkg.com/borsh/-/borsh-0.7.0.tgz#6e9560d719d86d90dc589bca60ffc8a6c51fec2a" - integrity sha512-CLCsZGIBCFnPtkNnieW/a8wmreDmfUtjU2m9yHrzPXIlNbqVs0AQrSatSG6vdNYUqdc83tkQi2eHfF98ubzQLA== - dependencies: - bn.js "^5.2.0" - bs58 "^4.0.0" - text-encoding-utf-8 "^1.0.2" - boxen@^5.1.2: version "5.1.2" resolved "https://registry.yarnpkg.com/boxen/-/boxen-5.1.2.tgz#788cb686fc83c1f486dfa8a40c68fc2b831d2b50" @@ -11109,11 +8331,6 @@ brorand@^1.0.1, brorand@^1.1.0: resolved "https://registry.yarnpkg.com/brorand/-/brorand-1.1.0.tgz#12c25efe40a45e3c323eb8675a0a0ce57b22371f" integrity sha1-EsJe/kCkXjwyPrhnWgoM5XsiNx8= -browser-headers@^0.4.1: - version "0.4.1" - resolved "https://registry.yarnpkg.com/browser-headers/-/browser-headers-0.4.1.tgz#4308a7ad3b240f4203dbb45acedb38dc2d65dd02" - integrity sha512-CA9hsySZVo9371qEHjHZtYxV2cFtVj5Wj/ZHi8ooEsrtm4vOnl9Y9HmyYWk9q+05d7K3rdoAE0j3MVEFVvtQtg== - browser-process-hrtime@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/browser-process-hrtime/-/browser-process-hrtime-1.0.0.tgz#3c9b4b7d782c8121e56f10106d84c0d0ffc94626" @@ -11212,7 +8429,7 @@ browserslist@^4.14.5: node-releases "^2.0.3" picocolors "^1.0.0" -bs58@^4.0.0, bs58@^4.0.1: +bs58@^4.0.0: version "4.0.1" resolved "https://registry.yarnpkg.com/bs58/-/bs58-4.0.1.tgz#be161e76c354f6f788ae4071f63f34e8c4f0a42a" integrity sha1-vhYedsNU9veIrkBx9j806MTwpCo= @@ -11245,32 +8462,11 @@ buffer-indexof@^1.0.0: resolved "https://registry.yarnpkg.com/buffer-indexof/-/buffer-indexof-1.1.1.tgz#52fabcc6a606d1a00302802648ef68f639da268c" integrity sha512-4/rOEg86jivtPTeOUUT61jJO1Ya1TrR/OkqCSZDyq84WJh3LuuiphBYJN+fm5xufIk4XAFcEwte/8WzC8If/1g== -buffer-layout@^1.2.0: - version "1.2.2" - resolved "https://registry.yarnpkg.com/buffer-layout/-/buffer-layout-1.2.2.tgz#b9814e7c7235783085f9ca4966a0cfff112259d5" - integrity sha512-kWSuLN694+KTk8SrYvCqwP2WcgQjoRCiF5b4QDvkkz8EmgD+aWAIceGFKMIAdmF/pH+vpgNV3d3kAKorcdAmWA== - buffer-xor@^1.0.3: version "1.0.3" resolved "https://registry.yarnpkg.com/buffer-xor/-/buffer-xor-1.0.3.tgz#26e61ed1422fb70dd42e6e36729ed51d855fe8d9" integrity sha1-JuYe0UIvtw3ULm42cp7VHYVf6Nk= -buffer@6.0.1: - version "6.0.1" - resolved "https://registry.yarnpkg.com/buffer/-/buffer-6.0.1.tgz#3cbea8c1463e5a0779e30b66d4c88c6ffa182ac2" - integrity sha512-rVAXBwEcEoYtxnHSO5iWyhzV/O1WMtkUYWlfdLS7FjU4PnSJJHEfHXi/uHPI5EwltmOA794gN3bm3/pzuctWjQ== - dependencies: - base64-js "^1.3.1" - ieee754 "^1.2.1" - -buffer@6.0.3, buffer@^6.0.1, buffer@^6.0.2, buffer@^6.0.3, buffer@~6.0.3: - version "6.0.3" - resolved "https://registry.yarnpkg.com/buffer/-/buffer-6.0.3.tgz#2ace578459cc8fbe2a70aaa8f52ee63b6a74c6c6" - integrity sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA== - dependencies: - base64-js "^1.3.1" - ieee754 "^1.2.1" - buffer@^4.3.0: version "4.9.2" resolved "https://registry.yarnpkg.com/buffer/-/buffer-4.9.2.tgz#230ead344002988644841ab0244af8c44bbe3ef8" @@ -11280,14 +8476,15 @@ buffer@^4.3.0: ieee754 "^1.1.4" isarray "^1.0.0" -bufferutil@^4.0.1: - version "4.0.6" - resolved "https://registry.yarnpkg.com/bufferutil/-/bufferutil-4.0.6.tgz#ebd6c67c7922a0e902f053e5d8be5ec850e48433" - integrity sha512-jduaYOYtnio4aIAyc6UbvPCVcgq7nYpVnucyxr6eCYg/Woad9Hf/oxxBRDnGGjPfjUm6j5O/uBWhIu4iLebFaw== +buffer@^6.0.3: + version "6.0.3" + resolved "https://registry.yarnpkg.com/buffer/-/buffer-6.0.3.tgz#2ace578459cc8fbe2a70aaa8f52ee63b6a74c6c6" + integrity sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA== dependencies: - node-gyp-build "^4.3.0" + base64-js "^1.3.1" + ieee754 "^1.2.1" -bufferutil@^4.0.3: +bufferutil@^4.0.1: version "4.0.7" resolved "https://registry.yarnpkg.com/bufferutil/-/bufferutil-4.0.7.tgz#60c0d19ba2c992dd8273d3f73772ffc894c153ad" integrity sha512-kukuqc39WOHtdxtw4UScxF/WVnMFVSQVKhtx3AjZJzhd0RGZZldcrfSEbVsWWe6KNH253574cq5F+wpv0G9pJw== @@ -11314,13 +8511,6 @@ bunyan@^1.8.1: mv "~2" safe-json-stringify "~1" -bytebuffer@~5: - version "5.0.1" - resolved "https://registry.yarnpkg.com/bytebuffer/-/bytebuffer-5.0.1.tgz#582eea4b1a873b6d020a48d58df85f0bba6cfddd" - integrity sha512-IuzSdmADppkZ6DlpycMkm8l9zeEq16fWtLvunEwFiYciR/BHo4E8/xs5piFquG+Za8OWmMqHF8zuRviz2LHvRQ== - dependencies: - long "~3" - bytes@3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/bytes/-/bytes-3.0.0.tgz#d32815404d689699f85a4ea4fa8755dd13a96048" @@ -11409,14 +8599,6 @@ cache-base@^1.0.1: union-value "^1.0.0" unset-value "^1.0.0" -cache-content-type@^1.0.0: - version "1.0.1" - resolved "https://registry.yarnpkg.com/cache-content-type/-/cache-content-type-1.0.1.tgz#035cde2b08ee2129f4a8315ea8f00a00dba1453c" - integrity sha512-IKufZ1o4Ut42YUrZSo8+qnMTrFuKkvyoLXUywKz9GJ5BrhOFGhLdkx9sG4KAnVvbY6kEcSFjLQul+DVmBm2bgA== - dependencies: - mime-types "^2.1.18" - ylru "^1.2.0" - cacheable-lookup@^7.0.0: version "7.0.0" resolved "https://registry.yarnpkg.com/cacheable-lookup/-/cacheable-lookup-7.0.0.tgz#3476a8215d046e5a3202a9209dd13fec1f933a27" @@ -11507,12 +8689,12 @@ camelcase@5.3.1, camelcase@^5.0.0, camelcase@^5.3.1: resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-5.3.1.tgz#e3c9b31569e106811df242f715725a1f4c494320" integrity sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg== -camelcase@^2.0.0, camelcase@^2.0.1: +camelcase@^2.0.0: version "2.1.1" resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-2.1.1.tgz#7c1d16d679a1bbe59ca02cacecfb011e201f5a1f" integrity sha512-DLIsRzJVBQu72meAKPkWQOLcujdXT32hwdfnkI1frSiSRMK1MofjKHf+MEx0SB6fjEFXL8fBDv1dKymBlOp4Qw== -camelcase@^6.0.0, camelcase@^6.1.0, camelcase@^6.2.0, camelcase@^6.2.1: +camelcase@^6.0.0, camelcase@^6.1.0, camelcase@^6.2.0: version "6.3.0" resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-6.3.0.tgz#5685b95eb209ac9c0c177467778c9c84df58ba9a" integrity sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA== @@ -11817,16 +8999,7 @@ cli-truncate@^2.1.0: cli-width@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/cli-width/-/cli-width-4.0.0.tgz#a5622f6a3b0a9e3e711a25f099bf2399f608caf6" - integrity sha512-ZksGS2xpa/bYkNzN3BAw1wEjsLV/ZKOf/CCrJ/QOBsxx6fOARIkwTutxp1XIOIohi6HKmOFjMoK/XaqDVUpEEw== - -cliui@^3.0.3: - version "3.2.0" - resolved "https://registry.yarnpkg.com/cliui/-/cliui-3.2.0.tgz#120601537a916d29940f934da3b48d585a39213d" - integrity sha512-0yayqDxWQbqk3ojkYqUKqaAQ6AfNKeKWRNA8kR0WXzAsdHpP4BIaOmMAG87JGuO6qcobyW4GjxHd9PmhEd+T9w== - dependencies: - string-width "^1.0.1" - strip-ansi "^3.0.1" - wrap-ansi "^2.0.0" + integrity sha512-ZksGS2xpa/bYkNzN3BAw1wEjsLV/ZKOf/CCrJ/QOBsxx6fOARIkwTutxp1XIOIohi6HKmOFjMoK/XaqDVUpEEw== cliui@^5.0.0: version "5.0.0" @@ -11888,11 +9061,6 @@ coa@^2.0.2: chalk "^2.4.1" q "^1.1.2" -code-point-at@^1.0.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/code-point-at/-/code-point-at-1.1.0.tgz#0d070b4d043a5bea33a2f1a40e2edb3d9a4ccf77" - integrity sha512-RpAVKQA5T63xEj6/giIbUEtZwJ4UFIc3ZtvEkiaUERylqe8xb5IvqcgOurZLahv93CLKfxcw5YI+DZcUBRyLXA== - collapse-white-space@^1.0.2: version "1.0.6" resolved "https://registry.yarnpkg.com/collapse-white-space/-/collapse-white-space-1.0.6.tgz#e63629c0016665792060dbbeb79c42239d2c5287" @@ -11974,11 +9142,6 @@ colors@1.4.0: resolved "https://registry.yarnpkg.com/colors/-/colors-1.4.0.tgz#c50491479d4c1bdaed2c9ced32cf7c7dc2360f78" integrity sha512-a+UqTh4kgZg/SlGvfbzDHpgRu7AAQOmmqRHJnxhRZICKFUT91brVhNNt58CMWU9PsBbv3PDCZUHbVxuDiH2mtA== -colour@~0.7.1: - version "0.7.1" - resolved "https://registry.yarnpkg.com/colour/-/colour-0.7.1.tgz#9cb169917ec5d12c0736d3e8685746df1cadf778" - integrity sha512-Rel466v0EnmKPcsxHo91L4kgPs/6XF7Pu2LJNszq9lXYwi5CFWEeIiRaTX5ym7PPMdj4udDHkLSVC1//JVkZQg== - combined-stream@^1.0.6, combined-stream@^1.0.8, combined-stream@~1.0.6: version "1.0.8" resolved "https://registry.yarnpkg.com/combined-stream/-/combined-stream-1.0.8.tgz#c3d45a8b34fd730631a110a8a2520682b31d5a7f" @@ -11991,7 +9154,7 @@ comma-separated-tokens@^1.0.0: resolved "https://registry.yarnpkg.com/comma-separated-tokens/-/comma-separated-tokens-1.0.8.tgz#632b80b6117867a158f1080ad498b2fbe7e3f5ea" integrity sha512-GHuDRO12Sypu2cV70d1dkA2EUmXHgntrzbpvOB+Qy+49ypNfGgFQIC2fhhXbnyrJRynDCAARsT7Ou0M6hirpfw== -commander@^2.19.0, commander@^2.20.0, commander@^2.20.3: +commander@^2.19.0, commander@^2.20.0: version "2.20.3" resolved "https://registry.yarnpkg.com/commander/-/commander-2.20.3.tgz#fd485e84c03eb4881c20722ba48035e8531aeb33" integrity sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ== @@ -12001,7 +9164,7 @@ commander@^4.1.1: resolved "https://registry.yarnpkg.com/commander/-/commander-4.1.1.tgz#9fd602bd936294e9e9ef46a3f4d6964044b18068" integrity sha512-NOKm8xhkzAjzFx8B2v5OAHT+u5pRQc2UCa2Vq9jYL/31o2wi9mxBA7LIFs3sV5VSC49z6pEhfbMULvShKj26WA== -commander@^6.0.0, commander@^6.2.0, commander@^6.2.1: +commander@^6.2.0, commander@^6.2.1: version "6.2.1" resolved "https://registry.yarnpkg.com/commander/-/commander-6.2.1.tgz#0792eb682dfbc325999bb2b84fddddba110ac73c" integrity sha512-U7VdrJFnJgo4xjrHpTzu0yrHPGImdsmD95ZlgYSEajAn2JKzDhDTPG9kBTefmObL2w/ngeZnilk+OV9CG3d7UA== @@ -12130,14 +9293,14 @@ constants-browserify@^1.0.0: resolved "https://registry.yarnpkg.com/constants-browserify/-/constants-browserify-1.0.0.tgz#c20b96d8c617748aaf1c16021760cd27fcb8cb75" integrity sha1-wguW2MYXdIqvHBYCF2DNJ/y4y3U= -content-disposition@0.5.4, content-disposition@~0.5.2: +content-disposition@0.5.4: version "0.5.4" resolved "https://registry.yarnpkg.com/content-disposition/-/content-disposition-0.5.4.tgz#8b82b4efac82512a02bb0b1dcec9d2c5e8eb5bfe" integrity sha512-FveZTNuGw04cxlAiWbzi6zTAL/lhehaWbTtgluJh4/E95DqMwTmha3KZN1aAWA8cFIhHzMZUvLevkw5Rqk+tSQ== dependencies: safe-buffer "5.2.1" -content-type@^1.0.4, content-type@~1.0.4: +content-type@~1.0.4: version "1.0.4" resolved "https://registry.yarnpkg.com/content-type/-/content-type-1.0.4.tgz#e138cc75e040c727b1966fe5e5f8c9aee256fe3b" integrity sha512-hIP3EEPs8tB9AT1L+NUqtwOAps4mk2Zob89MWXMHjHWg9milF/j4osnnQLXBCBFBk/tvIG/tUc9mOUJiPBhPXA== @@ -12200,14 +9363,6 @@ cookie@0.4.2: resolved "https://registry.yarnpkg.com/cookie/-/cookie-0.4.2.tgz#0e41f24de5ecf317947c82fc789e06a884824432" integrity sha512-aSWTXFzaKWkvHO1Ny/s+ePFpvKsPnjc551iI41v3ny/ow6tBG5Vd+FuqGNhh1LxOmVzOlGUriIlOaokOvhaStA== -cookies@~0.8.0: - version "0.8.0" - resolved "https://registry.yarnpkg.com/cookies/-/cookies-0.8.0.tgz#1293ce4b391740a8406e3c9870e828c4b54f3f90" - integrity sha512-8aPsApQfebXnuI+537McwYsDtjVxGm8gTIzQI3FDW6t5t/DAhERxtnbEPN/8RX+uZthoz4eCOgloXaE5cYyNow== - dependencies: - depd "~2.0.0" - keygrip "~1.1.0" - copy-concurrently@^1.0.0: version "1.0.5" resolved "https://registry.yarnpkg.com/copy-concurrently/-/copy-concurrently-1.0.5.tgz#92297398cae34937fcafd6ec8139c18051f0b5e0" @@ -12722,11 +9877,6 @@ csstype@^3.0.2, csstype@^3.0.6: resolved "https://registry.yarnpkg.com/csstype/-/csstype-3.0.11.tgz#d66700c5eacfac1940deb4e3ee5642792d85cd33" integrity sha512-sa6P2wJ+CAbgyy4KFssIb/JNMLxFvKF1pCYCSXS8ZMuqZnMsrxqI2E5sPyoTpxoPU/gVZMzr2zjOfg8GIZOMsw== -cuint@^0.2.2: - version "0.2.2" - resolved "https://registry.yarnpkg.com/cuint/-/cuint-0.2.2.tgz#408086d409550c2631155619e9fa7bcadc3b991b" - integrity sha512-d4ZVpCW31eWwCMe1YT3ur7mUDnTXbgwyzaL320DrcRT45rfjYxkt5QWLrmOJ+/UEAI2+fQgKe/fCjR8l4TpRgw== - currently-unhandled@^0.4.1: version "0.4.1" resolved "https://registry.yarnpkg.com/currently-unhandled/-/currently-unhandled-0.4.1.tgz#988df33feab191ef799a61369dd76c17adf957ea" @@ -12795,14 +9945,14 @@ debug@2.6.9, debug@^2.2.0, debug@^2.3.3, debug@^2.6.0, debug@^2.6.9: dependencies: ms "2.0.0" -debug@4, debug@^4.0.1, debug@^4.1.0, debug@^4.1.1, debug@^4.2.0, debug@^4.3.1, debug@^4.3.2: +debug@4, debug@^4.0.1, debug@^4.1.0, debug@^4.1.1, debug@^4.2.0, debug@^4.3.1: version "4.3.4" resolved "https://registry.yarnpkg.com/debug/-/debug-4.3.4.tgz#1319f6579357f2338d3337d2cdd4914bb5dcc865" integrity sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ== dependencies: ms "2.1.2" -debug@^3.0.0, debug@^3.1.0, debug@^3.1.1, debug@^3.2.7: +debug@^3.0.0, debug@^3.1.1, debug@^3.2.7: version "3.2.7" resolved "https://registry.yarnpkg.com/debug/-/debug-3.2.7.tgz#72580b7e9145fb39b6676f9c5e5fb100b934179a" integrity sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ== @@ -12825,7 +9975,7 @@ decamelize-keys@^1.1.0: decamelize "^1.1.0" map-obj "^1.0.0" -decamelize@^1.1.0, decamelize@^1.1.1, decamelize@^1.1.2, decamelize@^1.2.0: +decamelize@^1.1.0, decamelize@^1.1.2, decamelize@^1.2.0: version "1.2.0" resolved "https://registry.yarnpkg.com/decamelize/-/decamelize-1.2.0.tgz#f6534d15148269b20352e7bee26f501f9a191290" integrity sha1-9lNNFRSCabIDUue+4m9QH5oZEpA= @@ -12852,11 +10002,6 @@ dedent@^0.7.0: resolved "https://registry.yarnpkg.com/dedent/-/dedent-0.7.0.tgz#2495ddbaf6eb874abb0e1be9df22d2e5a544326c" integrity sha1-JJXduvbrh0q7Dhvp3yLS5aVEMmw= -deep-diff@^0.3.5: - version "0.3.8" - resolved "https://registry.yarnpkg.com/deep-diff/-/deep-diff-0.3.8.tgz#c01de63efb0eec9798801d40c7e0dae25b582c84" - integrity sha1-wB3mPvsO7JeYgB1Ax+Da4ltYLIQ= - deep-equal@^1.0.1: version "1.1.1" resolved "https://registry.yarnpkg.com/deep-equal/-/deep-equal-1.1.1.tgz#b5c98c942ceffaf7cb051e24e1434a25a2e6076a" @@ -12869,11 +10014,6 @@ deep-equal@^1.0.1: object-keys "^1.1.1" regexp.prototype.flags "^1.2.0" -deep-equal@~1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/deep-equal/-/deep-equal-1.0.1.tgz#f5d260292b660e084eff4cdbc9f08ad3247448b5" - integrity sha1-9dJgKStmDghO/0zbyfCK0yR0SLU= - deep-extend@^0.6.0: version "0.6.0" resolved "https://registry.yarnpkg.com/deep-extend/-/deep-extend-0.6.0.tgz#c4fa7c95404a17a9c3e8ca7e1537312b736330ac" @@ -12889,6 +10029,11 @@ deep-object-diff@^1.1.0: resolved "https://registry.yarnpkg.com/deep-object-diff/-/deep-object-diff-1.1.7.tgz#348b3246f426427dd633eaa50e1ed1fc2eafc7e4" integrity sha512-QkgBca0mL08P6HiOjoqvmm6xOAl2W6CT2+34Ljhg0OeFan8cwlcdq8jrLKsBBuUFAZLsN5b6y491KdKEoSo9lg== +deepmerge@^2.1.1: + version "2.2.1" + resolved "https://registry.yarnpkg.com/deepmerge/-/deepmerge-2.2.1.tgz#5d3ff22a01c00f645405a2fbc17d0778a1801170" + integrity sha512-R9hc1Xa/NOBi9WRVUWg19rl1UB7Tt4kuPd+thNJgFZoxXsTz7ncaPaeIm+40oSGuP33DfMb4sZt1QIGiJzC4EA== + deepmerge@^4.2.2: version "4.2.2" resolved "https://registry.yarnpkg.com/deepmerge/-/deepmerge-4.2.2.tgz#44d2ea3679b8f4d4ffba33f03d865fc1e7bf4955" @@ -12985,11 +10130,6 @@ del@^4.1.1: pify "^4.0.1" rimraf "^2.6.3" -delay@^5.0.0: - version "5.0.0" - resolved "https://registry.yarnpkg.com/delay/-/delay-5.0.0.tgz#137045ef1b96e5071060dd5be60bf9334436bd1d" - integrity sha512-ReEBKkIfe4ya47wlPYf/gu5ib6yUG0/Aez0JQZQz94kiWtRQvZIQbTiehsnwHvLSWJnQdhVeqYue7Id1dKr0qw== - delayed-stream@~1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/delayed-stream/-/delayed-stream-1.0.0.tgz#df3ae199acadfb7d440aaae0b29e2272b24ec619" @@ -13000,7 +10140,7 @@ delegates@^1.0.0: resolved "https://registry.yarnpkg.com/delegates/-/delegates-1.0.0.tgz#84c6e159b81904fdca59a0ef44cd870d31250f9a" integrity sha1-hMbhWbgZBP3KWaDvRM2HDTElD5o= -depd@2.0.0, depd@^2.0.0, depd@~2.0.0: +depd@2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/depd/-/depd-2.0.0.tgz#b696163cc757560d09cf22cc8fad1571b79e76df" integrity sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw== @@ -13023,11 +10163,6 @@ des.js@^1.0.0: inherits "^2.0.1" minimalistic-assert "^1.0.0" -destroy@^1.0.4: - version "1.1.1" - resolved "https://registry.yarnpkg.com/destroy/-/destroy-1.1.1.tgz#38a65ed2f2615ad12bf59c6b5e885512c0cf13dd" - integrity sha512-jxwFW+yrVOLdwqIWvowFOM8UPdhZnvOF6mhXQQLXMxBDLtv2JVJlVJPEwkDv9prqscEtGtmnxuuI6pQKStK1vA== - destroy@~1.0.4: version "1.0.4" resolved "https://registry.yarnpkg.com/destroy/-/destroy-1.0.4.tgz#978857442c44749e4206613e37946205826abd80" @@ -13040,11 +10175,6 @@ detab@2.0.4: dependencies: repeat-string "^1.5.4" -detect-libc@^2.0.0: - version "2.0.1" - resolved "https://registry.yarnpkg.com/detect-libc/-/detect-libc-2.0.1.tgz#e1897aa88fa6ad197862937fbc0441ef352ee0cd" - integrity sha512-463v3ZeIrcWtdgIg6vI6XUncguvr2TnGl4SzDXinkt9mSLpBJKXT3mW6xT3VQdDN11+WVs29pgvivTc4Lp8v+w== - detect-newline@^3.0.0: version "3.1.0" resolved "https://registry.yarnpkg.com/detect-newline/-/detect-newline-3.1.0.tgz#576f5dfc63ae1a192ff192d8ad3af6308991b651" @@ -13181,6 +10311,11 @@ dom-accessibility-api@^0.5.6: resolved "https://registry.yarnpkg.com/dom-accessibility-api/-/dom-accessibility-api-0.5.14.tgz#56082f71b1dc7aac69d83c4285eef39c15d93f56" integrity sha512-NMt+m9zFMPZe0JcY9gN224Qvk6qLIdqex29clBvc/y75ZBX9YA9wNK3frsYvu2DI1xcCIwxwnX+TlsJ2DSOADg== +dom-accessibility-api@^0.5.9: + version "0.5.16" + resolved "https://registry.yarnpkg.com/dom-accessibility-api/-/dom-accessibility-api-0.5.16.tgz#5a7429e6066eb3664d911e33fb0e45de8eb08453" + integrity sha512-X7BJ2yElsnOJ30pZF4uIIDfBEVgF4XEBxL9Bxhy6dnrm5hkzqmsWHGTiHqRiITNhMyFLyAiWndIJP7Z1NTteDg== + dom-converter@^0.2.0: version "0.2.0" resolved "https://registry.yarnpkg.com/dom-converter/-/dom-converter-0.2.0.tgz#6721a9daee2e293682955b6afe416771627bb768" @@ -13291,11 +10426,6 @@ dotenv-expand@5.1.0, dotenv-expand@^5.1.0: resolved "https://registry.yarnpkg.com/dotenv-expand/-/dotenv-expand-5.1.0.tgz#3fbaf020bfd794884072ea26b1e9791d45a629f0" integrity sha512-YXQl1DSa4/PQyRfgrv6aoNjhasp/p4qs9FjJ4q4cQk+8m4r6k4ZSiEyytKG8f8W9gi8WsQtIObNmKd+tMzNTmA== -dotenv@10.0.0: - version "10.0.0" - resolved "https://registry.yarnpkg.com/dotenv/-/dotenv-10.0.0.tgz#3d4227b8fb95f81096cdd2b66653fb2c7085ba81" - integrity sha512-rlBi9d8jpv9Sf1klPjNfFAuWDjKLwTIJJ/VxtoTwIR6hnZxcEOQCZg2oIL3MWBYw5GpUDKOEnND7LXTbIpQ03Q== - dotenv@8.2.0: version "8.2.0" resolved "https://registry.yarnpkg.com/dotenv/-/dotenv-8.2.0.tgz#97e619259ada750eea3e4ea3e26bceea5424b16a" @@ -13368,7 +10498,7 @@ electron-to-chromium@^1.4.118: resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.4.138.tgz#3ec41ca589aaf505dfe2034fde913329af801730" integrity sha512-IOyp2Seq3w4QLln+yZWcMF3VXhhduz4bwg9gfI+CnP5TkzwNXQ8FCZuwwPsnes73AfWdf5J2n2OXdUwDUspDPQ== -elliptic@6.5.4, elliptic@^6.4.0, elliptic@^6.5.3, elliptic@^6.5.4: +elliptic@6.5.4, elliptic@^6.4.0, elliptic@^6.5.3: version "6.5.4" resolved "https://registry.yarnpkg.com/elliptic/-/elliptic-6.5.4.tgz#da37cebd31e79a1367e941b592ed1fbebd58abbb" integrity sha512-iLhC6ULemrljPZb+QutR5TQGB+pdW6KGD5RSegS+8sorOZT+rdQFbsQFJgvN3eRqNALqJer4oQ16YvJHlU8hzQ== @@ -13420,7 +10550,7 @@ emotion-theming@^10.0.27: "@emotion/weak-memoize" "0.2.5" hoist-non-react-statics "^3.3.0" -encodeurl@^1.0.2, encodeurl@~1.0.2: +encodeurl@~1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/encodeurl/-/encodeurl-1.0.2.tgz#ad3ff4c86ec2d029322f5a02c3a9a606c95b3f59" integrity sha1-rT/0yG7C0CkyL1oCw6mmBslbP1k= @@ -13582,18 +10712,6 @@ es6-iterator@2.0.3, es6-iterator@^2.0.3: es5-ext "^0.10.35" es6-symbol "^3.1.1" -es6-promise@^4.0.3: - version "4.2.8" - resolved "https://registry.yarnpkg.com/es6-promise/-/es6-promise-4.2.8.tgz#4eb21594c972bc40553d276e510539143db53e0a" - integrity sha512-HJDGx5daxeIvxdBxvG2cb9g4tEvwIk3i8+nhX0yGrYmZUzbkdg8QbDevheDB8gd0//uPj4c1EQua8Q+MViT0/w== - -es6-promisify@^5.0.0: - version "5.0.0" - resolved "https://registry.yarnpkg.com/es6-promisify/-/es6-promisify-5.0.0.tgz#5109d62f3e56ea967c4b63505aef08291c8a5203" - integrity sha512-C+d6UdsYDk0lMebHNR4S2NybQMMngAOnOwYBQjTOiv0MkoJMP0Myw2mgpDLBcpfCmRLxyFqYhS/CfOENq4SJhQ== - dependencies: - es6-promise "^4.0.3" - es6-shim@^0.35.5: version "0.35.6" resolved "https://registry.yarnpkg.com/es6-shim/-/es6-shim-0.35.6.tgz#d10578301a83af2de58b9eadb7c2c9945f7388a0" @@ -13627,7 +10745,7 @@ escape-goat@^4.0.0: resolved "https://registry.yarnpkg.com/escape-goat/-/escape-goat-4.0.0.tgz#9424820331b510b0666b98f7873fe11ac4aa8081" integrity sha512-2Sd4ShcWxbx6OY1IHyla/CVNwvg7XwZVoXZHcSu9w9SReNP1EzzD5T8NWKIR38fIqEns9kDWKUQTXXAmlDrdPg== -escape-html@^1.0.3, escape-html@~1.0.3: +escape-html@~1.0.3: version "1.0.3" resolved "https://registry.yarnpkg.com/escape-html/-/escape-html-1.0.3.tgz#0258eae4d3d0c0974de1c169188ef0051d1d1988" integrity sha1-Aljq5NPQwJdN4cFpGI7wBR0dGYg= @@ -13963,7 +11081,7 @@ etag@~1.8.1: resolved "https://registry.yarnpkg.com/etag/-/etag-1.8.1.tgz#41ae2eeb65efa62268aebfea83ac7d79299b0887" integrity sha1-Qa4u62XvpiJorr/qg6x9eSmbCIc= -ethers@^5.6.2: +ethers@^5.6.2, ethers@~5.7.0: version "5.7.2" resolved "https://registry.yarnpkg.com/ethers/-/ethers-5.7.2.tgz#3a7deeabbb8c030d4126b24f84e525466145872e" integrity sha512-wswUsmWo1aOK8rR7DIKiWSw9DbLWe6x98Jrn8wcTflTVvaXhAMaB5zGAXy0GYQEQp9iO1iSHWVyARQm11zUtyg== @@ -13999,42 +11117,6 @@ ethers@^5.6.2: "@ethersproject/web" "5.7.1" "@ethersproject/wordlists" "5.7.0" -ethers@~5.5.0: - version "5.5.4" - resolved "https://registry.yarnpkg.com/ethers/-/ethers-5.5.4.tgz#e1155b73376a2f5da448e4a33351b57a885f4352" - integrity sha512-N9IAXsF8iKhgHIC6pquzRgPBJEzc9auw3JoRkaKe+y4Wl/LFBtDDunNe7YmdomontECAcC5APaAgWZBiu1kirw== - dependencies: - "@ethersproject/abi" "5.5.0" - "@ethersproject/abstract-provider" "5.5.1" - "@ethersproject/abstract-signer" "5.5.0" - "@ethersproject/address" "5.5.0" - "@ethersproject/base64" "5.5.0" - "@ethersproject/basex" "5.5.0" - "@ethersproject/bignumber" "5.5.0" - "@ethersproject/bytes" "5.5.0" - "@ethersproject/constants" "5.5.0" - "@ethersproject/contracts" "5.5.0" - "@ethersproject/hash" "5.5.0" - "@ethersproject/hdnode" "5.5.0" - "@ethersproject/json-wallets" "5.5.0" - "@ethersproject/keccak256" "5.5.0" - "@ethersproject/logger" "5.5.0" - "@ethersproject/networks" "5.5.2" - "@ethersproject/pbkdf2" "5.5.0" - "@ethersproject/properties" "5.5.0" - "@ethersproject/providers" "5.5.3" - "@ethersproject/random" "5.5.1" - "@ethersproject/rlp" "5.5.0" - "@ethersproject/sha2" "5.5.0" - "@ethersproject/signing-key" "5.5.0" - "@ethersproject/solidity" "5.5.0" - "@ethersproject/strings" "5.5.0" - "@ethersproject/transactions" "5.5.0" - "@ethersproject/units" "5.5.0" - "@ethersproject/wallet" "5.5.0" - "@ethersproject/web" "5.5.1" - "@ethersproject/wordlists" "5.5.0" - event-emitter@^0.3.5: version "0.3.5" resolved "https://registry.yarnpkg.com/event-emitter/-/event-emitter-0.3.5.tgz#df8c69eef1647923c7157b9ce83840610b02cc39" @@ -14048,6 +11130,11 @@ eventemitter3@^4.0.0, eventemitter3@^4.0.7: resolved "https://registry.yarnpkg.com/eventemitter3/-/eventemitter3-4.0.7.tgz#2de9b68f6528d5644ef5c59526a1b4a07306169f" integrity sha512-8guHBZCwKnFhYdHr2ysuRWErTwhoN2X8XELRlrRwpmfeY2jjuUN4taQMsULKUVo1K4DvZl+0pgfyoysHxvmvEw== +eventemitter3@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/eventemitter3/-/eventemitter3-5.0.0.tgz#084eb7f5b5388df1451e63f4c2aafd71b217ccb3" + integrity sha512-riuVbElZZNXLeLEoprfNYoDSwTBRR44X3mnhdI1YcnENpWTCsTTVZ2zFuqQcpoyqPQIUXdiPEU0ECAq0KQRaHg== + events@^3.0.0, events@^3.2.0: version "3.3.0" resolved "https://registry.yarnpkg.com/events/-/events-3.3.0.tgz#31a95ad0a924e2d2c419a813aeb2c4e878ea7400" @@ -14273,11 +11360,6 @@ extsprintf@^1.2.0: resolved "https://registry.yarnpkg.com/extsprintf/-/extsprintf-1.4.1.tgz#8d172c064867f235c0c84a596806d279bf4bcc07" integrity sha512-Wrk35e8ydCKDj/ArClo1VrPVmN8zph5V4AtHwIuHhvMXsKf73UT3BOD+azBIW+3wOJ4FhEH7zyaJCFvChjYvMA== -eyes@^0.1.8: - version "0.1.8" - resolved "https://registry.yarnpkg.com/eyes/-/eyes-0.1.8.tgz#62cf120234c683785d902348a800ef3e0cc20bc0" - integrity sha512-GipyPsXO1anza0AOZdy69Im7hGFCNB7Y/NGjDlZGJ3GJJLtwNSb2vrzYrTYJRrRloVx7pl+bhUaTB8yiccPvFQ== - fast-deep-equal@^3.1.1, fast-deep-equal@^3.1.3: version "3.1.3" resolved "https://registry.yarnpkg.com/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz#3a7d56b559d6cbc3eb512325244e619a65c6c525" @@ -14342,11 +11424,6 @@ fast-shallow-equal@^1.0.0: resolved "https://registry.yarnpkg.com/fast-shallow-equal/-/fast-shallow-equal-1.0.0.tgz#d4dcaf6472440dcefa6f88b98e3251e27f25628b" integrity sha512-HPtaa38cPgWvaCFmRNhlc6NG7pv6NUHqjPgVAkWGoB9mQMwYB27/K0CvOM5Czy+qpT3e8XJ6Q4aPAnzpNpzNaw== -fast-stable-stringify@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/fast-stable-stringify/-/fast-stable-stringify-1.0.0.tgz#5c5543462b22aeeefd36d05b34e51c78cb86d313" - integrity sha512-wpYMUmFu5f00Sm0cj2pfivpmawLZ0NKdviQ4w9zJeR8JVtOpOxHmLaJuj0vxvGqMJQWyP/COUkF75/57OKyRag== - fastest-stable-stringify@^2.0.2: version "2.0.2" resolved "https://registry.yarnpkg.com/fastest-stable-stringify/-/fastest-stable-stringify-2.0.2.tgz#3757a6774f6ec8de40c4e86ec28ea02417214c76" @@ -14679,6 +11756,19 @@ formdata-polyfill@^4.0.10: dependencies: fetch-blob "^3.1.2" +formik@^2.2.9: + version "2.2.9" + resolved "https://registry.yarnpkg.com/formik/-/formik-2.2.9.tgz#8594ba9c5e2e5cf1f42c5704128e119fc46232d0" + integrity sha512-LQLcISMmf1r5at4/gyJigGn0gOwFbeEAlji+N9InZF6LIMXnFNkO42sCI8Jt84YZggpD4cPWObAZaxpEFtSzNA== + dependencies: + deepmerge "^2.1.1" + hoist-non-react-statics "^3.3.0" + lodash "^4.17.21" + lodash-es "^4.17.21" + react-fast-compare "^2.0.1" + tiny-warning "^1.0.2" + tslib "^1.10.0" + forwarded@0.2.0: version "0.2.0" resolved "https://registry.yarnpkg.com/forwarded/-/forwarded-0.2.0.tgz#2269936428aad4c15c7ebe9779a84bf0b2a81811" @@ -14691,7 +11781,7 @@ fragment-cache@^0.2.1: dependencies: map-cache "^0.2.2" -fresh@0.5.2, fresh@~0.5.2: +fresh@0.5.2: version "0.5.2" resolved "https://registry.yarnpkg.com/fresh/-/fresh-0.5.2.tgz#3d8cadd90d976569fa835ab1f8e4b23a105605a7" integrity sha1-PYyt2Q2XZWn6g1qx+OSyOhBWBac= @@ -15011,7 +12101,7 @@ glob@^6.0.1: once "^1.3.0" path-is-absolute "^1.0.0" -glob@^7.0.0, glob@^7.0.5, glob@^7.1.3: +glob@^7.0.0, glob@^7.1.3: version "7.2.3" resolved "https://registry.yarnpkg.com/glob/-/glob-7.2.3.tgz#b8df0fb802bbfa8e89bd1d938b4e16578ed44f2b" integrity sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q== @@ -15023,7 +12113,7 @@ glob@^7.0.0, glob@^7.0.5, glob@^7.1.3: once "^1.3.0" path-is-absolute "^1.0.0" -glob@^7.0.3, glob@^7.1.1, glob@^7.1.2, glob@^7.1.4, glob@^7.1.6, glob@^7.1.7, glob@^7.2.0: +glob@^7.0.3, glob@^7.1.1, glob@^7.1.2, glob@^7.1.4, glob@^7.1.6, glob@^7.1.7: version "7.2.0" resolved "https://registry.yarnpkg.com/glob/-/glob-7.2.0.tgz#d15535af7732e02e948f4c41628bd910293f6023" integrity sha512-lmLf6gtyrPq8tTjSmrO94wBeQbFR3HbLHbuyD69wuyQkImp2hWqMGB47OX65FBkPffO641IP9jWa1z4ivqG26Q== @@ -15152,11 +12242,6 @@ globby@^9.2.0: pify "^4.0.1" slash "^2.0.0" -google-protobuf@^3.17.3: - version "3.21.2" - resolved "https://registry.yarnpkg.com/google-protobuf/-/google-protobuf-3.21.2.tgz#4580a2bea8bbb291ee579d1fefb14d6fa3070ea4" - integrity sha512-3MSOYFO5U9mPGikIYCzK0SaThypfGgS6bHqrUGXG3DPHCrb+txNqeEcns1W0lkGfk0rCyNXm7xB9rMxnCiZOoA== - got@12.5.1: version "12.5.1" resolved "https://registry.yarnpkg.com/got/-/got-12.5.1.tgz#0796191c61478273f4cdbeb19d358a75a54a008d" @@ -15234,18 +12319,6 @@ growly@^1.3.0: resolved "https://registry.yarnpkg.com/growly/-/growly-1.3.0.tgz#f10748cbe76af964b7c96c93c6bcc28af120c081" integrity sha1-8QdIy+dq+WS3yWyTxrzCivEgwIE= -grpc@^1.24.11: - version "1.24.11" - resolved "https://registry.yarnpkg.com/grpc/-/grpc-1.24.11.tgz#7039da9f6f22ce35168535a6d5dda618398a5966" - integrity sha512-8/AQdFCzCeCDWW3SoaMNp6ccbRvTQEH1O1u1uFtt29eWsg5gSZCJ3m6fbkduEIh3smY7WAPP+LgVJ5n3nZRxcA== - dependencies: - "@mapbox/node-pre-gyp" "^1.0.4" - "@types/bytebuffer" "^5.0.40" - lodash.camelcase "^4.3.0" - lodash.clone "^4.5.0" - nan "^2.13.2" - protobufjs "^5.0.3" - gzip-size@5.1.1: version "5.1.1" resolved "https://registry.yarnpkg.com/gzip-size/-/gzip-size-5.1.1.tgz#cb9bee692f87c0612b232840a873904e4c135274" @@ -15392,7 +12465,7 @@ hash-base@^3.0.0: readable-stream "^3.6.0" safe-buffer "^5.2.0" -hash.js@1.1.7, hash.js@^1.0.0, hash.js@^1.0.3, hash.js@^1.1.7: +hash.js@1.1.7, hash.js@^1.0.0, hash.js@^1.0.3: version "1.1.7" resolved "https://registry.yarnpkg.com/hash.js/-/hash.js-1.1.7.tgz#0babca538e8d4ee4a0f8988d68866537a003cf42" integrity sha512-taOaskGt4z4SOANNseOviYDvjEJinIkRgmp7LbKP2YTTmVxWBl87s/uzK9r+44BclBSp2X7K1hqeNfz9JbBeXA== @@ -15478,11 +12551,6 @@ hex-color-regex@^1.1.0: resolved "https://registry.yarnpkg.com/hex-color-regex/-/hex-color-regex-1.1.0.tgz#4c06fccb4602fe2602b3c93df82d7e7dbf1a8a8e" integrity sha512-l9sfDFsuqtOqKDsQdqrMRk0U85RZc0RtOR9yPI7mRVOa4FsR/BVnZ0shmQRM96Ji99kYZP/7hn1cedc1+ApsTQ== -hi-base32@^0.5.1: - version "0.5.1" - resolved "https://registry.yarnpkg.com/hi-base32/-/hi-base32-0.5.1.tgz#1279f2ddae2673219ea5870c2121d2a33132857e" - integrity sha512-EmBBpvdYh/4XxsnUybsPag6VikPYnN30td+vQk+GI3qpahVEG9+gTkG0aXVxTjBqQ5T6ijbWIu77O+C5WFWsnA== - highlight.js@^10.1.1, highlight.js@^10.4.1, highlight.js@~10.7.0: version "10.7.3" resolved "https://registry.yarnpkg.com/highlight.js/-/highlight.js-10.7.3.tgz#697272e3991356e40c3cac566a74eef681756531" @@ -15671,14 +12739,6 @@ htmlparser2@^6.1.0: domutils "^2.5.2" entities "^2.0.0" -http-assert@^1.3.0: - version "1.5.0" - resolved "https://registry.yarnpkg.com/http-assert/-/http-assert-1.5.0.tgz#c389ccd87ac16ed2dfa6246fd73b926aa00e6b8f" - integrity sha512-uPpH7OKX4H25hBmU6G1jWNaqJGpTXxey+YOUizJUAgu0AjLUeC8D73hTrhvDS5D+GJN1DN1+hhc/eF/wpxtp0w== - dependencies: - deep-equal "~1.0.1" - http-errors "~1.8.0" - http-cache-semantics@^4.1.0: version "4.1.0" resolved "https://registry.yarnpkg.com/http-cache-semantics/-/http-cache-semantics-4.1.0.tgz#49e91c5cbf36c9b94bcfcd71c23d5249ec74e390" @@ -15689,7 +12749,7 @@ http-deceiver@^1.2.7: resolved "https://registry.yarnpkg.com/http-deceiver/-/http-deceiver-1.2.7.tgz#fa7168944ab9a519d337cb0bec7284dc3e723d87" integrity sha1-+nFolEq5pRnTN8sL7HKE3D5yPYc= -http-errors@1.8.1, http-errors@^1.6.3, http-errors@^1.7.3, http-errors@~1.8.0: +http-errors@1.8.1: version "1.8.1" resolved "https://registry.yarnpkg.com/http-errors/-/http-errors-1.8.1.tgz#7c3f28577cbc8a207388455dbd62295ed07bd68c" integrity sha512-Kpk9Sm7NmI+RHhnj6OIWDI1d6fIoFAtFt9RLaTMRlg/8w49juAStsrBgp0Dp4OdxdVbRIeKhtCUvoi/RuAhO4g== @@ -16060,21 +13120,11 @@ invariant@^2.2.1, invariant@^2.2.4: dependencies: loose-envify "^1.0.0" -invert-kv@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/invert-kv/-/invert-kv-1.0.0.tgz#104a8e4aaca6d3d8cd157a8ef8bfab2d7a3ffdb6" - integrity sha512-xgs2NH9AE66ucSq4cNG1nhSFghr5l6tdL15Pk+jl46bmmBapgoaY/AacXyaDznAqmGL99TiLSQgO/XazFSKYeQ== - ip-regex@^2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/ip-regex/-/ip-regex-2.1.0.tgz#fa78bf5d2e6913c911ce9f819ee5146bb6d844e9" integrity sha1-+ni/XS5pE8kRzp+BnuUUa7bYROk= -ip-regex@^4.3.0: - version "4.3.0" - resolved "https://registry.yarnpkg.com/ip-regex/-/ip-regex-4.3.0.tgz#687275ab0f57fa76978ff8f4dddc8a23d5990db5" - integrity sha512-B9ZWJxHHOHUhUjCPrMpLD4xEq35bUTClHM1S6CBU5ixQnkZmwipwgc96vAd7AAGM9TGHvJR+Uss+/Ak6UphK+Q== - ip@^1.1.0, ip@^1.1.5: version "1.1.5" resolved "https://registry.yarnpkg.com/ip/-/ip-1.1.5.tgz#bdded70114290828c0a039e72ef25f5aaec4354a" @@ -16320,13 +13370,6 @@ is-finite@^1.0.0: resolved "https://registry.yarnpkg.com/is-finite/-/is-finite-1.1.0.tgz#904135c77fb42c0641d6aa1bcdbc4daa8da082f3" integrity sha512-cdyMtqX/BOqqNBBiKlIVkytNHm49MtMlYyn1zxzvJKWmFMlGzm+ry5BBfYyeY9YmNKbRSo/o7OX9w9ale0wg3w== -is-fullwidth-code-point@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/is-fullwidth-code-point/-/is-fullwidth-code-point-1.0.0.tgz#ef9e31386f031a7f0d643af82fde50c457ef00cb" - integrity sha512-1pqUqRjkhPJ9miNq9SwMfdvi6lBJcd6eFxvfaivQhaH3SgisfiuudvFntdKOmxuee/77l+FPjKrQjWvmPjWrRw== - dependencies: - number-is-nan "^1.0.0" - is-fullwidth-code-point@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz#a3b30a5c4f199183167aaab93beefae3ddfb654f" @@ -16347,13 +13390,6 @@ is-generator-fn@^2.0.0: resolved "https://registry.yarnpkg.com/is-generator-fn/-/is-generator-fn-2.1.0.tgz#7d140adc389aaf3011a8f2a2a4cfa6faadffb118" integrity sha512-cTIB4yPYL/Grw0EaSzASzg6bBy9gqCofvWN8okThAYIxKJZC+udlRAmGbM0XLeniEJSs8uEgHPGuHSe1XsOLSQ== -is-generator-function@^1.0.7: - version "1.0.10" - resolved "https://registry.yarnpkg.com/is-generator-function/-/is-generator-function-1.0.10.tgz#f1558baf1ac17e0deea7c0415c438351ff2b3c72" - integrity sha512-jsEjy9l3yiXEQ+PsXdmBwEPcOxaXWLspKdplFUVI9vq1iZgIekeC0L167qeu86czQaxed3q/Uzuw0swL0irL8A== - dependencies: - has-tostringtag "^1.0.0" - is-glob@^3.0.0, is-glob@^3.1.0: version "3.1.0" resolved "https://registry.yarnpkg.com/is-glob/-/is-glob-3.1.0.tgz#7ba5ae24217804ac70707b96922567486cc3e84a" @@ -16689,11 +13725,6 @@ isomorphic-unfetch@^3.1.0: node-fetch "^2.6.1" unfetch "^4.2.0" -isomorphic-ws@^4.0.1: - version "4.0.1" - resolved "https://registry.yarnpkg.com/isomorphic-ws/-/isomorphic-ws-4.0.1.tgz#55fd4cd6c5e6491e76dc125938dd863f5cd4f2dc" - integrity sha512-BhBvN2MBpWTaSHdWRb/bwdZJ1WaehQ2L1KngkCkfLUGF0mAWAT1sQUQacEmQ0jXkFw/czDXPNQSL5u2/Krsz1w== - isstream@~0.1.2: version "0.1.2" resolved "https://registry.yarnpkg.com/isstream/-/isstream-0.1.2.tgz#47e63f7af55afa6f92e1500e690eb8b8529c099a" @@ -16764,25 +13795,6 @@ iterate-value@^1.0.2: es-get-iterator "^1.0.2" iterate-iterator "^1.0.1" -jayson@^3.4.4: - version "3.7.0" - resolved "https://registry.yarnpkg.com/jayson/-/jayson-3.7.0.tgz#b735b12d06d348639ae8230d7a1e2916cb078f25" - integrity sha512-tfy39KJMrrXJ+mFcMpxwBvFDetS8LAID93+rycFglIQM4kl3uNR3W4lBLE/FFhsoUCEox5Dt2adVpDm/XtebbQ== - dependencies: - "@types/connect" "^3.4.33" - "@types/node" "^12.12.54" - "@types/ws" "^7.4.4" - JSONStream "^1.3.5" - commander "^2.20.3" - delay "^5.0.0" - es6-promisify "^5.0.0" - eyes "^0.1.8" - isomorphic-ws "^4.0.1" - json-stringify-safe "^5.0.1" - lodash "^4.17.20" - uuid "^8.3.2" - ws "^7.4.5" - jest-changed-files@^26.6.2: version "26.6.2" resolved "https://registry.yarnpkg.com/jest-changed-files/-/jest-changed-files-26.6.2.tgz#f6198479e1cc66f22f9ae1e22acaa0b429c042d0" @@ -17008,6 +14020,11 @@ jest-matcher-utils@^28.1.3: jest-get-type "^28.0.2" pretty-format "^28.1.3" +jest-matchmedia-mock@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/jest-matchmedia-mock/-/jest-matchmedia-mock-1.1.0.tgz#eaae8c5d1dee4e4f7c59f8cb1b38b5d7ea842552" + integrity sha512-REnJRsOSCMpGAlkxmvVTqEBpregyFVi9MPEH3N83W1yLKzDdNehtCkcdDZduXq74PLtfI+11NyM4zKCK5ynV9g== + jest-message-util@^26.6.0, jest-message-util@^26.6.2: version "26.6.2" resolved "https://registry.yarnpkg.com/jest-message-util/-/jest-message-util-26.6.2.tgz#58173744ad6fc0506b5d21150b9be56ef001ca07" @@ -17279,31 +14296,16 @@ jest@26.6.0: import-local "^3.0.2" jest-cli "^26.6.0" -js-base64@^3.6.1: - version "3.7.3" - resolved "https://registry.yarnpkg.com/js-base64/-/js-base64-3.7.3.tgz#2e784bb0851636bf1e99ef12e4f3a8a8c9b7639f" - integrity sha512-PAr6Xg2jvd7MCR6Ld9Jg3BmTcjYsHEBx1VlwEwULb/qowPf5VD9kEMagj23Gm7JRnSvE/Da/57nChZjnvL8v6A== - js-cookie@^2.2.1: version "2.2.1" resolved "https://registry.yarnpkg.com/js-cookie/-/js-cookie-2.2.1.tgz#69e106dc5d5806894562902aa5baec3744e9b2b8" integrity sha512-HvdH2LzI/EAZcUwA8+0nKNtWHqS+ZmijLA30RwZA0bo7ToCckjK5MkGhjED9KoRcXO6BaGI3I9UIzSA1FKFPOQ== -js-sha256@^0.9.0: - version "0.9.0" - resolved "https://registry.yarnpkg.com/js-sha256/-/js-sha256-0.9.0.tgz#0b89ac166583e91ef9123644bd3c5334ce9d0966" - integrity sha512-sga3MHh9sgQN2+pJ9VYZ+1LPwXOxuBJBA5nrR5/ofPfuiJBE2hnjsaN8se8JznOmGLN2p49Pe5U/ttafcs/apA== - -js-sha3@0.8.0, js-sha3@^0.8.0: +js-sha3@0.8.0: version "0.8.0" resolved "https://registry.yarnpkg.com/js-sha3/-/js-sha3-0.8.0.tgz#b9b7a5da73afad7dedd0f8c463954cbde6818840" integrity sha512-gF1cRrHhIzNfToc802P800N8PpXS+evLLXfsVpowqmAFR9uwbi89WvXg2QspOmXL8QL86J4T1EpFu+yUkwJY3Q== -js-sha512@^0.8.0: - version "0.8.0" - resolved "https://registry.yarnpkg.com/js-sha512/-/js-sha512-0.8.0.tgz#dd22db8d02756faccf19f218e3ed61ec8249f7d4" - integrity sha512-PWsmefG6Jkodqt+ePTvBZCSMFgN7Clckjd0O7su3I0+BW2QWUTJNzjktHsztGLhncP2h8mcF9V9Y2Ha59pAViQ== - js-string-escape@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/js-string-escape/-/js-string-escape-1.0.1.tgz#e2625badbc0d67c7533e9edc1068c587ae4137ef" @@ -17327,11 +14329,6 @@ jsbn@~0.1.0: resolved "https://registry.yarnpkg.com/jsbn/-/jsbn-0.1.1.tgz#a5e654c2e5a2deb5f201d96cefbca80c0ef2f513" integrity sha1-peZUwuWi3rXyAdls77yoDA7y9RM= -jscrypto@^1.0.1: - version "1.0.3" - resolved "https://registry.yarnpkg.com/jscrypto/-/jscrypto-1.0.3.tgz#598febca2a939d6f679c54f56e1fe364cef30cc9" - integrity sha512-lryZl0flhodv4SZHOqyb1bx5sKcJxj0VBo0Kzb4QMAg3L021IC9uGpl0RCZa+9KJwlRGSK2C80ITcwbe19OKLQ== - jsdom@^16.4.0: version "16.7.0" resolved "https://registry.yarnpkg.com/jsdom/-/jsdom-16.7.0.tgz#918ae71965424b197c819f8183a754e18977b710" @@ -17382,13 +14379,6 @@ json-bigint@^0.2.0: dependencies: bignumber.js "^4.0.0" -json-bigint@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/json-bigint/-/json-bigint-1.0.0.tgz#ae547823ac0cad8398667f8cd9ef4730f5b01ff1" - integrity sha512-SiPv/8VpZuWbvLSMtTDU8hEfrZWg/mH/nV/b4o0CYbSxu1UIQPLdwKOCIyLQX+VIPO5vrLX3i8qtqFyhdPSUSQ== - dependencies: - bignumber.js "^9.0.0" - json-buffer@3.0.1: version "3.0.1" resolved "https://registry.yarnpkg.com/json-buffer/-/json-buffer-3.0.1.tgz#9338802a30d3b6605fbe0613e094008ca8c05a13" @@ -17494,13 +14484,6 @@ junk@^3.1.0: resolved "https://registry.yarnpkg.com/junk/-/junk-3.1.0.tgz#31499098d902b7e98c5d9b9c80f43457a88abfa1" integrity sha512-pBxcB3LFc8QVgdggvZWyeys+hnrNWg4OcZIU/1X59k5jQdLBlCsYGRQaz234SqoRLTCgMH00fY0xRJH+F9METQ== -keygrip@~1.1.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/keygrip/-/keygrip-1.1.0.tgz#871b1681d5e159c62a445b0c74b615e0917e7226" - integrity sha512-iYSchDJ+liQ8iwbSI2QqsQOvqv58eJCEanyJPJi+Khyu8smkcKSFUCbPwzFcL7YVtZ6eONjqRX/38caJ7QjRAQ== - dependencies: - tsscmp "1.0.6" - keyv@^4.5.0: version "4.5.0" resolved "https://registry.yarnpkg.com/keyv/-/keyv-4.5.0.tgz#dbce9ade79610b6e641a9a65f2f6499ba06b9bc6" @@ -17554,65 +14537,6 @@ klona@^2.0.4: resolved "https://registry.yarnpkg.com/klona/-/klona-2.0.5.tgz#d166574d90076395d9963aa7a928fabb8d76afbc" integrity sha512-pJiBpiXMbt7dkzXe8Ghj/u4FfXOOa98fPW+bihOJ4SjnoijweJrNThJfd3ifXpXhREjpoF2mZVH1GfS9LV3kHQ== -koa-compose@^4.1.0: - version "4.1.0" - resolved "https://registry.yarnpkg.com/koa-compose/-/koa-compose-4.1.0.tgz#507306b9371901db41121c812e923d0d67d3e877" - integrity sha512-8ODW8TrDuMYvXRwra/Kh7/rJo9BtOfPc6qO8eAfC80CnCvSjSl0bkRM24X6/XBBEyj0v1nRUQ1LyOy3dbqOWXw== - -koa-convert@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/koa-convert/-/koa-convert-2.0.0.tgz#86a0c44d81d40551bae22fee6709904573eea4f5" - integrity sha512-asOvN6bFlSnxewce2e/DK3p4tltyfC4VM7ZwuTuepI7dEQVcvpyFuBcEARu1+Hxg8DIwytce2n7jrZtRlPrARA== - dependencies: - co "^4.6.0" - koa-compose "^4.1.0" - -koa-send@^5.0.0: - version "5.0.1" - resolved "https://registry.yarnpkg.com/koa-send/-/koa-send-5.0.1.tgz#39dceebfafb395d0d60beaffba3a70b4f543fe79" - integrity sha512-tmcyQ/wXXuxpDxyNXv5yNNkdAMdFRqwtegBXUaowiQzUKqJehttS0x2j0eOZDQAyloAth5w6wwBImnFzkUz3pQ== - dependencies: - debug "^4.1.1" - http-errors "^1.7.3" - resolve-path "^1.4.0" - -koa-static@^5.0.0: - version "5.0.0" - resolved "https://registry.yarnpkg.com/koa-static/-/koa-static-5.0.0.tgz#5e92fc96b537ad5219f425319c95b64772776943" - integrity sha512-UqyYyH5YEXaJrf9S8E23GoJFQZXkBVJ9zYYMPGz919MSX1KuvAcycIuS0ci150HCoPf4XQVhQ84Qf8xRPWxFaQ== - dependencies: - debug "^3.1.0" - koa-send "^5.0.0" - -koa@^2.12.0: - version "2.13.4" - resolved "https://registry.yarnpkg.com/koa/-/koa-2.13.4.tgz#ee5b0cb39e0b8069c38d115139c774833d32462e" - integrity sha512-43zkIKubNbnrULWlHdN5h1g3SEKXOEzoAlRsHOTFpnlDu8JlAOZSMJBLULusuXRequboiwJcj5vtYXKB3k7+2g== - dependencies: - accepts "^1.3.5" - cache-content-type "^1.0.0" - content-disposition "~0.5.2" - content-type "^1.0.4" - cookies "~0.8.0" - debug "^4.3.2" - delegates "^1.0.0" - depd "^2.0.0" - destroy "^1.0.4" - encodeurl "^1.0.2" - escape-html "^1.0.3" - fresh "~0.5.2" - http-assert "^1.3.0" - http-errors "^1.6.3" - is-generator-function "^1.0.7" - koa-compose "^4.1.0" - koa-convert "^2.0.0" - on-finished "^2.3.0" - only "~0.0.2" - parseurl "^1.3.2" - statuses "^1.5.0" - type-is "^1.6.16" - vary "^1.1.2" - language-subtag-registry@~0.3.2: version "0.3.21" resolved "https://registry.yarnpkg.com/language-subtag-registry/-/language-subtag-registry-0.3.21.tgz#04ac218bea46f04cb039084602c6da9e788dd45a" @@ -17651,13 +14575,6 @@ lazy-universal-dotenv@^3.0.1: dotenv "^8.0.0" dotenv-expand "^5.1.0" -lcid@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/lcid/-/lcid-1.0.0.tgz#308accafa0bc483a3867b4b6f2b9506251d1b835" - integrity sha512-YiGkH6EnGrDGqLMITnGjXtGmNtjoXw9SVUzcaos8RBi7Ps0VBylkq+vOcY9QE5poLasPCR849ucFUkl0UzUyOw== - dependencies: - invert-kv "^1.0.0" - leven@^3.1.0: version "3.1.0" resolved "https://registry.yarnpkg.com/leven/-/leven-3.1.0.tgz#77891de834064cccba82ae7842bb6b14a13ed7f2" @@ -17811,6 +14728,11 @@ locate-path@^6.0.0: dependencies: p-locate "^5.0.0" +lodash-es@^4.17.21: + version "4.17.21" + resolved "https://registry.yarnpkg.com/lodash-es/-/lodash-es-4.17.21.tgz#43e626c46e6591b7750beb2b50117390c609e3ee" + integrity sha512-mKnC+QJ9pWVzv+C4/U3rRsHapFfHvQFoFB92e52xeyGMcX6/OlIl78je1u8vePzYZSkkogMPJ2yjxxsb89cxyw== + lodash._reinterpolate@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/lodash._reinterpolate/-/lodash._reinterpolate-3.0.0.tgz#0ccf2d89166af03b3663c796538b75ac6e114d9d" @@ -17821,11 +14743,6 @@ lodash.camelcase@^4.3.0: resolved "https://registry.yarnpkg.com/lodash.camelcase/-/lodash.camelcase-4.3.0.tgz#b28aa6288a2b9fc651035c7711f65ab6190331a6" integrity sha512-TwuEnCnxbc3rAvhf/LbG7tJUDzhqXyFnv3dtzLOPgCG/hODL7WFnsbwktkD7yUV0RrreP/l1PALq/YSg6VvjlA== -lodash.clone@^4.5.0: - version "4.5.0" - resolved "https://registry.yarnpkg.com/lodash.clone/-/lodash.clone-4.5.0.tgz#195870450f5a13192478df4bc3d23d2dea1907b6" - integrity sha512-GhrVeweiTD6uTmmn5hV/lzgCQhccwReIVRLHp7LT4SopOjqEZ5BbX8b5WWEtAKasjmy8hR7ZPwsYlxRCku5odg== - lodash.debounce@^4.0.8: version "4.0.8" resolved "https://registry.yarnpkg.com/lodash.debounce/-/lodash.debounce-4.0.8.tgz#82d79bff30a67c4005ffd5e2515300ad9ca4d7af" @@ -17912,21 +14829,6 @@ loglevel@^1.6.8: resolved "https://registry.yarnpkg.com/loglevel/-/loglevel-1.8.0.tgz#e7ec73a57e1e7b419cb6c6ac06bf050b67356114" integrity sha512-G6A/nJLRgWOuuwdNuA6koovfEV1YpqqAG4pRUlFaz3jj2QNZ8M4vBqnVA+HBTmU/AMNUtlOsMmSpF6NyOjztbA== -long@^4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/long/-/long-4.0.0.tgz#9a7b71cfb7d361a194ea555241c92f7468d5bf28" - integrity sha512-XsP+KhQif4bjX1kbuSiySJFNAehNxgLb6hPRGJ9QsUr8ajHkuXGdrHmFUTUUXhDwVX2R5bY4JNZEwbUiMhV+MA== - -long@^5.0.0: - version "5.2.1" - resolved "https://registry.yarnpkg.com/long/-/long-5.2.1.tgz#e27595d0083d103d2fa2c20c7699f8e0c92b897f" - integrity sha512-GKSNGeNAtw8IryjjkhZxuKB3JzlcLTwjtiQCHKvqQet81I93kXslhDQruGI/QsddO83mcDToBVy7GqGS/zYf/A== - -long@~3: - version "3.2.0" - resolved "https://registry.yarnpkg.com/long/-/long-3.2.0.tgz#d821b7138ca1cb581c172990ef14db200b5c474b" - integrity sha512-ZYvPPOMqUwPoDsbJaR10iQJYnMuZhRTvHYl62ErLIEX7RgFlziSBUUvrt3OVfc47QlHHpzPZYP17g3Fv7oeJkg== - loose-envify@^1.0.0, loose-envify@^1.1.0, loose-envify@^1.2.0, loose-envify@^1.3.1, loose-envify@^1.4.0: version "1.4.0" resolved "https://registry.yarnpkg.com/loose-envify/-/loose-envify-1.4.0.tgz#71ee51fa7be4caec1a63839f7e682d8132d30caf" @@ -17976,6 +14878,11 @@ lru-cache@^6.0.0: dependencies: yallist "^4.0.0" +lru-cache@^7.14.1: + version "7.14.1" + resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-7.14.1.tgz#8da8d2f5f59827edb388e63e459ac23d6d408fea" + integrity sha512-ysxwsnTKdAx96aTRdhDOCQfDgbHnt8SK0KY8SEjO0wHinhWOFTESbjVCMPbU1uGXg/ch4lifqx0wfjOawU2+WA== + lru-cache@~7.8.2: version "7.8.2" resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-7.8.2.tgz#db4d3bbcc05b2e7a2ae063f57fdb42d8d45f1773" @@ -17989,9 +14896,9 @@ lru-queue@^0.1.0: es5-ext "~0.10.2" lz-string@^1.4.4: - version "1.4.4" - resolved "https://registry.yarnpkg.com/lz-string/-/lz-string-1.4.4.tgz#c0d8eaf36059f705796e1e344811cf4c498d3a26" - integrity sha512-0ckx7ZHRPqb0oUm8zNr+90mtf9DQB60H1wMCjBtfi62Kl3a7JbHob6gA2bC+xRvZoOL+1hzUK8jeuEIQE8svEQ== + version "1.5.0" + resolved "https://registry.yarnpkg.com/lz-string/-/lz-string-1.5.0.tgz#c1ab50f77887b712621201ba9fd4e3a6ed099941" + integrity sha512-h5bgJWpxJNswbU7qCrV0tIKQCaS3blPDrqKWx+QxzuzL1zGUzij9XCWLrSLsJPu5t+eWA/ycetzYAO5IOMcWAQ== macos-release@^3.0.1: version "3.1.0" @@ -18013,7 +14920,7 @@ make-dir@^2.0.0, make-dir@^2.1.0: pify "^4.0.1" semver "^5.6.0" -make-dir@^3.0.0, make-dir@^3.0.2, make-dir@^3.1.0: +make-dir@^3.0.0, make-dir@^3.0.2: version "3.1.0" resolved "https://registry.yarnpkg.com/make-dir/-/make-dir-3.1.0.tgz#415e967046b3a7f1d185277d84aa58203726a13f" integrity sha512-g3FeP20LNwhALb/6Cz6Dd4F2ngze0jz7tbzrD2wAV+o9FeNHe4rL+yK2md0J/fiSf1sa1ADhXqi5+oVwOM/eGw== @@ -18236,16 +15143,11 @@ merkle-lib@^2.0.10: resolved "https://registry.yarnpkg.com/merkle-lib/-/merkle-lib-2.0.10.tgz#82b8dbae75e27a7785388b73f9d7725d0f6f3326" integrity sha1-grjbrnXieneFOItz+ddyXQ9vMyY= -methods@^1.1.2, methods@~1.1.2: +methods@~1.1.2: version "1.1.2" resolved "https://registry.yarnpkg.com/methods/-/methods-1.1.2.tgz#5529a4d67654134edcc5266656835b0f851afcee" integrity sha1-VSmk1nZUE07cxSZmVoNbD4Ua/O4= -micro-base@^0.9.0: - version "0.9.0" - resolved "https://registry.yarnpkg.com/micro-base/-/micro-base-0.9.0.tgz#09cfe20285bec0ea97f41dc3d10e3fba3d0266ee" - integrity sha512-4+tOMKidYT5nQ6/UNmYrGVO5PMcnJdfuR4NC8HK8s2H61B4itOhA9yrsjBdqGV7ecdtej36x3YSIfPLRmPrspg== - microevent.ts@~0.1.1: version "0.1.1" resolved "https://registry.yarnpkg.com/microevent.ts/-/microevent.ts-0.1.1.tgz#70b09b83f43df5172d0205a63025bce0f7357fa0" @@ -18296,7 +15198,7 @@ mime-db@1.52.0, "mime-db@>= 1.43.0 < 2": resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.52.0.tgz#bbabcdc02859f4987301c856e3387ce5ec43bf70" integrity sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg== -mime-types@2.1.35, mime-types@^2.1.12, mime-types@^2.1.18, mime-types@^2.1.27, mime-types@~2.1.17, mime-types@~2.1.19, mime-types@~2.1.24, mime-types@~2.1.34: +mime-types@2.1.35, mime-types@^2.1.12, mime-types@^2.1.27, mime-types@~2.1.17, mime-types@~2.1.19, mime-types@~2.1.24, mime-types@~2.1.34: version "2.1.35" resolved "https://registry.yarnpkg.com/mime-types/-/mime-types-2.1.35.tgz#381a871b62a734450660ae3deee44813f70d959a" integrity sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw== @@ -18493,10 +15395,10 @@ mobx@^5.15.7: resolved "https://registry.yarnpkg.com/mobx/-/mobx-5.15.7.tgz#b9a5f2b6251f5d96980d13c78e9b5d8d4ce22665" integrity sha512-wyM3FghTkhmC+hQjyPGGFdpehrcX1KOXsDuERhfK2YbJemkUhEB+6wzEN639T21onxlfYBmriA1PFnvxTUhcKw== -mock-socket@^9.1.2, mock-socket@^9.1.4, mock-socket@^9.1.5: - version "9.1.5" - resolved "https://registry.yarnpkg.com/mock-socket/-/mock-socket-9.1.5.tgz#2c4e44922ad556843b6dfe09d14ed8041fa2cdeb" - integrity sha512-3DeNIcsQixWHHKk6NdoBhWI4t1VMj5/HzfnI1rE/pLl5qKx7+gd4DNA07ehTaZ6MoUU053si6Hd+YtiM/tQZfg== +mock-socket@^9.2.1: + version "9.2.1" + resolved "https://registry.yarnpkg.com/mock-socket/-/mock-socket-9.2.1.tgz#cc9c0810aa4d0afe02d721dcb2b7e657c00e2282" + integrity sha512-aw9F9T9G2zpGipLLhSNh6ZpgUyUl4frcVmRN08uE1NWPWg43Wx6+sGPDbQ7E5iFZZDJW5b5bypMeAEHqTbIFag== modern-normalize@^1.1.0: version "1.1.0" @@ -18606,6 +15508,11 @@ nano-time@1.0.0: dependencies: big-integer "^1.6.16" +nanoclone@^0.2.1: + version "0.2.1" + resolved "https://registry.yarnpkg.com/nanoclone/-/nanoclone-0.2.1.tgz#dd4090f8f1a110d26bb32c49ed2f5b9235209ed4" + integrity sha512-wynEP02LmIbLpcYw8uBKpcfF6dmg2vcpKqxeH5UcoKEYdExslsdUA4ugFauuaeYdTB76ez6gJW8XAZ6CgkXYxA== + nanoid@^3.3.1: version "3.3.1" resolved "https://registry.yarnpkg.com/nanoid/-/nanoid-3.3.1.tgz#6347a18cac88af88f58af0b3594b723d5e99bb35" @@ -18690,31 +15597,16 @@ no-case@^3.0.4: lower-case "^2.0.2" tslib "^2.0.3" -nock@^13.2.4, nock@^13.2.9: - version "13.2.9" - resolved "https://registry.yarnpkg.com/nock/-/nock-13.2.9.tgz#4faf6c28175d36044da4cfa68e33e5a15086ad4c" - integrity sha512-1+XfJNYF1cjGB+TKMWi29eZ0b82QOvQs2YoLNzbpWGqFMtRQHTa57osqdGj4FrFPgkO4D4AZinzUJR9VvW3QUA== - dependencies: - debug "^4.1.0" - json-stringify-safe "^5.0.1" - lodash "^4.17.21" - propagate "^2.0.0" - -nock@^13.2.6: - version "13.2.6" - resolved "https://registry.yarnpkg.com/nock/-/nock-13.2.6.tgz#35e419cd9d385ffa67e59523d9699e41b29e1a03" - integrity sha512-GbyeSwSEP0FYouzETZ0l/XNm5tNcDNcXJKw3LCAb+mx8bZSwg1wEEvdL0FAyg5TkBJYiWSCtw6ag4XfmBy60FA== +nock@^13.3.0: + version "13.3.0" + resolved "https://registry.yarnpkg.com/nock/-/nock-13.3.0.tgz#b13069c1a03f1ad63120f994b04bfd2556925768" + integrity sha512-HHqYQ6mBeiMc+N038w8LkMpDCRquCHWeNmN3v6645P3NhN2+qXOBqvPqo7Rt1VyCMzKhJ733wZqw5B7cQVFNPg== dependencies: debug "^4.1.0" json-stringify-safe "^5.0.1" lodash "^4.17.21" propagate "^2.0.0" -node-addon-api@^2.0.0: - version "2.0.2" - resolved "https://registry.yarnpkg.com/node-addon-api/-/node-addon-api-2.0.2.tgz#432cfa82962ce494b132e9d72a15b29f71ff5d32" - integrity sha512-Ntyt4AIXyaLIuMHF6IOoTakB3K+RWxwtsHNRxllEoA6vPwP9o4866g6YWDLUdnucilZhmkxiHwHr11gAENw+QA== - node-dir@^0.1.10: version "0.1.17" resolved "https://registry.yarnpkg.com/node-dir/-/node-dir-0.1.17.tgz#5f5665d93351335caabef8f1c554516cf5f1e4e5" @@ -18734,7 +15626,7 @@ node-emoji@^1.11.0: dependencies: lodash "^4.17.21" -node-fetch@2, node-fetch@2.6.7, node-fetch@^2.6.1, node-fetch@^2.6.7: +node-fetch@2.6.7, node-fetch@^2.6.1, node-fetch@^2.6.7: version "2.6.7" resolved "https://registry.yarnpkg.com/node-fetch/-/node-fetch-2.6.7.tgz#24de9fba827e3b4ae44dc8b20256a379160052ad" integrity sha512-ZjMPFEfVx5j+y2yF35Kzx5sF7kDzxuDj6ziH4FFbOp87zKDZNx8yExJIb05OGF4Nlt9IHFIMBkRl41VdvcNdbQ== @@ -18751,9 +15643,9 @@ node-fetch@3.2.10: formdata-polyfill "^4.0.10" node-fetch@^3.3.0: - version "3.3.0" - resolved "https://registry.yarnpkg.com/node-fetch/-/node-fetch-3.3.0.tgz#37e71db4ecc257057af828d523a7243d651d91e4" - integrity sha512-BKwRP/O0UvoMKp7GNdwPlObhYGB5DQqwhEDQlNKuoqwVYSxkSZCSbHjnFFmUEtwSKRPU4kNK8PbDYYitwaE3QA== + version "3.3.1" + resolved "https://registry.yarnpkg.com/node-fetch/-/node-fetch-3.3.1.tgz#b3eea7b54b3a48020e46f4f88b9c5a7430d20b2e" + integrity sha512-cRVc/kyto/7E5shrWca1Wsea4y6tL9iYJE5FBCius3JQfb/4P4I295PfhgbJQBLTx6lATE4z+wK0rPM4VS2uow== dependencies: data-uri-to-buffer "^4.0.0" fetch-blob "^3.1.4" @@ -18764,15 +15656,10 @@ node-forge@^0.10.0: resolved "https://registry.yarnpkg.com/node-forge/-/node-forge-0.10.0.tgz#32dea2afb3e9926f02ee5ce8794902691a676bf3" integrity sha512-PPmu8eEeG9saEUvI97fm4OYxXVB6bFvyNTyiUOBichBpFG8A1Ljw3bY62+5oOjDEMHRnd0Y7HQ+x7uzxOzC6JA== -node-gyp-build@^4.2.0: - version "4.5.0" - resolved "https://registry.yarnpkg.com/node-gyp-build/-/node-gyp-build-4.5.0.tgz#7a64eefa0b21112f89f58379da128ac177f20e40" - integrity sha512-2iGbaQBV+ITgCz76ZEjmhUKAKVf7xfY1sRl4UiKQspfZMH2h06SyhNsnSVy50cwkFQDGLyif6m/6uFXHkOZ6rg== - node-gyp-build@^4.3.0: - version "4.3.0" - resolved "https://registry.yarnpkg.com/node-gyp-build/-/node-gyp-build-4.3.0.tgz#9f256b03e5826150be39c764bf51e993946d71a3" - integrity sha512-iWjXZvmboq0ja1pUGULQBexmxq8CV4xBhX7VDOTbL7ZR4FOowwY/VOtRxBN/yKxmdGoIp4j5ysNT4u3S2pDQ3Q== + version "4.6.0" + resolved "https://registry.yarnpkg.com/node-gyp-build/-/node-gyp-build-4.6.0.tgz#0c52e4cbf54bbd28b709820ef7b6a3c2d6209055" + integrity sha512-NTZVKn9IylLwUzaKjkas1e4u2DLNcV4rdYagA4PWdPwW87Bi7z+BznyKSRwS/761tV/lzCGXplWsiaMjLqP2zQ== node-int64@^0.4.0: version "0.4.0" @@ -18835,13 +15722,6 @@ node-releases@^2.0.3: resolved "https://registry.yarnpkg.com/node-releases/-/node-releases-2.0.5.tgz#280ed5bc3eba0d96ce44897d8aee478bfb3d9666" integrity sha512-U9h1NLROZTq9uE1SNffn6WuPDg8icmi3ns4rEl/oTfIle4iLjTliCzgTsbaIFMq/Xn078/lfY/BL0GWZ+psK4Q== -nopt@^5.0.0: - version "5.0.0" - resolved "https://registry.yarnpkg.com/nopt/-/nopt-5.0.0.tgz#530942bb58a512fccafe53fe210f13a25355dc88" - integrity sha512-Tbj67rffqceeLpcRXrT7vKAN8CwfPeIBgM7E6iBkmKLV7bEMwpGgYLGv0jACUsECaa/vuxP0IjEont6umdMgtQ== - dependencies: - abbrev "1" - normalize-package-data@^2.3.2, normalize-package-data@^2.3.4, normalize-package-data@^2.5.0: version "2.5.0" resolved "https://registry.yarnpkg.com/normalize-package-data/-/normalize-package-data-2.5.0.tgz#e66db1838b200c1dfc233225d12cb36520e234a8" @@ -18949,11 +15829,6 @@ num2fraction@^1.2.2: resolved "https://registry.yarnpkg.com/num2fraction/-/num2fraction-1.2.2.tgz#6f682b6a027a4e9ddfa4564cd2589d1d4e669ede" integrity sha1-b2gragJ6Tp3fpFZM0lidHU5mnt4= -number-is-nan@^1.0.0: - version "1.0.1" - resolved "https://registry.yarnpkg.com/number-is-nan/-/number-is-nan-1.0.1.tgz#097b602b53422a522c1afb8790318336941a011d" - integrity sha512-4jbtZXNAsfZbAHiiqjLPBiCl16dES1zI4Hpzzxw61Tk+loF+sBDBKx1ICKKKwIqQ7M0mFn1TmkN7euSncWgHiQ== - nwsapi@^2.2.0: version "2.2.0" resolved "https://registry.yarnpkg.com/nwsapi/-/nwsapi-2.2.0.tgz#204879a9e3d068ff2a55139c2c772780681a38b7" @@ -19084,13 +15959,6 @@ obuf@^1.0.0, obuf@^1.1.2: resolved "https://registry.yarnpkg.com/obuf/-/obuf-1.1.2.tgz#09bea3343d41859ebd446292d11c9d4db619084e" integrity sha512-PX1wu0AmAdPqOL1mWhqmlOd8kOIZQwGZw6rh7uby9fTc5lhaOWFLX3I6R1hrF9k3zUY40e6igsLGkDXK92LJNg== -on-finished@^2.3.0: - version "2.4.1" - resolved "https://registry.yarnpkg.com/on-finished/-/on-finished-2.4.1.tgz#58c8c44116e54845ad57f14ab10b03533184ac3f" - integrity sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg== - dependencies: - ee-first "1.1.1" - on-finished@~2.3.0: version "2.3.0" resolved "https://registry.yarnpkg.com/on-finished/-/on-finished-2.3.0.tgz#20f1336481b083cd75337992a16971aa2d906947" @@ -19124,11 +15992,6 @@ onetime@^6.0.0: dependencies: mimic-fn "^4.0.0" -only@~0.0.2: - version "0.0.2" - resolved "https://registry.yarnpkg.com/only/-/only-0.0.2.tgz#2afde84d03e50b9a8edc444e30610a70295edfb4" - integrity sha1-Kv3oTQPlC5qO3EROMGEKcCle37Q= - open@8.4.0, open@^8.4.0: version "8.4.0" resolved "https://registry.yarnpkg.com/open/-/open-8.4.0.tgz#345321ae18f8138f82565a910fdc6b39e8c244f8" @@ -19138,7 +16001,7 @@ open@8.4.0, open@^8.4.0: is-docker "^2.1.1" is-wsl "^2.2.0" -open@^7.0.2, open@^7.0.3, open@^7.0.4: +open@^7.0.2, open@^7.0.3: version "7.4.2" resolved "https://registry.yarnpkg.com/open/-/open-7.4.2.tgz#b8147e26dcf3e426316c730089fd71edd29c2321" integrity sha512-MVHddDVweXZF3awtlAS+6pgKLlm/JgxZ90+/NBurBoQctVOOB/zDdVjcyPzQ+0laDGbsWgrRkflI65sQeOgT9Q== @@ -19190,11 +16053,6 @@ optionator@^0.9.1: type-check "^0.4.0" word-wrap "^1.2.3" -optjs@~3.2.2: - version "3.2.2" - resolved "https://registry.yarnpkg.com/optjs/-/optjs-3.2.2.tgz#69a6ce89c442a44403141ad2f9b370bd5bb6f4ee" - integrity sha512-f8lTJm4LKirX+45xsFhuRNjA4f46QVLQKfGoNH7e2AEWS+24eM4XNH4pQ8Tw2LISCIvbST/wNcLdtgvgcqVaxA== - ora@6.1.2, ora@^6.1.2: version "6.1.2" resolved "https://registry.yarnpkg.com/ora/-/ora-6.1.2.tgz#7b3c1356b42fd90fb1dad043d5dbe649388a0bf5" @@ -19227,13 +16085,6 @@ os-homedir@^1.0.0: resolved "https://registry.yarnpkg.com/os-homedir/-/os-homedir-1.0.2.tgz#ffbc4988336e0e833de0c168c7ef152121aa7fb3" integrity sha1-/7xJiDNuDoM94MFox+8VISGqf7M= -os-locale@^1.4.0: - version "1.4.0" - resolved "https://registry.yarnpkg.com/os-locale/-/os-locale-1.4.0.tgz#20f9f17ae29ed345e8bde583b13d2009803c14d9" - integrity sha512-PRT7ZORmwu2MEFt4/fv3Q+mEfN4zetKxufQrkShY2oGvUms9r8otu5HfdyIFHkYXjO7laNsoVGmM2MANfuTA8g== - dependencies: - lcid "^1.0.0" - os-name@5.0.1: version "5.0.1" resolved "https://registry.yarnpkg.com/os-name/-/os-name-5.0.1.tgz#acb4f996ec5bd86c41755fef9d6d31905c47172e" @@ -19515,7 +16366,7 @@ parse5@6.0.1, parse5@^6.0.0: resolved "https://registry.yarnpkg.com/parse5/-/parse5-6.0.1.tgz#e1a1c085c569b3dc08321184f19a39cc27f7c30b" integrity sha512-Ofn/CTFzRGTTxwpNEs9PP93gXShHcTq255nzRYSKe8AkVpZY7e1fpmTfOyoIvjP5HG7Z2ZM7VS9PPhQGW2pOpw== -parseurl@^1.3.2, parseurl@~1.3.2, parseurl@~1.3.3: +parseurl@~1.3.2, parseurl@~1.3.3: version "1.3.3" resolved "https://registry.yarnpkg.com/parseurl/-/parseurl-1.3.3.tgz#9da19e7bee8d12dff0513ed5b76957793bc2e8d4" integrity sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ== @@ -19560,7 +16411,7 @@ path-exists@^4.0.0: resolved "https://registry.yarnpkg.com/path-exists/-/path-exists-4.0.0.tgz#513bdbe2d3b95d7762e8c1137efa195c6c61b5b3" integrity sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w== -path-is-absolute@1.0.1, path-is-absolute@^1.0.0: +path-is-absolute@^1.0.0: version "1.0.1" resolved "https://registry.yarnpkg.com/path-is-absolute/-/path-is-absolute-1.0.1.tgz#174b9268735534ffbc7ace6bf53a5a9e1b5c5f5f" integrity sha1-F0uSaHNVNP+8es5r9TpanhtcX18= @@ -19602,11 +16453,6 @@ path-to-regexp@^1.7.0: dependencies: isarray "0.0.1" -path-to-regexp@^6.1.0: - version "6.2.0" - resolved "https://registry.yarnpkg.com/path-to-regexp/-/path-to-regexp-6.2.0.tgz#f7b3803336104c346889adece614669230645f38" - integrity sha512-f66KywYG6+43afgE/8j/GoiNyygk/bnoCbps++3ErRKsIYkGGupyv07R2Ok5m9i67Iqc+T2g1eAUGUPzWhYTyg== - path-type@^1.0.0: version "1.1.0" resolved "https://registry.yarnpkg.com/path-type/-/path-type-1.1.0.tgz#59c44f7ee491da704da415da5a4070ba4f8fe441" @@ -19628,7 +16474,7 @@ path-type@^4.0.0: resolved "https://registry.yarnpkg.com/path-type/-/path-type-4.0.0.tgz#84ed01c0a7ba380afe09d90a8c180dcd9d03043b" integrity sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw== -pbkdf2@^3.0.3, pbkdf2@^3.0.9: +pbkdf2@^3.0.3: version "3.1.2" resolved "https://registry.yarnpkg.com/pbkdf2/-/pbkdf2-3.1.2.tgz#dd822aa0887580e52f1a039dc3eda108efae3075" integrity sha512-iuh7L6jA7JEGu2WxDwtQP1ddOpaJNC4KlDEFfdQajSGgGPNi4OyDc2R7QnbY2bR9QjBVGwgvTdNJZoE7RaxUMA== @@ -20545,6 +17391,15 @@ pretty-format@^26.0.0, pretty-format@^26.6.0, pretty-format@^26.6.2: ansi-styles "^4.0.0" react-is "^17.0.1" +pretty-format@^27.0.2: + version "27.5.1" + resolved "https://registry.yarnpkg.com/pretty-format/-/pretty-format-27.5.1.tgz#2181879fdea51a7a5851fb39d920faa63f01d88e" + integrity sha512-Qb1gy5OrP5+zDf2Bvnzdl3jsTf1qXVMazbvCoKhtKqVs4/YK4ozX4gKQJJVyNe+cajNPn0KoC0MC3FUmaHWEmQ== + dependencies: + ansi-regex "^5.0.1" + ansi-styles "^5.0.0" + react-is "^17.0.1" + pretty-format@^28.0.0, pretty-format@^28.1.3: version "28.1.3" resolved "https://registry.yarnpkg.com/pretty-format/-/pretty-format-28.1.3.tgz#c9fba8cedf99ce50963a11b27d982a9ae90970d5" @@ -20648,6 +17503,11 @@ propagate@^2.0.0: resolved "https://registry.yarnpkg.com/propagate/-/propagate-2.0.1.tgz#40cdedab18085c792334e64f0ac17256d38f9a45" integrity sha512-vGrhOavPSTz4QVNuBNdcNXePNdNMaO1xj9yBeH1ScQPjk/rhg9sSlCXPhMkFuaNNW/syTvYqsnbIJxMBfRbbag== +property-expr@^2.0.4: + version "2.0.5" + resolved "https://registry.yarnpkg.com/property-expr/-/property-expr-2.0.5.tgz#278bdb15308ae16af3e3b9640024524f4dc02cb4" + integrity sha512-IJUkICM5dP5znhCckHSv30Q4b5/JA5enCtkRHYaOVOAocnH/1BQEYTC5NMfT3AVl/iXKdr3aqQbQn9DxyWknwA== + property-information@^5.0.0, property-information@^5.3.0: version "5.6.0" resolved "https://registry.yarnpkg.com/property-information/-/property-information-5.6.0.tgz#61675545fb23002f245c6540ec46077d4da3ed69" @@ -20660,53 +17520,6 @@ proto-list@~1.2.1: resolved "https://registry.yarnpkg.com/proto-list/-/proto-list-1.2.4.tgz#212d5bfe1318306a420f6402b8e26ff39647a849" integrity sha512-vtK/94akxsTMhe0/cbfpR+syPuszcuwhqVjJq26CuNDgFGj682oRBXOP5MJpv2r7JtE8MsiepGIqvvOTBwn2vA== -protobufjs@^5.0.3: - version "5.0.3" - resolved "https://registry.yarnpkg.com/protobufjs/-/protobufjs-5.0.3.tgz#e4dfe9fb67c90b2630d15868249bcc4961467a17" - integrity sha512-55Kcx1MhPZX0zTbVosMQEO5R6/rikNXd9b6RQK4KSPcrSIIwoXTtebIczUrXlwaSrbz4x8XUVThGPob1n8I4QA== - dependencies: - ascli "~1" - bytebuffer "~5" - glob "^7.0.5" - yargs "^3.10.0" - -protobufjs@^7.0.0: - version "7.1.2" - resolved "https://registry.yarnpkg.com/protobufjs/-/protobufjs-7.1.2.tgz#a0cf6aeaf82f5625bffcf5a38b7cd2a7de05890c" - integrity sha512-4ZPTPkXCdel3+L81yw3dG6+Kq3umdWKh7Dc7GW/CpNk4SX3hK58iPCWeCyhVTDrbkNeKrYNZ7EojM5WDaEWTLQ== - dependencies: - "@protobufjs/aspromise" "^1.1.2" - "@protobufjs/base64" "^1.1.2" - "@protobufjs/codegen" "^2.0.4" - "@protobufjs/eventemitter" "^1.1.0" - "@protobufjs/fetch" "^1.1.0" - "@protobufjs/float" "^1.0.2" - "@protobufjs/inquire" "^1.1.0" - "@protobufjs/path" "^1.1.2" - "@protobufjs/pool" "^1.1.0" - "@protobufjs/utf8" "^1.1.0" - "@types/node" ">=13.7.0" - long "^5.0.0" - -protobufjs@~6.11.2: - version "6.11.3" - resolved "https://registry.yarnpkg.com/protobufjs/-/protobufjs-6.11.3.tgz#637a527205a35caa4f3e2a9a4a13ddffe0e7af74" - integrity sha512-xL96WDdCZYdU7Slin569tFX712BxsxslWwAfAhCYjQKGTq7dAU91Lomy6nLLhh/dyGhk/YH4TwTSRxTzhuHyZg== - dependencies: - "@protobufjs/aspromise" "^1.1.2" - "@protobufjs/base64" "^1.1.2" - "@protobufjs/codegen" "^2.0.4" - "@protobufjs/eventemitter" "^1.1.0" - "@protobufjs/fetch" "^1.1.0" - "@protobufjs/float" "^1.0.2" - "@protobufjs/inquire" "^1.1.0" - "@protobufjs/path" "^1.1.2" - "@protobufjs/pool" "^1.1.0" - "@protobufjs/utf8" "^1.1.0" - "@types/long" "^4.0.1" - "@types/node" ">=13.7.0" - long "^4.0.0" - protocols@^2.0.0, protocols@^2.0.1: version "2.0.1" resolved "https://registry.yarnpkg.com/protocols/-/protocols-2.0.1.tgz#8f155da3fc0f32644e83c5782c8e8212ccf70a86" @@ -21099,6 +17912,11 @@ react-error-overlay@6.0.9, react-error-overlay@^6.0.9: resolved "https://registry.yarnpkg.com/react-error-overlay/-/react-error-overlay-6.0.9.tgz#3c743010c9359608c375ecd6bc76f35d93995b0a" integrity sha512-nQTTcUu+ATDbrSD1BZHr5kgSD4oF8OFjxun8uAaL8RwPBacGBNPf/yAuVVdx17N8XNzRDMrZ9XcKZHCjPW+9ew== +react-fast-compare@^2.0.1: + version "2.0.4" + resolved "https://registry.yarnpkg.com/react-fast-compare/-/react-fast-compare-2.0.4.tgz#e84b4d455b0fec113e0402c329352715196f81f9" + integrity sha512-suNP+J1VU1MWFKcyt7RtjiSWUjvidmQSlqu+eHslq+342xCbGTYmC0mEhPCOHxlW0CywylOC1u2DFAT+bv4dBw== + react-fast-compare@^3.0.1, react-fast-compare@^3.2.0: version "3.2.0" resolved "https://registry.yarnpkg.com/react-fast-compare/-/react-fast-compare-3.2.0.tgz#641a9da81b6a6320f270e89724fb45a0b39e43bb" @@ -21380,6 +18198,16 @@ react-transition-group@^4.4.1: loose-envify "^1.4.0" prop-types "^15.6.2" +react-transition-group@^4.4.5: + version "4.4.5" + resolved "https://registry.yarnpkg.com/react-transition-group/-/react-transition-group-4.4.5.tgz#e53d4e3f3344da8521489fbef8f2581d42becdd1" + integrity sha512-pZcd1MCJoiKiBR2NRxeCRg13uCXbydPnmB4EOeRrY7480qNWO8IIgQG6zlDkm6uRMsURXPuKq0GWtiM59a5Q6g== + dependencies: + "@babel/runtime" "^7.5.5" + dom-helpers "^5.0.1" + loose-envify "^1.4.0" + prop-types "^15.6.2" + react-universal-interface@^0.6.2: version "0.6.2" resolved "https://registry.yarnpkg.com/react-universal-interface/-/react-universal-interface-0.6.2.tgz#5e8d438a01729a4dbbcbeeceb0b86be146fe2b3b" @@ -21535,13 +18363,6 @@ reduce-css-calc@^2.1.8: css-unit-converter "^1.1.1" postcss-value-parser "^3.3.0" -redux-logger@^3.0.6: - version "3.0.6" - resolved "https://registry.yarnpkg.com/redux-logger/-/redux-logger-3.0.6.tgz#f7555966f3098f3c88604c449cf0baf5778274bf" - integrity sha1-91VZZvMJjzyIYExEnPC69XeCdL8= - dependencies: - deep-diff "^0.3.5" - redux@^4.0.0, redux@^4.0.5: version "4.1.2" resolved "https://registry.yarnpkg.com/redux/-/redux-4.1.2.tgz#140f35426d99bb4729af760afcf79eaaac407104" @@ -21575,7 +18396,7 @@ regenerator-runtime@^0.11.0: resolved "https://registry.yarnpkg.com/regenerator-runtime/-/regenerator-runtime-0.11.1.tgz#be05ad7f9bf7d22e056f9726cee5017fbf19e2e9" integrity sha512-MguG95oij0fC3QV3URf4V2SDYGJhJnJGqvIIgdECeODCT98wSWDAJ94SSuVpYQUoTcGUIL6L4yNB7j1DFFHSBg== -regenerator-runtime@^0.13.10, regenerator-runtime@^0.13.11: +regenerator-runtime@^0.13.11: version "0.13.11" resolved "https://registry.yarnpkg.com/regenerator-runtime/-/regenerator-runtime-0.13.11.tgz#f6dca3e7ceec20590d07ada785636a90cdca17f9" integrity sha512-kY1AZVr2Ra+t+piVaJ4gxaFaReZVH40AKNo7UCX6W+dEwBo/2oZJzqfuN1qLq1oL45o56cPaTXELwrTh8Fpggg== @@ -21809,15 +18630,6 @@ repeating@^2.0.0: dependencies: is-finite "^1.0.0" -replace-in-file@^6.1.0: - version "6.3.2" - resolved "https://registry.yarnpkg.com/replace-in-file/-/replace-in-file-6.3.2.tgz#0f19835137177c89932f45df319f3539a019484f" - integrity sha512-Dbt5pXKvFVPL3WAaEB3ZX+95yP0CeAtIPJDwYzHbPP5EAHn+0UoegH/Wg3HKflU9dYBH8UnBC2NvY3P+9EZtTg== - dependencies: - chalk "^4.1.2" - glob "^7.2.0" - yargs "^17.2.1" - request@^2.53.0: version "2.88.2" resolved "https://registry.yarnpkg.com/request/-/request-2.88.2.tgz#d73c918731cb5a87da047e207234146f664d12b3" @@ -21910,14 +18722,6 @@ resolve-global@1.0.0, resolve-global@^1.0.0: dependencies: global-dirs "^0.1.1" -resolve-path@^1.4.0: - version "1.4.0" - resolved "https://registry.yarnpkg.com/resolve-path/-/resolve-path-1.4.0.tgz#c4bda9f5efb2fce65247873ab36bb4d834fe16f7" - integrity sha1-xL2p9e+y/OZSR4c6s2u02DT+Fvc= - dependencies: - http-errors "~1.6.2" - path-is-absolute "1.0.1" - resolve-pathname@^2.2.0: version "2.2.0" resolved "https://registry.yarnpkg.com/resolve-pathname/-/resolve-pathname-2.2.0.tgz#7e9ae21ed815fd63ab189adeee64dc831eefa879" @@ -22118,19 +18922,6 @@ rollup@^1.31.1: "@types/node" "*" acorn "^7.1.0" -rpc-websockets@^7.5.0: - version "7.5.0" - resolved "https://registry.yarnpkg.com/rpc-websockets/-/rpc-websockets-7.5.0.tgz#bbeb87572e66703ff151e50af1658f98098e2748" - integrity sha512-9tIRi1uZGy7YmDjErf1Ax3wtqdSSLIlnmL5OtOzgd5eqPKbsPpwDP5whUDO2LQay3Xp0CcHlcNSGzacNRluBaQ== - dependencies: - "@babel/runtime" "^7.17.2" - eventemitter3 "^4.0.7" - uuid "^8.3.2" - ws "^8.5.0" - optionalDependencies: - bufferutil "^4.0.1" - utf-8-validate "^5.0.2" - rsvp@^4.8.4: version "4.8.5" resolved "https://registry.yarnpkg.com/rsvp/-/rsvp-4.8.5.tgz#c8f155311d167f68f21e168df71ec5b083113734" @@ -22162,31 +18953,24 @@ run-queue@^1.0.0, run-queue@^1.0.3: dependencies: aproba "^1.1.1" -rxjs@^6.6.7: - version "6.6.7" - resolved "https://registry.yarnpkg.com/rxjs/-/rxjs-6.6.7.tgz#90ac018acabf491bf65044235d5863c4dab804c9" - integrity sha512-hTdwr+7yYNIT5n4AMYp85KA6yw2Va0FLa3Rguvbpa4W3I5xynaBZo41cM3XM+4Q6fRMj3sBYIR1VAmZMXYJvRQ== - dependencies: - tslib "^1.9.0" - -rxjs@^7.2.0, rxjs@^7.4.0, rxjs@^7.5.6, rxjs@^7.5.7: +rxjs@^7.2.0, rxjs@^7.5.6, rxjs@^7.5.7: version "7.5.7" resolved "https://registry.yarnpkg.com/rxjs/-/rxjs-7.5.7.tgz#2ec0d57fdc89ece220d2e702730ae8f1e49def39" integrity sha512-z9MzKh/UcOqB3i20H6rtrlaE/CgjLOvheWK/9ILrbhROGTweAi1BaFsTT9FbwZi5Trr1qNRs+MXkhmR06awzQA== dependencies: tslib "^2.1.0" -rxjs@^7.5.1, rxjs@^7.5.5: +rxjs@^7.5.1: version "7.5.5" resolved "https://registry.yarnpkg.com/rxjs/-/rxjs-7.5.5.tgz#2ebad89af0f560f460ad5cc4213219e1f7dd4e9f" integrity sha512-sy+H0pQofO95VDmFLzyaw9xNJU4KTRSwQIGM6+iG3SypAtCiLDzpeG8sJrNCWn2Up9km+KhkvTdbkrdy+yzZdw== dependencies: tslib "^2.1.0" -rxjs@^7.6.0: - version "7.6.0" - resolved "https://registry.yarnpkg.com/rxjs/-/rxjs-7.6.0.tgz#361da5362b6ddaa691a2de0b4f2d32028f1eb5a2" - integrity sha512-DDa7d8TFNUalGC9VqXvQ1euWNN7sc63TrUCuM9J998+ViviahMIjKSOU7rfcgFOF+FCD71BhDRv4hrFz+ImDLQ== +rxjs@^7.8.0: + version "7.8.0" + resolved "https://registry.yarnpkg.com/rxjs/-/rxjs-7.8.0.tgz#90a938862a82888ff4c7359811a595e14e1e09a4" + integrity sha512-F2+gxDshqmIub1KdvZkaEfGDwLNpPvk9Fs6LD/MyQxNgMds/WH9OdDDXOmxUZpME+iSK3rQCctkL0DYyytUqMg== dependencies: tslib "^2.1.0" @@ -22319,20 +19103,6 @@ scrypt-js@3.0.1: resolved "https://registry.yarnpkg.com/scrypt-js/-/scrypt-js-3.0.1.tgz#d314a57c2aef69d1ad98a138a21fe9eafa9ee312" integrity sha512-cdwTTnqPu0Hyvf5in5asVdZocVDTNRmR7XEcJuIzMjJeSHybHl7vpB66AzwTaIg6CLSbtjcxc8fqcySfnTkccA== -scryptsy@^2.1.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/scryptsy/-/scryptsy-2.1.0.tgz#8d1e8d0c025b58fdd25b6fa9a0dc905ee8faa790" - integrity sha512-1CdSqHQowJBnMAFyPEBRfqag/YP9OF394FV+4YREIJX4ljD7OxvQRDayyoyyCk+senRjSkP6VnUNQmVQqB6g7w== - -secp256k1@^4.0.2: - version "4.0.3" - resolved "https://registry.yarnpkg.com/secp256k1/-/secp256k1-4.0.3.tgz#c4559ecd1b8d3c1827ed2d1b94190d69ce267303" - integrity sha512-NLZVf+ROMxwtEj3Xa562qgv2BK5e2WNmXPiOdVIPLgs6lyTzMvBq0aWTYMI5XCP9jZMVKOcqZLw/Wc4vDkuxhA== - dependencies: - elliptic "^6.5.4" - node-addon-api "^2.0.0" - node-gyp-build "^4.2.0" - select-hose@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/select-hose/-/select-hose-2.0.0.tgz#625d8658f865af43ec962bfc376a37359a4994ca" @@ -23033,15 +19803,6 @@ string-natural-compare@^3.0.1: resolved "https://registry.yarnpkg.com/string-natural-compare/-/string-natural-compare-3.0.1.tgz#7a42d58474454963759e8e8b7ae63d71c1e7fdf4" integrity sha512-n3sPwynL1nwKi3WJ6AIsClwBMa0zTi54fn2oLU6ndfTSIO05xaznjSf15PcBZU6FNWbmN5Q6cxT4V5hGvB4taw== -string-width@^1.0.1: - version "1.0.2" - resolved "https://registry.yarnpkg.com/string-width/-/string-width-1.0.2.tgz#118bdf5b8cdc51a2a7e70d211e07e2b0b9b107d3" - integrity sha512-0XsVpQLnVCXHJfyEs8tC0zpTVIr5PKKsQtkT29IwupnPTjtPmQ3xT/4yCREF9hYkV/3M3kzcUTSAZT6a6h81tw== - dependencies: - code-point-at "^1.0.0" - is-fullwidth-code-point "^1.0.0" - strip-ansi "^3.0.0" - "string-width@^1.0.2 || 2 || 3 || 4", string-width@^4.0.0, string-width@^4.1.0, string-width@^4.2.0, string-width@^4.2.2, string-width@^4.2.3: version "4.2.3" resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.2.3.tgz#269c7117d27b05ad2e536830a8ec895ef9c6d010" @@ -23152,7 +19913,7 @@ strip-ansi@6.0.0: dependencies: ansi-regex "^5.0.0" -strip-ansi@^3.0.0, strip-ansi@^3.0.1: +strip-ansi@^3.0.1: version "3.0.1" resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-3.0.1.tgz#6a385fb8853d952d5ff05d0e8aaf94278dc63dcf" integrity sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8= @@ -23289,11 +20050,6 @@ stylis@^4.0.6: resolved "https://registry.yarnpkg.com/stylis/-/stylis-4.0.13.tgz#f5db332e376d13cc84ecfe5dace9a2a51d954c91" integrity sha512-xGPXiFVl4YED9Jh7Euv2V220mriG9u4B2TA6Ybjc1catrstKD2PpIdU3U0RKpkVBC2EhmL/F0sPCr9vrFTNRag== -superstruct@^0.14.2: - version "0.14.2" - resolved "https://registry.yarnpkg.com/superstruct/-/superstruct-0.14.2.tgz#0dbcdf3d83676588828f1cf5ed35cda02f59025b" - integrity sha512-nPewA6m9mR3d6k7WkZ8N8zpTWfenFH3q9pA2PkuiZxINr9DKB2+40wEQf0ixn8VaGuJ78AB6iWOtStI+/4FKZQ== - supports-color@^5.3.0, supports-color@^5.4.0, supports-color@^5.5.0: version "5.5.0" resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-5.5.0.tgz#e2e69a44ac8772f78a1ec0b35b689df6530efc8f" @@ -23390,20 +20146,6 @@ table@^6.0.9: string-width "^4.2.3" strip-ansi "^6.0.1" -tailwind-config-viewer@^1.5.1: - version "1.6.3" - resolved "https://registry.yarnpkg.com/tailwind-config-viewer/-/tailwind-config-viewer-1.6.3.tgz#cbd6ba6d4a6f063bb5023261dfaf3c05358cf8ba" - integrity sha512-XesuasyVLDXadHhLLDeS0H5EiAhhWnRPDjDRsP4sYRXGn8h7Vgnv9TzFXvoaNG6dvuRuwWQxJRfNOjaWid2V1g== - dependencies: - "@koa/router" "^9.0.1" - commander "^6.0.0" - fs-extra "^9.0.1" - koa "^2.12.0" - koa-static "^5.0.0" - open "^7.0.4" - portfinder "^1.0.26" - replace-in-file "^6.1.0" - tailwindcss-pseudo-elements@^1.5.1: version "1.5.1" resolved "https://registry.yarnpkg.com/tailwindcss-pseudo-elements/-/tailwindcss-pseudo-elements-1.5.1.tgz#81603ddd96670d0d18bc85dc26c742b2cfe2aa8a" @@ -23431,18 +20173,6 @@ tar@^6.0.2: mkdirp "^1.0.3" yallist "^4.0.0" -tar@^6.1.11: - version "6.1.12" - resolved "https://registry.yarnpkg.com/tar/-/tar-6.1.12.tgz#3b742fb05669b55671fb769ab67a7791ea1a62e6" - integrity sha512-jU4TdemS31uABHd+Lt5WEYJuzn+TJTCBLljvIAHZOz6M9Os5pJ4dD+vRFLxPa/n3T0iEFzpi+0x1UfuDZYbRMw== - dependencies: - chownr "^2.0.0" - fs-minipass "^2.0.0" - minipass "^3.0.0" - minizlib "^2.1.1" - mkdirp "^1.0.3" - yallist "^4.0.0" - telejson@^5.3.2: version "5.3.3" resolved "https://registry.yarnpkg.com/telejson/-/telejson-5.3.3.tgz#fa8ca84543e336576d8734123876a9f02bf41d2e" @@ -23572,11 +20302,6 @@ test-exclude@^6.0.0: glob "^7.1.4" minimatch "^3.0.4" -text-encoding-utf-8@^1.0.2: - version "1.0.2" - resolved "https://registry.yarnpkg.com/text-encoding-utf-8/-/text-encoding-utf-8-1.0.2.tgz#585b62197b0ae437e3c7b5d0af27ac1021e10d13" - integrity sha512-8bw4MY9WjdsD2aMtO0OzOCY3pXGYNx2d2FfHRVUKkiCPDWjKuOlhLVASS+pD7VkLTVjW268LYJHwsnPFlBpbAg== - text-extensions@^1.0.0: version "1.9.0" resolved "https://registry.yarnpkg.com/text-extensions/-/text-extensions-1.9.0.tgz#1853e45fee39c945ce6f6c36b2d659b5aabc2a26" @@ -23658,7 +20383,7 @@ tiny-secp256k1@^1.1.1, tiny-secp256k1@^1.1.3: elliptic "^6.4.0" nan "^2.13.2" -tiny-warning@^1.0.0, tiny-warning@^1.0.3: +tiny-warning@^1.0.0, tiny-warning@^1.0.2, tiny-warning@^1.0.3: version "1.0.3" resolved "https://registry.yarnpkg.com/tiny-warning/-/tiny-warning-1.0.3.tgz#94a30db453df4c643d0fd566060d60a875d84754" integrity sha512-lBN9zLN/oAf68o3zNXYrdCt1kP8WsiGW8Oo2ka41b2IM5JL/S1CTyX1rW0mb/zSuJun0ZUrDxx4sqvYS2FWzPA== @@ -23734,6 +20459,11 @@ toidentifier@1.0.1: resolved "https://registry.yarnpkg.com/toidentifier/-/toidentifier-1.0.1.tgz#3be34321a88a820ed1bd80dfaa33e479fbb8dd35" integrity sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA== +toposort@^2.0.2: + version "2.0.2" + resolved "https://registry.yarnpkg.com/toposort/-/toposort-2.0.2.tgz#ae21768175d1559d48bef35420b2f4962f09c330" + integrity sha512-0a5EOkAUp8D4moMi2W8ZF8jcga7BgZd91O/yabJCFY8az+XSzeGyTKs0Aoo897iV1Nj6guFq8orWDS96z91oGg== + totalist@^1.0.0: version "1.1.0" resolved "https://registry.yarnpkg.com/totalist/-/totalist-1.1.0.tgz#a4d65a3e546517701e3e5c37a47a70ac97fe56df" @@ -23865,7 +20595,7 @@ tslib@2.4.0: resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.4.0.tgz#7cecaa7f073ce680a05847aa77be941098f36dc3" integrity sha512-d6xOpEDfsi2CZVlPQzGeux8XMwLT9hssAsaPYExaQMuYskwb+x1x7J371tWlbBdWHroy99KnVB6qIkUbs5X3UQ== -tslib@^1.8.1, tslib@^1.9.0: +tslib@^1.10.0, tslib@^1.8.1: version "1.14.1" resolved "https://registry.yarnpkg.com/tslib/-/tslib-1.14.1.tgz#cf2d38bdc34a134bcaf1091c41f6619e2f672d00" integrity sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg== @@ -23880,11 +20610,6 @@ tslib@^2.4.0: resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.4.1.tgz#0d0bfbaac2880b91e22df0768e55be9753a5b17e" integrity sha512-tGyy4dAjRIEwI7BzsB0lynWgOpfqjUdq91XXAlIWD2OwKBH7oCl/GZG/HT4BOHrTlPMOASlMQ7veyTqpmRcrNA== -tsscmp@1.0.6: - version "1.0.6" - resolved "https://registry.yarnpkg.com/tsscmp/-/tsscmp-1.0.6.tgz#85b99583ac3589ec4bfef825b5000aa911d605eb" - integrity sha512-LxhtAkPDTkVCMQjt2h6eBVY28KCjikZqZfMcC15YBeNjkgUpdCfBu5HoiOTDu86v6smE8yOjyEktJ8hlbANHQA== - tsutils@^3.17.1, tsutils@^3.21.0: version "3.21.0" resolved "https://registry.yarnpkg.com/tsutils/-/tsutils-3.21.0.tgz#b48717d394cea6c1e096983eed58e9d61715b623" @@ -23973,7 +20698,7 @@ type-fest@^2.13.0, type-fest@^2.5.1: resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-2.19.0.tgz#88068015bb33036a598b952e55e9311a60fd3a9b" integrity sha512-RAH822pAdBgcNMAfWnCBU3CFZcfZ/i1eZjwFU/dsLKumyuuP3niueg2UAukXYF0E2AAoc82ZSSf9J0WQBinzHA== -type-is@^1.6.16, type-is@~1.6.18: +type-is@~1.6.18: version "1.6.18" resolved "https://registry.yarnpkg.com/type-is/-/type-is-1.6.18.tgz#4e552cd05df09467dcbc4ef739de89f2cf37c131" integrity sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g== @@ -24317,14 +21042,6 @@ use-composed-ref@^1.0.0: resolved "https://registry.yarnpkg.com/use-composed-ref/-/use-composed-ref-1.2.1.tgz#9bdcb5ccd894289105da2325e1210079f56bf849" integrity sha512-6+X1FLlIcjvFMAeAD/hcxDT8tmyrWnbSPMU0EnxQuDLIxokuFzWliXBiYZuGIx+mrAMLBw0WFfCkaPw8ebzAhw== -use-dark-mode@^2.3.1: - version "2.3.1" - resolved "https://registry.yarnpkg.com/use-dark-mode/-/use-dark-mode-2.3.1.tgz#d506349c7b7e09e9977cb8a6ab4470896aa3779a" - integrity sha512-hmcdJR96tTustRQdaQwe6jMrZHnmPqXBxgy4jaQ4gsfhwajsCpjECuq9prgDe9XxMx/f9r96o2/md6O4Lwhwjg== - dependencies: - "@use-it/event-listener" "^0.1.2" - use-persisted-state "^0.3.0" - use-isomorphic-layout-effect@^1.0.0: version "1.1.1" resolved "https://registry.yarnpkg.com/use-isomorphic-layout-effect/-/use-isomorphic-layout-effect-1.1.1.tgz#7bb6589170cd2987a152042f9084f9effb75c225" @@ -24337,26 +21054,12 @@ use-latest@^1.0.0: dependencies: use-isomorphic-layout-effect "^1.0.0" -use-persisted-state@^0.3.0: - version "0.3.3" - resolved "https://registry.yarnpkg.com/use-persisted-state/-/use-persisted-state-0.3.3.tgz#5e0f2236967cec7c34de33abc07ae6818e7c7451" - integrity sha512-pCNlvYC8+XjRxwnIut4teGC9f2p9aD88R8OGseQGZa2dvqG/h1vEGk1vRE1IZG0Vf161UDpn+NlW4+UGubQflQ== - dependencies: - "@use-it/event-listener" "^0.1.2" - use@^3.1.0: version "3.1.1" resolved "https://registry.yarnpkg.com/use/-/use-3.1.1.tgz#d50c8cac79a19fbc20f2911f56eb973f4e10070f" integrity sha512-cwESVXlO3url9YWlFW/TA9cshCEhtu7IKJ/p5soJ/gGpj7vbvFrAY/eIioQ6Dw23KjZhYgiIo8HOs1nQ2vr/oQ== utf-8-validate@^5.0.2: - version "5.0.9" - resolved "https://registry.yarnpkg.com/utf-8-validate/-/utf-8-validate-5.0.9.tgz#ba16a822fbeedff1a58918f2a6a6b36387493ea3" - integrity sha512-Yek7dAy0v3Kl0orwMlvi7TPtiCNrdfHNd7Gcc/pLq4BLXqfAmd0J7OWMizUQnTTJsyjKn02mU7anqwfmUP4J8Q== - dependencies: - node-gyp-build "^4.3.0" - -utf-8-validate@^5.0.5: version "5.0.10" resolved "https://registry.yarnpkg.com/utf-8-validate/-/utf-8-validate-5.0.10.tgz#d7d10ea39318171ca982718b6b96a8d2442571a2" integrity sha512-Z6czzLq4u8fPOyx7TU6X3dvUZVvoJmxSQ+IcrlmagKhilxlhZgxPK6C5Jqbkw1IDUmFTM+cz9QDnnLTwDz/2gQ== @@ -24478,7 +21181,7 @@ varuint-bitcoin@^1.0.4: dependencies: safe-buffer "^5.1.1" -vary@^1.1.2, vary@~1.1.2: +vary@~1.1.2: version "1.1.2" resolved "https://registry.yarnpkg.com/vary/-/vary-1.1.2.tgz#2299f02c6ded30d4a5961b0b9f74524a18f634fc" integrity sha1-IpnwLG3tMNSllhsLn3RSShj2NPw= @@ -24520,11 +21223,6 @@ vfile@^4.0.0: unist-util-stringify-position "^2.0.0" vfile-message "^2.0.0" -vlq@^2.0.4: - version "2.0.4" - resolved "https://registry.yarnpkg.com/vlq/-/vlq-2.0.4.tgz#6057b85729245b9829e3cc7755f95b228d4fe041" - integrity sha512-aodjPa2wPQFkra1G8CzJBTHXhgk3EVSwxSWXNPr1fgdFLUb8kvLV1iEb6rFgasIsjP82HWI6dsb5Io26DDnasA== - vm-browserify@^1.0.1: version "1.1.2" resolved "https://registry.yarnpkg.com/vm-browserify/-/vm-browserify-1.1.2.tgz#78641c488b8e6ca91a75f511e7a3b32a86e5dda0" @@ -24888,7 +21586,7 @@ websocket-extensions@>=0.1.1: resolved "https://registry.yarnpkg.com/websocket-extensions/-/websocket-extensions-0.1.4.tgz#7f8473bc839dfd87608adb95d7eb075211578a42" integrity sha512-OqedPIGOfsDlo31UNwYbCFMSaO9m9G/0faIHj5/dZFDMFqPTcx6UwqyOy3COEaEOg/9VsGIpdqn62W5KhoKSpg== -websocket@^1.0.32, websocket@^1.0.34: +websocket@^1.0.34: version "1.0.34" resolved "https://registry.yarnpkg.com/websocket/-/websocket-1.0.34.tgz#2bdc2602c08bf2c82253b730655c0ef7dcab3111" integrity sha512-PRDso2sGwF6kM75QykIesBijKSVceR6jL2G8NGYyq2XrItNC2P5/qL5XeR056GhA+Ly7JMFvJb9I312mJfmqnQ== @@ -25006,11 +21704,6 @@ wildcard-match@5.1.2: resolved "https://registry.yarnpkg.com/wildcard-match/-/wildcard-match-5.1.2.tgz#66b438001391674d8599b45da051e0bd9f33cd2a" integrity sha512-qNXwI591Z88c8bWxp+yjV60Ch4F8Riawe3iGxbzquhy8Xs9m+0+SLFBGb/0yCTIDElawtaImC37fYZ+dr32KqQ== -window-size@^0.1.4: - version "0.1.4" - resolved "https://registry.yarnpkg.com/window-size/-/window-size-0.1.4.tgz#f8e1aa1ee5a53ec5bf151ffa09742a6ad7697876" - integrity sha512-2thx4pB0cV3h+Bw7QmMXcEbdmOzv9t0HFplJH/Lz6yu60hXYy5RT8rUu+wlIreVxWsGN20mo+MHeCSfUpQBwPw== - windows-release@^5.0.1: version "5.0.1" resolved "https://registry.yarnpkg.com/windows-release/-/windows-release-5.0.1.tgz#d1f7cd1f25660ba05cac6359711844dce909a8ed" @@ -25195,14 +21888,6 @@ worker-rpc@^0.1.0: dependencies: microevent.ts "~0.1.1" -wrap-ansi@^2.0.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-2.1.0.tgz#d8fc3d284dd05794fe84973caecdd1cf824fdd85" - integrity sha512-vAaEaDM946gbNpH5pLVNR+vX2ht6n0Bt3GXwVB1AuAqZosOvHNF3P7wDnh8KLkSqgUh0uh77le7Owgoz+Z9XBw== - dependencies: - string-width "^1.0.1" - strip-ansi "^3.0.1" - wrap-ansi@^5.1.0: version "5.1.0" resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-5.1.0.tgz#1fd1f67235d5b6d0fee781056001bfb694c03b09" @@ -25271,21 +21956,11 @@ ws@^7.3.1, ws@^7.4.6: resolved "https://registry.yarnpkg.com/ws/-/ws-7.5.7.tgz#9e0ac77ee50af70d58326ecff7e85eb3fa375e67" integrity sha512-KMvVuFzpKBuiIXW3E4u3mySRO2/mCHSyZDJQM5NQ9Q9KHWHWh0NHgfbRMLLrceUK5qAL4ytALJbpRMjixFZh8A== -ws@^7.4.5, ws@^7.5.9: - version "7.5.9" - resolved "https://registry.yarnpkg.com/ws/-/ws-7.5.9.tgz#54fa7db29f4c7cec68b1ddd3a89de099942bb591" - integrity sha512-F+P9Jil7UiSKSkppIiD94dN07AwvFixvLIj1Og1Rl9GGMuNipJnV9JzjD6XuqmAeiswGvUmNLjr5cFuXwNS77Q== - ws@^8.2.3: version "8.5.0" resolved "https://registry.yarnpkg.com/ws/-/ws-8.5.0.tgz#bfb4be96600757fe5382de12c670dab984a1ed4f" integrity sha512-BWX0SWVgLPzYwF8lTzEy1egjhS4S4OEAHfsO8o65WOVsrnSRGaSiUaa9e0ggGlkMTtBlmOpEXiie9RUcBO86qg== -ws@^8.5.0: - version "8.11.0" - resolved "https://registry.yarnpkg.com/ws/-/ws-8.11.0.tgz#6a0d36b8edfd9f96d8b25683db2f8d7de6e8e143" - integrity sha512-HPG3wQd9sNQoT9xHyNCXoDUa+Xw/VevmY9FoHyQ+g+rrMn4j6FB4np7Z0OhdTgjx6MgQLK7jwSy1YecU1+4Asg== - ws@^8.8.1: version "8.9.0" resolved "https://registry.yarnpkg.com/ws/-/ws-8.9.0.tgz#2a994bb67144be1b53fe2d23c53c028adeb7f45e" @@ -25323,18 +21998,6 @@ xtend@^4.0.0, xtend@^4.0.1, xtend@^4.0.2, xtend@~4.0.1: resolved "https://registry.yarnpkg.com/xtend/-/xtend-4.0.2.tgz#bb72779f5fa465186b1f438f674fa347fdb5db54" integrity sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ== -xxhashjs@^0.2.2: - version "0.2.2" - resolved "https://registry.yarnpkg.com/xxhashjs/-/xxhashjs-0.2.2.tgz#8a6251567621a1c46a5ae204da0249c7f8caa9d8" - integrity sha512-AkTuIuVTET12tpsVIQo+ZU6f/qDmKuRUcjaqR+OIvm+aCBsZ95i7UVY5WJ9TMsSaZ0DA2WxoZ4acu0sPH+OKAw== - dependencies: - cuint "^0.2.2" - -y18n@^3.2.0: - version "3.2.2" - resolved "https://registry.yarnpkg.com/y18n/-/y18n-3.2.2.tgz#85c901bd6470ce71fc4bb723ad209b70f7f28696" - integrity sha512-uGZHXkHnhF0XeeAPgnKfPv1bgKAYyVvmNL1xlKsPYZPaIHxGti2hHqvOCQv71XMsLxu1QjergkqogUnms5D3YQ== - y18n@^4.0.0: version "4.0.3" resolved "https://registry.yarnpkg.com/y18n/-/y18n-4.0.3.tgz#b5f259c82cd6e336921efd7bfd8bf560de9eeedf" @@ -25348,7 +22011,7 @@ y18n@^5.0.5: yaeti@^0.0.6: version "0.0.6" resolved "https://registry.yarnpkg.com/yaeti/-/yaeti-0.0.6.tgz#f26f484d72684cf42bedfb76970aa1608fbf9577" - integrity sha1-8m9ITXJoTPQr7ft2lwqhYI+/lXc= + integrity sha512-MvQa//+KcZCUkBTIC9blM+CU9J2GzuTytsOUwf2lidtvkx/6gnEp1QvJv34t9vdjhFmha/mUiNDbN0D0mJWdug== yallist@^3.0.2: version "3.1.1" @@ -25455,37 +22118,6 @@ yargs@^17.0.0: y18n "^5.0.5" yargs-parser "^21.0.0" -yargs@^17.2.1: - version "17.3.1" - resolved "https://registry.yarnpkg.com/yargs/-/yargs-17.3.1.tgz#da56b28f32e2fd45aefb402ed9c26f42be4c07b9" - integrity sha512-WUANQeVgjLbNsEmGk20f+nlHgOqzRFpiGWVaBrYGYIGANIIu3lWjoyi0fNlFmJkvfhCZ6BXINe7/W2O2bV4iaA== - dependencies: - cliui "^7.0.2" - escalade "^3.1.1" - get-caller-file "^2.0.5" - require-directory "^2.1.1" - string-width "^4.2.3" - y18n "^5.0.5" - yargs-parser "^21.0.0" - -yargs@^3.10.0: - version "3.32.0" - resolved "https://registry.yarnpkg.com/yargs/-/yargs-3.32.0.tgz#03088e9ebf9e756b69751611d2a5ef591482c995" - integrity sha512-ONJZiimStfZzhKamYvR/xvmgW3uEkAUFSP91y2caTEPhzF6uP2JfPiVZcq66b/YR0C3uitxSV7+T1x8p5bkmMg== - dependencies: - camelcase "^2.0.1" - cliui "^3.0.3" - decamelize "^1.1.1" - os-locale "^1.4.0" - string-width "^1.0.1" - window-size "^0.1.4" - y18n "^3.2.0" - -ylru@^1.2.0: - version "1.3.2" - resolved "https://registry.yarnpkg.com/ylru/-/ylru-1.3.2.tgz#0de48017473275a4cbdfc83a1eaf67c01af8a785" - integrity sha512-RXRJzMiK6U2ye0BlGGZnmpwJDPgakn6aNQ0A7gHRbD4I0uvK4TW6UqkK1V0pp9jskjJBAXd3dRrbzWkqJ+6cxA== - yn@3.1.1: version "3.1.1" resolved "https://registry.yarnpkg.com/yn/-/yn-3.1.1.tgz#1e87401a09d767c1d5eab26a6e4c185182d2eb50" @@ -25496,10 +22128,18 @@ yocto-queue@^0.1.0: resolved "https://registry.yarnpkg.com/yocto-queue/-/yocto-queue-0.1.0.tgz#0294eb3dee05028d31ee1a5fa2c556a6aaf10a1b" integrity sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q== -zod@^3.18.0: - version "3.18.0" - resolved "https://registry.yarnpkg.com/zod/-/zod-3.18.0.tgz#2eed58b3cafb8d9a67aa2fee69279702f584f3bc" - integrity sha512-gwTm8RfUCe8l9rDwN5r2A17DkAa8Ez4Yl4yXqc5VqeGaXaJahzYYXbTwvhroZi0SNBqTwh/bKm2N0mpCzuw4bA== +yup@^0.32.11: + version "0.32.11" + resolved "https://registry.yarnpkg.com/yup/-/yup-0.32.11.tgz#d67fb83eefa4698607982e63f7ca4c5ed3cf18c5" + integrity sha512-Z2Fe1bn+eLstG8DRR6FTavGD+MeAwyfmouhHsIUgaADz8jvFKbO/fXc2trJKZg+5EBjh4gGm3iU/t3onKlXHIg== + dependencies: + "@babel/runtime" "^7.15.4" + "@types/lodash" "^4.14.175" + lodash "^4.17.21" + lodash-es "^4.17.21" + nanoclone "^0.2.1" + property-expr "^2.0.4" + toposort "^2.0.2" zwitch@^1.0.0: version "1.0.5"