{
+ const theme = useTheme();
+
+ return (
+
+
+ {`Relay chain: ${formatBalance(relayBalance.toString(), false)} ${symbol}`}
+
+
+ {`Coretime chain: ${formatBalance(coretimeBalance.toString(), false)} ${symbol}`}
+
+
+ );
+};
+
+export default Balance;
diff --git a/src/components/Elements/Selectors/AssetSelector/index.module.scss b/src/components/Elements/Selectors/AssetSelector/index.module.scss
new file mode 100644
index 00000000..91eb8bb9
--- /dev/null
+++ b/src/components/Elements/Selectors/AssetSelector/index.module.scss
@@ -0,0 +1,9 @@
+.options {
+ display: flex;
+ justify-content: center;
+}
+
+.option {
+ min-width: 250px;
+ margin: 1em 5em;
+}
\ No newline at end of file
diff --git a/src/components/Elements/Selectors/AssetSelector/index.tsx b/src/components/Elements/Selectors/AssetSelector/index.tsx
new file mode 100644
index 00000000..9c4d95c0
--- /dev/null
+++ b/src/components/Elements/Selectors/AssetSelector/index.tsx
@@ -0,0 +1,45 @@
+import { ToggleButton, ToggleButtonGroup, useTheme } from '@mui/material';
+import FormControl from '@mui/material/FormControl';
+
+import { AssetType } from '@/models';
+
+import styles from './index.module.scss';
+
+interface AssetSelectorProps {
+ asset: AssetType;
+ setAsset: (_: AssetType) => void;
+ symbol: string;
+}
+
+export default function AssetSelector({
+ asset,
+ setAsset,
+ symbol,
+}: AssetSelectorProps) {
+ const theme = useTheme();
+ return (
+
+ setAsset(parseInt(e.target.value) as AssetType)}
+ className={styles.options}
+ >
+
+ {symbol}
+
+
+ Region
+
+
+
+ );
+}
diff --git a/src/components/Elements/Selectors/ChainSelector/index.tsx b/src/components/Elements/Selectors/ChainSelector/index.tsx
index 1787cb4b..7ddcf621 100644
--- a/src/components/Elements/Selectors/ChainSelector/index.tsx
+++ b/src/components/Elements/Selectors/ChainSelector/index.tsx
@@ -1,11 +1,43 @@
-import { FormControl, InputLabel, MenuItem, Select } from '@mui/material';
+import {
+ Box,
+ FormControl,
+ InputLabel,
+ MenuItem,
+ Select,
+ Typography,
+} from '@mui/material';
+import Image from 'next/image';
+
+import CoretimeIcon from '@/assets/networks/coretime.png';
+import RegionXIcon from '@/assets/networks/regionx.png';
+// import KusamaIcon from '@/assets/networks/kusama.png';
+import RococoIcon from '@/assets/networks/rococo.png';
+import { ChainType } from '@/models';
interface ChainSelectorProps {
- chain: string;
- setChain: (_: string) => void;
+ chain: ChainType;
+ setChain: (_: ChainType) => void;
}
export const ChainSelector = ({ chain, setChain }: ChainSelectorProps) => {
+ const menuItems = [
+ {
+ icon: RococoIcon,
+ label: 'Relay Chain',
+ value: ChainType.RELAY,
+ },
+ {
+ icon: CoretimeIcon,
+ label: 'Coretime Chain',
+ value: ChainType.CORETIME,
+ },
+
+ {
+ icon: RegionXIcon,
+ label: 'RegionX Chain',
+ value: ChainType.REGIONX,
+ },
+ ];
return (
Chain
@@ -14,10 +46,22 @@ export const ChainSelector = ({ chain, setChain }: ChainSelectorProps) => {
id='origin-selector'
value={chain}
label='Origin'
- onChange={(e) => setChain(e.target.value)}
+ onChange={(e) => setChain(e.target.value as ChainType)}
>
-
-
+ {menuItems.map(({ icon, label, value }, index) => (
+
+ ))}
);
diff --git a/src/components/Layout/index.module.scss b/src/components/Layout/index.module.scss
index 01a5c053..f405f427 100644
--- a/src/components/Layout/index.module.scss
+++ b/src/components/Layout/index.module.scss
@@ -19,4 +19,9 @@
flex-grow: 1;
min-height: calc(100vh - 9rem);
max-height: calc(100vh - 9rem);
+ overflow-y: scroll;
}
+
+.main::-webkit-scrollbar {
+ display: none;
+}
\ No newline at end of file
diff --git a/src/hooks/balance.tsx b/src/hooks/balance.tsx
index 34cc3502..52ff25b3 100644
--- a/src/hooks/balance.tsx
+++ b/src/hooks/balance.tsx
@@ -1,42 +1,73 @@
+import { ApiPromise } from '@polkadot/api';
import { useInkathon } from '@scio-labs/use-inkathon';
import { useCallback, useEffect, useState } from 'react';
-import { useCoretimeApi } from '@/contexts/apis';
+import { parseHNString } from '@/utils/functions';
+
+import { useCoretimeApi, useRelayApi } from '@/contexts/apis';
import { ApiState } from '@/contexts/apis/types';
import { useToast } from '@/contexts/toast';
// React hook for fetching balance.
const useBalance = () => {
const {
- state: { api, apiState, symbol },
+ state: { api: coretimeApi, apiState: coretimeApiState, symbol },
} = useCoretimeApi();
+ const {
+ state: { api: relayApi, apiState: relayApiState },
+ } = useRelayApi();
+
const { activeAccount } = useInkathon();
- const [balance, setBalance] = useState(0);
+ const [coretimeBalance, setCoretimeBalance] = useState(0);
+ const [relayBalance, setRelayBalance] = useState(0);
const { toastWarning } = useToast();
- const fetchBalance = useCallback(async () => {
- if (api && apiState == ApiState.READY && activeAccount) {
+ const fetchBalance = useCallback(
+ async (api: ApiPromise): Promise => {
+ if (!activeAccount) return;
+
const accountData: any = (
await api.query.system.account(activeAccount.address)
).toHuman();
- const balance = parseFloat(accountData.data.free.toString());
- setBalance(balance);
-
- if (balance === 0) {
- toastWarning(
- `The selected account does not have any ${symbol} tokens on the Coretime chain.`
- );
- }
+ const balance = parseHNString(accountData.data.free.toString());
+
+ return balance;
+ },
+ [activeAccount]
+ );
+
+ const fetchBalances = useCallback(() => {
+ if (coretimeApi && coretimeApiState == ApiState.READY) {
+ fetchBalance(coretimeApi).then((balance) => {
+ balance !== undefined && setCoretimeBalance(balance);
+ balance === 0 &&
+ toastWarning(
+ `The selected account does not have any ${symbol} tokens on the Coretime chain.`
+ );
+ });
+ }
+ if (relayApi && relayApiState == ApiState.READY) {
+ fetchBalance(relayApi).then((balance) => {
+ balance !== undefined && setRelayBalance(balance);
+ });
}
- }, [api, apiState, activeAccount, toastWarning, symbol]);
+ }, [
+ fetchBalance,
+ toastWarning,
+ symbol,
+ coretimeApi,
+ coretimeApiState,
+ relayApi,
+ relayApiState,
+ ]);
useEffect(() => {
- fetchBalance();
- }, [fetchBalance]);
+ fetchBalances();
+ });
- return balance;
+ return { coretimeBalance, relayBalance, fetchBalances };
};
export default useBalance;
diff --git a/src/models/types.ts b/src/models/types.ts
index e53df5a7..73a11532 100644
--- a/src/models/types.ts
+++ b/src/models/types.ts
@@ -16,6 +16,26 @@ export type ParaId = number;
export type BlockNumber = number;
+export enum AssetType {
+ // eslint-disable-next-line no-unused-vars
+ NONE = 0,
+ // eslint-disable-next-line no-unused-vars
+ TOKEN = 1,
+ // eslint-disable-next-line no-unused-vars
+ REGION = 2,
+}
+
+export enum ChainType {
+ // eslint-disable-next-line no-unused-vars
+ NONE = 0,
+ // eslint-disable-next-line no-unused-vars
+ CORETIME = 1,
+ // eslint-disable-next-line no-unused-vars
+ RELAY = 2,
+ // eslint-disable-next-line no-unused-vars
+ REGIONX = 3,
+}
+
export type Sender = {
address: string;
signer: Signer;
@@ -27,6 +47,7 @@ export type TxStatusHandlers = {
finalized: () => void;
success: () => void;
error: () => void;
+ finally?: () => void;
};
export enum RegionLocation {
diff --git a/src/pages/purchase.tsx b/src/pages/purchase.tsx
index 1fafe01d..97b53c05 100644
--- a/src/pages/purchase.tsx
+++ b/src/pages/purchase.tsx
@@ -8,9 +8,10 @@ import { useState } from 'react';
import useBalance from '@/hooks/balance';
import useSalePhase from '@/hooks/salePhase';
import useSalePrice from '@/hooks/salePrice';
-import { formatBalance } from '@/utils/functions';
+import { sendTx } from '@/utils/functions';
import { CoreDetailsPanel, ProgressButton, SaleInfoPanel } from '@/components';
+import Balance from '@/components/Elements/Balance';
import { useCoretimeApi } from '@/contexts/apis';
import { ApiState } from '@/contexts/apis/types';
@@ -35,7 +36,7 @@ const Purchase = () => {
const { fetchRegions } = useRegions();
- const balance = useBalance();
+ const { coretimeBalance, relayBalance } = useBalance();
const currentPrice = useSalePrice();
const { currentPhase, progress, saleStartTimestamp, saleEndTimestamp } =
useSalePhase();
@@ -46,31 +47,18 @@ const Purchase = () => {
const txPurchase = api.tx.broker.purchase(currentPrice);
- try {
- setWorking(true);
- await txPurchase.signAndSend(
- activeAccount.address,
- { signer: activeSigner },
- ({ status, events }) => {
- if (status.isReady) toastInfo('Transaction was initiated');
- else if (status.isInBlock) toastInfo(`In Block`);
- else if (status.isFinalized) {
- setWorking(false);
- events.forEach(({ event: { method } }) => {
- if (method === 'ExtrinsicSuccess') {
- toastSuccess('Transaction successful');
- fetchRegions();
- } else if (method === 'ExtrinsicFailed') {
- toastError(`Failed to purchase the region`);
- }
- });
- }
- }
- );
- } catch (e) {
- toastError(`Failed to purchase the region. ${e}`);
- setWorking(false);
- }
+ sendTx(txPurchase, activeAccount.address, activeSigner, {
+ ready: () => toastInfo('Transaction was initiated'),
+ inBlock: () => toastInfo(`In Block`),
+ finalized: () => setWorking(false),
+ success: () => {
+ toastSuccess('Transaction successful');
+ fetchRegions();
+ },
+ error: () => {
+ toastError(`Failed to purchase the region`);
+ },
+ });
};
return (
@@ -96,12 +84,11 @@ const Purchase = () => {
Buy a core straight from the Coretime chain
-
- {`Your balance: ${formatBalance(
- balance.toString(),
- false
- )} ${symbol}`}
-
+
{loading ||
diff --git a/src/pages/transfer.tsx b/src/pages/transfer.tsx
index 9361f42f..85d40431 100644
--- a/src/pages/transfer.tsx
+++ b/src/pages/transfer.tsx
@@ -9,32 +9,54 @@ import {
Stack,
Typography,
} from '@mui/material';
+import { Keyring } from '@polkadot/api';
import { useInkathon } from '@scio-labs/use-inkathon';
import { Region } from 'coretime-utils';
import { useEffect, useState } from 'react';
+import useBalance from '@/hooks/balance';
+import {
+ transferTokensFromCoretimeToRelay,
+ transferTokensFromRelayToCoretime,
+} from '@/utils/crossChain/transfer';
import theme from '@/utils/muiTheme';
-import { transferRegionOnCoretimeChain } from '@/utils/native/transfer';
+import {
+ transferNativeToken,
+ transferRegionOnCoretimeChain,
+} from '@/utils/native/transfer';
import {
+ AmountInput,
ChainSelector,
RecipientSelector,
RegionCard,
RegionSelector,
} from '@/components';
+import Balance from '@/components/Elements/Balance';
+import AssetSelector from '@/components/Elements/Selectors/AssetSelector';
-import { useCoretimeApi } from '@/contexts/apis';
+import { useCoretimeApi, useRelayApi } from '@/contexts/apis';
+import { ApiState } from '@/contexts/apis/types';
import { useRegions } from '@/contexts/regions';
import { useToast } from '@/contexts/toast';
-import { RegionLocation, RegionMetadata } from '@/models';
+import {
+ AssetType,
+ ChainType,
+ CORETIME_DECIMALS,
+ RegionLocation,
+ RegionMetadata,
+} from '@/models';
const TransferPage = () => {
const { activeAccount, activeSigner } = useInkathon();
const { toastError, toastInfo, toastWarning, toastSuccess } = useToast();
const {
- state: { api: coretimeApi },
+ state: { api: coretimeApi, apiState: coretimeApiState, symbol },
} = useCoretimeApi();
+ const {
+ state: { api: relayApi, apiState: relayApiState },
+ } = useRelayApi();
const { regions } = useRegions();
const [filteredRegions, setFilteredRegions] = useState>(
@@ -43,42 +65,123 @@ const TransferPage = () => {
const [working, setWorking] = useState(false);
const [newOwner, setNewOwner] = useState('');
- const [originChain, setOriginChain] = useState('');
- const [destinationChain, setDestinationChain] = useState('');
+ const [originChain, setOriginChain] = useState(ChainType.NONE);
+ const [destinationChain, setDestinationChain] = useState(
+ ChainType.NONE
+ );
const [statusLabel, _setStatusLabel] = useState('');
const [selectedRegion, setSelectedRegion] = useState(
null
);
+ const [asset, setAsset] = useState(AssetType.TOKEN);
+ const [transferAmount, setTransferAmount] = useState('');
+
+ const { coretimeBalance, relayBalance, fetchBalances } = useBalance();
+
+ const defaultHandler = {
+ ready: () => toastInfo('Transaction was initiated.'),
+ inBlock: () => toastInfo(`In Block`),
+ finalized: () => setWorking(false),
+ success: () => {
+ fetchBalances();
+ toastSuccess('Successfully transferred.');
+ },
+ error: () => {
+ toastError(`Failed to transfer.`);
+ setWorking(false);
+ },
+ };
+
useEffect(() => {
setFilteredRegions(
regions.filter((r) => r.location != RegionLocation.MARKET)
);
}, [regions]);
- const handleOriginChange = (newOrigin: string) => {
+ const handleOriginChange = (newOrigin: ChainType) => {
setOriginChain(newOrigin);
- if (newOrigin === 'CoretimeChain') {
- setFilteredRegions(
- regions.filter((r) => r.location == RegionLocation.CORETIME_CHAIN)
+ setFilteredRegions(
+ regions.filter(
+ (r) =>
+ r.location ===
+ (newOrigin === ChainType.CORETIME
+ ? RegionLocation.CORETIME_CHAIN
+ : RegionLocation.REGIONX_CHAIN)
+ )
+ );
+ if (newOrigin === ChainType.RELAY) setAsset(AssetType.TOKEN);
+ };
+
+ const handleTransfer = async () => {
+ if (!activeAccount || !activeSigner) {
+ toastWarning('Connect wallet first');
+ return;
+ }
+ if (asset === AssetType.REGION) {
+ handleRegionTransfer();
+ } else if (asset === AssetType.TOKEN) {
+ handleTokenTransfer();
+ }
+ };
+
+ const handleTokenTransfer = async () => {
+ if (!activeAccount || !activeSigner) return;
+ if (!originChain || !destinationChain) return;
+
+ if (!coretimeApi || coretimeApiState !== ApiState.READY) {
+ toastError('Not connected to the Coretime chain');
+ return;
+ }
+ if (!relayApi || relayApiState !== ApiState.READY) {
+ toastError('Not connected to the relay chain');
+ return;
+ }
+
+ const amount = Number(transferAmount) * Math.pow(10, CORETIME_DECIMALS);
+ if (originChain === destinationChain) {
+ if (!newOwner) {
+ toastError('Recipient must be selected');
+ return;
+ }
+ transferNativeToken(
+ originChain === ChainType.CORETIME ? coretimeApi : relayApi,
+ activeSigner,
+ activeAccount.address,
+ newOwner,
+ amount.toString(),
+ defaultHandler
);
} else {
- setFilteredRegions(
- regions.filter((r) => r.location == RegionLocation.REGIONX_CHAIN)
+ const receiverKeypair = new Keyring();
+ receiverKeypair.addFromAddress(
+ newOwner ? newOwner : activeAccount.address
+ );
+
+ (originChain === ChainType.CORETIME
+ ? transferTokensFromCoretimeToRelay
+ : transferTokensFromRelayToCoretime
+ ).call(
+ this,
+ originChain === ChainType.CORETIME ? coretimeApi : relayApi,
+ { address: activeAccount.address, signer: activeSigner },
+ amount.toString(),
+ receiverKeypair.pairs[0].publicKey,
+ defaultHandler
);
}
};
- const handleTransfer = async () => {
+ const handleRegionTransfer = async () => {
if (!selectedRegion) {
toastError('Select a region');
return;
}
if (originChain === destinationChain) {
- originChain === 'CoretimeChain'
- ? transferCoretimeRegion(selectedRegion.region)
+ originChain === ChainType.CORETIME
+ ? await transferCoretimeRegion(selectedRegion.region)
: toastWarning('Currently not supported');
} else {
toastWarning('Currently not supported');
@@ -98,37 +201,47 @@ const TransferPage = () => {
region,
activeSigner,
activeAccount.address,
- newOwner ? newOwner : activeAccount.address,
- {
- ready: () => toastInfo('Transaction was initiated.'),
- inBlock: () => toastInfo(`In Block`),
- finalized: () => setWorking(false),
- success: () => toastSuccess('Successfully transferred the region.'),
- error: () => {
- toastError(`Failed to transfer the region.`);
- setWorking(false);
- },
- }
+ newOwner ?? activeAccount.address,
+ defaultHandler
);
};
return (
-
-
- Cross-Chain Transfer
-
-
- Cross-chain transfer regions
-
+
+
+
+ Cross-Chain Transfer
+
+
+ Cross-chain transfer regions
+
+
+
-
+
Origin chain:
@@ -140,21 +253,36 @@ const TransferPage = () => {
setChain={setDestinationChain}
/>
- {originChain && (
-
- Region
- setSelectedRegion(regions[indx])}
- />
-
- )}
+ {originChain !== ChainType.NONE &&
+ destinationChain !== ChainType.NONE && (
+
+
+
+ )}
+
+ {asset === AssetType.REGION &&
+ originChain !== ChainType.NONE &&
+ destinationChain !== ChainType.NONE && (
+
+ Region
+ setSelectedRegion(regions[indx])}
+ />
+
+ )}
{selectedRegion && (
@@ -168,12 +296,24 @@ const TransferPage = () => {
Transfer to:
+ {asset === AssetType.TOKEN &&
+ originChain !== ChainType.NONE &&
+ destinationChain !== ChainType.NONE && (
+
+
+
+ )}
{statusLabel && (
{statusLabel}
)}
-
+
diff --git a/src/utils/crossChain/consts.tsx b/src/utils/crossChain/consts.tsx
index 5f508a6a..4a79d5ad 100644
--- a/src/utils/crossChain/consts.tsx
+++ b/src/utils/crossChain/consts.tsx
@@ -9,6 +9,15 @@ export const CoretimeChain = {
},
};
+export const CoretimeChainFromRelayPerspective = {
+ parents: 0,
+ interior: {
+ X1: {
+ Parachain: CORETIME_PARA_ID,
+ },
+ },
+};
+
export const RegionXChain = {
parents: 1,
interior: {
@@ -33,3 +42,18 @@ export const CoretimeRegionFromRegionXPerspective = {
X2: [{ Parachain: CORETIME_PARA_ID }, { PalletInstance: BROKER_PALLET_ID }],
},
};
+
+export const RelayChainFromParachainPerspective = {
+ parents: 1,
+ interior: 'Here',
+};
+
+export const RcTokenFromParachainPerspective = {
+ parents: 1,
+ interior: 'Here',
+};
+
+export const RcTokenFromRelayPerspective = {
+ parents: 0,
+ interior: 'Here',
+};
diff --git a/src/utils/crossChain/transfer.tsx b/src/utils/crossChain/transfer.tsx
index d402be3c..670e9613 100644
--- a/src/utils/crossChain/transfer.tsx
+++ b/src/utils/crossChain/transfer.tsx
@@ -5,11 +5,20 @@ import { Sender, TxStatusHandlers } from '@/models';
import {
CoretimeChain,
+ CoretimeChainFromRelayPerspective,
CoretimeRegionFromCoretimePerspective,
CoretimeRegionFromRegionXPerspective,
+ RcTokenFromParachainPerspective,
+ RcTokenFromRelayPerspective,
RegionXChain,
+ RelayChainFromParachainPerspective,
} from './consts';
-import { versionedNonfungibleAssetWrap, versionedWrap } from './utils';
+import {
+ versionWrap,
+ versionWrappeddFungibleAsset,
+ versionWrappeddNonfungibleAsset,
+} from './utils';
+import { sendTx } from '../functions';
export async function coretimeToRegionXTransfer(
coretimeApi: ApiPromise,
@@ -35,9 +44,9 @@ export async function coretimeToRegionXTransfer(
const reserveTransfer =
coretimeApi.tx.polkadotXcm.limitedReserveTransferAssets(
- versionedWrap(RegionXChain),
- versionedWrap(beneficiary),
- versionedNonfungibleAssetWrap(
+ versionWrap(RegionXChain),
+ versionWrap(beneficiary),
+ versionWrappeddNonfungibleAsset(
CoretimeRegionFromCoretimePerspective,
rawRegionId.toString()
),
@@ -45,28 +54,8 @@ export async function coretimeToRegionXTransfer(
weightLimit
);
- try {
- reserveTransfer.signAndSend(
- sender.address,
- { signer: sender.signer },
- ({ status, events }) => {
- if (status.isReady) handlers.ready();
- else if (status.isInBlock) handlers.inBlock();
- else if (status.isFinalized) {
- handlers.finalized();
- events.forEach(({ event: { method } }) => {
- if (method === 'ExtrinsicSuccess') {
- handlers.success();
- } else if (method === 'ExtrinsicFailed') {
- handlers.error();
- }
- });
- }
- }
- );
- } catch (e) {
- handlers.error();
- }
+ const { address, signer } = sender;
+ sendTx(reserveTransfer, address, signer, handlers);
}
export function regionXToCoretimeTransfer(
@@ -92,9 +81,9 @@ export function regionXToCoretimeTransfer(
const weightLimit = 'Unlimited';
const reserveTransfer = api.tx.polkadotXcm.limitedReserveTransferAssets(
- versionedWrap(CoretimeChain),
- versionedWrap(beneficiary),
- versionedNonfungibleAssetWrap(
+ versionWrap(CoretimeChain),
+ versionWrap(beneficiary),
+ versionWrappeddNonfungibleAsset(
CoretimeRegionFromRegionXPerspective,
rawRegionId.toString()
),
@@ -102,26 +91,76 @@ export function regionXToCoretimeTransfer(
weightLimit
);
- try {
- reserveTransfer.signAndSend(
- sender.address,
- { signer: sender.signer },
- ({ status, events }) => {
- if (status.isReady) handlers.ready();
- else if (status.isInBlock) handlers.inBlock();
- else if (status.isFinalized) {
- handlers.finalized();
- events.forEach(({ event: { method } }) => {
- if (method === 'ExtrinsicSuccess') {
- handlers.success();
- } else if (method === 'ExtrinsicFailed') {
- handlers.error();
- }
- });
- }
- }
- );
- } catch (e) {
- handlers.error();
- }
+ const { address, signer } = sender;
+ sendTx(reserveTransfer, address, signer, handlers);
+}
+
+export function transferTokensFromCoretimeToRelay(
+ coretimeApi: ApiPromise,
+ sender: Sender,
+ amount: string,
+ receiver: Uint8Array,
+ handlers: TxStatusHandlers
+) {
+ const beneficiary = {
+ parents: 0,
+ interior: {
+ X1: {
+ AccountId32: {
+ chain: 'Any',
+ id: receiver,
+ },
+ },
+ },
+ };
+
+ const feeAssetItem = 0;
+ const weightLimit = 'Unlimited';
+
+ const teleportTransfer = coretimeApi.tx.polkadotXcm.limitedTeleportAssets(
+ versionWrap(RelayChainFromParachainPerspective),
+ versionWrap(beneficiary),
+ versionWrappeddFungibleAsset(RcTokenFromParachainPerspective, amount),
+ feeAssetItem,
+ weightLimit
+ );
+
+ const { address, signer } = sender;
+
+ sendTx(teleportTransfer, address, signer, handlers);
+}
+
+export function transferTokensFromRelayToCoretime(
+ coretimeApi: ApiPromise,
+ sender: Sender,
+ amount: string,
+ receiver: Uint8Array,
+ handlers: TxStatusHandlers
+) {
+ const beneficiary = {
+ parents: 0,
+ interior: {
+ X1: {
+ AccountId32: {
+ chain: 'Any',
+ id: receiver,
+ },
+ },
+ },
+ };
+
+ const feeAssetItem = 0;
+ const weightLimit = 'Unlimited';
+
+ const teleportTransfer = coretimeApi.tx.xcmPallet.limitedTeleportAssets(
+ versionWrap(CoretimeChainFromRelayPerspective),
+ versionWrap(beneficiary),
+ versionWrappeddFungibleAsset(RcTokenFromRelayPerspective, amount),
+ feeAssetItem,
+ weightLimit
+ );
+
+ const { address, signer } = sender;
+
+ sendTx(teleportTransfer, address, signer, handlers);
}
diff --git a/src/utils/crossChain/utils.tsx b/src/utils/crossChain/utils.tsx
index e8dd2099..dbc76f28 100644
--- a/src/utils/crossChain/utils.tsx
+++ b/src/utils/crossChain/utils.tsx
@@ -1,16 +1,16 @@
import { SAFE_XCM_VERSION } from '@/models';
-export const versionedWrap = (xcm: any) => {
+export const versionWrap = (xcm: any) => {
return {
[`V${SAFE_XCM_VERSION}`]: xcm,
};
};
-export const versionedNonfungibleAssetWrap = (
+export const versionWrappeddNonfungibleAsset = (
assetLocation: any,
index: string
) => {
- return versionedWrap([
+ return versionWrap([
{
id: {
Concrete: assetLocation,
@@ -23,3 +23,19 @@ export const versionedNonfungibleAssetWrap = (
},
]);
};
+
+export const versionWrappeddFungibleAsset = (
+ assetLocation: any,
+ amount: string
+) => {
+ return versionWrap([
+ {
+ id: {
+ Concrete: assetLocation,
+ },
+ fun: {
+ Fungible: amount,
+ },
+ },
+ ]);
+};
diff --git a/src/utils/functions.ts b/src/utils/functions.ts
index 86cff9d8..63f0167d 100644
--- a/src/utils/functions.ts
+++ b/src/utils/functions.ts
@@ -1,9 +1,15 @@
import { ApiPromise } from '@polkadot/api';
+import { AddressOrPair, SubmittableExtrinsic } from '@polkadot/api/types';
+import { ISubmittableResult, Signer } from '@polkadot/types/types';
import { formatBalance as polkadotFormatBalance } from '@polkadot/util';
import { CoreMask, RegionId } from 'coretime-utils';
import Decimal from 'decimal.js';
-import { CORETIME_DECIMALS, REGIONX_DECIMALS } from '@/models';
+import {
+ CORETIME_DECIMALS,
+ REGIONX_DECIMALS,
+ TxStatusHandlers,
+} from '@/models';
// parse human readable number string
export const parseHNString = (str: string): number => {
@@ -110,6 +116,45 @@ export const extractRegionIdFromRaw = (rawRegionId: bigint): RegionId => {
};
};
+export const fetchBalance = async (
+ api: ApiPromise,
+ address: string
+): Promise => {
+ const coretimeAccount = (
+ await api.query.system.account(address)
+ ).toHuman() as any;
+
+ return parseHNString(coretimeAccount.data.free.toString());
+};
+
+export const sendTx = (
+ tx: SubmittableExtrinsic<'promise', ISubmittableResult>,
+ account: AddressOrPair,
+ signer: Signer,
+ handlers: TxStatusHandlers
+) => {
+ try {
+ tx.signAndSend(account, { signer }, ({ status, events }) => {
+ if (status.isReady) handlers.ready();
+ else if (status.isInBlock) handlers.inBlock();
+ else if (status.isFinalized) {
+ handlers.finalized();
+ events.forEach(({ event: { method } }) => {
+ if (method === 'ExtrinsicSuccess') {
+ handlers.success();
+ } else if (method === 'ExtrinsicFailed') {
+ handlers.error();
+ }
+ });
+ }
+ });
+ } catch {
+ handlers.error();
+ } finally {
+ handlers.finally && handlers.finally();
+ }
+};
+
export const getBlockTime = (network: any): number => {
// Coretime on Rococo has async backing and due to this it has a block time of 6 seconds.
const blockTime = !network || network == 'rococo' ? 6000 : 12000;
diff --git a/src/utils/native/transfer.tsx b/src/utils/native/transfer.tsx
index 852cc3e3..8bdf6b33 100644
--- a/src/utils/native/transfer.tsx
+++ b/src/utils/native/transfer.tsx
@@ -4,6 +4,20 @@ import { Region } from 'coretime-utils';
import { TxStatusHandlers } from '@/models';
+import { sendTx } from '../functions';
+
+export const transferNativeToken = async (
+ api: ApiPromise,
+ signer: Signer,
+ senderAddress: string,
+ destination: string,
+ amount: string,
+ handlers: TxStatusHandlers
+) => {
+ const txTransfer = api.tx.balances.transferKeepAlive(destination, amount);
+ sendTx(txTransfer, senderAddress, signer, handlers);
+};
+
export const transferRegionOnCoretimeChain = async (
coretimeApi: ApiPromise,
region: Region,
@@ -16,27 +30,5 @@ export const transferRegionOnCoretimeChain = async (
region.getOnChainRegionId(),
newOwner
);
-
- try {
- await txTransfer.signAndSend(
- senderAddress,
- { signer },
- ({ status, events }) => {
- if (status.isReady) handlers.ready();
- else if (status.isInBlock) handlers.inBlock();
- else if (status.isFinalized) {
- handlers.finalized();
- events.forEach(({ event: { method } }) => {
- if (method === 'ExtrinsicSuccess') {
- handlers.success();
- } else if (method === 'ExtrinsicFailed') {
- handlers.error();
- }
- });
- }
- }
- );
- } catch (e) {
- handlers.error();
- }
+ sendTx(txTransfer, senderAddress, signer, handlers);
};