diff --git a/package.json b/package.json index 1e13f61b5..bf74eb4c3 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "polkaswap-exchange-web", - "version": "1.17.0", + "version": "1.18.0", "repository": { "type": "git", "url": "https://github.com/sora-xor/polkaswap-exchange-web.git" @@ -23,7 +23,7 @@ }, "dependencies": { "@metamask/detect-provider": "^2.0.0", - "@soramitsu/soraneo-wallet-web": "1.17.18", + "@soramitsu/soraneo-wallet-web": "1.18.1", "@walletconnect/web3-provider": "^1.8.0", "base-64": "^1.0.0", "core-js": "^3.26.0", diff --git a/public/env.json b/public/env.json index 1d212e6ca..2fc153aa1 100644 --- a/public/env.json +++ b/public/env.json @@ -38,7 +38,6 @@ "EVM_NETWORKS_IDS": [97, 1001], "SUB_NETWORKS": { "Rococo": "wss://ws.relaychain-node-1.r1.dev.sora2.soramitsu.co.jp", - "RococoKarura": "wss://ws.karura-node-1.c2.dev.sora2.soramitsu.co.jp", "RococoSora": "wss://ws.parachain-collator-1.c1.dev.sora2.soramitsu.co.jp" }, "ETH_BRIDGE": { diff --git a/src/components/mixins/AssetsSearchMixin.ts b/src/components/mixins/AssetsSearchMixin.ts index c100fb926..0a0bbbf10 100644 --- a/src/components/mixins/AssetsSearchMixin.ts +++ b/src/components/mixins/AssetsSearchMixin.ts @@ -2,8 +2,7 @@ import { Component, Mixins } from 'vue-property-decorator'; import SearchInputMixin from '@/components/mixins/SearchInputMixin'; -import type { RegisteredAccountAsset } from '@sora-substrate/util'; -import type { Asset, AccountAsset } from '@sora-substrate/util/build/assets/types'; +import type { Asset, AccountAsset, RegisteredAccountAsset } from '@sora-substrate/util/build/assets/types'; @Component export default class AssetsSearchMixin extends Mixins(SearchInputMixin) { diff --git a/src/components/mixins/BridgeMixin.ts b/src/components/mixins/BridgeMixin.ts index 74d2323f2..9f3a1a627 100644 --- a/src/components/mixins/BridgeMixin.ts +++ b/src/components/mixins/BridgeMixin.ts @@ -4,12 +4,14 @@ import { Component, Mixins } from 'vue-property-decorator'; import WalletConnectMixin from '@/components/mixins/WalletConnectMixin'; import { getter, mutation, state } from '@/store/decorators'; -import type { CodecString, RegisteredAccountAsset } from '@sora-substrate/util'; +import type { CodecString } from '@sora-substrate/util'; +import type { RegisteredAccountAsset } from '@sora-substrate/util/build/assets/types'; @Component export default class BridgeMixin extends Mixins(mixins.LoadingMixin, WalletConnectMixin) { @state.bridge.externalNativeBalance externalNativeBalance!: CodecString; @state.bridge.externalBlockNumber externalBlockNumber!: number; + @state.bridge.assetLockedBalance assetLockedBalance!: Nullable; @getter.web3.isValidNetwork isValidNetwork!: boolean; @getter.bridge.sender sender!: string; diff --git a/src/components/mixins/ExplorePageMixin.ts b/src/components/mixins/ExplorePageMixin.ts index 92019e953..46f229dc4 100644 --- a/src/components/mixins/ExplorePageMixin.ts +++ b/src/components/mixins/ExplorePageMixin.ts @@ -6,7 +6,7 @@ import { Component, Mixins, Prop, Ref, Watch } from 'vue-property-decorator'; import { getter } from '@/store/decorators'; -import type { RegisteredAccountAsset } from '@sora-substrate/util'; +import type { RegisteredAccountAsset } from '@sora-substrate/util/build/assets/types'; @Component export default class ExplorePageMixin extends Mixins( diff --git a/src/components/mixins/NetworkFormatterMixin.ts b/src/components/mixins/NetworkFormatterMixin.ts index 398336b30..bfd23ec95 100644 --- a/src/components/mixins/NetworkFormatterMixin.ts +++ b/src/components/mixins/NetworkFormatterMixin.ts @@ -77,7 +77,7 @@ export default class NetworkFormatterMixin extends Mixins(TranslationMixin) { return 'kusama'; case SubNetwork.Rococo: return 'rococo'; - case SubNetwork.RococoKarura: + case SubNetwork.KusamaKarura: return 'karura'; default: return 'ethereum'; diff --git a/src/components/mixins/SelectAssetMixin.ts b/src/components/mixins/SelectAssetMixin.ts index e7345d1dc..a41e847f6 100644 --- a/src/components/mixins/SelectAssetMixin.ts +++ b/src/components/mixins/SelectAssetMixin.ts @@ -5,8 +5,7 @@ import { Component, Mixins, Watch } from 'vue-property-decorator'; import AssetsSearchMixin from '@/components/mixins/AssetsSearchMixin'; import { getter } from '@/store/decorators'; -import type { RegisteredAccountAsset } from '@sora-substrate/util'; -import type { Asset, AccountAsset } from '@sora-substrate/util/build/assets/types'; +import type { Asset, AccountAsset, RegisteredAccountAsset } from '@sora-substrate/util/build/assets/types'; @Component export default class SelectAsset extends Mixins(mixins.DialogMixin, AssetsSearchMixin) { diff --git a/src/components/pages/Bridge/SelectAsset.vue b/src/components/pages/Bridge/SelectAsset.vue index 37b8d64fd..721651578 100644 --- a/src/components/pages/Bridge/SelectAsset.vue +++ b/src/components/pages/Bridge/SelectAsset.vue @@ -33,12 +33,11 @@ import SelectAssetMixin from '@/components/mixins/SelectAssetMixin'; import TranslationMixin from '@/components/mixins/TranslationMixin'; import { Components, ObjectInit } from '@/consts'; import { lazyComponent } from '@/router'; -import type { BridgeAccountAsset } from '@/store/assets/types'; +import type { BridgeRegisteredAsset } from '@/store/assets/types'; import { state, getter } from '@/store/decorators'; import type { NetworkData } from '@/types/bridge'; -import type { RegisteredAccountAsset } from '@sora-substrate/util'; -import type { AccountAsset } from '@sora-substrate/util/build/assets/types'; +import type { AccountAsset, RegisteredAccountAsset } from '@sora-substrate/util/build/assets/types'; @Component({ components: { @@ -51,7 +50,7 @@ export default class BridgeSelectAsset extends Mixins(TranslationMixin, SelectAs @Prop({ default: ObjectInit, type: Object }) readonly asset!: AccountAsset; @getter.web3.selectedNetwork private selectedNetwork!: Nullable; - @state.assets.registeredAssets private registeredAssets!: Record; + @state.assets.registeredAssets private registeredAssets!: Record; @state.bridge.isSoraToEvm isSoraToEvm!: boolean; @state.wallet.settings.shouldBalanceBeHidden shouldBalanceBeHidden!: boolean; diff --git a/src/components/pages/Bridge/TransferNotification.vue b/src/components/pages/Bridge/TransferNotification.vue index 1fe34719a..d27b46490 100644 --- a/src/components/pages/Bridge/TransferNotification.vue +++ b/src/components/pages/Bridge/TransferNotification.vue @@ -27,8 +27,8 @@ import { getter, state, mutation } from '@/store/decorators'; import { isOutgoingTransaction } from '@/utils/bridge/common/utils'; import ethersUtil from '@/utils/ethers-util'; -import type { IBridgeTransaction, RegisteredAccountAsset } from '@sora-substrate/util'; -import type { Whitelist } from '@sora-substrate/util/build/assets/types'; +import type { IBridgeTransaction } from '@sora-substrate/util'; +import type { Whitelist, RegisteredAccountAsset } from '@sora-substrate/util/build/assets/types'; @Component({ components: { diff --git a/src/components/pages/Moonpay/BridgeInitMixin.ts b/src/components/pages/Moonpay/BridgeInitMixin.ts index f76d72f11..0dd2430db 100644 --- a/src/components/pages/Moonpay/BridgeInitMixin.ts +++ b/src/components/pages/Moonpay/BridgeInitMixin.ts @@ -5,7 +5,7 @@ import { Component, Mixins } from 'vue-property-decorator'; import BridgeHistoryMixin from '@/components/mixins/BridgeHistoryMixin'; import WalletConnectMixin from '@/components/mixins/WalletConnectMixin'; import { MoonpayNotifications } from '@/components/pages/Moonpay/consts'; -import type { RegisteredAccountAssetObject, BridgeAccountAsset } from '@/store/assets/types'; +import type { BridgeRegisteredAsset } from '@/store/assets/types'; import { state, action, mutation, getter } from '@/store/decorators'; import type { BridgeTxData } from '@/store/moonpay/types'; import { getMaxValue, hasInsufficientEvmNativeTokenForFee } from '@/utils'; @@ -14,6 +14,7 @@ import type { MoonpayTransaction } from '@/utils/moonpay'; import { MoonpayEVMTransferAssetData, MoonpayApi } from '@/utils/moonpay'; import type { CodecString, BridgeHistory } from '@sora-substrate/util'; +import type { Asset, AccountBalance } from '@sora-substrate/util/build/assets/types'; import type { EvmNetwork } from '@sora-substrate/util/build/bridgeProxy/evm/types'; import type { WALLET_CONSTS } from '@soramitsu/soraneo-wallet-web'; @@ -29,10 +30,10 @@ export default class MoonpayBridgeInitMixin extends Mixins(BridgeHistoryMixin, W @state.moonpay.bridgeTransactionData bridgeTransactionData!: Nullable; @state.web3.ethBridgeEvmNetwork ethBridgeEvmNetwork!: EvmNetwork; @state.wallet.settings.soraNetwork soraNetwork!: Nullable; - @state.assets.registeredAssets private registeredAssets!: Record; + @state.assets.registeredAssets private registeredAssets!: Record; @getter.settings.moonpayApiKey moonpayApiKey!: string; - @getter.assets.assetsDataTable assetsDataTable!: RegisteredAccountAssetObject; + @getter.assets.assetsDataTable assetsDataTable!: Record; @mutation.moonpay.setConfirmationVisibility setConfirmationVisibility!: (flag: boolean) => void; @mutation.moonpay.setNotificationVisibility setNotificationVisibility!: (flag: boolean) => void; @@ -123,23 +124,25 @@ export default class MoonpayBridgeInitMixin extends Mixins(BridgeHistoryMixin, W ); } - const [soraAddress] = + const [soraAddress, registeredAsset] = Object.entries(this.registeredAssets).find(([soraAddress, registeredAsset]) => ethersUtil.addressesAreEqual(registeredAsset.address, ethTransferData.address) ) ?? []; - if (!soraAddress) { + if (!(soraAddress && registeredAsset)) { throw createError( `Asset is not registered: ethereum address ${ethTransferData.address}`, MoonpayNotifications.SupportError ); } - const asset = this.assetsDataTable[soraAddress]; - - const externalBalance = (await ethersUtil.getAccountAssetBalance(ethTransferData.to, asset.externalAddress)) + const externalBalance = (await ethersUtil.getAccountAssetBalance(ethTransferData.to, registeredAsset.address)) .value; - const evmNetworkFee: CodecString = await ethersUtil.getEvmNetworkFee(soraAddress, false); + const evmNetworkFee: CodecString = await ethersUtil.getEvmNetworkFee( + registeredAsset.address, + registeredAsset.kind, + false + ); const evmNativeBalance = await ethersUtil.getAccountBalance(ethTransferData.to); const hasEthForFee = !hasInsufficientEvmNativeTokenForFee(evmNativeBalance, evmNetworkFee); @@ -147,7 +150,14 @@ export default class MoonpayBridgeInitMixin extends Mixins(BridgeHistoryMixin, W throw createError('Insufficient ETH for fee', MoonpayNotifications.FeeError); } - const maxAmount = getMaxValue({ ...asset, externalBalance }, evmNetworkFee, true); // max balance (minus fee if eth asset) + const accountAsset = { + ...this.assetsDataTable[soraAddress], + balance: {} as AccountBalance, + externalBalance, + externalAddress: registeredAsset.address, + externalDecimals: registeredAsset.decimals, + }; + const maxAmount = getMaxValue(accountAsset, evmNetworkFee, true); // max balance (minus fee if eth asset) const amount = Math.min(Number(maxAmount), Number(ethTransferData.amount)); if (amount <= 0) { @@ -157,8 +167,8 @@ export default class MoonpayBridgeInitMixin extends Mixins(BridgeHistoryMixin, W return { type: Operation.EthBridgeIncoming, amount: String(amount), - symbol: asset.symbol, - assetAddress: asset.address, + symbol: accountAsset.symbol, + assetAddress: accountAsset.address, soraNetworkFee: this.networkFees[Operation.EthBridgeIncoming], externalNetworkFee: evmNetworkFee, externalNetwork: this.ethBridgeEvmNetwork, diff --git a/src/consts/sub.ts b/src/consts/sub.ts index 1fc5d4e77..f2c98be3f 100644 --- a/src/consts/sub.ts +++ b/src/consts/sub.ts @@ -39,8 +39,8 @@ export const SUB_NETWORKS: Partial> = { blockExplorerUrls: [], shortName: 'Rococo', }, - [SubNetwork.RococoKarura]: { - id: SubNetwork.RococoKarura, + [SubNetwork.KusamaKarura]: { + id: SubNetwork.KusamaKarura, name: 'Karura', nativeCurrency: { name: 'KAR', diff --git a/src/store/addLiquidity/getters.ts b/src/store/addLiquidity/getters.ts index bb3fe4f05..3c632c665 100644 --- a/src/store/addLiquidity/getters.ts +++ b/src/store/addLiquidity/getters.ts @@ -6,7 +6,7 @@ import { ZeroStringValue } from '@/consts'; import { addLiquidityGetterContext } from '@/store/addLiquidity'; import type { AddLiquidityState } from './types'; -import type { RegisteredAccountAsset } from '@sora-substrate/util'; +import type { RegisteredAccountAsset } from '@sora-substrate/util/build/assets/types'; import type { AccountLiquidity } from '@sora-substrate/util/build/poolXyk/types'; const getters = defineGetters()({ diff --git a/src/store/assets/actions.ts b/src/store/assets/actions.ts index 66ddbef0f..160f92799 100644 --- a/src/store/assets/actions.ts +++ b/src/store/assets/actions.ts @@ -1,86 +1,82 @@ import { BridgeNetworkType } from '@sora-substrate/util/build/bridgeProxy/consts'; -import { SubNetwork } from '@sora-substrate/util/build/bridgeProxy/sub/consts'; import { defineActions } from 'direct-vuex'; -import { ZeroStringValue } from '@/consts'; import { assetsActionContext } from '@/store/assets'; -import type { BridgeAccountAsset } from '@/store/assets/types'; +import type { BridgeRegisteredAsset } from '@/store/assets/types'; import { ethBridgeApi } from '@/utils/bridge/eth/api'; import { evmBridgeApi } from '@/utils/bridge/evm/api'; import { subBridgeApi } from '@/utils/bridge/sub/api'; import ethersUtil from '@/utils/ethers-util'; +import type { EvmNetwork } from '@sora-substrate/util/build/bridgeProxy/evm/types'; +import type { SubNetwork } from '@sora-substrate/util/build/bridgeProxy/sub/consts'; import type { ActionContext } from 'vuex'; -async function getEthRegisteredAssets(context: ActionContext): Promise[]> { +async function getEthRegisteredAssets( + context: ActionContext +): Promise[]> { const { rootDispatch } = assetsActionContext(context); const networkAssets = await ethBridgeApi.getRegisteredAssets(); - const registeredAssets: Record[] = []; - - for (const asset of networkAssets) { - const soraAddress = asset.address; - const address = asset.externalAddress || (await rootDispatch.web3.getEvmTokenAddressByAssetId(soraAddress)); - // [TODO] fix js-lib: externalDecimals = 0 by default, what could be with nft - const decimals = +asset.externalDecimals || (await ethersUtil.getAssetDecimals(address)); - - registeredAssets.push({ - [soraAddress]: { - address, - balance: ZeroStringValue, - decimals, - }, - }); - } + const registeredAssets = await Promise.all( + Object.entries(networkAssets).map(async ([soraAddress, assetData]) => { + const address = assetData.address || (await rootDispatch.web3.getEvmTokenAddressByAssetId(soraAddress)); + const decimals = assetData.decimals ?? (await ethersUtil.getAssetDecimals(address)); + + return { + [soraAddress]: { + address, + decimals, + kind: assetData.assetKind, + }, + }; + }) + ); return registeredAssets; } -async function getEvmRegisteredAssets(context: ActionContext): Promise[]> { +async function getEvmRegisteredAssets( + context: ActionContext +): Promise[]> { const { rootState } = assetsActionContext(context); - const evmNetworkId = rootState.web3.networkSelected; - const networkAssets = await evmBridgeApi.getRegisteredAssets(evmNetworkId as number); - + const evmNetwork = rootState.web3.networkSelected; + const networkAssets = await evmBridgeApi.getRegisteredAssets(evmNetwork as EvmNetwork); const registeredAssets = Object.entries(networkAssets).map(([soraAddress, assetData]) => { - const accountAsset = { - address: assetData.address as string, - balance: ZeroStringValue, - contract: '', // map with appKinds - decimals: assetData.decimals, - }; - return { - [soraAddress]: accountAsset, + [soraAddress]: { + address: assetData.address, + decimals: assetData.decimals, + kind: assetData.appKind, + }, }; }); return registeredAssets; } -async function getSubRegisteredAssets(context: ActionContext): Promise[]> { +async function getSubRegisteredAssets( + context: ActionContext +): Promise[]> { const { rootState } = assetsActionContext(context); const subNetwork = rootState.web3.networkSelected; - const networkAssets = await subBridgeApi.getRegisteredAssets(subNetwork as SubNetwork); - const registeredAssets = Object.entries(networkAssets).map(([soraAddress, assetData]) => { - const accountAsset = { - address: '', - balance: ZeroStringValue, - decimals: assetData.decimals, - }; - return { - [soraAddress]: accountAsset, + [soraAddress]: { + address: '', + decimals: assetData.decimals, + kind: assetData.assetKind, + }, }; }); return registeredAssets; } -async function getRegisteredAssets(context: ActionContext): Promise[]> { +async function getRegisteredAssets(context: ActionContext): Promise[]> { const { rootState } = assetsActionContext(context); switch (rootState.web3.networkType) { @@ -93,6 +89,8 @@ async function getRegisteredAssets(context: ActionContext): Promise()({ whitelistAssets(...args): Array { @@ -35,11 +35,7 @@ const getters = defineGetters()({ if (!asset) return null; - const { - address: externalAddress, - balance: externalBalance, - decimals: externalDecimals, - } = state.registeredAssets[asset.address] || {}; + const { address: externalAddress, decimals: externalDecimals } = state.registeredAssets[asset.address] || {}; const { balance } = rootGetters.wallet.account.accountAssetsAddressTable[asset.address] || {}; @@ -47,7 +43,7 @@ const getters = defineGetters()({ ...asset, balance, externalAddress, - externalBalance, + externalBalance: ZeroStringValue, // remove externalBalance externalDecimals, }; }; diff --git a/src/store/assets/mutations.ts b/src/store/assets/mutations.ts index b783fafe9..5d0fcc359 100644 --- a/src/store/assets/mutations.ts +++ b/src/store/assets/mutations.ts @@ -1,12 +1,12 @@ import { defineMutations } from 'direct-vuex'; -import type { AssetsState, BridgeAccountAsset } from './types'; +import type { AssetsState, BridgeRegisteredAsset } from './types'; const mutations = defineMutations()({ setRegisteredAssetsFetching(state, value: boolean): void { state.registeredAssetsFetching = value; }, - setRegisteredAssets(state, assets: Record): void { + setRegisteredAssets(state, assets: Record): void { state.registeredAssets = assets; state.registeredAssetsFetching = false; }, diff --git a/src/store/assets/types.ts b/src/store/assets/types.ts index ee294d4ec..10939cbc2 100644 --- a/src/store/assets/types.ts +++ b/src/store/assets/types.ts @@ -1,9 +1,9 @@ -import type { RegisteredAccountAsset, CodecString } from '@sora-substrate/util'; +import type { CodecString } from '@sora-substrate/util'; -export type BridgeAccountAsset = { +export type BridgeRegisteredAsset = { address: string; decimals: number; - balance: CodecString; + kind: string; contract?: string; }; @@ -13,11 +13,7 @@ export type SubAccountAsset = { balance: CodecString; }; -export type RegisteredAccountAssetObject = { - [key: string]: RegisteredAccountAsset; -}; - export type AssetsState = { - registeredAssets: Record; + registeredAssets: Record; registeredAssetsFetching: boolean; }; diff --git a/src/store/bridge/actions.ts b/src/store/bridge/actions.ts index d91f12891..40e1db8ca 100644 --- a/src/store/bridge/actions.ts +++ b/src/store/bridge/actions.ts @@ -1,4 +1,4 @@ -import { BridgeCurrencyType, BridgeHistory, FPNumber, Operation } from '@sora-substrate/util'; +import { BridgeCurrencyType, BridgeRequestAssetKind, BridgeHistory, FPNumber, Operation } from '@sora-substrate/util'; import { getAssetBalance } from '@sora-substrate/util/build/assets'; import { BridgeTxStatus, BridgeTxDirection, BridgeNetworkType } from '@sora-substrate/util/build/bridgeProxy/consts'; import { api, WALLET_CONSTS } from '@soramitsu/soraneo-wallet-web'; @@ -21,7 +21,8 @@ import { subConnector } from '@/utils/bridge/sub/classes/adapter'; import ethersUtil from '@/utils/ethers-util'; import type { SignTxResult } from './types'; -import type { IBridgeTransaction, RegisteredAccountAsset } from '@sora-substrate/util'; +import type { IBridgeTransaction } from '@sora-substrate/util'; +import type { RegisteredAccountAsset } from '@sora-substrate/util/build/assets/types'; import type { EvmHistory, EvmNetwork } from '@sora-substrate/util/build/bridgeProxy/evm/types'; import type { SubNetwork } from '@sora-substrate/util/build/bridgeProxy/sub/consts'; import type { SubHistory } from '@sora-substrate/util/build/bridgeProxy/sub/types'; @@ -145,23 +146,34 @@ function bridgeDataToHistoryItem( } async function getEvmNetworkFee(context: ActionContext): Promise { - const { getters, commit, state } = bridgeActionContext(context); - if (!getters.asset?.address) { - return; - } + const { commit, state, rootState } = bridgeActionContext(context); + + let fee = ZeroStringValue; - try { - const fee = await ethersUtil.getEvmNetworkFee(getters.asset.address, state.isSoraToEvm); - commit.setExternalNetworkFee(fee); - } catch (error) { - commit.setExternalNetworkFee(ZeroStringValue); + const address = state.assetAddress; + const registeredAsset = address ? rootState.assets.registeredAssets[address] : null; + + if (registeredAsset) { + fee = await ethersUtil.getEvmNetworkFee(registeredAsset.address, registeredAsset.kind, state.isSoraToEvm); } + + commit.setExternalNetworkFee(fee); } async function getSubNetworkFee(context: ActionContext): Promise { - const { commit } = bridgeActionContext(context); - // [TODO] fetch fee - commit.setExternalNetworkFee(ZeroStringValue); + const { commit, rootState } = bridgeActionContext(context); + + let fee = ZeroStringValue; + + const network = rootState.web3.networkSelected; + + if (network) { + const adapter = subConnector.getAdapterForNetwork(network as SubNetwork); + await adapter.connect(); + fee = await adapter.getNetworkFee(); + } + + commit.setExternalNetworkFee(fee); } async function updateEvmBalances(context: ActionContext): Promise { @@ -312,11 +324,48 @@ async function updateEvmHistory(context: ActionContext): Promise commit.setHistoryLoading(false); } +async function updateEthLockedBalance(context: ActionContext): Promise { + const { commit, getters, rootGetters, rootState } = bridgeActionContext(context); + const { address, externalAddress } = getters.asset || {}; + const bridgeContractAddress = rootGetters.web3.contractAddress(KnownEthBridgeAsset.Other); + + if (address && externalAddress && bridgeContractAddress) { + const assetKind = rootState.assets.registeredAssets[address]?.kind; + + if (assetKind === BridgeRequestAssetKind.Sidechain) { + const { value } = await ethersUtil.getAccountAssetBalance(bridgeContractAddress, externalAddress); + commit.setAssetLockedBalance(value); + return; + } + } + + commit.setAssetLockedBalance(); +} + +async function updateBridgeProxyLockedBalance(context: ActionContext): Promise { + const { commit, getters, rootState } = bridgeActionContext(context); + const { address } = getters.asset || {}; + const { networkSelected, networkType } = rootState.web3; + + if (address && networkSelected && networkType) { + const bridgeApi = getters.bridgeApi as typeof evmBridgeApi | typeof subBridgeApi; + const data = await bridgeApi.getLockedAssets(networkSelected as never, address); + const balance = data.toString(); + commit.setAssetLockedBalance(balance); + } + + commit.setAssetLockedBalance(); +} + const actions = defineActions({ async updateBalancesAndFees(context): Promise { const { dispatch } = bridgeActionContext(context); - await Promise.all([dispatch.updateExternalBalance(), dispatch.getExternalNetworkFee()]); + await Promise.all([ + dispatch.updateExternalBalance(), + dispatch.updateExternalLockedBalance(), + dispatch.getExternalNetworkFee(), + ]); }, async switchDirection(context): Promise { @@ -367,6 +416,20 @@ const actions = defineActions({ commit.setExternalBalancesFetching(false); }, + async updateExternalLockedBalance(context): Promise { + const { commit, getters } = bridgeActionContext(context); + + commit.setAssetLockedBalanceFetching(true); + + if (getters.isEthBridge) { + await updateEthLockedBalance(context); + } else { + await updateBridgeProxyLockedBalance(context); + } + + commit.setAssetLockedBalanceFetching(false); + }, + async updateExternalBlockNumber(context): Promise { const { getters, commit } = bridgeActionContext(context); diff --git a/src/store/bridge/getters.ts b/src/store/bridge/getters.ts index b87da02ec..702f6e7d1 100644 --- a/src/store/bridge/getters.ts +++ b/src/store/bridge/getters.ts @@ -9,7 +9,8 @@ import { evmBridgeApi } from '@/utils/bridge/evm/api'; import { subBridgeApi } from '@/utils/bridge/sub/api'; import type { BridgeState } from './types'; -import type { IBridgeTransaction, CodecString, RegisteredAccountAsset } from '@sora-substrate/util'; +import type { IBridgeTransaction, CodecString } from '@sora-substrate/util'; +import type { RegisteredAccountAsset } from '@sora-substrate/util/build/assets/types'; const getters = defineGetters()({ asset(...args): Nullable { @@ -94,10 +95,10 @@ const getters = defineGetters()({ const { state, getters } = bridgeGetterContext(args); if (getters.isEthBridge) { - return state.evmNetworkFee; + return state.externalNetworkFee; } else { // In direction SORA -> EVM evm network fee is 0 - return !state.isSoraToEvm ? state.evmNetworkFee : ZeroStringValue; + return !state.isSoraToEvm ? state.externalNetworkFee : ZeroStringValue; } }, history(...args): Record { diff --git a/src/store/bridge/mutations.ts b/src/store/bridge/mutations.ts index 8d9441694..b80f9c791 100644 --- a/src/store/bridge/mutations.ts +++ b/src/store/bridge/mutations.ts @@ -23,10 +23,16 @@ const mutations = defineMutations()({ state.assetRecipientBalance = balance; }, + setAssetLockedBalance(state, balance: Nullable = null): void { + state.assetLockedBalance = balance; + }, + setAssetLockedBalanceFetching(state, flag: boolean): void { + state.assetLockedBalanceFetching = flag; + }, + setExternalBalance(state, balance: CodecString = ZeroStringValue): void { state.externalNativeBalance = balance; }, - setExternalBalancesFetching(state, flag: boolean): void { state.externalBalancesFetching = flag; }, @@ -40,7 +46,7 @@ const mutations = defineMutations()({ }, setExternalNetworkFee(state, fee: CodecString): void { - state.evmNetworkFee = fee; + state.externalNetworkFee = fee; }, /** diff --git a/src/store/bridge/state.ts b/src/store/bridge/state.ts index 8d614aa29..ed0ea79ca 100644 --- a/src/store/bridge/state.ts +++ b/src/store/bridge/state.ts @@ -8,8 +8,10 @@ function initialState(): BridgeState { assetAddress: '', assetSenderBalance: ZeroStringValue, // balance for sora assetRecipientBalance: ZeroStringValue, // balance for bridge network + assetLockedBalance: null, // asset balance locked on bridge + assetLockedBalanceFetching: false, amount: '', - evmNetworkFee: ZeroStringValue, + externalNetworkFee: ZeroStringValue, externalNetworkFeeFetching: false, externalNativeBalance: ZeroStringValue, // balance for external native token (like ETH) externalBalancesFetching: false, diff --git a/src/store/bridge/types.ts b/src/store/bridge/types.ts index 4ee329300..be333ef52 100644 --- a/src/store/bridge/types.ts +++ b/src/store/bridge/types.ts @@ -5,8 +5,10 @@ export type BridgeState = { assetAddress: string; assetSenderBalance: CodecString; assetRecipientBalance: CodecString; + assetLockedBalance: Nullable; + assetLockedBalanceFetching: boolean; amount: string; - evmNetworkFee: CodecString; + externalNetworkFee: CodecString; externalNetworkFeeFetching: boolean; externalNativeBalance: CodecString; externalBalancesFetching: boolean; diff --git a/src/store/removeLiquidity/getters.ts b/src/store/removeLiquidity/getters.ts index 688b419a3..2fae226d6 100644 --- a/src/store/removeLiquidity/getters.ts +++ b/src/store/removeLiquidity/getters.ts @@ -6,7 +6,7 @@ import { ZeroStringValue } from '@/consts'; import { removeLiquidityGetterContext } from '@/store/removeLiquidity'; import type { RemoveLiquidityState } from './types'; -import type { RegisteredAccountAsset } from '@sora-substrate/util'; +import type { RegisteredAccountAsset } from '@sora-substrate/util/build/assets/types'; import type { AccountLiquidity } from '@sora-substrate/util/build/poolXyk/types'; const getters = defineGetters()({ diff --git a/src/store/swap/getters.ts b/src/store/swap/getters.ts index 85e06b4d8..33077f7a6 100644 --- a/src/store/swap/getters.ts +++ b/src/store/swap/getters.ts @@ -12,7 +12,8 @@ import { import { swapGetterContext } from '@/store/swap'; import type { SwapState } from './types'; -import type { CodecString, RegisteredAccountAsset } from '@sora-substrate/util'; +import type { CodecString } from '@sora-substrate/util'; +import type { RegisteredAccountAsset } from '@sora-substrate/util/build/assets/types'; const getters = defineGetters()({ tokenFrom(...args): Nullable { diff --git a/src/store/web3/actions.ts b/src/store/web3/actions.ts index 94e88227f..fd7363a24 100644 --- a/src/store/web3/actions.ts +++ b/src/store/web3/actions.ts @@ -101,27 +101,26 @@ const actions = defineActions({ }); }, - async restoreSelectedNetwork(context): Promise { - const { commit, getters, state } = web3ActionContext(context); - - if (getters.selectedNetwork) return; - - const selectedNetworkId = ethersUtil.getSelectedNetwork() ?? state.ethBridgeEvmNetwork; - - commit.setSelectedNetwork(selectedNetworkId); - }, - /** - * Restore selected by user network type (Hashi, EVM, Substrate) + * Restore selected by user network & network type (EVMLegacy, EVM, Sub) */ - async restoreNetworkType(context): Promise { - const { commit, state } = web3ActionContext(context); + async restoreSelectedNetwork(context): Promise { + const { commit, state, getters } = web3ActionContext(context); + + const [type, id] = [ethersUtil.getSelectedBridgeType(), ethersUtil.getSelectedNetwork()]; - if (state.networkType) return; + if (type && id) { + const networkData = getters.availableNetworks[type]?.[id]; - const networkType = ethersUtil.getSelectedBridgeType() ?? BridgeNetworkType.EvmLegacy; + if (!!networkData && !networkData.disabled) { + commit.setNetworkType(type); + commit.setSelectedNetwork(id); + return; + } + } - commit.setNetworkType(networkType); + commit.setNetworkType(BridgeNetworkType.EvmLegacy); + commit.setSelectedNetwork(state.ethBridgeEvmNetwork); }, async getEvmTokenAddressByAssetId(context, soraAssetId: string): Promise { diff --git a/src/store/web3/state.ts b/src/store/web3/state.ts index 29338e72f..febd3e895 100644 --- a/src/store/web3/state.ts +++ b/src/store/web3/state.ts @@ -11,7 +11,7 @@ export function initialState(): Web3State { evmAddress: '', // external evm address subAddress: '', // external sub address - networkType: ethersUtil.getSelectedBridgeType() ?? BridgeNetworkType.EvmLegacy, + networkType: null, // network type for selected network networkSelected: null, // network selected by user evmNetworkProvided: null, // evm network in provider diff --git a/src/store/web3/types.ts b/src/store/web3/types.ts index e1583ecc7..5457d36a5 100644 --- a/src/store/web3/types.ts +++ b/src/store/web3/types.ts @@ -27,7 +27,7 @@ export type Web3State = { evmAddress: string; subAddress: string; - networkType: BridgeNetworkType; + networkType: Nullable; networkSelected: Nullable; evmNetworkProvided: Nullable; diff --git a/src/utils/bridge/common/classes.ts b/src/utils/bridge/common/classes.ts index 5d9e751fc..03bc1fd67 100644 --- a/src/utils/bridge/common/classes.ts +++ b/src/utils/bridge/common/classes.ts @@ -1,3 +1,6 @@ +import { WALLET_CONSTS } from '@soramitsu/soraneo-wallet-web'; + +import { delay } from '@/utils'; import type { SignExternal, SignSora, @@ -19,6 +22,8 @@ import type { import type { IBridgeTransaction } from '@sora-substrate/util'; +const { BLOCK_PRODUCE_TIME } = WALLET_CONSTS; + export class BridgeReducer implements IBridgeReducer { protected readonly signExternal!: SignExternal; protected readonly signSora!: SignSora; @@ -147,6 +152,41 @@ export class BridgeReducer implements IB this.addTransactionToProgress(id); } + + async waitForTransactionStatus(id: string): Promise { + const { status } = this.getTransaction(id); + + if (status) return; + + await delay(1_000); + await this.waitForTransactionStatus(id); + } + + private async checkTransactionBlockId(id: string): Promise { + const { blockId } = this.getTransaction(id); + + if (blockId) return; + + await delay(1_000); + await this.checkTransactionBlockId(id); + } + + async waitForTransactionBlockId(id: string): Promise { + const { txId } = this.getTransaction(id); + + if (!txId) { + throw new Error(`[${this.constructor.name}]: Transaction "id" is empty, first sign the transaction`); + } + + try { + await Promise.race([ + this.checkTransactionBlockId(id), + new Promise((resolve, reject) => setTimeout(reject, BLOCK_PRODUCE_TIME * 3)), + ]); + } catch (error) { + console.info(`[${this.constructor.name}]: Implement "blockId" restoration by "txId"`); + } + } } export class Bridge< diff --git a/src/utils/bridge/common/types.ts b/src/utils/bridge/common/types.ts index 4014b3b9d..a695c459d 100644 --- a/src/utils/bridge/common/types.ts +++ b/src/utils/bridge/common/types.ts @@ -1,4 +1,5 @@ -import type { IBridgeTransaction, RegisteredAccountAsset } from '@sora-substrate/util'; +import type { IBridgeTransaction } from '@sora-substrate/util'; +import type { RegisteredAccountAsset } from '@sora-substrate/util/build/assets/types'; export type AddAsset = (address: string) => Promise; export type GetAssetByAddress = (address: string) => Nullable; diff --git a/src/utils/bridge/common/utils.ts b/src/utils/bridge/common/utils.ts index 13352020b..6374dd496 100644 --- a/src/utils/bridge/common/utils.ts +++ b/src/utils/bridge/common/utils.ts @@ -1,20 +1,17 @@ import { Operation, isBridgeOperation, isEvmOperation, isSubstrateOperation } from '@sora-substrate/util'; -import { api, WALLET_CONSTS } from '@soramitsu/soraneo-wallet-web'; +import { api } from '@soramitsu/soraneo-wallet-web'; import { ethers } from 'ethers'; -import { delay } from '@/utils'; -import type { GetTransaction } from '@/utils/bridge/common/types'; 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'; import type { IBridgeTransaction, BridgeHistory } from '@sora-substrate/util'; import type { EvmHistory } from '@sora-substrate/util/build/bridgeProxy/evm/types'; import type { SubHistory } from '@sora-substrate/util/build/bridgeProxy/sub/types'; -const { BLOCK_PRODUCE_TIME } = WALLET_CONSTS; // Block production time - export const waitForEvmTransactionStatus = async ( hash: string, replaceCallback: (hash: string) => any, @@ -104,53 +101,26 @@ export const findUserTxIdInBlock = async ( return userExtrinsic.hash.toString(); }; -export const waitForSoraTransactionHash = - (options: { - section: string; - method: string; - eventMethod: string; - eventSection?: string; - }) => - async (id: string, getTransaction: GetTransaction): Promise => { - const { blockId, status, from } = getTransaction(id); - - if (status && blockId) { - const extrinsics = await api.system.getExtrinsicsFromBlock(blockId); - - if (extrinsics.length) { - const extrinsicIndex = extrinsics.findIndex((item) => { - const { - signer, - method: { method, section }, - } = item; - - return signer.toString() === from && section === options.section && method === options.method; - }); - - if (!Number.isFinite(extrinsicIndex)) throw new Error('[Bridge]: Transaction was failed'); - - const blockEvents = await api.system.getBlockEvents(blockId); - - const event = blockEvents.find( - ({ phase, event }) => - phase.isApplyExtrinsic && - phase.asApplyExtrinsic.eq(extrinsicIndex) && - event.section === (options.eventSection ?? options.section) && - event.method === options.eventMethod - ); - - if (!event) { - throw new Error('[Bridge]: Transaction was failed'); - } - - return event.event.data; - } - } - - await delay(BLOCK_PRODUCE_TIME); - - return await waitForSoraTransactionHash(options)(id, getTransaction); - }; +export const findEventInBlock = async ({ + api, + blockId, + section, + method, +}: { + api: ApiPromise; + blockId: string; + section: string; + method: string; +}) => { + const apiInstanceAtBlock = await api.at(blockId); + const blockEvents = (await apiInstanceAtBlock.query.system.events()).toArray(); + + const event = blockEvents.find(({ event }) => event.section === section && event.method === method); + + if (!event) throw new Error('Event not found'); + + return event.event.data; +}; export const isOutgoingTransaction = (transaction: Nullable): boolean => { if (!transaction?.type) return false; diff --git a/src/utils/bridge/eth/classes/reducers.ts b/src/utils/bridge/eth/classes/reducers.ts index 5099bb558..c196ee4d7 100644 --- a/src/utils/bridge/eth/classes/reducers.ts +++ b/src/utils/bridge/eth/classes/reducers.ts @@ -1,10 +1,10 @@ -import { SUBQUERY_TYPES, WALLET_CONSTS } from '@soramitsu/soraneo-wallet-web'; +import { api, SUBQUERY_TYPES, WALLET_CONSTS } from '@soramitsu/soraneo-wallet-web'; import { ethers } from 'ethers'; import first from 'lodash/fp/first'; import { BridgeReducer } from '@/utils/bridge/common/classes'; import type { IBridgeReducerOptions, GetBridgeHistoryInstance } from '@/utils/bridge/common/types'; -import { getEvmTransactionRecieptByHash, waitForSoraTransactionHash } from '@/utils/bridge/common/utils'; +import { getEvmTransactionRecieptByHash, findEventInBlock } from '@/utils/bridge/common/utils'; import { ethBridgeApi } from '@/utils/bridge/eth/api'; import type { EthBridgeHistory } from '@/utils/bridge/eth/history'; import { @@ -139,11 +139,17 @@ export class EthBridgeOutgoingReducer extends EthBridgeReducer { nextState: ETH_BRIDGE_STATES.EVM_SUBMITTED, rejectState: ETH_BRIDGE_STATES.SORA_REJECTED, handler: async (id: string) => { - const eventData = await waitForSoraTransactionHash({ + await this.waitForTransactionStatus(id); + await this.waitForTransactionBlockId(id); + + const { blockId } = this.getTransaction(id); + + const eventData = await findEventInBlock({ + api: api.api, + blockId: blockId as string, section: 'ethBridge', - method: 'transferToSidechain', - eventMethod: 'RequestRegistered', - })(id, this.getTransaction); + method: 'RequestRegistered', + }); const hash = eventData[0].toString(); @@ -231,7 +237,7 @@ export class EthBridgeIncomingReducer extends EthBridgeReducer { case ETH_BRIDGE_STATES.SORA_REJECTED: { return await this.handleState(transaction.id, { - nextState: ETH_BRIDGE_STATES.SORA_SUBMITTED, + nextState: ETH_BRIDGE_STATES.SORA_PENDING, rejectState: ETH_BRIDGE_STATES.SORA_REJECTED, }); } diff --git a/src/utils/bridge/evm/classes/reducers.ts b/src/utils/bridge/evm/classes/reducers.ts index 70ee678ff..f8dd85bcc 100644 --- a/src/utils/bridge/evm/classes/reducers.ts +++ b/src/utils/bridge/evm/classes/reducers.ts @@ -1,10 +1,8 @@ import { BridgeTxStatus } from '@sora-substrate/util/build/bridgeProxy/consts'; -import { WALLET_CONSTS } from '@soramitsu/soraneo-wallet-web'; -import { delay } from '@/utils'; import { BridgeReducer } from '@/utils/bridge/common/classes'; import type { RemoveTransactionByHash, IBridgeReducerOptions } from '@/utils/bridge/common/types'; -import { waitForSoraTransactionHash } from '@/utils/bridge/common/utils'; +import { findEventInBlock } from '@/utils/bridge/common/utils'; import { evmBridgeApi } from '@/utils/bridge/evm/api'; import type { EvmHistory } from '@sora-substrate/util/build/bridgeProxy/evm/types'; @@ -14,8 +12,6 @@ type EvmBridgeReducerOptions = IBridgeReducerOptions & removeTransactionByHash: RemoveTransactionByHash; }; -const { BLOCK_PRODUCE_TIME } = WALLET_CONSTS; - export class EvmBridgeReducer extends BridgeReducer { protected readonly removeTransactionByHash!: RemoveTransactionByHash; @@ -65,7 +61,7 @@ export class EvmBridgeOutgoingReducer extends EvmBridgeReducer { this.beforeSubmit(currentId); this.updateTransactionParams(currentId, { transactionState: BridgeTxStatus.Pending }); await this.checkTxId(currentId); - await this.checkTxBlockId(currentId); + await this.waitForTransactionBlockId(currentId); currentId = await this.checkTxSoraHash(currentId); await this.subscribeOnTxBySoraHash(currentId); @@ -94,42 +90,17 @@ export class EvmBridgeOutgoingReducer extends EvmBridgeReducer { } } - private async checkTxBlockId(id: string): Promise { - const { txId } = this.getTransaction(id); - - if (!txId) { - throw new Error(`[${this.constructor.name}]: Transaction "id" is empty, first sign the transaction`); - } - - try { - await Promise.race([ - this.waitForSoraBlockId(id), - new Promise((resolve, reject) => setTimeout(reject, BLOCK_PRODUCE_TIME * 3)), - ]); - } catch (error) { - console.info(`[${this.constructor.name}]: Implement "blockId" restoration`); - } - } - - private async waitForSoraBlockId(id: string): Promise { - const { blockId } = this.getTransaction(id); - - if (blockId) return Promise.resolve(); - - await delay(1_000); - await this.waitForSoraBlockId(id); - } - private async checkTxSoraHash(id: string): Promise { const tx = this.getTransaction(id); if (tx.hash) return tx.hash; - const eventData = await waitForSoraTransactionHash({ + const eventData = await findEventInBlock({ + api: evmBridgeApi.api, + blockId: tx.blockId as string, section: 'bridgeProxy', - method: 'burn', - eventMethod: 'RequestStatusUpdate', - })(id, this.getTransaction); + method: 'RequestStatusUpdate', + }); const hash = eventData[0].toString(); diff --git a/src/utils/bridge/sub/classes/adapter.ts b/src/utils/bridge/sub/classes/adapter.ts index 0ae8d5eed..6ec433537 100644 --- a/src/utils/bridge/sub/classes/adapter.ts +++ b/src/utils/bridge/sub/classes/adapter.ts @@ -8,7 +8,8 @@ import { ZeroStringValue } from '@/consts'; import { subBridgeApi } from '@/utils/bridge/sub/api'; import type { ApiPromise } from '@polkadot/api'; -import type { RegisteredAsset, CodecString } from '@sora-substrate/util'; +import type { CodecString } from '@sora-substrate/util'; +import type { RegisteredAsset } from '@sora-substrate/util/build/assets/types'; class SubAdapter { protected endpoint!: string; @@ -73,6 +74,11 @@ class SubAdapter { public async transfer(asset: RegisteredAsset, recipient: string, amount: string | number, historyId?: string) { console.info(`[${this.constructor.name}] transfer method is not implemented`); } + + public async getNetworkFee(): Promise { + console.info(`[${this.constructor.name}] getNetworkFee method is not implemented`); + return ZeroStringValue; + } } class RococoAdapter extends SubAdapter { @@ -80,26 +86,15 @@ class RococoAdapter extends SubAdapter { return await this.getAccountBalance(accountAddress); } - public async transfer(asset: RegisteredAsset, recipient: string, amount: string | number, historyId?: string) { - const value = new FPNumber(amount, asset.externalDecimals).toCodecString(); - - const historyItem = subBridgeApi.getHistory(historyId as string) || { - type: Operation.SubstrateIncoming, - symbol: asset.symbol, - assetAddress: asset.address, - amount: `${amount}`, - externalNetwork: SubNetwork.Rococo, - externalNetworkType: BridgeNetworkType.Sub, - }; - - const extrinsic = this.api.tx.xcmPallet.reserveTransferAssets( + protected getTransferExtrinsic(value: CodecString, recipient: string) { + return this.api.tx.xcmPallet.reserveTransferAssets( // dest { V3: { parents: 0, interior: { X1: { - Parachain: 2011, + Parachain: subBridgeApi.parachainIds[SubNetwork.RococoSora], }, }, }, @@ -136,12 +131,43 @@ class RococoAdapter extends SubAdapter { // feeAssetItem 0 ); + } + + public async getNetworkFee(): Promise { + /* Runtime call transactionPaymentApi not works, not decorated? */ + + // try { + // const tx = this.getTransferExtrinsic(ZeroStringValue, ''); + // const res = await tx.paymentInfo(''); + + // return new FPNumber(res.partialFee, 12).toCodecString(); + // } catch (error) { + // console.error(error); + // return ZeroStringValue; + // } + + return ZeroStringValue; + } + + public async transfer(asset: RegisteredAsset, recipient: string, amount: string | number, historyId?: string) { + const value = new FPNumber(amount, asset.externalDecimals).toCodecString(); + + const historyItem = subBridgeApi.getHistory(historyId as string) || { + type: Operation.SubstrateIncoming, + symbol: asset.symbol, + assetAddress: asset.address, + amount: `${amount}`, + externalNetwork: SubNetwork.Rococo, + externalNetworkType: BridgeNetworkType.Sub, + }; + + const extrinsic = this.getTransferExtrinsic(value, recipient); await subBridgeApi.submitApiExtrinsic(this.api, extrinsic, subBridgeApi.account.pair, historyItem); } } -class RococoKaruraAdapter extends SubAdapter { +class KusamaKaruraAdapter extends SubAdapter { // [TODO] fetch balance by symbol public async getTokenBalance(accountAddress: string, tokenAddress?: string): Promise { return await this.getAccountBalance(accountAddress); @@ -155,7 +181,7 @@ class RococoKaruraAdapter extends SubAdapter { symbol: asset.symbol, assetAddress: asset.address, amount: `${amount}`, - externalNetwork: SubNetwork.RococoKarura, + externalNetwork: SubNetwork.KusamaKarura, externalNetworkType: BridgeNetworkType.Sub, }; @@ -173,7 +199,7 @@ class RococoKaruraAdapter extends SubAdapter { interior: { X2: [ { - Parachain: 2011, + Parachain: subBridgeApi.parachainIds[SubNetwork.RococoSora], }, { AccountId32: { @@ -196,7 +222,7 @@ class SubConnector { public readonly adapters = { [SubNetwork.Rococo]: new RococoAdapter(), [SubNetwork.RococoSora]: new SubAdapter(), - [SubNetwork.RococoKarura]: new RococoKaruraAdapter(), + [SubNetwork.KusamaKarura]: new KusamaKaruraAdapter(), }; public adapter: SubAdapter = this.adapters[SubNetwork.Rococo]; diff --git a/src/utils/bridge/sub/classes/reducers.ts b/src/utils/bridge/sub/classes/reducers.ts index f11469918..fc2a6b9b6 100644 --- a/src/utils/bridge/sub/classes/reducers.ts +++ b/src/utils/bridge/sub/classes/reducers.ts @@ -1,16 +1,15 @@ +import { FPNumber } from '@sora-substrate/util'; import { BridgeTxStatus } from '@sora-substrate/util/build/bridgeProxy/consts'; import { SubNetwork } from '@sora-substrate/util/build/bridgeProxy/sub/consts'; -import { WALLET_CONSTS } from '@soramitsu/soraneo-wallet-web'; import { combineLatest } from 'rxjs'; -import { delay } from '@/utils'; +import { ZeroStringValue } from '@/consts'; import { BridgeReducer } from '@/utils/bridge/common/classes'; import type { RemoveTransactionByHash, IBridgeReducerOptions } from '@/utils/bridge/common/types'; -import { waitForSoraTransactionHash } from '@/utils/bridge/common/utils'; +import { findEventInBlock } from '@/utils/bridge/common/utils'; +import { subBridgeApi } from '@/utils/bridge/sub/api'; import { subConnector } from '@/utils/bridge/sub/classes/adapter'; -import { subBridgeApi } from '../api'; - import type { SubHistory } from '@sora-substrate/util/build/bridgeProxy/sub/types'; import type { Subscription } from 'rxjs'; @@ -18,8 +17,6 @@ type SubBridgeReducerOptions = IBridgeReducerOptions & removeTransactionByHash: RemoveTransactionByHash; }; -const { BLOCK_PRODUCE_TIME } = WALLET_CONSTS; - export class SubBridgeReducer extends BridgeReducer { protected readonly removeTransactionByHash!: RemoveTransactionByHash; @@ -29,39 +26,11 @@ export class SubBridgeReducer extends BridgeReducer { this.removeTransactionByHash = options.removeTransactionByHash; } - protected async waitForTxStatus(id: string): Promise { - const { status } = this.getTransaction(id); - - if (status) return Promise.resolve(); - - await delay(1_000); - await this.waitForTxStatus(id); - } - - protected async waitForTxBlockId(id: string): Promise { - const { blockId } = this.getTransaction(id); - - if (blockId) return Promise.resolve(); + async getAssetExternalDecimals(externalNetwork: SubNetwork, soraAssetId: string): Promise { + const data = await subBridgeApi.api.query.substrateBridgeApp.sidechainPrecision(externalNetwork, soraAssetId); + const decimals = data.unwrap().toNumber(); - await delay(1_000); - await this.waitForTxBlockId(id); - } - - protected async checkTxBlockId(id: string): Promise { - const { txId } = this.getTransaction(id); - - if (!txId) { - throw new Error(`[${this.constructor.name}]: Transaction "id" is empty, first sign the transaction`); - } - - try { - await Promise.race([ - this.waitForTxBlockId(id), - new Promise((resolve, reject) => setTimeout(reject, BLOCK_PRODUCE_TIME * 3)), - ]); - } catch (error) { - console.info(`[${this.constructor.name}]: Implement "blockId" restoration`); - } + return decimals; } } @@ -106,9 +75,40 @@ export class SubBridgeIncomingReducer extends SubBridgeReducer { this.updateHistory(); } + private async updateExternalNetworkFee(id: string): Promise { + const tx = this.getTransaction(id); + const network = tx.externalNetwork as SubNetwork; + const blockId = tx.externalBlockId as string; + const adapter = subConnector.getAdapterForNetwork(network); + + if (!adapter.connected) { + await adapter.connect(); + } + + const eventData = await findEventInBlock({ + api: adapter.api, + blockId, + section: 'transactionPayment', + method: 'TransactionFeePaid', + }); + + const decimals = adapter.api.registry.chainDecimals[0]; + + if (subConnector.adapter !== adapter) { + await adapter.stop(); + } + + // expecting that prev fee is 0 + const prevFee = FPNumber.fromCodecValue(tx.externalNetworkFee ?? ZeroStringValue, decimals); + const txFee = FPNumber.fromCodecValue(eventData[1].toString(), decimals); + const externalNetworkFee = txFee.add(prevFee).toCodecString(); + + this.updateTransactionParams(id, { externalNetworkFee }); + } + private async updateTxIncomingData(id: string): Promise { - await this.waitForTxStatus(id); - await this.checkTxBlockId(id); + await this.waitForTransactionStatus(id); + await this.waitForTransactionBlockId(id); const { txId, blockId } = this.getTransaction(id); @@ -116,6 +116,8 @@ export class SubBridgeIncomingReducer extends SubBridgeReducer { externalHash: txId, // parachain tx hash externalBlockId: blockId, // parachain block hash }); + + await this.updateExternalNetworkFee(id); } private async waitForCollatorMessageNonce(id: string): Promise { @@ -128,6 +130,7 @@ export class SubBridgeIncomingReducer extends SubBridgeReducer { let subscription!: Subscription; let messageNonce!: number; let batchNonce!: number; + let recipientAmount!: number; try { await collator.connect(); @@ -136,27 +139,28 @@ export class SubBridgeIncomingReducer extends SubBridgeReducer { const eventsObservable = collator.apiRx.query.system.events(); subscription = eventsObservable.subscribe((events) => { - const messageAcceptedEvent = events.find( - (e) => - e.phase.isApplyExtrinsic && - e.event.section === 'substrateBridgeOutboundChannel' && - e.event.method === 'MessageAccepted' + const messageAcceptedEvent = events.find((e) => + collator.api.events.substrateBridgeOutboundChannel.MessageAccepted.is(e.event) ); if (!messageAcceptedEvent) return; - const assetAddedToChannelEvent = events.find( - (e) => e.phase.isApplyExtrinsic && e.event.section === 'xcmApp' && e.event.method === 'AssetAddedToChannel' + const assetAddedToChannelEvent = events.find((e) => + collator.api.events.xcmApp.AssetAddedToChannel.is(e.event) ); if (!assetAddedToChannelEvent) { reject(new Error(`[${this.constructor.name}]: Unable to find "xcmApp.AssetAddedToChannel" event`)); } - // [TODO]: check assetAddedToChannelEvent data + const { amount, recipient } = assetAddedToChannelEvent.event.data[0].asTransfer; + const address = subBridgeApi.formatAddress(recipient.toString()); + + if (address !== tx.to) return; batchNonce = messageAcceptedEvent.event.data[1].toNumber(); messageNonce = messageAcceptedEvent.event.data[2].toNumber(); + recipientAmount = amount.toNumber(); resolve(); }); @@ -167,9 +171,28 @@ export class SubBridgeIncomingReducer extends SubBridgeReducer { await collator.stop(); - const payload = { ...tx.payload, messageNonce, batchNonce }; + const { + amount, + assetAddress, + externalNetwork, + externalNetworkFee: prevFee, + payload: prevPayload, + } = this.getTransaction(id); - this.updateTransactionParams(id, { payload }); + const decimals = await this.getAssetExternalDecimals(externalNetwork as SubNetwork, assetAddress as string); + + const sended = new FPNumber(amount as string); + const received = FPNumber.fromCodecValue(recipientAmount, decimals); + const amount2 = received.toString(); + + const xcmFee = sended.sub(received); + const externalNetworkFee = FPNumber.fromCodecValue(prevFee as string, decimals) + .add(xcmFee) + .toCodecString(); + + const payload = { ...prevPayload, messageNonce, batchNonce }; + + this.updateTransactionParams(id, { amount2, externalNetworkFee, payload }); } private async waitForSoraInboundMessageNonce(id: string): Promise { @@ -188,11 +211,8 @@ export class SubBridgeIncomingReducer extends SubBridgeReducer { const eventsObservable = subBridgeApi.apiRx.query.system.events(); subscription = eventsObservable.subscribe((events) => { - const substrateDispatchEvent = events.find( - (e) => - e.phase.isApplyExtrinsic && - e.event.section === 'substrateDispatch' && - e.event.method === 'MessageDispatched' + const substrateDispatchEvent = events.find((e) => + subBridgeApi.api.events.substrateDispatch.MessageDispatched.is(e.event) ); if (!substrateDispatchEvent) return; @@ -206,9 +226,8 @@ export class SubBridgeIncomingReducer extends SubBridgeReducer { if (eventBatchNonce !== batchNonce || eventMessageNonce !== messageNonce) return; - const bridgeProxyEvent = events.find( - (e) => - e.phase.isApplyExtrinsic && e.event.section === 'bridgeProxy' && e.event.method === 'RequestStatusUpdate' + const bridgeProxyEvent = events.find((e) => + subBridgeApi.api.events.bridgeProxy.RequestStatusUpdate.is(e.event) ); if (!bridgeProxyEvent) { @@ -274,8 +293,8 @@ export class SubBridgeOutgoingReducer extends SubBridgeReducer { this.beforeSubmit(id); this.updateTransactionParams(id, { transactionState: BridgeTxStatus.Pending }); await this.checkTxId(id); - await this.waitForTxStatus(id); - await this.checkTxBlockId(id); + await this.waitForTransactionStatus(id); + await this.waitForTransactionBlockId(id); await this.checkTxSoraHash(id); await this.waitForSoraOutboundMessageNonce(id); await this.waitForCollatorMessageHash(id); @@ -309,11 +328,12 @@ export class SubBridgeOutgoingReducer extends SubBridgeReducer { if (tx.hash) return; - const eventData = await waitForSoraTransactionHash({ + const eventData = await findEventInBlock({ + api: subBridgeApi.api, + blockId: tx.blockId as string, section: 'bridgeProxy', - method: 'burn', - eventMethod: 'RequestStatusUpdate', - })(id, this.getTransaction); + method: 'RequestStatusUpdate', + }); const hash = eventData[0].toString(); @@ -325,12 +345,12 @@ export class SubBridgeOutgoingReducer extends SubBridgeReducer { if (tx.payload?.messageNonce) return; - const eventData = await waitForSoraTransactionHash({ - section: 'bridgeProxy', - method: 'burn', - eventSection: 'substrateBridgeOutboundChannel', - eventMethod: 'MessageAccepted', - })(id, this.getTransaction); + const eventData = await findEventInBlock({ + api: subBridgeApi.api, + blockId: tx.blockId as string, + section: 'substrateBridgeOutboundChannel', + method: 'MessageAccepted', + }); const batchNonce = eventData[1].toNumber(); const messageNonce = eventData[2].toNumber(); @@ -359,11 +379,8 @@ export class SubBridgeOutgoingReducer extends SubBridgeReducer { const eventsObservable = collator.apiRx.query.system.events(); subscription = eventsObservable.subscribe((events) => { - const substrateDispatchEvent = events.find( - (e) => - e.phase.isApplyExtrinsic && - e.event.section === 'substrateDispatch' && - e.event.method === 'MessageDispatched' + const substrateDispatchEvent = events.find((e) => + collator.api.events.substrateDispatch.MessageDispatched.is(e.event) ); if (!substrateDispatchEvent) return; @@ -381,11 +398,8 @@ export class SubBridgeOutgoingReducer extends SubBridgeReducer { if (eventBatchNonce !== batchNonce || eventMessageNonce !== messageNonce) return; - const parachainSystemEvent = events.find( - (e) => - e.phase.isApplyExtrinsic && - e.event.section === 'parachainSystem' && - e.event.method === 'UpwardMessageSent' + const parachainSystemEvent = events.find((e) => + collator.api.events.parachainSystem.UpwardMessageSent.is(e.event) ); if (!parachainSystemEvent) { @@ -410,56 +424,81 @@ export class SubBridgeOutgoingReducer extends SubBridgeReducer { private async waitForDestinationMessageHash(id: string): Promise { const tx = this.getTransaction(id); - const messageHash = tx.payload.messageHash; - const network = tx.externalNetwork as SubNetwork; - const adapter = subConnector.getAdapterForNetwork(network); + const messageHash = tx.payload.messageHash as string; + + if (!messageHash) throw new Error(`[${this.constructor.name}]: Transaction paylaod messageHash cannot be empty`); + + const adapter = subConnector.getAdapterForNetwork(tx.externalNetwork as SubNetwork); let subscription!: Subscription; let blockNumber!: number; let extrinsicIndex!: number; + let amount!: string; try { if (!adapter.connected) { await adapter.connect(); } - await new Promise((resolve) => { + await new Promise((resolve, reject) => { const eventsObservable = adapter.apiRx.query.system.events(); const blockNumberObservable = adapter.apiRx.query.system.number(); subscription = combineLatest([eventsObservable, blockNumberObservable]).subscribe(([events, blockNum]) => { - const umpExecutedUpwardEvent = events.find( - (e) => e.phase.isApplyExtrinsic && e.event.section === 'ump' && e.event.method === 'ExecutedUpward' - ); + const umpExecutedUpwardEvent = events.find((e) => adapter.api.events.ump.ExecutedUpward.is(e.event)); if (!umpExecutedUpwardEvent) return; const eventMessageHash = umpExecutedUpwardEvent.event.data[0].toString(); - if (eventMessageHash === messageHash) { - blockNumber = blockNum.toNumber(); - extrinsicIndex = umpExecutedUpwardEvent.phase.asApplyExtrinsic.toNumber(); - resolve(); - } + if (eventMessageHash !== messageHash) return; + + // Native token for network + const balancesDepositEvent = events.find( + (e) => + adapter.api.events.balances.Deposit.is(e.event) && + subBridgeApi.formatAddress(e.event.data.who.toString()) === tx.to + ); + + if (!balancesDepositEvent) + reject(new Error(`[${this.constructor.name}]: Unable to find "balances.Deposit" event`)); + + amount = balancesDepositEvent.event.data.amount.toString(); + blockNumber = blockNum.toNumber(); + extrinsicIndex = umpExecutedUpwardEvent.phase.asApplyExtrinsic.toNumber(); + resolve(); }); }); } finally { subscription.unsubscribe(); } + if (subConnector.adapter !== adapter) { + await adapter.stop(); + } + + const decimals = await this.getAssetExternalDecimals(tx.externalNetwork as SubNetwork, tx.assetAddress as string); + + const sended = new FPNumber(tx.amount as string, decimals); + const received = FPNumber.fromCodecValue(amount, decimals); + const amount2 = received.toString(); + + const xcmFee = sended.sub(received); + const externalNetworkFee = FPNumber.fromCodecValue(tx.externalNetworkFee as string, decimals) + .add(xcmFee) + .toCodecString(); + const blockHash = await adapter.api.rpc.chain.getBlockHash(blockNumber); const blockData = await adapter.api.rpc.chain.getBlock(blockHash); const extrinsic = blockData.block.extrinsics.at(extrinsicIndex); const externalHash = extrinsic?.hash.toString() ?? ''; this.updateTransactionParams(id, { + amount2, + externalNetworkFee, externalHash, // parachain tx hash externalBlockId: blockHash.toString(), // parachain block hash externalBlockHeight: blockNumber, // parachain block number }); - - if (subConnector.adapter !== adapter) { - await adapter.stop(); - } } } diff --git a/src/utils/ethers-util.ts b/src/utils/ethers-util.ts index a3f8cedab..7edbf8a37 100644 --- a/src/utils/ethers-util.ts +++ b/src/utils/ethers-util.ts @@ -1,13 +1,12 @@ import detectEthereumProvider from '@metamask/detect-provider'; import { decodeAddress } from '@polkadot/util-crypto'; -import { FPNumber } from '@sora-substrate/util'; -import { XOR, VAL, PSWAP, ETH } from '@sora-substrate/util/build/assets/consts'; +import { FPNumber, BridgeRequestAssetKind } from '@sora-substrate/util'; import { BridgeNetworkType } from '@sora-substrate/util/build/bridgeProxy/consts'; import WalletConnectProvider from '@walletconnect/web3-provider'; import { ethers } from 'ethers'; import { ZeroStringValue } from '@/consts'; -import { KnownEthBridgeAsset, SmartContracts, SmartContractType } from '@/consts/evm'; +import { SmartContracts, SmartContractType } from '@/consts/evm'; import type { NetworkData } from '@/types/bridge'; import { settingsStorage } from '@/utils/storage'; @@ -44,28 +43,27 @@ const gasLimit = { /** * It's in gwei. - * Zero index means ETH -> SORA - * First index means SORA -> ETH */ -// TODO [EVM] -const EthereumGasLimits = [ - // ETH -> SORA - { - [XOR.address]: gasLimit.approve + gasLimit.sendERC20ToSidechain, - [VAL.address]: gasLimit.approve + gasLimit.sendERC20ToSidechain, - [PSWAP.address]: gasLimit.approve + gasLimit.sendERC20ToSidechain, - [ETH.address]: gasLimit.sendEthToSidechain, - [KnownEthBridgeAsset.Other]: gasLimit.approve + gasLimit.sendERC20ToSidechain, - }, - // SORA -> ETH - { - [XOR.address]: gasLimit.mintTokensByPeers, - [VAL.address]: gasLimit.mintTokensByPeers, - [PSWAP.address]: gasLimit.receiveBySidechainAssetId, - [ETH.address]: gasLimit.receiveByEthereumAssetAddress.ETH, - [KnownEthBridgeAsset.Other]: gasLimit.receiveByEthereumAssetAddress.OTHER, - }, -]; +const getEthBridgeGasLimit = (assetEvmAddress: string, kind: BridgeRequestAssetKind, isSoraToEvm: boolean) => { + if (isSoraToEvm) { + switch (kind) { + case BridgeRequestAssetKind.SidechainOwned: + return gasLimit.mintTokensByPeers; + case BridgeRequestAssetKind.Thischain: + return gasLimit.receiveBySidechainAssetId; + case BridgeRequestAssetKind.Sidechain: + return isNativeEvmTokenAddress(assetEvmAddress) + ? gasLimit.receiveByEthereumAssetAddress.ETH + : gasLimit.receiveByEthereumAssetAddress.OTHER; + default: + throw new Error(`Unknown kind "${kind}" for asset "${assetEvmAddress}"`); + } + } else { + return isNativeEvmTokenAddress(assetEvmAddress) + ? gasLimit.sendEthToSidechain + : gasLimit.approve + gasLimit.sendERC20ToSidechain; + } +}; async function onConnect(options: ConnectOptions): Promise { if (options.provider === Provider.Metamask) { @@ -303,14 +301,16 @@ async function getEvmNetworkId(): Promise { /** * Fetch EVM Network fee for passed asset address */ -async function getEvmNetworkFee(address: string, isSoraToEvm: boolean): Promise { +async function getEvmNetworkFee( + assetEvmAddress: string, + assetKind: string, + isSoraToEvm: boolean +): Promise { try { const ethersInstance = await getEthersInstance(); const { maxFeePerGas } = await ethersInstance.getFeeData(); const gasPrice = maxFeePerGas?.toNumber() ?? 0; - const gasLimits = EthereumGasLimits[+isSoraToEvm]; - const key = address in gasLimits ? address : KnownEthBridgeAsset.Other; - const gasLimit = gasLimits[key]; + const gasLimit = getEthBridgeGasLimit(assetEvmAddress, assetKind as BridgeRequestAssetKind, isSoraToEvm); const fee = calcEvmFee(gasPrice, gasLimit); return fee; diff --git a/src/utils/index.ts b/src/utils/index.ts index 863811dc6..a317cb462 100644 --- a/src/utils/index.ts +++ b/src/utils/index.ts @@ -10,8 +10,7 @@ import type { AmountWithSuffix } from '@/types/formats'; import storage from './storage'; -import type { RegisteredAccountAsset } from '@sora-substrate/util'; -import type { Asset, AccountAsset } from '@sora-substrate/util/build/assets/types'; +import type { Asset, AccountAsset, RegisteredAccountAsset } from '@sora-substrate/util/build/assets/types'; import type { AccountLiquidity } from '@sora-substrate/util/build/poolXyk/types'; import type { Route } from 'vue-router'; diff --git a/src/views/Bridge.vue b/src/views/Bridge.vue index d459f0183..f1fbc7390 100644 --- a/src/views/Bridge.vue +++ b/src/views/Bridge.vue @@ -237,6 +237,9 @@ + @@ -295,7 +298,7 @@ import BridgeMixin from '@/components/mixins/BridgeMixin'; import NetworkFeeDialogMixin from '@/components/mixins/NetworkFeeDialogMixin'; import NetworkFormatterMixin from '@/components/mixins/NetworkFormatterMixin'; import TokenSelectMixin from '@/components/mixins/TokenSelectMixin'; -import { Components, PageNames } from '@/consts'; +import { Components, PageNames, ZeroStringValue } from '@/consts'; import router, { lazyComponent } from '@/router'; import { getter, action, mutation, state } from '@/store/decorators'; import { @@ -308,10 +311,9 @@ import { asZeroValue, delay, } from '@/utils'; -import ethersUtil from '@/utils/ethers-util'; -import type { IBridgeTransaction, RegisteredAccountAsset } from '@sora-substrate/util'; -import type { AccountAsset } from '@sora-substrate/util/build/assets/types'; +import type { IBridgeTransaction } from '@sora-substrate/util'; +import type { AccountAsset, RegisteredAccountAsset } from '@sora-substrate/util/build/assets/types'; @Component({ components: { @@ -344,6 +346,7 @@ export default class Bridge extends Mixins( @state.bridge.externalNetworkFeeFetching private externalNetworkFeeFetching!: boolean; @state.bridge.externalBalancesFetching private externalBalancesFetching!: boolean; + @state.bridge.assetLockedBalanceFetching private assetLockedBalanceFetching!: boolean; @state.bridge.amount amount!: string; @state.bridge.isSoraToEvm isSoraToEvm!: boolean; @state.assets.registeredAssetsFetching registeredAssetsFetching!: boolean; @@ -400,36 +403,40 @@ export default class Bridge extends Mixins( } get isZeroAmount(): boolean { - return +this.amount === 0; + return asZeroValue(this.amount); } - get isMaxAvailable(): boolean { - if (!(this.areNetworksConnected && this.asset)) { - return false; - } - if (!(this.isRegisteredAsset || this.isSoraToEvm)) { - return false; + get maxValue(): string { + if (!(this.asset && this.isRegisteredAsset)) return ZeroStringValue; + + const fee = this.isSoraToEvm ? this.soraNetworkFee : this.evmNetworkFee; + const maxBalance = getMaxValue(this.asset, fee, !this.isSoraToEvm); + + if (this.assetLockedBalance && this.isSoraToEvm) { + const fpBalance = this.getFPNumber(maxBalance, this.asset.decimals); + const fpLocked = this.getFPNumberFromCodec(this.assetLockedBalance, this.asset.decimals); + + if (FPNumber.gt(fpBalance, fpLocked)) return fpLocked.toString(); } - const balance = getAssetBalance(this.asset, { internal: this.isSoraToEvm }); - if (asZeroValue(balance)) { + + return maxBalance; + } + + get isMaxAvailable(): boolean { + if (!(this.asset && this.isRegisteredAsset && this.areNetworksConnected && !asZeroValue(this.maxValue))) return false; - } + + return this.maxValue !== this.amount; + } + + get isInsufficientLiquidity(): boolean { + if (!(this.asset && this.assetLockedBalance && this.isSoraToEvm)) return false; + const decimals = this.asset.decimals; - const fpBalance = this.getFPNumberFromCodec(balance, decimals); - const fpAmount = this.getFPNumber(this.amount, decimals); - if (isXorAccountAsset(this.asset) && this.isSoraToEvm) { - const fpFee = this.getFPNumberFromCodec(this.soraNetworkFee, decimals); - return !FPNumber.eq(fpFee, fpBalance.sub(fpAmount)) && FPNumber.gt(fpBalance, fpFee); - } - if ( - this.asset.externalAddress && - ethersUtil.isNativeEvmTokenAddress(this.asset.externalAddress) && - !this.isSoraToEvm - ) { - const fpFee = this.getFPNumberFromCodec(this.evmNetworkFee); - return !FPNumber.eq(fpFee, fpBalance.sub(fpAmount)) && FPNumber.gt(fpBalance, fpFee); - } - return !FPNumber.eq(fpBalance, fpAmount); + const fpAmount = new FPNumber(this.amount, decimals); + const fpLocked = FPNumber.fromCodecValue(this.assetLockedBalance, decimals); + + return FPNumber.gt(fpAmount, fpLocked); } get isInsufficientXorForFee(): boolean { @@ -475,7 +482,8 @@ export default class Bridge extends Mixins( this.isZeroAmount || this.isInsufficientXorForFee || this.isInsufficientEvmNativeTokenForFee || - this.isInsufficientBalance + this.isInsufficientBalance || + this.isInsufficientLiquidity ); } @@ -484,7 +492,8 @@ export default class Bridge extends Mixins( this.isSelectAssetLoading || this.externalNetworkFeeFetching || this.externalBalancesFetching || - this.registeredAssetsFetching + this.registeredAssetsFetching || + this.assetLockedBalanceFetching ); } @@ -555,19 +564,10 @@ export default class Bridge extends Mixins( } handleMaxValue(): void { - if (this.asset && this.isRegisteredAsset) { - const fee = this.isSoraToEvm ? this.soraNetworkFee : this.evmNetworkFee; - const max = getMaxValue(this.asset, fee, !this.isSoraToEvm); - this.setAmount(max); - } + this.setAmount(this.maxValue); } async handleConfirmButtonClick(): Promise { - if (!this.isValidNetwork) { - this.updateNetworkProvided(); - return; - } - // XOR check if (!this.isXorSufficientForNextOperation) { this.openWarningFeeDialog(); @@ -635,14 +635,6 @@ export default class Bridge extends Mixins( } } - connectInternalWallet(): void { - if (this.isSubBridge) { - this.connectSubWallet(); - } else { - this.connectSoraWallet(); - } - } - connectSenderWallet() { if (this.isSoraToEvm || this.isSubBridge) { this.connectSoraWallet(); @@ -658,6 +650,14 @@ export default class Bridge extends Mixins( this.connectInternalWallet(); } } + + private connectInternalWallet(): void { + if (this.isSubBridge) { + this.connectSubWallet(); + } else { + this.connectSoraWallet(); + } + } } diff --git a/src/views/BridgeContainer.vue b/src/views/BridgeContainer.vue index bdbebd27d..65065713f 100644 --- a/src/views/BridgeContainer.vue +++ b/src/views/BridgeContainer.vue @@ -25,7 +25,6 @@ export default class BridgeContainer extends Mixins(mixins.LoadingMixin, WalletC @action.bridge.updateExternalBalance private updateExternalBalance!: AsyncFnWithoutArgs; @action.bridge.updateExternalBlockNumber private updateExternalBlockNumber!: AsyncFnWithoutArgs; @action.web3.getSupportedApps private getSupportedApps!: AsyncFnWithoutArgs; - @action.web3.restoreNetworkType restoreNetworkType!: AsyncFnWithoutArgs; @action.web3.restoreSelectedNetwork restoreSelectedNetwork!: AsyncFnWithoutArgs; private unwatchEthereum!: FnWithoutArgs; @@ -37,7 +36,6 @@ export default class BridgeContainer extends Mixins(mixins.LoadingMixin, WalletC await this.withParentLoading(async () => { await this.getSupportedApps(); - await this.restoreNetworkType(); await this.restoreSelectedNetwork(); await this.connectExternalNetwork(); }); diff --git a/src/views/BridgeTransaction.vue b/src/views/BridgeTransaction.vue index cd8d6e948..36bb02f25 100644 --- a/src/views/BridgeTransaction.vue +++ b/src/views/BridgeTransaction.vue @@ -27,7 +27,7 @@ @@ -59,6 +59,15 @@ :asset-symbol="assetSymbol" :fiat-value="amountFiatValue" /> + {{ t('insufficientBalanceText', { tokenSymbol: evmTokenSymbol }) }} +