-
-
-
-
-
-
-
-
- {{ name }}
-
-
-
-
-
-
- {{ t('changeAccountText') }}
-
-
- {{ t('disconnectWalletText') }}
-
-
+
+
+
+
+
+ {{ name }}
+
+
-
-
- {{ t('connectWalletText') }}
-
diff --git a/src/components/pages/Bridge/TransferNotification.vue b/src/components/pages/Bridge/TransferNotification.vue
index eb9c16a24..8b93f1c66 100644
--- a/src/components/pages/Bridge/TransferNotification.vue
+++ b/src/components/pages/Bridge/TransferNotification.vue
@@ -26,13 +26,10 @@ import { Component, Mixins } from 'vue-property-decorator';
import BridgeTransactionMixin from '@/components/mixins/BridgeTransactionMixin';
import { getter, state, mutation } from '@/store/decorators';
import { isOutgoingTransaction } from '@/utils/bridge/common/utils';
-import { subBridgeApi } from '@/utils/bridge/sub/api';
-import type { SubNetworksConnector } from '@/utils/bridge/sub/classes/adapter';
import ethersUtil from '@/utils/ethers-util';
import type { IBridgeTransaction } from '@sora-substrate/util';
import type { Whitelist, RegisteredAccountAsset } from '@sora-substrate/util/build/assets/types';
-import type { SubNetwork } from '@sora-substrate/util/build/bridgeProxy/sub/types';
import type { BridgeNetworkId } from '@sora-substrate/util/build/bridgeProxy/types';
@Component({
@@ -45,7 +42,6 @@ import type { BridgeNetworkId } from '@sora-substrate/util/build/bridgeProxy/typ
})
export default class BridgeTransferNotification extends Mixins(BridgeTransactionMixin) {
@state.bridge.notificationData private notificationData!: Nullable
;
- @state.bridge.subBridgeConnector private subBridgeConnector!: SubNetworksConnector;
@getter.wallet.account.whitelist private whitelist!: Whitelist;
@getter.assets.assetDataByAddress private getAsset!: (addr?: string) => Nullable;
@@ -77,22 +73,13 @@ export default class BridgeTransferNotification extends Mixins(BridgeTransaction
return this.asset?.symbol ?? '';
}
- get isSubEvm(): boolean {
- return subBridgeApi.isEvmAccount(this.tx?.externalNetwork as SubNetwork);
- }
-
- get isEvmNetwork(): boolean {
- if (!this.tx?.externalNetworkType) return false;
- if (this.tx.externalNetworkType === BridgeNetworkType.Sub && !this.isSubEvm) return false;
- return true;
- }
-
get addTokenBtnVisibility(): boolean {
- if (!this.isEvmNetwork) return false;
-
- const address = this.asset?.externalAddress;
+ if (!this.tx?.externalNetworkType) return false;
+ if (this.tx.externalNetworkType === BridgeNetworkType.Sub) return false;
- return !!address && !ethersUtil.isNativeEvmTokenAddress(address) && isOutgoingTransaction(this.tx);
+ return (
+ !!this.asset && !ethersUtil.isNativeEvmTokenAddress(this.asset.externalAddress) && isOutgoingTransaction(this.tx)
+ );
}
get txLink() {
@@ -131,30 +118,11 @@ export default class BridgeTransferNotification extends Mixins(BridgeTransaction
async addToken(): Promise {
if (!this.asset) return;
- try {
- const { externalAddress, externalDecimals, symbol, address } = this.asset;
- const image = this.whitelist[address]?.icon;
-
- let tokenAddress = externalAddress;
- let tokenSymbol = symbol;
- let tokenDecimals = +externalDecimals;
-
- if (this.isSubEvm) {
- const adapter = this.subBridgeConnector.parachain;
- if (!adapter) throw new Error('Adapter not found');
- const assetMeta = adapter.getAssetMeta(this.asset);
- if (!assetMeta) throw new Error('Asset metadata not found');
- tokenAddress = adapter.assetIdToEvmContractAddress(externalAddress);
- tokenSymbol = assetMeta.symbol;
- tokenDecimals = assetMeta.decimals;
- }
-
- await ethersUtil.addToken(tokenAddress, tokenSymbol, tokenDecimals, image);
- } catch (error) {
- console.error(error);
- } finally {
- this.visibility = false;
- }
+ const { externalAddress, externalDecimals, symbol, address } = this.asset;
+ const image = this.whitelist[address]?.icon;
+ await ethersUtil.addToken(externalAddress, symbol, +externalDecimals, image);
+
+ this.visibility = false;
}
}
diff --git a/src/components/shared/Dialog/SelectSoraAccount.vue b/src/components/shared/Dialog/SelectSoraAccount.vue
index bea8dffd7..095e52f18 100644
--- a/src/components/shared/Dialog/SelectSoraAccount.vue
+++ b/src/components/shared/Dialog/SelectSoraAccount.vue
@@ -33,7 +33,7 @@ export default class SelectSoraAccountDialog extends Mixins(TranslationMixin) {
@getter.wallet.account.account public soraAccount!: Nullable;
@action.wallet.account.loginAccount public loginAccount!: (account: WALLET_TYPES.PolkadotJsAccount) => Promise;
- @action.wallet.account.logout public logout!: () => Promise;
+ @action.wallet.account.logout public logout!: (forgetAddress?: string) => Promise;
@action.wallet.account.renameAccount public rename!: (data: { address: string; name: string }) => Promise;
get chainApi() {
diff --git a/src/consts/sub.ts b/src/consts/sub.ts
index 8f7591fa3..d2d05bd7f 100644
--- a/src/consts/sub.ts
+++ b/src/consts/sub.ts
@@ -98,31 +98,6 @@ export const SUB_NETWORKS: Partial> = {
},
],
},
- [SubNetworkId.PolkadotMoonbeam]: {
- id: SubNetworkId.PolkadotMoonbeam,
- name: 'Moonbeam',
- nativeCurrency: {
- name: 'GLMR',
- symbol: 'GLMR',
- decimals: 18,
- },
- endpointUrls: ['https://rpc.api.moonbeam.network', 'https://moonbeam-rpc.dwellir.com'],
- blockExplorerUrls: ['https://moonbeam.subscan.io'],
- shortName: 'Moonbeam',
- nodes: [
- {
- chain: 'Moonbeam',
- name: 'Moonbeam Foundation',
- address: 'wss://wss.api.moonbeam.network',
- },
- {
- chain: 'Moonbeam',
- name: 'Dwellir',
- address: 'wss://moonbeam-rpc.dwellir.com',
- },
- ],
- evmId: 1284,
- },
[SubNetworkId.Rococo]: {
id: SubNetworkId.Rococo,
name: 'Rococo',
@@ -155,7 +130,7 @@ export const SUB_NETWORKS: Partial> = {
{
chain: 'Moonbase Relay Testnet',
name: 'Parity',
- address: 'wss://fro-moon-rpc-1-moonbase-relay-rpc-1.moonbase.ol-infra.network',
+ address: 'wss://frag-moonbase-relay-rpc-ws.g.moonbase.moonbeam.network',
},
],
},
@@ -167,9 +142,8 @@ export const SUB_NETWORKS: Partial> = {
symbol: 'GLMR', // "DEV"
decimals: 18,
},
- endpointUrls: ['https://rpc.api.moonbase.moonbeam.network', 'https://moonbase-rpc.dwellir.com'],
- blockExplorerUrls: ['https://moonbase.subscan.io'],
- shortName: 'Moonbase',
+ blockExplorerUrls: [],
+ shortName: 'Alpha',
nodes: [
{
chain: 'Moonbase Alpha',
@@ -177,7 +151,6 @@ export const SUB_NETWORKS: Partial> = {
address: 'wss://wss.api.moonbase.moonbeam.network',
},
],
- evmId: 1287,
},
// SORA Parachains
[SubNetworkId.RococoSora]: {
@@ -327,24 +300,10 @@ export const SUB_TRANSFER_FEES: SubNetworksFees = {
[BridgeTxDirection.Incoming]: '0',
},
},
- [SubNetworkId.Alphanet]: {
- ALPHA: {
- [BridgeTxDirection.Outgoing]: '2700000000',
- [BridgeTxDirection.Incoming]: '0',
- },
- },
[SubNetworkId.AlphanetMoonbase]: {
- GLMR: {
+ ACA: {
[BridgeTxDirection.Outgoing]: '34313700000000',
[BridgeTxDirection.Incoming]: '0',
},
- ALPHA: {
- [BridgeTxDirection.Outgoing]: '44415350668',
- [BridgeTxDirection.Incoming]: '46453162841',
- },
- XOR: {
- [BridgeTxDirection.Outgoing]: '8140448382622083802',
- [BridgeTxDirection.Incoming]: '0',
- },
},
};
diff --git a/src/store/assets/actions.ts b/src/store/assets/actions.ts
index b0f6f2856..c0c378edd 100644
--- a/src/store/assets/actions.ts
+++ b/src/store/assets/actions.ts
@@ -91,6 +91,31 @@ async function getSubRegisteredAssets(
if (!subNetwork) return [];
const subNetworkId = subNetwork as SubNetwork;
+
+ // [TODO] remove when non native parachain tokens are supported
+ if (subNetworkId === SubNetworkId.PolkadotAcala) {
+ return [
+ {
+ '0x001ddbe1a880031da72f7ea421260bec635fa7d1aa72593d5412795408b6b2ba': {
+ address: '',
+ decimals: 12,
+ kind: 'Sidechain',
+ },
+ },
+ ];
+ }
+ if (subNetworkId === SubNetworkId.PolkadotAstar) {
+ return [
+ {
+ '0x009dd037fcb32f4fe17c513abd4641a2ece844d106e30788124f0c0acc6e748e': {
+ address: '',
+ decimals: 18,
+ kind: 'Sidechain',
+ },
+ },
+ ];
+ }
+
const networkAssets = await subBridgeApi.getRegisteredAssets(subNetworkId);
const registeredAssets = Object.entries(networkAssets).map(([soraAddress, assetData]) => {
return {
@@ -105,36 +130,6 @@ async function getSubRegisteredAssets(
return registeredAssets;
}
-async function updateSubAssetsData(context: ActionContext): Promise {
- const { state, commit, rootState, rootGetters } = assetsActionContext(context);
- const { registeredAssets } = state;
-
- const { destinationNetwork, soraParachain, parachain } = rootState.bridge.subBridgeConnector;
-
- if (!subBridgeApi.isParachain(destinationNetwork)) return;
-
- if (!(soraParachain && parachain)) return;
-
- await Promise.all([soraParachain.connect(), parachain.connect()]);
-
- const updatedEntries = await Promise.all(
- Object.entries(registeredAssets).map(async ([soraAddress, assetData]) => {
- const asset = { ...assetData };
- const soraAsset = rootGetters.wallet.account.assetsDataTable[soraAddress];
- if (!asset.address && soraAsset) {
- const multilocation = await subBridgeApi.soraParachainApi.getAssetMulilocation(soraAddress, soraParachain.api);
- const id = await parachain.getAssetIdByMultilocation(soraAsset, multilocation);
- asset.address = id;
- }
- return [soraAddress, asset];
- })
- );
-
- const assets = Object.fromEntries(updatedEntries);
-
- commit.setRegisteredAssets(assets);
-}
-
async function getRegisteredAssets(context: ActionContext): Promise[]> {
const { rootState } = assetsActionContext(context);
@@ -178,16 +173,14 @@ const actions = defineActions({
async updateRegisteredAssets(context): Promise {
const { commit, rootState } = assetsActionContext(context);
+ // only for ETH bridge, because of sora assets broken registration
+ if (rootState.web3.networkType === BridgeNetworkType.Eth) {
+ commit.setRegisteredAssetsFetching(true);
- commit.setRegisteredAssetsFetching(true);
-
- if (rootState.web3.networkType === BridgeNetworkType.Sub) {
- await updateSubAssetsData(context);
- } else {
await updateEthAssetsData(context);
- }
- commit.setRegisteredAssetsFetching(false);
+ commit.setRegisteredAssetsFetching(false);
+ }
},
});
diff --git a/src/store/bridge/actions.ts b/src/store/bridge/actions.ts
index bec895a54..67d517d76 100644
--- a/src/store/bridge/actions.ts
+++ b/src/store/bridge/actions.ts
@@ -84,26 +84,6 @@ function getBridgeApi(context: ActionContext) {
return ethBridgeApi;
}
-async function switchAmounts(context: ActionContext): Promise {
- const { state, dispatch } = bridgeActionContext(context);
-
- if (state.focusedField === FocusedField.Received) {
- await dispatch.setSendedAmount(state.amountReceived);
- } else {
- await dispatch.setReceivedAmount(state.amountSend);
- }
-}
-
-async function updateAmounts(context: ActionContext): Promise {
- const { state, dispatch } = bridgeActionContext(context);
-
- if (state.focusedField === FocusedField.Received) {
- await dispatch.setReceivedAmount(state.amountReceived);
- } else {
- await dispatch.setSendedAmount(state.amountSend);
- }
-}
-
function checkEvmNetwork(context: ActionContext): void {
const { rootGetters } = bridgeActionContext(context);
if (!rootGetters.web3.isValidNetwork) {
@@ -414,7 +394,7 @@ async function updateSoraNetworkFee(context: ActionContext): Promise): Promise {
+async function updateBalancesAndFees(context: ActionContext): Promise {
const { dispatch } = bridgeActionContext(context);
await Promise.allSettled([
@@ -468,14 +448,19 @@ const actions = defineActions({
},
async switchDirection(context): Promise {
- const { commit, state } = bridgeActionContext(context);
+ const { commit, dispatch, state } = bridgeActionContext(context);
commit.setSoraToEvm(!state.isSoraToEvm);
commit.setAssetSenderBalance();
commit.setAssetRecipientBalance();
- await updateBalancesFeesAndAmounts(context);
- await switchAmounts(context);
+ await updateBalancesAndFees(context);
+
+ if (state.focusedField === FocusedField.Received) {
+ await dispatch.setSendedAmount(state.amountReceived);
+ } else {
+ await dispatch.setReceivedAmount(state.amountSend);
+ }
},
async setAssetAddress(context, address?: string): Promise {
@@ -489,9 +474,8 @@ const actions = defineActions({
dispatch.updateOutgoingMinLimit(),
dispatch.updateOutgoingMaxLimit(),
dispatch.updateIncomingMinLimit(),
- updateBalancesFeesAndAmounts(context),
+ updateBalancesAndFees(context),
]);
- await updateAmounts(context);
},
async updateExternalBalance(context): Promise {
@@ -583,7 +567,7 @@ const actions = defineActions({
const subscription = api.system.updated.subscribe(() => {
updateExternalBlockNumber(context);
- updateBalancesFeesAndAmounts(context);
+ updateBalancesAndFees(context);
});
commit.setBlockUpdatesSubscription(subscription);
diff --git a/src/store/bridge/getters.ts b/src/store/bridge/getters.ts
index 2da5b4384..3d8a24dbd 100644
--- a/src/store/bridge/getters.ts
+++ b/src/store/bridge/getters.ts
@@ -110,22 +110,11 @@ const getters = defineGetters()({
return assetIds[0];
},
- isSubAccountType(...args): boolean {
- const { rootState } = bridgeGetterContext(args);
- const { networkSelected, networkType } = rootState.web3;
-
- if (networkType === BridgeNetworkType.Sub) {
- return !subBridgeApi.isEvmAccount(networkSelected as SubNetwork);
- }
-
- return false;
- },
-
externalAccount(...args): string {
const { getters, rootState } = bridgeGetterContext(args);
const { evmAddress, subAddress } = rootState.web3;
- if (getters.isSubAccountType) {
+ if (getters.isSubBridge) {
return subAddress;
} else {
return evmAddress;
@@ -139,7 +128,7 @@ const getters = defineGetters()({
if (state.isSoraToEvm) return soraAddress;
- return getters.isSubAccountType ? chainAddress(subAddress, state.subBridgeConnector) : evmAddress;
+ return getters.isSubBridge ? chainAddress(subAddress, state.subBridgeConnector) : evmAddress;
},
senderName(...args): string {
@@ -149,7 +138,7 @@ const getters = defineGetters()({
if (state.isSoraToEvm) return soraName;
- return getters.isSubAccountType ? subAddressName : '';
+ return getters.isSubBridge ? subAddressName : '';
},
recipient(...args): string {
@@ -159,7 +148,7 @@ const getters = defineGetters()({
if (!state.isSoraToEvm) return soraAddress;
- return getters.isSubAccountType ? chainAddress(subAddress, state.subBridgeConnector) : evmAddress;
+ return getters.isSubBridge ? chainAddress(subAddress, state.subBridgeConnector) : evmAddress;
},
recipientName(...args): string {
@@ -169,7 +158,7 @@ const getters = defineGetters()({
if (!state.isSoraToEvm) return soraName;
- return getters.isSubAccountType ? subAddressName : '';
+ return getters.isSubBridge ? subAddressName : '';
},
isEthBridge(...args): boolean {
diff --git a/src/store/moonpay/actions.ts b/src/store/moonpay/actions.ts
index eded9490d..b23cd4384 100644
--- a/src/store/moonpay/actions.ts
+++ b/src/store/moonpay/actions.ts
@@ -84,7 +84,7 @@ const actions = defineActions({
};
} else {
// Parse ERC-20 transfer
- const abi = SmartContracts[SmartContractType.ERC20];
+ const abi = SmartContracts[SmartContractType.ERC20].abi;
const inter = new ethers.Interface(abi);
const decodedInput = inter.parseTransaction({ data: tx.data });
diff --git a/src/store/web3/actions.ts b/src/store/web3/actions.ts
index 9fa2abe67..77ba5f684 100644
--- a/src/store/web3/actions.ts
+++ b/src/store/web3/actions.ts
@@ -1,8 +1,9 @@
import { BridgeNetworkType } from '@sora-substrate/util/build/bridgeProxy/consts';
import { SubNetworkId } from '@sora-substrate/util/build/bridgeProxy/sub/consts';
import { BridgeNetworkId } from '@sora-substrate/util/build/bridgeProxy/types';
-import { api as soraApi, accountUtils, WALLET_TYPES, WALLET_CONSTS } from '@soramitsu/soraneo-wallet-web';
+import { accountUtils, WALLET_TYPES, WALLET_CONSTS } from '@soramitsu/soraneo-wallet-web';
import { defineActions } from 'direct-vuex';
+import { ethers } from 'ethers';
import { KnownEthBridgeAsset, SmartContracts, SmartContractType } from '@/consts/evm';
import { web3ActionContext } from '@/store/web3';
@@ -172,18 +173,21 @@ const actions = defineActions({
},
async changeEvmNetworkProvided(context): Promise {
- const { getters } = web3ActionContext(context);
+ const { getters, state } = web3ActionContext(context);
const { selectedNetwork } = getters;
+ const { networkType } = state;
- if (!selectedNetwork) return;
-
- await ethersUtil.switchOrAddChain(selectedNetwork);
+ if (selectedNetwork && networkType !== BridgeNetworkType.Sub) {
+ await ethersUtil.switchOrAddChain(selectedNetwork);
+ }
},
async getSupportedApps(context): Promise {
const { commit, getters } = web3ActionContext(context);
- // production mock
- let supportedApps = {
+ // [TODO] uncomment
+ // const supportedApps = await api.bridgeProxy.getListApps();
+ // [TODO] remove this production mock after nodes update
+ const supportedApps = {
[BridgeNetworkType.Eth]: {},
[BridgeNetworkType.Evm]: {},
[BridgeNetworkType.Sub]: [
@@ -197,12 +201,6 @@ const actions = defineActions({
],
};
- try {
- supportedApps = await soraApi.bridgeProxy.getListApps();
- } catch (error) {
- console.error(error);
- }
-
commit.setSupportedApps(supportedApps as any);
const networks = getters.availableNetworks[BridgeNetworkType.Sub];
@@ -249,12 +247,13 @@ const actions = defineActions({
if (!soraAssetId) {
return '';
}
- const contractAbi = SmartContracts[SmartContractType.EthBridge][KnownEthBridgeAsset.Other];
+ const contractAbi = SmartContracts[SmartContractType.EthBridge][KnownEthBridgeAsset.Other].abi;
const contractAddress = getters.contractAddress(KnownEthBridgeAsset.Other);
if (!contractAddress || !contractAbi) {
throw new Error('Contract address/abi is not found');
}
- const contractInstance = await ethersUtil.getContract(contractAddress, contractAbi);
+ const signer = await ethersUtil.getSigner();
+ const contractInstance = new ethers.Contract(contractAddress, contractAbi, signer);
const methodArgs = [soraAssetId];
const externalAddress = await contractInstance._sidechainTokens(...methodArgs);
// Not (wrong) registered Sora asset on bridge contract return '0' address (like native token)
diff --git a/src/store/web3/getters.ts b/src/store/web3/getters.ts
index 10533d3db..7e3c675c8 100644
--- a/src/store/web3/getters.ts
+++ b/src/store/web3/getters.ts
@@ -14,7 +14,7 @@ import type { BridgeNetworkId } from '@sora-substrate/util/build/bridgeProxy/typ
const getters = defineGetters()({
availableNetworks(...args): Record>> {
- const { state } = web3GetterContext(args);
+ const { state, rootState } = web3GetterContext(args);
const hashi = [state.ethBridgeEvmNetwork].reduce((buffer, id) => {
const data = EVM_NETWORKS[id];
@@ -79,21 +79,11 @@ const getters = defineGetters()({
},
isValidNetwork(...args): boolean {
- const { state, getters } = web3GetterContext(args);
- const { evmProviderNetwork } = state;
- const { selectedNetwork } = getters;
-
- if (!selectedNetwork) return false;
+ const { state } = web3GetterContext(args);
- if (state.networkType === BridgeNetworkType.Sub) {
- if (selectedNetwork.evmId) {
- return evmProviderNetwork === selectedNetwork.evmId;
- } else {
- return true;
- }
- }
+ if (state.networkType === BridgeNetworkType.Sub) return true;
- return evmProviderNetwork === selectedNetwork.id;
+ return state.evmProviderNetwork === state.networkSelected;
},
contractAddress(...args): (asset: KnownEthBridgeAsset) => Nullable {
diff --git a/src/styles/common.scss b/src/styles/common.scss
index 380a49e41..396a81201 100644
--- a/src/styles/common.scss
+++ b/src/styles/common.scss
@@ -22,7 +22,7 @@ $country-emoji-font: 'Twemoji Country Flags';
}
}
-$networks: 'sora', 'ethereum', 'ethereum-classic', 'avalanche', 'klaytn', 'polygon', 'binance-smart-chain', 'rococo', 'polkadot', 'kusama', 'acala', 'astar', 'shiden', 'liberland', 'alphanet', 'moonbase', 'moonbeam', 'sora-polkadot', 'sora-kusama';
+$networks: 'sora', 'ethereum', 'ethereum-classic', 'avalanche', 'klaytn', 'polygon', 'binance-smart-chain', 'rococo', 'polkadot', 'kusama', 'acala', 'astar', 'shiden', 'liberland', 'moonbase', 'sora-polkadot', 'sora-kusama';
/* Networks Icons */
.network-icon {
display: inline-block;
@@ -41,17 +41,15 @@ $networks: 'sora', 'ethereum', 'ethereum-classic', 'avalanche', 'klaytn', 'polyg
}
}
- @each $network in 'rococo', 'alphanet' {
- &--sora-#{$network} {
- background-image: url('~@/assets/img/networks/#{$network}.svg');
- &::after {
- content: '';
- display: block;
- width: 50%;
- height: 50%;
- filter: drop-shadow(2px 4px 6px black);
- background-image: url('~@/assets/img/networks/sora.svg');
- }
+ &--sora-rococo {
+ background-image: url('~@/assets/img/networks/rococo.svg');
+ &::after {
+ content: '';
+ display: block;
+ width: 50%;
+ height: 50%;
+ filter: drop-shadow(2px 4px 6px black);
+ background-image: url('~@/assets/img/networks/sora.svg');
}
}
}
diff --git a/src/types/bridge.ts b/src/types/bridge.ts
index f0e306b5d..1227f5467 100644
--- a/src/types/bridge.ts
+++ b/src/types/bridge.ts
@@ -19,8 +19,6 @@ export interface NetworkData {
endpointUrls?: string[];
/** Nodes for Substrate network */
nodes?: Node[];
- /** Evm chain id for substrate network */
- evmId?: number;
}
export type SubNetworksFees = Partial>>>;
diff --git a/src/utils/bridge/common/classes.ts b/src/utils/bridge/common/classes.ts
index 9ebef9dd5..12d6a6f7f 100644
--- a/src/utils/bridge/common/classes.ts
+++ b/src/utils/bridge/common/classes.ts
@@ -18,7 +18,6 @@ import type {
IBridgeConstructorOptions,
TransactionHandlerPayload,
} from '@/utils/bridge/common/types';
-import { isUnsignedTx } from '@/utils/bridge/common/utils';
import type { IBridgeTransaction } from '@sora-substrate/util';
@@ -181,27 +180,25 @@ export class BridgeReducer implements IB
}
private async checkTransactionBlockId(id: string): Promise {
- const { blockId, externalBlockId } = this.getTransaction(id);
+ const { blockId } = this.getTransaction(id);
- if (blockId || externalBlockId) return;
+ if (blockId) return;
await delay(1_000);
await this.checkTransactionBlockId(id);
}
async waitForTransactionBlockId(id: string): Promise {
- const tx = this.getTransaction(id);
+ const { txId } = this.getTransaction(id);
- if (isUnsignedTx(tx)) {
- throw new Error(
- `[${this.constructor.name}]: Transaction "id" or "externalHash" is empty, first sign the transaction`
- );
+ if (!txId) {
+ throw new Error(`[${this.constructor.name}]: Transaction "id" is empty, first sign the transaction`);
}
try {
await Promise.race([
this.checkTransactionBlockId(id),
- delay(BLOCK_PRODUCE_TIME * 10, false), // 60s
+ delay(BLOCK_PRODUCE_TIME * 6, false), // 36s
]);
} catch (error) {
console.info(`[${this.constructor.name}]: Implement "blockId" restoration by "txId"`);
diff --git a/src/utils/bridge/common/utils.ts b/src/utils/bridge/common/utils.ts
index 3cb8cd989..37c9737a9 100644
--- a/src/utils/bridge/common/utils.ts
+++ b/src/utils/bridge/common/utils.ts
@@ -1,11 +1,10 @@
-import { isEthOperation, isEvmOperation, isSubstrateOperation } from '@sora-substrate/util';
+import { Operation, isEthOperation, isEvmOperation, isSubstrateOperation } from '@sora-substrate/util';
import { api as soraApi } from '@soramitsu/soraneo-wallet-web';
import { ethers } from 'ethers';
-import type { GetTransaction, UpdateTransaction } from '@/utils/bridge/common/types';
-import { isUnsignedTx as isUnsignedEthTx, isOutgoingTx as isOutgoingEthTx } from '@/utils/bridge/eth/utils';
-import { isUnsignedTx as isUnsignedEvmTx, isOutgoingTx as isOutgoingEvmTx } from '@/utils/bridge/evm/utils';
-import { isUnsignedTx as isUnsignedSubTx, isOutgoingTx as isOutgoingSubTx } from '@/utils/bridge/sub/utils';
+import { isUnsignedTx as isUnsignedEthTx } from '@/utils/bridge/eth/utils';
+import { isUnsignedTx as isUnsignedEvmTx } from '@/utils/bridge/evm/utils';
+import { isUnsignedTx as isUnsignedSubTx } from '@/utils/bridge/sub/utils';
import ethersUtil from '@/utils/ethers-util';
import type { ApiPromise } from '@polkadot/api';
@@ -14,13 +13,6 @@ import type { EthHistory } from '@sora-substrate/util/build/bridgeProxy/eth/type
import type { EvmHistory } from '@sora-substrate/util/build/bridgeProxy/evm/types';
import type { SubHistory } from '@sora-substrate/util/build/bridgeProxy/sub/types';
-export const getEvmTransactionFee = (tx: ethers.TransactionResponse | ethers.TransactionReceipt) => {
- const gasPrice = tx.gasPrice;
- const gasAmount = 'gasUsed' in tx ? tx.gasUsed : tx.gasLimit;
-
- return ethersUtil.calcEvmFee(gasPrice, gasAmount);
-};
-
export const waitForEvmTransactionMined = async (
tx: ethers.TransactionResponse | null,
replaceCallback?: (tx: ethers.TransactionResponse | null) => void
@@ -85,11 +77,7 @@ export const getTransactionEvents = async (blockHash: string, transactionHash: s
export const isOutgoingTransaction = (transaction: Nullable): boolean => {
if (!transaction?.type) return false;
- if (isEthOperation(transaction.type)) return isOutgoingEthTx(transaction as EthHistory);
- if (isEvmOperation(transaction.type)) return isOutgoingEvmTx(transaction as EvmHistory);
- if (isSubstrateOperation(transaction.type)) return isOutgoingSubTx(transaction as SubHistory);
-
- return false;
+ return [Operation.EthBridgeOutgoing, Operation.EvmOutgoing, Operation.SubstrateOutgoing].includes(transaction.type);
};
export const isUnsignedTx = (transaction: Nullable): boolean => {
@@ -101,40 +89,3 @@ export const isUnsignedTx = (transaction: Nullable): boolean
return true;
};
-
-export const onEvmTransactionPending = async (
- id: string,
- getTransaction: GetTransaction,
- updateTransaction: UpdateTransaction
-) => {
- const tx = getTransaction(id);
- const hash = tx.externalHash;
-
- if (!hash) throw new Error(`[onEvmTransactionPending] Evm transaction hash is empty`);
-
- const txResponse = await ethersUtil.getEvmTransaction(hash);
- const txReceipt = await waitForEvmTransactionMined(txResponse, (replacedTx) => {
- if (replacedTx) {
- updateTransaction(id, {
- externalHash: replacedTx.hash,
- externalNetworkFee: getEvmTransactionFee(replacedTx),
- });
- }
- });
-
- const { fee, blockNumber, blockHash } = txReceipt || {};
-
- if (!(fee && blockNumber && blockHash)) {
- updateTransaction(id, { externalHash: undefined, externalNetworkFee: undefined });
- throw new Error(
- `[onEvmTransactionPending]: Ethereum transaction not found, hash: ${tx.externalHash}. 'externalHash' is reset`
- );
- }
-
- // In EthHistory 'blockHeight' will store evm block number
- updateTransaction(id, {
- externalNetworkFee: fee.toString(),
- externalBlockHeight: blockNumber,
- externalBlockId: blockHash,
- });
-};
diff --git a/src/utils/bridge/eth/classes/history.ts b/src/utils/bridge/eth/classes/history.ts
index 0d4f20e0c..c122fa706 100644
--- a/src/utils/bridge/eth/classes/history.ts
+++ b/src/utils/bridge/eth/classes/history.ts
@@ -33,8 +33,8 @@ export default class EtherscanHistoryProvider extends EtherscanProvider {
}
const BRIDGE_INTERFACE = new ethers.Interface([
- ...SmartContracts[SmartContractType.EthBridge][KnownEthBridgeAsset.XOR], // XOR or VAL
- ...SmartContracts[SmartContractType.EthBridge][KnownEthBridgeAsset.Other], // Other
+ ...SmartContracts[SmartContractType.EthBridge][KnownEthBridgeAsset.XOR].abi, // XOR or VAL
+ ...SmartContracts[SmartContractType.EthBridge][KnownEthBridgeAsset.Other].abi, // Other
]);
const { ETH_BRIDGE_STATES } = WALLET_CONSTS;
diff --git a/src/utils/bridge/eth/classes/reducers.ts b/src/utils/bridge/eth/classes/reducers.ts
index ee7a2f40f..09c6c1e0f 100644
--- a/src/utils/bridge/eth/classes/reducers.ts
+++ b/src/utils/bridge/eth/classes/reducers.ts
@@ -3,10 +3,16 @@ import first from 'lodash/fp/first';
import { BridgeReducer } from '@/utils/bridge/common/classes';
import type { IBridgeReducerOptions, GetBridgeHistoryInstance, SignExternal } from '@/utils/bridge/common/types';
-import { getTransactionEvents, getEvmTransactionFee, onEvmTransactionPending } from '@/utils/bridge/common/utils';
+import { getTransactionEvents, waitForEvmTransactionMined } from '@/utils/bridge/common/utils';
import { ethBridgeApi } from '@/utils/bridge/eth/api';
import type { EthBridgeHistory } from '@/utils/bridge/eth/classes/history';
-import { getTransaction, waitForApprovedRequest, waitForIncomingRequest } from '@/utils/bridge/eth/utils';
+import {
+ getTransaction,
+ getTransactionFee,
+ waitForApprovedRequest,
+ waitForIncomingRequest,
+} from '@/utils/bridge/eth/utils';
+import ethersUtil from '@/utils/ethers-util';
import type { IBridgeTransaction } from '@sora-substrate/util';
import type { RegisteredAccountAsset } from '@sora-substrate/util/build/assets/types';
@@ -34,7 +40,36 @@ export class EthBridgeReducer extends BridgeReducer {
}
async onEvmPending(id: string): Promise {
- await onEvmTransactionPending(id, this.getTransaction, this.updateTransactionParams);
+ const tx = this.getTransaction(id);
+ const hash = tx.externalHash;
+
+ if (!hash) throw new Error(`[${this.constructor.name}]: Ethereum transaction hash is empty`);
+
+ const txResponse = await ethersUtil.getEvmTransaction(hash);
+ const txReceipt = await waitForEvmTransactionMined(txResponse, (replacedTx) => {
+ if (replacedTx) {
+ this.updateTransactionParams(id, {
+ externalHash: replacedTx.hash,
+ externalNetworkFee: getTransactionFee(replacedTx),
+ });
+ }
+ });
+
+ const { fee, blockNumber, blockHash } = txReceipt || {};
+
+ if (!(fee && blockNumber && blockHash)) {
+ this.updateTransactionParams(id, { externalHash: undefined, externalNetworkFee: undefined });
+ throw new Error(
+ `[${this.constructor.name}]: Ethereum transaction not found, hash: ${tx.externalHash}. 'externalHash' is reset`
+ );
+ }
+
+ // In EthHistory 'blockHeight' will store evm block number
+ this.updateTransactionParams(id, {
+ externalNetworkFee: fee.toString(),
+ externalBlockHeight: blockNumber,
+ externalBlockId: blockHash,
+ });
}
async onEvmSubmitted(id: string, signExternal: SignExternal): Promise {
@@ -48,7 +83,7 @@ export class EthBridgeReducer extends BridgeReducer {
// update after sign
this.updateTransactionParams(id, {
externalHash: signedTx.hash,
- externalNetworkFee: getEvmTransactionFee(signedTx),
+ externalNetworkFee: getTransactionFee(signedTx),
});
} catch (error: any) {
// maybe transaction already completed, try to restore ethereum transaction hash
diff --git a/src/utils/bridge/eth/utils.ts b/src/utils/bridge/eth/utils.ts
index c6f4d3945..cde815bb4 100644
--- a/src/utils/bridge/eth/utils.ts
+++ b/src/utils/bridge/eth/utils.ts
@@ -1,6 +1,7 @@
import { Operation, FPNumber } from '@sora-substrate/util';
import { BridgeTxStatus } from '@sora-substrate/util/build/bridgeProxy/consts';
import { EthCurrencyType, EthAssetKind } from '@sora-substrate/util/build/bridgeProxy/eth/consts';
+import { ethers } from 'ethers';
import { SmartContractType, KnownEthBridgeAsset, SmartContracts } from '@/consts/evm';
import { asZeroValue } from '@/utils';
@@ -19,15 +20,13 @@ type EthTxParams = {
request?: EthApprovedRequest;
};
-export const isOutgoingTx = (tx: EthHistory): boolean => {
- return tx.type === Operation.EthBridgeOutgoing;
-};
-
export const isUnsignedFromPart = (tx: EthHistory): boolean => {
- if (isOutgoingTx(tx)) {
+ if (tx.type === Operation.EthBridgeOutgoing) {
return !tx.blockId && !tx.txId;
- } else {
+ } else if (tx.type === Operation.EthBridgeIncoming) {
return !tx.externalHash;
+ } else {
+ return true;
}
};
@@ -125,12 +124,14 @@ export const waitForIncomingRequest = async (tx: EthHistory): Promise<{ hash: st
export async function getIncomingEvmTransactionData({ asset, value, recipient, getContractAddress }: EthTxParams) {
const isNativeEvmToken = ethersUtil.isNativeEvmTokenAddress(asset.externalAddress);
+ const signer = await ethersUtil.getSigner();
const accountId = ethersUtil.accountAddressToHex(recipient);
+
const amount = new FPNumber(value, asset.externalDecimals).toCodecString();
const contractAddress = getContractAddress(KnownEthBridgeAsset.Other) as string;
- const contractAbi = SmartContracts[SmartContractType.EthBridge][KnownEthBridgeAsset.Other];
- const contract = await ethersUtil.getContract(contractAddress, contractAbi);
+ const contractAbi = SmartContracts[SmartContractType.EthBridge][KnownEthBridgeAsset.Other].abi;
+ const contract = new ethers.Contract(contractAddress, contractAbi, signer);
const method = isNativeEvmToken ? 'sendEthToSidechain' : 'sendERC20ToSidechain';
const methodArgs = isNativeEvmToken
@@ -161,14 +162,14 @@ export async function getOutgoingEvmTransactionData({
}: EthTxParams) {
if (!request) throw new Error('request is required!');
+ const signer = await ethersUtil.getSigner();
const symbol = asset.symbol as KnownEthBridgeAsset;
const isValOrXor = [KnownEthBridgeAsset.XOR, KnownEthBridgeAsset.VAL].includes(symbol);
const bridgeAsset: KnownEthBridgeAsset = isValOrXor ? symbol : KnownEthBridgeAsset.Other;
-
const contractAddress = getContractAddress(bridgeAsset) as string;
- const contractAbi = SmartContracts[SmartContractType.EthBridge][bridgeAsset];
- const contract = await ethersUtil.getContract(contractAddress, contractAbi);
+ const contractAbi = SmartContracts[SmartContractType.EthBridge][bridgeAsset].abi;
+ const contract = new ethers.Contract(contractAddress, contractAbi, signer);
const amount = new FPNumber(value, asset.externalDecimals).toCodecString();
const isEthereumCurrency = request.currencyType === EthCurrencyType.TokenAddress;
@@ -291,3 +292,10 @@ export async function getEthNetworkFee(
return ethersUtil.calcEvmFee(gasPrice, gasLimitTotal);
}
+
+export const getTransactionFee = (tx: ethers.TransactionResponse | ethers.TransactionReceipt) => {
+ const gasPrice = tx.gasPrice;
+ const gasAmount = 'gasUsed' in tx ? tx.gasUsed : tx.gasLimit;
+
+ return ethersUtil.calcEvmFee(gasPrice, gasAmount);
+};
diff --git a/src/utils/bridge/evm/utils.ts b/src/utils/bridge/evm/utils.ts
index 5dca0845c..8640cd6ee 100644
--- a/src/utils/bridge/evm/utils.ts
+++ b/src/utils/bridge/evm/utils.ts
@@ -4,15 +4,13 @@ import { evmBridgeApi } from '@/utils/bridge/evm/api';
import type { EvmHistory } from '@sora-substrate/util/build/bridgeProxy/evm/types';
-export const isOutgoingTx = (tx: EvmHistory): boolean => {
- return tx.type === Operation.EvmOutgoing;
-};
-
export const isUnsignedTx = (tx: EvmHistory): boolean => {
- if (isOutgoingTx(tx)) {
+ if (tx.type === Operation.EvmOutgoing) {
return !tx.blockId && !tx.txId;
+ } else if (tx.type === Operation.EvmIncoming) {
+ return true;
} else {
- return !tx.externalHash;
+ return true;
}
};
diff --git a/src/utils/bridge/sub/classes/adapter.ts b/src/utils/bridge/sub/classes/adapter.ts
index 4880e970a..ca668c2b0 100644
--- a/src/utils/bridge/sub/classes/adapter.ts
+++ b/src/utils/bridge/sub/classes/adapter.ts
@@ -10,10 +10,8 @@ import { determineTransferType } from '@/utils/bridge/sub/utils';
import { AcalaParachainAdapter } from './adapters/parachain/acala';
import { AstarParachainAdapter } from './adapters/parachain/astar';
import { MoonbaseParachainAdapter } from './adapters/parachain/moonbase';
-import { ParachainAdapter } from './adapters/parachain/parachain';
import { SoraParachainAdapter } from './adapters/parachain/sora';
-import { AlphanetRelaychainAdapter } from './adapters/relaychain/alphanet';
-import { RelaychainAdapter } from './adapters/relaychain/relaychain';
+import { RelaychainAdapter } from './adapters/relaychain';
import { LiberlandAdapter } from './adapters/standalone/liberland';
import { SubAdapter } from './adapters/substrate';
@@ -29,8 +27,8 @@ type PathNetworks = {
export class SubNetworksConnector {
public soraParachain?: SoraParachainAdapter;
- public relaychain?: RelaychainAdapter;
- public parachain?: ParachainAdapter;
+ public relaychain?: SubAdapter;
+ public parachain?: SubAdapter;
public standalone?: SubAdapter;
public destinationNetwork!: SubNetwork;
@@ -97,9 +95,6 @@ export class SubNetworksConnector {
protected getAdapter(network: SubNetwork) {
if (subBridgeApi.isRelayChain(network)) {
- if (network === SubNetworkId.Alphanet) {
- return new AlphanetRelaychainAdapter(network);
- }
return new RelaychainAdapter(network);
}
if (subBridgeApi.isParachain(network)) {
@@ -115,7 +110,6 @@ export class SubNetworksConnector {
if (subBridgeApi.isSoraParachain(network)) {
return new SoraParachainAdapter(network);
}
- return new ParachainAdapter(network);
}
if (subBridgeApi.isStandalone(network)) {
if (network === SubNetworkId.Liberland) {
@@ -198,38 +192,27 @@ export class SubNetworksConnector {
await Promise.all(this.uniqueAdapters.map((c) => c.stop()));
}
- /**
- * Transfer funds from SORA to destination network
- */
- public async outgoingTransfer(asset: RegisteredAsset, recipient: string, amount: string | number, historyId: string) {
- await subBridgeApi.transfer(asset, recipient, amount, this.destinationNetwork, historyId);
- }
-
/**
* Transfer funds from destination network to SORA
*/
- public async incomingTransfer(asset: RegisteredAsset, recipient: string, amount: string | number, historyId: string) {
- if (subBridgeApi.isEvmAccount(this.destinationNetwork)) {
- await this.network.transfer(asset, recipient, amount, historyId);
- } else {
- const { api, accountPair, signer } = this.accountApi;
-
- if (!accountPair) throw new Error(`[${this.constructor.name}] Account pair is not set.`);
-
- const historyItem = subBridgeApi.getHistory(historyId as string) ?? {
- type: Operation.SubstrateIncoming,
- symbol: asset.symbol,
- assetAddress: asset.address,
- amount: `${amount}`,
- externalNetwork: this.destinationNetwork,
- externalNetworkType: BridgeNetworkType.Sub,
- from: subBridgeApi.address, // "from" is always SORA account address
- to: this.accountApi.address,
- };
-
- const extrinsic = this.network.getTransferExtrinsic(asset, recipient, amount);
- // submit extrinsic using SORA api, because current implementation using "subHistory" from SORA api scope
- await subBridgeApi.submitApiExtrinsic(api, extrinsic as any, accountPair, signer, historyItem);
- }
+ public async transfer(asset: RegisteredAsset, recipient: string, amount: string | number, historyId?: string) {
+ const { api, accountPair, signer } = this.accountApi;
+
+ if (!accountPair) throw new Error(`[${this.constructor.name}] Account pair is not set.`);
+
+ const historyItem = subBridgeApi.getHistory(historyId as string) ?? {
+ type: Operation.SubstrateIncoming,
+ symbol: asset.symbol,
+ assetAddress: asset.address,
+ amount: `${amount}`,
+ externalNetwork: this.destinationNetwork,
+ externalNetworkType: BridgeNetworkType.Sub,
+ from: subBridgeApi.address, // "from" is always SORA account address
+ to: this.accountApi.address,
+ };
+
+ const extrinsic = this.network.getTransferExtrinsic(asset, recipient, amount);
+ // submit extrinsic using SORA api, because current implementation using "subHistory" from SORA api scope
+ await subBridgeApi.submitApiExtrinsic(api, extrinsic as any, accountPair, signer, historyItem);
}
}
diff --git a/src/utils/bridge/sub/classes/adapters/parachain/acala.ts b/src/utils/bridge/sub/classes/adapters/parachain/acala.ts
index 5f8b7a97d..05ea461ea 100644
--- a/src/utils/bridge/sub/classes/adapters/parachain/acala.ts
+++ b/src/utils/bridge/sub/classes/adapters/parachain/acala.ts
@@ -3,10 +3,10 @@ import { formatBalance } from '@sora-substrate/util/build/assets';
import { ZeroStringValue } from '@/consts';
-import { ParachainAdapter, type IParachainAssetMetadata } from './parachain';
+import { SubAdapter } from '../substrate';
import type { CodecString } from '@sora-substrate/util';
-import type { Asset, RegisteredAsset } from '@sora-substrate/util/build/assets/types';
+import type { RegisteredAsset } from '@sora-substrate/util/build/assets/types';
enum AcalaPrimitivesCurrencyCurrencyId {
Token = 'Token',
@@ -29,6 +29,13 @@ type IAcalaCurrencyId =
[AcalaPrimitivesCurrencyCurrencyId.Erc20]: string;
};
+type IAcalaAssetMetadata = {
+ id: IAcalaCurrencyId;
+ symbol: string;
+ decimals: number;
+ minimalBalance: string;
+};
+
function getAcalaCurrencyId(nature: any): Nullable {
if (nature.isNativeAssetId) {
const value = nature.asNativeAssetId;
@@ -43,18 +50,13 @@ function getAcalaCurrencyId(nature: any): Nullable {
return null;
}
-export class AcalaParachainAdapter extends ParachainAdapter {
- // overrides "SubAdapter"
- public override async connect(): Promise {
- await super.connect();
- await this.getAssetsMetadata();
- }
+export class AcalaParachainAdapter extends SubAdapter {
+ protected assets: Record | null = null;
- // overrides "ParachainAdapter"
- protected override async getAssetsMetadata(): Promise {
- if (Array.isArray(this.assets)) return;
+ protected async getAssetsMetadata(): Promise {
+ if (this.assets) return;
- const assets: IParachainAssetMetadata[] = [];
+ const assets = {};
const entries = await (this.api.query.assetRegistry as any).assetMetadatas.entries();
for (const [key, option] of entries) {
@@ -67,36 +69,32 @@ export class AcalaParachainAdapter extends ParachainAdapter {
const decimals = option.value.decimals.toNumber();
const minimalBalance = option.value.minimalBalance.toString();
- assets.push({ id, symbol, decimals, minimalBalance });
+ assets[symbol] = { id, symbol, decimals, minimalBalance };
}
this.assets = Object.freeze(assets);
}
- // overrides "ParachainAdapter"
- public override getAssetMeta(asset: RegisteredAsset): Nullable> {
- if (!Array.isArray(this.assets)) return null;
+ private getAssetMeta(asset: RegisteredAsset): Nullable {
+ if (!(asset.symbol && this.assets)) return null;
- return this.assets.find(
- (item) => JSON.stringify(item.id) === asset.externalAddress || item.symbol === asset.symbol
- );
+ return this.assets[asset.symbol];
}
- // overrides "ParachainAdapter"
- public override async getAssetIdByMultilocation(asset: Asset, multilocation: any): Promise {
- const v3Multilocation = multilocation;
+ // overrides SubAdapter
+ public override async connect(): Promise {
+ await super.connect();
+ await this.getAssetsMetadata();
+ }
- const result = await (this.api.query.assetRegistry as any).locationToCurrencyIds(v3Multilocation);
+ protected override async getAssetDeposit(asset: RegisteredAsset): Promise {
+ const assetMeta = this.getAssetMeta(asset);
- let id!: Nullable;
+ if (!assetMeta) return ZeroStringValue;
- if (result.isEmpty) {
- id = { [AcalaPrimitivesCurrencyCurrencyId.Token]: asset.symbol };
- } else {
- id = getAcalaCurrencyId(result.unwrap());
- }
+ const minBalance = assetMeta.minimalBalance;
- return id ? JSON.stringify(id) : '';
+ return minBalance > '1' ? minBalance : ZeroStringValue;
}
protected override async getAccountAssetBalance(
@@ -115,7 +113,7 @@ export class AcalaParachainAdapter extends ParachainAdapter {
}, ZeroStringValue);
}
- // overrides "SubAdapter"
+ // overrides SubAdapter
public override getTransferExtrinsic(asset: RegisteredAsset, recipient: string, amount: number | string) {
const assetMeta = this.getAssetMeta(asset);
@@ -150,4 +148,11 @@ export class AcalaParachainAdapter extends ParachainAdapter {
'Unlimited'
);
}
+
+ public override async getNetworkFee(asset: RegisteredAsset, sender: string, recipient: string): Promise {
+ /* Throws error until Substrate 5 migration */
+ // return await super.getNetworkFee(asset, sender, recipient);
+ // Hardcoded value for Acala - 0.003 ACA
+ return '3000000000';
+ }
}
diff --git a/src/utils/bridge/sub/classes/adapters/parachain/astar.ts b/src/utils/bridge/sub/classes/adapters/parachain/astar.ts
index 3c4239e38..eab8b563f 100644
--- a/src/utils/bridge/sub/classes/adapters/parachain/astar.ts
+++ b/src/utils/bridge/sub/classes/adapters/parachain/astar.ts
@@ -1,32 +1,60 @@
import { FPNumber } from '@sora-substrate/util';
+import { formatBalance } from '@sora-substrate/util/build/assets';
import { ZeroStringValue } from '@/consts';
-import { ParachainAdapter } from './parachain';
+import { SubAdapter } from '../substrate';
import type { CodecString } from '@sora-substrate/util';
-import type { Asset, RegisteredAsset } from '@sora-substrate/util/build/assets/types';
+import type { RegisteredAsset } from '@sora-substrate/util/build/assets/types';
+
+type IAstarAssetMetadata = {
+ id: string;
+ symbol: string;
+ decimals: number;
+ minimalBalance: string;
+};
+
+export class AstarParachainAdapter extends SubAdapter {
+ protected assets: Record | null = null;
+
+ protected async getAssetsMetadata(): Promise {
+ if (this.assets) return;
+
+ const assets = {};
+ const entries = await (this.api.query.assets as any).metadata.entries();
+
+ for (const [key, value] of entries) {
+ const id = key.args[0].toString();
+ const symbol = new TextDecoder().decode(value.symbol); // bytes to string
+ const decimals = value.decimals.toNumber();
+ const minimalBalance = value.deposit.toString();
+
+ assets[symbol] = { id, symbol, decimals, minimalBalance };
+ }
+
+ this.assets = Object.freeze(assets);
+ }
+
+ private getAssetMeta(asset: RegisteredAsset): Nullable {
+ if (!(asset.symbol && this.assets)) return null;
+
+ return this.assets[asset.symbol];
+ }
-export class AstarParachainAdapter extends ParachainAdapter {
- // overrides "SubAdapter"
public override async connect(): Promise {
await super.connect();
await this.getAssetsMetadata();
}
- // overrides "ParachainAdapter"
- public override async getAssetIdByMultilocation(asset: Asset, multilocation: any): Promise {
- const versionedMultilocation = {
- V3: multilocation,
- };
-
- const result = await (this.api.query.xcAssetConfig as any).assetLocationToId(versionedMultilocation);
+ protected override async getAssetDeposit(asset: RegisteredAsset): Promise {
+ const assetMeta = this.getAssetMeta(asset);
- if (result.isEmpty) return '';
+ if (!assetMeta) return ZeroStringValue;
- const id = result.unwrap().toString();
+ const minBalance = assetMeta.minimalBalance;
- return id;
+ return minBalance > '1' ? minBalance : ZeroStringValue;
}
protected override async getAccountAssetBalance(
@@ -40,12 +68,6 @@ export class AstarParachainAdapter extends ParachainAdapter {
return await this.assetsAccountRequest(accountAddress, assetMeta.id);
}
- public override getTransferExtrinsic(asset: RegisteredAsset, recipient: string, amount: number | string) {
- return asset.symbol === this.chainSymbol
- ? this.getNativeTransferExtrinsic(asset, recipient, amount)
- : this.getAssetTransferExtrinsic(asset, recipient, amount);
- }
-
/**
* Transfer native token (ASTR)
*/
@@ -135,4 +157,17 @@ export class AstarParachainAdapter extends ParachainAdapter {
'Unlimited'
);
}
+
+ public override getTransferExtrinsic(asset: RegisteredAsset, recipient: string, amount: number | string) {
+ return asset.symbol === this.chainSymbol
+ ? this.getNativeTransferExtrinsic(asset, recipient, amount)
+ : this.getAssetTransferExtrinsic(asset, recipient, amount);
+ }
+
+ public async getNetworkFee(asset: RegisteredAsset, sender: string, recipient: string): Promise {
+ /* Throws error until Substrate 5 migration */
+ // return await super.getNetworkFee(asset, sender, recipient);
+ // Hardcoded value for Astar - 0.057 ASTR
+ return '57000000000000000';
+ }
}
diff --git a/src/utils/bridge/sub/classes/adapters/parachain/moonbase.ts b/src/utils/bridge/sub/classes/adapters/parachain/moonbase.ts
index 76dfeebe6..3bc0669c7 100644
--- a/src/utils/bridge/sub/classes/adapters/parachain/moonbase.ts
+++ b/src/utils/bridge/sub/classes/adapters/parachain/moonbase.ts
@@ -1,147 +1,13 @@
-import { FPNumber, TransactionStatus } from '@sora-substrate/util';
-import { SubNetworkId } from '@sora-substrate/util/build/bridgeProxy/sub/consts';
-import BN from 'bignumber.js';
-
-import xTokensAbi from '@/abi/ethereum/other/moonbeam/xTokens.json';
-import { ZeroStringValue } from '@/consts';
-import { SUB_NETWORKS } from '@/consts/sub';
-import { delay } from '@/utils';
-import { getEvmTransactionFee, onEvmTransactionPending } from '@/utils/bridge/common/utils';
-import { getTransaction, updateTransaction } from '@/utils/bridge/sub/utils';
-import ethersUtil from '@/utils/ethers-util';
-
-import { ParachainAdapter } from './parachain';
-
-import type { CodecString } from '@sora-substrate/util';
-import type { Asset, RegisteredAsset } from '@sora-substrate/util/build/assets/types';
-import type { ethers } from 'ethers';
-
-const MOONBASE_DATA = SUB_NETWORKS[SubNetworkId.AlphanetMoonbase];
-
-export class MoonbaseParachainAdapter extends ParachainAdapter {
- protected nativeAssetContractAddress = '0x0000000000000000000000000000000000000802';
- protected xTokensContractAddress = '0x0000000000000000000000000000000000000804';
-
- // overrides "WithConnectionApi"
- override get chainSymbol(): string | undefined {
- return MOONBASE_DATA?.nativeCurrency?.symbol;
- }
-
- // overrides "WithConnectionApi"
- public override formatAddress(address: string, _withPrefix = true): string {
- // return evm address without changes
- return address;
- }
-
- // overrides "SubAdapter"
- public override async connect(): Promise {
- await super.connect();
- await this.getAssetsMetadata();
- }
-
- // overrides "SubAdapter"
- protected override async getAccountAssetBalance(
- accountAddress: string,
- asset: RegisteredAsset
- ): Promise {
- return await this.assetsAccountRequest(accountAddress, asset.externalAddress);
- }
-
- // Assets has not minimal deposit on Moonbase
- // overrides "ParachainAdapter"
- protected override async getAssetDeposit(asset: RegisteredAsset): Promise {
- return ZeroStringValue;
- }
-
- // overrides "ParachainAdapter"
- public override async getAssetIdByMultilocation(asset: Asset, multilocation: any): Promise {
- const assetType = {
- XCM: multilocation,
- };
-
- const result = await (this.api.query.assetManager as any).assetTypeId(assetType);
-
- if (result.isEmpty) return '';
-
- const id = result.unwrap().toString();
-
- return id;
- }
-
- // overrides "ParachainAdapter"
- public override assetIdToEvmContractAddress(id: string): string {
- const base = new BN(id).toString(16);
- const padded = base.padStart(40, 'f');
- return `0x${padded}`;
- }
-
- /**
- * Convert parachain id to xcm junction
- */
- protected toParachainAddress(id: number | undefined): string {
- const selector = '0x00'; // Parachain selector
- const address = Number(id ?? 0)
- .toString(16)
- .padStart(8, '0'); // bytes4
-
- return selector + address;
- }
-
- /**
- * Convert substrate account address to xcm junction
- */
- protected toAccountId32(address: string): string {
- const selector = '0x01'; // AccountKey32 selector
- const publicKey = this.getPublicKeyByAddress(address); // AccountId32 address in hex
- const networkOption = '00'; // Network(Option) Null
-
- return selector + publicKey + networkOption;
- }
-
- public override async transfer(
- asset: RegisteredAsset,
- recipient: string,
- amount: string | number,
- historyId: string
- ) {
- const currencyAddress =
- asset.symbol === this.chainSymbol
- ? this.nativeAssetContractAddress
- : this.assetIdToEvmContractAddress(asset.externalAddress);
-
- const value = new FPNumber(amount, asset.externalDecimals).toCodecString();
- const weight = 5_000_000_000; // max weight, taken from successful xcm message on SORA parachain
-
- const parents = 1;
- const parachainJunction = this.toParachainAddress(this.getSoraParachainId());
- const accountJunction = this.toAccountId32(recipient);
- const interior = [parachainJunction, accountJunction]; // interior = X2 (the array has a length of 2)
- const destination = [parents, interior];
-
- const xTokens = await ethersUtil.getContract(this.xTokensContractAddress, xTokensAbi);
-
- const signedTx: ethers.TransactionResponse = await xTokens.transfer(currencyAddress, value, destination, weight);
-
- updateTransaction(historyId, {
- externalHash: signedTx.hash,
- externalNetworkFee: getEvmTransactionFee(signedTx),
- status: TransactionStatus.InBlock,
- });
-
- // wait a little to update tx in storage
- await delay();
-
- // run non blocking promise to update tx data
- onEvmTransactionPending(historyId, getTransaction, updateTransaction)
- .then(() => {
- updateTransaction(historyId, {
- status: TransactionStatus.Finalized,
- });
- })
- .catch(() => {
- updateTransaction(historyId, {
- status: TransactionStatus.Error,
- });
- });
- }
+import { u8aToHex } from '@polkadot/util';
+import { addressToEvm } from '@polkadot/util-crypto';
+
+import { SubAdapter } from '../substrate';
+
+export class MoonbaseParachainAdapter extends SubAdapter {
+ // overrides SubAdapter method
+ public formatAddress = (address?: string): string => {
+ if (!address) return '';
+ // [TODO] research how to get evm address as on moonbase
+ return u8aToHex(addressToEvm(address));
+ };
}
diff --git a/src/utils/bridge/sub/classes/adapters/parachain/parachain.ts b/src/utils/bridge/sub/classes/adapters/parachain/parachain.ts
deleted file mode 100644
index b6f7f7cfd..000000000
--- a/src/utils/bridge/sub/classes/adapters/parachain/parachain.ts
+++ /dev/null
@@ -1,85 +0,0 @@
-import { SubNetworkId } from '@sora-substrate/util/build/bridgeProxy/sub/consts';
-
-import { ZeroStringValue } from '@/consts';
-
-import { SubAdapter } from '../substrate';
-
-import type { CodecString } from '@sora-substrate/util';
-import type { Asset, RegisteredAsset } from '@sora-substrate/util/build/assets/types';
-
-export type IParachainAssetMetadata = {
- id: AssetId;
- symbol: string;
- decimals: number;
- minimalBalance?: string;
-};
-
-export class ParachainAdapter extends SubAdapter {
- protected assets: readonly IParachainAssetMetadata[] | null = null;
-
- protected async getAssetsMetadata(): Promise {
- if (Array.isArray(this.assets)) return;
-
- const assets: IParachainAssetMetadata[] = [];
- const entries = await (this.api.query.assets as any).metadata.entries();
-
- for (const [key, value] of entries) {
- const id = key.args[0].toString();
- const symbol = new TextDecoder().decode(value.symbol); // bytes to string
- const decimals = value.decimals.toNumber();
-
- assets.push({ id, symbol, decimals });
- }
-
- this.assets = Object.freeze(assets);
- }
-
- public getAssetMeta(asset: RegisteredAsset): Nullable> {
- if (!Array.isArray(this.assets)) return null;
-
- return this.assets.find((item) => item.id === asset.externalAddress || item.symbol === asset.symbol);
- }
-
- /**
- * Get asset external address by multilocation (for "registeredAsset.externalAddress" struct))
- */
- public async getAssetIdByMultilocation(asset: Asset, multilocation: any): Promise {
- throw new Error(`[${this.constructor.name}] "getAssetIdByMultilocation" method is not implemented`);
- }
-
- /**
- * Convert substrate asset id to evm token contract address
- */
- public assetIdToEvmContractAddress(id: string): string {
- throw new Error(`[${this.constructor.name}] "assetIdToEvmContractAddress" method is not implemented`);
- }
-
- // overrides SubAdapter
- protected override async getAssetDeposit(asset: RegisteredAsset): Promise {
- const assetMeta = this.getAssetMeta(asset);
-
- if (!assetMeta) return ZeroStringValue;
-
- const { minimalBalance, id } = assetMeta;
-
- if (minimalBalance) return minimalBalance;
-
- return await this.assetMinBalanceRequest(id as string);
- }
-
- public override async getNetworkFee(asset: RegisteredAsset, sender: string, recipient: string): Promise {
- /* Throws error until Substrate 5 migration */
- // return await super.getNetworkFee(asset, sender, recipient);
- // Hardcoded values
- switch (this.subNetwork) {
- case SubNetworkId.PolkadotAcala:
- return '3000000000';
- case SubNetworkId.PolkadotAstar:
- return '57000000000000000';
- case SubNetworkId.AlphanetMoonbase:
- return '40000000000000';
- default:
- return '0';
- }
- }
-}
diff --git a/src/utils/bridge/sub/classes/adapters/relaychain/relaychain.ts b/src/utils/bridge/sub/classes/adapters/relaychain.ts
similarity index 94%
rename from src/utils/bridge/sub/classes/adapters/relaychain/relaychain.ts
rename to src/utils/bridge/sub/classes/adapters/relaychain.ts
index dc226789e..5adc2fcca 100644
--- a/src/utils/bridge/sub/classes/adapters/relaychain/relaychain.ts
+++ b/src/utils/bridge/sub/classes/adapters/relaychain.ts
@@ -1,7 +1,7 @@
import { FPNumber } from '@sora-substrate/util';
import { SubNetworkId } from '@sora-substrate/util/build/bridgeProxy/sub/consts';
-import { SubAdapter } from '../substrate';
+import { SubAdapter } from './substrate';
import type { CodecString } from '@sora-substrate/util';
import type { RegisteredAsset } from '@sora-substrate/util/build/assets/types';
@@ -71,11 +71,11 @@ export class RelaychainAdapter extends SubAdapter {
case SubNetworkId.Rococo:
return toCodec(0.000125);
case SubNetworkId.Alphanet:
- return toCodec(0.019 + 0.037);
+ return toCodec(0.019);
case SubNetworkId.Kusama:
return toCodec(0.002);
case SubNetworkId.Polkadot:
- return toCodec(0.019 + 0.037);
+ return toCodec(0.059);
default:
return '0';
}
diff --git a/src/utils/bridge/sub/classes/adapters/relaychain/alphanet.ts b/src/utils/bridge/sub/classes/adapters/relaychain/alphanet.ts
deleted file mode 100644
index 0c16e220c..000000000
--- a/src/utils/bridge/sub/classes/adapters/relaychain/alphanet.ts
+++ /dev/null
@@ -1,24 +0,0 @@
-import { SubNetworkId } from '@sora-substrate/util/build/bridgeProxy/sub/consts';
-
-import { SUB_NETWORKS } from '@/consts/sub';
-
-import { RelaychainAdapter } from './relaychain';
-
-const ALPHANET_DATA = SUB_NETWORKS[SubNetworkId.Alphanet];
-
-export class AlphanetRelaychainAdapter extends RelaychainAdapter {
- // overrides "WithConnectionApi"
- override get chainSymbol(): string | undefined {
- return ALPHANET_DATA?.nativeCurrency?.symbol;
- }
-
- // overrides "WithConnectionApi"
- override get chainDecimals(): number | undefined {
- return ALPHANET_DATA?.nativeCurrency?.decimals;
- }
-
- // overrides "WithConnectionApi"
- override get chainSS58(): number | undefined {
- return 42; // (Substrate, 42)
- }
-}
diff --git a/src/utils/bridge/sub/classes/adapters/standalone/liberland.ts b/src/utils/bridge/sub/classes/adapters/standalone/liberland.ts
index db4bbdea7..dae7d0931 100644
--- a/src/utils/bridge/sub/classes/adapters/standalone/liberland.ts
+++ b/src/utils/bridge/sub/classes/adapters/standalone/liberland.ts
@@ -9,9 +9,7 @@ import type { RegisteredAsset } from '@sora-substrate/util/build/assets/types';
export class LiberlandAdapter extends SubAdapter {
protected override async getAssetDeposit(asset: RegisteredAsset): Promise {
- const assetId = Number(asset.externalAddress);
-
- return await this.assetMinBalanceRequest(assetId);
+ return await this.assetsAssetMinBalanceRequest(Number(asset.externalAddress));
}
protected override async getAccountAssetBalance(
diff --git a/src/utils/bridge/sub/classes/adapters/substrate.ts b/src/utils/bridge/sub/classes/adapters/substrate.ts
index 4b7ddf6e9..68856c606 100644
--- a/src/utils/bridge/sub/classes/adapters/substrate.ts
+++ b/src/utils/bridge/sub/classes/adapters/substrate.ts
@@ -135,10 +135,6 @@ class BaseSubAdapter extends WithConnectionApi {
public getTransferExtrinsic(asset: RegisteredAsset, recipient: string, amount: string | number) {
throw new Error(`[${this.constructor.name}] "getTransferExtrinsic" method is not implemented`);
}
-
- public async transfer(asset: RegisteredAsset, recipient: string, amount: string | number, historyId: string) {
- throw new Error(`[${this.constructor.name}] "transfer" method is not implemented`);
- }
}
export class SubAdapter extends BaseSubAdapter {
@@ -158,7 +154,7 @@ export class SubAdapter extends BaseSubAdapter {
}, ZeroStringValue);
}
- protected async assetMinBalanceRequest(assetId: number | string): Promise {
+ protected async assetsAssetMinBalanceRequest(assetId: number | string): Promise {
return await this.withConnection(async () => {
const result = await (this.api.query.assets as any).asset(assetId);
diff --git a/src/utils/bridge/sub/classes/history.ts b/src/utils/bridge/sub/classes/history.ts
index 20da783af..36a498a0a 100644
--- a/src/utils/bridge/sub/classes/history.ts
+++ b/src/utils/bridge/sub/classes/history.ts
@@ -12,14 +12,10 @@ import {
getDepositedBalance,
getMessageAcceptedNonces,
getMessageDispatchedNonces,
- getParachainSystemMessageHash,
isMessageDispatchedNonces,
getReceivedAmount,
- isParaInclusion,
+ isEvent,
isTransactionFeePaid,
- isBridgeProxyHash,
- isMessageAccepted,
- isQueueMessage,
} from '@/utils/bridge/sub/utils';
import type { ApiPromise } from '@polkadot/api';
@@ -49,7 +45,9 @@ const getTxEvents = (blockEvents: any[], txIndex: number) => {
const findTxInBlock = async (blockHash: string, soraHash: string) => {
const blockEvents = await api.system.getBlockEvents(blockHash);
- const event = blockEvents.find((e) => isBridgeProxyHash(e, soraHash));
+ const event = blockEvents.find(
+ (e) => isEvent(e, 'bridgeProxy', 'RequestStatusUpdate') && e.event.data?.[0]?.toString() === soraHash
+ );
if (!event) throw new Error('Unable to find "bridgeProxy.RequestStatusUpdate" event');
@@ -243,7 +241,7 @@ class SubBridgeHistory extends SubNetworksConnector {
const soraFeeEvent = events.find((e) => isTransactionFeePaid(e));
history.soraNetworkFee = soraFeeEvent.event.data[1].toString();
// sended from SORA nonces
- const [soraBatchNonce, soraMessageNonce] = getMessageAcceptedNonces(events);
+ const [soraBatchNonce, soraMessageNonce] = getMessageAcceptedNonces(events, this.soraApi);
// api for Standalone network or SORA parachain
const networkApi = this.getIntermediateApi(history);
const networkBlockId = history.externalBlockId as string;
@@ -252,7 +250,7 @@ class SubBridgeHistory extends SubNetworksConnector {
const networkEventsReversed = [...networkEvents].reverse();
// Network received nonces
const messageDispatchedIndex = networkEventsReversed.findIndex((e) =>
- isMessageDispatchedNonces(soraBatchNonce, soraMessageNonce, e)
+ isMessageDispatchedNonces(soraBatchNonce, soraMessageNonce, e, networkApi)
);
if (messageDispatchedIndex === -1) {
@@ -271,15 +269,19 @@ class SubBridgeHistory extends SubNetworksConnector {
// SORA Parachain extrinsic events for next search
const parachainExtrinsicEvents = networkEventsReversed.slice(messageDispatchedIndex);
- // sended from SORA Parachain message hash (1)
- const messageHash = getParachainSystemMessageHash(parachainExtrinsicEvents);
+ // sended from SORA Parachain to Relaychain message hash (1)
+ const messageToRelaychain = parachainExtrinsicEvents.find((e) =>
+ isEvent(e, 'parachainSystem', 'UpwardMessageSent')
+ );
+ // sended from SORA Parachain to Parachain message hash (2)
+ const messageToParachain = parachainExtrinsicEvents.find((e) => isEvent(e, 'xcmpQueue', 'XcmpMessageSent'));
- if (!messageHash) {
+ if (!messageToRelaychain && !messageToParachain) {
return await this.processOutgoingToSoraParachain(history, asset, parachainExtrinsicEvents);
}
- const isRelaychain = subBridgeApi.isRelayChain(externalNetwork);
- const isParachain = subBridgeApi.isParachain(externalNetwork);
+ const isRelaychain = subBridgeApi.isRelayChain(externalNetwork) && messageToRelaychain;
+ const isParachain = subBridgeApi.isParachain(externalNetwork) && messageToParachain;
if (!isRelaychain && !isParachain) {
console.info(`[${history.id}] not "${externalNetwork}" transaction, skip;`);
@@ -288,6 +290,7 @@ class SubBridgeHistory extends SubNetworksConnector {
this.updateSoraParachainBlockData(history);
+ const messageHash = (messageToRelaychain ?? messageToParachain).event.data.messageHash.toString();
const relayChainBlockNumber = await subBridgeApi.soraParachainApi.getRelayChainBlockNumber(
history.parachainBlockId as string,
soraParachainApi
@@ -331,7 +334,7 @@ class SubBridgeHistory extends SubNetworksConnector {
const [receivedAmount, externalEventIndex] = getDepositedBalance(
extrinsicEvents,
history.to as string,
- soraParachain
+ soraParachainApi
);
// balances.Deposit event index
history.externalEventIndex = externalEventIndex;
@@ -364,15 +367,23 @@ class SubBridgeHistory extends SubNetworksConnector {
txEvents: any[];
blockEvents: any[];
}): Promise> {
+ const { soraApi } = this;
// Token is minted to account event
- const [_, eventIndex] = getDepositedBalance(blockEvents, history.from as string, subBridgeApi);
+ const [_, eventIndex] = getDepositedBalance(blockEvents, history.from as string, this.soraApi);
history.payload.eventIndex = eventIndex;
// find SORA hash event index
- const requestStatusUpdateEventIndex = txEvents.findIndex((e) => isBridgeProxyHash(e, history.id as string));
+ const requestStatusUpdateEventIndex = txEvents.findIndex((e) => {
+ if (!isEvent(e, 'bridgeProxy', 'RequestStatusUpdate')) return false;
+
+ const hash = e.event.data[0].toString();
+
+ return hash === history.id;
+ });
// Received on SORA nonces
const [soraBatchNonce, soraMessageNonce] = getMessageDispatchedNonces(
- txEvents.slice(requestStatusUpdateEventIndex)
+ txEvents.slice(requestStatusUpdateEventIndex),
+ soraApi
);
// api for Standalone network or SORA parachain
const networkApi = this.getIntermediateApi(history);
@@ -381,9 +392,9 @@ class SubBridgeHistory extends SubNetworksConnector {
const networkEvents = await api.system.getBlockEvents(networkBlockId, networkApi);
// Network message sended to SORA
const messageToSoraEvent = networkEvents.find((e) => {
- if (!isMessageAccepted(e)) return false;
+ if (!isEvent(e, 'substrateBridgeOutboundChannel', 'MessageAccepted')) return false;
- const [networkBatchNonce, networkMessageNonce] = getMessageAcceptedNonces([e]);
+ const [networkBatchNonce, networkMessageNonce] = getMessageAcceptedNonces([e], networkApi);
return networkBatchNonce === soraBatchNonce && networkMessageNonce === soraMessageNonce;
});
@@ -417,7 +428,7 @@ class SubBridgeHistory extends SubNetworksConnector {
if (!soraParachainApi) throw new Error('SORA Parachain Api is not exists');
// If transfer received from Parachain, extrinsic events should have xcmpQueue.Success event
- const messageEvent = networkExtrinsicEvents.find((e) => isQueueMessage(e));
+ const messageEvent = networkExtrinsicEvents.find((e) => isEvent(e, 'xcmpQueue', 'Success'));
const externalNetwork = history.externalNetwork as SubNetwork;
const isRelayChain = subBridgeApi.isRelayChain(externalNetwork) && !messageEvent;
const isParachain =
@@ -436,7 +447,7 @@ class SubBridgeHistory extends SubNetworksConnector {
);
if (isParachain) {
- const messageHash = messageEvent.event.data[0].toString();
+ const messageHash = messageEvent.event.data.messageHash.toString();
// Parachain block, found through relaychain validation data
const parachainBlockId = await this.findParachainBlockIdOnRelaychain(history, relayChainBlockNumber, false);
@@ -455,7 +466,7 @@ class SubBridgeHistory extends SubNetworksConnector {
// relay chain should have send message in this blocks range
const startSearch = relayChainBlockNumber;
- const endSearch = startSearch - 10;
+ const endSearch = startSearch - 6;
for (let relaychainBlockHeight = startSearch; relaychainBlockHeight >= endSearch; relaychainBlockHeight--) {
const blockId = await api.system.getBlockHash(relaychainBlockHeight, this.externalApi);
@@ -517,7 +528,7 @@ class SubBridgeHistory extends SubNetworksConnector {
// relay chain should have send validation data in this blocks range
const startSearch = relayChainBlockNumber;
- const blocksRange = 10;
+ const blocksRange = 3;
const endSearch = isOutgoing ? startSearch + blocksRange : startSearch - blocksRange;
for (let relaychainBlockHeight = startSearch; relaychainBlockHeight !== endSearch; ) {
@@ -525,14 +536,11 @@ class SubBridgeHistory extends SubNetworksConnector {
const events = await api.system.getBlockEvents(blockId, relaychainApi);
for (const e of events) {
- if (!isParaInclusion(e)) continue;
+ if (!isEvent(e, 'paraInclusion', 'CandidateIncluded')) continue;
const { descriptor } = e.event.data[0];
- const descriptorParaId = descriptor.paraId.toNumber();
- const paraId = network.getParachainId();
-
- if (descriptorParaId !== paraId) continue;
+ if (descriptor.paraId.toNumber() !== network.getParachainId()) continue;
history.relaychainBlockHeight = relaychainBlockHeight;
history.relaychainBlockId = blockId;
@@ -564,10 +572,10 @@ class SubBridgeHistory extends SubNetworksConnector {
const extrinsicEvents = parachainBlockEvents.filter(
({ phase }) => phase.isApplyExtrinsic && phase.asApplyExtrinsic.toNumber() === extrinsicIndex
);
+ const messageSentEvent = extrinsicEvents.find((e) => isEvent(e, 'xcmpQueue', 'XcmpMessageSent'));
- const messageSentHash = getParachainSystemMessageHash(extrinsicEvents);
-
- if (messageSentHash !== messageHash) continue;
+ if (!messageSentEvent) continue;
+ if (messageSentEvent.event.data[0].toString() !== messageHash) continue;
const parachainBlockHeight = await api.system.getBlockNumber(parachainBlockId, externalApi);
const signer = extrinsic.signer.toString();
@@ -596,17 +604,15 @@ class SubBridgeHistory extends SubNetworksConnector {
endSearch: number
) {
for (let blockHeight = startSearch; blockHeight <= endSearch; blockHeight++) {
- let isReliableMessage = false;
-
try {
const blockId = await api.system.getBlockHash(blockHeight, this.externalApi);
const blockEvents = await api.system.getBlockEvents(blockId, this.externalApi);
const messageEventIndex = blockEvents.findIndex((e) => {
- if (isQueueMessage(e)) {
- isReliableMessage = e.event.data[0].toString() === messageHash;
+ if (isEvent(e, 'messageQueue', 'Processed') || isEvent(e, 'xcmpQueue', 'Success')) {
+ const messageHashMatches = e.event.data[0].toString() === messageHash;
- return true;
+ return messageHashMatches;
}
return false;
});
@@ -620,7 +626,7 @@ class SubBridgeHistory extends SubNetworksConnector {
const [receivedAmount, externalEventIndex] = getDepositedBalance(
blockEvents.slice(0, messageEventIndex),
history.to,
- this.network
+ this.externalApi
);
// Deposit event index
@@ -637,11 +643,7 @@ class SubBridgeHistory extends SubNetworksConnector {
return history;
} catch {
- if (isReliableMessage) {
- break;
- } else {
- continue;
- }
+ continue;
}
}
diff --git a/src/utils/bridge/sub/classes/reducers.ts b/src/utils/bridge/sub/classes/reducers.ts
index 031d09024..5aa0958bd 100644
--- a/src/utils/bridge/sub/classes/reducers.ts
+++ b/src/utils/bridge/sub/classes/reducers.ts
@@ -21,9 +21,8 @@ import {
determineTransferType,
getReceivedAmount,
getParachainSystemMessageHash,
- isXcmPalletAttempted,
+ isEvent,
isTransactionFeePaid,
- isQueueMessage,
} from '@/utils/bridge/sub/utils';
import type { ApiRx } from '@polkadot/api';
@@ -106,11 +105,6 @@ export class SubBridgeReducer extends BridgeReducer {
// update history data
this.updateTransactionPayload(id, { startBlock });
}
-
- async waitForTxBlockAndStatus(id: string): Promise {
- await this.waitForTransactionStatus(id);
- await this.waitForTransactionBlockId(id);
- }
}
export class SubBridgeIncomingReducer extends SubBridgeReducer {
@@ -162,7 +156,7 @@ export class SubBridgeIncomingReducer extends SubBridgeReducer {
// open connections
await this.connector.start();
// sign transaction (from is sora account)
- await this.connector.incomingTransfer(asset, tx.from as string, tx.amount as string, id);
+ await this.connector.transfer(asset, tx.from as string, tx.amount as string, id);
// save start block when tx was signed
await this.saveStartBlock(id);
}
@@ -189,7 +183,6 @@ export class SubBridgeIncomingReducer extends SubBridgeReducer {
private async updateTxExternalData(id: string): Promise {
const tx = this.getTransaction(id);
-
const adapter = this.connector.network;
await adapter.connect();
@@ -207,7 +200,7 @@ export class SubBridgeIncomingReducer extends SubBridgeReducer {
}
if (this.transferType === SubTransferType.Relaychain) {
- const xcmEvent = transactionEvents.find((e) => isXcmPalletAttempted(e));
+ const xcmEvent = transactionEvents.find((e) => isEvent(e, 'xcmPallet', 'Attempted'));
if (!xcmEvent?.event?.data?.[0]?.isComplete) {
throw new Error(`[${this.constructor.name}]: Transaction is not completed`);
@@ -216,10 +209,8 @@ export class SubBridgeIncomingReducer extends SubBridgeReducer {
}
private async updateTxIncomingData(id: string): Promise {
- await this.waitForTxBlockAndStatus(id);
-
- if (subBridgeApi.isEvmAccount(this.getTransaction(id).externalNetwork as SubNetwork)) return;
-
+ await this.waitForTransactionStatus(id);
+ await this.waitForTransactionBlockId(id);
await this.updateTxSigningData(id);
await this.updateTxExternalData(id);
}
@@ -240,8 +231,8 @@ export class SubBridgeIncomingReducer extends SubBridgeReducer {
const isFirstStep = [SubTransferType.SoraParachain, SubTransferType.Standalone].includes(this.transferType);
const sended = new FPNumber(tx.amount as string, this.asset.externalDecimals).toCodecString();
- const sender = tx.to as string;
- const recipient = tx.from as string;
+ const from = tx.from as string;
+ const to = tx.to as string;
let subscription!: Subscription;
let messageNonce!: number;
@@ -271,7 +262,7 @@ export class SubBridgeIncomingReducer extends SubBridgeReducer {
if (!isStandalone) {
assetSendEventIndex = events.findIndex((e) =>
- isAssetAddedToChannel(e, this.asset, recipient, sended, adapter)
+ isAssetAddedToChannel(e, this.asset, to, sended, adapter.api)
);
if (assetSendEventIndex !== -1) {
@@ -282,7 +273,7 @@ export class SubBridgeIncomingReducer extends SubBridgeReducer {
}
} else {
assetSendEventIndex = events.findIndex((e) =>
- isSoraBridgeAppBurned(e, this.asset, sender, recipient, sended, adapter)
+ isSoraBridgeAppBurned(e, this.asset, from, to, sended, adapter.api)
);
if (assetSendEventIndex !== -1) {
@@ -293,7 +284,7 @@ export class SubBridgeIncomingReducer extends SubBridgeReducer {
if (assetSendEventIndex === -1) return;
blockNumber = blockHeight;
- [batchNonce, messageNonce] = getMessageAcceptedNonces(events.slice(assetSendEventIndex));
+ [batchNonce, messageNonce] = getMessageAcceptedNonces(events.slice(assetSendEventIndex), adapter.api);
resolve();
} catch (error) {
@@ -350,16 +341,16 @@ export class SubBridgeIncomingReducer extends SubBridgeReducer {
try {
const events = [...eventsVec.toArray()].reverse();
const substrateDispatchEventIndex = events.findIndex((e) =>
- isMessageDispatchedNonces(tx.payload.batchNonce, tx.payload.messageNonce, e)
+ isMessageDispatchedNonces(tx.payload.batchNonce, tx.payload.messageNonce, e, subBridgeApi.api)
);
if (substrateDispatchEventIndex === -1) return;
const foundedEvents = events.slice(substrateDispatchEventIndex);
- soraHash = getBridgeProxyHash(foundedEvents);
+ soraHash = getBridgeProxyHash(foundedEvents, subBridgeApi.api);
- [amount, eventIndex] = getDepositedBalance(foundedEvents, tx.from as string, subBridgeApi);
+ [amount, eventIndex] = getDepositedBalance(foundedEvents, tx.to as string, subBridgeApi.api);
resolve();
} catch (error) {
@@ -378,9 +369,9 @@ export class SubBridgeIncomingReducer extends SubBridgeReducer {
}
private async waitSoraBlockByHash(id: string): Promise {
- const { hash, from, externalNetwork } = this.getTransaction(id);
+ const { hash, to, externalNetwork } = this.getTransaction(id);
- if (!(hash && from && externalNetwork)) {
+ if (!(hash && to && externalNetwork)) {
throw new Error(`[${this.constructor.name}] Lost transaction params`);
}
@@ -389,7 +380,7 @@ export class SubBridgeIncomingReducer extends SubBridgeReducer {
try {
await new Promise((resolve) => {
- subscription = subBridgeApi.subscribeOnTransactionDetails(from, externalNetwork, hash).subscribe((data) => {
+ subscription = subBridgeApi.subscribeOnTransactionDetails(to, externalNetwork, hash).subscribe((data) => {
if (data?.endBlock) {
soraBlockNumber = data.endBlock;
resolve();
@@ -426,7 +417,8 @@ export class SubBridgeOutgoingReducer extends SubBridgeReducer {
this.updateTransactionParams(id, { transactionState: BridgeTxStatus.Pending });
await this.checkTxId(id);
- await this.waitForTxBlockAndStatus(id);
+ await this.waitForTransactionStatus(id);
+ await this.waitForTransactionBlockId(id);
await this.waitForSendingExecution(id);
await this.waitForIntermediateExecution(id);
@@ -474,7 +466,7 @@ export class SubBridgeOutgoingReducer extends SubBridgeReducer {
// open connections
await this.connector.start();
// sign transaction
- await this.connector.outgoingTransfer(asset, tx.to as string, tx.amount as string, id);
+ await subBridgeApi.transfer(asset, tx.to as string, tx.amount as string, tx.externalNetwork as SubNetwork, id);
// save start block when tx was signed
await this.saveStartBlock(id);
}
@@ -488,10 +480,10 @@ export class SubBridgeOutgoingReducer extends SubBridgeReducer {
const transactionHash = tx.txId as string;
const transactionEvents = await getTransactionEvents(blockHash, transactionHash, subBridgeApi.api);
- const hash = getBridgeProxyHash(transactionEvents);
+ const hash = getBridgeProxyHash(transactionEvents, subBridgeApi.api);
this.updateTransactionParams(id, { hash });
- const [batchNonce, messageNonce] = getMessageAcceptedNonces(transactionEvents);
+ const [batchNonce, messageNonce] = getMessageAcceptedNonces(transactionEvents, subBridgeApi.api);
this.updateTransactionPayload(id, { batchNonce, messageNonce });
}
@@ -529,7 +521,7 @@ export class SubBridgeOutgoingReducer extends SubBridgeReducer {
try {
const events = [...eventsVec.toArray()].reverse();
const substrateDispatchEventIndex = events.findIndex((e) =>
- isMessageDispatchedNonces(tx.payload.batchNonce, tx.payload.messageNonce, e)
+ isMessageDispatchedNonces(tx.payload.batchNonce, tx.payload.messageNonce, e, adapter.api)
);
if (substrateDispatchEventIndex === -1) return;
@@ -542,11 +534,11 @@ export class SubBridgeOutgoingReducer extends SubBridgeReducer {
[amountReceived] = getDepositedBalance(
events.slice(substrateDispatchEventIndex),
tx.to as string,
- adapter
+ adapter.api
);
}
} else {
- messageHash = getParachainSystemMessageHash(events.slice(substrateDispatchEventIndex));
+ messageHash = getParachainSystemMessageHash(events.slice(substrateDispatchEventIndex), adapter.api);
}
resolve();
@@ -609,16 +601,11 @@ export class SubBridgeOutgoingReducer extends SubBridgeReducer {
subscription = combineLatest([eventsObservable, blockNumberObservable]).subscribe(
([eventsVec, blockHeight]) => {
- // when received message is equal to sended
- let isReliableMessage = false;
-
try {
const events = eventsVec.toArray();
const messageQueueProcessedEventIndex = events.findIndex((e) => {
- if (isQueueMessage(e)) {
- isReliableMessage = e.event.data[0].toString() === messageHash;
-
- return true;
+ if (isEvent(e, 'messageQueue', 'Processed') || isEvent(e, 'xcmpQueue', 'Success')) {
+ return e.event.data[0].toString() === messageHash;
}
return false;
});
@@ -626,19 +613,16 @@ export class SubBridgeOutgoingReducer extends SubBridgeReducer {
if (messageQueueProcessedEventIndex === -1) return;
blockNumber = blockHeight;
- // throws error, is deposit not found
+
[amount, externalEventIndex] = getDepositedBalance(
events.slice(0, messageQueueProcessedEventIndex),
tx.to as string,
- adapter
+ adapter.api
);
resolve();
} catch (error) {
- // The message is reliable, but the deposit was not found
- if (isReliableMessage) {
- reject(error);
- }
+ reject(error);
}
}
);
diff --git a/src/utils/bridge/sub/utils.ts b/src/utils/bridge/sub/utils.ts
index a328fd3fe..401bd4a99 100644
--- a/src/utils/bridge/sub/utils.ts
+++ b/src/utils/bridge/sub/utils.ts
@@ -1,21 +1,15 @@
-import { FPNumber, Operation } from '@sora-substrate/util';
+import { FPNumber } from '@sora-substrate/util';
import { subBridgeApi } from '@/utils/bridge/sub/api';
import { SubTransferType } from '@/utils/bridge/sub/types';
-import type { CodecString, WithConnectionApi } from '@sora-substrate/util';
+import type { ApiPromise } from '@polkadot/api';
+import type { CodecString } from '@sora-substrate/util';
import type { RegisteredAccountAsset } from '@sora-substrate/util/build/assets/types';
import type { SubNetwork, SubHistory } from '@sora-substrate/util/build/bridgeProxy/sub/types';
-export const isOutgoingTx = (tx: SubHistory): boolean => {
- return tx.type === Operation.SubstrateOutgoing;
-};
-
export const isUnsignedTx = (tx: SubHistory): boolean => {
- const signId =
- subBridgeApi.isEvmAccount(tx.externalNetwork as SubNetwork) && !isOutgoingTx(tx) ? tx.externalHash : tx.txId;
-
- return !tx.blockId && !signId;
+ return !tx.blockId && !tx.txId;
};
export const getTransaction = (id: string): SubHistory => {
@@ -44,26 +38,8 @@ export const determineTransferType = (network: SubNetwork) => {
}
};
-export const isEvent = (e, section: string, method: string) => {
- return e.event.section === section && e.event.method === method;
-};
-
-export const isQueueMessage = (e) =>
- isEvent(e, 'messageQueue', 'Processed') ||
- isEvent(e, 'xcmpQueue', 'Success') ||
- isEvent(e, 'xcmpQueue', 'Fail') ||
- isEvent(e, 'dmpQueue', 'ExecutedDownward');
-
-export const isParaInclusion = (e) => isEvent(e, 'paraInclusion', 'CandidateIncluded');
-
-export const isXcmPalletAttempted = (e) => isEvent(e, 'xcmPallet', 'Attempted');
-
-export const isTransactionFeePaid = (e) => isEvent(e, 'transactionPayment', 'TransactionFeePaid');
-
-export const isBridgeProxyUpdate = (e) => isEvent(e, 'bridgeProxy', 'RequestStatusUpdate');
-
-export const getBridgeProxyHash = (events: Array): string => {
- const bridgeProxyEvent = events.find((e) => isBridgeProxyUpdate(e));
+export const getBridgeProxyHash = (events: Array, api: ApiPromise): string => {
+ const bridgeProxyEvent = events.find((e) => api.events.bridgeProxy.RequestStatusUpdate.is(e.event));
if (!bridgeProxyEvent) {
throw new Error(`Unable to find "bridgeProxy.RequestStatusUpdate" event`);
@@ -72,16 +48,14 @@ export const getBridgeProxyHash = (events: Array): string => {
return bridgeProxyEvent.event.data[0].toString();
};
-export const isBridgeProxyHash = (e: any, hash: string): boolean => {
- if (!isBridgeProxyUpdate(e)) return false;
-
- const proxyHash = e.event.data[0].toString();
-
- return hash === proxyHash;
+export const isEvent = (e, section: string, method: string) => {
+ return e.event.section === section && e.event.method === method;
};
-export const getDepositedBalance = (events: Array, to: string, chainApi: WithConnectionApi): [string, number] => {
- const recipient = chainApi.formatAddress(to).toLowerCase();
+export const isTransactionFeePaid = (e) => isEvent(e, 'transactionPayment', 'TransactionFeePaid');
+
+export const getDepositedBalance = (events: Array, to: string, api: ApiPromise): [string, number] => {
+ const recipient = subBridgeApi.formatAddress(to);
const index = events.findIndex((e) => {
let eventRecipient = '';
@@ -90,15 +64,11 @@ export const getDepositedBalance = (events: Array, to: string, chainApi: Wi
eventRecipient = e.event.data.who.toString();
} else if (isEvent(e, 'assets', 'Transfer')) {
eventRecipient = e.event.data[1].toString();
- } else if (isEvent(e, 'assets', 'Issued')) {
- eventRecipient = e.event.data.owner.toString();
}
if (!eventRecipient) return false;
- const formatted = chainApi.formatAddress(eventRecipient).toLowerCase();
-
- return formatted === recipient;
+ return subBridgeApi.formatAddress(eventRecipient) === recipient;
});
if (index === -1) throw new Error(`Unable to find balance deposit like event`);
@@ -118,22 +88,22 @@ export const getReceivedAmount = (sendedAmount: string, receivedAmount: CodecStr
return { amount: amount2, transferFee };
};
-export const getParachainSystemMessageHash = (events: Array) => {
+export const getParachainSystemMessageHash = (events: Array, api: ApiPromise) => {
const parachainSystemEvent = events.find(
- (e) => isEvent(e, 'parachainSystem', 'UpwardMessageSent') || isEvent(e, 'xcmpQueue', 'XcmpMessageSent')
+ (e) => api.events.parachainSystem.UpwardMessageSent.is(e.event) || api.events.xcmpQueue.XcmpMessageSent.is(e.event)
);
if (!parachainSystemEvent) {
throw new Error(`Unable to find "parachainSystem.UpwardMessageSent" event`);
}
- return parachainSystemEvent.event.data[0].toString();
+ return parachainSystemEvent.event.data.messageHash.toString();
};
-export const isMessageAccepted = (e) => isEvent(e, 'substrateBridgeOutboundChannel', 'MessageAccepted');
-
-export const getMessageAcceptedNonces = (events: Array): [number, number] => {
- const messageAcceptedEvent = events.find((e) => isMessageAccepted(e));
+export const getMessageAcceptedNonces = (events: Array, api: ApiPromise): [number, number] => {
+ const messageAcceptedEvent = events.find((e) =>
+ api.events.substrateBridgeOutboundChannel.MessageAccepted.is(e.event)
+ );
if (!messageAcceptedEvent) {
throw new Error('Unable to find "substrateBridgeOutboundChannel.MessageAccepted" event');
@@ -145,10 +115,8 @@ export const getMessageAcceptedNonces = (events: Array): [number, number] =
return [batchNonce, messageNonce];
};
-export const isMessageDispatched = (e) => isEvent(e, 'substrateDispatch', 'MessageDispatched');
-
-export const getMessageDispatchedNonces = (events: Array): [number, number] => {
- const messageDispatchedEvent = events.find((e) => isMessageDispatched(e));
+export const getMessageDispatchedNonces = (events: Array, api: ApiPromise): [number, number] => {
+ const messageDispatchedEvent = events.find((e) => api.events.substrateDispatch.MessageDispatched.is(e.event));
if (!messageDispatchedEvent) {
throw new Error('Unable to find "substrateDispatch.MessageDispatched" event');
@@ -161,8 +129,13 @@ export const getMessageDispatchedNonces = (events: Array): [number, number]
return [eventBatchNonce, eventMessageNonce];
};
-export const isMessageDispatchedNonces = (sendedBatchNonce: number, sendedMessageNonce: number, e: any): boolean => {
- if (!isMessageDispatched(e)) return false;
+export const isMessageDispatchedNonces = (
+ sendedBatchNonce: number,
+ sendedMessageNonce: number,
+ e: any,
+ api: ApiPromise
+): boolean => {
+ if (!api.events.substrateDispatch.MessageDispatched.is(e.event)) return false;
const { batchNonce, messageNonce } = e.event.data[0];
@@ -183,13 +156,13 @@ export const isAssetAddedToChannel = (
asset: RegisteredAccountAsset,
to: string,
sended: CodecString,
- chainApi: WithConnectionApi
+ api: ApiPromise
): boolean => {
- if (!isEvent(e, 'xcmApp', 'AssetAddedToChannel')) return false;
+ if (!api.events.xcmApp.AssetAddedToChannel.is(e.event)) return false;
const { amount, assetId, recipient } = e.event.data[0].asTransfer;
// address check
- if (chainApi.formatAddress(recipient.toString()) !== chainApi.formatAddress(to)) return false;
+ if (subBridgeApi.formatAddress(recipient.toString()) !== subBridgeApi.formatAddress(to)) return false;
// asset check
if (assetId.toString() !== asset.address) return false;
// amount check
@@ -206,9 +179,9 @@ export const isSoraBridgeAppBurned = (
from: string,
to: string,
sended: CodecString,
- chainApi: WithConnectionApi
+ api: ApiPromise
) => {
- if (!isEvent(e, 'soraBridgeApp', 'Burned')) return false;
+ if (!api.events.soraBridgeApp.Burned.is(e.event)) return false;
const [networkIdCodec, assetIdCodec, senderCodec, recipientCodec, amountCodec] = e.event.data;
@@ -220,8 +193,8 @@ export const isSoraBridgeAppBurned = (
const amount = amountCodec.toString();
// address check
- if (chainApi.formatAddress(sender) !== chainApi.formatAddress(from)) return false;
- if (chainApi.formatAddress(recipient) !== chainApi.formatAddress(to)) return false;
+ if (subBridgeApi.formatAddress(sender) !== subBridgeApi.formatAddress(from)) return false;
+ if (subBridgeApi.formatAddress(recipient) !== subBridgeApi.formatAddress(to)) return false;
// asset check
if (assetId !== asset.externalAddress) return false;
// amount check
diff --git a/src/utils/ethers-util.ts b/src/utils/ethers-util.ts
index 3a11feb11..b64ccebe5 100644
--- a/src/utils/ethers-util.ts
+++ b/src/utils/ethers-util.ts
@@ -160,15 +160,9 @@ async function getAccount(): Promise {
return signer.getAddress();
}
-async function getContract(contractAddress: string, contractAbi: ethers.InterfaceAbi): Promise {
- const signer = await getSigner();
- const contract = new ethers.Contract(contractAddress, contractAbi, signer);
-
- return contract;
-}
-
async function getTokenContract(tokenAddress: string): Promise {
- const contract = await getContract(tokenAddress, SmartContracts[SmartContractType.ERC20]);
+ const signer = await getSigner();
+ const contract = new ethers.Contract(tokenAddress, SmartContracts[SmartContractType.ERC20].abi, signer);
return contract;
}
@@ -302,7 +296,7 @@ async function addToken(address: string, symbol: string, decimals: number, image
* @param chainName translated chain name
*/
async function switchOrAddChain(network: NetworkData, chainName?: string): Promise {
- const chainId = ethers.toQuantity(network.evmId ?? network.id);
+ const chainId = ethers.toQuantity(network.id);
try {
await ethereumProvider.request({
@@ -454,7 +448,6 @@ export default {
getAccount,
getAccountBalance,
getAccountAssetBalance,
- getContract,
getTokenContract,
getTokenDecimals,
getAllowance,
diff --git a/src/views/Bridge.vue b/src/views/Bridge.vue
index ffba73f6d..2ed68c431 100644
--- a/src/views/Bridge.vue
+++ b/src/views/Bridge.vue
@@ -68,15 +68,31 @@
/>
-
+
+
+
+
+
+
+
+
+ {{ t('changeAccountText') }}
+
+
+ {{ t('disconnectWalletText') }}
+
+
+
+