From ab6a5549984e92641bfe0701027dd25edeb4a10b Mon Sep 17 00:00:00 2001 From: tomjeatt <40243778+tomjeatt@users.noreply.github.com> Date: Mon, 22 May 2023 09:15:34 +0100 Subject: [PATCH] Feature/updated transfer UI (#876) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * refactor: use updated tab component * refactor: duplicated form titles * refactor: remove redundant hook calls * refactor: prefer title case * wip: XCM transfer form UI * wip: updated form UI * wip: account selector placeholder component * wip: account selector modal * wip: modal open and close actions * wip: update modal type * wip: get accounts * wip: add identicon and rename component for consistency * wip: account input component * fix: remove redundant icons prop * feat: implement with SelectTrigger * wip: styling and account selection value * wip: handle setting account data * refactor: better naming * wip: address list styling * refactor: rename defaultAccount * wip: chain selector placeholder component * wip: duplicate account component and rename * chore: delete redundant legacy component * wip: logic for fetching and rendering chain ids * wip: chain item styling * wip: selected chain styling * chore: add comment * refactor: pass through native token to icon component * feature: add chain icon component * chore: add comment * chore: correct file name casing * refactor: improve folder structure * wip: form layout styling * chore: add arrow icon * chore: add logos and correct svg titles * chore: remove redundant svg prop * chore: rename arrow icon * chore: consistent use of styled components * refactor: remove padding from modal body * wip: formik integration work * wip: extend useXCMBridge to return available chains and utility methods * chore: move Chain and Chains types to types directory * feat: layout and form implementation * feat: add schema * feat: final * wip: refactor useXCMBridge hook * refactor: add endpoints type * refactor: wrap methods in useCallback * refactor: fix bug in hook method * chore: bump bridge version * wip: set originating and destination chain values * refactor: set from chain value on field change * wip: set originating chain value * refactor: mergeProps to set field value * refactor: handle setting origin/destination chain values * wip: get tokens method * wip: first iteration of balances function * wip: handle tokens array * wip: set token value * wip: get token balances * wip: return token and balances in single method * wip: mapped tokens * refactor: handle default chain values * refactor: better organised function order * wip: handle change events * wip: handle setting tokens * wip: handle fetching tokens and balances * wip: convert input configs * wip: handle token change * wip: get token USD price * Trigger Build * chore: remove unused import * chore: correct eslintignore syntax * wip: handle breaking changes * wip: disable token input when select items value is 1 * chore: set first token item as variable * wip: handle setting and changing values * chire: add loading spinner * refactor: add loading state * refactor: filter destination chains * chore: remove console log * chore: bump XCM bridge version * chore: update config * refactor: configure validation * chore: revert change to useForm hook * wip: form validation * wip: working form validation * wip: undefined validation parameters * refactor: return dest fee estimate from bridge hook * feature: show fees and fee estimates * chore: conditional operators * refactor: handle ticker change correctly * wip: sendTransaction method * Revert "wip: sendTransaction method" This reverts commit 3ade26dda26c7cc14f9db9e7c005b66863fa9139. * fix: USD amounts * wip: send transactions * refactor: bump bridge and use getNativeToken method * chore: bump bridge * refactor: move submit logic to useMutation hook * fix: type mismatches * refactor: white space/comments * refactor: add transaction fee validation * chore: typo * chore: remove console log * refactor: remove duplicated monetary conversion * refactor: remove duplicate code * Revert "refactor: remove duplicate code" This reverts commit bd29f8c5661e327c5285d1020c534dab2deae806. * Revert "refactor: remove duplicated monetary conversion" This reverts commit 5fd3d645eb7d8edc00cfe8ced186d4e2432af9fc. * refactor: use monetaryAmount when constructing transaction * refactor: remove duplicated code for fetching tokens * refactor: default XCM origin * Revert "refactor: remove duplicated code for fetching tokens" This reverts commit 8f31ee8667adcd49f5aaebb7db2f205afb5e9725. * chore: remove comment * chore: fix errors * fix: set default value to empty string to prevent React error * refactor: removed unwanted force validation parameters * refactor: remove redundant method * refactor: add method return type * refactor: add method return type * refactor: correct type error * refactor: fix destFee type error * refactor: remove fees validation and revert destFee return value * chore: remove console log * refactor: remove redundant method * refactor: disable validation on change * chore: remove commented out code * wip: use select component for chain selector * fix: handle chain select functions * refactor: type chain id as ChainName * Revert "refactor: type chain id as ChainName" This reverts commit d05e0128cb4b5ac1d00ac07808ebdf9858739165. * chore: remove unused component files * refactor: remove duplicated transaction logic * fix: make to/from field types more specific * fix: revert yup.custom changes and cast validation * fix: set correct destination chain * refator: handle token data * refactor: add use callback * fix: correct rendering logic * fix: update dependencies * chore: delete unused styles * chore: fix merge issue with transfer form * fix: change validation handling * Revert "fix: change validation handling" This reverts commit c0cb3062aad3540b2afad7d375024d872924a62c. * refactor: only display transfer amount if amount has been entered * chore: config changes * chore: add missing icons * chore: Hydra chain icon * fix: add error text to CTA * Tom/xcm fixes (#1213) * refactor: specify endpoints and remove unnecessary logic * fix: save file before committing * fix: disable refetch * chore: update endpoints * chore: remove log * chore: rename file * chore: add additional acala/karura endpoints --------- Co-authored-by: Rui Simão --- package.json | 3 +- src/assets/icons/ArrowRightCircle.tsx | 13 + src/assets/icons/index.ts | 1 + src/assets/locales/en/translation.json | 1 + src/component-library/Label/Label.style.tsx | 1 + .../Select/SelectTrigger.tsx | 2 +- src/component-library/index.tsx | 2 + src/component-library/theme/theme.ts | 10 +- src/components/AccountSelect/AccountLabel.tsx | 34 ++ src/components/AccountSelect/AccountList.tsx | 58 +++ .../AccountSelect/AccountListModal.tsx | 34 ++ .../AccountSelect/AccountSelect.style.tsx | 68 +++ .../AccountSelect/AccountSelect.tsx | 99 +++++ src/components/AccountSelect/index.tsx | 2 + src/components/index.tsx | 2 + src/config/relay-chains.tsx | 14 +- .../Chains/ChainSelector/index.tsx | 64 --- src/legacy-components/Chains/index.tsx | 24 -- src/lib/form/index.tsx | 6 + src/lib/form/schemas/index.ts | 9 + src/lib/form/schemas/transfers.ts | 54 +++ src/lib/form/use-form.tsx | 4 +- .../CrossChainTransferForm.styles.tsx | 42 ++ .../CrossChainTransferForm.tsx | 293 +++++++++++++ .../components/ChainIcon/ChainIcon.tsx | 6 +- .../components/ChainIcon/icons/Bifrost.tsx | 38 ++ .../components/ChainIcon/icons/Heiko.tsx | 47 ++ .../components/ChainIcon/icons/Hydra.tsx | 32 ++ .../components/ChainIcon/icons/Karura.tsx | 51 +++ .../components/ChainIcon/icons/index.ts | 4 + .../ChainSelect/ChainSelect.style.tsx | 29 ++ .../components/ChainSelect/ChainSelect.tsx | 53 +++ .../components/ChainSelect/index.tsx | 2 + .../components/index.tsx | 6 +- .../Transfer/CrossChainTransferForm/index.tsx | 378 +--------------- src/pages/Transfer/Transfer.style.tsx | 9 + src/pages/Transfer/index.tsx | 112 +---- src/types/chains.d.ts | 10 + src/types/chains.types.ts | 3 - src/utils/hooks/api/xcm/use-xcm-bridge.ts | 182 +++++--- src/utils/hooks/api/xcm/xcm-endpoints.ts | 33 ++ yarn.lock | 402 ++++++++++++------ 42 files changed, 1478 insertions(+), 759 deletions(-) create mode 100644 src/assets/icons/ArrowRightCircle.tsx create mode 100644 src/components/AccountSelect/AccountLabel.tsx create mode 100644 src/components/AccountSelect/AccountList.tsx create mode 100644 src/components/AccountSelect/AccountListModal.tsx create mode 100644 src/components/AccountSelect/AccountSelect.style.tsx create mode 100644 src/components/AccountSelect/AccountSelect.tsx create mode 100644 src/components/AccountSelect/index.tsx delete mode 100644 src/legacy-components/Chains/ChainSelector/index.tsx delete mode 100644 src/legacy-components/Chains/index.tsx create mode 100644 src/lib/form/schemas/transfers.ts create mode 100644 src/pages/Transfer/CrossChainTransferForm/CrossChainTransferForm.styles.tsx create mode 100644 src/pages/Transfer/CrossChainTransferForm/CrossChainTransferForm.tsx create mode 100644 src/pages/Transfer/CrossChainTransferForm/components/ChainIcon/icons/Bifrost.tsx create mode 100644 src/pages/Transfer/CrossChainTransferForm/components/ChainIcon/icons/Heiko.tsx create mode 100644 src/pages/Transfer/CrossChainTransferForm/components/ChainIcon/icons/Hydra.tsx create mode 100644 src/pages/Transfer/CrossChainTransferForm/components/ChainIcon/icons/Karura.tsx create mode 100644 src/pages/Transfer/CrossChainTransferForm/components/ChainSelect/ChainSelect.style.tsx create mode 100644 src/pages/Transfer/CrossChainTransferForm/components/ChainSelect/ChainSelect.tsx create mode 100644 src/pages/Transfer/CrossChainTransferForm/components/ChainSelect/index.tsx create mode 100644 src/pages/Transfer/Transfer.style.tsx create mode 100644 src/types/chains.d.ts delete mode 100644 src/types/chains.types.ts create mode 100644 src/utils/hooks/api/xcm/xcm-endpoints.ts diff --git a/package.json b/package.json index 973c99347b..1362974961 100644 --- a/package.json +++ b/package.json @@ -6,11 +6,12 @@ "@craco/craco": "^6.1.1", "@headlessui/react": "^1.1.1", "@heroicons/react": "^2.0.0", - "@interlay/bridge": "^0.2.4", + "@interlay/bridge": "^0.3.9", "@interlay/interbtc-api": "2.2.2", "@interlay/monetary-js": "0.7.2", "@polkadot/api": "9.14.2", "@polkadot/extension-dapp": "0.44.1", + "@polkadot/react-identicon": "^2.11.1", "@polkadot/ui-keyring": "^2.9.7", "@reach/tooltip": "^0.16.0", "@react-aria/accordion": "^3.0.0-alpha.14", diff --git a/src/assets/icons/ArrowRightCircle.tsx b/src/assets/icons/ArrowRightCircle.tsx new file mode 100644 index 0000000000..8cef0e7841 --- /dev/null +++ b/src/assets/icons/ArrowRightCircle.tsx @@ -0,0 +1,13 @@ +import { forwardRef } from 'react'; + +import { Icon, IconProps } from '@/component-library/Icon'; + +const ArrowRightCircle = forwardRef((props, ref) => ( + + + +)); + +ArrowRightCircle.displayName = 'ArrowRightCircle'; + +export { ArrowRightCircle }; diff --git a/src/assets/icons/index.ts b/src/assets/icons/index.ts index 6393c7b057..bf537097cc 100644 --- a/src/assets/icons/index.ts +++ b/src/assets/icons/index.ts @@ -1,4 +1,5 @@ export { ArrowRight } from './ArrowRight'; +export { ArrowRightCircle } from './ArrowRightCircle'; export { ArrowsUpDown } from './ArrowsUpDown'; export { ArrowTopRightOnSquare } from './ArrowTopRightOnSquare'; export { ChevronDown } from './ChevronDown'; diff --git a/src/assets/locales/en/translation.json b/src/assets/locales/en/translation.json index 439791e0dd..2959b1bd8d 100644 --- a/src/assets/locales/en/translation.json +++ b/src/assets/locales/en/translation.json @@ -602,6 +602,7 @@ }, "forms": { "please_enter_your_field": "Please enter your {{field}}", + "please_select_your_field": "Please select 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}} {{token}}", "amount_must_be_at_most": "Amount to {{action}} must be at most {{amount}}", diff --git a/src/component-library/Label/Label.style.tsx b/src/component-library/Label/Label.style.tsx index 55c1a22cb1..d3209b0dee 100644 --- a/src/component-library/Label/Label.style.tsx +++ b/src/component-library/Label/Label.style.tsx @@ -8,6 +8,7 @@ const StyledLabel = styled.label` font-size: ${theme.text.xs}; color: ${theme.colors.textTertiary}; padding: ${theme.spacing.spacing1} 0; + align-self: flex-start; `; export { StyledLabel }; diff --git a/src/component-library/Select/SelectTrigger.tsx b/src/component-library/Select/SelectTrigger.tsx index 2229fd8f1c..8c2b52e27e 100644 --- a/src/component-library/Select/SelectTrigger.tsx +++ b/src/component-library/Select/SelectTrigger.tsx @@ -8,7 +8,7 @@ import { Sizes } from '../utils/prop-types'; import { StyledChevronDown, StyledTrigger, StyledTriggerValue } from './Select.style'; type Props = { - as: any; + as?: any; size?: Sizes; isOpen?: boolean; hasError?: boolean; diff --git a/src/component-library/index.tsx b/src/component-library/index.tsx index b91ec169da..d385b075d1 100644 --- a/src/component-library/index.tsx +++ b/src/component-library/index.tsx @@ -32,6 +32,8 @@ export type { ModalBodyProps, ModalDividerProps, ModalFooterProps, ModalHeaderPr export { Modal, ModalBody, ModalDivider, ModalFooter, ModalHeader } from './Modal'; export type { NumberInputProps } from './NumberInput'; export { NumberInput } from './NumberInput'; +export type { SelectProps } from './Select'; +export { Item, Select } from './Select'; export type { StackProps } from './Stack'; export { Stack } from './Stack'; export type { SwitchProps } from './Switch'; diff --git a/src/component-library/theme/theme.ts b/src/component-library/theme/theme.ts index 86e1295e36..c7a21c8791 100644 --- a/src/component-library/theme/theme.ts +++ b/src/component-library/theme/theme.ts @@ -510,15 +510,19 @@ const theme = { size: { small: { padding: 'var(--spacing-1)', - text: 'var(--text-s)' + text: 'var(--text-s)', + // TODO: to be determined + maxHeight: 'calc(var(--spacing-6) - 1px)' }, medium: { padding: 'var(--spacing-2)', - text: 'var(--text-base)' + text: 'var(--text-base)', + maxHeight: 'calc(var(--spacing-10) - 1px)' }, large: { padding: 'var(--spacing-5) var(--spacing-2)', - text: 'var(--text-lg)' + text: 'var(--text-lg)', + maxHeight: 'calc(var(--spacing-16) - 1px)' } } } diff --git a/src/components/AccountSelect/AccountLabel.tsx b/src/components/AccountSelect/AccountLabel.tsx new file mode 100644 index 0000000000..965d1128c2 --- /dev/null +++ b/src/components/AccountSelect/AccountLabel.tsx @@ -0,0 +1,34 @@ +import Identicon from '@polkadot/react-identicon'; + +import { FlexProps } from '@/component-library/Flex'; + +import { StyledAccountLabelAddress, StyledAccountLabelName, StyledAccountLabelWrapper } from './AccountSelect.style'; + +type Props = { + isSelected?: boolean; + address: string; + name?: string; +}; + +type InheritAttrs = Omit; + +type AccountLabelProps = Props & InheritAttrs; + +const AccountLabel = ({ isSelected, address, name, ...props }: AccountLabelProps): JSX.Element => ( + + + + {name && ( + + {name} + + )} + + {address} + + + +); + +export { AccountLabel }; +export type { AccountLabelProps }; diff --git a/src/components/AccountSelect/AccountList.tsx b/src/components/AccountSelect/AccountList.tsx new file mode 100644 index 0000000000..ca12d8b20e --- /dev/null +++ b/src/components/AccountSelect/AccountList.tsx @@ -0,0 +1,58 @@ +import { InjectedAccountWithMeta } from '@polkadot/extension-inject/types'; + +import { ListItem, ListProps } from '@/component-library/List'; + +import { AccountLabel } from './AccountLabel'; +import { StyledList } from './AccountSelect.style'; + +type Props = { + items: InjectedAccountWithMeta[]; + selectedAccount?: string; + onSelectionChange?: (account: string) => void; +}; + +type InheritAttrs = Omit; + +type AccountListProps = Props & InheritAttrs; + +const AccountList = ({ items, selectedAccount, onSelectionChange, ...props }: AccountListProps): JSX.Element => { + const handleSelectionChange: ListProps['onSelectionChange'] = (key) => { + const [selectedKey] = [...key]; + + if (!selectedKey) return; + + onSelectionChange?.(selectedKey as string); + }; + + return ( + + {items.map((item) => { + const accountText = item.address; + + const isSelected = selectedAccount === accountText; + + return ( + + + + ); + })} + + ); +}; + +export { AccountList }; +export type { AccountListProps }; diff --git a/src/components/AccountSelect/AccountListModal.tsx b/src/components/AccountSelect/AccountListModal.tsx new file mode 100644 index 0000000000..07b211e2a9 --- /dev/null +++ b/src/components/AccountSelect/AccountListModal.tsx @@ -0,0 +1,34 @@ +import { InjectedAccountWithMeta } from '@polkadot/extension-inject/types'; + +import { Modal, ModalBody, ModalHeader, ModalProps } from '@/component-library/Modal'; + +import { AccountList } from './AccountList'; + +type Props = { + accounts: InjectedAccountWithMeta[]; + onSelectionChange?: (account: string) => void; + selectedAccount?: string; +}; + +type InheritAttrs = Omit; + +type AccountListModalProps = Props & InheritAttrs; + +const AccountListModal = ({ + selectedAccount, + accounts, + onSelectionChange, + ...props +}: AccountListModalProps): JSX.Element => ( + + + Select Account + + + + + +); + +export { AccountListModal }; +export type { AccountListModalProps }; diff --git a/src/components/AccountSelect/AccountSelect.style.tsx b/src/components/AccountSelect/AccountSelect.style.tsx new file mode 100644 index 0000000000..850c26e0fc --- /dev/null +++ b/src/components/AccountSelect/AccountSelect.style.tsx @@ -0,0 +1,68 @@ +import styled from 'styled-components'; + +import { ChevronDown } from '@/assets/icons'; +import { Flex } from '@/component-library/Flex'; +import { List } from '@/component-library/List'; +import { Span } from '@/component-library/Text'; +import { theme } from '@/component-library/theme'; + +type StyledClickableProps = { + $isClickable: boolean; +}; + +type StyledListItemSelectedLabelProps = { + $isSelected: boolean; +}; + +const StyledAccount = styled.span` + font-size: ${theme.text.s}; + color: ${theme.colors.textPrimary}; + overflow: hidden; + text-overflow: ellipsis; +`; + +const StyledAccountSelect = styled(Flex)` + background-color: ${theme.tokenInput.endAdornment.bg}; + border-radius: ${theme.rounded.md}; + font-size: ${theme.text.xl2}; + padding: ${theme.spacing.spacing3}; + cursor: ${({ $isClickable }) => $isClickable && 'pointer'}; + height: 3rem; + width: auto; + overflow: hidden; +`; + +const StyledChevronDown = styled(ChevronDown)` + margin-left: ${theme.spacing.spacing1}; +`; + +const StyledAccountLabelAddress = styled(Span)` + text-overflow: ellipsis; + overflow: hidden; + white-space: nowrap; +`; + +const StyledAccountLabelName = styled(StyledAccountLabelAddress)` + color: ${({ $isSelected }) => + $isSelected ? theme.tokenInput.list.item.selected.text : theme.tokenInput.list.item.default.text}; +`; + +const StyledList = styled(List)` + overflow: auto; + padding: 0 ${theme.modal.body.paddingX} ${theme.modal.body.paddingY} ${theme.modal.body.paddingX}; +`; + +const StyledAccountLabelWrapper = styled(Flex)` + flex-grow: 1; + overflow: hidden; +`; + +export { + StyledAccount, + StyledAccountLabelAddress, + StyledAccountLabelName, + StyledAccountLabelWrapper, + StyledAccountSelect, + StyledChevronDown, + StyledList +}; diff --git a/src/components/AccountSelect/AccountSelect.tsx b/src/components/AccountSelect/AccountSelect.tsx new file mode 100644 index 0000000000..b9712491b5 --- /dev/null +++ b/src/components/AccountSelect/AccountSelect.tsx @@ -0,0 +1,99 @@ +import { InjectedAccountWithMeta } from '@polkadot/extension-inject/types'; +import { useLabel } from '@react-aria/label'; +import { chain, mergeProps } from '@react-aria/utils'; +import { VisuallyHidden } from '@react-aria/visually-hidden'; +import { forwardRef, InputHTMLAttributes, ReactNode, useEffect, useState } from 'react'; + +import { Flex, Label } from '@/component-library'; +import { SelectTrigger } from '@/component-library/Select'; +import { useDOMRef } from '@/component-library/utils/dom'; +import { triggerChangeEvent } from '@/component-library/utils/input'; + +import { AccountLabel } from './AccountLabel'; +import { AccountListModal } from './AccountListModal'; + +const getAccount = (accountValue?: string, accounts?: InjectedAccountWithMeta[]) => + accounts?.find((account) => account.address === accountValue); + +type Props = { + value: string; + defaultValue?: string; + icons?: string[]; + isDisabled?: boolean; + label?: ReactNode; + accounts?: InjectedAccountWithMeta[]; +}; + +type NativeAttrs = Omit & { ref?: any }, keyof Props>; + +type AccountSelectProps = Props & NativeAttrs; + +const AccountSelect = forwardRef( + ({ value: valueProp, defaultValue = '', accounts, disabled, label, className, ...props }, ref): JSX.Element => { + const inputRef = useDOMRef(ref); + + const [isOpen, setOpen] = useState(false); + const [value, setValue] = useState(defaultValue); + + const { fieldProps, labelProps } = useLabel({ ...props, label }); + + useEffect(() => { + if (valueProp === undefined) return; + + setValue(valueProp); + }, [valueProp]); + + const handleAccount = (account: string) => { + triggerChangeEvent(inputRef, account); + setValue(account); + }; + + const handleClose = () => setOpen(false); + + const isDisabled = !accounts?.length || disabled; + + const selectedAccount = getAccount(value, accounts); + + return ( + <> + + {label && } + setOpen(true)} + disabled={isDisabled} + {...mergeProps(fieldProps, { + // MEMO: when the button is blurred, a focus and blur is executed on the input + // so that validation gets triggered. + onBlur: () => { + if (!isOpen) { + inputRef.current?.focus(); + inputRef.current?.blur(); + } + } + })} + > + {selectedAccount && } + + + + + + {accounts && ( + + )} + + ); + } +); + +AccountSelect.displayName = 'AccountSelect'; + +export { AccountSelect }; +export type { AccountSelectProps }; diff --git a/src/components/AccountSelect/index.tsx b/src/components/AccountSelect/index.tsx new file mode 100644 index 0000000000..83aa80ccbd --- /dev/null +++ b/src/components/AccountSelect/index.tsx @@ -0,0 +1,2 @@ +export type { AccountSelectProps } from './AccountSelect'; +export { AccountSelect } from './AccountSelect'; diff --git a/src/components/index.tsx b/src/components/index.tsx index 51545514ef..5149bf3220 100644 --- a/src/components/index.tsx +++ b/src/components/index.tsx @@ -1,3 +1,5 @@ +export type { AccountSelectProps } from './AccountSelect'; +export { AccountSelect } from './AccountSelect'; export type { AuthCTAProps } from './AuthCTA'; export { AuthCTA } from './AuthCTA'; export type { AssetCellProps, BalanceCellProps, CellProps, TableProps } from './DataGrid'; diff --git a/src/config/relay-chains.tsx b/src/config/relay-chains.tsx index d358c50be6..de9302d73f 100644 --- a/src/config/relay-chains.tsx +++ b/src/config/relay-chains.tsx @@ -1,4 +1,9 @@ +import { AcalaAdapter, KaruraAdapter } from '@interlay/bridge/build/adapters/acala'; +import { AstarAdapter } from '@interlay/bridge/build/adapters/astar'; +import { BifrostAdapter } from '@interlay/bridge/build/adapters/bifrost'; +import { HydraAdapter } from '@interlay/bridge/build/adapters/hydradx'; import { InterlayAdapter, KintsugiAdapter } from '@interlay/bridge/build/adapters/interlay'; +import { HeikoAdapter, ParallelAdapter } from '@interlay/bridge/build/adapters/parallel'; import { KusamaAdapter, PolkadotAdapter } from '@interlay/bridge/build/adapters/polkadot'; import { StatemineAdapter, StatemintAdapter } from '@interlay/bridge/build/adapters/statemint'; import { BaseCrossChainAdapter } from '@interlay/bridge/build/base-chain-adapter'; @@ -156,6 +161,10 @@ switch (process.env.REACT_APP_RELAY_CHAIN_NAME) { TRANSACTION_FEE_AMOUNT = newMonetaryAmount(0.2, GOVERNANCE_TOKEN, true); XCM_ADAPTERS = { interlay: new InterlayAdapter(), + acala: new AcalaAdapter(), + astar: new AstarAdapter(), + hydra: new HydraAdapter(), + parallel: new ParallelAdapter(), polkadot: new PolkadotAdapter(), statemint: new StatemintAdapter() }; @@ -199,7 +208,10 @@ switch (process.env.REACT_APP_RELAY_CHAIN_NAME) { XCM_ADAPTERS = { kintsugi: new KintsugiAdapter(), kusama: new KusamaAdapter(), - statemine: new StatemineAdapter() + karura: new KaruraAdapter(), + statemine: new StatemineAdapter(), + bifrost: new BifrostAdapter(), + heiko: new HeikoAdapter() }; SS58_PREFIX = 2; break; diff --git a/src/legacy-components/Chains/ChainSelector/index.tsx b/src/legacy-components/Chains/ChainSelector/index.tsx deleted file mode 100644 index aa3c8d0e7c..0000000000 --- a/src/legacy-components/Chains/ChainSelector/index.tsx +++ /dev/null @@ -1,64 +0,0 @@ -import clsx from 'clsx'; - -import Select, { - SELECT_VARIANTS, - SelectBody, - SelectButton, - SelectCheck, - SelectLabel, - SelectOption, - SelectOptions, - SelectText -} from '@/legacy-components/Select'; -import { XCMChains } from '@/types/chains.types'; - -interface ChainOption { - type: XCMChains; - name: string; - icon: JSX.Element; -} - -interface Props { - chainOptions: Array; - selectedChain: ChainOption | undefined; - label: string; - onChange?: (chain: ChainOption) => void; -} - -const ChainSelector = ({ chainOptions, selectedChain, label, onChange }: Props): JSX.Element => ( - -); - -export type { ChainOption }; -export default ChainSelector; diff --git a/src/legacy-components/Chains/index.tsx b/src/legacy-components/Chains/index.tsx deleted file mode 100644 index 16873ecdbf..0000000000 --- a/src/legacy-components/Chains/index.tsx +++ /dev/null @@ -1,24 +0,0 @@ -import ChainSelector, { ChainOption } from './ChainSelector'; - -interface Props { - label: string; - chainOptions: Array | undefined; - onChange?: (chain: ChainOption) => void; - selectedChain: ChainOption | undefined; -} - -const Chains = ({ onChange, chainOptions, label, selectedChain }: Props): JSX.Element | null => { - if (!selectedChain || !chainOptions) { - return null; - } - - return ( -
- -
- ); -}; - -export type { ChainOption }; - -export default Chains; diff --git a/src/lib/form/index.tsx b/src/lib/form/index.tsx index 60fbf3b63b..af76026d2e 100644 --- a/src/lib/form/index.tsx +++ b/src/lib/form/index.tsx @@ -1,3 +1,9 @@ +export type { + CreateVaultFormData, + CrossChainTransferFormData, + DepositLiquidityPoolFormData, + LoanFormData +} from './schemas'; export * from './schemas'; export type { FormErrors } from './use-form'; export { useForm } from './use-form'; diff --git a/src/lib/form/schemas/index.ts b/src/lib/form/schemas/index.ts index 1d584fcd5d..a792ef0fbe 100644 --- a/src/lib/form/schemas/index.ts +++ b/src/lib/form/schemas/index.ts @@ -15,5 +15,14 @@ export { SwapErrorMessage, swapSchema } from './swap'; +export type { CrossChainTransferFormData, CrossChainTransferValidationParams } from './transfers'; +export { + CROSS_CHAIN_TRANSFER_AMOUNT_FIELD, + CROSS_CHAIN_TRANSFER_FROM_FIELD, + CROSS_CHAIN_TRANSFER_TO_ACCOUNT_FIELD, + CROSS_CHAIN_TRANSFER_TO_FIELD, + CROSS_CHAIN_TRANSFER_TOKEN_FIELD, + crossChainTransferSchema +} from './transfers'; export type { CreateVaultFormData } from './vaults'; export { CREATE_VAULT_DEPOSIT_FIELD, createVaultSchema } from './vaults'; diff --git a/src/lib/form/schemas/transfers.ts b/src/lib/form/schemas/transfers.ts new file mode 100644 index 0000000000..a6fe5c5f47 --- /dev/null +++ b/src/lib/form/schemas/transfers.ts @@ -0,0 +1,54 @@ +import { ChainName } from '@interlay/bridge'; +import { TFunction } from 'react-i18next'; + +import yup, { MaxAmountValidationParams, MinAmountValidationParams } from '../yup.custom'; + +const CROSS_CHAIN_TRANSFER_FROM_FIELD = 'transfer-from'; +const CROSS_CHAIN_TRANSFER_TO_FIELD = 'transfer-to'; +const CROSS_CHAIN_TRANSFER_AMOUNT_FIELD = 'transfer-amount'; +const CROSS_CHAIN_TRANSFER_TOKEN_FIELD = 'transfer-token'; +const CROSS_CHAIN_TRANSFER_TO_ACCOUNT_FIELD = 'transfer-account'; + +type CrossChainTransferFormData = { + [CROSS_CHAIN_TRANSFER_FROM_FIELD]?: ChainName; + [CROSS_CHAIN_TRANSFER_TO_FIELD]?: ChainName; + [CROSS_CHAIN_TRANSFER_AMOUNT_FIELD]?: string; + [CROSS_CHAIN_TRANSFER_TOKEN_FIELD]?: string; + [CROSS_CHAIN_TRANSFER_TO_ACCOUNT_FIELD]?: string; +}; + +type CrossChainTransferValidationParams = { + [CROSS_CHAIN_TRANSFER_AMOUNT_FIELD]: Partial & Partial; +}; + +// MEMO: until now, only CROSS_CHAIN_TRANSFER_AMOUNT_FIELD needs validation +const crossChainTransferSchema = (params: CrossChainTransferValidationParams, t: TFunction): yup.ObjectSchema => + yup.object().shape({ + [CROSS_CHAIN_TRANSFER_AMOUNT_FIELD]: yup + .string() + .requiredAmount('transfer') + .maxAmount(params[CROSS_CHAIN_TRANSFER_AMOUNT_FIELD] as MaxAmountValidationParams) + .minAmount(params[CROSS_CHAIN_TRANSFER_AMOUNT_FIELD] as MinAmountValidationParams, 'transfer'), + [CROSS_CHAIN_TRANSFER_FROM_FIELD]: yup + .string() + .required(t('forms.please_enter_your_field', { field: 'source chain' })), + [CROSS_CHAIN_TRANSFER_TO_FIELD]: yup + .string() + .required(t('forms.please_enter_your_field', { field: 'destination chain' })), + [CROSS_CHAIN_TRANSFER_TO_ACCOUNT_FIELD]: yup + .string() + .required(t('forms.please_enter_your_field', { field: 'destination' })), + [CROSS_CHAIN_TRANSFER_TOKEN_FIELD]: yup + .string() + .required(t('forms.please_select_your_field', { field: 'transfer token' })) + }); + +export { + CROSS_CHAIN_TRANSFER_AMOUNT_FIELD, + CROSS_CHAIN_TRANSFER_FROM_FIELD, + CROSS_CHAIN_TRANSFER_TO_ACCOUNT_FIELD, + CROSS_CHAIN_TRANSFER_TO_FIELD, + CROSS_CHAIN_TRANSFER_TOKEN_FIELD, + crossChainTransferSchema +}; +export type { CrossChainTransferFormData, CrossChainTransferValidationParams }; diff --git a/src/lib/form/use-form.tsx b/src/lib/form/use-form.tsx index 48c668f13e..7e62e97bd9 100644 --- a/src/lib/form/use-form.tsx +++ b/src/lib/form/use-form.tsx @@ -15,7 +15,7 @@ type GetFieldProps = ( withErrorMessage?: boolean ) => FieldInputProps & { errorMessage?: string | string[] }; -type UseFormAgrs = FormikConfig & { +type UseFormArgs = FormikConfig & { disableValidation?: boolean; getFieldProps?: GetFieldProps; }; @@ -25,7 +25,7 @@ const useForm = ({ validationSchema, disableValidation, ...args -}: UseFormAgrs) => { +}: UseFormArgs) => { const { t } = useTranslation(); const { validateForm, values, getFieldProps: getFormikFieldProps, ...formik } = useFormik({ ...args, diff --git a/src/pages/Transfer/CrossChainTransferForm/CrossChainTransferForm.styles.tsx b/src/pages/Transfer/CrossChainTransferForm/CrossChainTransferForm.styles.tsx new file mode 100644 index 0000000000..1c1ee33b0b --- /dev/null +++ b/src/pages/Transfer/CrossChainTransferForm/CrossChainTransferForm.styles.tsx @@ -0,0 +1,42 @@ +import styled from 'styled-components'; + +import { ArrowRightCircle } from '@/assets/icons'; +import { Dl, Flex, theme } from '@/component-library'; + +import { ChainSelect } from './components'; + +const StyledDl = styled(Dl)` + background-color: ${theme.card.bg.secondary}; + padding: ${theme.spacing.spacing4}; + font-size: ${theme.text.xs}; + border-radius: ${theme.rounded.rg}; +`; + +const StyledArrowRightCircle = styled(ArrowRightCircle)` + transform: rotate(90deg); + align-self: center; + + @media (min-width: 30em) { + transform: rotate(0deg); + margin-top: 1.75rem; + } +`; + +const ChainSelectSection = styled(Flex)` + flex-direction: column; + + @media (min-width: 30em) { + flex-direction: row; + gap: ${theme.spacing.spacing4}; + } +`; + +const StyledSourceChainSelect = styled(ChainSelect)` + margin-bottom: ${theme.spacing.spacing3}; + + @media (min-width: 30em) { + margin-bottom: 0; + } +`; + +export { ChainSelectSection, StyledArrowRightCircle, StyledDl, StyledSourceChainSelect }; diff --git a/src/pages/Transfer/CrossChainTransferForm/CrossChainTransferForm.tsx b/src/pages/Transfer/CrossChainTransferForm/CrossChainTransferForm.tsx new file mode 100644 index 0000000000..85b5cd0eb1 --- /dev/null +++ b/src/pages/Transfer/CrossChainTransferForm/CrossChainTransferForm.tsx @@ -0,0 +1,293 @@ +import { FixedPointNumber } from '@acala-network/sdk-core'; +import { ChainName, CrossChainTransferParams } from '@interlay/bridge'; +import { newMonetaryAmount } from '@interlay/interbtc-api'; +import { web3FromAddress } from '@polkadot/extension-dapp'; +import { mergeProps } from '@react-aria/utils'; +import { ChangeEventHandler, Key, useCallback, useEffect, useState } from 'react'; +import { useTranslation } from 'react-i18next'; +import { useMutation } from 'react-query'; +import { toast } from 'react-toastify'; + +import { convertMonetaryAmountToValueInUSD, newSafeMonetaryAmount } from '@/common/utils/utils'; +import { Dd, DlGroup, Dt, Flex, LoadingSpinner, TokenInput } from '@/component-library'; +import { AccountSelect, AuthCTA } from '@/components'; +import { + CROSS_CHAIN_TRANSFER_AMOUNT_FIELD, + CROSS_CHAIN_TRANSFER_FROM_FIELD, + CROSS_CHAIN_TRANSFER_TO_ACCOUNT_FIELD, + CROSS_CHAIN_TRANSFER_TO_FIELD, + CROSS_CHAIN_TRANSFER_TOKEN_FIELD, + CrossChainTransferFormData, + crossChainTransferSchema, + CrossChainTransferValidationParams, + isFormDisabled, + useForm +} from '@/lib/form'; +import { useSubstrateSecureState } from '@/lib/substrate'; +import { Chains } from '@/types/chains'; +import { submitExtrinsic } from '@/utils/helpers/extrinsic'; +import { getTokenPrice } from '@/utils/helpers/prices'; +import { useGetCurrencies } from '@/utils/hooks/api/use-get-currencies'; +import { useGetPrices } from '@/utils/hooks/api/use-get-prices'; +import { useXCMBridge, XCMTokenData } from '@/utils/hooks/api/xcm/use-xcm-bridge'; +import useAccountId from '@/utils/hooks/use-account-id'; + +import { ChainSelect } from './components'; +import { + ChainSelectSection, + StyledArrowRightCircle, + StyledDl, + StyledSourceChainSelect +} from './CrossChainTransferForm.styles'; + +const CrossChainTransferForm = (): JSX.Element => { + const [destinationChains, setDestinationChains] = useState([]); + const [transferableTokens, setTransferableTokens] = useState([]); + const [currentToken, setCurrentToken] = useState(); + + const prices = useGetPrices(); + const { t } = useTranslation(); + const { getCurrencyFromTicker } = useGetCurrencies(true); + + const accountId = useAccountId(); + const { accounts } = useSubstrateSecureState(); + + const { data, getDestinationChains, originatingChains, getAvailableTokens } = useXCMBridge(); + + const schema: CrossChainTransferValidationParams = { + [CROSS_CHAIN_TRANSFER_AMOUNT_FIELD]: { + minAmount: currentToken + ? newMonetaryAmount(currentToken.minTransferAmount, getCurrencyFromTicker(currentToken.value), true) + : undefined, + maxAmount: currentToken + ? newMonetaryAmount(currentToken.balance, getCurrencyFromTicker(currentToken.value), true) + : undefined + } + }; + + const mutateXcmTransfer = async (formData: CrossChainTransferFormData) => { + if (!data || !formData || !currentToken) return; + + const { signer } = await web3FromAddress(formData[CROSS_CHAIN_TRANSFER_TO_ACCOUNT_FIELD] as string); + const adapter = data.bridge.findAdapter(formData[CROSS_CHAIN_TRANSFER_FROM_FIELD] as ChainName); + const apiPromise = data.provider.getApiPromise(formData[CROSS_CHAIN_TRANSFER_FROM_FIELD] as string); + + apiPromise.setSigner(signer); + adapter.setApi(apiPromise); + + const transferAmount = newMonetaryAmount( + form.values[CROSS_CHAIN_TRANSFER_AMOUNT_FIELD] || 0, + getCurrencyFromTicker(currentToken.value), + true + ); + + const transferAmountString = transferAmount.toString(true); + const transferAmountDecimals = transferAmount.currency.decimals; + + const tx = adapter.createTx({ + amount: FixedPointNumber.fromInner(transferAmountString, transferAmountDecimals), + to: formData[CROSS_CHAIN_TRANSFER_TO_FIELD], + token: formData[CROSS_CHAIN_TRANSFER_TOKEN_FIELD], + address: formData[CROSS_CHAIN_TRANSFER_TO_ACCOUNT_FIELD] + } as CrossChainTransferParams); + + await submitExtrinsic({ extrinsic: tx }); + }; + + const handleSubmit = (formData: CrossChainTransferFormData) => { + xcmTransferMutation.mutate(formData); + }; + + const form = useForm({ + initialValues: { + [CROSS_CHAIN_TRANSFER_AMOUNT_FIELD]: '', + [CROSS_CHAIN_TRANSFER_TOKEN_FIELD]: '', + [CROSS_CHAIN_TRANSFER_TO_ACCOUNT_FIELD]: accountId?.toString() || '' + }, + onSubmit: handleSubmit, + validationSchema: crossChainTransferSchema(schema, t) + }); + + const xcmTransferMutation = useMutation(mutateXcmTransfer, { + onSuccess: async () => { + toast.success('Transfer successful'); + + setTokenData(form.values[CROSS_CHAIN_TRANSFER_TO_FIELD] as ChainName); + form.setFieldValue(CROSS_CHAIN_TRANSFER_AMOUNT_FIELD, ''); + }, + onError: (err) => { + toast.error(err.message); + } + }); + + const handleOriginatingChainChange = (chain: ChainName, name: string) => { + form.setFieldValue(name, chain); + + const destinationChains = getDestinationChains(chain); + + setDestinationChains(destinationChains); + form.setFieldValue(CROSS_CHAIN_TRANSFER_TO_FIELD, destinationChains[0].id); + }; + + const handleDestinationChainChange = async (chain: ChainName, name: string) => { + if (!accountId) return; + + form.setFieldValue(name, chain); + + setTokenData(chain); + }; + + const handleTickerChange = (ticker: string, name: string) => { + form.setFieldValue(name, ticker); + setCurrentToken(transferableTokens.find((token) => token.value === ticker)); + }; + + const handleDestinationAccountChange: ChangeEventHandler = (e) => { + form.setFieldValue(CROSS_CHAIN_TRANSFER_TO_ACCOUNT_FIELD, e.target.value); + }; + + const setTokenData = useCallback( + async (destination: ChainName) => { + if (!accountId || !form) return; + + const tokens = await getAvailableTokens( + form.values[CROSS_CHAIN_TRANSFER_FROM_FIELD] as ChainName, + destination, + accountId.toString(), + form.values[CROSS_CHAIN_TRANSFER_TO_ACCOUNT_FIELD] as string + ); + + if (!tokens) return; + + setTransferableTokens(tokens); + + // Update token data if selected token exists in new data + const token = tokens.find((token) => token.value === currentToken?.value) || tokens[0]; + + setCurrentToken(token); + form.setFieldValue(CROSS_CHAIN_TRANSFER_TOKEN_FIELD, token.value); + }, + [accountId, currentToken, form, getAvailableTokens] + ); + + const transferMonetaryAmount = currentToken + ? newSafeMonetaryAmount( + form.values[CROSS_CHAIN_TRANSFER_AMOUNT_FIELD] || 0, + getCurrencyFromTicker(currentToken.value), + true + ) + : 0; + + const valueUSD = transferMonetaryAmount + ? convertMonetaryAmountToValueInUSD( + transferMonetaryAmount, + getTokenPrice(prices, currentToken?.value as string)?.usd + ) + : 0; + + const isCTADisabled = isFormDisabled(form) || form.values[CROSS_CHAIN_TRANSFER_AMOUNT_FIELD] === ''; + const amountShouldValidate = form.values[CROSS_CHAIN_TRANSFER_AMOUNT_FIELD] !== ''; + + useEffect(() => { + if (!originatingChains?.length) return; + + // This prevents a render loop caused by setFieldValue + if (form.values[CROSS_CHAIN_TRANSFER_FROM_FIELD]) return; + + const destinationChains = getDestinationChains(originatingChains[0].id); + + form.setFieldValue(CROSS_CHAIN_TRANSFER_FROM_FIELD, originatingChains[0].id); + form.setFieldValue(CROSS_CHAIN_TRANSFER_TO_FIELD, destinationChains[0].id); + + setDestinationChains(destinationChains); + + // eslint-disable-next-line react-hooks/exhaustive-deps + }, [originatingChains]); + + useEffect(() => { + if (!destinationChains?.length) return; + if (!accountId) return; + + setTokenData(destinationChains[0].id); + // eslint-disable-next-line react-hooks/exhaustive-deps + }, [accountId, destinationChains]); + + if (!originatingChains || !destinationChains || !transferableTokens.length) { + return ( + + + + ); + } + + return ( +
+ + + + handleOriginatingChainChange(chain as ChainName, CROSS_CHAIN_TRANSFER_FROM_FIELD) + } + {...mergeProps(form.getFieldProps(CROSS_CHAIN_TRANSFER_FROM_FIELD, false), { + onChange: handleOriginatingChainChange + })} + /> + + + handleDestinationChainChange(chain as ChainName, CROSS_CHAIN_TRANSFER_TO_FIELD) + } + {...mergeProps(form.getFieldProps(CROSS_CHAIN_TRANSFER_TO_FIELD, false), { + onChange: handleDestinationChainChange + })} + /> + +
+ + handleTickerChange(ticker as string, CROSS_CHAIN_TRANSFER_TOKEN_FIELD), + items: transferableTokens + })} + {...mergeProps(form.getFieldProps(CROSS_CHAIN_TRANSFER_AMOUNT_FIELD, amountShouldValidate))} + /> +
+ + + +
+ Origin chain transfer fee +
+
{currentToken?.originFee}
+
+ +
+ Destination chain transfer fee estimate +
+
{`${currentToken?.destFee.toString()} ${currentToken?.value}`}
+
+
+ + {isCTADisabled ? 'Enter transfer amount' : t('transfer')} + +
+
+ ); +}; + +export default CrossChainTransferForm; diff --git a/src/pages/Transfer/CrossChainTransferForm/components/ChainIcon/ChainIcon.tsx b/src/pages/Transfer/CrossChainTransferForm/components/ChainIcon/ChainIcon.tsx index 3a6611d6e2..339e83d336 100644 --- a/src/pages/Transfer/CrossChainTransferForm/components/ChainIcon/ChainIcon.tsx +++ b/src/pages/Transfer/CrossChainTransferForm/components/ChainIcon/ChainIcon.tsx @@ -3,12 +3,16 @@ 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'; +import { BIFROST, HEIKO, HYDRA, INTERLAY, KARURA, KINTSUGI, KUSAMA, POLKADOT, STATEMINE, STATEMINT } from './icons'; type ChainComponent = ForwardRefExoticComponent>; const chainsIcon: Record = { + BIFROST, + HEIKO, + HYDRA, INTERLAY, + KARURA, KINTSUGI, KUSAMA, POLKADOT, diff --git a/src/pages/Transfer/CrossChainTransferForm/components/ChainIcon/icons/Bifrost.tsx b/src/pages/Transfer/CrossChainTransferForm/components/ChainIcon/icons/Bifrost.tsx new file mode 100644 index 0000000000..6abf1e24bd --- /dev/null +++ b/src/pages/Transfer/CrossChainTransferForm/components/ChainIcon/icons/Bifrost.tsx @@ -0,0 +1,38 @@ +import { forwardRef } from 'react'; + +import { Icon, IconProps } from '@/component-library/Icon'; + +const BIFROST = forwardRef((props, ref) => ( + + BIFROST + + + + + + + + + + + + + + + + + + + + + + +)); + +BIFROST.displayName = 'KINTSUGI'; + +export { BIFROST }; diff --git a/src/pages/Transfer/CrossChainTransferForm/components/ChainIcon/icons/Heiko.tsx b/src/pages/Transfer/CrossChainTransferForm/components/ChainIcon/icons/Heiko.tsx new file mode 100644 index 0000000000..39afe777c3 --- /dev/null +++ b/src/pages/Transfer/CrossChainTransferForm/components/ChainIcon/icons/Heiko.tsx @@ -0,0 +1,47 @@ +import { forwardRef } from 'react'; + +import { Icon, IconProps } from '@/component-library/Icon'; + +const HEIKO = forwardRef((props, ref) => ( + + HEIKO + + + + + + + + + + + + + + + + + +)); + +HEIKO.displayName = 'HEIKO'; + +export { HEIKO }; diff --git a/src/pages/Transfer/CrossChainTransferForm/components/ChainIcon/icons/Hydra.tsx b/src/pages/Transfer/CrossChainTransferForm/components/ChainIcon/icons/Hydra.tsx new file mode 100644 index 0000000000..58f99aa0e3 --- /dev/null +++ b/src/pages/Transfer/CrossChainTransferForm/components/ChainIcon/icons/Hydra.tsx @@ -0,0 +1,32 @@ +import { forwardRef } from 'react'; + +import { Icon, IconProps } from '@/component-library/Icon'; + +const HYDRA = forwardRef((props, ref) => ( + + HYDRA + + + + + + + + + + + + + + + +)); + +HYDRA.displayName = 'INTERLAY'; + +export { HYDRA }; diff --git a/src/pages/Transfer/CrossChainTransferForm/components/ChainIcon/icons/Karura.tsx b/src/pages/Transfer/CrossChainTransferForm/components/ChainIcon/icons/Karura.tsx new file mode 100644 index 0000000000..2306754c2f --- /dev/null +++ b/src/pages/Transfer/CrossChainTransferForm/components/ChainIcon/icons/Karura.tsx @@ -0,0 +1,51 @@ +import { forwardRef } from 'react'; + +import { Icon, IconProps } from '@/component-library/Icon'; + +const KARURA = forwardRef((props, ref) => ( + + KARURA + + + + + + + + + + + + + + + + + + + +)); + +KARURA.displayName = 'KINTSUGI'; + +export { KARURA }; diff --git a/src/pages/Transfer/CrossChainTransferForm/components/ChainIcon/icons/index.ts b/src/pages/Transfer/CrossChainTransferForm/components/ChainIcon/icons/index.ts index df6ed50da9..e5d13a7014 100644 --- a/src/pages/Transfer/CrossChainTransferForm/components/ChainIcon/icons/index.ts +++ b/src/pages/Transfer/CrossChainTransferForm/components/ChainIcon/icons/index.ts @@ -1,4 +1,8 @@ +export { BIFROST } from './Bifrost'; +export { HEIKO } from './Heiko'; +export { HYDRA } from './Hydra'; export { INTERLAY } from './Interlay'; +export { KARURA } from './Karura'; export { KINTSUGI } from './Kintsugi'; export { KUSAMA } from './Kusama'; export { POLKADOT } from './Polkadot'; diff --git a/src/pages/Transfer/CrossChainTransferForm/components/ChainSelect/ChainSelect.style.tsx b/src/pages/Transfer/CrossChainTransferForm/components/ChainSelect/ChainSelect.style.tsx new file mode 100644 index 0000000000..fe740a65eb --- /dev/null +++ b/src/pages/Transfer/CrossChainTransferForm/components/ChainSelect/ChainSelect.style.tsx @@ -0,0 +1,29 @@ +import styled from 'styled-components'; + +import { Flex } from '@/component-library/Flex'; +import { Span } from '@/component-library/Text'; +import { theme } from '@/component-library/theme'; + +type StyledListItemSelectedLabelProps = { + $isSelected: boolean; +}; + +const StyledChain = styled.span` + font-size: ${theme.text.s}; + color: ${theme.colors.textPrimary}; + overflow: hidden; + text-overflow: ellipsis; +`; + +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 StyledListChainWrapper = styled(Flex)` + overflow: hidden; +`; + +export { StyledChain, StyledListChainWrapper, StyledListItemLabel }; diff --git a/src/pages/Transfer/CrossChainTransferForm/components/ChainSelect/ChainSelect.tsx b/src/pages/Transfer/CrossChainTransferForm/components/ChainSelect/ChainSelect.tsx new file mode 100644 index 0000000000..1506d894e6 --- /dev/null +++ b/src/pages/Transfer/CrossChainTransferForm/components/ChainSelect/ChainSelect.tsx @@ -0,0 +1,53 @@ +import { Flex } from '@/component-library'; +import { Item, Select, SelectProps } from '@/component-library'; +import { useSelectModalContext } from '@/component-library/Select/SelectModalContext'; +import { ChainData } from '@/types/chains'; + +import { ChainIcon } from '../ChainIcon'; +import { StyledChain, StyledListChainWrapper, StyledListItemLabel } from './ChainSelect.style'; + +type ChainSelectProps = Omit, 'children' | 'type'>; + +const ListItem = ({ data }: { data: ChainData }) => { + const isSelected = useSelectModalContext().selectedItem?.key === data.id; + + return ( + + + + {data.display} + + + ); +}; + +const Value = ({ data }: { data: ChainData }) => ( + + + {data.display} + +); + +const ChainSelect = ({ ...props }: ChainSelectProps): JSX.Element => { + return ( + + + {...props} + type='modal' + renderValue={(item) => } + modalTitle='Select Token' + > + {(data: ChainData) => ( + + + + )} + + + ); +}; + +ChainSelect.displayName = 'ChainSelect'; + +export { ChainSelect }; +export type { ChainSelectProps }; diff --git a/src/pages/Transfer/CrossChainTransferForm/components/ChainSelect/index.tsx b/src/pages/Transfer/CrossChainTransferForm/components/ChainSelect/index.tsx new file mode 100644 index 0000000000..2e2851d120 --- /dev/null +++ b/src/pages/Transfer/CrossChainTransferForm/components/ChainSelect/index.tsx @@ -0,0 +1,2 @@ +export type { ChainSelectProps } from './ChainSelect'; +export { ChainSelect } from './ChainSelect'; diff --git a/src/pages/Transfer/CrossChainTransferForm/components/index.tsx b/src/pages/Transfer/CrossChainTransferForm/components/index.tsx index 98ff9e319b..6cb7e0e8e5 100644 --- a/src/pages/Transfer/CrossChainTransferForm/components/index.tsx +++ b/src/pages/Transfer/CrossChainTransferForm/components/index.tsx @@ -1,4 +1,4 @@ -import { ChainIcon, ChainIconProps } from './ChainIcon'; +import { ChainSelect, ChainSelectProps } from './ChainSelect'; -export { ChainIcon }; -export type { ChainIconProps }; +export { ChainSelect }; +export type { ChainSelectProps }; diff --git a/src/pages/Transfer/CrossChainTransferForm/index.tsx b/src/pages/Transfer/CrossChainTransferForm/index.tsx index 0996fe956d..b2417c4fb7 100644 --- a/src/pages/Transfer/CrossChainTransferForm/index.tsx +++ b/src/pages/Transfer/CrossChainTransferForm/index.tsx @@ -1,377 +1,3 @@ -import { FixedPointNumber } from '@acala-network/sdk-core'; -import { BasicToken, CrossChainTransferParams } from '@interlay/bridge'; -import { CurrencyExt, DefaultTransactionAPI, newMonetaryAmount } from '@interlay/interbtc-api'; -import { MonetaryAmount } from '@interlay/monetary-js'; -import { ApiPromise } from '@polkadot/api'; -import { web3FromAddress } from '@polkadot/extension-dapp'; -import Big from 'big.js'; -import * as React from 'react'; -import { useEffect } from 'react'; -import { withErrorBoundary } from 'react-error-boundary'; -import { useForm } from 'react-hook-form'; -import { useTranslation } from 'react-i18next'; -import { useSelector } from 'react-redux'; -import { toast } from 'react-toastify'; -import { firstValueFrom } from 'rxjs'; +import CrossChainTransferForm from './CrossChainTransferForm'; -import { ParachainStatus, StoreType } from '@/common/types/util.types'; -import { displayMonetaryAmountInUSDFormat } from '@/common/utils/utils'; -import { AuthCTA } from '@/components'; -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 TokenField from '@/legacy-components/TokenField'; -import { KeyringPair, useSubstrateSecureState } from '@/lib/substrate'; -import STATUSES from '@/utils/constants/statuses'; -import { getExtrinsicStatus } from '@/utils/helpers/extrinsic'; -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 = { - [TRANSFER_AMOUNT]: string; -}; - -const CrossChainTransferForm = (): JSX.Element => { - const [fromChains, setFromChains] = React.useState | undefined>(undefined); - const [fromChain, setFromChain] = React.useState(undefined); - const [toChains, setToChains] = React.useState | undefined>(undefined); - const [toChain, setToChain] = React.useState(undefined); - const [transferableBalance, setTransferableBalance] = React.useState(undefined); - const [destination, setDestination] = React.useState(undefined); - const [submitStatus, setSubmitStatus] = React.useState(STATUSES.IDLE); - const [submitError, setSubmitError] = React.useState(null); - const [approxUsdValue, setApproxUsdValue] = React.useState('0'); - const [currency, setCurrency] = React.useState(undefined); - - // TODO: this will need to be refactored when we support multiple currencies - // per channel, but so will the UI so better to handle this then. - const { t } = useTranslation(); - const prices = useGetPrices(); - - const { XCMBridge, XCMProvider } = useXCMBridge(); - - const { - register, - handleSubmit, - formState: { errors }, - reset, - setValue, - trigger - } = useForm({ - mode: 'onChange' - }); - - const { selectedAccount } = useSubstrateSecureState(); - const { parachainStatus } = useSelector((state: StoreType) => state.general); - - useEffect(() => { - if (!XCMBridge) return; - if (!fromChain) return; - if (!toChain) return; - // TODO: This handles a race condition. Will need to be fixed properly - // when supporting USDT - if (fromChain.name === toChain.name) return; - - const tokens = XCMBridge.router.getAvailableTokens({ from: fromChain.type, to: toChain.type }); - - const supportedCurrency = XCMBridge.findAdapter(fromChain.type).getToken(tokens[0], fromChain.type); - - setCurrency(supportedCurrency); - }, [fromChain, toChain, XCMBridge]); - - useEffect(() => { - if (!XCMBridge) return; - if (!fromChain) return; - if (!toChain) return; - if (!selectedAccount) return; - if (!currency) return; - if (!destination) return; - // TODO: This handles a race condition. Will need to be fixed properly - // when supporting USDT - if (toChain.type === fromChain.type) return; - - const getMaxTransferrable = async () => { - // TODO: Resolve type issue caused by version mismatch - // and remove casting to `any` - const inputConfigs: any = await firstValueFrom( - XCMBridge.findAdapter(fromChain.type).subscribeInputConfigs({ - to: toChain?.type, - token: currency.symbol, - address: destination.address, - signer: selectedAccount.address - }) as any - ); - - const maxInputToBig = Big(inputConfigs.maxInput.toString()); - - // Never show less than zero - const transferableBalance = inputConfigs.maxInput < inputConfigs.minInput ? 0 : maxInputToBig; - - setTransferableBalance(newMonetaryAmount(transferableBalance, (currency as unknown) as CurrencyExt, true)); - }; - - getMaxTransferrable(); - }, [currency, fromChain, toChain, selectedAccount, destination, XCMBridge]); - - useEffect(() => { - if (!XCMBridge) return; - if (!XCMProvider) return; - - const availableFromChains: Array = XCMBridge.adapters.map((adapter: any) => { - return { - type: adapter.chain.id, - name: adapter.chain.display, - icon: - }; - }); - - setFromChains(availableFromChains); - setFromChain(availableFromChains[0]); - }, [XCMBridge, XCMProvider]); - - useEffect(() => { - if (!XCMBridge) return; - if (!fromChain) return; - - const destinationChains = XCMBridge.router.getDestinationChains({ from: fromChain.type }); - - const availableToChains = destinationChains.map((chain: any) => { - return { - type: chain.id, - name: chain.display, - icon: - }; - }); - - setToChains(availableToChains); - setToChain(availableToChains[0]); - }, [fromChain, XCMBridge]); - - const onSubmit = async (data: CrossChainTransferFormData) => { - if (!selectedAccount) return; - if (!destination) return; - - try { - setSubmitStatus(STATUSES.PENDING); - - if (!XCMBridge || !fromChain || !toChain) return; - - const sendTransaction = async () => { - const { signer } = await web3FromAddress(selectedAccount.address.toString()); - - const adapter = XCMBridge.findAdapter(fromChain.type); - - const apiPromise = (XCMProvider.getApiPromise(fromChain.type) as unknown) as ApiPromise; - - apiPromise.setSigner(signer); - - // TODO: Version mismatch with ApiPromise type. This should be inferred. - adapter.setApi(apiPromise as any); - - const transferAmount = new MonetaryAmount((currency as unknown) as CurrencyExt, data[TRANSFER_AMOUNT]); - const transferAmountString = transferAmount.toString(true); - const transferAmountDecimals = transferAmount.currency.decimals; - - // TODO: Transaction is in promise form - const tx: any = adapter.createTx({ - amount: FixedPointNumber.fromInner(transferAmountString, transferAmountDecimals), - to: toChain.type, - token: currency?.symbol, - address: destination.address - } as CrossChainTransferParams); - - const inBlockStatus = getExtrinsicStatus('InBlock'); - - await DefaultTransactionAPI.sendLogged(apiPromise, selectedAccount.address, tx, undefined, inBlockStatus); - }; - - await sendTransaction(); - - setSubmitStatus(STATUSES.RESOLVED); - } catch (error) { - setSubmitStatus(STATUSES.REJECTED); - setSubmitError(error); - } - }; - - const handleUpdateUsdAmount = (value: string) => { - if (!value) return; - - const tokenAmount = newMonetaryAmount(value, (currency as unknown) as CurrencyExt, true); - - const usd = currency - ? displayMonetaryAmountInUSDFormat(tokenAmount, getTokenPrice(prices, currency.symbol)?.usd) - : '0'; - - setApproxUsdValue(usd); - }; - - const validateTransferAmount = async (value: string) => { - if (!toChain) return; - if (!fromChain) return; - if (!destination) return; - if (!selectedAccount) return; - if (!currency) return; - - const balanceMonetaryAmount = newMonetaryAmount(transferableBalance, (currency as unknown) as CurrencyExt, true); - const transferAmount = newMonetaryAmount(value, (currency as unknown) as CurrencyExt, true); - - // TODO: Resolve type issue caused by version mismatch - // and remove casting to `any` - const inputConfigs: any = await firstValueFrom( - XCMBridge.findAdapter(fromChain.type).subscribeInputConfigs({ - to: toChain?.type, - token: currency?.symbol, - address: destination.address, - signer: selectedAccount.address - }) as any - ); - - const minInputToBig = Big(inputConfigs.minInput.toString()); - const maxInputToBig = Big(inputConfigs.maxInput.toString()); - - if (balanceMonetaryAmount.lt(transferAmount)) { - return t('xcm_transfer.validation.insufficient_funds'); - } else if (minInputToBig.gt(transferableBalance)) { - return t('xcm_transfer.validation.balance_lower_minimum'); - } else if (minInputToBig.gt(transferAmount.toBig())) { - return t('xcm_transfer.validation.transfer_more_than_minimum', { - amount: `${inputConfigs.minInput.toString()} ${currency.symbol}` - }); - } else if (maxInputToBig.lt(transferAmount.toBig())) { - return t('xcm_transfer.validation.transfer_less_than_maximum', { - amount: `${inputConfigs.maxInput.toString()} ${currency.symbol}` - }); - } else { - return undefined; - } - }; - - const handleSetFromChain = (chain: ChainOption) => { - // Return from function is user clicks on current chain option - if (chain === fromChain) return; - - // Note: this is a workaround but ok for now. Component will be refactored - // when we introduce support for multiple currencies per channel - setCurrency(undefined); - setToChain(undefined); - setValue(TRANSFER_AMOUNT, ''); - setFromChain(chain); - }; - - const handleSetToChain = (chain: ChainOption) => { - // Return from function is user clicks on current chain option - if (chain === toChain) return; - - // Note: this is a workaround but ok for now. Component will be refactored - // when we introduce support for multiple currencies per channel - setCurrency(undefined); - setValue(TRANSFER_AMOUNT, ''); - setToChain(chain); - }; - - const handleClickBalance = () => { - setValue(TRANSFER_AMOUNT, transferableBalance.toString()); - handleUpdateUsdAmount(transferableBalance); - trigger(TRANSFER_AMOUNT); - }; - - // 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('transfer_page.successfully_transferred')); - - reset({ - [TRANSFER_AMOUNT]: '' - }); - }, [submitStatus, reset, t]); - - if (!XCMBridge || !toChain || !fromChain || !currency) { - return ; - } - - return ( - <> -
- {t('transfer_page.cross_chain_transfer_form.title')} -
- - handleUpdateUsdAmount(e.target.value), - required: { - value: true, - message: t('transfer_page.cross_chain_transfer_form.please_enter_amount') - }, - validate: (value) => validateTransferAmount(value) - })} - error={!!errors[TRANSFER_AMOUNT]} - helperText={errors[TRANSFER_AMOUNT]?.message} - label={currency.symbol} - approxUSD={`≈ ${approxUsdValue}`} - /> -
- - - - - {t('transfer')} - - - {submitStatus === STATUSES.REJECTED && submitError && ( - { - setSubmitStatus(STATUSES.IDLE); - setSubmitError(null); - }} - title='Error' - description={typeof submitError === 'string' ? submitError : submitError.message} - /> - )} - - ); -}; - -export default withErrorBoundary(CrossChainTransferForm, { - FallbackComponent: ErrorFallback, - onReset: () => { - window.location.reload(); - } -}); +export default CrossChainTransferForm; diff --git a/src/pages/Transfer/Transfer.style.tsx b/src/pages/Transfer/Transfer.style.tsx new file mode 100644 index 0000000000..4ec3066518 --- /dev/null +++ b/src/pages/Transfer/Transfer.style.tsx @@ -0,0 +1,9 @@ +import styled from 'styled-components'; + +import { theme } from '@/component-library'; + +const StyledWrapper = styled.div` + margin-top: ${theme.spacing.spacing6}; +`; + +export { StyledWrapper }; diff --git a/src/pages/Transfer/index.tsx b/src/pages/Transfer/index.tsx index ed2ea96777..2dbbc7ac0b 100644 --- a/src/pages/Transfer/index.tsx +++ b/src/pages/Transfer/index.tsx @@ -1,111 +1,31 @@ import clsx from 'clsx'; -import * as React from 'react'; -import { useTranslation } from 'react-i18next'; -import Hr1 from '@/legacy-components/hrs/Hr1'; +import { Flex, Tabs, TabsItem } from '@/component-library'; import Panel from '@/legacy-components/Panel'; -import InterlayRouterLink from '@/legacy-components/UI/InterlayRouterLink'; -import InterlayTabGroup, { - InterlayTab, - InterlayTabList, - InterlayTabPanel, - InterlayTabPanels -} 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'; -import useQueryParams from '@/utils/hooks/use-query-params'; -import useUpdateQueryParameters, { QueryParameters } from '@/utils/hooks/use-update-query-parameters'; import CrossChainTransferForm from './CrossChainTransferForm'; +import { StyledWrapper } from './Transfer.style'; import TransferForm from './TransferForm'; -const TAB_IDS = Object.freeze({ - transfer: 'transfer', - crossChainTransfer: 'crossChainTransfer' -}); - -const TAB_ITEMS = [ - { - id: TAB_IDS.transfer, - label: 'transfer' - }, - { - id: TAB_IDS.crossChainTransfer, - label: 'cross chain transfer' - } -]; - const Transfer = (): JSX.Element | null => { - const queryParams = useQueryParams(); - const selectedTabId = queryParams.get(QUERY_PARAMETERS.TAB); - const updateQueryParameters = useUpdateQueryParameters(); - - const { t } = useTranslation(); - - const updateQueryParametersRef = React.useRef<(newQueryParameters: QueryParameters) => void>(); - - React.useLayoutEffect(() => { - updateQueryParametersRef.current = updateQueryParameters; - }); - - React.useEffect(() => { - if (!updateQueryParametersRef.current) return; - - const tabIdValues = Object.values(TAB_IDS); - switch (true) { - case selectedTabId === null: - case selectedTabId && !tabIdValues.includes(selectedTabId): - updateQueryParametersRef.current({ - [QUERY_PARAMETERS.TAB]: TAB_IDS.transfer - }); - } - }, [selectedTabId]); - - const selectedTabIndex = TAB_ITEMS.findIndex((tabItem) => tabItem.id === selectedTabId); - - const handleTabSelect = (index: number) => { - updateQueryParameters({ - [QUERY_PARAMETERS.TAB]: TAB_ITEMS[index].id - }); - }; - return ( - {process.env.REACT_APP_RELAY_CHAIN_NAME === POLKADOT && ( - -

