From 091eb09b2f5ab726991e8e765153446dd6ebebbf Mon Sep 17 00:00:00 2001 From: impelcrypto Date: Wed, 11 Jan 2023 16:53:40 +0800 Subject: [PATCH 1/4] feat: made a template structure for dapp-staking v2 --- src/hooks/custom-signature/message.ts | 42 +---- .../dapps-staking/useNominationTransfer.ts | 165 ------------------ src/hooks/index.ts | 1 - src/hooks/useClaimAll.ts | 62 +++---- src/v2/services/IDappStakingService.ts | 17 +- src/v2/services/IWalletService.ts | 4 +- .../implementations/DappStakingService.ts | 26 ++- .../implementations/MetamaskWalletService.ts | 82 +++++---- .../implementations/PolkadotWalletService.ts | 75 ++++++-- .../services/implementations/WalletService.ts | 18 +- 10 files changed, 184 insertions(+), 308 deletions(-) delete mode 100644 src/hooks/dapps-staking/useNominationTransfer.ts diff --git a/src/hooks/custom-signature/message.ts b/src/hooks/custom-signature/message.ts index 34cbe46cb..792ed2d36 100644 --- a/src/hooks/custom-signature/message.ts +++ b/src/hooks/custom-signature/message.ts @@ -6,7 +6,6 @@ import { hasExtrinsicFailedEvent } from 'src/store/dapp-staking/actions'; export enum TxType { dappsStaking = 'dappsStaking', - requiredClaim = 'requiredClaim', } export const displayCustomMessage = ({ @@ -29,12 +28,6 @@ export const displayCustomMessage = ({ senderAddress, t, }); - } else if (txType === TxType.requiredClaim) { - dispatchRequiredClaimMessage({ - result, - store, - t, - }); } }; @@ -49,7 +42,7 @@ const dispatchClaimMessage = ({ senderAddress: string; t: (...arg: any) => void; }): void => { - if (result.status.isFinalized) { + if (result.isCompleted) { if (!hasExtrinsicFailedEvent(result.events, store.dispatch)) { const totalClaimedStaker = calculateClaimedStaker({ events: result.events, @@ -77,36 +70,3 @@ const dispatchClaimMessage = ({ } } }; - -const dispatchRequiredClaimMessage = ({ - store, - result, - t, -}: { - store: Store; - result: ISubmittableResult; - t: (...arg: any) => void; -}): void => { - if (result.status.isFinalized) { - let errorMessage = ''; - const res = hasExtrinsicFailedEvent( - result.events, - store.dispatch, - (message: string) => (errorMessage = message) - ); - if (res) { - if (errorMessage.includes('TooManyEraStakeValues')) { - const msg = t('dappStaking.toast.requiredClaimFirst'); - - store.dispatch( - 'general/showAlertMsg', - { - msg, - alertType: 'error', - }, - { root: true } - ); - } - } - } -}; diff --git a/src/hooks/dapps-staking/useNominationTransfer.ts b/src/hooks/dapps-staking/useNominationTransfer.ts deleted file mode 100644 index e7d0b029e..000000000 --- a/src/hooks/dapps-staking/useNominationTransfer.ts +++ /dev/null @@ -1,165 +0,0 @@ -import { - useGasPrice, - useAccount, - useCustomSignature, - useGetMinStaking, - useStakingList, - useNetworkInfo, - useChainMetadata, -} from 'src/hooks'; -import { ISubmittableResult } from '@polkadot/types/types'; -import { ethers } from 'ethers'; -import { getDappAddressEnum } from 'src/modules/dapp-staking'; -import { showError } from 'src/modules/extrinsic'; -import { useStore } from 'src/store'; -import { computed, ref, watch, watchEffect } from 'vue'; -import { TxType } from 'src/hooks/custom-signature/message'; -import { ASTAR_DECIMALS, balanceFormatter } from 'src/hooks/helper/plasmUtils'; -import { signAndSend } from 'src/hooks/helper/wallet'; -import { $api } from 'src/boot/api'; - -export function useNominationTransfer() { - const { currentAccount } = useAccount(); - const { minStaking } = useGetMinStaking(); - const { stakingList } = useStakingList(); - useChainMetadata(); - const store = useStore(); - const addressTransferFrom = ref(currentAccount.value); - const isEnableNominationTransfer = ref(false); - const substrateAccounts = computed(() => store.getters['general/substrateAccounts']); - const { isCustomSig, handleResult, handleCustomExtrinsic } = useCustomSignature({ - txType: TxType.requiredClaim, - }); - const { selectedTip, nativeTipPrice, setSelectedTip } = useGasPrice(); - - const setIsEnableNominationTransfer = () => { - try { - const metadata = $api!.runtimeMetadata; - const metadataJson = JSON.stringify(metadata.toJSON()); - const result = metadataJson.includes('nomination_transfer'); - isEnableNominationTransfer.value = result; - } catch (error) { - console.error(error); - isEnableNominationTransfer.value = false; - } - }; - - const setAddressTransferFrom = (address: string) => { - addressTransferFrom.value = address; - }; - - const { nativeTokenSymbol } = useNetworkInfo(); - - const formattedTransferFrom = computed(() => { - const defaultData = { text: '', item: null, isNominationTransfer: false }; - try { - const stakingListRef = stakingList.value; - if (!stakingListRef) return defaultData; - const item = stakingListRef.find((it) => it.address === addressTransferFrom.value); - if (!item) return defaultData; - - const name = item.name === currentAccount.value ? 'Transferable Balance' : item.name; - const isNominationTransfer = item.address !== currentAccount.value; - - const formattedText = `${name} (${balanceFormatter(item.balance, ASTAR_DECIMALS)})`; - return { text: formattedText, item, isNominationTransfer }; - } catch (error) { - console.error(error); - return defaultData; - } - }); - - const formattedMinStaking = computed(() => { - return Number(ethers.utils.formatEther(minStaking.value).toString()); - }); - - const isDisabledNominationTransfer = ({ - amount, - stakedAmount, - }: { - amount: number; - stakedAmount: number; - }): boolean => { - if (!formattedTransferFrom.value.item) return false; - const stakeAmount = amount + stakedAmount; - const isNotEnoughMinAmount = formattedMinStaking.value > stakeAmount; - const transferFromRef = formattedTransferFrom.value; - if (!transferFromRef) return isNotEnoughMinAmount; - - const balTransferFrom = Number( - ethers.utils.formatEther(formattedTransferFrom.value.item.balance.toString()) - ); - const isShortageFromBalance = amount > balTransferFrom; - - const result = isNotEnoughMinAmount || isShortageFromBalance; - return result; - }; - - const nominationTransfer = async ({ - amount, - targetContractId, - }: { - amount: string; - targetContractId: string; - }): Promise => { - try { - const apiRef = $api!; - const transferFromRef = formattedTransferFrom.value; - if (!transferFromRef || !formattedTransferFrom.value.item) return false; - - const value = ethers.utils.parseEther(String(amount)).toString(); - const transaction = apiRef.tx.dappsStaking.nominationTransfer( - getDappAddressEnum(formattedTransferFrom.value.item.address), - value, - getDappAddressEnum(targetContractId) - ); - - const txResHandler = async (result: ISubmittableResult): Promise => { - return await handleResult(result); - }; - - await signAndSend({ - transaction, - senderAddress: currentAccount.value, - substrateAccounts: substrateAccounts.value, - isCustomSignature: isCustomSig.value, - txResHandler, - handleCustomExtrinsic, - dispatch: store.dispatch, - tip: selectedTip.value.price, - }); - return true; - } catch (error: any) { - console.error(error); - showError(store.dispatch, error.message); - return false; - } - }; - - watchEffect(() => { - setIsEnableNominationTransfer(); - }); - - watch( - [currentAccount], - () => { - addressTransferFrom.value = currentAccount.value; - }, - { immediate: true } - ); - - return { - formattedTransferFrom, - addressTransferFrom, - currentAccount, - formattedMinStaking, - nativeTokenSymbol, - isEnableNominationTransfer, - setAddressTransferFrom, - nominationTransfer, - isDisabledNominationTransfer, - selectedTip, - nativeTipPrice, - setSelectedTip, - }; -} diff --git a/src/hooks/index.ts b/src/hooks/index.ts index 6c416bda7..965ac783a 100644 --- a/src/hooks/index.ts +++ b/src/hooks/index.ts @@ -25,7 +25,6 @@ export * from './useAppRouter'; export * from './chain/useAvgBlockTime'; export * from './dapps-staking/useStake'; export * from './dapps-staking/useUnbond'; -export * from './dapps-staking/useNominationTransfer'; export * from './dapps-staking/useStakerInfo'; export * from './dapps-staking/useStakingList'; export * from './dapps-staking/useSignPayload'; diff --git a/src/hooks/useClaimAll.ts b/src/hooks/useClaimAll.ts index a2611c835..300d5c08e 100644 --- a/src/hooks/useClaimAll.ts +++ b/src/hooks/useClaimAll.ts @@ -1,19 +1,17 @@ -import { bool } from '@polkadot/types'; import { ISubmittableResult } from '@polkadot/types/types'; import { BN } from '@polkadot/util'; import { $api } from 'boot/api'; -import { useCurrentEra, useCustomSignature, useGasPrice } from 'src/hooks'; -import { TxType } from 'src/hooks/custom-signature/message'; +import { useCurrentEra } from 'src/hooks'; +import { displayCustomMessage, TxType } from 'src/hooks/custom-signature/message'; import { ExtrinsicPayload } from 'src/hooks/helper'; import { getIndividualClaimTxs, PayloadWithWeight } from 'src/hooks/helper/claim'; -import { signAndSend } from 'src/hooks/helper/wallet'; import { useStore } from 'src/store'; -import { hasExtrinsicFailedEvent } from 'src/store/dapp-staking/actions'; import { container } from 'src/v2/common'; import { DappCombinedInfo } from 'src/v2/models/DappsStaking'; import { IDappStakingService } from 'src/v2/services'; import { Symbols } from 'src/v2/symbols'; import { computed, ref, watchEffect } from 'vue'; +import { useI18n } from 'vue-i18n'; const MAX_BATCH_WEIGHT = new BN('50000000000'); @@ -25,16 +23,11 @@ export function useClaimAll() { const isLoading = ref(true); const store = useStore(); const senderAddress = computed(() => store.getters['general/selectedAddress']); - const substrateAccounts = computed(() => store.getters['general/substrateAccounts']); const dapps = computed(() => store.getters['dapps/getAllDapps']); const isH160 = computed(() => store.getters['general/isH160Formatted']); const isSendingTx = computed(() => store.getters['general/isLoading']); - const { nativeTipPrice } = useGasPrice(); - + const { t } = useI18n(); const { era } = useCurrentEra(); - const { handleResult, handleCustomExtrinsic, isCustomSig } = useCustomSignature({ - txType: TxType.dappsStaking, - }); watchEffect(async () => { try { @@ -57,7 +50,6 @@ export function useClaimAll() { senderAddress: senderAddressRef, currentEra: era.value, }); - return transactions.length ? transactions : null; } else { return null; @@ -92,9 +84,10 @@ export function useClaimAll() { } const txsToExecute: ExtrinsicPayload[] = []; - let totalWeight: BN = new BN(0); + let totalWeight: BN = new BN('0'); for (let i = 0; i < batchTxsRef.length; i++) { const tx = batchTxsRef[i]; + console.log('tx.weight', tx.weight.toString()); if (totalWeight.add(tx.weight).gt(MAX_BATCH_WEIGHT)) { break; } @@ -103,42 +96,27 @@ export function useClaimAll() { totalWeight = totalWeight.add(tx.weight); } - // The fix causes problems and confusion for stakers because rewards are not restaked, - // second thing is that developers are unable to stake. - // Need to change approach - // Temporary disable restaking reward to avoid possible claim errors. - // const dappStakingRepository = container.get( - // Symbols.DappStakingRepository - // ); - // const ledger = await dappStakingRepository.getLedger(senderAddress.value); - - // if (ledger.rewardDestination === RewardDestination.StakeBalance) { - // txsToExecute.unshift(api.tx.dappsStaking.setRewardDestination(RewardDestination.FreeBalance)); - // txsToExecute.push(api.tx.dappsStaking.setRewardDestination(RewardDestination.StakeBalance)); - // } - console.info( `Batch weight: ${totalWeight.toString()}, transactions no. ${txsToExecute.length}` ); const transaction = api.tx.utility.batch(txsToExecute); + const finalizedCallback = (result: ISubmittableResult): void => { + displayCustomMessage({ + txType: TxType.dappsStaking, + result, + senderAddress: senderAddress.value, + store, + t, + }); + }; try { - const txResHandler = async (result: ISubmittableResult): Promise => { - const res = await handleResult(result); - hasExtrinsicFailedEvent(result.events, store.dispatch); - return res; - }; - - await signAndSend({ - transaction, + const dappStakingService = container.get(Symbols.DappStakingService); + console.log('transaction', transaction); + await dappStakingService.claim({ senderAddress: senderAddress.value, - substrateAccounts: substrateAccounts.value, - isCustomSignature: isCustomSig.value, - txResHandler, - handleCustomExtrinsic, - dispatch: store.dispatch, - tip: nativeTipPrice.value.fast, //note: this is a quick hack to speed of the tx. We should add the custom speed modal later - //tip: selectedTip.value.price, + transaction, + finalizedCallback, }); } catch (error: any) { console.error(error.message); diff --git a/src/v2/services/IDappStakingService.ts b/src/v2/services/IDappStakingService.ts index 2aa9b81b3..f286c797d 100644 --- a/src/v2/services/IDappStakingService.ts +++ b/src/v2/services/IDappStakingService.ts @@ -1,3 +1,5 @@ +import { SubmittableExtrinsic } from '@polkadot/api/types'; +import { ISubmittableResult } from '@polkadot/types/types'; import { BN } from '@polkadot/util'; import { EditDappItem } from 'src/store/dapp-staking/state'; import { TvlModel } from 'src/v2/models'; @@ -14,7 +16,7 @@ export interface IDappStakingService { getTvl(): Promise; /** - * Stakes given ammount to contract. + * Stakes given amount to contract. * @param contractAddress Contract address. * @param stakerAddress Staked address. * @param amount Amount to stake. @@ -80,4 +82,17 @@ export interface IDappStakingService { * @param accountAddress User account address */ canClaimRewardWithoutErrors(accountAddress: string): Promise; + + /** + * claim dApp staking rewards + */ + claim({ + senderAddress, + transaction, + finalizedCallback, + }: { + senderAddress: string; + transaction: SubmittableExtrinsic<'promise'>; + finalizedCallback: (result: ISubmittableResult) => void; + }): Promise; } diff --git a/src/v2/services/IWalletService.ts b/src/v2/services/IWalletService.ts index 03337e552..3dd6b6ca4 100644 --- a/src/v2/services/IWalletService.ts +++ b/src/v2/services/IWalletService.ts @@ -1,4 +1,5 @@ import { SubmittableExtrinsic } from '@polkadot/api/types'; +import { ISubmittableResult } from '@polkadot/types/types'; export enum WalletType { Metamask = 'Metamask', @@ -17,6 +18,7 @@ export interface IWalletService { extrinsic: SubmittableExtrinsic<'promise'>, senderAddress: string, successMessage?: string, - transactionTip?: number + transactionTip?: number, + finalizedCallback?: (result?: ISubmittableResult) => void ): Promise; } diff --git a/src/v2/services/implementations/DappStakingService.ts b/src/v2/services/implementations/DappStakingService.ts index 2a0175ae8..1f19aaf1a 100644 --- a/src/v2/services/implementations/DappStakingService.ts +++ b/src/v2/services/implementations/DappStakingService.ts @@ -5,7 +5,7 @@ import { astarMainnetNativeToken, ASTAR_NATIVE_TOKEN } from 'src/config/chain'; import { EditDappItem } from 'src/store/dapp-staking/state'; import { Guard } from 'src/v2/common'; import { TvlModel } from 'src/v2/models'; -import { DappCombinedInfo, StakerInfo, RewardDestination } from 'src/v2/models/DappsStaking'; +import { DappCombinedInfo, StakerInfo } from 'src/v2/models/DappsStaking'; import { IDappStakingRepository, IMetadataRepository, @@ -16,6 +16,8 @@ import { IBalanceFormatterService, IDappStakingService } from 'src/v2/services'; import { Symbols } from 'src/v2/symbols'; import { IWalletService } from '../IWalletService'; import { AccountLedger } from 'src/v2/models/DappsStaking'; +import { SubmittableExtrinsic } from '@polkadot/api/types'; +import { ISubmittableResult } from '@polkadot/types/types'; @injectable() export class DappStakingService implements IDappStakingService { @@ -193,4 +195,26 @@ export class DappStakingService implements IDappStakingService { return true; } + + public async claim({ + senderAddress, + transaction, + finalizedCallback, + }: { + senderAddress: string; + transaction: SubmittableExtrinsic<'promise'>; + finalizedCallback: (result?: ISubmittableResult) => void; + }): Promise { + Guard.ThrowIfUndefined('senderAddress', senderAddress); + Guard.ThrowIfUndefined('transaction', transaction); + + console.log('sign'); + await this.wallet.signAndSend( + transaction, + senderAddress, + undefined, + undefined, + finalizedCallback + ); + } } diff --git a/src/v2/services/implementations/MetamaskWalletService.ts b/src/v2/services/implementations/MetamaskWalletService.ts index 4302bd7ad..bc300da93 100644 --- a/src/v2/services/implementations/MetamaskWalletService.ts +++ b/src/v2/services/implementations/MetamaskWalletService.ts @@ -35,57 +35,67 @@ export class MetamaskWalletService extends WalletService implements IWalletServi public async signAndSend( extrinsic: SubmittableExtrinsic<'promise', ISubmittableResult>, senderAddress: string, - successMessage?: string + successMessage?: string, + transactionTip?: number, + finalizedCallback?: (result?: ISubmittableResult) => void ): Promise { Guard.ThrowIfUndefined('extrinsic', extrinsic); Guard.ThrowIfUndefined('senderAddress', senderAddress); - let result = null; - try { - const account = await this.systemRepository.getAccountInfo(senderAddress); - const payload = await this.ethCallRepository.getPayload(extrinsic, account.nonce); + return new Promise(async (resolve) => { + const account = await this.systemRepository.getAccountInfo(senderAddress); + const payload = await this.ethCallRepository.getPayload(extrinsic, account.nonce); - const web3 = new Web3(this.provider as any); - const accounts = await web3.eth.getAccounts(); + const web3 = new Web3(this.provider as any); + const accounts = await web3.eth.getAccounts(); - const signedPayload = await this.provider.request({ - method: 'personal_sign', - params: [accounts[0], payload], - }); + const signedPayload = await this.provider.request({ + method: 'personal_sign', + params: [accounts[0], payload], + }); - const call = await this.ethCallRepository.getCall( - extrinsic, - senderAddress, - signedPayload as string, - account.nonce - ); + const call = await this.ethCallRepository.getCall( + extrinsic, + senderAddress, + signedPayload as string, + account.nonce + ); - await call.send((result) => { - if (result.isFinalized) { - if (!this.isExtrinsicFailed(result.events)) { - this.eventAggregator.publish( - new ExtrinsicStatusMessage( - true, - successMessage ?? 'Transaction successfully executed', - `${extrinsic.method.section}.${extrinsic.method.method}`, - result.txHash.toHex() - ) - ); - } + const unsub = await call.send((result) => { + try { + if (result.isCompleted) { + if (!this.isExtrinsicFailed(result.events)) { + this.eventAggregator.publish( + new ExtrinsicStatusMessage( + true, + successMessage ?? 'Transaction successfully executed', + `${extrinsic.method.section}.${extrinsic.method.method}`, + result.txHash.toHex() + ) + ); + } - this.eventAggregator.publish(new BusyMessage(false)); - } else { - this.eventAggregator.publish(new BusyMessage(true)); - } + this.eventAggregator.publish(new BusyMessage(false)); + if (finalizedCallback) { + finalizedCallback(result); + } + resolve(result.txHash.toHex()); + unsub(); + } else { + this.eventAggregator.publish(new BusyMessage(true)); + } + } catch (error) { + this.eventAggregator.publish(new BusyMessage(false)); + unsub(); + } + }); }); - result = call.hash.toHex(); } catch (e) { const error = e as unknown as Error; this.eventAggregator.publish(new ExtrinsicStatusMessage(false, error.message)); this.eventAggregator.publish(new BusyMessage(false)); + throw Error(error.message); } - - return result; } } diff --git a/src/v2/services/implementations/PolkadotWalletService.ts b/src/v2/services/implementations/PolkadotWalletService.ts index 7c161d67e..0465af353 100644 --- a/src/v2/services/implementations/PolkadotWalletService.ts +++ b/src/v2/services/implementations/PolkadotWalletService.ts @@ -1,3 +1,4 @@ +import { isMobileDevice } from './../../../hooks/helper/wallet'; import { SubmittableExtrinsic } from '@polkadot/api/types'; import { ISubmittableResult, Signer } from '@polkadot/types/types'; import { InjectedExtension } from '@polkadot/extension-inject/types'; @@ -36,7 +37,8 @@ export class PolkadotWalletService extends WalletService implements IWalletServi extrinsic: SubmittableExtrinsic<'promise', ISubmittableResult>, senderAddress: string, successMessage?: string, - transactionTip?: number + transactionTip?: number, + finalizedCallback?: (result?: ISubmittableResult) => void ): Promise { Guard.ThrowIfUndefined('extrinsic', extrinsic); Guard.ThrowIfUndefined('senderAddress', senderAddress); @@ -44,6 +46,7 @@ export class PolkadotWalletService extends WalletService implements IWalletServi let result: string | null = null; try { return new Promise(async (resolve) => { + !isMobileDevice && this.detectExtensionsAction(true); await this.checkExtension(); let tip = transactionTip?.toString(); if (!tip) { @@ -53,7 +56,7 @@ export class PolkadotWalletService extends WalletService implements IWalletServi console.info('transaction tip', tip); - await extrinsic.signAndSend( + const unsub = await extrinsic.signAndSend( senderAddress, { signer: await this.getSigner(senderAddress), @@ -61,22 +64,34 @@ export class PolkadotWalletService extends WalletService implements IWalletServi tip, }, (result) => { - if (result.isFinalized) { - if (!this.isExtrinsicFailed(result.events)) { - this.eventAggregator.publish( - new ExtrinsicStatusMessage( - true, - successMessage ?? 'Transaction successfully executed', - `${extrinsic.method.section}.${extrinsic.method.method}`, - result.txHash.toHex() - ) - ); + try { + !isMobileDevice && this.detectExtensionsAction(false); + if (result.isCompleted) { + if (!this.isExtrinsicFailed(result.events)) { + this.eventAggregator.publish( + new ExtrinsicStatusMessage( + true, + successMessage ?? 'Transaction successfully executed', + `${extrinsic.method.section}.${extrinsic.method.method}`, + result.txHash.toHex() + ) + ); + } + + this.eventAggregator.publish(new BusyMessage(false)); + if (finalizedCallback) { + finalizedCallback(result); + } + resolve(extrinsic.hash.toHex()); + unsub(); + } else { + if (isMobileDevice && !result.isCompleted) { + this.eventAggregator.publish(new BusyMessage(true)); + } } - + } catch (error) { this.eventAggregator.publish(new BusyMessage(false)); - resolve(extrinsic.hash.toHex()); - } else { - !result.isCompleted && this.eventAggregator.publish(new BusyMessage(true)); + unsub(); } } ); @@ -135,4 +150,32 @@ export class PolkadotWalletService extends WalletService implements IWalletServi this.extensions.push(...extensions); } } + + // Memo: detects status in the wallet extension + // Fixme: doesn't work on MathWallet Mobile + // Ref: https://github.com/polkadot-js/extension/issues/674 + // Ref: https://github.com/polkadot-js/extension/blob/297b2af14c68574b24bb8fdeda2208c473eccf43/packages/extension/src/page.ts#L10-L22 + private detectExtensionsAction(isMonitorExtension: boolean): void { + const handleDetectSign = (listener: any): void => { + const { source, data } = listener; + if (source !== window || !data.origin) { + return; + } + if (data.id) { + if (data.response && data.response.hasOwnProperty('signature')) { + this.eventAggregator.publish(new BusyMessage(true)); + return; + } + // Memo: detect if the transaction was canceled by users + if (data.error === 'Cancelled') { + this.eventAggregator.publish(new BusyMessage(false)); + throw Error(data.error); + } + } + }; + + isMonitorExtension + ? window.addEventListener('message', handleDetectSign) + : window.removeEventListener('message', handleDetectSign); + } } diff --git a/src/v2/services/implementations/WalletService.ts b/src/v2/services/implementations/WalletService.ts index 89ff16bc0..70db6589f 100644 --- a/src/v2/services/implementations/WalletService.ts +++ b/src/v2/services/implementations/WalletService.ts @@ -1,7 +1,11 @@ +import { Null, Result } from '@polkadot/types-codec'; +import { DispatchError, EventRecord } from '@polkadot/types/interfaces'; import { ITuple } from '@polkadot/types/types'; -import { EventRecord, DispatchError } from '@polkadot/types/interfaces'; import { ExtrinsicStatusMessage, IEventAggregator } from 'src/v2/messaging'; -import { Null, Result } from '@polkadot/types-codec'; + +enum ErrorCode { + TooManyEraStakeValues = 'dappsStaking.TooManyEraStakeValues', +} export class WalletService { constructor(protected readonly eventAggregator: IEventAggregator) {} @@ -34,9 +38,15 @@ export class WalletService { }); if (result) { - this.eventAggregator.publish(new ExtrinsicStatusMessage(false, message)); + let msg = ''; + if (message === ErrorCode.TooManyEraStakeValues) { + msg = 'Please claim your rewards before sending transaction'; + } else { + msg = message; + } + this.eventAggregator.publish(new ExtrinsicStatusMessage(false, msg)); + throw Error(msg); } - return result; } From 7701920ab5ed21e7a785e1d398153c1a839d4309 Mon Sep 17 00:00:00 2001 From: impelcrypto Date: Wed, 11 Jan 2023 17:04:40 +0800 Subject: [PATCH 2/4] refactor: cleanup --- src/hooks/useClaimAll.ts | 1 - src/v2/services/implementations/DappStakingService.ts | 1 - 2 files changed, 2 deletions(-) diff --git a/src/hooks/useClaimAll.ts b/src/hooks/useClaimAll.ts index 300d5c08e..4696b8b64 100644 --- a/src/hooks/useClaimAll.ts +++ b/src/hooks/useClaimAll.ts @@ -112,7 +112,6 @@ export function useClaimAll() { try { const dappStakingService = container.get(Symbols.DappStakingService); - console.log('transaction', transaction); await dappStakingService.claim({ senderAddress: senderAddress.value, transaction, diff --git a/src/v2/services/implementations/DappStakingService.ts b/src/v2/services/implementations/DappStakingService.ts index 1f19aaf1a..248941f53 100644 --- a/src/v2/services/implementations/DappStakingService.ts +++ b/src/v2/services/implementations/DappStakingService.ts @@ -208,7 +208,6 @@ export class DappStakingService implements IDappStakingService { Guard.ThrowIfUndefined('senderAddress', senderAddress); Guard.ThrowIfUndefined('transaction', transaction); - console.log('sign'); await this.wallet.signAndSend( transaction, senderAddress, From 8cc0fd65e9dad24d2d7cddfa0925baffe90b31b7 Mon Sep 17 00:00:00 2001 From: Will Date: Fri, 13 Jan 2023 16:54:32 +0900 Subject: [PATCH 3/4] refactor useStakerInfo hook (#664) * add getStakeInfo function in repo * migrate getStake function to v2 * fix: modified fetching 'My staking information' logic when users change the account (#666) Co-authored-by: impelcrypto <92044428+impelcrypto@users.noreply.github.com> --- src/hooks/dapps-staking/useStakerInfo.ts | 21 ++-- src/modules/dapp-staking/utils/index.ts | 6 - src/v2/repositories/IDappStakingRepository.ts | 3 + .../implementations/DappStakingRepository.ts | 117 +++++++++++++++++- src/v2/services/IDappStakingService.ts | 3 + .../implementations/DappStakingService.ts | 10 ++ .../repositories/DappStakingRepositoryMock.ts | 9 +- .../test/services/DappStakingService.spec.ts | 6 + 8 files changed, 150 insertions(+), 25 deletions(-) diff --git a/src/hooks/dapps-staking/useStakerInfo.ts b/src/hooks/dapps-staking/useStakerInfo.ts index dd656c652..00be1cbcd 100644 --- a/src/hooks/dapps-staking/useStakerInfo.ts +++ b/src/hooks/dapps-staking/useStakerInfo.ts @@ -1,14 +1,15 @@ import { BN } from 'bn.js'; -import { $api } from 'boot/api'; import { ethers } from 'ethers'; import { useAccount } from 'src/hooks'; -import { getStakeInfo } from 'src/modules/dapp-staking/utils/index'; import { useStore } from 'src/store'; import { StakeInfo } from 'src/store/dapp-staking/actions'; import { DappItem } from 'src/store/dapp-staking/state'; import { DappCombinedInfo } from 'src/v2/models/DappsStaking'; import { computed, ref, watch, watchEffect } from 'vue'; import { useI18n } from 'vue-i18n'; +import { container } from 'src/v2/common'; +import { Symbols } from 'src/v2/symbols'; +import { IDappStakingService } from 'src/v2/services'; export type MyStakeInfo = StakeInfo | DappItem; @@ -26,21 +27,17 @@ export function useStakerInfo() { const dapps = computed(() => store.getters['dapps/getAllDapps']); const isH160 = computed(() => store.getters['general/isH160Formatted']); - const getData = async (address: string) => { - return await getStakeInfo({ - api: $api!, - dappAddress: address, - currentAccount: currentAccount.value, - }); - }; - const setStakeInfo = async () => { let data: StakeInfo[] = []; let myData: MyStakeInfo[] = []; + const dappStakingService = container.get(Symbols.DappStakingService); data = await Promise.all( dapps.value.map(async (it: DappCombinedInfo) => { - const stakeData = await getData(it.dapp?.address!); + const stakeData = await dappStakingService.getStakeInfo( + it.dapp?.address!, + currentAccount.value + ); if (stakeData?.hasStake) { myData.push({ ...stakeData, ...it.dapp }); } @@ -65,7 +62,7 @@ export function useStakerInfo() { }; watchEffect(async () => { - if (isLoading.value || !dapps.value) { + if (isLoading.value || !dapps.value || !currentAccount.value) { return; } try { diff --git a/src/modules/dapp-staking/utils/index.ts b/src/modules/dapp-staking/utils/index.ts index fc5c3b527..d8dd4d74b 100644 --- a/src/modules/dapp-staking/utils/index.ts +++ b/src/modules/dapp-staking/utils/index.ts @@ -10,12 +10,6 @@ import { EraStakingPoints, StakeInfo } from 'src/store/dapp-staking/actions'; import { isEthereumAddress } from '@polkadot/util-crypto'; import { isValidAddressPolkadotAddress } from 'src/hooks/helper/plasmUtils'; -interface StakeData { - address: string; - balance: string; - name: string; -} - export const checkIsLimitedProvider = (): boolean => { const limitedProvider = ['onfinality']; const selectedEndpoint = JSON.parse( diff --git a/src/v2/repositories/IDappStakingRepository.ts b/src/v2/repositories/IDappStakingRepository.ts index 604e974df..00cdc9b2b 100644 --- a/src/v2/repositories/IDappStakingRepository.ts +++ b/src/v2/repositories/IDappStakingRepository.ts @@ -6,6 +6,7 @@ import { EditDappItem } from 'src/store/dapp-staking/state'; import { AccountLedger } from '../models/DappsStaking'; import { u32 } from '@polkadot/types'; import { GeneralStakerInfo } from 'src/hooks/helper/claim'; +import { StakeInfo } from 'src/store/dapp-staking/actions'; /** * Definition of repository to access dapps staking pallet. @@ -90,4 +91,6 @@ export interface IDappStakingRepository { stakerAddress: string, contractAddress: string ): Promise>; + + getStakeInfo(dappAddress: string, currentAccount: string): Promise; } diff --git a/src/v2/repositories/implementations/DappStakingRepository.ts b/src/v2/repositories/implementations/DappStakingRepository.ts index c322f1af9..6cb93b7cd 100644 --- a/src/v2/repositories/implementations/DappStakingRepository.ts +++ b/src/v2/repositories/implementations/DappStakingRepository.ts @@ -1,14 +1,14 @@ -import { isValidAddressPolkadotAddress } from 'src/hooks/helper/plasmUtils'; +import { isValidAddressPolkadotAddress, balanceFormatter } from 'src/hooks/helper/plasmUtils'; import { BN } from '@polkadot/util'; import { u32, Option, Struct } from '@polkadot/types'; import { Codec, ISubmittableResult } from '@polkadot/types/types'; import type { SubmittableExtrinsic } from '@polkadot/api/types'; -import { AccountId, Balance, EraIndex, UnknownTransaction } from '@polkadot/types/interfaces'; +import { AccountId, Balance, EraIndex } from '@polkadot/types/interfaces'; +import { ApiPromise } from '@polkadot/api'; import { injectable, inject } from 'inversify'; import { IDappStakingRepository } from 'src/v2/repositories'; import { IApi } from 'src/v2/integration'; import { Symbols } from 'src/v2/symbols'; -import { ApiPromise } from '@polkadot/api'; import { DappStakingConstants, RewardDestination, @@ -17,14 +17,17 @@ import { StakerInfo, } from 'src/v2/models/DappsStaking'; import { EventAggregator, NewEraMessage } from 'src/v2/messaging'; -import { GeneralStakerInfo } from 'src/hooks/helper/claim'; +import { GeneralStakerInfo, checkIsDappRegistered } from 'src/hooks/helper/claim'; import { ethers } from 'ethers'; import { EditDappItem } from 'src/store/dapp-staking/state'; +import { StakeInfo, EraStakingPoints } from 'src/store/dapp-staking/actions'; import { TOKEN_API_URL } from 'src/modules/token-api'; import axios from 'axios'; import { getDappAddressEnum } from 'src/modules/dapp-staking/utils'; import { Guard } from 'src/v2/common'; import { AccountLedger } from 'src/v2/models/DappsStaking'; +import { wait } from 'src/hooks/helper/common'; +import { checkIsLimitedProvider } from 'src/modules/dapp-staking/utils'; // TODO type generation interface EraInfo extends Struct { @@ -325,7 +328,109 @@ export class DappStakingRepository implements IDappStakingRepository { } } - private getAddressEnum(address: string) { - return { Evm: address }; + public async getStakeInfo( + dappAddress: string, + currentAccount: string + ): Promise { + const api = await this.api.getApi(); + const stakeInfo = new Promise(async (resolve) => { + const data = await this.handleGetStakeInfo({ api, dappAddress, currentAccount }); + resolve(data); + }); + const fallbackTimeout = new Promise(async (resolve) => { + const timeout = 4 * 1000; + await wait(timeout); + resolve('timeout'); + }); + + const race = Promise.race([stakeInfo, fallbackTimeout]); + const result = race.then((res) => { + if (res === 'timeout') { + return undefined; + } else { + return res as StakeInfo; + } + }); + return result; + } + + private async handleGetStakeInfo({ + api, + dappAddress, + currentAccount, + }: { + api: ApiPromise; + dappAddress: string; + currentAccount: string; + }): Promise { + const initialYourStake = { + formatted: '', + denomAmount: new BN('0'), + }; + + const stakeInfo = await this.getLatestStakePoint(api, dappAddress); + if (!stakeInfo) return undefined; + + const data = { + totalStake: balanceFormatter(stakeInfo.total.toString()), + yourStake: initialYourStake, + claimedRewards: '0', + hasStake: false, + stakersCount: Number(stakeInfo.numberOfStakers.toString()), + dappAddress, + isRegistered: true, + }; + + try { + const [stakerInfo, { isRegistered }] = await Promise.all([ + api.query.dappsStaking.generalStakerInfo( + currentAccount, + getDappAddressEnum(dappAddress) + ), + checkIsDappRegistered({ dappAddress, api }), + ]); + + const balance = stakerInfo.stakes.length && stakerInfo.stakes.slice(-1)[0].staked.toString(); + const yourStake = balance + ? { + formatted: balanceFormatter(balance), + denomAmount: new BN(balance.toString()), + } + : initialYourStake; + + return { + ...data, + hasStake: Number(balance.toString()) > 0, + yourStake, + isRegistered, + }; + } catch (error) { + return data; + } + } + + private async getLatestStakePoint( + api: ApiPromise, + contract: string + ): Promise { + if (!contract) { + return undefined; + } + const currentEra = await (await api.query.dappsStaking.currentEra()).toNumber(); + const contractAddress = getDappAddressEnum(contract); + // iterate from currentEra backwards until you find record for ContractEraStake + for (let era = currentEra; era > 0; era -= 1) { + // Memo: wait for avoiding provider limitation + checkIsLimitedProvider() && (await wait(200)); + const stakeInfoPromise = await api.query.dappsStaking.contractEraStake< + Option + >(contractAddress, era); + const stakeInfo = stakeInfoPromise.unwrapOr(undefined); + if (stakeInfo) { + return stakeInfo; + } + } + + return undefined; } } diff --git a/src/v2/services/IDappStakingService.ts b/src/v2/services/IDappStakingService.ts index f286c797d..0ca65b5d6 100644 --- a/src/v2/services/IDappStakingService.ts +++ b/src/v2/services/IDappStakingService.ts @@ -5,6 +5,7 @@ import { EditDappItem } from 'src/store/dapp-staking/state'; import { TvlModel } from 'src/v2/models'; import { DappCombinedInfo, StakerInfo } from '../models/DappsStaking'; import { AccountLedger } from '../models/DappsStaking'; +import { StakeInfo } from 'src/store/dapp-staking/actions'; /** * Definition of service used to manage dapps staking. @@ -95,4 +96,6 @@ export interface IDappStakingService { transaction: SubmittableExtrinsic<'promise'>; finalizedCallback: (result: ISubmittableResult) => void; }): Promise; + + getStakeInfo(dappAddress: string, currentAccount: string): Promise; } diff --git a/src/v2/services/implementations/DappStakingService.ts b/src/v2/services/implementations/DappStakingService.ts index 248941f53..752188ad4 100644 --- a/src/v2/services/implementations/DappStakingService.ts +++ b/src/v2/services/implementations/DappStakingService.ts @@ -18,6 +18,7 @@ import { IWalletService } from '../IWalletService'; import { AccountLedger } from 'src/v2/models/DappsStaking'; import { SubmittableExtrinsic } from '@polkadot/api/types'; import { ISubmittableResult } from '@polkadot/types/types'; +import { StakeInfo } from 'src/store/dapp-staking/actions'; @injectable() export class DappStakingService implements IDappStakingService { @@ -216,4 +217,13 @@ export class DappStakingService implements IDappStakingService { finalizedCallback ); } + + public async getStakeInfo( + dappAddress: string, + currentAccount: string + ): Promise { + Guard.ThrowIfUndefined('currentAccount', currentAccount); + + return await this.dappStakingRepository.getStakeInfo(dappAddress, currentAccount); + } } diff --git a/src/v2/test/mocks/repositories/DappStakingRepositoryMock.ts b/src/v2/test/mocks/repositories/DappStakingRepositoryMock.ts index 144a988a1..96761ddfe 100644 --- a/src/v2/test/mocks/repositories/DappStakingRepositoryMock.ts +++ b/src/v2/test/mocks/repositories/DappStakingRepositoryMock.ts @@ -8,7 +8,7 @@ import { EditDappItem } from 'src/store/dapp-staking/state'; import { AccountLedger } from 'src/v2/models/DappsStaking'; import { u32 } from '@polkadot/types'; import { GeneralStakerInfo } from 'src/hooks/helper/claim'; - +import { StakeInfo } from 'src/store/dapp-staking/actions'; @injectable() export class DappStakingRepositoryMock implements IDappStakingRepository { public readonly bondAndStakeCallMock = jest.fn(); @@ -105,4 +105,11 @@ export class DappStakingRepositoryMock implements IDappStakingRepository { ): Promise> { return new Map(); } + + public async getStakeInfo( + dappAddress: string, + currentAccount: string + ): Promise { + return {} as StakeInfo; + } } diff --git a/src/v2/test/services/DappStakingService.spec.ts b/src/v2/test/services/DappStakingService.spec.ts index a7a53d0ec..bf85312c4 100644 --- a/src/v2/test/services/DappStakingService.spec.ts +++ b/src/v2/test/services/DappStakingService.spec.ts @@ -98,4 +98,10 @@ describe('DappStakingService.ts', () => { expect(wallet.walletSignAndSendMock).toBeCalledTimes(1); expect(wallet.walletSignAndSendMock).toBeCalledWith({}, stakerAddress, expect.any(String)); }); + + it('getStakeInfo - throws exception if invalid argument', async () => { + const sut = container.get(Symbols.DappStakingService); + + await expect(sut.getStakeInfo('', '')).rejects.toThrow('Invalid argument currentAccount'); + }); }); From 58d0afa8062ab3e3fba12931341bd5dc76b9c17e Mon Sep 17 00:00:00 2001 From: Will Date: Mon, 16 Jan 2023 16:07:21 +0900 Subject: [PATCH 4/4] Refactor/unbonding hook (#670) * diminish snowpack & put space on tvl text (#663) * feat:Support for BNC and vDOT assets (#658) * feat:Support for BNC and vDOT assets * Fix some errors * Update BifrostXcmRepository.ts * Add some constants * 0.0.9 * feat: updated japanese translation (#665) * go back logo (#667) * migrate for useUnbonding hook Co-authored-by: NingBo Wang <2536935847@qq.com> Co-authored-by: github-actions Co-authored-by: impelcrypto <92044428+impelcrypto@users.noreply.github.com> --- package.json | 2 +- src/components/common/Logo.vue | 2 +- .../dapp-staking/my-staking/TopMetric.vue | 4 +- .../my-staking/styles/top-metric.scss | 1 + src/hooks/custom-signature/message.ts | 35 ++ src/hooks/dapps-staking/useUnbonding.ts | 53 +-- src/hooks/useClaimAll.ts | 2 +- src/i18n/ja/index.ts | 443 +++++++++++++++++- src/modules/xcm/index.ts | 9 + src/modules/xcm/tokens/index.ts | 20 + .../config/xcm/XcmRepositoryConfiguration.ts | 2 + src/v2/models/XcmModels.ts | 2 + src/v2/repositories/implementations/index.ts | 1 + .../xcm/BifrostXcmRepository.ts | 104 ++++ src/v2/services/IDappStakingService.ts | 2 +- .../implementations/DappStakingService.ts | 2 +- 16 files changed, 629 insertions(+), 55 deletions(-) create mode 100644 src/v2/repositories/implementations/xcm/BifrostXcmRepository.ts diff --git a/package.json b/package.json index bd24bff8a..043575304 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "astar-portal", - "version": "0.0.8", + "version": "0.0.9", "description": "Your one-stop platform for the Astar ecosystem - Wallet / Staking / Bridging", "productName": "Astar Portal - Astar & Shiden Network", "author": "Astar Network", diff --git a/src/components/common/Logo.vue b/src/components/common/Logo.vue index 6ef13d895..5328dcec4 100644 --- a/src/components/common/Logo.vue +++ b/src/components/common/Logo.vue @@ -7,7 +7,7 @@ diff --git a/src/components/dapp-staking/my-staking/TopMetric.vue b/src/components/dapp-staking/my-staking/TopMetric.vue index 4072e1322..6dcf9fb9a 100644 --- a/src/components/dapp-staking/my-staking/TopMetric.vue +++ b/src/components/dapp-staking/my-staking/TopMetric.vue @@ -11,7 +11,6 @@ {{ $t('topMetric.wayOfStaking') }} -

@@ -114,9 +113,8 @@ import { DappCombinedInfo } from 'src/v2/models/DappsStaking'; import { computed, defineComponent, ref, watch } from 'vue'; import { useRouter } from 'vue-router'; import PieChart from 'src/components/common/PieChart.vue'; -import SnowPack from 'src/components/common/SnowPack.vue'; export default defineComponent({ - components: { PieChart, SnowPack }, + components: { PieChart }, setup() { const store = useStore(); const { stakerApr, stakerApy } = useApr(); diff --git a/src/components/dapp-staking/my-staking/styles/top-metric.scss b/src/components/dapp-staking/my-staking/styles/top-metric.scss index a933553b9..6256ef706 100644 --- a/src/components/dapp-staking/my-staking/styles/top-metric.scss +++ b/src/components/dapp-staking/my-staking/styles/top-metric.scss @@ -122,6 +122,7 @@ .txt--tvl { white-space: nowrap; + margin-right: 5px; } .detail-value { diff --git a/src/hooks/custom-signature/message.ts b/src/hooks/custom-signature/message.ts index 792ed2d36..c84e0ab21 100644 --- a/src/hooks/custom-signature/message.ts +++ b/src/hooks/custom-signature/message.ts @@ -6,8 +6,10 @@ import { hasExtrinsicFailedEvent } from 'src/store/dapp-staking/actions'; export enum TxType { dappsStaking = 'dappsStaking', + withdrawUnbonded = 'withdrawUnbonded', } +// @TODO: we need to clean up this later in a way that can be solved without send over the store export const displayCustomMessage = ({ txType, store, @@ -28,6 +30,13 @@ export const displayCustomMessage = ({ senderAddress, t, }); + } else if (txType === TxType.withdrawUnbonded) { + dispatchUnbondedMessage({ + result, + store, + senderAddress, + t, + }); } }; @@ -70,3 +79,29 @@ const dispatchClaimMessage = ({ } } }; + +const dispatchUnbondedMessage = ({ + store, + result, + senderAddress, + t, +}: { + store: Store; + result: ISubmittableResult; + senderAddress: string; + t: (...arg: any) => void; +}): void => { + if (result.isCompleted) { + if (!hasExtrinsicFailedEvent(result.events, store.dispatch)) { + store.commit('dapps/setUnlockingChunks', -1); + store.dispatch( + 'general/showAlertMsg', + { + msg: t('dappStaking.toast.successfullyWithdrew'), + alertType: 'success', + }, + { root: true } + ); + } + } +}; diff --git a/src/hooks/dapps-staking/useUnbonding.ts b/src/hooks/dapps-staking/useUnbonding.ts index 9ab3e0dac..b6212880a 100644 --- a/src/hooks/dapps-staking/useUnbonding.ts +++ b/src/hooks/dapps-staking/useUnbonding.ts @@ -3,10 +3,8 @@ import { u32 } from '@polkadot/types'; import { ISubmittableResult } from '@polkadot/types/types'; import BN from 'bn.js'; import { $api } from 'boot/api'; -import { useCustomSignature, useGasPrice } from 'src/hooks'; -import { signAndSend } from 'src/hooks/helper/wallet'; +import { displayCustomMessage, TxType } from 'src/hooks/custom-signature/message'; import { useUnbondWithdraw } from 'src/hooks/useUnbondWithdraw'; -import { hasExtrinsicFailedEvent } from 'src/modules/extrinsic'; import { useStore } from 'src/store'; import { computed, onUnmounted, ref, watch } from 'vue'; import { container } from 'src/v2/common'; @@ -18,12 +16,6 @@ import { useI18n } from 'vue-i18n'; export function useUnbonding() { const store = useStore(); const { t } = useI18n(); - const { isCustomSig, handleCustomExtrinsic } = useCustomSignature({ - fn: () => { - store.commit('dapps/setUnlockingChunks', -1); - }, - }); - const { selectedTip } = useGasPrice(); const selectedAccountAddress = computed(() => store.getters['general/selectedAddress']); const unlockingChunksCount = computed(() => store.getters['dapps/getUnlockingChunks']); const maxUnlockingChunks = computed(() => store.getters['dapps/getMaxUnlockingChunks']); @@ -32,39 +24,30 @@ export function useUnbonding() { const canWithdraw = ref(false); const totalToWithdraw = ref(new BN(0)); const { canUnbondWithdraw } = useUnbondWithdraw($api); - const substrateAccounts = computed(() => store.getters['general/substrateAccounts']); const withdraw = async (): Promise => { try { const transaction = $api!.tx.dappsStaking.withdrawUnbonded(); - const txResHandler = (result: ISubmittableResult): Promise => { - return new Promise(async (resolve) => { - if (result.status.isFinalized) { - if (!hasExtrinsicFailedEvent(result.events, store.dispatch)) { - store.commit('dapps/setUnlockingChunks', -1); - store.dispatch('general/showAlertMsg', { - msg: t('dappStaking.toast.successfullyWithdrew'), - alertType: 'success', - }); - } - store.commit('general/setLoading', false); - resolve(true); - } else { - store.commit('general/setLoading', true); - } + const finalizedCallback = (result: ISubmittableResult): void => { + displayCustomMessage({ + txType: TxType.withdrawUnbonded, + result, + senderAddress: selectedAccountAddress.value, + store, + t, }); }; - await signAndSend({ - transaction, - senderAddress: selectedAccountAddress.value, - substrateAccounts: substrateAccounts.value, - isCustomSignature: isCustomSig.value, - txResHandler, - handleCustomExtrinsic, - dispatch: store.dispatch, - tip: selectedTip.value.price, - }); + try { + const dappStakingService = container.get(Symbols.DappStakingService); + await dappStakingService.sendTx({ + senderAddress: selectedAccountAddress.value, + transaction, + finalizedCallback, + }); + } catch (error: any) { + console.error(error.message); + } } catch (error) { console.error(error); } diff --git a/src/hooks/useClaimAll.ts b/src/hooks/useClaimAll.ts index 4696b8b64..c331e7c1c 100644 --- a/src/hooks/useClaimAll.ts +++ b/src/hooks/useClaimAll.ts @@ -112,7 +112,7 @@ export function useClaimAll() { try { const dappStakingService = container.get(Symbols.DappStakingService); - await dappStakingService.claim({ + await dappStakingService.sendTx({ senderAddress: senderAddress.value, transaction, finalizedCallback, diff --git a/src/i18n/ja/index.ts b/src/i18n/ja/index.ts index d1c16cd3e..5ebd585b5 100644 --- a/src/i18n/ja/index.ts +++ b/src/i18n/ja/index.ts @@ -2,36 +2,455 @@ export default { confirm: '確認', cancel: 'キャンセル', change: '変更', + connect: '接続', + disconnect: '接続を切る', copy: 'コピー', - max: '最大', - close: '閉じる', + from: 'From', + to: 'To', + add: '追加', + estimated: 'およそ', forget: 'Forget', + remove: 'Remove', + close: 'Close', + manage: 'Manage', subscan: 'Subscan', + blockscout: 'Blockscout', + usd: 'USD', + 'polkadot-js': 'Polkadot.js', + 'polkadot-js-app': 'Polkadot.js App', + metamask: 'MetaMask', + clover: 'Clover', + mathwallet: 'Math Wallet', + wallet3: 'Wallet 3', + alert: 'Alert', + max: '最大', + native: 'Native', + evm: 'EVM', + wasm: 'WASM', + addressFormat: '{network} Wallet アドレス', + addressPlaceholder: '送金先 {network} アドレス', + evmAddressPlaceholder: '送金先 EVM アドレス', + ticker: 'Ticker', + isComingSoon: '{value} is coming soon', + amountToken: '{amount} {token}', + select: 'Select', + sort: { + sortBy: 'Sort by', + amountHightToLow: 'Amount: High to Low', + amountLowToHigh: 'Amount: Low to High', + alphabeticalAtoZ: 'Alphabetical: A to Z', + alphabeticalZtoA: 'Alphabetical: Z to A', + }, + warning: { + insufficientBalance: '{token} の残高不足', + insufficientFee: '<注意> 資金不足のためトランザクションが失敗する可能性があります。', + inputtedInvalidDestAddress: '送金先アドレスが有効ではありません。', + inputtedInvalidAddress: 'アドレスが有効ではありません。', + selectedInvalidNetworkInWallet: 'ウォレットに対して無効なネットワークです。', + insufficientBridgeAmount: '最小送金額は {amount} {token} です。', + insufficientOriginChainBalance: ' {chain} network の最低必要残高は {amount} {token} です。', + insufficientExistentialDeposit: + '{network} network にある残高が existential depositi 以下です。', + withdrawalNotSupport: '現在のところ {chain} への送金はサポートされていません。', + }, + toast: { + transactionFailed: '送金失敗: {message}', + completedHash: '表記ブロックハッシュで完了 #{hash}', + completedTxHash: '表記トランザクションハッシュで完了 #{hash}', + unableCalculateMsgPayload: 'Unable to calculate the message payload', + amountMustNotBeZero: '送金額は0以上でなければいけません。', + copyAddressSuccessfully: 'コピー完了!', + }, common: { - updateMetadata: 'Metadataアップデート', - metadataAlreadyInstalled: 'Metadata Already Installed', - lightMode: 'ライトモード', - darkMode: 'ダークモード', + updateMetadata: 'Metadata をアップデートしてください。', + metadataAlreadyInstalled: 'Metadata はインストールされています。', + lightMode: 'Light mode', + darkMode: 'Dark mode', dApps: 'dApps', - store: 'Store', - plasmLockdrop: 'Plasm ロックドロップ', - closeSidebar: 'サイドバーを閉じる', + dappStaking: 'dApp Staking', + staking: 'Staking', + contract: 'Contract', + plasmLockdrop: 'Plasm Lockdrop', + closeSidebar: 'Close sidebar', twitter: 'Twitter', telegram: 'Telegram', discord: 'Discord', github: 'GitHub', - docs: 'ドキュメント', + linkedIn: 'LinkedIn', + reddit: 'Reddit', + facebook: 'Facebook', + instagram: 'Instagram', + youtube: 'YouTube', + docs: 'Documentation', + speed: { + speed: 'Transaction speed', + speedTip: 'Transaction speed (Tip)', + average: 'Average', + fast: 'Fast', + superFast: 'Super Fast', + tipHelp: 'Tipは送金スピードを上げるのに重要です。', + }, + }, + sidenavi: { + community: 'Community', + discord: 'Discord', + twitter: 'Twitter', + telegram: 'Telegram', + reddit: 'Reddit', + youtube: 'Youtube', + forum: 'Forum', + github: 'Github', + docs: 'Docs', + settings: 'Settings', + language: 'Language', + theme: 'Theme', + close: 'Close', + }, + drawer: { + endpoint: 'Endpoint', + viaEndpoint: 'via {value}', + }, + wallet: { + connectWallet: 'Connect Wallet', + select: '接続するウォレットを選んでください。', + nativeAccount: 'Native Accounts', + evmAccount: 'EVM Accounts', + math: { + supportsNetwork: 'Math Wallet は Shiden Network のみサポートしています。', + switchNetwork: + "Math Wallet エクステンションのネットワークを 'Shiden' に変更し、ページをリフレッシュしてください。", + }, + showBalance: '{token} バランスを表示', + }, + installWallet: { + getWallet: 'Haven’t got a {value} yet?', + installWallet: + '{value} をインストールする必要があります。完了したらこのページをリフレッシュしてください。', + installExtension: '{value} エクステンションをインストールする。', + howToConnect: '接続方法を確認する。', + }, + updateWallet: { + getUpdatedWallet: 'Haven’t updated {value} yet?', + updateWallet: + '{value} をインストールする必要があります。完了したらこのページをリフレッシュしてください。', + updateExtension: '{value} エクステンションをアップデートしてください。', + }, + topMetric: { + build2earn: 'Build2Earn', + wayOfStaking: 'An innovative way of Staking', + tvlInDapps: 'TVL in dApps', + currentEra: 'Current Era', + eraInfo: '(ETA: {eta})', + stakersRewards: 'Stakers Rewards', + currentBlock: 'Current Block', + totalDapps: 'Total dApps', + apr: 'APR', + apy: 'APY', + }, + myDapps: { + index: 'Index', + dapps: 'dApps', + stakedAmount: 'Staked Amount', + unbondingAmount: 'Unbonding Amount', + remainingEra: 'Remaining Era', + withdraw: 'Withdraw', + rebond: 'Re-bond', + totalEarned: 'Total Earned', + manage: 'Manage', + add: 'Add', + unbond: 'Unbond', + rebondGuide: + 'Re-bondしたら資金はステーキングに戻ります。引き出す際は再度10eraのunbond期間が必要になります。', + rebondTitle: 'The amount you wish to rebond', + withdrawGuide: '引き出せる資金があります。', + withdrawTitle: 'Ready to withdraw', + unregisteredAlert: + 'このプロジェクトの登録は解除されています。Claimをしていただくことにより、ステーキング資金が返金されます。', + claimAndUnbond: 'ステーキング解除と請求', + }, + myReward: { + totalStaked: 'Total Staked', + availableToClaim: '請求可能額', + era: 'Era', + claim: '請求', + restake: '請求後に再ステーク', + turnOff: 'Turn Off', + totalEarned: '現在までのリワード総額', + availableToClaimTip: + '記載されているeraの数はdAppごとの総数になります。一度に請求できるのは50個までで、長く放置すると複数回の請求(Claim)が必要になります。', + restakeTip: + 'オンにすることで、請求(Claim)時に自動的にリワードが再ステークされるようになります。', }, dappStaking: { + myStaking: 'My Staking', + myRewards: 'My Rewards', + unbonding: 'Unbonding', + myDapps: 'My dApps', + dappRegistered: 'Congrats!! your contract is approved. Please submit the details', + welcomeBanner: + 'Congratulations 🎉 Please fill in your dApp information to give users better overview of your application. Updated data will appear on the dApp page shortly.', + registerNow: 'Register now', + transferableBalance: '送金可能残高', totalStake: 'Total stake:', - availableToStake: 'Available to stake', yourStake: 'Your stake: ', add: 'Add', + unbond: 'Unbond', unstake: 'Unstake', stake: 'Stake', - claim: '報酬', + claim: 'Claim', + withdraw: 'Withdraw', + unbondingEra: '引き出し可能まで {unbondingPeriod} eras のUnbonding期間があります。', + turnOn: 'Turn on', + turnOff: 'Turn off', + on: 'ON', + off: 'OFF', + stakeNow: 'Stake Now', + edit: 'Edit', + developerIncentive: 'Developer Incentive', + tokenEra: '{token}/era', + dappStakingEvm: 'dApp Staking is available on EVM', + onChainData: 'On-Chain Data', + stakingTvl: 'Staking TVL', + cantClaimWihtoutError: + '一定期間請求(Claim)がなかったためRe-Stakeの機能が一時的に使用できない状態です。一度Re-Stakeの機能をオフにし、Claim完了後に再度オンにしてください。', + stakePage: { + backToDappList: 'Back to dApps list', + whereFundsFrom: '資金はどこから使用しますか?', + }, + dappPage: { + back: 'Back', + goBackToTopPage: 'Go back to the top page', + stakeOrSwitchTo: 'Stake or switch to', + totalStaked: 'Total Staked', + totalStaker: 'Total Staker', + team: 'Team', + projectOverview: 'Project Overview', + projectSite: 'Project Site', + goToApp: 'Go to App', + goToWebsite: 'Go to website', + virtualMachine: 'Virtual Machine', + contractAddress: 'Contract Address', + license: 'License', + community: 'Community', + wallets: 'Wallets', + stats: 'Stats', + }, modals: { contractAddress: 'Contract address {address}', + license: 'License', + startUnbonding: 'Start unbonding', + unbondFrom: 'Unbond from {name}', + builder: { + title: 'Builder', + githubAccount: 'GitHub account', + twitterAccount: 'Twitter account', + linkedInAccount: 'LinkedIn account', + image: "Builder's image", + imageRecomendation: 'A square image of minimum 500px is recommended.', + error: { + name: 'Builder name is required.', + nameExists: 'Given name is already used by another developer', + invalidUrl: 'Invalid url.', + accountRequired: 'At least one account is required.', + builderImageRequired: 'Builder image is required', + buildersRequired: 'At least two builders are required.', + builderUrlRequired: 'At least one account URL is required.', + }, + }, + builders: 'Builders information', + communityLabel: 'Community', + community: { + title: 'Communities', + discordAccount: 'Discord account', + twitterAccount: 'Twitter account', + redditAccount: 'Reddit account', + facebookAccount: 'Facebook account', + tiktokAccount: 'TikTok account', + youtubeAccount: 'YouTube account', + instagramAccount: 'Instagram account', + communityRequired: 'At least one community link is required.', + }, + description: 'Description', + markdown: 'Markdown', + preview: 'Preview', + addAccount: 'Add an account', + addLogo: 'Add a logo image', + addImage: 'Add an image', + images: 'Images', + imagesRequired: 'At least 4 images are required.', + descriptionRequired: 'Tell the world something about your dApp.', + contractTypeTitle: 'Is your project on', + tagsTitle: 'Tags', + categoryTitle: 'Choose main category', + submit: 'Submit', + dappNameRequired: 'dApp name is required', + projectUrlRequired: 'Project URL is required.', + name: 'Name', + projectUrl: 'Project URL', + dappImageRequired: 'Project logo is required.', + projectLogo: 'Project logo', + }, + toast: { + successfullyClaimed: 'Successfully claimed {amount}', + requiredClaimFirst: 'トランザクションを送る前に請求(Claim)をしてください。', + requiredClaimFirstCompounding: + '{message} -コンパウンドをオフにし、請求(Claim)してから再度オンにしてください。', + successfullyWithdrew: '引き出しに成功しました。', + successfullySetRewardDest: 'You successfully set reward destination', + }, + error: { + onlySupportsSubstrate: 'dApp staking only supports Substrate wallets', + notEnoughMinAmount: '最小{amount} {symbol}が必要です。', + allFundsWillBeTransferred: + '最小 {minStakingAmount} {symbol} が必要なので全てのトークンがトランスファーされます。', + }, + }, + assets: { + assets: 'Assets', + xcmAssets: 'XCM Assets', + xvmAssets: 'XVM ERC20 Assets', + nativeAccount: 'Native Account', + evmAccount: 'EVM Account', + switchToNative: 'Switch to Lockdrop', + switchToEvm: 'Switch to EVM', + totalBalance: 'Total Balance', + transfer: 'Transfer', + faucet: 'Faucet', + bridge: 'Bridge', + manage: 'Manage', + xcm: 'XCM', + wrap: 'Wrap', + explorer: 'Explorer', + withdraw: 'Withdraw', + view: 'View', + transferableBalance: '送金可能バランス', + yourEvmDeposit: 'Your EVM deposit', + yourVestingInfo: 'Your Vesting Info', + yourStaking: 'Your Staking', + lockdropAccount: 'Lockdrop Account', + inLockdropAccount: 'You are in a Lockdrop account', + cantTransferToExcahges: '取引所には送金できません。', + noHash: 'このトランザクションにハッシュはつきません。', + addToWallet: 'Add to wallet', + noResults: 'No results found :(', + wrongNetwork: 'ウォレットが間違ったNetworkに接続されています。', + connectNetwork: 'Connect to {network} RPC', + invalidAddress: 'アドレスが有効ではありません。', + importTokens: 'トークンをインポート', + importXvmTokens: 'XVM を使いERC20トークンをインポートする。', + importErc20Tokens: 'ERC20トークンをインポート', + hideSmallBalances: '少ないバランスのトークンを非表示', + unhideSmallBalances: '0バランス含む全てのトークンを表示', + tokenHasBeenAdded: 'このトークンはすでに追加されています。', + transferPage: { + backToAssets: 'Back to Assets', + crossChainTransfer: 'Cross-chain Transfer', + xcm: '(XCM)', + faq: 'FAQ', + recentHistory: 'Recent History', + hotTopic: 'Hot Topic', + inputAddressManually: 'マニュアルでアドレスを入力する', + goBack: 'Go back', + selectChain: 'Select Chain', + selectToken: 'Select Token', + noTxRecords: 'トランザクションのレコードがありません。', + mintTransferAmount: '最小送金額は {amount} {symbol} です。', + howToUsePortal: 'HOW TO USE THE PORTAL', + }, + modals: { + max: 'Max', + balance: 'Balance: {amount} {token}', + available: 'Available: {amount} {token}', + notSendToExchanges: '取引所には送金しません。', + youWillReceive: 'You will receive', + faucetNextRequest: 'Time left until the next request', + countDown: '{hrs} hrs {mins} mins {secs} secs', + whatIsFaucet: ' Faucet とは?', + faucetBalance: 'Faucet Balance: {amount} {symbol}', + faucetIntro: + '各トランザクションには少額の費用がかかり、 {symbol} がない場合は送金ができないので、トランザクションに必要な {symbol} トークンをFaucetから受け取ります。', + faucetDriedOut: 'Faucetが枯渇しています。Discordにレポートください。', + availableToWithdraw: '引き出し可能', + totalDistribution: 'Total Distribution', + alreadyVested: 'Already vested', + remainingVests: 'Remaining vests', + unlockPerBlock: '{perToken} {symbol} per block until block {untilBlock}', + availableToUnlock: 'Available to unlock', + unlock: 'Unlock', + transfer: 'Transfer', + evmXcmDeposit: 'EVM (Deposit)', + evmXcmWithdrawal: 'EVM (Withdrawal)', + depositToNative: 'Deposit to Native', + depositToEvm: 'Deposit to EVM', + evmWalletAddress: 'EVM Wallet Address', + howToImportXvmTokens: 'Find out how to import ERC20 tokens via XVM', + riskOfImportTokens: + '既存のトークンの偽バージョンを作成するなど、誰でもトークンを作成できるリスクにご注意ください。', + tokenContractAddress: 'Token Contract Address', + erc20ContractAddress: 'ERC20 Token Contract Address', + xvmPsp22ContractAddress: 'XVM PSP22 Contract Address', + evmErc20ContractAddress: 'XVM ERC20 Contract Address', + tipDestAddressFormat: '{chain} address はどこで確認すればいい?', + titleWithdraw: 'Withdraw {token}', + titleVesting: 'Vesting info', + xcmWarning: { + minBalIsRequired: 'Origin chain に最小バランスが設定されています。', + fee: '費用は入力した金額から差し引かれます。', + notInputExchanges: '取引所のアドレスを入力しないでください。', + tooltip: + '資金の損失を防ぐために、 {amount} {symbol} をオリジンチェーンアカウントに保管しています。origin chainから入金する場合、最小バランスを超えたトークンのみが送金可能です。', + }, + }, + }, + dashboard: { + dashboard: 'Dashboard', + tvl: 'TVL', + circulating: { + circulatingSupply: 'Circulating Supply', + supply: 'of {totalSupply}: ', + }, + block: { + block: 'Block', + blockHeight: 'Block Height', + blockTime: 'Block Time', + avgBlockTime: 'avg. block time (secs)', + oneEra: '1 era:', + sevenEras: '7 eras:', + thirtyEras: '30 eras:', + secs: ' secs', + era: 'Era', + progress: '{value}%', + eta: 'ETA {value}', + }, + }, + chart: { + tvl: { + title: 'Total Value Locked', + tooltip: 'TVL', + }, + dappStaking: { + title: 'Total Value Locked in dApp Staking', + tooltip: 'TVL in dApp Staking', + }, + ecosystem: { + title: 'Total Value Locked in EVM Ecosystem', + tooltip: 'TVL in EVM Ecosystem', + }, + ttlTransactions: { + title: 'Total Transactions', + tooltip: 'Total Transactions', + }, + tokenPrice: { + title: 'Token Price', + tooltip: 'Token Price', + }, + uniqueActiveUsers: { + title: 'Unique Active Users', + tooltip: 'Unique Active Users', + }, + numberOfCalls: { + title: 'Number Of Transactions', + tooltip: "Number of transactions users called to dApp's smart contract address", }, }, }; diff --git a/src/modules/xcm/index.ts b/src/modules/xcm/index.ts index 240588144..78c2e5611 100644 --- a/src/modules/xcm/index.ts +++ b/src/modules/xcm/index.ts @@ -199,6 +199,15 @@ export const xcmChainObj: XcmChainObj = { subscan: 'https://phala.subscan.io', isAstarNativeToken: true, }, + [Chain.BIFROST_POLKADOT]: { + name: Chain.BIFROST_POLKADOT, + relayChain: Chain.POLKADOT, + img: 'https://polkadot.js.org/apps/static/bifrost.76510acf..svg', + parachainId: parachainIds.BIFROST_POLKADOT, + endpoint: 'wss://hk.p.bifrost-rpc.liebi.com/ws', + subscan: 'https://bifrost.subscan.io/', + isAstarNativeToken: true, + }, }; export const xcmChains = objToArray(xcmChainObj); diff --git a/src/modules/xcm/tokens/index.ts b/src/modules/xcm/tokens/index.ts index ae071a5fe..eea70ce90 100644 --- a/src/modules/xcm/tokens/index.ts +++ b/src/modules/xcm/tokens/index.ts @@ -104,6 +104,26 @@ export const xcmToken = { originChain: Chain.PHALA, minBridgeAmount: '0.2', }, + { + symbol: 'BNC', + isNativeToken: true, + assetId: '18446744073709551623', + originAssetId: 'BNC', + logo: 'https://bifrost-kusama.subscan.io/static/img/bifrost.42f1933c.svg', + isXcmCompatible: true, + originChain: Chain.BIFROST_POLKADOT, + minBridgeAmount: '0.1', + }, + { + symbol: 'vDOT', + isNativeToken: false, + assetId: '18446744073709551624', + originAssetId: 'vDOT', + logo: 'https://bifrost.subscan.io/static/img/vDOT.75c0b67b.svg', + isXcmCompatible: true, + originChain: Chain.BIFROST_POLKADOT, + minBridgeAmount: '0.1', + }, ], [endpointKey.SHIDEN]: [ { diff --git a/src/v2/config/xcm/XcmRepositoryConfiguration.ts b/src/v2/config/xcm/XcmRepositoryConfiguration.ts index 6d31376cf..a35ae346c 100644 --- a/src/v2/config/xcm/XcmRepositoryConfiguration.ts +++ b/src/v2/config/xcm/XcmRepositoryConfiguration.ts @@ -7,6 +7,7 @@ import { InterlayXcmRepository, CrustShadowXcmRepository, PhalaXcmRepository, + BifrostXcmRepository, } from 'src/v2/repositories/implementations'; import { Chain } from 'src/v2/models/XcmModels'; import { TypeMapping } from 'src/v2/config/types'; @@ -27,6 +28,7 @@ export const XcmRepositoryConfiguration: TypeMapping = { [Chain.CRUST_SHADOW]: CrustShadowXcmRepository, [Chain.KHALA]: PhalaXcmRepository, [Chain.PHALA]: PhalaXcmRepository, + [Chain.BIFROST_POLKADOT]: BifrostXcmRepository, }; export type AstarToken = 'ASTR' | 'SDN'; diff --git a/src/v2/models/XcmModels.ts b/src/v2/models/XcmModels.ts index cf518e4c3..9f9c2069a 100644 --- a/src/v2/models/XcmModels.ts +++ b/src/v2/models/XcmModels.ts @@ -17,6 +17,7 @@ export enum Chain { CRUST_SHADOW = 'Crust-shadow', KHALA = 'Khala', PHALA = 'Phala', + BIFROST_POLKADOT = 'Bifrost-polkadot', } export enum parachainIds { @@ -33,6 +34,7 @@ export enum parachainIds { CRUST_SHADOW = 2012, KHALA = 2004, PHALA = 2035, + BIFROST_POLKADOT = 2030, } export interface XcmChain { diff --git a/src/v2/repositories/implementations/index.ts b/src/v2/repositories/implementations/index.ts index 77ca4569b..d99760411 100644 --- a/src/v2/repositories/implementations/index.ts +++ b/src/v2/repositories/implementations/index.ts @@ -13,3 +13,4 @@ export * from './xcm/PhalaXcmRepository'; export * from './TokenApiRepository'; export * from './EvmAssetsRepository'; export * from './xcm/CrustShadowXcmRepository'; +export * from './xcm/BifrostXcmRepository'; diff --git a/src/v2/repositories/implementations/xcm/BifrostXcmRepository.ts b/src/v2/repositories/implementations/xcm/BifrostXcmRepository.ts new file mode 100644 index 000000000..00c38d5ed --- /dev/null +++ b/src/v2/repositories/implementations/xcm/BifrostXcmRepository.ts @@ -0,0 +1,104 @@ +import { BN } from '@polkadot/util'; +import { decodeAddress } from '@polkadot/util-crypto'; +import { XcmTokenInformation } from 'src/modules/xcm'; +import { container } from 'src/v2/common'; +import { ExtrinsicPayload, IApi, IApiFactory } from 'src/v2/integration'; +import { Asset } from 'src/v2/models'; +import { XcmChain } from 'src/v2/models/XcmModels'; +import { TokensAccounts } from 'src/v2/repositories/implementations/xcm/AcalaXcmRepository'; +import { Symbols } from 'src/v2/symbols'; +import { XcmRepository } from '../XcmRepository'; + +const BNC = { Native: 'BNC' }; +const vDOT = { VToken2: 0 }; +const ASTR = { Token2: 3 }; + +/** + * Used to transfer assets from Bifrost + */ +export class BifrostXcmRepository extends XcmRepository { + constructor() { + const defaultApi = container.get(Symbols.DefaultApi); + const apiFactory = container.get(Symbols.ApiFactory); + const registeredTokens = container.get(Symbols.RegisteredTokens); + super(defaultApi, apiFactory, registeredTokens); + this.astarTokens = { + ASTR: 3, + }; + } + + public async getTransferCall( + from: XcmChain, + to: XcmChain, + recipientAddress: string, + token: Asset, + amount: BN + ): Promise { + if (!to.parachainId) { + throw `Parachain id for ${to.name} is not defined`; + } + let tokenData; + if (token.originAssetId == 'BNC') { + tokenData = BNC; + } else if (token.originAssetId == 'vDOT') { + tokenData = vDOT; + } else if (token.originAssetId == 'ASTR') { + tokenData = ASTR; + } + const destination = { + V1: { + parents: '1', + interior: { + X2: [ + { + Parachain: to.parachainId, + }, + { + AccountId32: { + network: { + Any: null, + }, + id: decodeAddress(recipientAddress), + }, + }, + ], + }, + }, + }; + const destWeight = { + limited: new BN(10).pow(new BN(9)).muln(5), + }; + return await this.buildTxCall( + from, + 'xTokens', + 'transfer', + tokenData, + amount, + destination, + destWeight + ); + } + + public async getTokenBalance( + address: string, + chain: XcmChain, + token: Asset, + isNativeToken: boolean + ): Promise { + const api = await this.apiFactory.get(chain.endpoint); + try { + if (token.originAssetId == 'BNC') { + return (await this.getNativeBalance(address, chain)).toString(); + } else if (token.originAssetId == 'vDOT') { + const bal = await api.query.tokens.accounts(address, vDOT); + return bal.free.toString(); + } else { + const bal = await api.query.tokens.accounts(address, ASTR); + return bal.free.toString(); + } + } catch (e) { + console.error(e); + return '0'; + } + } +} diff --git a/src/v2/services/IDappStakingService.ts b/src/v2/services/IDappStakingService.ts index 0ca65b5d6..bf5c7166f 100644 --- a/src/v2/services/IDappStakingService.ts +++ b/src/v2/services/IDappStakingService.ts @@ -87,7 +87,7 @@ export interface IDappStakingService { /** * claim dApp staking rewards */ - claim({ + sendTx({ senderAddress, transaction, finalizedCallback, diff --git a/src/v2/services/implementations/DappStakingService.ts b/src/v2/services/implementations/DappStakingService.ts index 752188ad4..db52f96d5 100644 --- a/src/v2/services/implementations/DappStakingService.ts +++ b/src/v2/services/implementations/DappStakingService.ts @@ -197,7 +197,7 @@ export class DappStakingService implements IDappStakingService { return true; } - public async claim({ + public async sendTx({ senderAddress, transaction, finalizedCallback,