diff --git a/src/components/common/TokenBalanceNative.vue b/src/components/common/TokenBalanceNative.vue index f63387381..4e5e524d9 100644 --- a/src/components/common/TokenBalanceNative.vue +++ b/src/components/common/TokenBalanceNative.vue @@ -3,7 +3,7 @@ :balance="ethers.utils.formatEther(balance)" :text="text" :decimals="decimals" - :symbol="nativeTokenSymbol" + :symbol="showTokenSymbol ? nativeTokenSymbol : ''" /> @@ -32,6 +32,11 @@ export default defineComponent({ required: false, default: 3, }, + showTokenSymbol: { + type: Boolean, + required: false, + default: true, + }, }, setup() { const { nativeTokenSymbol } = useNetworkInfo(); diff --git a/src/components/dapp-staking/register/RegisterDapp.vue b/src/components/dapp-staking/register/RegisterDapp.vue index f128442c2..ef0db537a 100644 --- a/src/components/dapp-staking/register/RegisterDapp.vue +++ b/src/components/dapp-staking/register/RegisterDapp.vue @@ -1,7 +1,7 @@ + + + + diff --git a/src/staking-v3/components/my-staking/ModalUnbondDapp.vue b/src/staking-v3/components/my-staking/ModalUnbondDapp.vue index 8ebf0baba..7343e0f6e 100644 --- a/src/staking-v3/components/my-staking/ModalUnbondDapp.vue +++ b/src/staking-v3/components/my-staking/ModalUnbondDapp.vue @@ -112,7 +112,7 @@ export default defineComponent({ const nativeTokenImg = computed(() => getTokenImage({ isNativeToken: true, symbol: nativeTokenSymbol.value }) ); - const { stakerInfo, constants, unstake, canUnStake } = useDappStaking(); + const { stakerInfo, constants, unstake, canUnStake, getStakerInfo } = useDappStaking(); const store = useStore(); const minStakingAmount = computed(() => { @@ -123,7 +123,7 @@ export default defineComponent({ return minStakingAmount.value > Number(maxAmount.value) - Number(amount.value); }); const maxAmount = computed(() => { - const selectedDappStakes = stakerInfo.value?.get(props.dapp.chain.address); + const selectedDappStakes = getStakerInfo(props.dapp.chain.address); return selectedDappStakes ? String(ethers.utils.formatEther(selectedDappStakes.staked.totalStake.toString())) diff --git a/src/staking-v3/hooks/index.ts b/src/staking-v3/hooks/index.ts index 4747d4274..9411d2f13 100644 --- a/src/staking-v3/hooks/index.ts +++ b/src/staking-v3/hooks/index.ts @@ -6,3 +6,4 @@ export * from './useDappStakingNavigation'; export * from './usePeriod'; export * from './useAprV3'; export * from './useVotingCountdown'; +export * from './useRegisterDapp'; diff --git a/src/staking-v3/hooks/useDappStaking.ts b/src/staking-v3/hooks/useDappStaking.ts index a7dd6d234..ecae1a589 100644 --- a/src/staking-v3/hooks/useDappStaking.ts +++ b/src/staking-v3/hooks/useDappStaking.ts @@ -28,6 +28,7 @@ import { ethers } from 'ethers'; import { initialDappTiersConfiguration, initialTiersConfiguration } from '../store/state'; import { checkIsDappStakingV3 } from 'src/modules/dapp-staking'; import { ApiPromise } from '@polkadot/api'; +import { isValidEvmAddress } from '@astar-network/astar-sdk-core'; export interface RewardsPerPeriod { period: number; @@ -424,7 +425,7 @@ export function useDappStaking() { const canUnStake = (dappAddress: string, amount: number): [boolean, string] => { const unstakeAmount = BigInt(ethers.utils.parseEther(amount.toString()).toString()); - const dappInfo = stakerInfo.value.get(dappAddress); + const dappInfo = getStakerInfo(dappAddress); const stakedAmount = dappInfo?.staked.totalStake ?? BigInt(0); if (amount <= 0) { @@ -454,6 +455,12 @@ export function useDappStaking() { return [true, '']; }; + const getStakerInfo = (dappAddress: string): SingularStakingInfo | undefined => { + const isEvmAddress = isValidEvmAddress(dappAddress); + + return stakerInfo.value?.get(isEvmAddress ? dappAddress.toLowerCase() : dappAddress); + }; + const getDappTiers = async (era: number): Promise => { const stakingRepo = container.get(Symbols.DappStakingRepositoryV3); const tiers = await stakingRepo.getDappTiers(era); @@ -509,7 +516,7 @@ export function useDappStaking() { */ const updateStakersCount = (stakedContracts: string[], amount: number): void => { for (const contract of stakedContracts) { - const alreadyStaked = stakerInfo.value.get(contract); + const alreadyStaked = getStakerInfo(contract); if (!alreadyStaked) { const dapp = getDapp(contract); if (dapp && dapp.dappDetails) { @@ -564,5 +571,6 @@ export function useDappStaking() { fetchEraLengthsToStore, getUnclaimedDappRewardsPerPeriod, rewardExpiresInNextPeriod, + getStakerInfo, }; } diff --git a/src/staking-v3/hooks/useDapps.ts b/src/staking-v3/hooks/useDapps.ts index 4ddc71040..2d34098a6 100644 --- a/src/staking-v3/hooks/useDapps.ts +++ b/src/staking-v3/hooks/useDapps.ts @@ -1,6 +1,12 @@ import { computed } from 'vue'; import { container } from 'src/v2/common'; -import { CombinedDappInfo, IDappStakingRepository, IDappStakingService } from '../logic'; +import { + CombinedDappInfo, + DappInfo, + DappState, + IDappStakingRepository, + IDappStakingService, +} from '../logic'; import { Symbols } from 'src/v2/symbols'; import { useNetworkInfo } from 'src/hooks'; import { BusyMessage, IEventAggregator } from 'src/v2/messaging'; @@ -13,8 +19,8 @@ export function useDapps() { const registeredDapps = computed( () => store.getters['stakingV3/getRegisteredDapps'] ); - const allDapps = computed(() => store.getters['stakingV3/getDapps']); + const newDapps = computed(() => store.getters['stakingV3/getNewDapps']); const fetchDappsToStore = async (): Promise => { // Don't fetch if we already have dApps. @@ -29,7 +35,8 @@ export function useDapps() { try { aggregator.publish(new BusyMessage(true)); const dApps = await service.getDapps(currentNetworkName.value.toLowerCase()); - store.commit('stakingV3/addDapps', dApps); + store.commit('stakingV3/addDapps', dApps.fullInfo); + store.commit('stakingV3/addNewDapps', dApps.chainInfo); // Memo: this can a heavy operations since we are querying all dapps stakes for a chain. await fetchStakeAmountsToStore(); } finally { @@ -77,6 +84,11 @@ export function useDapps() { const getDapp = (dappAddress: string): CombinedDappInfo | undefined => allDapps.value.find((d) => d.chain.address.toLowerCase() === dappAddress?.toLowerCase()); + const getDappByOwner = (ownerAddress: string): DappInfo | undefined => { + const dapps = [...allDapps.value.map((x) => x.chain), ...newDapps.value]; + return dapps.find((d) => d.owner === ownerAddress && d.state === DappState.Registered); + }; + return { registeredDapps, allDapps, @@ -84,5 +96,6 @@ export function useDapps() { fetchDappToStore, fetchStakeAmountsToStore, getDapp, + getDappByOwner, }; } diff --git a/src/staking-v3/hooks/useRegisterDapp.ts b/src/staking-v3/hooks/useRegisterDapp.ts new file mode 100644 index 000000000..e0fcb0471 --- /dev/null +++ b/src/staking-v3/hooks/useRegisterDapp.ts @@ -0,0 +1,40 @@ +import { watch, ref } from 'vue'; +import { container } from 'src/v2/common'; +import { IDappStakingServiceV2V3 } from '../logic'; +import { Symbols } from 'src/v2/symbols'; +import { useAccount, useNetworkInfo } from 'src/hooks'; +import { useDapps } from './useDapps'; + +export function useRegisterDapp() { + const { currentAccount, isH160Formatted } = useAccount(); + const { getDappByOwner } = useDapps(); + const { currentNetworkName } = useNetworkInfo(); + + const dappAddressToRegister = ref(); + + const getDappAddressToRegister = async (): Promise => { + const service = container.get(Symbols.DappStakingServiceV2V3); + const developerContract = + currentAccount.value && !isH160Formatted.value + ? getDappByOwner(currentAccount.value)?.address + : undefined; + if (developerContract) { + const dapp = await service.getDapp(developerContract, currentNetworkName.value); + return dapp === undefined ? developerContract : undefined; + } + + return undefined; + }; + + watch( + [currentAccount], + async () => { + if (currentAccount) { + dappAddressToRegister.value = await getDappAddressToRegister(); + } + }, + { immediate: true } + ); + + return { dappAddressToRegister }; +} diff --git a/src/staking-v3/logic/services/DappStakingService.ts b/src/staking-v3/logic/services/DappStakingService.ts index 8bbb7253f..669bb489c 100644 --- a/src/staking-v3/logic/services/DappStakingService.ts +++ b/src/staking-v3/logic/services/DappStakingService.ts @@ -1,5 +1,11 @@ import { inject, injectable } from 'inversify'; -import { CombinedDappInfo, DappStakeInfo, SingularStakingInfo, StakeAmount } from '../models'; +import { + CombinedDappInfo, + DappInfo, + DappStakeInfo, + SingularStakingInfo, + StakeAmount, +} from '../models'; import { IDappStakingService } from './IDappStakingService'; import { Symbols } from 'src/v2/symbols'; import { IDappStakingRepository, IDataProviderRepository } from '../repositories'; @@ -19,7 +25,9 @@ export class DappStakingService implements IDappStakingService { ) {} // @inheritdoc - public async getDapps(network: string): Promise { + public async getDapps( + network: string + ): Promise<{ fullInfo: CombinedDappInfo[]; chainInfo: DappInfo[] }> { Guard.ThrowIfUndefined(network, 'network'); const [storeDapps, chainDapps, tokenApiDapps] = await Promise.all([ @@ -30,6 +38,7 @@ export class DappStakingService implements IDappStakingService { // Map on chain and in store dApps const dApps: CombinedDappInfo[] = []; + const onlyChain: DappInfo[] = []; chainDapps.forEach((chainDapp) => { const storeDapp = storeDapps.find( (x) => x.address.toLowerCase() === chainDapp.address.toLowerCase() @@ -43,10 +52,12 @@ export class DappStakingService implements IDappStakingService { chain: chainDapp, dappDetails, }); + } else { + onlyChain.push(chainDapp); } }); - return dApps; + return { fullInfo: dApps, chainInfo: onlyChain }; } // @inheritdoc diff --git a/src/staking-v3/logic/services/DappStakingServiceV2V3.ts b/src/staking-v3/logic/services/DappStakingServiceV2V3.ts new file mode 100644 index 000000000..f7688fe30 --- /dev/null +++ b/src/staking-v3/logic/services/DappStakingServiceV2V3.ts @@ -0,0 +1,62 @@ +// Temporary service to be used on places (e.g. dApp registration) where different dApp staking +// service implementation (v2 or v3) is needed, depending on dApp staking version deployed on a node. +// TODO remove after Astar release. + +import { container } from 'src/v2/common'; +import { useDappStaking } from '../../hooks'; +import { IDappStakingService } from 'src/v2/services'; +import { IDappStakingService as IDappStakingServiceV3 } from '.'; +import { Symbols } from 'src/v2/symbols'; +import { inject, injectable } from 'inversify'; +import { IApi } from 'src/v2/integration'; +import { IDappStakingRepository as IDappStakingRepositoryV3 } from '../repositories'; +import { EditDappItem } from 'src/store/dapp-staking/state'; + +export interface IDappStakingServiceV2V3 { + getRegisteredContract(developerAddress: string): Promise; + + /** + * Gets dapp data from Firebase. + * @param contractAddress Dapp contract address. + * @param network Name of the network where dapp has been deployed. + * @param forEdit Flag to indicate if dapp data should be fetched with encoded images. + */ + getDapp( + contractAddress: string, + network: string, + forEdit?: boolean + ): Promise; +} + +@injectable() +export class DappStakingServiceV2V3 implements IDappStakingServiceV2V3 { + constructor( + @inject(Symbols.DefaultApi) private api: IApi, + @inject(Symbols.DappStakingService) private stakingV2: IDappStakingService, + @inject(Symbols.DappStakingRepositoryV3) private repositoryV3: IDappStakingRepositoryV3 + ) {} + + public async getRegisteredContract(developerAddress: string): Promise { + if (await this.isV3()) { + const allDapps = await this.repositoryV3.getChainDapps(); + const dapp = allDapps.find((d) => d.owner === developerAddress); + + return dapp?.address; + } else { + return await this.stakingV2.getRegisteredContract(developerAddress); + } + } + + public async getDapp( + contractAddress: string, + network: string, + forEdit?: boolean + ): Promise { + return await this.stakingV2.getDapp(contractAddress, network, forEdit); + } + + private async isV3() { + const api = await this.api.getApi(); + return api.query.hasOwnProperty('dappStaking'); + } +} diff --git a/src/staking-v3/logic/services/IDappStakingService.ts b/src/staking-v3/logic/services/IDappStakingService.ts index 45a99178e..ee9716fd6 100644 --- a/src/staking-v3/logic/services/IDappStakingService.ts +++ b/src/staking-v3/logic/services/IDappStakingService.ts @@ -1,4 +1,10 @@ -import { CombinedDappInfo, DappStakeInfo, SingularStakingInfo, StakeAmount } from '../models'; +import { + CombinedDappInfo, + DappInfo, + DappStakeInfo, + SingularStakingInfo, + StakeAmount, +} from '../models'; /** * @interface IDappStakingService interface for a service containing business logic for dapp staking. @@ -6,8 +12,10 @@ import { CombinedDappInfo, DappStakeInfo, SingularStakingInfo, StakeAmount } fro export interface IDappStakingService { /** * Gets the dapps for the given network. + * @param network Name of the network to get dapps for. + * @returns A map containing full dapps info (chain and firebase data) and chain info (only for new dapps not stored in firebase yet). */ - getDapps(network: string): Promise; + getDapps(network: string): Promise<{ fullInfo: CombinedDappInfo[]; chainInfo: DappInfo[] }>; /** * Invokes claim staker rewards, unstake and unlock calls. diff --git a/src/staking-v3/logic/services/index.ts b/src/staking-v3/logic/services/index.ts index b57372ea0..125f3fb29 100644 --- a/src/staking-v3/logic/services/index.ts +++ b/src/staking-v3/logic/services/index.ts @@ -1,3 +1,4 @@ export * from './IDappStakingService'; export * from './DappStakingService'; export * from './DappStakingServiceEvm'; +export * from './DappStakingServiceV2V3'; diff --git a/src/staking-v3/store/getters.ts b/src/staking-v3/store/getters.ts index 2d694cef8..be61d78c7 100644 --- a/src/staking-v3/store/getters.ts +++ b/src/staking-v3/store/getters.ts @@ -13,11 +13,13 @@ import { DAppTierRewards, TiersConfiguration, EraLengths, + DappInfo, } from '../logic'; export interface DappStakingGetters { getVersion(state: DappStakingState): string; getDapps(state: DappStakingState): CombinedDappInfo[]; + getNewDapps(state: DappStakingState): DappInfo[]; getRegisteredDapps(state: DappStakingState): CombinedDappInfo[]; getProtocolState(state: DappStakingState): ProtocolState | undefined; getLedger(state: DappStakingState): AccountLedger | undefined; @@ -33,6 +35,7 @@ export interface DappStakingGetters { const getters: GetterTree & DappStakingGetters = { getVersion: (state) => state.version, getDapps: (state) => state.dapps, + getNewDapps: (state) => state.newDapps, getRegisteredDapps: (state) => state.dapps.filter((x) => x.chain.state === DappState.Registered), getProtocolState: (state) => state.protocolState, getLedger: (state) => state.ledger, diff --git a/src/staking-v3/store/mutations.ts b/src/staking-v3/store/mutations.ts index db74baf1e..e49ba51c8 100644 --- a/src/staking-v3/store/mutations.ts +++ b/src/staking-v3/store/mutations.ts @@ -18,6 +18,7 @@ import { export interface DappStakingMutations { addDapps(state: DappStakingState, dapps: CombinedDappInfo[]): void; + addNewDapps(state: DappStakingState, dapps: DappInfo[]): void; addDapp(state: DappStakingState, dapp: CombinedDappInfo): void; updateDappExtended(state: DappStakingState, dapp: Dapp): void; updateDappChain(state: DappStakingState, dapp: DappInfo): void; @@ -56,6 +57,9 @@ const mutations: MutationTree & DappStakingMutations = { addDapps(state, dapps) { state.dapps = dapps; }, + addNewDapps(state, dapps) { + state.newDapps = dapps; + }, addDapp(state, dapp) { state.dapps.push(dapp); }, diff --git a/src/staking-v3/store/state.ts b/src/staking-v3/store/state.ts index c819784b5..84453bfad 100644 --- a/src/staking-v3/store/state.ts +++ b/src/staking-v3/store/state.ts @@ -9,11 +9,13 @@ import { DAppTierRewards, TiersConfiguration, EraLengths, + DappInfo, } from '../logic'; export interface DappStakingState { version: string; dapps: CombinedDappInfo[]; + newDapps: DappInfo[]; protocolState: ProtocolState | undefined; ledger: AccountLedger | undefined; stakerInfo: Map | undefined; @@ -29,6 +31,7 @@ function state(): DappStakingState { return { version: '3.0.0', dapps: [], + newDapps: [], protocolState: undefined, ledger: undefined, stakerInfo: undefined, diff --git a/src/store/dapp-staking/actions.ts b/src/store/dapp-staking/actions.ts index 1fa7c4389..dc44078db 100644 --- a/src/store/dapp-staking/actions.ts +++ b/src/store/dapp-staking/actions.ts @@ -125,28 +125,6 @@ const actions: ActionTree = { async registerDappApi({ commit, dispatch }, parameters: RegisterParameters): Promise { if (parameters.api) { try { - if (!parameters.signature) { - // If no signature received, it means we are using the - // old dapp registration logic (to be removed after all networks are updated.) - const transaction = parameters.api.tx.dappsStaking.register( - getDappAddressEnum(parameters.dapp.address) - ); - - const signedTransaction = await sign({ - transaction, - senderAddress: parameters.senderAddress, - substrateAccounts: parameters.substrateAccounts, - isCustomSignature: parameters.isCustomSignature, - dispatch, - tip: parameters.tip, - getCallFunc: parameters.getCallFunc, - }); - - if (signedTransaction) { - parameters.signature = signedTransaction.toJSON(); - } - } - const payload = { name: parameters.dapp.name, iconFile: getFileInfo(parameters.dapp.iconFileName, parameters.dapp.iconFile), diff --git a/src/v2/app.container.ts b/src/v2/app.container.ts index 73285e5c8..d3760ba3b 100644 --- a/src/v2/app.container.ts +++ b/src/v2/app.container.ts @@ -79,6 +79,10 @@ import { xcmToken, XcmTokenInformation } from 'src/modules/xcm'; import { XvmRepository } from 'src/v2/repositories/implementations/XvmRepository'; import { XvmService } from 'src/v2/services/implementations/XvmService'; import { IdentityRepository } from './repositories/implementations/IdentityRepository'; +import { + DappStakingServiceV2V3, + IDappStakingServiceV2V3, +} from 'src/staking-v3/logic/services/DappStakingServiceV2V3'; import { IDataProviderRepository, TokenApiProviderRepository } from '../staking-v3/logic'; let currentWalletType = WalletType.Polkadot; @@ -220,6 +224,11 @@ export default function buildDependencyContainer(network: endpointKey): void { ); }); + container.addSingleton( + DappStakingServiceV2V3, + Symbols.DappStakingServiceV2V3 + ); + // Start block change subscription. Needed for remaining unlocking blocks calculation. container.get(Symbols.SystemRepository).startBlockSubscription(); } diff --git a/src/v2/symbols.ts b/src/v2/symbols.ts index 76b3670c9..df0e7c346 100644 --- a/src/v2/symbols.ts +++ b/src/v2/symbols.ts @@ -40,5 +40,6 @@ export const Symbols = { DappStakingServiceV3: Symbol.for('DappStakingServiceV3'), DappStakingServiceEvmV3: Symbol.for('DappStakingServiceEvmV3'), DappStakingServiceFactoryV3: Symbol.for('DappStakingServiceFactoryV3'), + DappStakingServiceV2V3: Symbol.for('DappStakingServiceV2V3'), TokenApiProviderRepository: Symbol.for('TokenApiProviderRepository'), }; diff --git a/tests/test_specs/dappstaking-transactions.spec.ts b/tests/test_specs/dappstaking-transactions.spec.ts index cecc3a9ac..c214cfe4c 100644 --- a/tests/test_specs/dappstaking-transactions.spec.ts +++ b/tests/test_specs/dappstaking-transactions.spec.ts @@ -77,7 +77,7 @@ test.describe('dApp staking transactions', () => { page .locator('div') .filter({ - hasText: /^Account must hold amount greater than 10ASTR in transferrable after you stake\.$/, + hasText: /^Account must hold amount greater than 10ASTR in transferable after you stake\.$/, }) .locator('span') ).toBeVisible();