- In order to transfer Interlay tokens to Acala or Moonbeam, please use their respective dApps. Send tokens to{' '} - - Acala - {' '} - |{' '} - - Moonbeam - -

-
- )} - - - {TAB_ITEMS.map((tabItem) => ( - - {t(tabItem.label)} - - ))} - - - - - - - - - - - + + + + + + + + + + + + + +
); diff --git a/src/types/chains.d.ts b/src/types/chains.d.ts new file mode 100644 index 0000000000..4f3fdfb322 --- /dev/null +++ b/src/types/chains.d.ts @@ -0,0 +1,10 @@ +import { ChainName } from '@interlay/bridge'; + +type ChainData = { + display: string; + id: ChainName; +}; + +type Chains = ChainData[]; + +export type { ChainData, Chains }; diff --git a/src/types/chains.types.ts b/src/types/chains.types.ts deleted file mode 100644 index 74ee07fd8b..0000000000 --- a/src/types/chains.types.ts +++ /dev/null @@ -1,3 +0,0 @@ -type XCMChains = 'polkadot' | 'interlay'; - -export type { XCMChains }; diff --git a/src/utils/hooks/api/xcm/use-xcm-bridge.ts b/src/utils/hooks/api/xcm/use-xcm-bridge.ts index 4b086c55c8..32a6845d77 100644 --- a/src/utils/hooks/api/xcm/use-xcm-bridge.ts +++ b/src/utils/hooks/api/xcm/use-xcm-bridge.ts @@ -1,70 +1,150 @@ +import { FixedPointNumber } from '@acala-network/sdk-core'; import { ApiProvider, Bridge, ChainName } from '@interlay/bridge/build'; -import { useEffect, useState } from 'react'; +import { BaseCrossChainAdapter } from '@interlay/bridge/build/base-chain-adapter'; +import { atomicToBaseAmount, CurrencyExt, newMonetaryAmount } from '@interlay/interbtc-api'; +import Big from 'big.js'; +import { useCallback } from 'react'; +import { useErrorHandler } from 'react-error-boundary'; +import { useQuery, UseQueryResult } from 'react-query'; import { firstValueFrom } from 'rxjs'; +import { convertMonetaryAmountToValueInUSD, formatUSD } from '@/common/utils/utils'; import { XCM_ADAPTERS } from '@/config/relay-chains'; -import { BITCOIN_NETWORK } from '@/constants'; +import { Chains } from '@/types/chains'; +import { getTokenPrice } from '@/utils/helpers/prices'; +import { useGetPrices } from '@/utils/hooks/api/use-get-prices'; -// MEMO: BitcoinNetwork type is not available on XCM bridge -const XCMNetwork = BITCOIN_NETWORK === 'mainnet' ? 'mainnet' : 'testnet'; +import { XCMEndpoints } from './xcm-endpoints'; const XCMBridge = new Bridge({ adapters: Object.values(XCM_ADAPTERS) }); -// TODO: This config needs to be pushed higher up the app. -// Not sure how this will look: something to decide when -// adding USDT support. -const getEndpoints = (chains: ChainName[]) => { - switch (true) { - case chains.includes('kusama'): - return { - kusama: ['wss://kusama-rpc.polkadot.io', 'wss://kusama.api.onfinality.io/public-ws'], - kintsugi: ['wss://api-kusama.interlay.io/parachain', 'wss://kintsugi.api.onfinality.io/public-ws'], - statemine: ['wss://statemine-rpc.polkadot.io', 'wss://statemine.api.onfinality.io/public-ws'] - }; - case chains.includes('polkadot'): - return { - polkadot: ['wss://rpc.polkadot.io', 'wss://polkadot.api.onfinality.io/public-ws'], - interlay: ['wss://api.interlay.io/parachain', 'wss://interlay.api.onfinality.io/public-ws'], - statemint: ['wss://statemint-rpc.polkadot.io', 'wss://statemint.api.onfinality.io/public-ws'] - }; - - default: - return undefined; - } +type XCMBridgeData = { + bridge: Bridge; + provider: ApiProvider; }; -// const useXCMBridge = (): { XCMProvider: ApiProvider; XCMBridge: Bridge } => { -const useXCMBridge = (): { XCMProvider: ApiProvider; XCMBridge: Bridge } => { - const [XCMProvider, setXCMProvider] = useState(); - - useEffect(() => { - const createBridge = async () => { - const XCMProvider = new ApiProvider(XCMNetwork); - const chains = Object.keys(XCM_ADAPTERS) as ChainName[]; - - // Check connection - // TODO: Get rid of any casting - mismatch between ApiRx types - await firstValueFrom(XCMProvider.connectFromChain(chains, getEndpoints(chains)) as any); - - // Set Apis - await Promise.all( - chains.map((chain: ChainName) => - // TODO: Get rid of any casting - mismatch between ApiRx types - XCMBridge.findAdapter(chain).setApi(XCMProvider.getApi(chain) as any) - ) - ); +type XCMTokenData = { + balance: string; + balanceUSD: string; + destFee: FixedPointNumber; + originFee: string; + minTransferAmount: Big; + value: string; +}; + +type UseXCMBridge = UseQueryResult & { + originatingChains: Chains | undefined; + getDestinationChains: (chain: ChainName) => Chains; + getAvailableTokens: ( + from: ChainName, + to: ChainName, + originAddress: string, + destinationAddress: string + ) => Promise; +}; + +const initXCMBridge = async () => { + const XCMProvider = new ApiProvider(); + const chains = Object.keys(XCM_ADAPTERS) as ChainName[]; + + await firstValueFrom(XCMProvider.connectFromChain(chains, XCMEndpoints)); + + // Set Apis + await Promise.all(chains.map((chain: ChainName) => XCMBridge.findAdapter(chain).setApi(XCMProvider.getApi(chain)))); + + return { provider: XCMProvider, bridge: XCMBridge }; +}; + +const useXCMBridge = (): UseXCMBridge => { + const queryKey = ['available-xcm-channels']; + + const queryResult = useQuery({ + queryKey, + queryFn: initXCMBridge, + refetchInterval: false + }); + + const { data, error } = queryResult; + const prices = useGetPrices(); - setXCMProvider(XCMProvider); + const originatingChains = data?.bridge.adapters.map((adapter: BaseCrossChainAdapter) => { + return { + display: adapter.chain.display, + id: adapter.chain.id as ChainName }; + }); + + const getDestinationChains = useCallback( + (chain: ChainName): Chains => { + return XCMBridge.router + .getDestinationChains({ from: chain }) + .filter((destinationChain) => + originatingChains?.some((originatingChain) => originatingChain.id === destinationChain.id) + ) as Chains; + }, + [originatingChains] + ); + + const getAvailableTokens = useCallback( + async (from, to, originAddress, destinationAddress) => { + if (!data) return; + + const tokens = XCMBridge.router.getAvailableTokens({ from, to }); + + const inputConfigs = await Promise.all( + tokens.map(async (token) => { + const inputConfig = await firstValueFrom( + data.bridge.findAdapter(from).subscribeInputConfigs({ + to, + token, + address: destinationAddress, + signer: originAddress + }) + ); + + // TODO: resolve type mismatch with BaseCrossChainAdapter and remove `any` + const originAdapter = data.bridge.findAdapter(from) as any; + + const maxInputToBig = Big(inputConfig.maxInput.toString()); + const minInputToBig = Big(inputConfig.minInput.toString()); + + // Never show less than zero + const transferableBalance = inputConfig.maxInput < inputConfig.minInput ? 0 : maxInputToBig; + const currency = XCMBridge.findAdapter(from).getToken(token, from); + + const nativeToken = originAdapter.getNativeToken(); + + const amount = newMonetaryAmount(transferableBalance, (currency as unknown) as CurrencyExt, true); + const balanceUSD = convertMonetaryAmountToValueInUSD(amount, getTokenPrice(prices, token)?.usd); + const originFee = atomicToBaseAmount(inputConfig.estimateFee, nativeToken as CurrencyExt); + + return { + balance: transferableBalance.toString(), + balanceUSD: formatUSD(balanceUSD || 0, { compact: true }), + destFee: inputConfig.destFee.balance, + originFee: `${originFee.toString()} ${nativeToken.symbol}`, + minTransferAmount: minInputToBig, + value: token + }; + }) + ); + + return inputConfigs; + }, + [data, prices] + ); - if (!XCMProvider) { - createBridge(); - } - }, [XCMProvider]); + useErrorHandler(error); - return { XCMProvider, XCMBridge }; + return { + ...queryResult, + originatingChains, + getDestinationChains, + getAvailableTokens + }; }; export { useXCMBridge }; +export type { UseXCMBridge, XCMTokenData }; diff --git a/src/utils/hooks/api/xcm/xcm-endpoints.ts b/src/utils/hooks/api/xcm/xcm-endpoints.ts new file mode 100644 index 0000000000..6b8407c0df --- /dev/null +++ b/src/utils/hooks/api/xcm/xcm-endpoints.ts @@ -0,0 +1,33 @@ +import { ChainName } from '@interlay/bridge'; + +type XCMEndpointsRecord = Record; + +const XCMEndpoints: XCMEndpointsRecord = { + acala: [ + 'wss://acala-rpc-0.aca-api.network', + 'wss://acala-rpc-1.aca-api.network', + 'wss://acala-rpc-3.aca-api.network/ws', + 'wss://acala-rpc.dwellir.com' + ], + astar: ['wss://rpc.astar.network', 'wss://astar-rpc.dwellir.com'], + bifrost: ['wss://bifrost-rpc.dwellir.com'], + heiko: ['wss://heiko-rpc.parallel.fi'], + hydra: ['wss://rpc.hydradx.cloud', 'wss://hydradx-rpc.dwellir.com'], + interlay: ['wss://api.interlay.io/parachain'], + karura: [ + 'wss://karura-rpc-0.aca-api.network', + 'wss://karura-rpc-1.aca-api.network', + 'wss://karura-rpc-2.aca-api.network/ws', + 'wss://karura-rpc-3.aca-api.network/ws', + 'wss://karura-rpc.dwellir.com' + ], + kintsugi: ['wss://api-kusama.interlay.io/parachain'], + kusama: ['wss://kusama-rpc.polkadot.io', 'wss://kusama-rpc.dwellir.com'], + parallel: ['wss://rpc.parallel.fi'], + polkadot: ['wss://rpc.polkadot.io', 'wss://polkadot-rpc.dwellir.com'], + statemine: ['wss://statemine-rpc.polkadot.io', 'wss://statemine-rpc.dwellir.com'], + statemint: ['wss://statemint-rpc.polkadot.io', 'wss://statemint-rpc.dwellir.com'] +}; + +export { XCMEndpoints }; +export type { XCMEndpointsRecord }; diff --git a/yarn.lock b/yarn.lock index 6e1717a6aa..c887f3b4fd 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2,6 +2,16 @@ # yarn lockfile v1 +"@acala-network/api-derive@4.1.8-13": + version "4.1.8-13" + resolved "https://registry.yarnpkg.com/@acala-network/api-derive/-/api-derive-4.1.8-13.tgz#0ac02da5494c9f6ea8d52235836ecb369dea443d" + integrity sha512-Bm7005fPvFMcohvlpbGJMpm0Vm/63PTkRcg0shZvcjuMak3YSR0NhceZRnMoHz+I0Ond5XGRjZVZA/eyRMbSsg== + dependencies: + "@acala-network/types" "4.1.8-13" + "@babel/runtime" "^7.10.2" + "@open-web3/orml-types" "^1.1.4" + "@polkadot/api-derive" "^8.5.1" + "@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" @@ -12,7 +22,19 @@ "@open-web3/orml-types" "^1.1.4" "@polkadot/api-derive" "^8.5.1" -"@acala-network/api@4.1.8-9", "@acala-network/api@~4.1.8-9": +"@acala-network/api@4.1.8-13": + version "4.1.8-13" + resolved "https://registry.yarnpkg.com/@acala-network/api/-/api-4.1.8-13.tgz#8127edaba9802eaa6a20678e823f43f2affb6067" + integrity sha512-+m032NiYPAvbOHeaJrCKQuACe9hykNTpQpDKeKkg0RME9JnFKeR7TYLkWtInhbmql6b8LxAAdpy2gdQctrsCRA== + dependencies: + "@acala-network/api-derive" "4.1.8-13" + "@acala-network/types" "4.1.8-13" + "@babel/runtime" "^7.10.2" + "@open-web3/orml-api-derive" "^1.1.4" + "@polkadot/api" "^9.9.1" + "@polkadot/rpc-core" "^9.9.1" + +"@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== @@ -29,7 +51,7 @@ 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.4": +"@acala-network/eth-providers@^2.5.9": version "2.6.5" resolved "https://registry.yarnpkg.com/@acala-network/eth-providers/-/eth-providers-2.6.5.tgz#9087abe44a0686de5188ea962961519ecff20e66" integrity sha512-Y0hi0LRN8pJ144dv9WcSi9nPn5Wez0h745EGa1/6NFtU7jsua0jg25WYJ53s17rXIMz8GUKdln9SAIeShQiEtw== @@ -79,10 +101,10 @@ "@ethersproject/wallet" "~5.7.0" "@polkadot/util-crypto" "^10.2.1" -"@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== +"@acala-network/sdk-core@4.1.8-13": + version "4.1.8-13" + resolved "https://registry.yarnpkg.com/@acala-network/sdk-core/-/sdk-core-4.1.8-13.tgz#ff69ef993f5a36caa31744384c389765ede7cc96" + integrity sha512-4q9lksLJ/8lXA/f/t9GHQqv8ePIT2vId7rkaoqE/jASq6ngRFg2heV/6eScCKudr2aJN68YX3Jf0hwH6eazVLQ== dependencies: "@polkadot/api" "^9.9.1" "@polkadot/types" "^9.9.1" @@ -91,14 +113,14 @@ events "^3.2.0" lodash "^4.17.20" -"@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== +"@acala-network/sdk@4.1.8-13": + version "4.1.8-13" + resolved "https://registry.yarnpkg.com/@acala-network/sdk/-/sdk-4.1.8-13.tgz#f603a6c84c4654971495676345f4a24041ff1c02" + integrity sha512-3apYrmQ+WZWzEYd0sdLCpTYe8SagMMK2+0vj35ANVvD92FHUUkHTtJAEiCu81y0ujFuFbtx/VxA0uGVb/fBZ6A== dependencies: - "@acala-network/api" "4.1.8-9" - "@acala-network/eth-providers" "^2.5.4" - "@acala-network/type-definitions" "4.1.8-9" + "@acala-network/api" "4.1.8-13" + "@acala-network/eth-providers" "^2.5.9" + "@acala-network/type-definitions" "4.1.8-13" "@ethersproject/bignumber" "^5.7.0" "@polkadot/api" "^9.9.1" "@polkadot/types" "^9.9.1" @@ -114,6 +136,13 @@ lru-cache "^7.14.1" rxjs "^7.5.7" +"@acala-network/type-definitions@4.1.8-13": + version "4.1.8-13" + resolved "https://registry.yarnpkg.com/@acala-network/type-definitions/-/type-definitions-4.1.8-13.tgz#a295d3f3feb1d36cadbda634c180f53eb90cca61" + integrity sha512-AMXbqsJehhDcwEngSB173eQvuCAsXEm/7rNZMQ8KLG56a8FrNAgrEz+83foogLuTcehCPUPfC0R1Ef/+874rRw== + dependencies: + "@open-web3/orml-type-definitions" "^1.1.4" + "@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" @@ -121,13 +150,23 @@ dependencies: "@open-web3/orml-type-definitions" "^1.1.4" -"@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== +"@acala-network/type-definitions@^4.1.8-1": + version "4.1.8-14" + resolved "https://registry.yarnpkg.com/@acala-network/type-definitions/-/type-definitions-4.1.8-14.tgz#f0d1dd5f0e50c5b16e19fc222d351b4ec4524928" + integrity sha512-3PDYFaT8s9PYgZZNNtOEco5Oyn/oQlnuYrBe6WQX1bQBhAbUQjMDhuaqoqRF61CFtxYTgw/6kiFRf/aUNhigGQ== dependencies: "@open-web3/orml-type-definitions" "^1.1.4" +"@acala-network/types@4.1.8-13": + version "4.1.8-13" + resolved "https://registry.yarnpkg.com/@acala-network/types/-/types-4.1.8-13.tgz#919fc5ad818f535caba0fc2ea0477085e570d93b" + integrity sha512-XBIupGrNyY1xSptC59GNE89C4wJ2pb/QwRiRkQUNzDSTfLbjUSCOpDqjSfZIxj21+/zhZtw+6+uS+HnoTpsQeg== + dependencies: + "@acala-network/type-definitions" "4.1.8-13" + "@babel/runtime" "^7.10.2" + "@open-web3/api-mobx" "^1.1.4" + "@open-web3/orml-types" "^1.1.4" + "@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" @@ -1270,7 +1309,7 @@ core-js-pure "^3.20.2" regenerator-runtime "^0.13.4" -"@babel/runtime@^7.0.0", "@babel/runtime@^7.1.2", "@babel/runtime@^7.10.2", "@babel/runtime@^7.12.0", "@babel/runtime@^7.12.1", "@babel/runtime@^7.12.13", "@babel/runtime@^7.12.5", "@babel/runtime@^7.14.5", "@babel/runtime@^7.14.6", "@babel/runtime@^7.15.4", "@babel/runtime@^7.16.3", "@babel/runtime@^7.17.8", "@babel/runtime@^7.18.3", "@babel/runtime@^7.18.6", "@babel/runtime@^7.18.9", "@babel/runtime@^7.20.1", "@babel/runtime@^7.20.13", "@babel/runtime@^7.20.6", "@babel/runtime@^7.3.1", "@babel/runtime@^7.5.0", "@babel/runtime@^7.5.5", "@babel/runtime@^7.6.2", "@babel/runtime@^7.7.2", "@babel/runtime@^7.7.6", "@babel/runtime@^7.8.4", "@babel/runtime@^7.8.7", "@babel/runtime@^7.9.2": +"@babel/runtime@^7.0.0", "@babel/runtime@^7.1.2", "@babel/runtime@^7.10.2", "@babel/runtime@^7.12.0", "@babel/runtime@^7.12.1", "@babel/runtime@^7.12.13", "@babel/runtime@^7.12.5", "@babel/runtime@^7.14.5", "@babel/runtime@^7.14.6", "@babel/runtime@^7.15.4", "@babel/runtime@^7.16.3", "@babel/runtime@^7.17.8", "@babel/runtime@^7.18.3", "@babel/runtime@^7.18.6", "@babel/runtime@^7.18.9", "@babel/runtime@^7.20.13", "@babel/runtime@^7.20.6", "@babel/runtime@^7.3.1", "@babel/runtime@^7.5.0", "@babel/runtime@^7.5.5", "@babel/runtime@^7.6.2", "@babel/runtime@^7.7.2", "@babel/runtime@^7.7.6", "@babel/runtime@^7.8.4", "@babel/runtime@^7.8.7", "@babel/runtime@^7.9.2": 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== @@ -1321,10 +1360,10 @@ resolved "https://registry.yarnpkg.com/@bcoe/v8-coverage/-/v8-coverage-0.2.3.tgz#75a2e8b51cb758a7553d6804a5932d7aace75c39" integrity sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw== -"@bifrost-finance/type-definitions@1.7.1": - version "1.7.1" - resolved "https://registry.yarnpkg.com/@bifrost-finance/type-definitions/-/type-definitions-1.7.1.tgz#d64e89eebf5d325ecca636261373945e14c4c508" - integrity sha512-9AJIFFtlTKUGNJ8ITkgDUUJD+Iodb2Cp6qbVl5mAKuaws9QrLpgKYTT09GoKltQTg5bbDc8+ygbcabntUeTZGw== +"@bifrost-finance/type-definitions@1.7.2": + version "1.7.2" + resolved "https://registry.yarnpkg.com/@bifrost-finance/type-definitions/-/type-definitions-1.7.2.tgz#13139a69e3e98d175a4751d7fd78dcfebac29943" + integrity sha512-JL19CHFL4DxO29LRrv9o7r7Au9TtY+8pwG4fMP8M6jq2/MkvWd7OQFn1lmEy58akntNrVReIkZPuP81MFKv9jg== dependencies: "@open-web3/orml-type-definitions" "^0.9.4-38" @@ -1552,10 +1591,10 @@ resolved "https://registry.yarnpkg.com/@discoveryjs/json-ext/-/json-ext-0.5.7.tgz#1d572bfbbe14b7704e0ba0f39b74815b84870d70" integrity sha512-dBVuXR082gk3jsFp7Rd/JI4kytwGHecnCoTtXFb7DB6CNHp4rg5k1bhg0nWdLGLnOV71lmDzGQaLMy8iPLY0pw== -"@docknetwork/node-types@0.13.0": - version "0.13.0" - resolved "https://registry.yarnpkg.com/@docknetwork/node-types/-/node-types-0.13.0.tgz#8b643f9cb52c3563d3db91ac84e06836b0f6f199" - integrity sha512-k+NZksUGqc1Cz8eG+EzCPRyRalgho/xy4fh5Dqsbe9LwLeklrrtfAMaklPRtkt0yja8ueg1DGnCtHq00e99j4Q== +"@docknetwork/node-types@0.15.0": + version "0.15.0" + resolved "https://registry.yarnpkg.com/@docknetwork/node-types/-/node-types-0.15.0.tgz#eed5c719380865bf989ccd2550844dadb7abdd19" + integrity sha512-ACIHUIiAt82nhYxtwHSyS4JaJ28UbWS+fAwbTblKcsQBe7YRM2tjbLmkaqQjGPjxJS+wmh/xf7/PnA8PfboNZg== "@edgeware/node-types@3.6.2-wako": version "3.6.2-wako" @@ -1584,10 +1623,10 @@ resolved "https://registry.yarnpkg.com/@emotion/unitless/-/unitless-0.7.5.tgz#77211291c1900a700b8a78cfafda3160d76949ed" integrity sha512-OWORNpfjMsSSUBVrRBVGECkhWcULOAJz9ZW8uK9qgxD+87M7jHRcvh/A96XXNhXTLmKcoYSQtBEX7lHMO7YRwg== -"@equilab/definitions@1.4.14": - version "1.4.14" - resolved "https://registry.yarnpkg.com/@equilab/definitions/-/definitions-1.4.14.tgz#c384f3eca003293d5f2c0a42235bdbe0a60626dd" - integrity sha512-F8jDESrhUpapqGSTXWND+5/DOqFlnh/oEejIYVIzF2WeUreHJzUPpI8a8Hb9plCLxj5sxYJ+3JL/5epMcrXDaQ== +"@equilab/definitions@1.4.18": + version "1.4.18" + resolved "https://registry.yarnpkg.com/@equilab/definitions/-/definitions-1.4.18.tgz#e544951b50278705af3d9fa4ba91e04df53a3d06" + integrity sha512-rFEPaHmdn5I1QItbQun9H/x+o3hgjA6kLYLrNN6nl/ndtQMY2tqx/mQfcGIlKA1xVmyn9mUAqD8G0P/nBHD3yA== "@eslint/eslintrc@^0.4.3": version "0.4.3" @@ -1985,6 +2024,15 @@ dependencies: tslib "2.4.0" +"@frequency-chain/api-augment@^1.0.0": + version "1.6.0" + resolved "https://registry.yarnpkg.com/@frequency-chain/api-augment/-/api-augment-1.6.0.tgz#a611d191328e11ccf24aff82fe2d165b9b6a0eb8" + integrity sha512-OkyLC4ttgkB+6PpTN94NIWPgi6rEclzK7pBSULtfl6ZhgjW9IalykbJmispG3Ntgwdb69TMUU0wSdDPBS15r9A== + dependencies: + "@polkadot/api" "^10.3.2" + "@polkadot/rpc-provider" "^10.3.2" + "@polkadot/types" "^10.3.2" + "@gar/promisify@^1.0.1": version "1.1.3" resolved "https://registry.yarnpkg.com/@gar/promisify/-/promisify-1.1.3.tgz#555193ab2e3bb3b6adc3d551c9c030d9e860daf6" @@ -2051,18 +2099,17 @@ resolved "https://registry.yarnpkg.com/@iarna/toml/-/toml-2.2.5.tgz#b32366c89b43c6f8cefbdefac778b9c828e3ba8c" integrity sha512-trnsAYxU3xnS1gPHPyU961coFyLkh4gAD/0zQ5mymY4yOZ+CYvsPqUbOFSw0aDM4y0tV7tiFxL/1XfXPNC6IPg== -"@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: - "@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.11.1" - "@polkadot/types-augment" "^9.11.1" +"@interlay/bridge@^0.3.9": + version "0.3.9" + resolved "https://registry.yarnpkg.com/@interlay/bridge/-/bridge-0.3.9.tgz#fc39c64708eab2f55cb0bbb970f2f0e72583cb37" + integrity sha512-QCeTux1f3LwLJ/dcfHmOTOuF8ocfpo9WDNV7Z1GWTHX/I8lspidj4xh8c/g2+jNZnHMiINXCSvHGPPr05lTnQg== + dependencies: + "@acala-network/api" "4.1.8-13" + "@acala-network/sdk" "4.1.8-13" + "@acala-network/sdk-core" "4.1.8-13" + "@polkadot/api" "^9.14.2" + "@polkadot/apps-config" "^0.124.1" + "@polkadot/types" "^9.14.2" axios "^0.27.2" lodash "^4.17.20" @@ -2090,16 +2137,16 @@ isomorphic-fetch "^3.0.0" regtest-client "^0.2.0" +"@interlay/interbtc-types@1.11.0": + version "1.11.0" + resolved "https://registry.yarnpkg.com/@interlay/interbtc-types/-/interbtc-types-1.11.0.tgz#5b94066ddee1fd677de928531db36e6ae439e08f" + integrity sha512-bn3XjyRlXyhe1QKUHx5IEQJDNC6LoSCJJIkTnSp5xm52GRBEWgHOvLAnfJi3gyj7A3lV/yA2Xjqf294bZgMmfw== + "@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.2": version "0.7.2" resolved "https://registry.yarnpkg.com/@interlay/monetary-js/-/monetary-js-0.7.2.tgz#a54a315b60be12f5b1a9c31f0d71d5e8ee7ba174" @@ -2434,10 +2481,10 @@ "@jridgewell/resolve-uri" "^3.0.3" "@jridgewell/sourcemap-codec" "^1.4.10" -"@kiltprotocol/type-definitions@^0.2.1": - version "0.2.1" - resolved "https://registry.yarnpkg.com/@kiltprotocol/type-definitions/-/type-definitions-0.2.1.tgz#0469b0bcc58063be0b02ffbf6f779176c6b0a00d" - integrity sha512-By09MH20P+rXadiZDnw2XeAw7bQLgNazOyNS3gPdU1L4Jx+lU9OtvIgZEA+T/TY/KM5nTv32s3c4wZ7v1s2znw== +"@kiltprotocol/type-definitions@^0.30.0": + version "0.30.0" + resolved "https://registry.yarnpkg.com/@kiltprotocol/type-definitions/-/type-definitions-0.30.0.tgz#00e99636a1c4405071021242cd509090c8f14287" + integrity sha512-1UpPDjX8PFqTFm3lRRfYUPEY9M8KrbpRinf4q4K843lY5GdTxQaevrVdK9/WCHKywLyDa4tSrlUv9KQjrTP4bg== "@laminar/type-definitions@0.3.1": version "0.3.1" @@ -2446,22 +2493,22 @@ dependencies: "@open-web3/orml-type-definitions" "^0.8.2-9" -"@logion/node-api@^0.7.0": - version "0.7.2" - resolved "https://registry.yarnpkg.com/@logion/node-api/-/node-api-0.7.2.tgz#af164f13831f1b89b130597ca70bf0e863f0b79f" - integrity sha512-EAyRp1MAAS4bGuxnuAYExssfwvFHLsmCyPWH0wMIik5eLHqNiPA1LwylYH5AQx8P1w11/nHVqRc5ly7dlf5E+w== +"@logion/node-api@^0.9.0-3": + version "0.9.0-3" + resolved "https://registry.yarnpkg.com/@logion/node-api/-/node-api-0.9.0-3.tgz#b02741acbf30517d537d48b75ffc83b366f09b87" + integrity sha512-6m2My8yI9jmhqP6FHPJdrsqdg/1vyJtY1/4cnuqCByJaVgNDGrcdtcmzW4BXCww+hJMrdm3PeLthKHCrwpo0gA== dependencies: - "@polkadot/api" "^9.8.1" - "@polkadot/util" "^10.1.12" - "@polkadot/util-crypto" "^10.1.12" + "@polkadot/api" "^9.10.1" + "@polkadot/util" "^10.2.1" + "@polkadot/util-crypto" "^10.2.1" "@types/uuid" "^8.3.4" fast-sha256 "^1.3.0" uuid "^8.3.2" -"@mangata-finance/types@^0.9.0": - version "0.9.0" - resolved "https://registry.yarnpkg.com/@mangata-finance/types/-/types-0.9.0.tgz#8272a01d87243f693d0e0889070afb0d00b4f91f" - integrity sha512-37yIP9xh+6kTt+UQJsbPt0OCrDIZsqGRh3f7sEtK7zX4G8uWrg/GHcHtGZQRruNIOAO8+1PLXuMfLokSzojVBw== +"@mangata-finance/types@^0.17.0": + version "0.17.0" + resolved "https://registry.yarnpkg.com/@mangata-finance/types/-/types-0.17.0.tgz#299b0bd21e30e17ee65c25f18d4a89871f521930" + integrity sha512-v0o7rePG4P2fDH1yVvSfuHpHQCA7Xki9IwPMTu51Y4FoQdvD1zHUOI4mIOc3ssjOAJsCePNdsTm+/xj3DeiSxQ== "@mdx-js/mdx@^1.6.22": version "1.6.22" @@ -2732,17 +2779,17 @@ dependencies: "@open-web3/orml-type-definitions" "1.1.4" -"@parallel-finance/type-definitions@1.7.13": - version "1.7.13" - resolved "https://registry.yarnpkg.com/@parallel-finance/type-definitions/-/type-definitions-1.7.13.tgz#08c92e07496d2757d9a89879e7d3d08b2bf49744" - integrity sha512-v2M1uCfBnQ2wiYEk/2ftTdSB4OV8nTL38qhB6ApykxHCdXu4Nz1lJKFjW8OW1DTXB8xCfkj8VX8eY0UDez535g== +"@parallel-finance/type-definitions@1.7.14": + version "1.7.14" + resolved "https://registry.yarnpkg.com/@parallel-finance/type-definitions/-/type-definitions-1.7.14.tgz#02ca0d8a8d2894fa1d22c8625bd4edfcfbeffc4c" + integrity sha512-64cIrOcS5z2SSzTAITg3qDdQReoBLCZhAGHzR1VnYQzF0u59Ow6XnWmg0/R4EuyhnsqW4aMhnrmlVE7RhG9kPg== dependencies: "@open-web3/orml-type-definitions" "^1.1.4" -"@phala/typedefs@0.2.32": - version "0.2.32" - resolved "https://registry.yarnpkg.com/@phala/typedefs/-/typedefs-0.2.32.tgz#4c66dce9b5a975226bbbdbdef09bccfde2e54878" - integrity sha512-G1ifICDNW6NtixqCVfJHBI82Detwzzmzs4gpE1RrMsTxfoKIbxkX8nx3DxZUhFYqikGEkQVxNmEi3jpC0zDrsw== +"@phala/typedefs@0.2.33": + version "0.2.33" + resolved "https://registry.yarnpkg.com/@phala/typedefs/-/typedefs-0.2.33.tgz#6f18d73b5104db6a594d08be571954385b3e509b" + integrity sha512-CaRzIGfU6CUIKLPswYtOw/xbtTttqmJZpr3fhkxLvkBQMXIH14iISD763OFXtWui7DrAMBKo/bHawvFNgWGKTg== "@pmmmwh/react-refresh-webpack-plugin@0.4.3", "@pmmmwh/react-refresh-webpack-plugin@^0.4.3": version "0.4.3" @@ -2829,7 +2876,7 @@ "@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": +"@polkadot/api-derive@9.10.3", "@polkadot/api-derive@9.14.2", "@polkadot/api-derive@^8.5.1", "@polkadot/api-derive@^9.13.2", "@polkadot/api-derive@^9.14.2": version "9.14.2" resolved "https://registry.yarnpkg.com/@polkadot/api-derive/-/api-derive-9.14.2.tgz#e8fcd4ee3f2b80b9fe34d4dec96169c3bdb4214d" integrity sha512-yw9OXucmeggmFqBTMgza0uZwhNjPxS7MaT7lSCUIRKckl1GejdV+qMhL3XFxPFeYzXwzFpdPG11zWf+qJlalqw== @@ -2845,7 +2892,7 @@ "@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": +"@polkadot/api@9.10.3", "@polkadot/api@9.14.2", "@polkadot/api@^10.3.2", "@polkadot/api@^7.2.1", "@polkadot/api@^9.10.1", "@polkadot/api@^9.13.2", "@polkadot/api@^9.14.2", "@polkadot/api@^9.4.2", "@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== @@ -2868,48 +2915,49 @@ eventemitter3 "^5.0.0" rxjs "^7.8.0" -"@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== +"@polkadot/apps-config@^0.124.1": + version "0.124.1" + resolved "https://registry.yarnpkg.com/@polkadot/apps-config/-/apps-config-0.124.1.tgz#4d993fcc198118dfe4aa9200ce6055b48cab96b3" + integrity sha512-SqDLf0ksU5WkU96L3nIiICwaBDLj4APYjKkwpSUAWk1NcvXDWZQQG56obgaLPHZ2If6GZrQge/fUmItuRBIZrg== dependencies: - "@acala-network/type-definitions" "^4.1.5" - "@babel/runtime" "^7.20.1" - "@bifrost-finance/type-definitions" "1.7.1" + "@acala-network/type-definitions" "^4.1.8-1" + "@babel/runtime" "^7.20.13" + "@bifrost-finance/type-definitions" "1.7.2" "@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" + "@docknetwork/node-types" "0.15.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" + "@equilab/definitions" "1.4.18" + "@frequency-chain/api-augment" "^1.0.0" + "@interlay/interbtc-types" "1.11.0" + "@kiltprotocol/type-definitions" "^0.30.0" "@laminar/type-definitions" "0.3.1" - "@logion/node-api" "^0.7.0" - "@mangata-finance/types" "^0.9.0" + "@logion/node-api" "^0.9.0-3" + "@mangata-finance/types" "^0.17.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" + "@parallel-finance/type-definitions" "1.7.14" + "@phala/typedefs" "0.2.33" + "@polkadot/api" "^9.13.2" + "@polkadot/api-derive" "^9.13.2" + "@polkadot/networks" "^10.3.1" + "@polkadot/types" "^9.13.2" + "@polkadot/util" "^10.3.1" + "@polkadot/x-fetch" "^10.3.1" "@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" + "@sora-substrate/type-definitions" "1.12.4" + "@subsocial/definitions" "^0.7.9" + "@unique-nft/opal-testnet-types" "930.34.0" + "@unique-nft/quartz-mainnet-types" "930.34.0" + "@unique-nft/unique-mainnet-types" "930.33.0" + "@zeitgeistpm/type-defs" "0.10.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" + rxjs "^7.8.0" "@polkadot/extension-dapp@0.44.1": version "0.44.1" @@ -2942,6 +2990,15 @@ "@polkadot/util" "10.4.2" "@polkadot/util-crypto" "10.4.2" +"@polkadot/keyring@^10.3.1": + version "10.3.1" + resolved "https://registry.yarnpkg.com/@polkadot/keyring/-/keyring-10.3.1.tgz#f13fed33686ff81b1e486721e52299eba9e6c4a6" + integrity sha512-xBkUtyQ766NVS1ccSYbQssWpxAhSf0uwkw9Amj8TFhu++pnZcVm+EmM2VczWqgOkmWepO7MGRjEXeOIw1YUGiw== + dependencies: + "@babel/runtime" "^7.20.13" + "@polkadot/util" "10.3.1" + "@polkadot/util-crypto" "10.3.1" + "@polkadot/keyring@^6.9.1": version "6.11.1" resolved "https://registry.yarnpkg.com/@polkadot/keyring/-/keyring-6.11.1.tgz#2510c349c965c74cc2f108f114f1048856940604" @@ -2969,7 +3026,7 @@ "@polkadot/util" "8.7.1" "@polkadot/util-crypto" "8.7.1" -"@polkadot/networks@10.4.2", "@polkadot/networks@^10.1.11", "@polkadot/networks@^10.1.6", "@polkadot/networks@^10.4.2": +"@polkadot/networks@10.4.2", "@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== @@ -2978,6 +3035,32 @@ "@polkadot/util" "10.4.2" "@substrate/ss58-registry" "^1.38.0" +"@polkadot/networks@^10.3.1": + version "10.3.1" + resolved "https://registry.yarnpkg.com/@polkadot/networks/-/networks-10.3.1.tgz#097a2c4cd25eff59fe6c11299f58feedd4335042" + integrity sha512-W9E1g6zRbIVyF7sGqbpxH0P6caxtBHNEwvDa5/8ZQi9UsLj6mUs0HdwZtAdIo3KcSO4uAyV9VYJjY/oAWWcnXg== + dependencies: + "@babel/runtime" "^7.20.13" + "@polkadot/util" "10.3.1" + "@substrate/ss58-registry" "^1.38.0" + +"@polkadot/react-identicon@^2.11.1": + version "2.11.1" + resolved "https://registry.yarnpkg.com/@polkadot/react-identicon/-/react-identicon-2.11.1.tgz#8f81f142f7c7763fe2d499580b85b3879f4eb081" + integrity sha512-pqEsiXuKOXDrXNnsFB1JyBbFqedbiDwtP0yewIQ9vtwJwm01o7oE0yGfYS88hnPN1mLDc++MMcqvrHBsxYr2Lw== + dependencies: + "@babel/runtime" "^7.20.13" + "@polkadot/keyring" "^10.3.1" + "@polkadot/ui-settings" "2.11.1" + "@polkadot/ui-shared" "2.11.1" + "@polkadot/util" "^10.3.1" + "@polkadot/util-crypto" "^10.3.1" + color "^3.2.1" + ethereum-blockies-base64 "^1.0.2" + jdenticon "3.2.0" + react-copy-to-clipboard "^5.1.0" + styled-components "^5.3.6" + "@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" @@ -3001,7 +3084,7 @@ "@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": +"@polkadot/rpc-provider@9.14.2", "@polkadot/rpc-provider@^10.3.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== @@ -3021,7 +3104,7 @@ optionalDependencies: "@substrate/connect" "0.7.19" -"@polkadot/types-augment@9.14.2", "@polkadot/types-augment@^9.11.1", "@polkadot/types-augment@^9.14.2": +"@polkadot/types-augment@9.14.2", "@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== @@ -3069,7 +3152,7 @@ "@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": +"@polkadot/types@9.10.3", "@polkadot/types@9.14.2", "@polkadot/types@^10.3.2", "@polkadot/types@^4.13.1", "@polkadot/types@^6.0.5", "@polkadot/types@^7.2.1", "@polkadot/types@^8.7.1", "@polkadot/types@^9.13.2", "@polkadot/types@^9.14.2", "@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== @@ -3097,6 +3180,17 @@ rxjs "^7.5.6" store "^2.0.12" +"@polkadot/ui-settings@2.11.1": + version "2.11.1" + resolved "https://registry.yarnpkg.com/@polkadot/ui-settings/-/ui-settings-2.11.1.tgz#e3474097a6f4246423731e9b5cce3a5bb9482349" + integrity sha512-7yZwb3VxGh7VPHkyygktL7Oep0c4XUyKkYGwSmgP2Gt2IcvnGXFUQVSEARKs9FCanl19f2CEU1m19+FFrjlUNQ== + dependencies: + "@babel/runtime" "^7.20.13" + "@polkadot/networks" "^10.3.1" + "@polkadot/util" "^10.3.1" + eventemitter3 "^4.0.7" + store "^2.0.12" + "@polkadot/ui-settings@2.9.7": version "2.9.7" resolved "https://registry.yarnpkg.com/@polkadot/ui-settings/-/ui-settings-2.9.7.tgz#c9fcd7dc8d1de36826e06c347f27d9a9df56810c" @@ -3108,7 +3202,15 @@ eventemitter3 "^4.0.7" store "^2.0.12" -"@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": +"@polkadot/ui-shared@2.11.1": + version "2.11.1" + resolved "https://registry.yarnpkg.com/@polkadot/ui-shared/-/ui-shared-2.11.1.tgz#b4dfe2310003ce4621fcbb5e94daa8c76b45a028" + integrity sha512-+qCLPT3SEnHOG3WvO0iYSJ6zArPQGCz9nHx8X8rw9GhffdiEC20ae63jB6dQTjR5GppPQx0aLE/cOppWn/HpRg== + dependencies: + "@babel/runtime" "^7.20.13" + color "^3.2.1" + +"@polkadot/util-crypto@10.3.1", "@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.6", "@polkadot/util-crypto@^10.2.1", "@polkadot/util-crypto@^10.2.4", "@polkadot/util-crypto@^10.3.1", "@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== @@ -3125,7 +3227,7 @@ ed2curve "^0.3.0" tweetnacl "^1.0.3" -"@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": +"@polkadot/util@10.3.1", "@polkadot/util@10.4.2", "@polkadot/util@6.11.1", "@polkadot/util@7.9.2", "@polkadot/util@8.7.1", "@polkadot/util@^10.1.6", "@polkadot/util@^10.2.1", "@polkadot/util@^10.2.4", "@polkadot/util@^10.3.1", "@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== @@ -3197,7 +3299,7 @@ "@babel/runtime" "^7.20.13" "@polkadot/x-global" "10.4.2" -"@polkadot/x-fetch@^10.1.11", "@polkadot/x-fetch@^10.4.2": +"@polkadot/x-fetch@^10.3.1", "@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== @@ -4599,10 +4701,10 @@ "@polkadot/keyring" "^8.2.2" "@polkadot/types" "^7.2.1" -"@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" - integrity sha512-QPtJk6ZjPK9RwpMG+YdMI319dRbSr01C5D52TNOf9UAk6FA9fGTXtn6kH6pR185Ssu/Ww50LmU+NpDP45RPYVA== +"@sora-substrate/type-definitions@1.12.4": + version "1.12.4" + resolved "https://registry.yarnpkg.com/@sora-substrate/type-definitions/-/type-definitions-1.12.4.tgz#e4bfc1d5d58c20dd589cfcd73ab86f798684221f" + integrity sha512-G+s1DTKGfkncUXUPXQNNrbj/9ZnNxksEXBmqP/RQrmnfYE3C59P5Zkp+D98WsXobkWOnMxqBDlK+VbUQbvMoRA== dependencies: "@open-web3/orml-type-definitions" "0.9.4-26" @@ -5434,7 +5536,7 @@ regenerator-runtime "^0.13.7" resolve-from "^5.0.0" -"@subsocial/definitions@^0.7.8-dev.0": +"@subsocial/definitions@^0.7.9": version "0.7.14" resolved "https://registry.yarnpkg.com/@subsocial/definitions/-/definitions-0.7.14.tgz#1397f1ec806d60d9deb112b9f36d530400b711fe" integrity sha512-dor5S6/tbY09n40e/dh7qFcqF9slMihOMDTXWBM5hTe8nS/Pf5Zp4/r9WiZxxYLoY2v5MlSqyJxjiSCjTxxjUw== @@ -5465,9 +5567,9 @@ ws "^8.8.1" "@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== + version "1.38.0" + resolved "https://registry.yarnpkg.com/@substrate/ss58-registry/-/ss58-registry-1.38.0.tgz#b50cb28c77a0375fbf33dd29b7b28ee32871af9f" + integrity sha512-sHiVRWekGMRZAjPukN9/W166NM6D5wtHcK6RVyLy66kg3CHNZ1BXfpXcjOiXSwhbd7guQFDEwnOVaDrbk1XL1g== "@surma/rollup-plugin-off-main-thread@^1.1.1": version "1.4.2" @@ -6311,20 +6413,20 @@ "@typescript-eslint/types" "4.33.0" eslint-visitor-keys "^2.0.0" -"@unique-nft/opal-testnet-types@930.31.0": - version "930.31.0" - resolved "https://registry.yarnpkg.com/@unique-nft/opal-testnet-types/-/opal-testnet-types-930.31.0.tgz#dc989976b5e91b4d8358a7af624d6e5e2ebf0b87" - integrity sha512-IY4AxUx3uqjMEXy6iXrfVmu4oDTXOXaPjg5sb3WqnXpA7czjfSWZsQ/OtJFAWO+cbXUt8DM9ifs9/2hY2+O4RA== +"@unique-nft/opal-testnet-types@930.34.0": + version "930.34.0" + resolved "https://registry.yarnpkg.com/@unique-nft/opal-testnet-types/-/opal-testnet-types-930.34.0.tgz#e4274976ebc9614dbec6c1a074674a3620eacb6f" + integrity sha512-6N5MQC5o4V5J0PZ/JmhRfYOtJTSpCjxxM1pdGysh6aIu/rSey8ELa/9BnGwLIZsOPxW77PKwnt7NIRc01Sze3g== -"@unique-nft/quartz-mainnet-types@930.31.0": - version "930.31.0" - resolved "https://registry.yarnpkg.com/@unique-nft/quartz-mainnet-types/-/quartz-mainnet-types-930.31.0.tgz#009a37ac2dad085cfe0ebdca98de06f345fa35d6" - integrity sha512-oZdHnX2TfglJ43PjKwAnUhx5VIcCDpeeQtRC8cXSvVKE2CqkW5ry/rgyavAs+HeUrwsD5JXHtebgH9P1dZ4vOw== +"@unique-nft/quartz-mainnet-types@930.34.0": + version "930.34.0" + resolved "https://registry.yarnpkg.com/@unique-nft/quartz-mainnet-types/-/quartz-mainnet-types-930.34.0.tgz#d99a744b10575533441a0ca13f855eeca45a9047" + integrity sha512-YwJ3h7Q0crnvGsYfBXjxtPIpQnB9T5JY1LLAapLGvOO3A0iA1PWbSiqAgOdjZTt4zivYm3IbdhxQhyyY6d5jLA== -"@unique-nft/unique-mainnet-types@930.31.0": - version "930.31.0" - resolved "https://registry.yarnpkg.com/@unique-nft/unique-mainnet-types/-/unique-mainnet-types-930.31.0.tgz#ea79a6fd3d9e8c115b13ef3048a87bf0a853c269" - integrity sha512-acAKLL2TNS7X886SiNjMHo0dVmCECFd9vUSJxBZ1yjVVOhf6P6Nn+gA8jjgLKSg89VAqNQ9Op7a/vBN0Xo8+nw== +"@unique-nft/unique-mainnet-types@930.33.0": + version "930.33.0" + resolved "https://registry.yarnpkg.com/@unique-nft/unique-mainnet-types/-/unique-mainnet-types-930.33.0.tgz#196bbe704882ad826b709c5ec9cbbb8067e456ee" + integrity sha512-KlliDzrwcyl1igi/rjltue/T6DZQP5yAijcFzWtCsKfLzkCPxcplzYgd5S+VKRoAFrndOMVXleXTUgpPSYiL9Q== "@uphold/request-logger@^2.0.0": version "2.0.0" @@ -6614,10 +6716,10 @@ resolved "https://registry.yarnpkg.com/@xtuc/long/-/long-4.2.2.tgz#d291c6a4e97989b5c61d9acf396ae4fe133a718d" integrity sha512-NuHqBY1PB/D8xU6s/thBgOAiAP7HOYDQ32+BFZILJ8ivkUkAHQnWfn6WhL79Owj1qmUnoN/YPhktdIoucipkAQ== -"@zeitgeistpm/type-defs@0.9.0": - version "0.9.0" - resolved "https://registry.yarnpkg.com/@zeitgeistpm/type-defs/-/type-defs-0.9.0.tgz#95496d4c7984c87cf53eeed1b97283e5c4538362" - integrity sha512-H3EuEjrKtMlZBKEl08427Bda/c0t9BaUiwBNPn2T8ppM1RCEzfd1/3riHce6CyBCAQKR+w47Dylc+qK2VrBbNQ== +"@zeitgeistpm/type-defs@0.10.0": + version "0.10.0" + resolved "https://registry.yarnpkg.com/@zeitgeistpm/type-defs/-/type-defs-0.10.0.tgz#7f551f949b45b082541a254a9845ab15b2ff9148" + integrity sha512-nQBdyRbkIopPOVjRHk9c/RBWiQI6iYE8fs5rmtSNCXm6IxoXssk/1PtWE+UxXXq9mco7rPao9nJMeYXJ1Ro2kg== "@zeroio/type-definitions@0.0.14": version "0.0.14" @@ -8160,6 +8262,13 @@ caniuse-lite@^1.0.0, caniuse-lite@^1.0.30000981, caniuse-lite@^1.0.30001109, can resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001342.tgz#87152b1e3b950d1fbf0093e23f00b6c8e8f1da96" integrity sha512-bn6sOCu7L7jcbBbyNhLg0qzXdJ/PMbybZTH/BA6Roet9wxYRm6Tr9D0s0uhLkOZ6MSG+QU6txUgdpr3MXIVqjA== +canvas-renderer@~2.2.0: + version "2.2.1" + resolved "https://registry.yarnpkg.com/canvas-renderer/-/canvas-renderer-2.2.1.tgz#c1d131f78a9799aca8af9679ad0a005052b65550" + integrity sha512-RrBgVL5qCEDIXpJ6NrzyRNoTnXxYarqm/cS/W6ERhUJts5UQtt/XPEosGN3rqUkZ4fjBArlnCbsISJ+KCFnIAg== + dependencies: + "@types/node" "*" + capture-exit@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/capture-exit/-/capture-exit-2.0.0.tgz#fb953bfaebeb781f62898239dabb426d08a509a4" @@ -8547,7 +8656,7 @@ color-support@^1.1.2: resolved "https://registry.yarnpkg.com/color-support/-/color-support-1.1.3.tgz#93834379a1cc9a0c61f82f52f0d04322251bd5a2" integrity sha512-qiBjkpbMLO/HL68y+lh4q0/O1MZFj2RX6X/KmMa3+gJD3z+WwI1ZzDHysvqHGS3mP6mznPckpXmw1nI9cJjyRg== -color@^3.0.0: +color@^3.0.0, color@^3.2.1: version "3.2.1" resolved "https://registry.yarnpkg.com/color/-/color-3.2.1.tgz#3544dc198caf4490c3ecc9a790b54fe9ff45e164" integrity sha512-aBl7dZI9ENN6fUGC7mWpMTPNHmWUSNan9tuWN6ahh5ZLNk9baLJOnSMlrQkHcrfFgz2/RigjUVAjdx36VcemKA== @@ -10464,6 +10573,13 @@ etag@~1.8.1: resolved "https://registry.yarnpkg.com/etag/-/etag-1.8.1.tgz#41ae2eeb65efa62268aebfea83ac7d79299b0887" integrity sha1-Qa4u62XvpiJorr/qg6x9eSmbCIc= +ethereum-blockies-base64@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/ethereum-blockies-base64/-/ethereum-blockies-base64-1.0.2.tgz#4aebca52142bf4d16a3144e6e2b59303e39ed2b3" + integrity sha512-Vg2HTm7slcWNKaRhCUl/L3b4KrB8ohQXdd5Pu3OI897EcR6tVRvUqdTwAyx+dnmoDzj8e2bwBLDQ50ByFmcz6w== + dependencies: + pnglib "0.0.1" + ethers@^5.6.2, ethers@~5.7.0: version "5.7.2" resolved "https://registry.yarnpkg.com/ethers/-/ethers-5.7.2.tgz#3a7deeabbb8c030d4126b24f84e525466145872e" @@ -13086,6 +13202,13 @@ iterate-value@^1.0.2: es-get-iterator "^1.0.2" iterate-iterator "^1.0.1" +jdenticon@3.2.0: + version "3.2.0" + resolved "https://registry.yarnpkg.com/jdenticon/-/jdenticon-3.2.0.tgz#b5b9ef413cb66f70c600d6e69a764c977f248a46" + integrity sha512-z6Iq3fTODUMSOiR2nNYrqigS6Y0GvdXfyQWrUby7htDHvX7GNEwaWR4hcaL+FmhEgBe08Xkup/BKxXQhDJByPA== + dependencies: + canvas-renderer "~2.2.0" + 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" @@ -15843,6 +15966,11 @@ please-upgrade-node@^3.2.0: dependencies: semver-compare "^1.0.0" +pnglib@0.0.1: + version "0.0.1" + resolved "https://registry.yarnpkg.com/pnglib/-/pnglib-0.0.1.tgz#f9ab6f9c688f4a9d579ad8be28878a716e30c096" + integrity sha512-95ChzOoYLOPIyVmL+Y6X+abKGXUJlvOVLkB1QQkyXl7Uczc6FElUy/x01NS7r2GX6GRezloO/ecCX9h4U9KadA== + pnp-webpack-plugin@1.6.4: version "1.6.4" resolved "https://registry.yarnpkg.com/pnp-webpack-plugin/-/pnp-webpack-plugin-1.6.4.tgz#c9711ac4dc48a685dabafc86f8b6dd9f8df84149" @@ -17071,6 +17199,14 @@ react-chartjs-2@^2.11.1: lodash "^4.17.19" prop-types "^15.7.2" +react-copy-to-clipboard@^5.1.0: + version "5.1.0" + resolved "https://registry.yarnpkg.com/react-copy-to-clipboard/-/react-copy-to-clipboard-5.1.0.tgz#09aae5ec4c62750ccb2e6421a58725eabc41255c" + integrity sha512-k61RsNgAayIJNoy9yDsYzDe/yAZAzEbEgcz3DZMhF686LEyukcE1hzurxe85JandPUG+yTfGVFzuEw3xt8WP/A== + dependencies: + copy-to-clipboard "^3.3.1" + prop-types "^15.8.1" + react-dev-utils@^11.0.3: version "11.0.4" resolved "https://registry.yarnpkg.com/react-dev-utils/-/react-dev-utils-11.0.4.tgz#a7ccb60257a1ca2e0efe7a83e38e6700d17aa37a" @@ -19177,7 +19313,7 @@ style-to-object@0.3.0, style-to-object@^0.3.0: dependencies: inline-style-parser "0.1.1" -styled-components@^5, styled-components@^5.3.5: +styled-components@^5, styled-components@^5.3.5, styled-components@^5.3.6: version "5.3.5" resolved "https://registry.yarnpkg.com/styled-components/-/styled-components-5.3.5.tgz#a750a398d01f1ca73af16a241dec3da6deae5ec4" integrity sha512-ndETJ9RKaaL6q41B69WudeqLzOpY1A/ET/glXkNZ2T7dPjPqpPCXXQjDFYZWwNnE5co0wX+gTCqx9mfxTmSIPg==