diff --git a/biome.json b/biome.json new file mode 100644 index 000000000..1efe951bc --- /dev/null +++ b/biome.json @@ -0,0 +1,26 @@ +{ + "$schema": "https://biomejs.dev/schemas/1.8.3/schema.json", + "organizeImports": { + "enabled": true + }, + "linter": { + "enabled": true, + "rules": { + "recommended": true, + "suspicious": { + "noExplicitAny": "off" + }, + "a11y": { + "noNoninteractiveTabindex": "off", + "useAnchorContent": "off" + } + } + }, + "formatter": { + "enabled": true, + "indentStyle": "space", + "indentWidth": 2, + "lineWidth": 100, + "ignore": ["node_modules", "dist", ".docusaurus", "static"] + } +} diff --git a/package.json b/package.json index b619ae066..c2f3f749d 100644 --- a/package.json +++ b/package.json @@ -44,7 +44,9 @@ "@polkadot/vue-identicon": "3.8.3", "@quasar/app-webpack": "^3.11.2", "@quasar/extras": "^1.16.7", + "@tanstack/vue-query": "5.56.2", "@vue/apollo-composable": "^4.0.0-beta.4", + "@wagmi/vue": "0.0.48", "@walletconnect/ethereum-provider": "^2.12.2", "@walletconnect/modal": "^2.6.2", "animate.css": "^4.1.1", @@ -80,6 +82,7 @@ "three": "^0.165.0", "v-odometer": "^2.0.1", "validator": "^13.7.0", + "viem": "2.x", "vue-i18n": "^9.2.2", "vue-js-progress": "^1.0.2", "vue-router": "^4.0.0", diff --git a/src/components/assets/Account.vue b/src/components/assets/Account.vue index 1e3489591..f3d09b0b7 100644 --- a/src/components/assets/Account.vue +++ b/src/components/assets/Account.vue @@ -144,15 +144,13 @@ diff --git a/src/components/header/modals/SelectAccount.vue b/src/components/header/modals/SelectAccount.vue index 548fd3ec1..00c6d00ad 100644 --- a/src/components/header/modals/SelectAccount.vue +++ b/src/components/header/modals/SelectAccount.vue @@ -90,9 +90,8 @@ import { truncate, wait, } from '@astar-network/astar-sdk-core'; -import { ApiPromise } from '@polkadot/api'; -import copy from 'copy-to-clipboard'; -import { ethers } from 'ethers'; +import type { ApiPromise } from "@polkadot/api"; +import copy from "copy-to-clipboard"; import { $api } from 'src/boot/api'; import { astarChain } from 'src/config/chain'; import { endpointKey, providerEndpoints } from 'src/config/chainEndpoints'; @@ -100,9 +99,10 @@ import { LOCAL_STORAGE } from 'src/config/localStorage'; import { SupportWallet } from 'src/config/wallets'; import { useAccount, useBreakpoints, useNetworkInfo } from 'src/hooks'; import { castMobileSource, checkIsEthereumWallet } from 'src/hooks/helper/wallet'; +import { formatEtherAsString } from "src/lib/formatters"; import { useStore } from 'src/store'; -import { SubstrateAccount } from 'src/store/general/state'; -import { PropType, computed, defineComponent, onUnmounted, ref, watch } from 'vue'; +import type { SubstrateAccount } from "src/store/general/state"; +import { type PropType, computed, defineComponent, onUnmounted, ref, watch } from "vue"; import { useI18n } from 'vue-i18n'; import Account from './Account.vue'; import UnifiedAccount from './UnifiedAccount.vue'; @@ -233,7 +233,7 @@ export default defineComponent({ if (!accountBalanceMap.value) return 0; const account = accountBalanceMap.value.find((it) => it.address === address); const balance = account ? account.balance : '0'; - return truncate(ethers.utils.formatEther(balance || '0')); + return truncate(formatEtherAsString(balance || "0")); }; const updateAccountMap = async (): Promise => { diff --git a/src/components/header/modals/SelectMultisigAccount.vue b/src/components/header/modals/SelectMultisigAccount.vue index bd53942ce..717bf2460 100644 --- a/src/components/header/modals/SelectMultisigAccount.vue +++ b/src/components/header/modals/SelectMultisigAccount.vue @@ -138,20 +138,27 @@ import { truncate, wait, } from '@astar-network/astar-sdk-core'; -import { ApiPromise } from '@polkadot/api'; -import copy from 'copy-to-clipboard'; -import { ethers } from 'ethers'; +import type { ApiPromise } from "@polkadot/api"; +import copy from "copy-to-clipboard"; import { $api } from 'src/boot/api'; import SelectSignatory from 'src/components/header/modals/SelectSignatory.vue'; import { astarChain } from 'src/config/chain'; import { providerEndpoints } from 'src/config/chainEndpoints'; import { LOCAL_STORAGE } from 'src/config/localStorage'; -import { SupportMultisig, SupportWallet } from 'src/config/wallets'; +import { SupportMultisig, type SupportWallet } from "src/config/wallets"; import { useAccount, useBreakpoints, useNetworkInfo } from 'src/hooks'; -import { MultisigAddress } from 'src/modules/multisig'; +import type { MultisigAddress } from "src/modules/multisig"; import { useStore } from 'src/store'; -import { SubstrateAccount } from 'src/store/general/state'; -import { PropType, computed, defineComponent, onUnmounted, ref, watch, watchEffect } from 'vue'; +import type { SubstrateAccount } from "src/store/general/state"; +import { + type PropType, + computed, + defineComponent, + onUnmounted, + ref, + watch, + watchEffect, +} from "vue"; import { useI18n } from 'vue-i18n'; import { hasProperty, isValidAddressPolkadotAddress } from '@astar-network/astar-sdk-core'; @@ -159,12 +166,13 @@ import { web3Enable } from '@polkadot/extension-dapp'; import type { InjectedExtension } from '@polkadot/extension-inject/types'; import { encodeAddress } from '@polkadot/util-crypto'; import { useExtensions } from 'src/hooks/useExtensions'; +import { formatEtherAsString } from "src/lib/formatters"; import { polkasafeUrl } from 'src/links'; -import { Multisig, addProxyAccounts } from 'src/modules/multisig'; +import { type Multisig, addProxyAccounts } from "src/modules/multisig"; +import { PolkasafeWrapper } from "src/types/polkasafe"; import { container } from 'src/v2/common'; import { ASTAR_ADDRESS_PREFIX } from 'src/v2/repositories/implementations'; -import { Symbols } from 'src/v2/symbols'; -import { PolkasafeWrapper } from 'src/types/polkasafe'; +import { Symbols } from "src/v2/symbols"; export default defineComponent({ components: { @@ -282,7 +290,7 @@ export default defineComponent({ onHeightChange(); const displayBalance = (balance: string): number => { - return truncate(ethers.utils.formatEther(balance || '0')); + return truncate(formatEtherAsString(balance || "0")); }; const setMultisigAccounts = async (c: PolkasafeWrapper, signatory: string): Promise => { @@ -373,7 +381,7 @@ export default defineComponent({ const setDefaultSelectedSignatory = (): void => { if (multisig.value) { - substrateAccounts.value.length === 0 && useExtensions($api!!, store); + substrateAccounts.value.length === 0 && useExtensions($api!, store); const account = substrateAccounts.value.find( (it) => it.address === multisig.value!.signatory.address ); diff --git a/src/hooks/helper/claim.ts b/src/hooks/helper/claim.ts index ad0f35194..f6500dde0 100644 --- a/src/hooks/helper/claim.ts +++ b/src/hooks/helper/claim.ts @@ -1,7 +1,8 @@ -import { EventRecord } from '@polkadot/types/interfaces'; +import type { EventRecord } from "@polkadot/types/interfaces"; import { BN } from '@polkadot/util'; import { ethers } from 'ethers'; import { balanceFormatter } from 'src/hooks/helper/plasmUtils'; +import { formatEtherAsNumber } from "src/lib/formatters"; export const calculateClaimedStaker = ({ events, @@ -22,7 +23,7 @@ export const calculateClaimedStaker = ({ } } }); - const claimedAmount = Number(ethers.utils.formatEther(totalClaimStaker.toString()).toString()); + const claimedAmount = formatEtherAsNumber(totalClaimStaker); const formattedAmount = balanceFormatter(totalClaimStaker); return { claimedAmount, formattedAmount }; }; diff --git a/src/hooks/transfer/useTokenTransfer.ts b/src/hooks/transfer/useTokenTransfer.ts index db7182d79..339904205 100644 --- a/src/hooks/transfer/useTokenTransfer.ts +++ b/src/hooks/transfer/useTokenTransfer.ts @@ -5,21 +5,22 @@ import { isValidAddressPolkadotAddress, isValidEvmAddress, } from '@astar-network/astar-sdk-core'; -import { $api, $web3 } from 'boot/api'; -import { ethers } from 'ethers'; +import { $api, $web3 } from "boot/api"; import { getTokenBal } from 'src/config/web3'; import { useAccount, useBalance, useGasPrice, useNetworkInfo } from 'src/hooks'; +import { formatEtherAsNumber, formatEtherAsString } from "src/lib/formatters"; import { HistoryTxType } from 'src/modules/account'; import { addTxHistories } from 'src/modules/account/utils/index'; import { fetchXcmBalance } from 'src/modules/xcm'; import { Path } from 'src/router'; import { useStore } from 'src/store'; import { container } from 'src/v2/common'; -import { Asset } from 'src/v2/models'; -import { FrameSystemAccountInfo } from 'src/v2/repositories/implementations'; -import { IAccountUnificationService, IAssetsService } from 'src/v2/services'; +import type { Asset } from "src/v2/models"; +import type { FrameSystemAccountInfo } from "src/v2/repositories/implementations"; +import type { IAccountUnificationService, IAssetsService } from "src/v2/services"; import { Symbols } from 'src/v2/symbols'; -import { Ref, computed, ref, watch, watchEffect } from 'vue'; +import { isAddress, parseUnits } from "viem"; +import { type Ref, computed, ref, watch, watchEffect } from "vue"; import { useI18n } from 'vue-i18n'; import { useRoute, useRouter } from 'vue-router'; @@ -37,8 +38,8 @@ export function useTokenTransfer(selectedToken: Ref) { const transferableBalance = computed(() => { const balance = accountData.value - ? ethers.utils.formatEther(accountData.value.getUsableTransactionBalance().toString()) - : '0'; + ? formatEtherAsString(accountData.value.getUsableTransactionBalance()) + : "0"; return Number(balance); }); const { selectedTip, nativeTipPrice, setSelectedTip, isEnableSpeedConfiguration } = useGasPrice(); @@ -177,7 +178,7 @@ export function useTokenTransfer(selectedToken: Ref) { } const decimals = Number(selectedToken.value.metadata.decimals); - const amount = ethers.utils.parseUnits(String(transferAmt), decimals).toString(); + const amount = parseUnits(String(transferAmt), decimals).toString(); try { const assetsService = container.get(Symbols.AssetsService); @@ -238,11 +239,11 @@ export function useTokenTransfer(selectedToken: Ref) { if (isValidAddressPolkadotAddress(address)) { const { data } = await apiRef.query.system.account(address); const transferableBalance = data.free.sub(data.frozen); - return Number(ethers.utils.formatEther(transferableBalance.toString())); + return formatEtherAsNumber(transferableBalance); } - if (ethers.utils.isAddress(address)) { + if (isAddress(address)) { const balance = await web3Ref.eth.getBalance(address); - return Number(ethers.utils.formatEther(balance)); + return formatEtherAsNumber(balance); } return 0; }; diff --git a/src/hooks/useEvmDeposit.ts b/src/hooks/useEvmDeposit.ts index 29feb7a21..3b1b22907 100644 --- a/src/hooks/useEvmDeposit.ts +++ b/src/hooks/useEvmDeposit.ts @@ -1,10 +1,10 @@ import { buildEvmAddress } from '@astar-network/astar-sdk-core'; -import { $web3 } from 'boot/api'; -import { ethers } from 'ethers'; +import { $web3 } from "boot/api"; import { useAccount, useGasPrice } from 'src/hooks'; +import { formatEtherAsNumber } from "src/lib/formatters"; import { useStore } from 'src/store'; import { container } from 'src/v2/common'; -import { IAssetsService } from 'src/v2/services'; +import type { IAssetsService } from "src/v2/services"; import { Symbols } from 'src/v2/symbols'; import { computed, ref, watch } from 'vue'; @@ -40,8 +40,8 @@ export function useEvmDeposit() { const h160Addr = buildEvmAddress(currentAccountRef); const deposit = await getData(h160Addr); evmDeposit.value = deposit; - numEvmDeposit.value = Number(ethers.utils.formatEther(deposit.toString())); - isEvmDeposit.value = deposit.toString() !== '0' && !isH160.value ? true : false; + numEvmDeposit.value = formatEtherAsNumber(deposit); + isEvmDeposit.value = deposit.toString() !== "0" && !isH160.value; } }, { immediate: true } diff --git a/src/hooks/useFaucet.ts b/src/hooks/useFaucet.ts index 0382200ea..607bf9da0 100644 --- a/src/hooks/useFaucet.ts +++ b/src/hooks/useFaucet.ts @@ -1,12 +1,12 @@ -import { $api } from 'boot/api'; -import axios from 'axios'; -import { DateTime } from 'luxon'; -import { providerEndpoints } from 'src/config/chainEndpoints'; -import { useStore } from 'src/store'; -import { onUnmounted, ref, Ref, watch, watchEffect, computed } from 'vue'; -import { useAccount, useNetworkInfo } from 'src/hooks'; -import { ethers } from 'ethers'; -import { fetchNativeBalance } from '@astar-network/astar-sdk-core'; +import { fetchNativeBalance } from "@astar-network/astar-sdk-core"; +import axios from "axios"; +import { $api } from "boot/api"; +import { DateTime } from "luxon"; +import { providerEndpoints } from "src/config/chainEndpoints"; +import { useAccount, useNetworkInfo } from "src/hooks"; +import { formatEtherAsString } from "src/lib/formatters"; +import { useStore } from "src/store"; +import { type Ref, computed, onUnmounted, ref, watch, watchEffect } from "vue"; interface Timestamps { lastRequestAt: number; @@ -32,11 +32,11 @@ export function useFaucet(isModalFaucet?: Ref) { const timestamps = ref(null); const faucetAmount = ref(0); const faucetBalRequirement = computed(() => faucetAmount.value / 2); - const unit = ref(''); + const unit = ref(""); const isAbleToFaucet = ref(false); - const hash = ref(''); + const hash = ref(""); const isLoading = ref(true); - const faucetHotWalletBalance = ref('0'); + const faucetHotWalletBalance = ref("0"); const countDown = ref({ hours: 0, minutes: 0, @@ -61,7 +61,7 @@ export function useFaucet(isModalFaucet?: Ref) { const { data } = await axios.get(url); return data; } catch (error: any) { - throw Error(error.message || 'Something went wrong'); + throw Error(error.message || "Something went wrong"); } finally { isLoading.value = false; } @@ -82,14 +82,14 @@ export function useFaucet(isModalFaucet?: Ref) { const requestFaucet = async (recaptchaResponse: string): Promise => { if (!senderSs58Account.value) { - throw Error('Address is empty'); + throw Error("Address is empty"); } try { - store.commit('general/setLoading', true); + store.commit("general/setLoading", true); const endpoint = providerEndpoints[currentNetworkIdx.value].faucetEndpoint; if (!endpoint) { - throw Error('Cannot find the request endpoint'); + throw Error("Cannot find the request endpoint"); } const url = `${endpoint}/drip`; @@ -99,20 +99,20 @@ export function useFaucet(isModalFaucet?: Ref) { }); const msg = `Completed at block hash #${data.hash}`; - store.dispatch('general/showAlertMsg', { + store.dispatch("general/showAlertMsg", { msg, - alertType: 'success', + alertType: "success", txHash: data.hash, }); hash.value = data.hash; } catch (e: any) { console.error(e); - store.dispatch('general/showAlertMsg', { + store.dispatch("general/showAlertMsg", { msg: `Transaction failed with error: ${e.message}`, - alertType: 'error', + alertType: "error", }); } finally { - store.commit('general/setLoading', false); + store.commit("general/setLoading", false); } }; @@ -123,7 +123,7 @@ export function useFaucet(isModalFaucet?: Ref) { if (!isAbleToFaucet.value) { const resetTime = DateTime.fromMillis(nextRequestAt); - const { hours, minutes, seconds } = resetTime.diffNow(['hours', 'minutes', 'seconds']); + const { hours, minutes, seconds } = resetTime.diffNow(["hours", "minutes", "seconds"]); countDown.value.hours = hours; countDown.value.minutes = minutes; countDown.value.seconds = Number(seconds.toFixed(0)); @@ -160,9 +160,9 @@ export function useFaucet(isModalFaucet?: Ref) { faucetAmount.value = data.faucet.amount; unit.value = data.faucet.unit; timestamps.value = data.timestamps; - faucetHotWalletBalance.value = ethers.utils.formatEther(hotWalletBal); + faucetHotWalletBalance.value = formatEtherAsString(hotWalletBal); }, - { immediate: false } + { immediate: false }, ); return { diff --git a/src/hooks/useInflation.ts b/src/hooks/useInflation.ts index 7010cac7b..dc96df886 100644 --- a/src/hooks/useInflation.ts +++ b/src/hooks/useInflation.ts @@ -1,19 +1,20 @@ -import { computed, watch, ref, Ref, ComputedRef } from 'vue'; -import { useI18n } from 'vue-i18n'; -import { useStore } from 'src/store'; -import { container } from 'src/v2/common'; -import { +import { ethers } from "ethers"; +import { PERIOD1_START_BLOCKS } from "src/constants"; +import { formatEtherAsNumber } from "src/lib/formatters"; +import { type InflationParam, useDappStaking } from "src/staking-v3"; +import { useStore } from "src/store"; +import { container } from "src/v2/common"; +import type { InflationConfiguration } from "src/v2/models"; +import type { BurnEvent, IBalancesRepository, IInflationRepository, ITokenApiRepository, -} from 'src/v2/repositories'; -import { Symbols } from 'src/v2/symbols'; -import { InflationConfiguration } from 'src/v2/models'; -import { InflationParam, useDappStaking } from 'src/staking-v3'; -import { ethers } from 'ethers'; -import { useNetworkInfo } from './useNetworkInfo'; -import { PERIOD1_START_BLOCKS } from 'src/constants'; +} from "src/v2/repositories"; +import { Symbols } from "src/v2/symbols"; +import { type ComputedRef, type Ref, computed, ref, watch } from "vue"; +import { useI18n } from "vue-i18n"; +import { useNetworkInfo } from "./useNetworkInfo"; type UseInflation = { activeInflationConfiguration: ComputedRef; @@ -39,23 +40,23 @@ export function useInflation(): UseInflation { const realizedAdjustableStakersPart = ref(0); const activeInflationConfiguration = computed( - () => store.getters['general/getActiveInflationConfiguration'] + () => store.getters["general/getActiveInflationConfiguration"], ); const inflationParameters = computed( - () => store.getters['general/getInflationParameters'] + () => store.getters["general/getInflationParameters"], ); - const currentBlock = computed(() => store.getters['general/getCurrentBlock']); + const currentBlock = computed(() => store.getters["general/getCurrentBlock"]); const fetchActiveConfigurationToStore = async (): Promise => { const inflationRepository = container.get(Symbols.InflationRepository); const activeConfiguration = await inflationRepository.getInflationConfiguration(); - store.commit('general/setActiveInflationConfiguration', activeConfiguration); + store.commit("general/setActiveInflationConfiguration", activeConfiguration); }; const fetchInflationParamsToStore = async (): Promise => { const inflationRepository = container.get(Symbols.InflationRepository); const params = await inflationRepository.getInflationParams(); - store.commit('general/setInflationParameters', params); + store.commit("general/setInflationParameters", params); }; const getInflationParameters = async (): Promise => { @@ -66,10 +67,10 @@ export function useInflation(): UseInflation { const getBurnEvents = async (): Promise => { // Ignore burn events with less than 1M ASTAR. They are not impacting charts a lot small burn amounts // could be a spam. - const minBurn = BigInt('1000000000000000000000000'); + const minBurn = BigInt("1000000000000000000000000"); const tokenApiRepository = container.get(Symbols.TokenApiRepository); const burnEvents = await tokenApiRepository.getBurnEvents( - networkNameSubstrate.value.toLowerCase() + networkNameSubstrate.value.toLowerCase(), ); return burnEvents.filter((item) => item.amount >= minBurn); @@ -88,7 +89,7 @@ export function useInflation(): UseInflation { if (!period1StartBlock) { console.warn( - t('dashboard.inflation.wrongNetwork', { network: networkNameSubstrate.value }) + t("dashboard.inflation.wrongNetwork", { network: networkNameSubstrate.value }), ); return; } @@ -102,13 +103,13 @@ export function useInflation(): UseInflation { burnEvents.splice(0, 0, { blockNumber: period1StartBlock, amount: BigInt(0), - user: '', + user: "", timestamp: 0, }); burnEvents.push({ blockNumber: currentBlock.value, amount: BigInt(0), - user: '', + user: "", timestamp: 0, }); @@ -133,17 +134,13 @@ export function useInflation(): UseInflation { // Estimate total issuance at the end of the current cycle. const endOfCycleBlock = period1StartBlock + cycleLengthInBlocks; - const endOfCycleTotalIssuance = Number( - ethers.utils.formatEther( - slope * BigInt(endOfCycleBlock - period1StartBlock) + initialTotalIssuanceWithoutBurn - ) + const endOfCycleTotalIssuance = formatEtherAsNumber( + slope * BigInt(endOfCycleBlock - period1StartBlock) + initialTotalIssuanceWithoutBurn, ); // Estimated inflation at the end of the current cycle. inflation = - (100 * - (endOfCycleTotalIssuance - - Number(ethers.utils.formatEther(initialTotalIssuanceWithoutBurn.toString())))) / + (100 * (endOfCycleTotalIssuance - formatEtherAsNumber(initialTotalIssuanceWithoutBurn))) / endOfCycleTotalIssuance; // Calculate maximum and realized inflation for each era in the cycle. @@ -154,7 +151,7 @@ export function useInflation(): UseInflation { cycleLengthInBlocks, inflationParameters.value?.maxInflationRate ?? 0, eraLengths.value.standardEraLength, - burnEvents + burnEvents, ); calculateRealizedInflationData( @@ -163,17 +160,17 @@ export function useInflation(): UseInflation { slope, eraLengths.value.standardEraLength, initialTotalIssuance, - burnEvents + burnEvents, ); calculateAdjustableStakerRewards( realizedTotalIssuance, currentEraInfo.value?.currentStakeAmount.totalStake ?? BigInt(0), inflationParameters.value.adjustableStakersPart, - inflationParameters.value.idealStakingRate + inflationParameters.value.idealStakingRate, ); } catch (error) { - console.error('Error calculating realized inflation', error); + console.error("Error calculating realized inflation", error); } estimatedInflation.value = inflation; @@ -186,11 +183,11 @@ export function useInflation(): UseInflation { cycleLengthInBlocks: number, maxInflation: number, eraLength: number, - burnEvents: BurnEvent[] + burnEvents: BurnEvent[], ): void => { const result: [number, number][] = []; - const inflation = BigInt(Math.floor(maxInflation * 100)) * BigInt('10000000000000000'); - const cycleProgression = (firstBlockIssuance * inflation) / BigInt('1000000000000000000'); + const inflation = BigInt(Math.floor(maxInflation * 100)) * BigInt("10000000000000000"); + const cycleProgression = (firstBlockIssuance * inflation) / BigInt("1000000000000000000"); const cycleLength = BigInt(cycleLengthInBlocks); // One sample per era. @@ -205,7 +202,7 @@ export function useInflation(): UseInflation { firstBlockIssuance - burnEvents[j].amount; - result.push([i, Number(ethers.utils.formatEther(inflation.toString()))]); + result.push([i, formatEtherAsNumber(inflation)]); } } @@ -218,7 +215,7 @@ export function useInflation(): UseInflation { slope: bigint, eraLength: number, firstBlockIssuance: bigint, - burnEvents: BurnEvent[] + burnEvents: BurnEvent[], ): void => { const result: [number, number][] = []; @@ -228,10 +225,8 @@ export function useInflation(): UseInflation { i <= burnEvents[j + 1].blockNumber + eraLength; i += eraLength ) { - const currentBlockIssuance = Number( - ethers.utils.formatEther( - slope * BigInt(i - firstBlock) + firstBlockIssuance - burnEvents[j].amount - ) + const currentBlockIssuance = formatEtherAsNumber( + slope * BigInt(i - firstBlock) + firstBlockIssuance - burnEvents[j].amount, ); result.push([i, currentBlockIssuance]); @@ -245,13 +240,13 @@ export function useInflation(): UseInflation { totalIssuance: bigint, totalStake: bigint, adjustableStakerPart: number, - idealStakingRate: number + idealStakingRate: number, ): void => { const stakeRate = totalStake <= BigInt(0) ? 0 - : Number(ethers.utils.formatEther(totalStake.toString())) / - Number(ethers.utils.formatEther(totalIssuance.toString())); + : formatEtherAsNumber(totalStake.toString()) / + formatEtherAsNumber(totalIssuance.toString()); const result = adjustableStakerPart * Math.min(1, stakeRate / idealStakingRate); realizedAdjustableStakersPart.value = Number(result.toFixed(3)); }; diff --git a/src/hooks/useTokenDistribution.ts b/src/hooks/useTokenDistribution.ts index 7166724bf..d5b2a7aac 100644 --- a/src/hooks/useTokenDistribution.ts +++ b/src/hooks/useTokenDistribution.ts @@ -1,16 +1,17 @@ +import { ethers } from "ethers"; +import { formatEtherAsNumber } from "src/lib/formatters"; +import type { IDappStakingService } from "src/staking-v3"; +import { container } from "src/v2/common"; // Provides an information about tokens allocation // Total supply, circulating supply, locked tokens, treasury tokens, etc.... -import { TvlModel } from 'src/v2/models'; -import { ref, watchEffect } from 'vue'; -import { useTokenCirculation } from './useTokenCirculation'; -import { container } from 'src/v2/common'; -import { Symbols } from 'src/v2/symbols'; -import { useBalance } from './useBalance'; -import { ethers } from 'ethers'; -import { IDappStakingService } from 'src/staking-v3'; +import type { TvlModel } from "src/v2/models"; +import { Symbols } from "src/v2/symbols"; +import { ref, watchEffect } from "vue"; +import { useBalance } from "./useBalance"; +import { useTokenCirculation } from "./useTokenCirculation"; export function useTokenDistribution() { - const treasuryAddress = ref('YQnbw3oWxBnCUarnbePrjFcrSgVPP2jqTZYzWcccmN8fXhd'); + const treasuryAddress = ref("YQnbw3oWxBnCUarnbePrjFcrSgVPP2jqTZYzWcccmN8fXhd"); const tvlModel = ref(); const { formatNumber, totalSupply, currentCirculating } = useTokenCirculation(); const { balance: treasuryBalance } = useBalance(treasuryAddress); @@ -30,7 +31,7 @@ export function useTokenDistribution() { watchEffect(() => { if (tvlModel?.value && treasuryBalance?.value && totalSupply?.value) { const tvlUnrounded = tvlModel?.value?.tvlDefaultUnit ?? 0; - const treasuryUnrounded = Number(ethers.utils.formatEther(treasuryBalance.value.toString())); + const treasuryUnrounded = formatEtherAsNumber(treasuryBalance.value); tvl.value = Math.round(tvlUnrounded); treasury.value = Math.round(treasuryUnrounded); diff --git a/src/hooks/useVesting.ts b/src/hooks/useVesting.ts index 3434559e7..b3dcb4311 100644 --- a/src/hooks/useVesting.ts +++ b/src/hooks/useVesting.ts @@ -1,8 +1,9 @@ import { ethers } from 'ethers'; -import { ExtendedVestingInfo, useBalance, useGasPrice } from 'src/hooks'; +import { type ExtendedVestingInfo, useBalance, useGasPrice } from "src/hooks"; +import { formatEtherAsNumber } from "src/lib/formatters"; import { useStore } from 'src/store'; import { container } from 'src/v2/common'; -import { IAssetsService } from 'src/v2/services'; +import type { IAssetsService } from "src/v2/services"; import { Symbols } from 'src/v2/symbols'; import { computed } from 'vue'; @@ -25,16 +26,14 @@ export const useVesting = () => { ], }; try { - if (accountData.value && accountData.value.vesting.length) { - const claimableAmount = Number( - ethers.utils.formatEther(accountData.value.vestedClaimable.toString()) - ); + if (accountData.value?.vesting.length) { + const claimableAmount = formatEtherAsNumber(accountData.value.vestedClaimable.toString()); const vestings = accountData.value.vesting.map((vesting: ExtendedVestingInfo) => { const { perBlock, locked, startingBlock } = vesting.basicInfo; - const vestedAmount = Number(ethers.utils.formatEther(vesting.vested.toString())); - const totalDistribution = Number(ethers.utils.formatEther(locked.toString())); - const unlockPerBlock = Number(ethers.utils.formatEther(perBlock.toString())); + const vestedAmount = formatEtherAsNumber(vesting.vested.toString()); + const totalDistribution = formatEtherAsNumber(locked.toString()); + const unlockPerBlock = formatEtherAsNumber(perBlock.toString()); const block = locked.div(perBlock).add(startingBlock); const untilBlock = block.toNumber(); return { diff --git a/src/hooks/wallet/useAccountUnification.ts b/src/hooks/wallet/useAccountUnification.ts index d8505e2da..9ae3e16a7 100644 --- a/src/hooks/wallet/useAccountUnification.ts +++ b/src/hooks/wallet/useAccountUnification.ts @@ -1,10 +1,10 @@ import { - ExtrinsicPayload, + type ExtrinsicPayload, PayloadWithWeight, checkSumEvmAddress, -} from '@astar-network/astar-sdk-core'; -import { SubmittableExtrinsic } from '@polkadot/api/types'; -import { ISubmittableResult } from '@polkadot/types/types'; +} from "@astar-network/astar-sdk-core"; +import type { SubmittableExtrinsic } from "@polkadot/api/types"; +import type { ISubmittableResult } from "@polkadot/types/types"; import { BN } from '@polkadot/util'; import { $api } from 'boot/api'; import { ethers } from 'ethers'; @@ -12,25 +12,26 @@ import { get } from 'lodash-es'; import ABI from 'src/config/abi/ERC20.json'; import { setupNetwork } from 'src/config/web3'; import { useAccount } from 'src/hooks/useAccount'; +import { formatEtherAsString } from "src/lib/formatters"; import { getEvmExplorerUrl } from 'src/links'; +import { getRawEvmTransaction } from "src/modules/evm"; import { evmPrecompiledContract } from 'src/modules/precompiled'; import { AlertMsg } from 'src/modules/toast'; +import { useDappStaking, useDapps } from "src/staking-v3"; import { useStore } from 'src/store'; -import { XcmAssets } from 'src/store/assets/state'; +import type { XcmAssets } from "src/store/assets/state"; +import type { UnifiedAccount } from "src/store/general/state"; import { container } from 'src/v2/common'; -import { ExtrinsicStatusMessage, IEventAggregator } from 'src/v2/messaging'; -import { Asset } from 'src/v2/models'; -import { IAccountUnificationService, IIdentityService } from 'src/v2/services'; +import { ExtrinsicStatusMessage, type IEventAggregator } from "src/v2/messaging"; +import type { Asset } from "src/v2/models"; +import type { IAccountUnificationRepository, IIdentityRepository } from "src/v2/repositories"; +import type { IAccountUnificationService, IIdentityService } from "src/v2/services"; import { Symbols } from 'src/v2/symbols'; -import { WatchCallback, computed, ref, watch } from 'vue'; +import { type WatchCallback, computed, ref, watch } from "vue"; import { useI18n } from 'vue-i18n'; import Web3 from 'web3'; -import { AbiItem } from 'web3-utils'; -import { useNetworkInfo } from '../useNetworkInfo'; -import { IAccountUnificationRepository, IIdentityRepository } from 'src/v2/repositories'; -import { UnifiedAccount } from 'src/store/general/state'; -import { getRawEvmTransaction } from 'src/modules/evm'; -import { useDappStaking, useDapps } from 'src/staking-v3'; +import type { AbiItem } from "web3-utils"; +import { useNetworkInfo } from "../useNetworkInfo"; const provider = get(window, 'ethereum') as any; @@ -117,7 +118,7 @@ export const useAccountUnification = () => { if (!selectedEvmAddress.value || !protocolState.value || !ledger.value) return; try { - let isPendingWithdrawal = + const isPendingWithdrawal = rewards.value.bonus > BigInt(0) || rewards.value.dApp > BigInt(0) || rewards.value.staker.amount > BigInt(0); @@ -311,7 +312,7 @@ export const useAccountUnification = () => { ]); const totalDeposit = depositInfo.basic + depositInfo.field * BigInt(TOTAL_FIELDS) + mappingFee; - return `${ethers.utils.formatEther(totalDeposit.toString())} ${nativeTokenSymbol.value}`; + return `${formatEtherAsString(totalDeposit)} ${nativeTokenSymbol.value}`; }; watch([web3], updateEvmProvider); diff --git a/src/hooks/xcm/useTransferRouter.ts b/src/hooks/xcm/useTransferRouter.ts index 9fdee4e65..b60077901 100644 --- a/src/hooks/xcm/useTransferRouter.ts +++ b/src/hooks/xcm/useTransferRouter.ts @@ -1,30 +1,31 @@ -import { Erc20Token } from 'src/modules/token'; -import { providerEndpoints } from 'src/config/chainEndpoints'; -import { ethers } from 'ethers'; -import { endpointKey } from 'src/config/chainEndpoints'; -import { useAccount, useBalance, useNetworkInfo } from 'src/hooks'; +import { capitalize } from "@astar-network/astar-sdk-core"; +import { ethers } from "ethers"; +import { providerEndpoints } from "src/config/chainEndpoints"; +import { endpointKey } from "src/config/chainEndpoints"; +import { useAccount, useBalance, useNetworkInfo } from "src/hooks"; +import { formatEtherAsNumber } from "src/lib/formatters"; +import { productionOrigin } from "src/links"; +import type { Erc20Token } from "src/modules/token"; import { checkIsSupportAstarNativeToken, removeEvmName, restrictedXcmNetwork, xcmChains, xcmToken, -} from 'src/modules/xcm'; -import { Chain, XcmChain } from 'src/v2/models/XcmModels'; -import { generateAssetFromEvmToken, generateNativeAsset } from 'src/modules/xcm/tokens'; -import { useStore } from 'src/store'; -import { Asset, astarChains } from 'src/v2/models'; -import { computed, ref, watch, watchEffect } from 'vue'; -import { useRoute, useRouter } from 'vue-router'; -import { EvmAssets, XcmAssets } from 'src/store/assets/state'; -import { capitalize } from '@astar-network/astar-sdk-core'; -import { Path } from 'src/router'; -import { productionOrigin } from 'src/links'; - -export const pathEvm = '-evm'; -export type TransferMode = 'local' | 'xcm'; -export const astarNetworks = ['astar', 'shiden', 'shibuya']; -export const astarNativeTokens = ['sdn', 'astr', 'sby']; +} from "src/modules/xcm"; +import { generateAssetFromEvmToken, generateNativeAsset } from "src/modules/xcm/tokens"; +import { Path } from "src/router"; +import { useStore } from "src/store"; +import type { EvmAssets, XcmAssets } from "src/store/assets/state"; +import { type Asset, astarChains } from "src/v2/models"; +import { Chain, type XcmChain } from "src/v2/models/XcmModels"; +import { computed, ref, watch, watchEffect } from "vue"; +import { useRoute, useRouter } from "vue-router"; + +export const pathEvm = "-evm"; +export type TransferMode = "local" | "xcm"; +export const astarNetworks = ["astar", "shiden", "shibuya"]; +export const astarNativeTokens = ["sdn", "astr", "sby"]; const disabledXcmChains: endpointKey[] = []; export interface NetworkFromTo { @@ -47,16 +48,16 @@ export function useTransferRouter() { const mode = computed(() => route.query.mode as TransferMode); const from = computed(() => route.query.from as string); const to = computed(() => route.query.to as string); - const isTransferPage = computed(() => route.fullPath.includes('transfer')); + const isTransferPage = computed(() => route.fullPath.includes("transfer")); const isEvmBridge = computed(() => { if (!isTransferPage.value || isLocalTransfer.value) return false; return to.value.includes(pathEvm); }); const { nativeTokenSymbol, currentNetworkName, currentNetworkIdx, currentNetworkChain, isZkEvm } = useNetworkInfo(); - const isH160 = computed(() => store.getters['general/isH160Formatted']); - const xcmAssets = computed(() => store.getters['assets/getAllAssets']); - const evmAssets = computed(() => store.getters['assets/getEvmAllAssets']); + const isH160 = computed(() => store.getters["general/isH160Formatted"]); + const xcmAssets = computed(() => store.getters["assets/getAllAssets"]); + const evmAssets = computed(() => store.getters["assets/getEvmAllAssets"]); const xcmOpponentChain = computed(() => { const chain = astarChains.includes(capitalize(from.value) as Chain) ? to.value : from.value; return capitalize(chain) as Chain; @@ -67,20 +68,20 @@ export function useTransferRouter() { if (restrictedNetworksArray.length === 0) return []; return restrictedNetworksArray .filter(({ isRestrictedFromEvm, isRestrictedFromNative }) => - isH160.value ? isRestrictedFromEvm : isRestrictedFromNative + isH160.value ? isRestrictedFromEvm : isRestrictedFromNative, ) .map(({ chain }) => chain); }); const setNativeTokenBalance = (): void => { - nativeTokenBalance.value = Number(ethers.utils.formatEther(useableBalance.value)); + nativeTokenBalance.value = formatEtherAsNumber(useableBalance.value); }; const redirect = (): void => { const token = nativeTokenSymbol.value.toLowerCase(); router.push({ path: `/${network.value}/assets/transfer`, - query: { token, mode: 'local' }, + query: { token, mode: "local" }, }); }; @@ -198,7 +199,7 @@ export function useTransferRouter() { originChain: string; }): void => { isLocalTransfer.value = isLocal; - const mode = isLocal ? 'local' : 'xcm'; + const mode = isLocal ? "local" : "xcm"; const isNativeAstarToken = tokenSymbol.value === nativeTokenSymbol.value.toLowerCase(); const opponentNetwork = isNativeAstarToken @@ -218,7 +219,7 @@ export function useTransferRouter() { }; const setToken = (t: Asset): void => { - const mode = isLocalTransfer.value ? 'local' : 'xcm'; + const mode = isLocalTransfer.value ? "local" : "xcm"; const token = t.metadata.symbol.toLowerCase(); const baseQuery = { token, mode }; const xcmQuery = { @@ -256,7 +257,7 @@ export function useTransferRouter() { const token = isAstarEvm ? tokenSymbol.value : t.toLowerCase(); router.replace({ path: `/${network.value}/assets/transfer`, - query: { ...route.query, token, from, to, mode: 'xcm' }, + query: { ...route.query, token, from, to, mode: "xcm" }, }); }; @@ -288,7 +289,7 @@ export function useTransferRouter() { if (!xcmAssets.value || !nativeTokenSymbol.value) return []; selectableTokens = xcmAssets.value.assets; tokens = selectableTokens.filter( - ({ isXcmCompatible, userBalance }) => isXcmCompatible || userBalance + ({ isXcmCompatible, userBalance }) => isXcmCompatible || userBalance, ); tokens.push(nativeTokenAsset); } @@ -301,14 +302,14 @@ export function useTransferRouter() { const isSupportAstarNativeToken = checkIsSupportAstarNativeToken(selectedNetwork); if (isH160.value) { const filteredToken = evmTokens.map((it) => - generateAssetFromEvmToken(it as Erc20Token, xcmAssets.value.assets) + generateAssetFromEvmToken(it as Erc20Token, xcmAssets.value.assets), ); selectableTokens = filteredToken .filter(({ isXcmCompatible }) => isXcmCompatible) .filter((it) => it.originChain === selectedNetwork); } else { selectableTokens = xcmAssets.value.assets.filter( - (it) => it.originChain === selectedNetwork + (it) => it.originChain === selectedNetwork, ); } tokens = selectableTokens.filter(({ isXcmCompatible }) => isXcmCompatible); @@ -326,7 +327,7 @@ export function useTransferRouter() { } const isCustomNetwork = network.value === providerEndpoints[endpointKey.CUSTOM].networkAlias; - isLocalTransfer.value = mode.value === 'local'; + isLocalTransfer.value = mode.value === "local"; const isRedirect = !isCustomNetwork && !isZkEvm.value && @@ -339,7 +340,7 @@ export function useTransferRouter() { const nativeTokenAsset = { ...nativeToken, userBalance: nativeBal }; token.value = nativeTokenAsset; token.value = tokens.value.find( - (it) => it.metadata.symbol.toLowerCase() === tokenSymbol.value.toLowerCase() + (it) => it.metadata.symbol.toLowerCase() === tokenSymbol.value.toLowerCase(), ); }; @@ -357,7 +358,7 @@ export function useTransferRouter() { const isFetchedXcmAssets = xcmAssets.value.assets.length > 0; if (isFetchedXcmAssets && tokenSymbol.value) { const isFound = tokens.value.find( - (it) => it.metadata.symbol.toLowerCase() === tokenSymbol.value.toLowerCase() + (it) => it.metadata.symbol.toLowerCase() === tokenSymbol.value.toLowerCase(), ); !isFound && redirect(); } @@ -377,7 +378,7 @@ export function useTransferRouter() { const isDisabledXcmChain = disabledXcmChains.some((it) => it === currentNetworkIdx.value); - const originChain = token.value?.originChain || ''; + const originChain = token.value?.originChain || ""; return checkIsDisabledToken(originChain) || isDisabledXcmChain; }); @@ -390,7 +391,7 @@ export function useTransferRouter() { // Memo: redirect to the assets page if users access to the XCM transfer page by inputting URL directly const handleDisableXcmTransfer = (): void => { - if (checkIsDisabledXcmChain(from.value, to.value) && mode.value === 'xcm') { + if (checkIsDisabledXcmChain(from.value, to.value) && mode.value === "xcm") { router.push(Path.Assets); } }; diff --git a/src/hooks/xvm/useXvmTokenTransfer.ts b/src/hooks/xvm/useXvmTokenTransfer.ts index c3553bef0..ab7849dab 100644 --- a/src/hooks/xvm/useXvmTokenTransfer.ts +++ b/src/hooks/xvm/useXvmTokenTransfer.ts @@ -1,33 +1,33 @@ -import { ethers } from 'ethers'; -import { getTokenBal } from 'src/config/web3'; -import { useBalance, useGasPrice, useNetworkInfo } from 'src/hooks'; import { ASTAR_SS58_FORMAT, + SUBSTRATE_SS58_FORMAT, buildEvmAddress, isValidAddressPolkadotAddress, isValidEvmAddress, - SUBSTRATE_SS58_FORMAT, -} from '@astar-network/astar-sdk-core'; -import { useAccount } from 'src/hooks/useAccount'; -import { Erc20Token } from 'src/modules/token'; -import { Path } from 'src/router'; -import { useStore } from 'src/store'; -import { container } from 'src/v2/common'; -import { IXvmService } from 'src/v2/services/IXvmService'; -import { Symbols } from 'src/v2/symbols'; -import { computed, ref, Ref, watch, watchEffect } from 'vue'; -import { useI18n } from 'vue-i18n'; -import { useRoute, useRouter } from 'vue-router'; - -type ContractType = 'wasm-erc20' | 'wasm-psp22'; +} from "@astar-network/astar-sdk-core"; +import { getTokenBal } from "src/config/web3"; +import { useBalance, useGasPrice, useNetworkInfo } from "src/hooks"; +import { useAccount } from "src/hooks/useAccount"; +import { formatEtherAsString } from "src/lib/formatters"; +import type { Erc20Token } from "src/modules/token"; +import { Path } from "src/router"; +import { useStore } from "src/store"; +import { container } from "src/v2/common"; +import type { IXvmService } from "src/v2/services/IXvmService"; +import { Symbols } from "src/v2/symbols"; +import { type Ref, computed, ref, watch, watchEffect } from "vue"; +import { useI18n } from "vue-i18n"; +import { useRoute, useRouter } from "vue-router"; + +type ContractType = "wasm-erc20" | "wasm-psp22"; export function useXvmTokenTransfer(selectedToken: Ref) { const transferAmt = ref(null); const toAddressBalance = ref(0); - const toAddress = ref(''); - const errMsg = ref(''); + const toAddress = ref(""); + const errMsg = ref(""); const isChecked = ref(false); - const xvmContract = ref('wasm-erc20'); + const xvmContract = ref("wasm-erc20"); const { t } = useI18n(); const store = useStore(); @@ -37,18 +37,18 @@ export function useXvmTokenTransfer(selectedToken: Ref) { const router = useRouter(); const { accountData } = useBalance(currentAccount); const { evmNetworkIdx, nativeTokenSymbol } = useNetworkInfo(); - const isH160 = computed(() => store.getters['general/isH160Formatted']); + const isH160 = computed(() => store.getters["general/isH160Formatted"]); const tokenSymbol = computed(() => route.query.token as string); - const isLoading = computed(() => store.getters['general/isLoading']); + const isLoading = computed(() => store.getters["general/isLoading"]); const isRequiredCheck = computed(() => true); const fromAddressBalance = computed(() => - selectedToken.value ? Number(selectedToken.value.userBalance) : 0 + selectedToken.value ? Number(selectedToken.value.userBalance) : 0, ); const transferableBalance = computed(() => { const balance = accountData.value - ? ethers.utils.formatEther(accountData.value.getUsableTransactionBalance().toString()) - : '0'; + ? formatEtherAsString(accountData.value.getUsableTransactionBalance()) + : "0"; return Number(balance); }); @@ -57,7 +57,7 @@ export function useXvmTokenTransfer(selectedToken: Ref) { 0 >= Number(transferAmt.value) || fromAddressBalance.value < Number(transferAmt.value); const noAddress = !toAddress.value; return ( - errMsg.value !== '' || + errMsg.value !== "" || isLessAmount || noAddress || (isRequiredCheck.value && !isChecked.value) @@ -74,13 +74,13 @@ export function useXvmTokenTransfer(selectedToken: Ref) { const inputHandler = (event: any): void => { transferAmt.value = event.target.value; - errMsg.value = ''; + errMsg.value = ""; }; const resetStates = (): void => { - transferAmt.value = ''; - toAddress.value = ''; - errMsg.value = ''; + transferAmt.value = ""; + toAddress.value = ""; + errMsg.value = ""; isChecked.value = false; toAddressBalance.value = 0; }; @@ -98,17 +98,17 @@ export function useXvmTokenTransfer(selectedToken: Ref) { const transferAmtRef = Number(transferAmt.value); try { if (transferAmtRef > fromAddressBalance.value) { - errMsg.value = t('warning.insufficientBalance', { + errMsg.value = t("warning.insufficientBalance", { token: selectedToken.value.symbol, }); } else if (toAddress.value && !isValidDestAddress.value) { - errMsg.value = 'warning.inputtedInvalidDestAddress'; + errMsg.value = "warning.inputtedInvalidDestAddress"; } else if (!transferableBalance.value) { - errMsg.value = t('warning.insufficientBalance', { + errMsg.value = t("warning.insufficientBalance", { token: nativeTokenSymbol.value, }); } else { - errMsg.value = ''; + errMsg.value = ""; } } catch (error: any) { errMsg.value = error.message; @@ -118,7 +118,7 @@ export function useXvmTokenTransfer(selectedToken: Ref) { const setXvmContract = (): void => { if (!toAddress.value) return; const isSendToH160 = isValidEvmAddress(toAddress.value); - xvmContract.value = isSendToH160 ? 'wasm-erc20' : 'wasm-psp22'; + xvmContract.value = isSendToH160 ? "wasm-erc20" : "wasm-psp22"; }; const setToAddressBalance = async (): Promise => { @@ -158,11 +158,11 @@ export function useXvmTokenTransfer(selectedToken: Ref) { }); } catch (e: any) { console.error(e); - store.dispatch('general/showAlertMsg', { - msg: e.message || 'Something went wrong during asset transfer.', - alertType: 'error', + store.dispatch("general/showAlertMsg", { + msg: e.message || "Something went wrong during asset transfer.", + alertType: "error", }); - store.commit('general/setLoading', false); + store.commit("general/setLoading", false); } }; diff --git a/src/hooks/xvm/useXvmTransferRouter.ts b/src/hooks/xvm/useXvmTransferRouter.ts index 0727ba932..09476fa5a 100644 --- a/src/hooks/xvm/useXvmTransferRouter.ts +++ b/src/hooks/xvm/useXvmTransferRouter.ts @@ -1,6 +1,7 @@ import { ethers } from 'ethers'; import { useAccount, useBalance } from 'src/hooks'; -import { Erc20Token } from 'src/modules/token'; +import { formatEtherAsNumber } from "src/lib/formatters"; +import type { Erc20Token } from "src/modules/token"; import { useStore } from 'src/store'; import { computed, ref, watchEffect } from 'vue'; import { useRoute, useRouter } from 'vue-router'; @@ -34,7 +35,7 @@ export function useXvmTransferRouter() { }); const setNativeTokenBalance = (): void => { - nativeTokenBalance.value = Number(ethers.utils.formatEther(useableBalance.value)); + nativeTokenBalance.value = formatEtherAsNumber(useableBalance.value); }; const redirect = (): void => { diff --git a/src/lib/formatters.ts b/src/lib/formatters.ts new file mode 100644 index 000000000..5fa5aa7db --- /dev/null +++ b/src/lib/formatters.ts @@ -0,0 +1,30 @@ + +import { BN } from "@polkadot/util"; +import { formatEther } from "viem"; + +export function formatEtherAsString(wei: bigint | string | BN): string { + let weiBigInt: bigint; + + if (typeof wei === "bigint") { + weiBigInt = wei; + } else if (typeof wei === "string") { + weiBigInt = BigInt(wei); + } else if (wei instanceof BN) { + weiBigInt = BigInt(wei.toString()); + } else { + throw new Error("Invalid input type"); + } + + return formatEther(weiBigInt); +} + +export function formatEtherAsNumber(wei: bigint | string | BN): number { + const etherBigInt = BigInt(formatEtherAsString(wei)); + const MAX_SAFE_INTEGER_BIGINT = BigInt(Number.MAX_SAFE_INTEGER); + + if (etherBigInt > MAX_SAFE_INTEGER_BIGINT || etherBigInt < -MAX_SAFE_INTEGER_BIGINT) { + throw new Error("Value is outside of safe integer bounds for JavaScript numbers"); + } + + return Number(etherBigInt); +} diff --git a/src/modules/information/recent-history/stake/index.ts b/src/modules/information/recent-history/stake/index.ts index 8a2d967f0..17149568b 100644 --- a/src/modules/information/recent-history/stake/index.ts +++ b/src/modules/information/recent-history/stake/index.ts @@ -1,8 +1,8 @@ import { TOKEN_API_URL } from '@astar-network/astar-sdk-core'; import axios from 'axios'; -import { ethers } from 'ethers'; -import { DappCombinedInfo } from 'src/v2/models'; -import { RecentHistory, RecentHistoryTxType } from './../../index'; +import { formatEtherAsString } from "src/lib/formatters"; +import type { DappCombinedInfo } from "src/v2/models"; +import type { RecentHistory, RecentHistoryTxType } from "./../../index"; interface UserStakeHistory { timestamp: string; @@ -63,7 +63,7 @@ export const getStakeTxHistories = async ({ const note = dapp && dapp.dapp ? dapp.dapp.name : ''; const explorerUrl = subScan + '/extrinsic/' + it.transactionHash; return { - amount: ethers.utils.formatEther(it.amount), + amount: formatEtherAsString(it.amount), timestamp: String(Number(it.timestamp) / 1000), txType: it.transaction as RecentHistoryTxType, explorerUrl, diff --git a/src/staking-v3/components/my-staking/ModalUnbondDapp.vue b/src/staking-v3/components/my-staking/ModalUnbondDapp.vue index 1379db174..0b990a4c4 100644 --- a/src/staking-v3/components/my-staking/ModalUnbondDapp.vue +++ b/src/staking-v3/components/my-staking/ModalUnbondDapp.vue @@ -69,19 +69,20 @@