diff --git a/.env.dev b/.env.dev
index 11f16713a9..95c64b470e 100644
--- a/.env.dev
+++ b/.env.dev
@@ -4,7 +4,7 @@ REACT_APP_FEATURE_FLAG_LENDING=enabled
REACT_APP_FEATURE_FLAG_AMM=enabled
REACT_APP_FEATURE_FLAG_WALLET=enabled
REACT_APP_FEATURE_FLAG_BANXA=enabled
-
+REACT_APP_FEATURE_FLAG_EARN_STRATEGIES=enabled
/* DEVELOPMENT */
diff --git a/api/health.py b/api/health.py
index 6638f2a4e8..ae977b2b53 100644
--- a/api/health.py
+++ b/api/health.py
@@ -2,7 +2,7 @@
import dateutil.parser
from datetime import datetime
from dateutil.tz import tzutc
-from flask import Flask, jsonify
+from flask import Flask, jsonify, abort
class Oracle:
@@ -80,30 +80,85 @@ def isHealthy(self):
return status["chainHeightDiff"] < 3 and status["secondsDiff"] < 7200 # 2hrs
-KSM_URL = "https://api-kusama.interlay.io/graphql/graphql"
-INTR_URL = "https://api.interlay.io/graphql/graphql"
-
-app = Flask(__name__)
-
+class Vault:
+ def __init__(self, baseUrl) -> None:
+ self.baseUrl = baseUrl
-@app.route("/_health/ksm/oracle", methods=["GET"])
-def get_ksm_oracle_health():
- return jsonify(Oracle(KSM_URL, "KSM").isHealthy())
+ def _latestVaults(self):
+ q = """
+ query MyQuery {
+ vaults(limit: 10, orderBy: registrationBlock_active_DESC) {
+ id
+ registrationTimestamp
+ }
+ }
+ """
+ payload = {"query": q, "variables": None}
+ resp = requests.post(self.baseUrl, json=payload)
+ return resp.json()["data"]["vaults"]
+ def isHealthy(self):
+ vaults = self._latestVaults()
+ return len(self._latestVaults()) > 0
-@app.route("/_health/ksm/relay", methods=["GET"])
-def get_ksm_relayer_health():
- return jsonify(Relayer(KSM_URL).isHealthy())
+KSM_URL = "https://api-kusama.interlay.io/graphql/graphql"
+INTR_URL = "https://api.interlay.io/graphql/graphql"
+TESTNET_INTR = "https://api-testnet.interlay.io/graphql/graphql"
+TESTNET_KINT = "https://api-dev-kintsugi.interlay.io/graphql/graphql"
-@app.route("/_health/intr/oracle", methods=["GET"])
-def get_intr_oracle_health():
- return jsonify(Oracle(INTR_URL, "DOT").isHealthy())
+app = Flask(__name__)
-@app.route("/_health/intr/relay", methods=["GET"])
-def get_intr_relayer_health():
- return jsonify(Relayer(INTR_URL).isHealthy())
+@app.route("/_health//oracle", methods=["GET"])
+def get_oracle_health(chain):
+ def oracle():
+ if chain == "kint":
+ return Oracle(KSM_URL, "KSM")
+ elif chain == "intr":
+ return Oracle(INTR_URL, "DOT")
+ elif chain == "testnet_kint":
+ return Oracle(TESTNET_KINT, "KSM")
+ elif chain == "testnet_intr":
+ return Oracle(TESTNET_INTR, "DOT")
+ else:
+ abort(404)
+
+ return jsonify(oracle().isHealthy())
+
+
+@app.route("/_health//relay", methods=["GET"])
+def get_relay_health(chain):
+ def relay():
+ if chain == "kint":
+ return Relayer(KSM_URL)
+ elif chain == "intr":
+ return Relayer(INTR_URL)
+ elif chain == "testnet_kint":
+ return Relayer(TESTNET_KINT)
+ elif chain == "testnet_intr":
+ return Relayer(TESTNET_INTR)
+ else:
+ abort(404)
+
+ return jsonify(relay().isHealthy())
+
+
+@app.route("/_health//vault", methods=["GET"])
+def get_vault_health(chain):
+ def vault():
+ if chain == "kint":
+ return Vault(KSM_URL)
+ elif chain == "intr":
+ return Vault(INTR_URL)
+ elif chain == "testnet_kint":
+ return Vault(TESTNET_KINT)
+ elif chain == "testnet_intr":
+ return Vault(TESTNET_INTR)
+ else:
+ abort(404)
+
+ return jsonify(vault().isHealthy())
if __name__ == "__main__":
diff --git a/package.json b/package.json
index a7a04ec4d7..735d72e184 100644
--- a/package.json
+++ b/package.json
@@ -1,16 +1,17 @@
{
"name": "interbtc-ui",
- "version": "2.31.3",
+ "version": "2.32.4",
"private": true,
"dependencies": {
"@craco/craco": "^6.1.1",
"@headlessui/react": "^1.1.1",
- "@heroicons/react": "^2.0.0",
- "@interlay/bridge": "^0.2.9",
- "@interlay/interbtc-api": "2.2.2",
- "@interlay/monetary-js": "0.7.2",
+ "@heroicons/react": "^2.0.18",
+ "@interlay/bridge": "^0.3.10",
+ "@interlay/interbtc-api": "2.2.4",
+ "@interlay/monetary-js": "0.7.3",
"@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/App.tsx b/src/App.tsx
index 3a16fbf509..463f6f1528 100644
--- a/src/App.tsx
+++ b/src/App.tsx
@@ -26,6 +26,7 @@ import TestnetBanner from './legacy-components/TestnetBanner';
import { FeatureFlags, useFeatureFlag } from './utils/hooks/use-feature-flag';
const Bridge = React.lazy(() => import(/* webpackChunkName: 'bridge' */ '@/pages/Bridge'));
+const EarnStrategies = React.lazy(() => import(/* webpackChunkName: 'earn-strategies' */ '@/pages/EarnStrategies'));
const Transfer = React.lazy(() => import(/* webpackChunkName: 'transfer' */ '@/pages/Transfer'));
const Transactions = React.lazy(() => import(/* webpackChunkName: 'transactions' */ '@/pages/Transactions'));
const TX = React.lazy(() => import(/* webpackChunkName: 'tx' */ '@/pages/TX'));
@@ -35,9 +36,9 @@ const Vaults = React.lazy(() => import(/* webpackChunkName: 'vaults' */ '@/pages
// TODO: last task will be to delete legacy dashboard and rename vault dashboard
const Vault = React.lazy(() => import(/* webpackChunkName: 'vault' */ '@/pages/Vaults/Vault'));
const Loans = React.lazy(() => import(/* webpackChunkName: 'loans' */ '@/pages/Loans'));
-const Swap = React.lazy(() => import(/* webpackChunkName: 'loans' */ '@/pages/AMM'));
-const Pools = React.lazy(() => import(/* webpackChunkName: 'loans' */ '@/pages/AMM/Pools'));
-const Wallet = React.lazy(() => import(/* webpackChunkName: 'loans' */ '@/pages/Wallet'));
+const Swap = React.lazy(() => import(/* webpackChunkName: 'amm' */ '@/pages/AMM'));
+const Pools = React.lazy(() => import(/* webpackChunkName: 'amm/pools' */ '@/pages/AMM/Pools'));
+const Wallet = React.lazy(() => import(/* webpackChunkName: 'wallet' */ '@/pages/Wallet'));
const Actions = React.lazy(() => import(/* webpackChunkName: 'actions' */ '@/pages/Actions'));
const NoMatch = React.lazy(() => import(/* webpackChunkName: 'no-match' */ '@/pages/NoMatch'));
@@ -50,6 +51,7 @@ const App = (): JSX.Element => {
const isLendingEnabled = useFeatureFlag(FeatureFlags.LENDING);
const isAMMEnabled = useFeatureFlag(FeatureFlags.AMM);
const isWalletEnabled = useFeatureFlag(FeatureFlags.WALLET);
+ const isEarnStrategiesEnabled = useFeatureFlag(FeatureFlags.EARN_STRATEGIES);
// Loads the connection to the faucet - only for testnet purposes
const loadFaucet = React.useCallback(async (): Promise => {
@@ -212,6 +214,11 @@ const App = (): JSX.Element => {
)}
+ {isEarnStrategiesEnabled && (
+
+
+
+ )}
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..e3a0d7164b 100644
--- a/src/assets/locales/en/translation.json
+++ b/src/assets/locales/en/translation.json
@@ -74,6 +74,7 @@
"issue": "Issue",
"redeem": "Redeem",
"nav_bridge": "Bridge",
+ "nav_earn_strategies": "Earn Strategies",
"nav_transfer": "Transfer",
"nav_lending": "Lending",
"nav_swap": "Swap",
@@ -602,6 +603,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/Geoblock/Geoblock.tsx b/src/components/Geoblock/Geoblock.tsx
new file mode 100644
index 0000000000..0a308d5619
--- /dev/null
+++ b/src/components/Geoblock/Geoblock.tsx
@@ -0,0 +1,14 @@
+import { ReactNode } from 'react';
+
+import { useGeoblocking } from '@/utils/hooks/use-geoblocking';
+
+type Props = {
+ children: ReactNode;
+};
+
+const GeoblockingWrapper = ({ children }: Props): JSX.Element => {
+ useGeoblocking();
+ return <>{children}>;
+};
+
+export { GeoblockingWrapper };
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/links.ts b/src/config/links.ts
index 8f1d7557b1..b2d84b6a02 100644
--- a/src/config/links.ts
+++ b/src/config/links.ts
@@ -21,8 +21,13 @@ const INTERLAY_DOS_AND_DONTS_DOCS_LINK = 'https://docs.interlay.io/#/vault/insta
const BANXA_LINK = 'http://talisman.banxa.com/';
+const GEOBLOCK_API_ENDPOINT = '/check_access';
+const GEOBLOCK_REDIRECTION_LINK = 'https://www.interlay.io/geoblock';
+
export {
BANXA_LINK,
+ GEOBLOCK_API_ENDPOINT,
+ GEOBLOCK_REDIRECTION_LINK,
INTERLAY_COMPANY_LINK,
INTERLAY_CROWDLOAN_LINK,
INTERLAY_DISCORD_LINK,
diff --git a/src/config/relay-chains.tsx b/src/config/relay-chains.tsx
index de17408744..de9302d73f 100644
--- a/src/config/relay-chains.tsx
+++ b/src/config/relay-chains.tsx
@@ -1,5 +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,8 +160,11 @@ switch (process.env.REACT_APP_RELAY_CHAIN_NAME) {
// TODO: temporary
TRANSACTION_FEE_AMOUNT = newMonetaryAmount(0.2, GOVERNANCE_TOKEN, true);
XCM_ADAPTERS = {
- hydra: new HydraAdapter(),
interlay: new InterlayAdapter(),
+ acala: new AcalaAdapter(),
+ astar: new AstarAdapter(),
+ hydra: new HydraAdapter(),
+ parallel: new ParallelAdapter(),
polkadot: new PolkadotAdapter(),
statemint: new StatemintAdapter()
};
@@ -201,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/index.tsx b/src/index.tsx
index 4a8ef1ba46..4901f7d9a1 100644
--- a/src/index.tsx
+++ b/src/index.tsx
@@ -18,6 +18,7 @@ import ThemeWrapper from '@/parts/ThemeWrapper';
import { Subscriptions } from '@/utils/hooks/api/tokens/use-balances-subscription';
import App from './App';
+import { GeoblockingWrapper } from './components/Geoblock/Geoblock';
import reportWebVitals from './reportWebVitals';
import { store } from './store';
@@ -30,26 +31,28 @@ const queryClient = new QueryClient();
// MEMO: temporarily removed React.StrictMode. We should add back when react-spectrum handles
// it across their library. (Issue: https://github.com/adobe/react-spectrum/issues/779#issuecomment-1353734729)
ReactDOM.render(
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- ,
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ ,
document.getElementById('root')
);
diff --git a/src/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/legacy-components/IssueUI/IssueRequestStatusUI/ConfirmedIssueRequest/index.tsx b/src/legacy-components/IssueUI/IssueRequestStatusUI/ConfirmedIssueRequest/index.tsx
index 3a758d2989..e76d52fe2d 100644
--- a/src/legacy-components/IssueUI/IssueRequestStatusUI/ConfirmedIssueRequest/index.tsx
+++ b/src/legacy-components/IssueUI/IssueRequestStatusUI/ConfirmedIssueRequest/index.tsx
@@ -1,8 +1,7 @@
-import { ISubmittableResult } from '@polkadot/types/types';
import clsx from 'clsx';
import { useTranslation } from 'react-i18next';
import { FaCheckCircle } from 'react-icons/fa';
-import { useMutation, useQueryClient } from 'react-query';
+import { useQueryClient } from 'react-query';
import { toast } from 'react-toastify';
import { BTC_EXPLORER_TRANSACTION_API } from '@/config/blockstream-explorer-links';
@@ -16,7 +15,7 @@ import { TABLE_PAGE_LIMIT } from '@/utils/constants/general';
import { QUERY_PARAMETERS } from '@/utils/constants/links';
import { KUSAMA, POLKADOT } from '@/utils/constants/relay-chain-names';
import { getColorShade } from '@/utils/helpers/colors';
-import { submitExtrinsicPromise } from '@/utils/helpers/extrinsic';
+import { Transaction, useTransaction } from '@/utils/hooks/transaction';
import useQueryParams from '@/utils/hooks/use-query-params';
import ManualIssueExecutionUI from '../ManualIssueExecutionUI';
@@ -34,21 +33,15 @@ const ConfirmedIssueRequest = ({ request }: Props): JSX.Element => {
const selectedPageIndex = selectedPage - 1;
const queryClient = useQueryClient();
- // TODO: should type properly (`Relay`)
- const executeMutation = useMutation(
- (variables: any) => {
- if (!variables.backingPayment.btcTxId) {
- throw new Error('Bitcoin transaction ID not identified yet.');
- }
- return submitExtrinsicPromise(window.bridge.issue.execute(variables.id, variables.backingPayment.btcTxId));
- },
- {
- onSuccess: (_, variables) => {
- queryClient.invalidateQueries([ISSUES_FETCHER, selectedPageIndex * TABLE_PAGE_LIMIT, TABLE_PAGE_LIMIT]);
- toast.success(t('issue_page.successfully_executed', { id: variables.id }));
- }
+
+ // TODO: check if this transaction is necessary
+ const transaction = useTransaction(Transaction.ISSUE_EXECUTE, {
+ onSuccess: (_, variables) => {
+ const [requestId] = variables.args;
+ queryClient.invalidateQueries([ISSUES_FETCHER, selectedPageIndex * TABLE_PAGE_LIMIT, TABLE_PAGE_LIMIT]);
+ toast.success(t('issue_page.successfully_executed', { id: requestId }));
}
- );
+ });
return (
<>
@@ -82,16 +75,14 @@ const ConfirmedIssueRequest = ({ request }: Props): JSX.Element => {
- {executeMutation.isError && executeMutation.error && (
+ {transaction.isError && transaction.error && (
{
- executeMutation.reset();
+ transaction.reset();
}}
title='Error'
- description={
- typeof executeMutation.error === 'string' ? executeMutation.error : executeMutation.error.message
- }
+ description={typeof transaction.error === 'string' ? transaction.error : transaction.error.message}
/>
)}
>
diff --git a/src/legacy-components/IssueUI/IssueRequestStatusUI/ManualIssueExecutionUI/index.tsx b/src/legacy-components/IssueUI/IssueRequestStatusUI/ManualIssueExecutionUI/index.tsx
index 5aee169f45..c93aa9aae2 100644
--- a/src/legacy-components/IssueUI/IssueRequestStatusUI/ManualIssueExecutionUI/index.tsx
+++ b/src/legacy-components/IssueUI/IssueRequestStatusUI/ManualIssueExecutionUI/index.tsx
@@ -5,10 +5,9 @@ import {
newAccountId,
newMonetaryAmount
} from '@interlay/interbtc-api';
-import { ISubmittableResult } from '@polkadot/types/types';
import clsx from 'clsx';
import { useTranslation } from 'react-i18next';
-import { useMutation, useQuery, useQueryClient } from 'react-query';
+import { useQuery, useQueryClient } from 'react-query';
import { toast } from 'react-toastify';
import { displayMonetaryAmount } from '@/common/utils/utils';
@@ -21,7 +20,7 @@ import { TABLE_PAGE_LIMIT } from '@/utils/constants/general';
import { QUERY_PARAMETERS } from '@/utils/constants/links';
import { KUSAMA, POLKADOT } from '@/utils/constants/relay-chain-names';
import { getColorShade } from '@/utils/helpers/colors';
-import { submitExtrinsicPromise } from '@/utils/helpers/extrinsic';
+import { Transaction, useTransaction } from '@/utils/hooks/transaction';
import useQueryParams from '@/utils/hooks/use-query-params';
// TODO: issue requests should not be typed here but further above in the app
@@ -57,21 +56,13 @@ const ManualIssueExecutionUI = ({ request }: Props): JSX.Element => {
const queryClient = useQueryClient();
- // TODO: should type properly (`Relay`)
- const executeMutation = useMutation(
- (variables: any) => {
- if (!variables.backingPayment.btcTxId) {
- throw new Error('Bitcoin transaction ID not identified yet.');
- }
- return submitExtrinsicPromise(window.bridge.issue.execute(variables.id, variables.backingPayment.btcTxId));
- },
- {
- onSuccess: (_, variables) => {
- queryClient.invalidateQueries([ISSUES_FETCHER, selectedPageIndex * TABLE_PAGE_LIMIT, TABLE_PAGE_LIMIT]);
- toast.success(t('issue_page.successfully_executed', { id: variables.id }));
- }
+ const transaction = useTransaction(Transaction.ISSUE_EXECUTE, {
+ onSuccess: (_, variables) => {
+ const [requestId] = variables.args;
+ queryClient.invalidateQueries([ISSUES_FETCHER, selectedPageIndex * TABLE_PAGE_LIMIT, TABLE_PAGE_LIMIT]);
+ toast.success(t('issue_page.successfully_executed', { id: requestId }));
}
- );
+ });
const { data: vaultCapacity, error: vaultCapacityError } = useQuery({
queryKey: 'vault-capacity',
@@ -91,7 +82,12 @@ const ManualIssueExecutionUI = ({ request }: Props): JSX.Element => {
// TODO: should type properly (`Relay`)
const handleExecute = (request: any) => () => {
- executeMutation.mutate(request);
+ if (!request.backingPayment.btcTxId) {
+ console.error('Bitcoin transaction ID not identified yet.');
+ return;
+ }
+
+ transaction.execute(request.id, request.backingPayment.btcTxId);
};
const backingPaymentAmount = newMonetaryAmount(request.backingPayment.amount, WRAPPED_TOKEN);
@@ -135,7 +131,7 @@ const ManualIssueExecutionUI = ({ request }: Props): JSX.Element => {
)}
@@ -143,16 +139,14 @@ const ManualIssueExecutionUI = ({ request }: Props): JSX.Element => {
wrappedTokenSymbol: WRAPPED_TOKEN_SYMBOL
})}
- {executeMutation.isError && executeMutation.error && (
+ {transaction.isError && transaction.error && (
{
- executeMutation.reset();
+ transaction.reset();
}}
title='Error'
- description={
- typeof executeMutation.error === 'string' ? executeMutation.error : executeMutation.error.message
- }
+ description={typeof transaction.error === 'string' ? transaction.error : transaction.error.message}
/>
)}
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/AMM/Pools/components/DepositForm/DepositForm.tsx b/src/pages/AMM/Pools/components/DepositForm/DepositForm.tsx
index 4a7030bc4c..4baf947a1b 100644
--- a/src/pages/AMM/Pools/components/DepositForm/DepositForm.tsx
+++ b/src/pages/AMM/Pools/components/DepositForm/DepositForm.tsx
@@ -1,11 +1,8 @@
-import { CurrencyExt, LiquidityPool, newMonetaryAmount, PooledCurrencies } from '@interlay/interbtc-api';
-import { AccountId } from '@polkadot/types/interfaces';
-import { ISubmittableResult } from '@polkadot/types/types';
+import { CurrencyExt, LiquidityPool, newMonetaryAmount } from '@interlay/interbtc-api';
import { mergeProps } from '@react-aria/utils';
import Big from 'big.js';
import { ChangeEventHandler, RefObject, useState } from 'react';
import { useTranslation } from 'react-i18next';
-import { useMutation } from 'react-query';
import { toast } from 'react-toastify';
import { displayMonetaryAmountInUSDFormat, newSafeMonetaryAmount } from '@/common/utils/utils';
@@ -21,10 +18,10 @@ import {
} from '@/lib/form';
import { SlippageManager } from '@/pages/AMM/shared/components';
import { AMM_DEADLINE_INTERVAL } from '@/utils/constants/api';
-import { submitExtrinsic } from '@/utils/helpers/extrinsic';
import { getTokenPrice } from '@/utils/helpers/prices';
import { useGetBalances } from '@/utils/hooks/api/tokens/use-get-balances';
import { useGetPrices } from '@/utils/hooks/api/use-get-prices';
+import { Transaction, useTransaction } from '@/utils/hooks/transaction';
import useAccountId from '@/utils/hooks/use-account-id';
import { PoolName } from '../PoolName';
@@ -35,17 +32,6 @@ import { DepositOutputAssets } from './DepositOutputAssets';
const isCustomAmountsMode = (form: ReturnType) =>
form.dirty && Object.values(form.touched).filter(Boolean).length > 0;
-type DepositData = {
- amounts: PooledCurrencies;
- pool: LiquidityPool;
- slippage: number;
- deadline: number;
- accountId: AccountId;
-};
-
-const mutateDeposit = ({ amounts, pool, slippage, deadline, accountId }: DepositData) =>
- submitExtrinsic(window.bridge.amm.addLiquidity(amounts, pool, slippage, deadline, accountId));
-
type DepositFormProps = {
pool: LiquidityPool;
slippageModalRef: RefObject;
@@ -65,7 +51,7 @@ const DepositForm = ({ pool, slippageModalRef, onDeposit }: DepositFormProps): J
const governanceBalance = getBalance(GOVERNANCE_TOKEN.ticker)?.free || newMonetaryAmount(0, GOVERNANCE_TOKEN);
- const depositMutation = useMutation(mutateDeposit, {
+ const transaction = useTransaction(Transaction.AMM_ADD_LIQUIDITY, {
onSuccess: () => {
onDeposit?.();
toast.success('Deposit successful');
@@ -85,7 +71,7 @@ const DepositForm = ({ pool, slippageModalRef, onDeposit }: DepositFormProps): J
const deadline = await window.bridge.system.getFutureBlockNumber(AMM_DEADLINE_INTERVAL);
- return depositMutation.mutate({ amounts, pool, slippage, deadline, accountId });
+ return transaction.execute(amounts, pool, slippage, deadline, accountId);
} catch (err: any) {
toast.error(err.toString());
}
@@ -106,7 +92,7 @@ const DepositForm = ({ pool, slippageModalRef, onDeposit }: DepositFormProps): J
initialValues: defaultValues,
validationSchema: depositLiquidityPoolSchema({ transactionFee: TRANSACTION_FEE_AMOUNT, governanceBalance, tokens }),
onSubmit: handleSubmit,
- disableValidation: depositMutation.isLoading
+ disableValidation: transaction.isLoading
});
const handleChange: ChangeEventHandler = (e) => {
@@ -203,7 +189,7 @@ const DepositForm = ({ pool, slippageModalRef, onDeposit }: DepositFormProps): J
-
+
{t('amm.pools.add_liquidity')}
diff --git a/src/pages/AMM/Pools/components/PoolsInsights/PoolsInsights.tsx b/src/pages/AMM/Pools/components/PoolsInsights/PoolsInsights.tsx
index a845df1639..1689c20a32 100644
--- a/src/pages/AMM/Pools/components/PoolsInsights/PoolsInsights.tsx
+++ b/src/pages/AMM/Pools/components/PoolsInsights/PoolsInsights.tsx
@@ -1,16 +1,15 @@
import { LiquidityPool } from '@interlay/interbtc-api';
import Big from 'big.js';
import { useTranslation } from 'react-i18next';
-import { useMutation } from 'react-query';
import { toast } from 'react-toastify';
import { formatUSD } from '@/common/utils/utils';
import { Card, Dl, DlGroup } from '@/component-library';
import { AuthCTA } from '@/components';
import { calculateAccountLiquidityUSD, calculateTotalLiquidityUSD } from '@/pages/AMM/shared/utils';
-import { submitExtrinsic } from '@/utils/helpers/extrinsic';
import { AccountPoolsData } from '@/utils/hooks/api/amm/use-get-account-pools';
import { useGetPrices } from '@/utils/hooks/api/use-get-prices';
+import { Transaction, useTransaction } from '@/utils/hooks/transaction';
import { StyledDd, StyledDt } from './PoolsInsights.style';
import { calculateClaimableFarmingRewardUSD } from './utils';
@@ -55,17 +54,11 @@ const PoolsInsights = ({ pools, accountPoolsData, refetch }: PoolsInsightsProps)
refetch();
};
- const mutateClaimRewards = async () => {
- if (accountPoolsData !== undefined) {
- await submitExtrinsic(window.bridge.amm.claimFarmingRewards(accountPoolsData.claimableRewards));
- }
- };
-
- const claimRewardsMutation = useMutation(mutateClaimRewards, {
+ const transaction = useTransaction(Transaction.AMM_CLAIM_REWARDS, {
onSuccess: handleSuccess
});
- const handleClickClaimRewards = () => claimRewardsMutation.mutate();
+ const handleClickClaimRewards = () => accountPoolsData && transaction.execute(accountPoolsData.claimableRewards);
const hasClaimableRewards = totalClaimableRewardUSD > 0;
return (
@@ -88,7 +81,7 @@ const PoolsInsights = ({ pools, accountPoolsData, refetch }: PoolsInsightsProps)
{formatUSD(totalClaimableRewardUSD, { compact: true })}
{hasClaimableRewards && (
-
+
Claim
)}
diff --git a/src/pages/AMM/Pools/components/WithdrawForm/WithdrawForm.tsx b/src/pages/AMM/Pools/components/WithdrawForm/WithdrawForm.tsx
index 3eeb392479..4f2ea60b55 100644
--- a/src/pages/AMM/Pools/components/WithdrawForm/WithdrawForm.tsx
+++ b/src/pages/AMM/Pools/components/WithdrawForm/WithdrawForm.tsx
@@ -1,11 +1,7 @@
-import { LiquidityPool, LpCurrency, newMonetaryAmount } from '@interlay/interbtc-api';
-import { MonetaryAmount } from '@interlay/monetary-js';
-import { AccountId } from '@polkadot/types/interfaces';
-import { ISubmittableResult } from '@polkadot/types/types';
+import { LiquidityPool, newMonetaryAmount } from '@interlay/interbtc-api';
import Big from 'big.js';
import { RefObject, useState } from 'react';
import { useTranslation } from 'react-i18next';
-import { useMutation } from 'react-query';
import { toast } from 'react-toastify';
import {
@@ -20,27 +16,16 @@ import { isFormDisabled, useForm, WITHDRAW_LIQUIDITY_POOL_FIELD } from '@/lib/fo
import { WithdrawLiquidityPoolFormData, withdrawLiquidityPoolSchema } from '@/lib/form/schemas';
import { SlippageManager } from '@/pages/AMM/shared/components';
import { AMM_DEADLINE_INTERVAL } from '@/utils/constants/api';
-import { submitExtrinsic } from '@/utils/helpers/extrinsic';
import { getTokenPrice } from '@/utils/helpers/prices';
import { useGetBalances } from '@/utils/hooks/api/tokens/use-get-balances';
import { useGetPrices } from '@/utils/hooks/api/use-get-prices';
+import { Transaction, useTransaction } from '@/utils/hooks/transaction';
import useAccountId from '@/utils/hooks/use-account-id';
import { PoolName } from '../PoolName';
import { WithdrawAssets } from './WithdrawAssets';
import { StyledDl } from './WithdrawForm.styles';
-type DepositData = {
- amount: MonetaryAmount;
- pool: LiquidityPool;
- slippage: number;
- deadline: number;
- accountId: AccountId;
-};
-
-const mutateWithdraw = ({ amount, pool, slippage, deadline, accountId }: DepositData) =>
- submitExtrinsic(window.bridge.amm.removeLiquidity(amount, pool, slippage, deadline, accountId));
-
type WithdrawFormProps = {
pool: LiquidityPool;
slippageModalRef: RefObject;
@@ -55,7 +40,7 @@ const WithdrawForm = ({ pool, slippageModalRef, onWithdraw }: WithdrawFormProps)
const prices = useGetPrices();
const { getBalance } = useGetBalances();
- const withdrawMutation = useMutation(mutateWithdraw, {
+ const transaction = useTransaction(Transaction.AMM_REMOVE_LIQUIDITY, {
onSuccess: () => {
onWithdraw?.();
toast.success('Withdraw successful');
@@ -85,7 +70,7 @@ const WithdrawForm = ({ pool, slippageModalRef, onWithdraw }: WithdrawFormProps)
const amount = newMonetaryAmount(data[WITHDRAW_LIQUIDITY_POOL_FIELD] || 0, lpToken, true);
const deadline = await window.bridge.system.getFutureBlockNumber(AMM_DEADLINE_INTERVAL);
- return withdrawMutation.mutate({ amount, pool, deadline, slippage, accountId });
+ return transaction.execute(amount, pool, slippage, deadline, accountId);
} catch (err: any) {
toast.error(err.toString());
}
@@ -157,7 +142,7 @@ const WithdrawForm = ({ pool, slippageModalRef, onWithdraw }: WithdrawFormProps)
-
+
{t('amm.pools.remove_liquidity')}
diff --git a/src/pages/AMM/Swap/components/SwapForm/SwapForm.tsx b/src/pages/AMM/Swap/components/SwapForm/SwapForm.tsx
index 716ca91398..a4d60bbcf6 100644
--- a/src/pages/AMM/Swap/components/SwapForm/SwapForm.tsx
+++ b/src/pages/AMM/Swap/components/SwapForm/SwapForm.tsx
@@ -1,12 +1,8 @@
import { CurrencyExt, LiquidityPool, newMonetaryAmount, Trade } from '@interlay/interbtc-api';
-import { MonetaryAmount } from '@interlay/monetary-js';
-import { AddressOrPair } from '@polkadot/api/types';
-import { ISubmittableResult } from '@polkadot/types/types';
import { mergeProps } from '@react-aria/utils';
import Big from 'big.js';
import { ChangeEventHandler, Key, useEffect, useMemo, useState } from 'react';
import { useTranslation } from 'react-i18next';
-import { useMutation } from 'react-query';
import { useSelector } from 'react-redux';
import { toast } from 'react-toastify';
import { useDebounce } from 'react-use';
@@ -26,11 +22,11 @@ import {
import { SlippageManager } from '@/pages/AMM/shared/components';
import { SwapPair } from '@/types/swap';
import { SWAP_PRICE_IMPACT_LIMIT } from '@/utils/constants/swap';
-import { submitExtrinsic } from '@/utils/helpers/extrinsic';
import { getTokenPrice } from '@/utils/helpers/prices';
import { useGetBalances } from '@/utils/hooks/api/tokens/use-get-balances';
import { useGetCurrencies } from '@/utils/hooks/api/use-get-currencies';
import { Prices, useGetPrices } from '@/utils/hooks/api/use-get-prices';
+import { Transaction, useTransaction } from '@/utils/hooks/transaction';
import useAccountId from '@/utils/hooks/use-account-id';
import { PriceImpactModal } from '../PriceImpactModal';
@@ -83,16 +79,6 @@ const getPoolPriceImpact = (trade: Trade | null | undefined, inputAmountUSD: num
: new Big(0)
});
-type SwapData = {
- trade: Trade;
- minimumAmountOut: MonetaryAmount;
- recipient: AddressOrPair;
- deadline: string | number;
-};
-
-const mutateSwap = ({ deadline, minimumAmountOut, recipient, trade }: SwapData) =>
- submitExtrinsic(window.bridge.amm.swap(trade, minimumAmountOut, recipient, deadline));
-
type Props = {
pair: SwapPair;
liquidityPools: LiquidityPool[];
@@ -126,6 +112,18 @@ const SwapForm = ({
const { data: balances, getBalance, getAvailableBalance } = useGetBalances();
const { data: currencies } = useGetCurrencies(bridgeLoaded);
+ const transaction = useTransaction(Transaction.AMM_SWAP, {
+ onSuccess: () => {
+ toast.success('Swap successful');
+ setTrade(undefined);
+ setInputAmount(undefined);
+ onSwap();
+ },
+ onError: (err) => {
+ toast.error(err.message);
+ }
+ });
+
useDebounce(
() => {
if (!pair.input || !pair.output || !inputAmount) {
@@ -141,18 +139,6 @@ const SwapForm = ({
[inputAmount, pair]
);
- const swapMutation = useMutation(mutateSwap, {
- onSuccess: () => {
- toast.success('Swap successful');
- setTrade(undefined);
- setInputAmount(undefined);
- onSwap();
- },
- onError: (err) => {
- toast.error(err.message);
- }
- });
-
const inputBalance = pair.input && getAvailableBalance(pair.input.ticker);
const outputBalance = pair.output && getAvailableBalance(pair.output.ticker);
@@ -174,12 +160,7 @@ const SwapForm = ({
const deadline = await window.bridge.system.getFutureBlockNumber(30 * 60);
- return swapMutation.mutate({
- trade,
- recipient: accountId,
- minimumAmountOut,
- deadline
- });
+ return transaction.execute(trade, minimumAmountOut, accountId, deadline);
} catch (err: any) {
toast.error(err.toString());
}
@@ -212,7 +193,7 @@ const SwapForm = ({
initialValues,
validationSchema: swapSchema({ [SWAP_INPUT_AMOUNT_FIELD]: inputSchemaParams }),
onSubmit: handleSubmit,
- disableValidation: swapMutation.isLoading,
+ disableValidation: transaction.isLoading,
validateOnMount: true
});
@@ -239,11 +220,11 @@ const SwapForm = ({
useEffect(() => {
const isAmountFieldEmpty = form.values[SWAP_INPUT_AMOUNT_FIELD] === '';
- if (isAmountFieldEmpty || !swapMutation.isSuccess) return;
+ if (isAmountFieldEmpty || !transaction.isSuccess) return;
form.setFieldValue(SWAP_INPUT_AMOUNT_FIELD, '');
// eslint-disable-next-line react-hooks/exhaustive-deps
- }, [swapMutation.isSuccess]);
+ }, [transaction.isSuccess]);
const handleChangeInput: ChangeEventHandler = (e) => {
setInputAmount(e.target.value);
@@ -341,7 +322,7 @@ const SwapForm = ({
/>
{trade && }
-
+
diff --git a/src/pages/Bridge/BurnForm/index.tsx b/src/pages/Bridge/BurnForm/index.tsx
index 622b842372..2063218a57 100644
--- a/src/pages/Bridge/BurnForm/index.tsx
+++ b/src/pages/Bridge/BurnForm/index.tsx
@@ -25,11 +25,11 @@ import Tokens, { TokenOption } from '@/legacy-components/Tokens';
import { ForeignAssetIdLiteral } from '@/types/currency';
import { KUSAMA, POLKADOT } from '@/utils/constants/relay-chain-names';
import STATUSES from '@/utils/constants/statuses';
-import { submitExtrinsic } from '@/utils/helpers/extrinsic';
import { getTokenPrice } from '@/utils/helpers/prices';
import { useGetBalances } from '@/utils/hooks/api/tokens/use-get-balances';
import { useGetCollateralCurrencies } from '@/utils/hooks/api/use-get-collateral-currencies';
import { useGetPrices } from '@/utils/hooks/api/use-get-prices';
+import { Transaction, useTransaction } from '@/utils/hooks/transaction';
const WRAPPED_TOKEN_AMOUNT = 'wrapped-token-amount';
@@ -73,6 +73,8 @@ const BurnForm = (): JSX.Element | null => {
const [submitStatus, setSubmitStatus] = React.useState(STATUSES.IDLE);
const [submitError, setSubmitError] = React.useState(null);
+ const transaction = useTransaction(Transaction.REDEEM_BURN);
+
const handleUpdateCollateral = (collateral: TokenOption) => {
const selectedCollateral = burnableCollateral?.find(
(token: BurnableCollateral) => token.currency.ticker === collateral.token.ticker
@@ -150,9 +152,8 @@ const BurnForm = (): JSX.Element | null => {
const onSubmit = async (data: BurnFormData) => {
try {
setSubmitStatus(STATUSES.PENDING);
- await submitExtrinsic(
- window.bridge.redeem.burn(new BitcoinAmount(data[WRAPPED_TOKEN_AMOUNT]), selectedCollateral.currency)
- );
+
+ await transaction.executeAsync(new BitcoinAmount(data[WRAPPED_TOKEN_AMOUNT]), selectedCollateral.currency);
setSubmitStatus(STATUSES.RESOLVED);
} catch (error) {
diff --git a/src/pages/Bridge/IssueForm/index.tsx b/src/pages/Bridge/IssueForm/index.tsx
index 6a871b7c1c..2e3b83db45 100644
--- a/src/pages/Bridge/IssueForm/index.tsx
+++ b/src/pages/Bridge/IssueForm/index.tsx
@@ -56,11 +56,11 @@ import genericFetcher, { GENERIC_FETCHER } from '@/services/fetchers/generic-fet
import { ForeignAssetIdLiteral } from '@/types/currency';
import { KUSAMA, POLKADOT } from '@/utils/constants/relay-chain-names';
import STATUSES from '@/utils/constants/statuses';
-import { getExtrinsicStatus, submitExtrinsic } from '@/utils/helpers/extrinsic';
import { getExchangeRate } from '@/utils/helpers/oracle';
import { getTokenPrice } from '@/utils/helpers/prices';
import { useGetBalances } from '@/utils/hooks/api/tokens/use-get-balances';
import { useGetPrices } from '@/utils/hooks/api/use-get-prices';
+import { Transaction, useTransaction } from '@/utils/hooks/transaction';
import ManualVaultSelectUI from '../ManualVaultSelectUI';
import SubmittedIssueRequestModal from './SubmittedIssueRequestModal';
@@ -142,6 +142,8 @@ const IssueForm = (): JSX.Element | null => {
});
useErrorHandler(requestLimitsError);
+ const transaction = useTransaction(Transaction.ISSUE_REQUEST);
+
React.useEffect(() => {
if (!bridgeLoaded) return;
if (!dispatch) return;
@@ -321,18 +323,14 @@ const IssueForm = (): JSX.Element | null => {
const collateralToken = await currencyIdToMonetaryCurrency(window.bridge.api, vaultId.currencies.collateral);
- const extrinsicData = await window.bridge.issue.request(
+ const result = await transaction.executeAsync(
monetaryBtcAmount,
vaultId.accountId,
collateralToken,
false, // default
vaults
);
- // When requesting an issue, wait for the finalized event because we cannot revert BTC transactions.
- // For more details see: https://github.com/interlay/interbtc-api/pull/373#issuecomment-1058949000
- const finalizedStatus = getExtrinsicStatus('Finalized');
- const extrinsicResult = await submitExtrinsic(extrinsicData, finalizedStatus);
- const issueRequests = await getIssueRequestsFromExtrinsicResult(window.bridge, extrinsicResult);
+ const issueRequests = await getIssueRequestsFromExtrinsicResult(window.bridge, result);
// TODO: handle issue aggregation
const issueRequest = issueRequests[0];
diff --git a/src/pages/Bridge/RedeemForm/index.tsx b/src/pages/Bridge/RedeemForm/index.tsx
index 1248b5167d..357bfcf540 100644
--- a/src/pages/Bridge/RedeemForm/index.tsx
+++ b/src/pages/Bridge/RedeemForm/index.tsx
@@ -1,5 +1,10 @@
-import { CollateralCurrencyExt, InterbtcPrimitivesVaultId, newMonetaryAmount, Redeem } from '@interlay/interbtc-api';
-import { getRedeemRequestsFromExtrinsicResult } from '@interlay/interbtc-api';
+import {
+ CollateralCurrencyExt,
+ getRedeemRequestsFromExtrinsicResult,
+ InterbtcPrimitivesVaultId,
+ newMonetaryAmount,
+ Redeem
+} from '@interlay/interbtc-api';
import { Bitcoin, BitcoinAmount, ExchangeRate } from '@interlay/monetary-js';
import Big from 'big.js';
import clsx from 'clsx';
@@ -44,11 +49,11 @@ import { ForeignAssetIdLiteral } from '@/types/currency';
import { KUSAMA, POLKADOT } from '@/utils/constants/relay-chain-names';
import STATUSES from '@/utils/constants/statuses';
import { getColorShade } from '@/utils/helpers/colors';
-import { getExtrinsicStatus, submitExtrinsic } from '@/utils/helpers/extrinsic';
import { getExchangeRate } from '@/utils/helpers/oracle';
import { getTokenPrice } from '@/utils/helpers/prices';
import { useGetBalances } from '@/utils/hooks/api/tokens/use-get-balances';
import { useGetPrices } from '@/utils/hooks/api/use-get-prices';
+import { Transaction, useTransaction } from '@/utils/hooks/transaction';
import ManualVaultSelectUI from '../ManualVaultSelectUI';
import SubmittedRedeemRequestModal from './SubmittedRedeemRequestModal';
@@ -114,6 +119,8 @@ const RedeemForm = (): JSX.Element | null => {
const [selectedVault, setSelectedVault] = React.useState();
+ const transaction = useTransaction(Transaction.REDEEM_REQUEST);
+
React.useEffect(() => {
if (!monetaryWrappedTokenAmount) return;
if (!maxRedeemableCapacity) return;
@@ -295,16 +302,10 @@ const RedeemForm = (): JSX.Element | null => {
const relevantVaults = new Map();
// FIXME: a bit of a dirty workaround with the capacity
relevantVaults.set(vaultId, monetaryWrappedTokenAmount.mul(2));
- const extrinsicData = await window.bridge.redeem.request(
- monetaryWrappedTokenAmount,
- data[BTC_ADDRESS],
- vaultId
- );
- // When requesting a redeem, wait for the finalized event because we cannot revert BTC transactions.
- // For more details see: https://github.com/interlay/interbtc-api/pull/373#issuecomment-1058949000
- const finalizedStatus = getExtrinsicStatus('Finalized');
- const extrinsicResult = await submitExtrinsic(extrinsicData, finalizedStatus);
- const redeemRequests = await getRedeemRequestsFromExtrinsicResult(window.bridge, extrinsicResult);
+
+ const result = await transaction.executeAsync(monetaryWrappedTokenAmount, data[BTC_ADDRESS], vaultId);
+
+ const redeemRequests = await getRedeemRequestsFromExtrinsicResult(window.bridge, result);
// TODO: handle redeem aggregator
const redeemRequest = redeemRequests[0];
diff --git a/src/pages/EarnStrategies/EarnStrategies.tsx b/src/pages/EarnStrategies/EarnStrategies.tsx
new file mode 100644
index 0000000000..84ab2c0d2b
--- /dev/null
+++ b/src/pages/EarnStrategies/EarnStrategies.tsx
@@ -0,0 +1,14 @@
+import { withErrorBoundary } from 'react-error-boundary';
+
+import ErrorFallback from '@/legacy-components/ErrorFallback';
+
+const EarnStrategies = (): JSX.Element => {
+ return Earn Strategies
;
+};
+
+export default withErrorBoundary(EarnStrategies, {
+ FallbackComponent: ErrorFallback,
+ onReset: () => {
+ window.location.reload();
+ }
+});
diff --git a/src/pages/EarnStrategies/index.tsx b/src/pages/EarnStrategies/index.tsx
new file mode 100644
index 0000000000..7a73a2c641
--- /dev/null
+++ b/src/pages/EarnStrategies/index.tsx
@@ -0,0 +1,3 @@
+import EarnStrategies from './EarnStrategies';
+
+export default EarnStrategies;
diff --git a/src/pages/Loans/LoansOverview/components/CollateralModal/CollateralModal.tsx b/src/pages/Loans/LoansOverview/components/CollateralModal/CollateralModal.tsx
index 9b545e17ff..2ec3d03b04 100644
--- a/src/pages/Loans/LoansOverview/components/CollateralModal/CollateralModal.tsx
+++ b/src/pages/Loans/LoansOverview/components/CollateralModal/CollateralModal.tsx
@@ -1,31 +1,19 @@
-import { CollateralPosition, CurrencyExt, LoanAsset } from '@interlay/interbtc-api';
-import { ISubmittableResult } from '@polkadot/types/types';
+import { CollateralPosition, LoanAsset } from '@interlay/interbtc-api';
import { TFunction, useTranslation } from 'react-i18next';
-import { useMutation } from 'react-query';
import { toast } from 'react-toastify';
import { Flex, Modal, ModalBody, ModalFooter, ModalHeader, ModalProps, Status } from '@/component-library';
import { AuthCTA } from '@/components';
import ErrorModal from '@/legacy-components/ErrorModal';
-import { submitExtrinsicPromise } from '@/utils/helpers/extrinsic';
import { useGetAccountLendingStatistics } from '@/utils/hooks/api/loans/use-get-account-lending-statistics';
import { useGetPrices } from '@/utils/hooks/api/use-get-prices';
+import { Transaction, useTransaction } from '@/utils/hooks/transaction';
import { useGetLTV } from '../../hooks/use-get-ltv';
import { BorrowLimit } from '../BorrowLimit';
import { LoanActionInfo } from '../LoanActionInfo';
import { StyledDescription } from './CollateralModal.style';
-type ToggleCollateralVariables = { isEnabling: boolean; underlyingCurrency: CurrencyExt };
-
-const toggleCollateral = ({ isEnabling, underlyingCurrency }: ToggleCollateralVariables) => {
- if (isEnabling) {
- return submitExtrinsicPromise(window.bridge.loans.enableAsCollateral(underlyingCurrency));
- } else {
- return submitExtrinsicPromise(window.bridge.loans.disableAsCollateral(underlyingCurrency));
- }
-};
-
type CollateralModalVariant = 'enable' | 'disable' | 'disable-error';
const getContentMap = (t: TFunction, variant: CollateralModalVariant, asset: LoanAsset) =>
@@ -73,14 +61,12 @@ const CollateralModal = ({ asset, position, onClose, ...props }: CollateralModal
const { getLTV } = useGetLTV();
const prices = useGetPrices();
- const handleSuccess = () => {
- toast.success('Successfully toggled collateral');
- onClose?.();
- refetch();
- };
-
- const toggleCollateralMutation = useMutation(toggleCollateral, {
- onSuccess: handleSuccess
+ const transaction = useTransaction({
+ onSuccess: () => {
+ toast.success('Successfully toggled collateral');
+ onClose?.();
+ refetch();
+ }
});
if (!asset || !position) {
@@ -100,9 +86,11 @@ const CollateralModal = ({ asset, position, onClose, ...props }: CollateralModal
return onClose?.();
}
- const isEnabling = variant === 'enable';
-
- return toggleCollateralMutation.mutate({ isEnabling, underlyingCurrency: position.amount.currency });
+ if (variant === 'enable') {
+ return transaction.execute(Transaction.LOANS_ENABLE_COLLATERAL, asset.currency);
+ } else {
+ return transaction.execute(Transaction.LOANS_DISABLE_COLLATERAL, asset.currency);
+ }
};
return (
@@ -117,17 +105,17 @@ const CollateralModal = ({ asset, position, onClose, ...props }: CollateralModal
-
+
{content.buttonLabel}
- {toggleCollateralMutation.isError && (
+ {transaction.isError && (
toggleCollateralMutation.reset()}
+ open={transaction.isError}
+ onClose={() => transaction.reset()}
title='Error'
- description={toggleCollateralMutation.error?.message || ''}
+ description={transaction.error?.message || ''}
/>
)}
>
diff --git a/src/pages/Loans/LoansOverview/components/LoanForm/LoanForm.tsx b/src/pages/Loans/LoansOverview/components/LoanForm/LoanForm.tsx
index bd89f6b57e..edc7763901 100644
--- a/src/pages/Loans/LoansOverview/components/LoanForm/LoanForm.tsx
+++ b/src/pages/Loans/LoansOverview/components/LoanForm/LoanForm.tsx
@@ -12,8 +12,8 @@ import { AuthCTA } from '@/components';
import { isFormDisabled, LoanFormData, loanSchema, LoanValidationParams, useForm } from '@/lib/form';
import { LoanAction } from '@/types/loans';
import { useGetAccountPositions } from '@/utils/hooks/api/loans/use-get-account-positions';
-import { useLoanMutation } from '@/utils/hooks/api/loans/use-loan-mutation';
import { useGetPrices } from '@/utils/hooks/api/use-get-prices';
+import { Transaction, useTransaction } from '@/utils/hooks/transaction';
import { useLoanFormData } from '../../hooks/use-loan-form-data';
import { isLendAsset } from '../../utils/is-loan-asset';
@@ -116,18 +116,45 @@ const LoanForm = ({ asset, variant, position, onChangeLoan }: LoanFormProps): JS
[inputAmount]
);
- const handleSuccess = () => {
- toast.success(`Successful ${content.title.toLowerCase()}`);
- onChangeLoan?.();
- refetch();
- };
+ const transaction = useTransaction({
+ onSuccess: () => {
+ toast.success(`Successful ${content.title.toLowerCase()}`);
+ onChangeLoan?.();
+ refetch();
+ },
+ onError: (error: Error) => {
+ toast.error(error.message);
+ }
+ });
- const handleError = (error: Error) => {
- toast.error(error.message);
+ const handleSubmit = (data: LoanFormData) => {
+ try {
+ const amount = data[variant] || 0;
+ const monetaryAmount = newMonetaryAmount(amount, asset.currency, true);
+
+ switch (variant) {
+ case 'lend':
+ return transaction.execute(Transaction.LOANS_LEND, monetaryAmount.currency, monetaryAmount);
+ case 'withdraw':
+ if (isMaxAmount) {
+ return transaction.execute(Transaction.LOANS_WITHDRAW_ALL, monetaryAmount.currency);
+ } else {
+ return transaction.execute(Transaction.LOANS_WITHDRAW, monetaryAmount.currency, monetaryAmount);
+ }
+ case 'borrow':
+ return transaction.execute(Transaction.LOANS_BORROW, monetaryAmount.currency, monetaryAmount);
+ case 'repay':
+ if (isMaxAmount) {
+ return transaction.execute(Transaction.LOANS_REPAY_ALL, monetaryAmount.currency);
+ } else {
+ return transaction.execute(Transaction.LOANS_REPAY, monetaryAmount.currency, monetaryAmount);
+ }
+ }
+ } catch (err: any) {
+ toast.error(err.toString());
+ }
};
- const loanMutation = useLoanMutation({ onSuccess: handleSuccess, onError: handleError });
-
const schemaParams: LoanValidationParams = {
governanceBalance,
transactionFee,
@@ -135,16 +162,6 @@ const LoanForm = ({ asset, variant, position, onChangeLoan }: LoanFormProps): JS
maxAmount: assetAmount.available
};
- const handleSubmit = (data: LoanFormData) => {
- try {
- const submittedAmount = data[variant] || 0;
- const submittedMonetaryAmount = newMonetaryAmount(submittedAmount, asset.currency, true);
- loanMutation.mutate({ amount: submittedMonetaryAmount, loanType: variant, isMaxAmount });
- } catch (err: any) {
- toast.error(err.toString());
- }
- };
-
const form = useForm({
initialValues: { [variant]: '' },
validationSchema: loanSchema(variant, schemaParams),
@@ -199,7 +216,7 @@ const LoanForm = ({ asset, variant, position, onChangeLoan }: LoanFormProps): JS
-
+
{content.title}
diff --git a/src/pages/Loans/LoansOverview/components/LoansInsights/LoansInsights.tsx b/src/pages/Loans/LoansOverview/components/LoansInsights/LoansInsights.tsx
index 41bfd6a148..6ad85f8d82 100644
--- a/src/pages/Loans/LoansOverview/components/LoansInsights/LoansInsights.tsx
+++ b/src/pages/Loans/LoansOverview/components/LoansInsights/LoansInsights.tsx
@@ -1,20 +1,16 @@
-import { ISubmittableResult } from '@polkadot/types/types';
import { useTranslation } from 'react-i18next';
-import { useMutation } from 'react-query';
import { toast } from 'react-toastify';
import { formatNumber, formatPercentage, formatUSD } from '@/common/utils/utils';
import { Card, Dl, DlGroup } from '@/component-library';
import { AuthCTA } from '@/components';
import ErrorModal from '@/legacy-components/ErrorModal';
-import { submitExtrinsic } from '@/utils/helpers/extrinsic';
import { AccountLendingStatistics } from '@/utils/hooks/api/loans/use-get-account-lending-statistics';
import { useGetAccountSubsidyRewards } from '@/utils/hooks/api/loans/use-get-account-subsidy-rewards';
+import { Transaction, useTransaction } from '@/utils/hooks/transaction';
import { StyledDd, StyledDt } from './LoansInsights.style';
-const mutateClaimRewards = () => submitExtrinsic(window.bridge.loans.claimAllSubsidyRewards());
-
type LoansInsightsProps = {
statistics?: AccountLendingStatistics;
};
@@ -23,16 +19,14 @@ const LoansInsights = ({ statistics }: LoansInsightsProps): JSX.Element => {
const { t } = useTranslation();
const { data: subsidyRewards, refetch } = useGetAccountSubsidyRewards();
- const handleSuccess = () => {
- toast.success(t('successfully_claimed_rewards'));
- refetch();
- };
-
- const claimRewardsMutation = useMutation(mutateClaimRewards, {
- onSuccess: handleSuccess
+ const transaction = useTransaction(Transaction.LOANS_CLAIM_REWARDS, {
+ onSuccess: () => {
+ toast.success(t('successfully_claimed_rewards'));
+ refetch();
+ }
});
- const handleClickClaimRewards = () => claimRewardsMutation.mutate();
+ const handleClickClaimRewards = () => transaction.execute();
const { supplyAmountUSD, netAPY } = statistics || {};
@@ -76,18 +70,18 @@ const LoansInsights = ({ statistics }: LoansInsightsProps): JSX.Element => {
{subsidyRewardsAmountLabel}
{hasSubsidyRewards && (
-
+
Claim
)}
- {claimRewardsMutation.isError && (
+ {transaction.isError && (
claimRewardsMutation.reset()}
+ open={transaction.isError}
+ onClose={() => transaction.reset()}
title='Error'
- description={claimRewardsMutation.error?.message || ''}
+ description={transaction.error?.message || ''}
/>
)}
>
diff --git a/src/pages/Staking/ClaimRewardsButton/index.tsx b/src/pages/Staking/ClaimRewardsButton/index.tsx
index 2ad34879cd..442da162c0 100644
--- a/src/pages/Staking/ClaimRewardsButton/index.tsx
+++ b/src/pages/Staking/ClaimRewardsButton/index.tsx
@@ -1,6 +1,5 @@
-import { ISubmittableResult } from '@polkadot/types/types';
import clsx from 'clsx';
-import { useMutation, useQueryClient } from 'react-query';
+import { useQueryClient } from 'react-query';
import { GOVERNANCE_TOKEN_SYMBOL } from '@/config/relay-chains';
import InterlayDenimOrKintsugiSupernovaContainedButton, {
@@ -9,7 +8,7 @@ import InterlayDenimOrKintsugiSupernovaContainedButton, {
import ErrorModal from '@/legacy-components/ErrorModal';
import { useSubstrateSecureState } from '@/lib/substrate';
import { GENERIC_FETCHER } from '@/services/fetchers/generic-fetcher';
-import { submitExtrinsic } from '@/utils/helpers/extrinsic';
+import { Transaction, useTransaction } from '@/utils/hooks/transaction';
interface CustomProps {
claimableRewardAmount: string;
@@ -24,20 +23,15 @@ const ClaimRewardsButton = ({
const queryClient = useQueryClient();
- const claimRewardsMutation = useMutation(
- () => {
- return submitExtrinsic(window.bridge.escrow.withdrawRewards());
- },
- {
- onSuccess: () => {
- queryClient.invalidateQueries([GENERIC_FETCHER, 'escrow', 'getRewardEstimate', selectedAccount?.address]);
- queryClient.invalidateQueries([GENERIC_FETCHER, 'escrow', 'getRewards', selectedAccount?.address]);
- }
+ const transaction = useTransaction(Transaction.ESCROW_WITHDRAW_REWARDS, {
+ onSuccess: () => {
+ queryClient.invalidateQueries([GENERIC_FETCHER, 'escrow', 'getRewardEstimate', selectedAccount?.address]);
+ queryClient.invalidateQueries([GENERIC_FETCHER, 'escrow', 'getRewards', selectedAccount?.address]);
}
- );
+ });
const handleClaimRewards = () => {
- claimRewardsMutation.mutate();
+ transaction.execute();
};
return (
@@ -45,19 +39,19 @@ const ClaimRewardsButton = ({
Claim {claimableRewardAmount} {GOVERNANCE_TOKEN_SYMBOL} Rewards
- {claimRewardsMutation.isError && (
+ {transaction.isError && (
{
- claimRewardsMutation.reset();
+ transaction.reset();
}}
title='Error'
- description={claimRewardsMutation.error?.message || ''}
+ description={transaction.error?.message || ''}
/>
)}
>
diff --git a/src/pages/Staking/index.tsx b/src/pages/Staking/index.tsx
index e7c8dfd505..043d6b1185 100644
--- a/src/pages/Staking/index.tsx
+++ b/src/pages/Staking/index.tsx
@@ -1,5 +1,4 @@
import { newMonetaryAmount } from '@interlay/interbtc-api';
-import { ISubmittableResult } from '@polkadot/types/types';
import Big from 'big.js';
import clsx from 'clsx';
import { add, format } from 'date-fns';
@@ -7,7 +6,7 @@ import * as React from 'react';
import { useErrorHandler, withErrorBoundary } from 'react-error-boundary';
import { useForm } from 'react-hook-form';
import { useTranslation } from 'react-i18next';
-import { useMutation, useQuery, useQueryClient } from 'react-query';
+import { useQuery, useQueryClient } from 'react-query';
import { useSelector } from 'react-redux';
import { StoreType } from '@/common/types/util.types';
@@ -44,10 +43,10 @@ import {
} from '@/services/fetchers/staking-transaction-fee-reserve-fetcher';
import { ZERO_GOVERNANCE_TOKEN_AMOUNT, ZERO_VOTE_GOVERNANCE_TOKEN_AMOUNT } from '@/utils/constants/currency';
import { YEAR_MONTH_DAY_PATTERN } from '@/utils/constants/date-time';
-import { submitExtrinsic } from '@/utils/helpers/extrinsic';
import { getTokenPrice } from '@/utils/helpers/prices';
import { useGetBalances } from '@/utils/hooks/api/tokens/use-get-balances';
import { useGetPrices } from '@/utils/hooks/api/use-get-prices';
+import { Transaction, useTransaction } from '@/utils/hooks/transaction';
import { useSignMessage } from '@/utils/hooks/use-sign-message';
import BalancesUI from './BalancesUI';
@@ -100,11 +99,6 @@ interface StakedAmountAndEndBlock {
endBlock: number;
}
-interface LockingAmountAndTime {
- amount: GovernanceTokenMonetaryAmount;
- time: number; // Weeks
-}
-
const Staking = (): JSX.Element => {
const [blockLockTimeExtension, setBlockLockTimeExtension] = React.useState(0);
@@ -248,63 +242,25 @@ const Staking = (): JSX.Element => {
);
useErrorHandler(transactionFeeReserveError);
- const initialStakeMutation = useMutation(
- (variables: LockingAmountAndTime) => {
- if (currentBlockNumber === undefined) {
- throw new Error('Something went wrong!');
- }
- const unlockHeight = currentBlockNumber + convertWeeksToBlockNumbers(variables.time);
-
- return submitExtrinsic(window.bridge.escrow.createLock(variables.amount, unlockHeight));
- },
- {
- onSuccess: () => {
- queryClient.invalidateQueries({ queryKey: [GENERIC_FETCHER, 'escrow'] });
- reset({
- [LOCKING_AMOUNT]: '0.0',
- [LOCK_TIME]: '0'
- });
- }
+ const initialStakeTransaction = useTransaction(Transaction.ESCROW_CREATE_LOCK, {
+ onSuccess: () => {
+ queryClient.invalidateQueries({ queryKey: [GENERIC_FETCHER, 'escrow'] });
+ reset({
+ [LOCKING_AMOUNT]: '0.0',
+ [LOCK_TIME]: '0'
+ });
}
- );
-
- const moreStakeMutation = useMutation(
- (variables: LockingAmountAndTime) => {
- return (async () => {
- if (stakedAmountAndEndBlock === undefined) {
- throw new Error('Something went wrong!');
- }
+ });
- if (checkIncreaseLockAmountAndExtendLockTime(variables.time, variables.amount)) {
- const unlockHeight = stakedAmountAndEndBlock.endBlock + convertWeeksToBlockNumbers(variables.time);
-
- const txs = [
- window.bridge.api.tx.escrow.increaseAmount(variables.amount.toString(true)),
- window.bridge.api.tx.escrow.increaseUnlockHeight(unlockHeight)
- ];
- const batch = window.bridge.api.tx.utility.batchAll(txs);
- await submitExtrinsic({ extrinsic: batch });
- } else if (checkOnlyIncreaseLockAmount(variables.time, variables.amount)) {
- await submitExtrinsic(window.bridge.escrow.increaseAmount(variables.amount));
- } else if (checkOnlyExtendLockTime(variables.time, variables.amount)) {
- const unlockHeight = stakedAmountAndEndBlock.endBlock + convertWeeksToBlockNumbers(variables.time);
-
- await submitExtrinsic(window.bridge.escrow.increaseUnlockHeight(unlockHeight));
- } else {
- throw new Error('Something went wrong!');
- }
- })();
- },
- {
- onSuccess: () => {
- queryClient.invalidateQueries({ queryKey: [GENERIC_FETCHER, 'escrow'] });
- reset({
- [LOCKING_AMOUNT]: '0.0',
- [LOCK_TIME]: '0'
- });
- }
+ const existingStakeTransaction = useTransaction({
+ onSuccess: () => {
+ queryClient.invalidateQueries({ queryKey: [GENERIC_FETCHER, 'escrow'] });
+ reset({
+ [LOCKING_AMOUNT]: '0.0',
+ [LOCK_TIME]: '0'
+ });
}
- );
+ });
React.useEffect(() => {
if (isValidating || !isValid || !estimatedRewardAmountAndAPYRefetch) return;
@@ -409,15 +365,30 @@ const Staking = (): JSX.Element => {
const numberTime = parseInt(lockTimeWithFallback);
if (votingBalanceGreaterThanZero) {
- moreStakeMutation.mutate({
- amount: monetaryAmount,
- time: numberTime
- });
+ if (stakedAmountAndEndBlock === undefined) {
+ throw new Error('Something went wrong!');
+ }
+
+ if (checkIncreaseLockAmountAndExtendLockTime(numberTime, monetaryAmount)) {
+ const unlockHeight = stakedAmountAndEndBlock.endBlock + convertWeeksToBlockNumbers(numberTime);
+
+ existingStakeTransaction.execute(
+ Transaction.ESCROW_INCREASE_LOOKED_TIME_AND_AMOUNT,
+ monetaryAmount.toString(true),
+ unlockHeight
+ );
+ } else if (checkOnlyIncreaseLockAmount(numberTime, monetaryAmount)) {
+ existingStakeTransaction.execute(Transaction.ESCROW_INCREASE_LOCKED_AMOUNT, monetaryAmount);
+ } else if (checkOnlyExtendLockTime(numberTime, monetaryAmount)) {
+ const unlockHeight = stakedAmountAndEndBlock.endBlock + convertWeeksToBlockNumbers(numberTime);
+
+ existingStakeTransaction.execute(Transaction.ESCROW_INCREASE_LOCKED_TIME, unlockHeight);
+ } else {
+ throw new Error('Something went wrong!');
+ }
} else {
- initialStakeMutation.mutate({
- amount: monetaryAmount,
- time: numberTime
- });
+ const unlockHeight = currentBlockNumber + convertWeeksToBlockNumbers(numberTime);
+ initialStakeTransaction.execute(monetaryAmount, unlockHeight);
}
};
@@ -856,7 +827,7 @@ const Staking = (): JSX.Element => {
size='large'
type='submit'
disabled={initializing || unlockFirst || !isValid}
- loading={initialStakeMutation.isLoading || moreStakeMutation.isLoading}
+ loading={initialStakeTransaction.isLoading || existingStakeTransaction.isLoading}
>
{submitButtonLabel}{' '}
{unlockFirst ? (
@@ -866,15 +837,15 @@ const Staking = (): JSX.Element => {
- {(initialStakeMutation.isError || moreStakeMutation.isError) && (
+ {(initialStakeTransaction.isError || existingStakeTransaction.isError) && (
{
- initialStakeMutation.reset();
- moreStakeMutation.reset();
+ initialStakeTransaction.reset();
+ existingStakeTransaction.reset();
}}
title='Error'
- description={initialStakeMutation.error?.message || moreStakeMutation.error?.message || ''}
+ description={initialStakeTransaction.error?.message || existingStakeTransaction.error?.message || ''}
/>
)}
>
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..1e7a864185
--- /dev/null
+++ b/src/pages/Transfer/CrossChainTransferForm/CrossChainTransferForm.tsx
@@ -0,0 +1,301 @@
+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]);
+
+ // TODO: When we refactor account select this should be handled there so
+ // that it's consitent across the application
+ useEffect(() => {
+ if (!accountId) return;
+ form.setFieldValue(CROSS_CHAIN_TRANSFER_TO_ACCOUNT_FIELD, accountId?.toString());
+ // eslint-disable-next-line react-hooks/exhaustive-deps
+ }, [accountId]);
+
+ if (!originatingChains || !destinationChains || !transferableTokens.length) {
+ return (
+
+
+
+ );
+ }
+
+ return (
+
+ );
+};
+
+export default CrossChainTransferForm;
diff --git a/src/pages/Transfer/CrossChainTransferForm/components/ChainIcon/ChainIcon.tsx b/src/pages/Transfer/CrossChainTransferForm/components/ChainIcon/ChainIcon.tsx
index 52bfdb6ad9..dd99e9e509 100644
--- a/src/pages/Transfer/CrossChainTransferForm/components/ChainIcon/ChainIcon.tsx
+++ b/src/pages/Transfer/CrossChainTransferForm/components/ChainIcon/ChainIcon.tsx
@@ -3,15 +3,35 @@ import { forwardRef, ForwardRefExoticComponent, RefAttributes } from 'react';
import { IconProps } from '@/component-library/Icon';
import { StyledFallbackIcon } from './ChainIcon.style';
-import { HYDRA, INTERLAY, KINTSUGI, KUSAMA, POLKADOT, STATEMINE, STATEMINT } from './icons';
+import {
+ ACALA,
+ ASTAR,
+ BIFROST,
+ HEIKO,
+ HYDRA,
+ INTERLAY,
+ KARURA,
+ KINTSUGI,
+ KUSAMA,
+ PARALLEL,
+ POLKADOT,
+ STATEMINE,
+ STATEMINT
+} from './icons';
type ChainComponent = ForwardRefExoticComponent>;
const chainsIcon: Record = {
+ ACALA,
+ ASTAR,
+ BIFROST,
+ HEIKO,
HYDRA,
INTERLAY,
+ KARURA,
KINTSUGI,
KUSAMA,
+ PARALLEL,
POLKADOT,
STATEMINE,
STATEMINT
diff --git a/src/pages/Transfer/CrossChainTransferForm/components/ChainIcon/icons/Acala.tsx b/src/pages/Transfer/CrossChainTransferForm/components/ChainIcon/icons/Acala.tsx
new file mode 100644
index 0000000000..137ffa2a35
--- /dev/null
+++ b/src/pages/Transfer/CrossChainTransferForm/components/ChainIcon/icons/Acala.tsx
@@ -0,0 +1,147 @@
+import { forwardRef } from 'react';
+
+import { Icon, IconProps } from '@/component-library/Icon';
+
+const ACALA = forwardRef((props, ref) => (
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+));
+
+ACALA.displayName = 'ACALA';
+
+export { ACALA };
diff --git a/src/pages/Transfer/CrossChainTransferForm/components/ChainIcon/icons/Astar.tsx b/src/pages/Transfer/CrossChainTransferForm/components/ChainIcon/icons/Astar.tsx
new file mode 100644
index 0000000000..61601ee25d
--- /dev/null
+++ b/src/pages/Transfer/CrossChainTransferForm/components/ChainIcon/icons/Astar.tsx
@@ -0,0 +1,58 @@
+import { forwardRef } from 'react';
+
+import { Icon, IconProps } from '@/component-library/Icon';
+
+const ASTAR = forwardRef((props, ref) => (
+
+ ASTAR
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+));
+
+ASTAR.displayName = 'ASTAR';
+
+export { ASTAR };
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/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/Parallel.tsx b/src/pages/Transfer/CrossChainTransferForm/components/ChainIcon/icons/Parallel.tsx
new file mode 100644
index 0000000000..b6fc644b49
--- /dev/null
+++ b/src/pages/Transfer/CrossChainTransferForm/components/ChainIcon/icons/Parallel.tsx
@@ -0,0 +1,47 @@
+import { forwardRef } from 'react';
+
+import { Icon, IconProps } from '@/component-library/Icon';
+
+const PARALLEL = forwardRef((props, ref) => (
+
+ PARALLEL
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+));
+
+PARALLEL.displayName = 'PARALLEL';
+
+export { PARALLEL };
diff --git a/src/pages/Transfer/CrossChainTransferForm/components/ChainIcon/icons/index.ts b/src/pages/Transfer/CrossChainTransferForm/components/ChainIcon/icons/index.ts
index 14d6a6c839..d3c471eb7a 100644
--- a/src/pages/Transfer/CrossChainTransferForm/components/ChainIcon/icons/index.ts
+++ b/src/pages/Transfer/CrossChainTransferForm/components/ChainIcon/icons/index.ts
@@ -1,7 +1,13 @@
+export { ACALA } from './Acala';
+export { ASTAR } from './Astar';
+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 { PARALLEL } from './Parallel';
export { POLKADOT } from './Polkadot';
export { STATEMINE } from './Statemine';
export { STATEMINT } from './Statemint';
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 (
+
+
+
+ );
+};
+
+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 (
- <>
-
- {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/TransferForm/index.tsx b/src/pages/Transfer/TransferForm/index.tsx
index e588456dc1..a488cd288f 100644
--- a/src/pages/Transfer/TransferForm/index.tsx
+++ b/src/pages/Transfer/TransferForm/index.tsx
@@ -18,8 +18,8 @@ import Tokens, { TokenOption } from '@/legacy-components/Tokens';
import InterlayButtonBase from '@/legacy-components/UI/InterlayButtonBase';
import { KUSAMA, POLKADOT } from '@/utils/constants/relay-chain-names';
import STATUSES from '@/utils/constants/statuses';
-import { submitExtrinsic } from '@/utils/helpers/extrinsic';
import isValidPolkadotAddress from '@/utils/helpers/is-valid-polkadot-address';
+import { Transaction, useTransaction } from '@/utils/hooks/transaction';
import TokenAmountField from '../TokenAmountField';
@@ -50,6 +50,8 @@ const TransferForm = (): JSX.Element => {
const [submitStatus, setSubmitStatus] = React.useState(STATUSES.IDLE);
const [submitError, setSubmitError] = React.useState(null);
+ const transaction = useTransaction(Transaction.TOKENS_TRANSFER);
+
const onSubmit = async (data: TransferFormData) => {
if (!activeToken) return;
if (data[TRANSFER_AMOUNT] === undefined) return;
@@ -57,11 +59,9 @@ const TransferForm = (): JSX.Element => {
try {
setSubmitStatus(STATUSES.PENDING);
- await submitExtrinsic(
- window.bridge.tokens.transfer(
- data[RECIPIENT_ADDRESS],
- newMonetaryAmount(data[TRANSFER_AMOUNT], activeToken.token, true)
- )
+ await transaction.executeAsync(
+ data[RECIPIENT_ADDRESS],
+ newMonetaryAmount(data[TRANSFER_AMOUNT], activeToken.token, true)
);
setSubmitStatus(STATUSES.RESOLVED);
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/pages/Vaults/Vault/RequestIssueModal/index.tsx b/src/pages/Vaults/Vault/RequestIssueModal/index.tsx
index 91c08d48cb..4e9215ce82 100644
--- a/src/pages/Vaults/Vault/RequestIssueModal/index.tsx
+++ b/src/pages/Vaults/Vault/RequestIssueModal/index.tsx
@@ -44,11 +44,11 @@ import SubmittedIssueRequestModal from '@/pages/Bridge/IssueForm/SubmittedIssueR
import { ForeignAssetIdLiteral } from '@/types/currency';
import { KUSAMA, POLKADOT } from '@/utils/constants/relay-chain-names';
import STATUSES from '@/utils/constants/statuses';
-import { getExtrinsicStatus, submitExtrinsic } from '@/utils/helpers/extrinsic';
import { getExchangeRate } from '@/utils/helpers/oracle';
import { getTokenPrice } from '@/utils/helpers/prices';
import { useGetBalances } from '@/utils/hooks/api/tokens/use-get-balances';
import { useGetPrices } from '@/utils/hooks/api/use-get-prices';
+import { Transaction, useTransaction } from '@/utils/hooks/transaction';
import useAccountId from '@/utils/hooks/use-account-id';
const WRAPPED_TOKEN_AMOUNT = 'amount';
@@ -108,6 +108,8 @@ const RequestIssueModal = ({ onClose, open, collateralToken, vaultAddress }: Pro
const vaultAccountId = useAccountId(vaultAddress);
+ const transaction = useTransaction(Transaction.ISSUE_REQUEST);
+
React.useEffect(() => {
if (!bridgeLoaded) return;
if (!handleError) return;
@@ -180,17 +182,14 @@ const RequestIssueModal = ({ onClose, open, collateralToken, vaultAddress }: Pro
const vaults = await window.bridge.vaults.getVaultsWithIssuableTokens();
- const extrinsicData = await window.bridge.issue.request(
+ const extrinsicResult = await transaction.executeAsync(
wrappedTokenAmount,
vaultAccountId,
collateralToken,
false, // default
vaults
);
- // When requesting an issue, wait for the finalized event because we cannot revert BTC transactions.
- // For more details see: https://github.com/interlay/interbtc-api/pull/373#issuecomment-1058949000
- const finalizedStatus = getExtrinsicStatus('Finalized');
- const extrinsicResult = await submitExtrinsic(extrinsicData, finalizedStatus);
+
const issueRequests = await getIssueRequestsFromExtrinsicResult(window.bridge, extrinsicResult);
// TODO: handle issue aggregation
diff --git a/src/pages/Vaults/Vault/RequestRedeemModal/index.tsx b/src/pages/Vaults/Vault/RequestRedeemModal/index.tsx
index 300211d7ec..dde21974e5 100644
--- a/src/pages/Vaults/Vault/RequestRedeemModal/index.tsx
+++ b/src/pages/Vaults/Vault/RequestRedeemModal/index.tsx
@@ -17,7 +17,7 @@ import ErrorMessage from '@/legacy-components/ErrorMessage';
import NumberInput from '@/legacy-components/NumberInput';
import TextField from '@/legacy-components/TextField';
import InterlayModal, { InterlayModalInnerWrapper, InterlayModalTitle } from '@/legacy-components/UI/InterlayModal';
-import { getExtrinsicStatus, submitExtrinsic } from '@/utils/helpers/extrinsic';
+import { Transaction, useTransaction } from '@/utils/hooks/transaction';
const WRAPPED_TOKEN_AMOUNT = 'amount';
const BTC_ADDRESS = 'btc-address';
@@ -47,6 +47,8 @@ const RequestRedeemModal = ({ onClose, open, collateralToken, vaultAddress, lock
const { t } = useTranslation();
const focusRef = React.useRef(null);
+ const transaction = useTransaction(Transaction.REDEEM_REQUEST);
+
const onSubmit = handleSubmit(async (data) => {
setRequestPending(true);
try {
@@ -61,11 +63,7 @@ const RequestRedeemModal = ({ onClose, open, collateralToken, vaultAddress, lock
}
const vaultId = newVaultId(window.bridge.api, vaultAddress, collateralToken, WRAPPED_TOKEN);
- const extrinsicData = await window.bridge.redeem.request(amountPolkaBtc, data[BTC_ADDRESS], vaultId);
- // When requesting a redeem, wait for the finalized event because we cannot revert BTC transactions.
- // For more details see: https://github.com/interlay/interbtc-api/pull/373#issuecomment-1058949000
- const finalizedStatus = getExtrinsicStatus('Finalized');
- await submitExtrinsic(extrinsicData, finalizedStatus);
+ await transaction.executeAsync(amountPolkaBtc, data[BTC_ADDRESS], vaultId);
queryClient.invalidateQueries(['vaultsOverview', vaultAddress, collateralToken.ticker]);
diff --git a/src/pages/Vaults/Vault/RequestReplacementModal/index.tsx b/src/pages/Vaults/Vault/RequestReplacementModal/index.tsx
index b296f04bf0..a92acc73b2 100644
--- a/src/pages/Vaults/Vault/RequestReplacementModal/index.tsx
+++ b/src/pages/Vaults/Vault/RequestReplacementModal/index.tsx
@@ -25,9 +25,9 @@ import PrimaryColorEllipsisLoader from '@/legacy-components/PrimaryColorEllipsis
import InterlayModal, { InterlayModalInnerWrapper, InterlayModalTitle } from '@/legacy-components/UI/InterlayModal';
import { GENERIC_FETCHER } from '@/services/fetchers/generic-fetcher';
import STATUSES from '@/utils/constants/statuses';
-import { getExtrinsicStatus, submitExtrinsic } from '@/utils/helpers/extrinsic';
import { getExchangeRate } from '@/utils/helpers/oracle';
import { useGetBalances } from '@/utils/hooks/api/tokens/use-get-balances';
+import { Transaction, useTransaction } from '@/utils/hooks/transaction';
const AMOUNT = 'amount';
@@ -78,6 +78,8 @@ const RequestReplacementModal = ({
);
const [submitStatus, setSubmitStatus] = React.useState(STATUSES.IDLE);
+ const transaction = useTransaction(Transaction.REPLACE_REQUEST);
+
useEffect(() => {
if (!bridgeLoaded) return;
if (!handleError) return;
@@ -105,10 +107,8 @@ const RequestReplacementModal = ({
try {
setSubmitStatus(STATUSES.PENDING);
const amountPolkaBtc = new BitcoinAmount(data[AMOUNT]);
- // When requesting a replace, wait for the finalized event because we cannot revert BTC transactions.
- // For more details see: https://github.com/interlay/interbtc-api/pull/373#issuecomment-1058949000
- const finalizedStatus = getExtrinsicStatus('Finalized');
- submitExtrinsic(window.bridge.replace.request(amountPolkaBtc, collateralToken), finalizedStatus);
+
+ await transaction.executeAsync(amountPolkaBtc, collateralToken);
const vaultId = window.bridge.api.createType(ACCOUNT_ID_TYPE_NAME, vaultAddress);
queryClient.invalidateQueries([GENERIC_FETCHER, 'mapReplaceRequests', vaultId]);
diff --git a/src/pages/Vaults/Vault/UpdateCollateralModal/index.tsx b/src/pages/Vaults/Vault/UpdateCollateralModal/index.tsx
index 772fa93e3d..dad669da97 100644
--- a/src/pages/Vaults/Vault/UpdateCollateralModal/index.tsx
+++ b/src/pages/Vaults/Vault/UpdateCollateralModal/index.tsx
@@ -21,10 +21,10 @@ import TokenField from '@/legacy-components/TokenField';
import InterlayModal, { InterlayModalInnerWrapper, InterlayModalTitle } from '@/legacy-components/UI/InterlayModal';
import genericFetcher, { GENERIC_FETCHER } from '@/services/fetchers/generic-fetcher';
import STATUSES from '@/utils/constants/statuses';
-import { submitExtrinsic, submitExtrinsicPromise } from '@/utils/helpers/extrinsic';
import { getTokenPrice } from '@/utils/helpers/prices';
import { useGetBalances } from '@/utils/hooks/api/tokens/use-get-balances';
import { useGetPrices } from '@/utils/hooks/api/use-get-prices';
+import { Transaction, useTransaction } from '@/utils/hooks/transaction';
enum CollateralUpdateStatus {
Close,
@@ -129,6 +129,8 @@ const UpdateCollateralModal = ({
);
useErrorHandler(vaultCollateralizationError);
+ const transaction = useTransaction();
+
const handleClose = chain(() => resetField(COLLATERAL_TOKEN_AMOUNT), onClose);
const onSubmit = async (data: UpdateCollateralFormData) => {
@@ -142,9 +144,9 @@ const UpdateCollateralModal = ({
true
) as MonetaryAmount;
if (collateralUpdateStatus === CollateralUpdateStatus.Deposit) {
- await submitExtrinsic(window.bridge.vaults.depositCollateral(collateralTokenAmount));
+ await transaction.executeAsync(Transaction.VAULTS_DEPOSIT_COLLATERAL, collateralTokenAmount);
} else if (collateralUpdateStatus === CollateralUpdateStatus.Withdraw) {
- await submitExtrinsicPromise(window.bridge.vaults.withdrawCollateral(collateralTokenAmount));
+ await transaction.executeAsync(Transaction.VAULTS_WITHDRAW_COLLATERAL, collateralTokenAmount);
} else {
throw new Error('Something went wrong!');
}
diff --git a/src/pages/Vaults/Vault/components/Rewards/Rewards.tsx b/src/pages/Vaults/Vault/components/Rewards/Rewards.tsx
index e2bc840f77..2b8cedbe6b 100644
--- a/src/pages/Vaults/Vault/components/Rewards/Rewards.tsx
+++ b/src/pages/Vaults/Vault/components/Rewards/Rewards.tsx
@@ -1,7 +1,6 @@
import { CollateralCurrencyExt, newVaultId, WrappedCurrency, WrappedIdLiteral } from '@interlay/interbtc-api';
-import { ISubmittableResult } from '@polkadot/types/types';
import Big from 'big.js';
-import { useMutation, useQueryClient } from 'react-query';
+import { useQueryClient } from 'react-query';
import { toast } from 'react-toastify';
import { formatNumber, formatUSD } from '@/common/utils/utils';
@@ -10,8 +9,8 @@ import { LoadingSpinner } from '@/component-library/LoadingSpinner';
import { GOVERNANCE_TOKEN_SYMBOL, WRAPPED_TOKEN } from '@/config/relay-chains';
import ErrorModal from '@/legacy-components/ErrorModal';
import { ZERO_GOVERNANCE_TOKEN_AMOUNT } from '@/utils/constants/currency';
-import { submitExtrinsicPromise } from '@/utils/helpers/extrinsic';
import { VaultData } from '@/utils/hooks/api/vaults/get-vault-data';
+import { Transaction, useTransaction } from '@/utils/hooks/transaction';
import useAccountId from '@/utils/hooks/use-account-id';
import { InsightListItem, InsightsList } from '../InsightsList';
@@ -49,31 +48,24 @@ const Rewards = ({
const queryClient = useQueryClient();
const vaultAccountId = useAccountId(vaultAddress);
- const claimRewardsMutation = useMutation(
- () => {
- if (vaultAccountId === undefined) {
- throw new Error('Something went wrong!');
- }
-
- const vaultId = newVaultId(
- window.bridge.api,
- vaultAccountId.toString(),
- collateralToken,
- WRAPPED_TOKEN as WrappedCurrency
- );
-
- return submitExtrinsicPromise(window.bridge.rewards.withdrawRewards(vaultId));
- },
- {
- onSuccess: () => {
- queryClient.invalidateQueries(['vaultsOverview', vaultAddress, collateralToken.ticker]);
- toast.success('Your rewards were successfully withdrawn.');
- }
+ const transaction = useTransaction(Transaction.REWARDS_WITHDRAW, {
+ onSuccess: () => {
+ queryClient.invalidateQueries(['vaultsOverview', vaultAddress, collateralToken.ticker]);
+ toast.success('Your rewards were successfully withdrawn.');
}
- );
+ });
const handleClickWithdrawRewards = () => {
- claimRewardsMutation.mutate();
+ if (vaultAccountId === undefined) return;
+
+ const vaultId = newVaultId(
+ window.bridge.api,
+ vaultAccountId.toString(),
+ collateralToken,
+ WRAPPED_TOKEN as WrappedCurrency
+ );
+
+ transaction.execute(vaultId);
};
const hasWithdrawableRewards =
@@ -87,11 +79,11 @@ const Rewards = ({
size='small'
variant='outlined'
onClick={handleClickWithdrawRewards}
- disabled={!hasWithdrawableRewards || claimRewardsMutation.isLoading}
- $loading={claimRewardsMutation.isLoading}
+ disabled={!hasWithdrawableRewards || transaction.isLoading}
+ $loading={transaction.isLoading}
>
{/* TODO: temporary approach. Loading spinner should be added to the CTA itself */}
- {claimRewardsMutation.isLoading && (
+ {transaction.isLoading && (
@@ -99,12 +91,12 @@ const Rewards = ({
Withdraw all rewards
)}
- {claimRewardsMutation.isError && (
+ {transaction.isError && (
claimRewardsMutation.reset()}
+ open={transaction.isError}
+ onClose={() => transaction.reset()}
title='Error'
- description={claimRewardsMutation.error?.message || ''}
+ description={transaction.error?.message || ''}
/>
)}
diff --git a/src/pages/Vaults/VaultsOverview/components/CreateVaultWizard/DespositCollateralStep.tsx b/src/pages/Vaults/VaultsOverview/components/CreateVaultWizard/DespositCollateralStep.tsx
index 32f28166d1..4fbe4efd89 100644
--- a/src/pages/Vaults/VaultsOverview/components/CreateVaultWizard/DespositCollateralStep.tsx
+++ b/src/pages/Vaults/VaultsOverview/components/CreateVaultWizard/DespositCollateralStep.tsx
@@ -1,9 +1,7 @@
import { CollateralCurrencyExt, newMonetaryAmount } from '@interlay/interbtc-api';
import { MonetaryAmount } from '@interlay/monetary-js';
-import { ISubmittableResult } from '@polkadot/types/types';
import { useId } from '@react-aria/utils';
import { useTranslation } from 'react-i18next';
-import { useMutation } from 'react-query';
import { convertMonetaryAmountToValueInUSD, newSafeMonetaryAmount } from '@/common/utils/utils';
import { CTA, ModalBody, ModalDivider, ModalFooter, ModalHeader, Span, Stack, TokenInput } from '@/component-library';
@@ -16,8 +14,8 @@ import {
isFormDisabled,
useForm
} from '@/lib/form';
-import { submitExtrinsic } from '@/utils/helpers/extrinsic';
import { StepComponentProps, withStep } from '@/utils/hocs/step';
+import { Transaction, useTransaction } from '@/utils/hooks/transaction';
import { useDepositCollateral } from '../../utils/use-deposit-collateral';
import { StyledDd, StyledDItem, StyledDl, StyledDt, StyledHr } from './CreateVaultWizard.styles';
@@ -39,6 +37,10 @@ const DepositCollateralStep = ({
const { t } = useTranslation();
const { collateral, fee, governance } = useDepositCollateral(collateralCurrency, minCollateralAmount);
+ const transaction = useTransaction(Transaction.VAULTS_REGISTER_NEW_COLLATERAL, {
+ onSuccess: onSuccessfulDeposit
+ });
+
const validationParams = {
minAmount: collateral.min.raw,
maxAmount: collateral.balance.raw,
@@ -50,7 +52,7 @@ const DepositCollateralStep = ({
if (!data.deposit) return;
const amount = newMonetaryAmount(data.deposit || 0, collateral.currency, true);
- registerNewVaultMutation.mutate(amount);
+ transaction.execute(amount);
};
const form = useForm({
@@ -59,13 +61,6 @@ const DepositCollateralStep = ({
onSubmit: handleSubmit
});
- const registerNewVaultMutation = useMutation>(
- (collateralAmount) => submitExtrinsic(window.bridge.vaults.registerNewCollateralVault(collateralAmount)),
- {
- onSuccess: onSuccessfulDeposit
- }
- );
-
const inputCollateralAmount = newSafeMonetaryAmount(form.values.deposit || 0, collateral.currency, true);
const isBtnDisabled = isFormDisabled(form);
@@ -108,17 +103,17 @@ const DepositCollateralStep = ({
-
+
{t('vault.deposit_collateral')}
- {registerNewVaultMutation.isError && (
+ {transaction.isError && (
registerNewVaultMutation.reset()}
+ open={transaction.isError}
+ onClose={() => transaction.reset()}
title='Error'
- description={registerNewVaultMutation.error?.message || ''}
+ description={transaction.error?.message || ''}
/>
)}
>
diff --git a/src/parts/Sidebar/SidebarContent/Navigation/index.tsx b/src/parts/Sidebar/SidebarContent/Navigation/index.tsx
index 83581e5880..54dabf7446 100644
--- a/src/parts/Sidebar/SidebarContent/Navigation/index.tsx
+++ b/src/parts/Sidebar/SidebarContent/Navigation/index.tsx
@@ -70,6 +70,7 @@ const Navigation = ({
const isLendingEnabled = useFeatureFlag(FeatureFlags.LENDING);
const isAMMEnabled = useFeatureFlag(FeatureFlags.AMM);
const isWalletEnabled = useFeatureFlag(FeatureFlags.WALLET);
+ const isEarnStrategiesEnabled = useFeatureFlag(FeatureFlags.EARN_STRATEGIES);
const NAVIGATION_ITEMS = React.useMemo(
() => [
@@ -79,6 +80,12 @@ const Navigation = ({
icon: UserIcon,
disabled: !isWalletEnabled
},
+ {
+ name: 'nav_earn_strategies',
+ link: PAGES.EARN_STRATEGIES,
+ icon: BanknotesIcon,
+ disabled: !isEarnStrategiesEnabled
+ },
{
name: 'nav_bridge',
link: PAGES.BRIDGE,
@@ -193,7 +200,14 @@ const Navigation = ({
}
}
],
- [isWalletEnabled, isLendingEnabled, isAMMEnabled, selectedAccount?.address, vaultClientLoaded]
+ [
+ isWalletEnabled,
+ isEarnStrategiesEnabled,
+ isLendingEnabled,
+ isAMMEnabled,
+ selectedAccount?.address,
+ vaultClientLoaded
+ ]
);
return (
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/constants/links.ts b/src/utils/constants/links.ts
index 7c64f49388..fb189728c4 100644
--- a/src/utils/constants/links.ts
+++ b/src/utils/constants/links.ts
@@ -12,6 +12,7 @@ const URL_PARAMETERS = Object.freeze({
const PAGES = Object.freeze({
HOME: '/',
BRIDGE: '/bridge',
+ EARN_STRATEGIES: '/earn-strategies',
TRANSFER: '/transfer',
TRANSACTIONS: '/transactions',
TX: '/tx',
diff --git a/src/utils/hooks/api/loans/use-loan-mutation.tsx b/src/utils/hooks/api/loans/use-loan-mutation.tsx
deleted file mode 100644
index 0057369b36..0000000000
--- a/src/utils/hooks/api/loans/use-loan-mutation.tsx
+++ /dev/null
@@ -1,49 +0,0 @@
-import { CurrencyExt } from '@interlay/interbtc-api';
-import { MonetaryAmount } from '@interlay/monetary-js';
-import { ISubmittableResult } from '@polkadot/types/types';
-import { useMutation, UseMutationResult } from 'react-query';
-
-import { LoanAction } from '@/types/loans';
-import { submitExtrinsicPromise } from '@/utils/helpers/extrinsic';
-
-type CreateLoanVariables = { loanType: LoanAction; amount: MonetaryAmount; isMaxAmount: boolean };
-
-const mutateLoan = ({ loanType, amount, isMaxAmount }: CreateLoanVariables) => {
- const extrinsicData = (() => {
- switch (loanType) {
- case 'lend':
- return window.bridge.loans.lend(amount.currency, amount);
- case 'withdraw':
- if (isMaxAmount) {
- return window.bridge.loans.withdrawAll(amount.currency);
- } else {
- return window.bridge.loans.withdraw(amount.currency, amount);
- }
- case 'borrow':
- return window.bridge.loans.borrow(amount.currency, amount);
- case 'repay':
- if (isMaxAmount) {
- return window.bridge.loans.repayAll(amount.currency);
- } else {
- return window.bridge.loans.repay(amount.currency, amount);
- }
- }
- })();
-
- return submitExtrinsicPromise(extrinsicData);
-};
-
-type UseLoanMutation = { onSuccess: () => void; onError: (error: Error) => void };
-
-const useLoanMutation = ({
- onSuccess,
- onError
-}: UseLoanMutation): UseMutationResult => {
- return useMutation(mutateLoan, {
- onSuccess,
- onError
- });
-};
-
-export { useLoanMutation };
-export type { UseLoanMutation };
diff --git a/src/utils/hooks/api/xcm/use-xcm-bridge.ts b/src/utils/hooks/api/xcm/use-xcm-bridge.ts
index 4b086c55c8..491a8931d3 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.isLessThan(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..fe751d615b
--- /dev/null
+++ b/src/utils/hooks/api/xcm/xcm-endpoints.ts
@@ -0,0 +1,28 @@
+import { ChainName } from '@interlay/bridge';
+
+type XCMEndpointsRecord = Record;
+
+const XCMEndpoints: XCMEndpointsRecord = {
+ acala: ['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/src/utils/hooks/transaction/index.ts b/src/utils/hooks/transaction/index.ts
new file mode 100644
index 0000000000..3a845f06ae
--- /dev/null
+++ b/src/utils/hooks/transaction/index.ts
@@ -0,0 +1,2 @@
+export { Transaction } from './types';
+export { useTransaction } from './use-transaction';
diff --git a/src/utils/hooks/transaction/types/amm.ts b/src/utils/hooks/transaction/types/amm.ts
new file mode 100644
index 0000000000..7b6cc56af9
--- /dev/null
+++ b/src/utils/hooks/transaction/types/amm.ts
@@ -0,0 +1,28 @@
+import { InterBtcApi } from '@interlay/interbtc-api';
+
+import { Transaction } from '../types';
+import { TransactionAction } from '.';
+
+interface SwapAction extends TransactionAction {
+ type: Transaction.AMM_SWAP;
+ args: Parameters;
+}
+
+interface PoolAddLiquidityAction extends TransactionAction {
+ type: Transaction.AMM_ADD_LIQUIDITY;
+ args: Parameters;
+}
+
+interface PoolRemoveLiquidityAction extends TransactionAction {
+ type: Transaction.AMM_REMOVE_LIQUIDITY;
+ args: Parameters;
+}
+
+interface PoolClaimRewardsAction extends TransactionAction {
+ type: Transaction.AMM_CLAIM_REWARDS;
+ args: Parameters;
+}
+
+type AMMActions = SwapAction | PoolAddLiquidityAction | PoolRemoveLiquidityAction | PoolClaimRewardsAction;
+
+export type { AMMActions };
diff --git a/src/utils/hooks/transaction/types/escrow.ts b/src/utils/hooks/transaction/types/escrow.ts
new file mode 100644
index 0000000000..7003d1f796
--- /dev/null
+++ b/src/utils/hooks/transaction/types/escrow.ts
@@ -0,0 +1,46 @@
+import { InterBtcApi } from '@interlay/interbtc-api';
+
+import { Transaction } from '../types';
+import { TransactionAction } from '.';
+
+interface EscrowCreateLockAction extends TransactionAction {
+ type: Transaction.ESCROW_CREATE_LOCK;
+ args: Parameters;
+}
+
+interface EscrowInscreaseLookedTimeAndAmountAction extends TransactionAction {
+ type: Transaction.ESCROW_INCREASE_LOOKED_TIME_AND_AMOUNT;
+ args: [
+ ...Parameters,
+ ...Parameters
+ ];
+}
+interface EscrowIncreaseLockAmountAction extends TransactionAction {
+ type: Transaction.ESCROW_INCREASE_LOCKED_AMOUNT;
+ args: Parameters;
+}
+
+interface EscrowIncreaseLockTimeAction extends TransactionAction {
+ type: Transaction.ESCROW_INCREASE_LOCKED_TIME;
+ args: Parameters;
+}
+
+interface EscrowWithdrawRewardsAction extends TransactionAction {
+ type: Transaction.ESCROW_WITHDRAW_REWARDS;
+ args: Parameters;
+}
+
+interface EscrowWithdrawAction extends TransactionAction {
+ type: Transaction.ESCROW_WITHDRAW;
+ args: Parameters;
+}
+
+type EscrowActions =
+ | EscrowCreateLockAction
+ | EscrowInscreaseLookedTimeAndAmountAction
+ | EscrowIncreaseLockAmountAction
+ | EscrowIncreaseLockTimeAction
+ | EscrowWithdrawRewardsAction
+ | EscrowWithdrawAction;
+
+export type { EscrowActions };
diff --git a/src/utils/hooks/transaction/types/index.ts b/src/utils/hooks/transaction/types/index.ts
new file mode 100644
index 0000000000..538f820678
--- /dev/null
+++ b/src/utils/hooks/transaction/types/index.ts
@@ -0,0 +1,79 @@
+import { ExtrinsicStatus } from '@polkadot/types/interfaces';
+
+import { AMMActions } from './amm';
+import { EscrowActions } from './escrow';
+import { IssueActions } from './issue';
+import { LoansActions } from './loans';
+import { RedeemActions } from './redeem';
+import { ReplaceActions } from './replace';
+import { RewardsActions } from './rewards';
+import { TokensActions } from './tokens';
+import { VaultsActions } from './vaults';
+
+enum Transaction {
+ // Issue
+ ISSUE_REQUEST = 'ISSUE_REQUEST',
+ ISSUE_EXECUTE = 'ISSUE_EXECUTE',
+ // Redeem
+ REDEEM_REQUEST = 'REDEEM_REQUEST',
+ REDEEM_CANCEL = 'REDEEM_CANCEL',
+ REDEEM_BURN = 'REDEEM_BURN',
+ // Replace
+ REPLACE_REQUEST = 'REPLACE_REQUEST',
+ // Escrow
+ ESCROW_CREATE_LOCK = 'ESCROW_CREATE_LOCK',
+ ESCROW_INCREASE_LOCKED_TIME = 'ESCROW_INCREASE_LOCKED_TIME',
+ ESCROW_INCREASE_LOCKED_AMOUNT = 'ESCROW_INCREASE_LOCKED_AMOUNT',
+ ESCROW_INCREASE_LOOKED_TIME_AND_AMOUNT = 'ESCROW_INCREASE_LOOKED_TIME_AND_AMOUNT',
+ ESCROW_WITHDRAW_REWARDS = 'ESCROW_WITHDRAW_REWARDS',
+ ESCROW_WITHDRAW = 'ESCROW_WITHDRAW',
+ // Tokens
+ TOKENS_TRANSFER = 'TOKENS_TRANSFER',
+ // Vaults
+ VAULTS_DEPOSIT_COLLATERAL = 'VAULTS_DEPOSIT_COLLATERAL',
+ VAULTS_WITHDRAW_COLLATERAL = 'VAULTS_WITHDRAW_COLLATERAL',
+ VAULTS_REGISTER_NEW_COLLATERAL = 'VAULTS_REGISTER_NEW_COLLATERAL',
+ // Rewards
+ REWARDS_WITHDRAW = 'REWARDS_WITHDRAW',
+ // Loans
+ LOANS_CLAIM_REWARDS = 'LOANS_CLAIM_REWARDS',
+ LOANS_ENABLE_COLLATERAL = 'LOANS_ENABLE_COLLATERAL',
+ LOANS_DISABLE_COLLATERAL = 'LOANS_DISABLE_COLLATERAL',
+ LOANS_LEND = 'LOANS_LEND',
+ LOANS_WITHDRAW = 'LOANS_WITHDRAW',
+ LOANS_WITHDRAW_ALL = 'LOANS_WITHDRAW_ALL',
+ LOANS_BORROW = 'LOANS_BORROW',
+ LOANS_REPAY = 'LOANS_REPAY',
+ LOANS_REPAY_ALL = 'LOANS_REPAY_ALL',
+ // AMM
+ AMM_SWAP = 'AMM_SWAP',
+ AMM_ADD_LIQUIDITY = 'AMM_ADD_LIQUIDITY',
+ AMM_REMOVE_LIQUIDITY = 'AMM_REMOVE_LIQUIDITY',
+ AMM_CLAIM_REWARDS = 'AMM_CLAIM_REWARDS'
+}
+
+type TransactionEvents = {
+ onReady?: () => void;
+};
+
+interface TransactionAction {
+ accountAddress: string;
+ events: TransactionEvents;
+ customStatus?: ExtrinsicStatus['type'];
+}
+
+type TransactionActions =
+ | EscrowActions
+ | IssueActions
+ | RedeemActions
+ | ReplaceActions
+ | TokensActions
+ | LoansActions
+ | AMMActions
+ | VaultsActions
+ | RewardsActions;
+
+type TransactionArgs = Extract['args'];
+
+export { Transaction };
+export type { TransactionAction, TransactionActions, TransactionArgs, TransactionEvents };
diff --git a/src/utils/hooks/transaction/types/issue.ts b/src/utils/hooks/transaction/types/issue.ts
new file mode 100644
index 0000000000..dfa3b9d5a3
--- /dev/null
+++ b/src/utils/hooks/transaction/types/issue.ts
@@ -0,0 +1,18 @@
+import { InterBtcApi } from '@interlay/interbtc-api';
+
+import { Transaction } from '../types';
+import { TransactionAction } from '.';
+
+interface IssueRequestAction extends TransactionAction {
+ type: Transaction.ISSUE_REQUEST;
+ args: Parameters;
+}
+
+interface IssueExecuteAction extends TransactionAction {
+ type: Transaction.ISSUE_EXECUTE;
+ args: Parameters;
+}
+
+type IssueActions = IssueRequestAction | IssueExecuteAction;
+
+export type { IssueActions };
diff --git a/src/utils/hooks/transaction/types/loans.ts b/src/utils/hooks/transaction/types/loans.ts
new file mode 100644
index 0000000000..27797c68d9
--- /dev/null
+++ b/src/utils/hooks/transaction/types/loans.ts
@@ -0,0 +1,62 @@
+import { InterBtcApi } from '@interlay/interbtc-api';
+
+import { Transaction } from '../types';
+import { TransactionAction } from '.';
+
+interface LoansClaimRewardsAction extends TransactionAction {
+ type: Transaction.LOANS_CLAIM_REWARDS;
+ args: Parameters;
+}
+
+interface LoansEnabledCollateralAction extends TransactionAction {
+ type: Transaction.LOANS_ENABLE_COLLATERAL;
+ args: Parameters;
+}
+
+interface LoansDisabledCollateralAction extends TransactionAction {
+ type: Transaction.LOANS_DISABLE_COLLATERAL;
+ args: Parameters;
+}
+
+interface LoansLendAction extends TransactionAction {
+ type: Transaction.LOANS_LEND;
+ args: Parameters;
+}
+
+interface LoansWithdrawAction extends TransactionAction {
+ type: Transaction.LOANS_WITHDRAW;
+ args: Parameters;
+}
+
+interface LoansWithdrawAllAction extends TransactionAction {
+ type: Transaction.LOANS_WITHDRAW_ALL;
+ args: Parameters;
+}
+
+interface LoansBorrowAction extends TransactionAction {
+ type: Transaction.LOANS_BORROW;
+ args: Parameters;
+}
+
+interface LoansRepayAction extends TransactionAction {
+ type: Transaction.LOANS_REPAY;
+ args: Parameters;
+}
+
+interface LoansRepayAllAction extends TransactionAction {
+ type: Transaction.LOANS_REPAY_ALL;
+ args: Parameters;
+}
+
+type LoansActions =
+ | LoansClaimRewardsAction
+ | LoansEnabledCollateralAction
+ | LoansDisabledCollateralAction
+ | LoansLendAction
+ | LoansWithdrawAction
+ | LoansWithdrawAllAction
+ | LoansBorrowAction
+ | LoansRepayAction
+ | LoansRepayAllAction;
+
+export type { LoansActions };
diff --git a/src/utils/hooks/transaction/types/redeem.ts b/src/utils/hooks/transaction/types/redeem.ts
new file mode 100644
index 0000000000..1282278693
--- /dev/null
+++ b/src/utils/hooks/transaction/types/redeem.ts
@@ -0,0 +1,23 @@
+import { InterBtcApi } from '@interlay/interbtc-api';
+
+import { Transaction } from '../types';
+import { TransactionAction } from '.';
+
+interface RedeemCancelAction extends TransactionAction {
+ type: Transaction.REDEEM_CANCEL;
+ args: Parameters;
+}
+
+interface RedeemBurnAction extends TransactionAction {
+ type: Transaction.REDEEM_BURN;
+ args: Parameters;
+}
+
+interface RedeemRequestAction extends TransactionAction {
+ type: Transaction.REDEEM_REQUEST;
+ args: Parameters;
+}
+
+type RedeemActions = RedeemRequestAction | RedeemCancelAction | RedeemBurnAction;
+
+export type { RedeemActions };
diff --git a/src/utils/hooks/transaction/types/replace.ts b/src/utils/hooks/transaction/types/replace.ts
new file mode 100644
index 0000000000..4fab08e0e7
--- /dev/null
+++ b/src/utils/hooks/transaction/types/replace.ts
@@ -0,0 +1,13 @@
+import { InterBtcApi } from '@interlay/interbtc-api';
+
+import { Transaction } from '../types';
+import { TransactionAction } from '.';
+
+interface ReplaceRequestAction extends TransactionAction {
+ type: Transaction.REPLACE_REQUEST;
+ args: Parameters;
+}
+
+type ReplaceActions = ReplaceRequestAction;
+
+export type { ReplaceActions };
diff --git a/src/utils/hooks/transaction/types/rewards.ts b/src/utils/hooks/transaction/types/rewards.ts
new file mode 100644
index 0000000000..f77f61f7c4
--- /dev/null
+++ b/src/utils/hooks/transaction/types/rewards.ts
@@ -0,0 +1,13 @@
+import { InterBtcApi } from '@interlay/interbtc-api';
+
+import { Transaction } from '.';
+import { TransactionAction } from '.';
+
+interface RewardsWithdrawAction extends TransactionAction {
+ type: Transaction.REWARDS_WITHDRAW;
+ args: Parameters;
+}
+
+type RewardsActions = RewardsWithdrawAction;
+
+export type { RewardsActions };
diff --git a/src/utils/hooks/transaction/types/tokens.ts b/src/utils/hooks/transaction/types/tokens.ts
new file mode 100644
index 0000000000..a1c1e0da64
--- /dev/null
+++ b/src/utils/hooks/transaction/types/tokens.ts
@@ -0,0 +1,13 @@
+import { InterBtcApi } from '@interlay/interbtc-api';
+
+import { Transaction } from '../types';
+import { TransactionAction } from '.';
+
+interface TokensTransferAction extends TransactionAction {
+ type: Transaction.TOKENS_TRANSFER;
+ args: Parameters;
+}
+
+type TokensActions = TokensTransferAction;
+
+export type { TokensActions };
diff --git a/src/utils/hooks/transaction/types/vaults.ts b/src/utils/hooks/transaction/types/vaults.ts
new file mode 100644
index 0000000000..1c4040fd17
--- /dev/null
+++ b/src/utils/hooks/transaction/types/vaults.ts
@@ -0,0 +1,23 @@
+import { InterBtcApi } from '@interlay/interbtc-api';
+
+import { Transaction } from '../types';
+import { TransactionAction } from '.';
+
+interface VaultsDepositCollateralAction extends TransactionAction {
+ type: Transaction.VAULTS_DEPOSIT_COLLATERAL;
+ args: Parameters;
+}
+
+interface VaultsWithdrawCollateralAction extends TransactionAction {
+ type: Transaction.VAULTS_WITHDRAW_COLLATERAL;
+ args: Parameters;
+}
+
+interface VaultsRegisterNewCollateralAction extends TransactionAction {
+ type: Transaction.VAULTS_REGISTER_NEW_COLLATERAL;
+ args: Parameters;
+}
+
+type VaultsActions = VaultsDepositCollateralAction | VaultsWithdrawCollateralAction | VaultsRegisterNewCollateralAction;
+
+export type { VaultsActions };
diff --git a/src/utils/hooks/transaction/use-transaction.ts b/src/utils/hooks/transaction/use-transaction.ts
new file mode 100644
index 0000000000..d18291f94c
--- /dev/null
+++ b/src/utils/hooks/transaction/use-transaction.ts
@@ -0,0 +1,122 @@
+import { ExtrinsicStatus } from '@polkadot/types/interfaces';
+import { ISubmittableResult } from '@polkadot/types/types';
+import { useCallback } from 'react';
+import { MutationFunction, useMutation, UseMutationOptions, UseMutationResult } from 'react-query';
+
+import { useSubstrate } from '@/lib/substrate';
+
+import { Transaction, TransactionActions, TransactionArgs } from './types';
+import { getExtrinsic, getStatus } from './utils/extrinsic';
+import { submitTransaction } from './utils/submit';
+
+type UseTransactionOptions = Omit<
+ UseMutationOptions,
+ 'mutationFn'
+> & {
+ customStatus?: ExtrinsicStatus['type'];
+};
+
+// TODO: add feeEstimate and feeEstimateAsync
+type ExecuteArgs = {
+ // Executes the transaction
+ execute(...args: TransactionArgs): void;
+ // Similar to execute but returns a promise which can be awaited.
+ executeAsync(...args: TransactionArgs): Promise;
+};
+
+// TODO: add feeEstimate and feeEstimateAsync
+type ExecuteTypeArgs = {
+ execute(type: D, ...args: TransactionArgs): void;
+ executeAsync(type: D, ...args: TransactionArgs): Promise;
+};
+
+type InheritAttrs = Omit<
+ UseMutationResult,
+ 'mutate' | 'mutateAsync'
+>;
+
+type UseTransactionResult = InheritAttrs & (ExecuteArgs | ExecuteTypeArgs);
+
+const mutateTransaction: MutationFunction = async (params) => {
+ const extrinsics = await getExtrinsic(params);
+ const expectedStatus = params.customStatus || getStatus(params.type);
+
+ return submitTransaction(window.bridge.api, params.accountAddress, extrinsics, expectedStatus, params.events);
+};
+
+// The three declared functions are use to infer types on diferent implementations
+// TODO: missing xcm transaction
+function useTransaction(
+ type: T,
+ options?: UseTransactionOptions
+): Exclude, ExecuteTypeArgs>;
+function useTransaction(
+ options?: UseTransactionOptions
+): Exclude, ExecuteArgs>;
+function useTransaction(
+ typeOrOptions?: T | UseTransactionOptions,
+ options?: UseTransactionOptions
+): UseTransactionResult {
+ const { state } = useSubstrate();
+
+ const hasOnlyOptions = typeof typeOrOptions !== 'string';
+
+ const { mutate, mutateAsync, ...transactionMutation } = useMutation(
+ mutateTransaction,
+ (hasOnlyOptions ? typeOrOptions : options) as UseTransactionOptions
+ );
+
+ // Handles params for both type of implementations
+ const getParams = useCallback(
+ (args: Parameters['execute']>) => {
+ let params = {};
+
+ // Assign correct params for when transaction type is declared on hook params
+ if (typeof typeOrOptions === 'string') {
+ params = { type: typeOrOptions, args };
+ } else {
+ // Assign correct params for when transaction type is declared on execution level
+ const [type, ...restArgs] = args;
+ params = { type, args: restArgs };
+ }
+
+ // Execution should only ran when authenticated
+ const accountAddress = state.selectedAccount?.address;
+
+ // TODO: add event `onReady`
+ return {
+ ...params,
+ accountAddress,
+ customStatus: options?.customStatus
+ } as TransactionActions;
+ },
+ [options?.customStatus, state.selectedAccount?.address, typeOrOptions]
+ );
+
+ const handleExecute = useCallback(
+ (...args: Parameters['execute']>) => {
+ const params = getParams(args);
+
+ return mutate(params);
+ },
+ [getParams, mutate]
+ );
+
+ const handleExecuteAsync = useCallback(
+ (...args: Parameters['executeAsync']>) => {
+ const params = getParams(args);
+
+ return mutateAsync(params);
+ },
+ [getParams, mutateAsync]
+ );
+
+ return {
+ ...transactionMutation,
+ execute: handleExecute,
+ executeAsync: handleExecuteAsync
+ };
+}
+
+export { useTransaction };
+export type { UseTransactionResult };
diff --git a/src/utils/hooks/transaction/utils/extrinsic.ts b/src/utils/hooks/transaction/utils/extrinsic.ts
new file mode 100644
index 0000000000..23346819db
--- /dev/null
+++ b/src/utils/hooks/transaction/utils/extrinsic.ts
@@ -0,0 +1,133 @@
+import { ExtrinsicData } from '@interlay/interbtc-api';
+import { ExtrinsicStatus } from '@polkadot/types/interfaces';
+
+import { Transaction, TransactionActions } from '../types';
+
+/**
+ * SUMMARY: Maps each transaction to the correct lib call,
+ * while maintaining a safe-type check.
+ * HOW TO ADD NEW TRANSACTION: find the correct module to add the transaction
+ * in the types folder. In case you are adding a new type to the loans modules, go
+ * to types/loans and add your new transaction as an action. This actions needs to also be added to the
+ * types/index TransactionActions type. After that, you should be able to add it to the function.
+ * @param {TransactionActions} params contains the type of transaction and
+ * the related args to call the mapped lib call
+ * @return {Promise} every transaction return an extrinsic
+ */
+const getExtrinsic = async (params: TransactionActions): Promise => {
+ switch (params.type) {
+ /* START - AMM */
+ case Transaction.AMM_SWAP:
+ return window.bridge.amm.swap(...params.args);
+ case Transaction.AMM_ADD_LIQUIDITY:
+ return window.bridge.amm.addLiquidity(...params.args);
+ case Transaction.AMM_REMOVE_LIQUIDITY:
+ return window.bridge.amm.removeLiquidity(...params.args);
+ case Transaction.AMM_CLAIM_REWARDS:
+ return window.bridge.amm.claimFarmingRewards(...params.args);
+ /* END - AMM */
+
+ /* START - ISSUE */
+ case Transaction.ISSUE_REQUEST:
+ return window.bridge.issue.request(...params.args);
+ case Transaction.ISSUE_EXECUTE:
+ return window.bridge.issue.execute(...params.args);
+ /* END - ISSUE */
+
+ /* START - REDEEM */
+ case Transaction.REDEEM_CANCEL:
+ return window.bridge.redeem.cancel(...params.args);
+ case Transaction.REDEEM_BURN:
+ return window.bridge.redeem.burn(...params.args);
+ case Transaction.REDEEM_REQUEST:
+ return window.bridge.redeem.request(...params.args);
+ /* END - REDEEM */
+
+ /* START - REPLACE */
+ case Transaction.REPLACE_REQUEST:
+ return window.bridge.replace.request(...params.args);
+ /* END - REPLACE */
+
+ /* START - TOKENS */
+ case Transaction.TOKENS_TRANSFER:
+ return window.bridge.tokens.transfer(...params.args);
+ /* END - TOKENS */
+
+ /* START - LOANS */
+ case Transaction.LOANS_CLAIM_REWARDS:
+ return window.bridge.loans.claimAllSubsidyRewards();
+ case Transaction.LOANS_BORROW:
+ return window.bridge.loans.borrow(...params.args);
+ case Transaction.LOANS_LEND:
+ return window.bridge.loans.lend(...params.args);
+ case Transaction.LOANS_REPAY:
+ return window.bridge.loans.repay(...params.args);
+ case Transaction.LOANS_REPAY_ALL:
+ return window.bridge.loans.repayAll(...params.args);
+ case Transaction.LOANS_WITHDRAW:
+ return window.bridge.loans.withdraw(...params.args);
+ case Transaction.LOANS_WITHDRAW_ALL:
+ return window.bridge.loans.withdrawAll(...params.args);
+ case Transaction.LOANS_DISABLE_COLLATERAL:
+ return window.bridge.loans.disableAsCollateral(...params.args);
+ case Transaction.LOANS_ENABLE_COLLATERAL:
+ return window.bridge.loans.enableAsCollateral(...params.args);
+ /* END - LOANS */
+
+ /* START - LOANS */
+ case Transaction.VAULTS_DEPOSIT_COLLATERAL:
+ return window.bridge.vaults.depositCollateral(...params.args);
+ case Transaction.VAULTS_WITHDRAW_COLLATERAL:
+ return window.bridge.vaults.withdrawCollateral(...params.args);
+ case Transaction.VAULTS_REGISTER_NEW_COLLATERAL:
+ return window.bridge.vaults.registerNewCollateralVault(...params.args);
+ /* START - REWARDS */
+ case Transaction.REWARDS_WITHDRAW:
+ return window.bridge.rewards.withdrawRewards(...params.args);
+ /* START - REWARDS */
+ /* END - LOANS */
+
+ /* START - ESCROW */
+ case Transaction.ESCROW_CREATE_LOCK:
+ return window.bridge.escrow.createLock(...params.args);
+ case Transaction.ESCROW_INCREASE_LOCKED_AMOUNT:
+ return window.bridge.escrow.increaseAmount(...params.args);
+ case Transaction.ESCROW_INCREASE_LOCKED_TIME:
+ return window.bridge.escrow.increaseUnlockHeight(...params.args);
+ case Transaction.ESCROW_WITHDRAW:
+ return window.bridge.escrow.withdraw(...params.args);
+ case Transaction.ESCROW_WITHDRAW_REWARDS:
+ return window.bridge.escrow.withdrawRewards(...params.args);
+ case Transaction.ESCROW_INCREASE_LOOKED_TIME_AND_AMOUNT: {
+ const [amount, unlockHeight] = params.args;
+ const txs = [
+ window.bridge.api.tx.escrow.increaseAmount(amount),
+ window.bridge.api.tx.escrow.increaseUnlockHeight(unlockHeight)
+ ];
+ const batch = window.bridge.api.tx.utility.batchAll(txs);
+
+ return { extrinsic: batch };
+ }
+ /* END - ESCROW */
+ }
+};
+
+/**
+ * The status where we want to be notified on the transaction completion
+ * @param {Transaction} type type of transaction
+ * @return {ExtrinsicStatus.type} transaction status
+ */
+const getStatus = (type: Transaction): ExtrinsicStatus['type'] => {
+ switch (type) {
+ // When requesting a replace, wait for the finalized event because we cannot revert BTC transactions.
+ // For more details see: https://github.com/interlay/interbtc-api/pull/373#issuecomment-1058949000
+ case Transaction.ISSUE_REQUEST:
+ case Transaction.REDEEM_REQUEST:
+ case Transaction.REPLACE_REQUEST:
+ return 'Finalized';
+ default:
+ return 'InBlock';
+ }
+};
+
+export { getExtrinsic, getStatus };
diff --git a/src/utils/hooks/transaction/utils/submit.ts b/src/utils/hooks/transaction/utils/submit.ts
new file mode 100644
index 0000000000..d1c832b023
--- /dev/null
+++ b/src/utils/hooks/transaction/utils/submit.ts
@@ -0,0 +1,107 @@
+import { ExtrinsicData } from '@interlay/interbtc-api';
+import { ApiPromise } from '@polkadot/api';
+import { AddressOrPair, SubmittableExtrinsic } from '@polkadot/api/types';
+import { DispatchError } from '@polkadot/types/interfaces';
+import { ExtrinsicStatus } from '@polkadot/types/interfaces/author';
+import { ISubmittableResult } from '@polkadot/types/types';
+
+import { TransactionEvents } from '../types';
+
+type HandleTransactionResult = { result: ISubmittableResult; unsubscribe: () => void };
+
+// When passing { nonce: -1 } to signAndSend the API will use system.accountNextIndex to determine the nonce
+const transactionOptions = { nonce: -1 };
+
+const handleTransaction = async (
+ account: AddressOrPair,
+ extrinsicData: ExtrinsicData,
+ expectedStatus?: ExtrinsicStatus['type'],
+ callbacks?: TransactionEvents
+) => {
+ let isComplete = false;
+
+ // Extrinsic status
+ let isReady = false;
+
+ return new Promise((resolve, reject) => {
+ let unsubscribe: () => void;
+
+ (extrinsicData.extrinsic as SubmittableExtrinsic<'promise'>)
+ .signAndSend(account, transactionOptions, callback)
+ .then((unsub) => (unsubscribe = unsub))
+ .catch((error) => reject(error));
+
+ function callback(result: ISubmittableResult): void {
+ const { onReady } = callbacks || {};
+
+ if (!isReady && result.status.isReady) {
+ onReady?.();
+ isReady = true;
+ }
+
+ if (!isComplete) {
+ isComplete = expectedStatus === result.status.type;
+ }
+
+ if (isComplete) {
+ resolve({ unsubscribe, result });
+ }
+ }
+ });
+};
+
+const getErrorMessage = (api: ApiPromise, dispatchError: DispatchError) => {
+ const { isModule, asModule, isBadOrigin } = dispatchError;
+
+ // Construct error message
+ const message = 'The transaction failed.';
+
+ // Runtime error in one of the parachain modules
+ if (isModule) {
+ // for module errors, we have the section indexed, lookup
+ const decoded = api.registry.findMetaError(asModule);
+ const { docs, name, section } = decoded;
+ return message.concat(` The error code is ${section}.${name}. ${docs.join(' ')}`);
+ }
+
+ // Bad origin
+ if (isBadOrigin) {
+ return message.concat(
+ ` The error is caused by using an incorrect account. The error code is BadOrigin ${dispatchError}.`
+ );
+ }
+
+ return message.concat(` The error is ${dispatchError}.`);
+};
+
+/**
+ * Handles transaction submittion and error
+ * @param {ApiPromise} api polkadot api wrapper
+ * @param {AddressOrPair} account account address
+ * @param {ExtrinsicData} extrinsicData transaction extrinsic data
+ * @param {ExtrinsicStatus.type} expectedStatus status where the transaction is counted as fulfilled
+ * @param {TransactionEvents} callbacks a set of events emitted accross the lifecycle of the transaction (i.e Bro)
+ * @return {Promise} transaction data that also can contain meta data in case of error
+ */
+const submitTransaction = async (
+ api: ApiPromise,
+ account: AddressOrPair,
+ extrinsicData: ExtrinsicData,
+ expectedStatus?: ExtrinsicStatus['type'],
+ callbacks?: TransactionEvents
+): Promise => {
+ const { result, unsubscribe } = await handleTransaction(account, extrinsicData, expectedStatus, callbacks);
+
+ unsubscribe();
+
+ const { dispatchError } = result;
+
+ if (dispatchError) {
+ const message = getErrorMessage(api, dispatchError);
+ throw new Error(message);
+ }
+
+ return result;
+};
+
+export { submitTransaction };
diff --git a/src/utils/hooks/use-feature-flag.ts b/src/utils/hooks/use-feature-flag.ts
index 9b5676a053..917bd3b3c8 100644
--- a/src/utils/hooks/use-feature-flag.ts
+++ b/src/utils/hooks/use-feature-flag.ts
@@ -2,14 +2,18 @@ enum FeatureFlags {
LENDING = 'lending',
AMM = 'amm',
WALLET = 'wallet',
- BANXA = 'banxa'
+ BANXA = 'banxa',
+ EARN_STRATEGIES = 'earn-strategies',
+ GEOBLOCK = 'geoblock'
}
const featureFlags: Record = {
[FeatureFlags.LENDING]: process.env.REACT_APP_FEATURE_FLAG_LENDING,
[FeatureFlags.AMM]: process.env.REACT_APP_FEATURE_FLAG_AMM,
[FeatureFlags.WALLET]: process.env.REACT_APP_FEATURE_FLAG_WALLET,
- [FeatureFlags.BANXA]: process.env.REACT_APP_FEATURE_FLAG_BANXA
+ [FeatureFlags.BANXA]: process.env.REACT_APP_FEATURE_FLAG_BANXA,
+ [FeatureFlags.EARN_STRATEGIES]: process.env.REACT_APP_FEATURE_FLAG_EARN_STRATEGIES,
+ [FeatureFlags.GEOBLOCK]: process.env.REACT_APP_FEATURE_FLAG_GEOBLOCK
};
const useFeatureFlag = (feature: FeatureFlags): boolean => featureFlags[feature] === 'enabled';
diff --git a/src/utils/hooks/use-geoblocking.ts b/src/utils/hooks/use-geoblocking.ts
new file mode 100644
index 0000000000..09805c8398
--- /dev/null
+++ b/src/utils/hooks/use-geoblocking.ts
@@ -0,0 +1,27 @@
+import { useEffect } from 'react';
+
+import { GEOBLOCK_API_ENDPOINT, GEOBLOCK_REDIRECTION_LINK } from '@/config/links';
+import { FeatureFlags, useFeatureFlag } from '@/utils/hooks/use-feature-flag';
+
+const useGeoblocking = (): void => {
+ const isGeoblockEnabled = useFeatureFlag(FeatureFlags.GEOBLOCK);
+
+ useEffect(() => {
+ if (!isGeoblockEnabled) return;
+
+ const checkCountry = async () => {
+ try {
+ const response = await fetch(GEOBLOCK_API_ENDPOINT);
+ if (response.status === 403) {
+ console.log('Access from forbidden country detected, user will be redirected.');
+ window.location.replace(GEOBLOCK_REDIRECTION_LINK);
+ }
+ } catch (error) {
+ console.log(error);
+ }
+ };
+ checkCountry();
+ }, [isGeoblockEnabled]);
+};
+
+export { useGeoblocking };
diff --git a/yarn.lock b/yarn.lock
index 55a00193f5..269fc4fdba 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"
@@ -2027,10 +2075,10 @@
resolved "https://registry.yarnpkg.com/@headlessui/react/-/react-1.5.0.tgz#483b44ba2c8b8d4391e1d2c863898d7dd0cc0296"
integrity sha512-aaRnYxBb3MU2FNJf3Ut9RMTUqqU3as0aI1lQhgo2n9Fa67wRu14iOGqx93xB+uMNVfNwZ5B3y/Ndm7qZGuFeMQ==
-"@heroicons/react@^2.0.0":
- version "2.0.12"
- resolved "https://registry.yarnpkg.com/@heroicons/react/-/react-2.0.12.tgz#7e5a16c82512f89a30266dd36f8b8465b3e3e216"
- integrity sha512-FZxKh3i9aKIDxyALTgIpSF2t6V6/eZfF5mRu41QlwkX3Oxzecdm1u6dpft6PQGxIBwO7TKYWaMAYYL8mp/EaOg==
+"@heroicons/react@^2.0.18":
+ version "2.0.18"
+ resolved "https://registry.yarnpkg.com/@heroicons/react/-/react-2.0.18.tgz#f80301907c243df03c7e9fd76c0286e95361f7c1"
+ integrity sha512-7TyMjRrZZMBPa+/5Y8lN0iyvUU/01PeMGX2+RE7cQWpEUIcb4QotzUObFkJDejj/HUH4qjP/eQ0gzzKs2f+6Yw==
"@humanwhocodes/config-array@^0.5.0":
version "0.5.0"
@@ -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.9":
- version "0.2.9"
- resolved "https://registry.yarnpkg.com/@interlay/bridge/-/bridge-0.2.9.tgz#3b3785ec4752c0c2f0cedaf9a6b62ef3f4235778"
- integrity sha512-J2yJNK2yCeUP5dMDgqGsMckRzmh1qM4ge/CGBK5AYHUHzEKr2KcCMY7UrKCaSXzRYMVdLc+f8hd+FeMHCznffg==
- 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.10":
+ version "0.3.10"
+ resolved "https://registry.yarnpkg.com/@interlay/bridge/-/bridge-0.3.10.tgz#d686b83af91f99a1f62b72cfbb8d127c60557200"
+ integrity sha512-8yDfhvDNIeaW07Kbfvjp37YUNpsKsSXooT5JVfC1AdQs9ej51fbuhINUK31JNncI0xCWhTBu72Wq0ZFIQNm8RA==
+ 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"
@@ -2073,14 +2120,14 @@
dependencies:
axios "^0.21.1"
-"@interlay/interbtc-api@2.2.2":
- version "2.2.2"
- resolved "https://registry.yarnpkg.com/@interlay/interbtc-api/-/interbtc-api-2.2.2.tgz#4803a80244abc9ef404dddefd265b9ece7ca859f"
- integrity sha512-NuRjjIqeUPkt+aOTTmjMhx3TKAsL4id8kubiQsrAcyhsZnsnv/1bCXECzAWaZHVSi+XcxzfuoNLOxqrrx2+ISw==
+"@interlay/interbtc-api@2.2.4":
+ version "2.2.4"
+ resolved "https://registry.yarnpkg.com/@interlay/interbtc-api/-/interbtc-api-2.2.4.tgz#28b429d066d35f77fdc72f4cf57e2452507c37f7"
+ integrity sha512-cJxSE7J41JPE8QhV0YiLCJEfvpv9JcSWmieITTSOWQCW8GFFXnSTU0iPA2Tgw6s9ea3uxoM2DLGhlDQL8c0ktw==
dependencies:
"@interlay/esplora-btc-api" "0.4.0"
"@interlay/interbtc-types" "1.12.0"
- "@interlay/monetary-js" "0.7.2"
+ "@interlay/monetary-js" "0.7.3"
"@polkadot/api" "9.14.2"
big.js "6.1.1"
bitcoin-core "^3.0.0"
@@ -2090,20 +2137,20 @@
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"
- integrity sha512-SqNKJKBEstXuLnzqWi+ON+9ImrNVlKqZpXfiTtT3bcvxqh4jnVsGbYVe2I0FqDWtilCWq6/1RjKkpaSG2dvJhA==
+"@interlay/monetary-js@0.7.3":
+ version "0.7.3"
+ resolved "https://registry.yarnpkg.com/@interlay/monetary-js/-/monetary-js-0.7.3.tgz#0bf4c56b15fde2fd0573e6cac185b0703f368133"
+ integrity sha512-LbCtLRNjl1/LO8R1ay6lJwKgOC/J40YywF+qSuQ7hEjLIkAslY5dLH11heQgQW9hOmqCSS5fTUQWXhmYQr6Ksg==
dependencies:
"@types/big.js" "6.1.2"
big.js "6.1.1"
@@ -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==