diff --git a/.eslintignore b/.eslintignore index 294d360a4..81332340f 100644 --- a/.eslintignore +++ b/.eslintignore @@ -7,5 +7,6 @@ .eslintrc.js babel.config.js /src-ssr +/src-pwa /tests/polkadot_wallet /tests/metamask_wallet \ No newline at end of file diff --git a/src/App.vue b/src/App.vue index 66bb86ad3..0ac2a13ca 100644 --- a/src/App.vue +++ b/src/App.vue @@ -63,7 +63,7 @@ import { useAccount, useAppRouter } from 'src/hooks'; import { LOCAL_STORAGE } from 'src/config/localStorage'; import { AccountLedgerChangedMessage, - IDappStakingRepository, + IDappStakingService, ProtocolStateChangedMessage, } from './staking-v3'; import { useDappStaking, useDapps } from './staking-v3/hooks'; @@ -179,15 +179,19 @@ export default defineComponent({ // Handle wallet change so we can inject proper wallet let previousAddress: string | undefined = undefined; - watch([isEthWallet, currentWallet, isH160, currentAccountName], () => { + watch([isEthWallet, currentWallet, isH160, currentAccountName], async () => { setCurrentWallet(isEthWallet.value, currentWallet.value); // Subscribe to an account specific dApp staking v3 data. if (!isDappStakingV3.value) return; + + // Memo: Can't use senderSs58Account here because unified account is not stored to vuex yet + // and senderSs58Account contains evm mapped address which is incorrect for unified account. if (currentAccount.value && currentAccount.value !== previousAddress) { - container - .get(Symbols.DappStakingRepositoryV3) - .startAccountLedgerSubscription(currentAccount.value); + const stakingService = container.get<() => IDappStakingService>( + Symbols.DappStakingServiceFactoryV3 + )(); + await stakingService.startAccountLedgerSubscription(currentAccount.value); fetchStakerInfoToStore(); getAllRewards(); fetchTiersConfigurationToStore(); diff --git a/src/staking-v3/components/Vote.vue b/src/staking-v3/components/Vote.vue index aa39cadba..f57d990ba 100644 --- a/src/staking-v3/components/Vote.vue +++ b/src/staking-v3/components/Vote.vue @@ -269,7 +269,9 @@ export default defineComponent({ } if (selectedDappAddress.value) { - const dapp = dapps.value.find((dapp) => dapp.address === selectedDappAddress.value); + const dapp = dapps.value.find( + (dapp) => dapp.address.toLowerCase() === selectedDappAddress.value.toLowerCase() + ); if (dapp) { selectedDapps.value = [dapp]; } diff --git a/src/staking-v3/components/my-staking/MyDapps.vue b/src/staking-v3/components/my-staking/MyDapps.vue index 157c09914..3748b129c 100644 --- a/src/staking-v3/components/my-staking/MyDapps.vue +++ b/src/staking-v3/components/my-staking/MyDapps.vue @@ -66,7 +66,7 @@ export default defineComponent({ }, }, setup() { - const { registeredDapps, getDapp } = useDapps(); + const { getDapp } = useDapps(); const { navigateToVote, navigateToMove } = useDappStakingNavigation(); const { unstake, unstakeFromUnregistered } = useDappStaking(); diff --git a/src/staking-v3/hooks/useDappStaking.ts b/src/staking-v3/hooks/useDappStaking.ts index cddc92052..d4505f309 100644 --- a/src/staking-v3/hooks/useDappStaking.ts +++ b/src/staking-v3/hooks/useDappStaking.ts @@ -1,6 +1,5 @@ import { $api } from 'boot/api'; import { computed } from 'vue'; -import { getShortenAddress } from '@astar-network/astar-sdk-core'; import { container } from 'src/v2/common'; import { AccountLedger, @@ -158,13 +157,16 @@ export function useDappStaking() { : true; const unstake = async (dapp: CombinedDappInfo, amount: number): Promise => { - const [result, error] = await canUnStake(dapp.chain.address, amount); - if (!result) { - popError(error); - return; - } - - const stakingService = container.get(Symbols.DappStakingServiceV3); + // TODO check implementation canStake, canUnstake + // const [result, error] = await canUnStake(dapp.chain.address, amount); + // if (!result) { + // popError(error); + // return; + // } + + const stakingService = container.get<() => IDappStakingService>( + Symbols.DappStakingServiceFactoryV3 + )(); await stakingService.claimUnstakeAndUnlock( dapp.chain.address, amount, @@ -180,7 +182,9 @@ export function useDappStaking() { }; const unstakeFromUnregistered = async (dappAddress: string, dappName: string): Promise => { - const stakingService = container.get(Symbols.DappStakingServiceV3); + const stakingService = container.get<() => IDappStakingService>( + Symbols.DappStakingServiceFactoryV3 + )(); await stakingService.claimAllAndUnstakeFromUnregistered( currentAccount.value, dappAddress, @@ -199,7 +203,9 @@ export function useDappStaking() { }; const claimStakerRewards = async (): Promise => { - const stakingService = container.get(Symbols.DappStakingServiceV3); + const stakingService = container.get<() => IDappStakingService>( + Symbols.DappStakingServiceFactoryV3 + )(); await stakingService.claimStakerRewards(currentAccount.value, 'success'); const staker = await stakingService.getStakerRewards(currentAccount.value); store.commit('stakingV3/setRewards', { ...rewards.value, staker }); @@ -211,7 +217,9 @@ export function useDappStaking() { unstakeFromAddress: string, unstakeAmount: bigint ): Promise => { - const stakingService = container.get(Symbols.DappStakingServiceV3); + const stakingService = container.get<() => IDappStakingService>( + Symbols.DappStakingServiceFactoryV3 + )(); await stakingService.claimLockAndStake( currentAccount.value, @@ -230,15 +238,19 @@ export function useDappStaking() { }; const claimBonusRewards = async (): Promise => { - const stakingService = container.get(Symbols.DappStakingServiceV3); + const stakingService = container.get<() => IDappStakingService>( + Symbols.DappStakingServiceFactoryV3 + )(); + await stakingService.claimBonusRewards(currentAccount.value, 'success'); const bonus = await stakingService.getBonusRewards(currentAccount.value); store.commit('stakingV3/setRewards', { ...rewards.value, bonus }); }; const claimDappRewards = async (contractAddress: string): Promise => { - const stakingService = container.get(Symbols.DappStakingServiceV3); - + const stakingService = container.get<() => IDappStakingService>( + Symbols.DappStakingServiceFactoryV3 + )(); if (contractAddress) { await stakingService.claimDappRewards(contractAddress, currentAccount.value, 'success'); const dApp = await stakingService.getDappRewards(contractAddress); @@ -249,7 +261,10 @@ export function useDappStaking() { }; const claimStakerAndBonusRewards = async (): Promise => { - const stakingService = container.get(Symbols.DappStakingServiceV3); + const stakingService = container.get<() => IDappStakingService>( + Symbols.DappStakingServiceFactoryV3 + )(); + await stakingService.claimStakerAndBonusRewards( currentAccount.value, t('stakingV3.claimRewardSuccess') @@ -260,13 +275,17 @@ export function useDappStaking() { }; const withdraw = async (): Promise => { - const stakingService = container.get(Symbols.DappStakingServiceV3); + const stakingService = container.get<() => IDappStakingService>( + Symbols.DappStakingServiceFactoryV3 + )(); await stakingService.claimUnlockedTokens(currentAccount.value, t('stakingV3.withdrawSuccess')); getCurrentEraInfo(); }; const relock = async (): Promise => { - const stakingService = container.get(Symbols.DappStakingServiceV3); + const stakingService = container.get<() => IDappStakingService>( + Symbols.DappStakingServiceFactoryV3 + )(); await stakingService.relockUnlockingTokens(currentAccount.value, t('stakingV3.relockSuccess')); }; @@ -280,7 +299,9 @@ export function useDappStaking() { }; const getAllRewards = async (): Promise => { - const stakingV3service = container.get(Symbols.DappStakingServiceV3); + const stakingV3service = container.get<() => IDappStakingService>( + Symbols.DappStakingServiceFactoryV3 + )(); const ownedContractAddress = getOwnedDappAddress(); let staker = BigInt(0); @@ -439,8 +460,10 @@ export function useDappStaking() { return; } - const stakingRepo = container.get(Symbols.DappStakingRepositoryV3); - const stakerInfo = await stakingRepo.getStakerInfo(currentAccount.value, false); + const stakingService = container.get<() => IDappStakingService>( + Symbols.DappStakingServiceFactoryV3 + )(); + const stakerInfo = await stakingService.getStakerInfo(currentAccount.value, false); store.commit('stakingV3/setStakerInfo', stakerInfo, { root: true }); }; diff --git a/src/staking-v3/hooks/useDappStakingNavigation.ts b/src/staking-v3/hooks/useDappStakingNavigation.ts index 7d1399117..289c99f3a 100644 --- a/src/staking-v3/hooks/useDappStakingNavigation.ts +++ b/src/staking-v3/hooks/useDappStakingNavigation.ts @@ -6,7 +6,7 @@ export function useDappStakingNavigation() { const navigateToVote = (dAppAddress: string | undefined = undefined): void => { const base = networkParam + Path.DappStaking + Path.Vote; - router.push(`${base}?dappAddress=${dAppAddress ?? ''}`); + router.push(`${base}?dappAddress=${dAppAddress?.toLowerCase() ?? ''}`); }; const navigateToMove = (dAppAddress: string): void => { diff --git a/src/staking-v3/logic/services/DappStakingService.ts b/src/staking-v3/logic/services/DappStakingService.ts index d4fa97f33..a7418c94e 100644 --- a/src/staking-v3/logic/services/DappStakingService.ts +++ b/src/staking-v3/logic/services/DappStakingService.ts @@ -1,5 +1,5 @@ import { inject, injectable } from 'inversify'; -import { CombinedDappInfo, DappStakeInfo, StakeAmount } from '../models'; +import { CombinedDappInfo, DappStakeInfo, SingularStakingInfo, StakeAmount } from '../models'; import { IDappStakingService } from './IDappStakingService'; import { Symbols } from 'src/v2/symbols'; import { IDappStakingRepository } from '../repositories'; @@ -12,7 +12,8 @@ import { IMetadataRepository } from 'src/v2/repositories'; @injectable() export class DappStakingService implements IDappStakingService { constructor( - @inject(Symbols.DappStakingRepositoryV3) private dappStakingRepository: IDappStakingRepository, + @inject(Symbols.DappStakingRepositoryV3) + protected dappStakingRepository: IDappStakingRepository, @inject(Symbols.WalletFactory) private walletFactory: () => IWalletService, @inject(Symbols.MetadataRepository) private metadataRepository: IMetadataRepository ) {} @@ -53,6 +54,15 @@ export class DappStakingService implements IDappStakingService { Guard.ThrowIfUndefined(contractAddress, 'contractAddress'); Guard.ThrowIfUndefined(senderAddress, 'senderAddress'); + const batch = await this.getClaimUnstakeAndUnlockBatch(contractAddress, amount, senderAddress); + await this.signCall(batch, senderAddress, successMessage); + } + + protected async getClaimUnstakeAndUnlockBatch( + contractAddress: string, + amount: number, + senderAddress: string + ): Promise { const claimStakerCalls = await this.getClaimStakerAndBonusRewardsCalls(senderAddress); const unstakeCalls = await this.dappStakingRepository.getUnstakeAndUnlockCalls( contractAddress, @@ -62,21 +72,25 @@ export class DappStakingService implements IDappStakingService { ...claimStakerCalls, ...unstakeCalls, ]); - await this.signCall(batch, senderAddress, successMessage); + + return batch; } // @inheritdoc public async claimStakerRewards(senderAddress: string, successMessage: string): Promise { Guard.ThrowIfUndefined(senderAddress, 'senderAddress'); + const batch = await this.getClaimStakerRewardsBatch(senderAddress); + await this.signCall(batch, senderAddress, successMessage); + } + protected async getClaimStakerRewardsBatch(senderAddress: string) { const calls = await this.getClaimStakerRewardsCall(senderAddress); if (!calls) { throw 'Staker rewards expired.'; } - const batch = await this.dappStakingRepository.batchAllCalls(calls); - await this.signCall(batch, senderAddress, successMessage); + return await this.dappStakingRepository.batchAllCalls(calls); } // @inheritdoc @@ -88,14 +102,27 @@ export class DappStakingService implements IDappStakingService { Guard.ThrowIfUndefined(senderAddress, 'senderAddress'); Guard.ThrowIfUndefined(contractAddress, 'contractAddress'); + const batch = await this.getClaimAllAndUnstakeFromUnregisteredBatch( + senderAddress, + contractAddress + ); + await this.signCall(batch, senderAddress, successMessage); + } + + // @inheritdoc + public async getClaimAllAndUnstakeFromUnregisteredBatch( + senderAddress: string, + contractAddress: string + ): Promise { + Guard.ThrowIfUndefined(senderAddress, 'senderAddress'); + Guard.ThrowIfUndefined(contractAddress, 'contractAddress'); + const stakerRewards = await this.getClaimStakerAndBonusRewardsCalls(senderAddress); const unstakeCall = await this.dappStakingRepository.getUnstakeFromUnregisteredCall( contractAddress ); - const batch = await this.dappStakingRepository.batchAllCalls([...stakerRewards, unstakeCall]); - - await this.signCall(batch, senderAddress, successMessage); + return await this.dappStakingRepository.batchAllCalls([...stakerRewards, unstakeCall]); } public async unlockTokens( @@ -109,7 +136,7 @@ export class DappStakingService implements IDappStakingService { await this.signCall(call, senderAddress, successMessage); } - private async getClaimStakerRewardsCall( + protected async getClaimStakerRewardsCall( senderAddress: string ): Promise { const { firstSpanIndex, lastSpanIndex, rewardsExpired, eraRewardSpanLength } = @@ -191,17 +218,11 @@ export class DappStakingService implements IDappStakingService { senderAddress: string, successMessage: string ): Promise { - const calls = await this.getClaimDappRewardsCalls(contractAddress); - - if (!calls) { - throw `No dApp rewards to claim for contract address ${contractAddress}.`; - } - - const batch = await this.dappStakingRepository.batchAllCalls(calls); + const batch = await this.getClaimDappRewardsBatch(contractAddress); await this.signCall(batch, senderAddress, successMessage); } - private async getClaimDappRewardsCalls( + protected async getClaimDappRewardsCalls( contractAddress: string ): Promise { const result = await this.getDappRewardsAndErasToClaim(contractAddress); @@ -216,6 +237,16 @@ export class DappStakingService implements IDappStakingService { ); } + protected async getClaimDappRewardsBatch(contractAddress: string): Promise { + const calls = await this.getClaimDappRewardsCalls(contractAddress); + + if (!calls) { + throw `No dApp rewards to claim for contract address ${contractAddress}.`; + } + + return await this.dappStakingRepository.batchAllCalls(calls); + } + // @inheritdoc public async getBonusRewards(senderAddress: string): Promise { const result = await this.getBonusRewardsAndContractsToClaim(senderAddress); @@ -225,17 +256,22 @@ export class DappStakingService implements IDappStakingService { public async claimBonusRewards(senderAddress: string, successMessage: string): Promise { Guard.ThrowIfUndefined('senderAddress', senderAddress); + + const batch = await this.claimBonusRewardsBatch(senderAddress); + await this.signCall(batch, senderAddress, successMessage); + } + + protected async claimBonusRewardsBatch(senderAddress: string): Promise { const calls = await this.getClaimBonusRewardsCalls(senderAddress); if (!calls) { throw `No bonus rewards to claim for sender address ${senderAddress}.`; } - const batch = await this.dappStakingRepository.batchAllCalls(calls); - await this.signCall(batch, senderAddress, successMessage); + return this.dappStakingRepository.batchAllCalls(calls); } - private async getClaimStakerAndBonusRewardsCalls( + protected async getClaimStakerAndBonusRewardsCalls( senderAddress: string ): Promise { const claimStakerCalls = await this.getClaimStakerRewardsCall(senderAddress); @@ -262,7 +298,7 @@ export class DappStakingService implements IDappStakingService { await this.signCall(batch, senderAddress, successMessage); } - private async getClaimBonusRewardsCalls( + protected async getClaimBonusRewardsCalls( senderAddress: string ): Promise { const result = await this.getBonusRewardsAndContractsToClaim(senderAddress); @@ -283,6 +319,25 @@ export class DappStakingService implements IDappStakingService { unstakeAmount: bigint, successMessage: string ): Promise { + this.guardStake(senderAddress, stakeInfo, unstakeFromAddress, unstakeAmount); + + const batch = await this.getClaimLockAndStakeBatch( + senderAddress, + amountToLock, + stakeInfo, + unstakeFromAddress, + unstakeAmount + ); + + await this.signCall(batch, senderAddress, successMessage); + } + + protected guardStake( + senderAddress: string, + stakeInfo: DappStakeInfo[], + unstakeFromAddress: string, + unstakeAmount: bigint + ): void { Guard.ThrowIfUndefined('senderAddress', senderAddress); if (stakeInfo.length === 0) { throw 'No stakeInfo provided'; @@ -294,7 +349,15 @@ export class DappStakingService implements IDappStakingService { // TODO there is a possibility that some address is wrong or some amount is below min staking amount // Check this also + } + protected async getClaimLockAndStakeBatch( + senderAddress: string, + amountToLock: bigint, + stakeInfo: DappStakeInfo[], + unstakeFromAddress: string, + unstakeAmount: bigint + ): Promise { const calls: ExtrinsicPayload[] = []; // Staker rewards @@ -326,7 +389,8 @@ export class DappStakingService implements IDappStakingService { } const batch = await this.dappStakingRepository.batchAllCalls(calls); - await this.signCall(batch, senderAddress, successMessage); + + return batch; } private async shouldCleanupExpiredEntries(senderAddress: string): Promise { @@ -527,6 +591,21 @@ export class DappStakingService implements IDappStakingService { return result; } + public async getStakerInfo( + address: string, + includePreviousPeriods: boolean + ): Promise> { + Guard.ThrowIfUndefined('address', address); + + return await this.dappStakingRepository.getStakerInfo(address, includePreviousPeriods); + } + + public async startAccountLedgerSubscription(address: string): Promise { + Guard.ThrowIfUndefined('address', address); + + await this.dappStakingRepository.startAccountLedgerSubscription(address); + } + private async getStakerEraRange(senderAddress: string) { const [protocolState, ledger, constants] = await Promise.all([ this.dappStakingRepository.getProtocolState(), diff --git a/src/staking-v3/logic/services/DappStakingServiceEvm.ts b/src/staking-v3/logic/services/DappStakingServiceEvm.ts new file mode 100644 index 000000000..611888e1e --- /dev/null +++ b/src/staking-v3/logic/services/DappStakingServiceEvm.ts @@ -0,0 +1,245 @@ +import { inject, injectable } from 'inversify'; +import { IDappStakingService } from './IDappStakingService'; +import { DappStakingService } from './DappStakingService'; +import { DappStakeInfo, SingularStakingInfo } from '../models'; +import { IWalletService } from '../../../v2/services/IWalletService'; +import { IDappStakingRepository } from '../repositories'; +import { Symbols } from 'src/v2/symbols'; +import { evmPrecompiledContract } from 'src/modules/precompiled'; +import { IAccountUnificationRepository, IMetadataRepository } from 'src/v2/repositories'; +import { Guard } from 'src/v2/common'; + +const { dispatch } = evmPrecompiledContract; + +@injectable() +export class DappStakingServiceEvm extends DappStakingService implements IDappStakingService { + private readonly wallet: IWalletService; + + constructor( + @inject(Symbols.DappStakingRepositoryV3) dappStakingRepository: IDappStakingRepository, + @inject(Symbols.WalletFactory) walletFactory: () => IWalletService, + @inject(Symbols.AccountUnificationRepository) + private accountUnificationRepository: IAccountUnificationRepository, + @inject(Symbols.MetadataRepository) metadataRepository: IMetadataRepository + ) { + super(dappStakingRepository, walletFactory, metadataRepository); + this.wallet = walletFactory(); + } + + // @inheritdoc + public async claimUnstakeAndUnlock( + contractAddress: string, + amount: number, + senderAddress: string, + successMessage: string + ): Promise { + Guard.ThrowIfUndefined(contractAddress, 'contractAddress'); + Guard.ThrowIfUndefined(senderAddress, 'senderAddress'); + Guard.ThrowIfNegative('amount', amount); + + const ss58Address = await this.getSS58Address(senderAddress); + const batch = await this.getClaimUnstakeAndUnlockBatch(contractAddress, amount, ss58Address); + + await this.wallet.sendEvmTransaction({ + from: senderAddress, + to: dispatch, + data: batch.method.toHex(), + successMessage, + failureMessage: 'Call failed', + }); + } + + // @inheritdoc + public async claimStakerRewards(senderAddress: string, successMessage: string): Promise { + Guard.ThrowIfUndefined(senderAddress, 'senderAddress'); + + const ss58Address = await this.getSS58Address(senderAddress); + const batch = await this.getClaimStakerRewardsBatch(ss58Address); + await this.wallet.sendEvmTransaction({ + from: senderAddress, + to: dispatch, + data: batch.method.toHex(), + successMessage, + failureMessage: 'Call failed', + }); + } + + // @inheritdoc + public async claimAllAndUnstakeFromUnregistered( + senderAddress: string, + contractAddress: string, + successMessage: string + ): Promise { + Guard.ThrowIfUndefined(senderAddress, 'senderAddress'); + Guard.ThrowIfUndefined(contractAddress, 'contractAddress'); + + const ss58Address = await this.getSS58Address(senderAddress); + + const batch = await this.getClaimAllAndUnstakeFromUnregisteredBatch( + ss58Address, + contractAddress + ); + + await this.wallet.sendEvmTransaction({ + from: senderAddress, + to: dispatch, + data: batch.method.toHex(), + successMessage, + failureMessage: 'Call failed', + }); + } + + public async unlockTokens( + senderAddress: string, + amount: number, + successMessage: string + ): Promise { + Guard.ThrowIfUndefined(senderAddress, 'senderAddress'); + + const call = await this.dappStakingRepository.getUnlockCall(amount); + await this.wallet.sendEvmTransaction({ + from: senderAddress, + to: dispatch, + data: call.method.toHex(), + successMessage, + failureMessage: 'Call failed', + }); + } + + // @inheritdoc + public async claimDappRewards( + contractAddress: string, + senderAddress: string, + successMessage: string + ): Promise { + const batch = await this.getClaimDappRewardsBatch(contractAddress); + await this.wallet.sendEvmTransaction({ + from: senderAddress, + to: dispatch, + data: batch.method.toHex(), + successMessage, + failureMessage: 'Call failed', + }); + } + + // @inheritdoc + public async claimBonusRewards(senderAddress: string, successMessage: string): Promise { + Guard.ThrowIfUndefined('senderAddress', senderAddress); + const ss58Address = await this.getSS58Address(senderAddress); + const batch = await this.claimBonusRewardsBatch(ss58Address); + + await this.wallet.sendEvmTransaction({ + from: senderAddress, + to: dispatch, + data: batch.method.toHex(), + successMessage, + failureMessage: 'Call failed', + }); + } + + public async claimStakerAndBonusRewards( + senderAddress: string, + successMessage: string + ): Promise { + Guard.ThrowIfUndefined('senderAddress', senderAddress); + const ss58Address = await this.getSS58Address(senderAddress); + const calls = await this.getClaimStakerAndBonusRewardsCalls(ss58Address); + const batch = await this.dappStakingRepository.batchAllCalls(calls); + + await this.wallet.sendEvmTransaction({ + from: senderAddress, + to: dispatch, + data: batch.method.toHex(), + successMessage, + failureMessage: 'Call failed', + }); + } + + // @inheritdoc + public async claimLockAndStake( + senderAddress: string, + amountToLock: bigint, + stakeInfo: DappStakeInfo[], + unstakeFromAddress: string, + unstakeAmount: bigint, + successMessage: string + ): Promise { + const ss58Address = await this.getSS58Address(senderAddress); + this.guardStake(ss58Address, stakeInfo, unstakeFromAddress, unstakeAmount); + + const batch = await this.getClaimLockAndStakeBatch( + ss58Address, + amountToLock, + stakeInfo, + unstakeFromAddress, + unstakeAmount + ); + + await this.wallet.sendEvmTransaction({ + from: senderAddress, + to: dispatch, + data: batch.method.toHex(), + successMessage, + failureMessage: 'Call failed', + }); + } + + // @inheritdoc + public async claimUnlockedTokens(senderAddress: string, successMessage: string): Promise { + Guard.ThrowIfUndefined('senderAddress', senderAddress); + + const call = await this.dappStakingRepository.getClaimUnlockedTokensCall(); + await this.wallet.sendEvmTransaction({ + from: senderAddress, + to: dispatch, + data: call.method.toHex(), + successMessage, + failureMessage: 'Call failed', + }); + } + + // @inheritdoc + public async relockUnlockingTokens(senderAddress: string, successMessage: string): Promise { + Guard.ThrowIfUndefined('senderAddress', senderAddress); + + const call = await this.dappStakingRepository.getRelockUnlockingTokensCall(); + await this.wallet.sendEvmTransaction({ + from: senderAddress, + to: dispatch, + data: call.method.toHex(), + successMessage, + failureMessage: 'Call failed', + }); + } + + public async getStakerInfo( + address: string, + includePreviousPeriods: boolean + ): Promise> { + const ss58Address = await this.getSS58Address(address); + + return await super.getStakerInfo(ss58Address, includePreviousPeriods); + } + + public async getStakerRewards(senderAddress: string): Promise { + const ss58Address = await this.getSS58Address(senderAddress); + + return await super.getStakerRewards(ss58Address); + } + + public async getBonusRewards(senderAddress: string): Promise { + const ss58Address = await this.getSS58Address(senderAddress); + + return await super.getBonusRewards(ss58Address); + } + + public async startAccountLedgerSubscription(address: string): Promise { + const ss58Address = await this.getSS58Address(address); + + await super.startAccountLedgerSubscription(ss58Address); + } + + private async getSS58Address(evmAddress: string): Promise { + return await this.accountUnificationRepository.getConvertedNativeAddress(evmAddress); + } +} diff --git a/src/staking-v3/logic/services/IDappStakingService.ts b/src/staking-v3/logic/services/IDappStakingService.ts index 871ef69f6..62137f3c1 100644 --- a/src/staking-v3/logic/services/IDappStakingService.ts +++ b/src/staking-v3/logic/services/IDappStakingService.ts @@ -1,4 +1,4 @@ -import { CombinedDappInfo, DappStakeInfo, StakeAmount } from '../models'; +import { CombinedDappInfo, DappStakeInfo, SingularStakingInfo, StakeAmount } from '../models'; /** * @interface IDappStakingService interface for a service containing business logic for dapp staking. @@ -135,4 +135,11 @@ export interface IDappStakingService { relockUnlockingTokens(senderAddress: string, successMessage: string): Promise; unlockTokens(senderAddress: string, amount: number, successMessage: string): Promise; + + getStakerInfo( + address: string, + includePreviousPeriods: boolean + ): Promise>; + + startAccountLedgerSubscription(address: string): Promise; } diff --git a/src/staking-v3/logic/services/index.ts b/src/staking-v3/logic/services/index.ts index 422d545a0..b57372ea0 100644 --- a/src/staking-v3/logic/services/index.ts +++ b/src/staking-v3/logic/services/index.ts @@ -1,2 +1,3 @@ export * from './IDappStakingService'; export * from './DappStakingService'; +export * from './DappStakingServiceEvm'; diff --git a/src/v2/app.container.ts b/src/v2/app.container.ts index aa4de014d..0cbb1c908 100644 --- a/src/v2/app.container.ts +++ b/src/v2/app.container.ts @@ -67,6 +67,7 @@ import { DappStakingRepository as DappStakingRepositoryV3, IDappStakingService as IDappStakingServiceV3, DappStakingService as DappStakingServiceV3, + DappStakingServiceEvm as DappStakingServiceEvmV3, } from 'src/staking-v3'; import { Symbols } from './symbols'; import { IEventAggregator, EventAggregator } from './messaging'; @@ -197,7 +198,22 @@ export default function buildDependencyContainer(network: endpointKey): void { DappStakingRepositoryV3, Symbols.DappStakingRepositoryV3 ); - container.addSingleton(DappStakingServiceV3, Symbols.DappStakingServiceV3); + container.addSingleton(DappStakingServiceV3, Symbols.DappStakingServiceV3); + container.addSingleton( + DappStakingServiceEvmV3, + Symbols.DappStakingServiceEvmV3 + ); + + container + .bind>(Symbols.DappStakingServiceFactoryV3) + .toFactory(() => { + return () => + container.get( + currentWalletType === WalletType.Polkadot + ? Symbols.DappStakingServiceV3 + : Symbols.DappStakingServiceEvmV3 + ); + }); // Start block change subscription. Needed for remaining unlocking blocks calculation. container.get(Symbols.SystemRepository).startBlockSubscription(); diff --git a/src/v2/repositories/implementations/AccountUnificationRepository.ts b/src/v2/repositories/implementations/AccountUnificationRepository.ts index 9414f3123..49a9cb2d5 100644 --- a/src/v2/repositories/implementations/AccountUnificationRepository.ts +++ b/src/v2/repositories/implementations/AccountUnificationRepository.ts @@ -12,6 +12,7 @@ import { ExtrinsicPayload, IApi } from 'src/v2/integration'; import { IdentityData } from 'src/v2/models'; import { IAccountUnificationRepository, IIdentityRepository } from 'src/v2/repositories'; import { Symbols } from 'src/v2/symbols'; +import e from 'express'; @injectable() export class AccountUnificationRepository implements IAccountUnificationRepository { @@ -35,6 +36,10 @@ export class AccountUnificationRepository implements IAccountUnificationReposito public async getMappedNativeAddress(evmAddress: string): Promise { Guard.ThrowIfUndefined('evmAddress', evmAddress); + if (!isValidEvmAddress(evmAddress)) { + return evmAddress; + } + const api = await this.api.getApi(); const nativeAddress = hasProperty(api.query, 'unifiedAccounts') ? await api.query.unifiedAccounts.evmToNative(evmAddress) diff --git a/src/v2/services/implementations/MetamaskWalletService.ts b/src/v2/services/implementations/MetamaskWalletService.ts index cbdd561dd..8008a7f4e 100644 --- a/src/v2/services/implementations/MetamaskWalletService.ts +++ b/src/v2/services/implementations/MetamaskWalletService.ts @@ -124,9 +124,8 @@ export class MetamaskWalletService extends WalletService implements IWalletServi try { const web3 = new Web3(this.provider as any); const rawTx = await getRawEvmTransaction(web3, from, to, data, value); - const estimatedGas = await web3.eth.estimateGas(rawTx); const transactionHash = await web3.eth - .sendTransaction({ ...rawTx, gas: estimatedGas }) + .sendTransaction({ ...rawTx }) .once('transactionHash', (transactionHash) => { this.eventAggregator.publish(new BusyMessage(true)); }) diff --git a/src/v2/symbols.ts b/src/v2/symbols.ts index 528d83dae..84f16bca4 100644 --- a/src/v2/symbols.ts +++ b/src/v2/symbols.ts @@ -38,4 +38,6 @@ export const Symbols = { AccountUnificationRepository: Symbol.for('AccountUnificationRepository'), DappStakingRepositoryV3: Symbol.for('DappStakingRepositoryV3'), DappStakingServiceV3: Symbol.for('DappStakingServiceV3'), + DappStakingServiceEvmV3: Symbol.for('DappStakingServiceEvmV3'), + DappStakingServiceFactoryV3: Symbol.for('DappStakingServiceFactoryV3'), }; diff --git a/tsconfig.json b/tsconfig.json index 2c5258f40..00c8e0ea2 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -6,5 +6,5 @@ "experimentalDecorators": true, "jsx": "preserve" }, - "exclude": ["**/node_modules/**", "**/tests/polkadot_wallet/**", "**/tests/metamask_wallet/**"] + "exclude": ["**/node_modules/**", "**/tests/polkadot_wallet/**", "**/tests/metamask_wallet/**", "**/src-pwa/**", "**/src-ssr/**", "**/rpc-tests/**", "**/playwright-report/**", "**/dist/**", "**/.quasar/**", "**/.github/**"] }