From bb8c86f6142a69e7fe31c1d8119dd0bd9991c074 Mon Sep 17 00:00:00 2001 From: Bobo Date: Tue, 7 Nov 2023 09:28:11 +0100 Subject: [PATCH 1/2] Account unification - add mapping fee to total costs calculation (#1000) * Add mapping fee to total costs calculation * Fix for missing AuIcon import * extra AuIcon import --------- Co-authored-by: Gregory Luneau Co-authored-by: Gregory Luneau --- src/hooks/wallet/useAccountUnification.ts | 12 +++++++++--- src/v2/repositories/IAccountUnificationRepository.ts | 6 ++++++ .../implementations/AccountUnificationRepository.ts | 8 ++++++++ 3 files changed, 23 insertions(+), 3 deletions(-) diff --git a/src/hooks/wallet/useAccountUnification.ts b/src/hooks/wallet/useAccountUnification.ts index 6819754cb..3697fa606 100644 --- a/src/hooks/wallet/useAccountUnification.ts +++ b/src/hooks/wallet/useAccountUnification.ts @@ -32,7 +32,7 @@ import { useI18n } from 'vue-i18n'; import Web3 from 'web3'; import { AbiItem } from 'web3-utils'; import { useNetworkInfo } from '../useNetworkInfo'; -import { IIdentityRepository } from 'src/v2/repositories'; +import { IAccountUnificationRepository, IIdentityRepository } from 'src/v2/repositories'; import { UnifiedAccount } from 'src/store/general/state'; const provider = get(window, 'ethereum') as any; @@ -369,8 +369,14 @@ export const useAccountUnification = () => { const getCost = async (): Promise => { const TOTAL_FIELDS = 5; // account name, avatar address key, avatar address value, token id key, token id value const identityRepository = container.get(Symbols.IdentityRepository); - const depositInfo = await identityRepository.getDepositInfo(); - const totalDeposit = depositInfo.basic + depositInfo.field * BigInt(TOTAL_FIELDS); + const auRepository = container.get( + Symbols.AccountUnificationRepository + ); + const [depositInfo, mappingFee] = await Promise.all([ + identityRepository.getDepositInfo(), + auRepository.getUnificationFee(), + ]); + const totalDeposit = depositInfo.basic + depositInfo.field * BigInt(TOTAL_FIELDS) + mappingFee; return `${ethers.utils.formatEther(totalDeposit.toString())} ${nativeTokenSymbol.value}`; }; diff --git a/src/v2/repositories/IAccountUnificationRepository.ts b/src/v2/repositories/IAccountUnificationRepository.ts index 625181d23..6b000f28b 100644 --- a/src/v2/repositories/IAccountUnificationRepository.ts +++ b/src/v2/repositories/IAccountUnificationRepository.ts @@ -45,4 +45,10 @@ export interface IAccountUnificationRepository { signature: string, identityInfo: IdentityData ): Promise; + + /** + * Gets an account unification fee. + * @returns Account unification fee. + */ + getUnificationFee(): Promise; } diff --git a/src/v2/repositories/implementations/AccountUnificationRepository.ts b/src/v2/repositories/implementations/AccountUnificationRepository.ts index 4ec15cd20..375dff864 100644 --- a/src/v2/repositories/implementations/AccountUnificationRepository.ts +++ b/src/v2/repositories/implementations/AccountUnificationRepository.ts @@ -1,3 +1,4 @@ +import { u128 } from '@polkadot/types'; import { buildEvmAddress, isValidEvmAddress, toSS58Address } from '@astar-network/astar-sdk-core'; import { AccountId32, H160 } from '@polkadot/types/interfaces'; import { inject, injectable } from 'inversify'; @@ -79,4 +80,11 @@ export class AccountUnificationRepository implements IAccountUnificationReposito return api.tx.utility.batchAll([identityCall, unifyCall]); } + + public async getUnificationFee(): Promise { + const api = await this.api.getApi(); + const fee = api.consts.unifiedAccounts.accountMappingStorageFee; + + return fee.toBigInt(); + } } From aaee0cc809f3498d9d161fccbf4becc52dd87abd Mon Sep 17 00:00:00 2001 From: Bobo Date: Tue, 7 Nov 2023 09:29:40 +0100 Subject: [PATCH 2/2] Select account - unified account display (#995) * Load mapped EVM account * Display unified account modal * Merge branch 'main' into feat/au-select-account * Connected account dot and radio button fixes * Revert radio changes * Cleanup * Amount repositioned * Fix for dark theme * dot margin --- src/components/assets/Account.vue | 1 + src/components/header/modals/Account.vue | 104 +++++++++++ src/components/header/modals/ModalAccount.vue | 94 +++++----- .../header/modals/UnifiedAccount.vue | 166 ++++++++++++++++++ .../header/styles/modal-account.scss | 15 +- src/hooks/useAccount.ts | 45 +++-- src/store/general/state.ts | 2 + .../implementations/NftRepository.ts | 2 +- 8 files changed, 351 insertions(+), 78 deletions(-) create mode 100644 src/components/header/modals/Account.vue create mode 100644 src/components/header/modals/UnifiedAccount.vue diff --git a/src/components/assets/Account.vue b/src/components/assets/Account.vue index e5329cab2..9d32fabcf 100644 --- a/src/components/assets/Account.vue +++ b/src/components/assets/Account.vue @@ -111,6 +111,7 @@ import { getEvmMappedSs58Address, setAddressMapping } from 'src/hooks/helper/add import { useStore } from 'src/store'; import { computed, defineComponent, ref, watch, watchEffect } from 'vue'; import { useI18n } from 'vue-i18n'; +import AuIcon from '../header/modals/account-unification/AuIcon.vue'; export default defineComponent({ components: { diff --git a/src/components/header/modals/Account.vue b/src/components/header/modals/Account.vue new file mode 100644 index 000000000..9fcb23a63 --- /dev/null +++ b/src/components/header/modals/Account.vue @@ -0,0 +1,104 @@ + + + diff --git a/src/components/header/modals/ModalAccount.vue b/src/components/header/modals/ModalAccount.vue index 9b5c36503..d66dce316 100644 --- a/src/components/header/modals/ModalAccount.vue +++ b/src/components/header/modals/ModalAccount.vue @@ -53,53 +53,26 @@ class="radio-btn" @change="selAccount = account.address" /> - + +
@@ -146,13 +119,17 @@ import { useStore } from 'src/store'; import { SubstrateAccount } from 'src/store/general/state'; import { computed, defineComponent, PropType, ref, watch, onUnmounted, watchEffect } from 'vue'; import { useI18n } from 'vue-i18n'; -import { useBreakpoints, useNetworkInfo } from 'src/hooks'; +import { useBreakpoints, useNetworkInfo, useAccount } from 'src/hooks'; import { Ledger } from '@polkadot/hw-ledger'; import { astarChain } from 'src/config/chain'; +import UnifiedAccount from './UnifiedAccount.vue'; +import Account from './Account.vue'; export default defineComponent({ components: { SelectedWallet, + UnifiedAccount, + Account, }, props: { isOpen: { @@ -207,6 +184,7 @@ export default defineComponent({ const { t } = useI18n(); const { width, screenSize } = useBreakpoints(); const { currentNetworkChain } = useNetworkInfo(); + const { checkIfUnified } = useAccount(); const substrateAccounts = computed(() => { const accounts: SubstrateAccount[] = accountBalanceMap.value || []; @@ -300,11 +278,21 @@ export default defineComponent({ isLoadingBalance.value = true; const updatedAccountMap = await Promise.all( substrateAccountsAll.value.map(async (it) => { - const balance = await fetchNativeBalance({ - api: $api as ApiPromise, - address: it.address, - }); - return { ...it, balance }; + const [balance, unifiedAccount] = await Promise.all([ + fetchNativeBalance({ + api: $api as ApiPromise, + address: it.address, + }), + checkIfUnified(it.address, false), + ]); + + return { + ...it, + balance, + evmAddress: unifiedAccount?.evmAddress, + name: unifiedAccount?.name ?? it.name, + avatarUrl: unifiedAccount?.avatarUrl, + }; }) ); // Memo: we use local `accountBalanceMap` state because updating global `substrateAccounts` state triggers UI bug on this drawer diff --git a/src/components/header/modals/UnifiedAccount.vue b/src/components/header/modals/UnifiedAccount.vue new file mode 100644 index 000000000..6c5edfa6a --- /dev/null +++ b/src/components/header/modals/UnifiedAccount.vue @@ -0,0 +1,166 @@ + + + diff --git a/src/components/header/styles/modal-account.scss b/src/components/header/styles/modal-account.scss index 6729b98f0..dbb4cd797 100644 --- a/src/components/header/styles/modal-account.scss +++ b/src/components/header/styles/modal-account.scss @@ -103,7 +103,6 @@ display: flex; align-items: flex-start; column-gap: 8px; - margin-top: 10px; } .row--account { @@ -124,12 +123,11 @@ } .address { - color: $gray-4; + color: $gray-3; font-weight: 510; font-size: 12px; line-height: 20px; text-align: left; - margin-top: 4px; position: relative; @media (min-width: $sm) { margin-top: 0px; @@ -210,8 +208,9 @@ } .dot { - position: relative; - top: -30px; + margin-top: 12px; + margin-left: 8px; + align-self: flex-start; height: 7px; width: 8px; border-radius: 90%; @@ -221,7 +220,7 @@ .text--balance { font-weight: 500; font-size: 12px; - color: $gray-5; + color: $gray-3; } .text--balance-hide { @@ -271,4 +270,8 @@ .text--balance { color: white; } + + .account-name { + color: $gray-1; + } } diff --git a/src/hooks/useAccount.ts b/src/hooks/useAccount.ts index 459e45b4c..9a276e667 100644 --- a/src/hooks/useAccount.ts +++ b/src/hooks/useAccount.ts @@ -4,7 +4,7 @@ import { LOCAL_STORAGE } from 'src/config/localStorage'; import { SupportMultisig, SupportWallet } from 'src/config/wallets'; import { Multisig } from 'src/modules/multisig'; import { useStore } from 'src/store'; -import { SubstrateAccount } from 'src/store/general/state'; +import { SubstrateAccount, UnifiedAccount } from 'src/store/general/state'; import { container } from 'src/v2/common'; import { IEventAggregator, UnifyAccountMessage } from 'src/v2/messaging'; import { IdentityRepository } from 'src/v2/repositories/implementations/IdentityRepository'; @@ -76,7 +76,10 @@ export const useAccount = () => { }); }; - const checkIfUnified = async (address: string): Promise => { + const checkIfUnified = async ( + address: string, + persistState = true + ): Promise => { const service = container.get(Symbols.AccountUnificationService); const isEvmAddress = isValidEvmAddress(address); @@ -107,26 +110,32 @@ export const useAccount = () => { } } - if (isValidEvmAddress(address)) { - store.commit('general/setUnifiedAccount', { - nativeAddress: mapped, - evmAddress: address, - name, - avatarUrl, - avatarMetadata: { ...nft, image: avatarUrl }, - }); - } else { - store.commit('general/setUnifiedAccount', { - nativeAddress: address, - evmAddress: mapped, - name, - avatarUrl, - avatarMetadata: { ...nft, image: avatarUrl }, - }); + const account: UnifiedAccount = { + nativeAddress: isEvmAddress ? mapped : address, + evmAddress: isEvmAddress ? address : mapped, + name, + avatarUrl, + avatarMetadata: { + name: nft?.name || '', + description: nft?.description || '', + image: avatarUrl || '', + contractAddress: nft?.contractAddress || '', + tokenId: nft?.tokenId || '', + tokenUri: nft?.tokenUri || '', + ownerAddress: nft?.ownerAddress || '', + }, + }; + + if (persistState) { + store.commit('general/setUnifiedAccount', account); } + + return account; } else { store.commit('general/setUnifiedAccount', undefined); } + + return undefined; }; const showAccountUnificationModal = (): void => { diff --git a/src/store/general/state.ts b/src/store/general/state.ts index 6f1e046a6..208d405f7 100644 --- a/src/store/general/state.ts +++ b/src/store/general/state.ts @@ -9,6 +9,8 @@ export type SubstrateAccount = { name: string; source: string; balance?: string; + evmAddress?: string; + avatarUrl?: string; }; export enum AlertType { diff --git a/src/v2/repositories/implementations/NftRepository.ts b/src/v2/repositories/implementations/NftRepository.ts index 2b217ac03..b7da2e4b5 100644 --- a/src/v2/repositories/implementations/NftRepository.ts +++ b/src/v2/repositories/implementations/NftRepository.ts @@ -21,7 +21,7 @@ export class NftRepository implements INftRepository { tokenId: string ): Promise { const metadataUri = `${TOKEN_API_URL}/v1/${network}/nft/metadata/${contractAddress}/${tokenId}`; - const result = await axios.get(metadataUri); + const result = await axios.get(metadataUri); return result.data ?? undefined; }