From 51b5ac97e114e66d003bd21b284c38d331f145f1 Mon Sep 17 00:00:00 2001 From: Michal Zielenkiewicz Date: Fri, 11 Oct 2024 09:23:20 +0200 Subject: [PATCH 01/11] Use eth address in account details --- .changelog/1564.bugfix.md | 1 + src/app/utils/helpers.ts | 3 +-- src/oasis-nexus/api.ts | 17 ++++++++++------- 3 files changed, 12 insertions(+), 9 deletions(-) create mode 100644 .changelog/1564.bugfix.md diff --git a/.changelog/1564.bugfix.md b/.changelog/1564.bugfix.md new file mode 100644 index 000000000..b565d84da --- /dev/null +++ b/.changelog/1564.bugfix.md @@ -0,0 +1 @@ +Retain ETH address in details page for accounts with no EVM transactions diff --git a/src/app/utils/helpers.ts b/src/app/utils/helpers.ts index 551c36d6c..663df7683 100644 --- a/src/app/utils/helpers.ts +++ b/src/app/utils/helpers.ts @@ -99,8 +99,7 @@ export function getEthAddressForAccount( possibleEthAddress?: string, ): string | undefined { // In case of an empty account - if (account.stats.num_txns <= 0 && possibleEthAddress && isValidEthAddress(possibleEthAddress)) - return possibleEthAddress + if (possibleEthAddress && isValidEthAddress(possibleEthAddress)) return possibleEthAddress return getEthAccountAddressFromPreimage(account.address_preimage) } diff --git a/src/oasis-nexus/api.ts b/src/oasis-nexus/api.ts index 6d85f8b52..db592ec95 100644 --- a/src/oasis-nexus/api.ts +++ b/src/oasis-nexus/api.ts @@ -16,7 +16,12 @@ import { RuntimeAccount, RuntimeEventType, } from './generated/api' -import { getAccountSize, getEthAddressForAccount, getOasisAddressOrNull } from '../app/utils/helpers' +import { + getAccountSize, + getEthAddressForAccount, + getOasisAddressOrNull, + isValidEthAddress, +} from '../app/utils/helpers' import { getCancelTitle, getParameterChangeTitle, getProposalTitle } from '../app/utils/proposals' import { Network } from '../types/network' import { SearchScope } from '../types/searchScope' @@ -374,7 +379,7 @@ export const useGetConsensusAccountsAddress: typeof generated.useGetConsensusAcc ...options, query: { ...(options?.query ?? {}), - enabled: !!address && (options?.query?.enabled ?? true), + enabled: options?.query?.enabled ?? true, }, request: { ...options?.request, @@ -413,14 +418,11 @@ export const useGetRuntimeAccountsAddress: typeof generated.useGetRuntimeAccount address, options, ) => { - // console.log('Should we get', runtime, '/', address, '?', options?.query?.enabled) - const oasisAddress = getOasisAddressOrNull(address) - - const query = generated.useGetRuntimeAccountsAddress(network, runtime, oasisAddress!, { + const query = generated.useGetRuntimeAccountsAddress(network, runtime, address, { ...options, query: { ...(options?.query ?? {}), - enabled: !!oasisAddress && (options?.query?.enabled ?? true), + enabled: !!address && (options?.query?.enabled ?? true), }, request: { ...options?.request, @@ -477,6 +479,7 @@ export const useGetRuntimeAccountsAddress: typeof generated.useGetRuntimeAccount const runtimeAccount = query.data?.data // TODO: Remove after account balances on Nexus are in sync with the node + const oasisAddress = getOasisAddressOrNull(address) const rpcAccountBalances = useQuery({ enabled: !!oasisAddress, queryKey: [oasisAddress, network, runtime], From f00e4a6a939897218a04d5f586fcc7edf3f55537 Mon Sep 17 00:00:00 2001 From: Michal Zielenkiewicz Date: Fri, 11 Oct 2024 09:34:20 +0200 Subject: [PATCH 02/11] Use eth address in runtime transactions --- src/app/pages/RuntimeAccountDetailsPage/hook.ts | 8 +------- src/oasis-nexus/api.ts | 6 ++++++ 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/src/app/pages/RuntimeAccountDetailsPage/hook.ts b/src/app/pages/RuntimeAccountDetailsPage/hook.ts index 0dd6694d8..871de43ef 100644 --- a/src/app/pages/RuntimeAccountDetailsPage/hook.ts +++ b/src/app/pages/RuntimeAccountDetailsPage/hook.ts @@ -34,19 +34,13 @@ export const useAccountTransactions = (scope: SearchScope, address: string) => { // We should use useGetConsensusTransactions() } - const oasisAddress = getOasisAddressOrNull(address) const query = useGetRuntimeTransactions( network, layer, // This is OK since consensus has been handled separately { limit, offset: offset, - rel: oasisAddress!, - }, - { - query: { - enabled: !!oasisAddress, - }, + rel: address, }, ) const { isFetched, isLoading, data } = query diff --git a/src/oasis-nexus/api.ts b/src/oasis-nexus/api.ts index db592ec95..9fd1f4305 100644 --- a/src/oasis-nexus/api.ts +++ b/src/oasis-nexus/api.ts @@ -248,8 +248,14 @@ export const useGetRuntimeTransactions: typeof generated.useGetRuntimeTransactio return { ...data, transactions: data.transactions.map(tx => { + const oasisAddress = + params?.rel && isValidEthAddress(params?.rel) + ? getOasisAddressOrNull(params.rel) + : params?.rel + return { ...tx, + to_eth: !tx.to_eth && tx.to === oasisAddress ? params?.rel : tx.to_eth, eth_hash: tx.eth_hash ? `0x${tx.eth_hash}` : undefined, // TODO: Decimals may not be correct, should not depend on ParaTime decimals, but fee_symbol fee: fromBaseUnits(tx.fee, paraTimesConfig[runtime].decimals), From 21f42c834a260fdeef5efa07024c8f6b71764899 Mon Sep 17 00:00:00 2001 From: Michal Zielenkiewicz Date: Fri, 11 Oct 2024 09:42:13 +0200 Subject: [PATCH 03/11] Use eth address in accont events tab --- .../pages/RuntimeAccountDetailsPage/hook.ts | 23 +++++-------------- src/oasis-nexus/api.ts | 9 ++++++++ 2 files changed, 15 insertions(+), 17 deletions(-) diff --git a/src/app/pages/RuntimeAccountDetailsPage/hook.ts b/src/app/pages/RuntimeAccountDetailsPage/hook.ts index 871de43ef..029421625 100644 --- a/src/app/pages/RuntimeAccountDetailsPage/hook.ts +++ b/src/app/pages/RuntimeAccountDetailsPage/hook.ts @@ -8,7 +8,6 @@ import { AppErrors } from '../../../types/errors' import { useSearchParamsPagination } from '../../components/Table/useSearchParamsPagination' import { NUMBER_OF_ITEMS_ON_SEPARATE_PAGE as limit } from '../../config' import { SearchScope } from '../../../types/searchScope' -import { getOasisAddressOrNull } from '../../utils/helpers' export const useAccount = (scope: SearchScope, address: string) => { const { network, layer } = scope @@ -73,22 +72,12 @@ export const useAccountEvents = (scope: SearchScope, address: string) => { // We should use useGetConsensusEvents() } - const oasisAddress = getOasisAddressOrNull(address) - const query = useGetRuntimeEvents( - network, - layer, - { - limit, - offset: offset, - rel: oasisAddress!, - // TODO: implement filtering for non-transactional events - }, - { - query: { - enabled: !!oasisAddress, - }, - }, - ) + const query = useGetRuntimeEvents(network, layer, { + limit, + offset: offset, + rel: address, + // TODO: implement filtering for non-transactional events + }) const { isFetched, isLoading, isError, data } = query const events = data?.data.events diff --git a/src/oasis-nexus/api.ts b/src/oasis-nexus/api.ts index 9fd1f4305..534692e04 100644 --- a/src/oasis-nexus/api.ts +++ b/src/oasis-nexus/api.ts @@ -861,6 +861,10 @@ export const useGetRuntimeEvents: typeof generated.useGetRuntimeEvents = ( return { ...data, events: data.events.map(event => { + const oasisAddress = + params?.rel && isValidEthAddress(params?.rel) + ? getOasisAddressOrNull(params.rel) + : params?.rel const adjustedHash = event.eth_tx_hash ? `0x${event.eth_tx_hash}` : undefined if ( event.type === RuntimeEventType.accountstransfer || @@ -885,6 +889,11 @@ export const useGetRuntimeEvents: typeof generated.useGetRuntimeEvents = ( event.body.amount.Denomination || getTokensForScope({ network, layer: runtime })[0].ticker, }, + owner: + event.body?.owner && event.body.owner === oasisAddress ? params?.rel : event.body.owner, + from: + event.body?.from && event.body.from === oasisAddress ? params?.rel : event.body.from, + to: event.body?.to && event.body.to === oasisAddress ? params?.rel : event.body.to, }, layer: runtime, network, From cbccf89a2f7e39a6a411ada7684b616166ca3c0d Mon Sep 17 00:00:00 2001 From: Michal Zielenkiewicz Date: Thu, 17 Oct 2024 08:16:43 +0200 Subject: [PATCH 04/11] Use eth address to fetch tokens --- src/app/pages/TokenDashboardPage/hook.ts | 79 +++++------------------- src/oasis-nexus/api.ts | 6 +- 2 files changed, 19 insertions(+), 66 deletions(-) diff --git a/src/app/pages/TokenDashboardPage/hook.ts b/src/app/pages/TokenDashboardPage/hook.ts index 84af94f76..84317e86c 100644 --- a/src/app/pages/TokenDashboardPage/hook.ts +++ b/src/app/pages/TokenDashboardPage/hook.ts @@ -16,7 +16,6 @@ import { SearchScope } from '../../../types/searchScope' import { useSearchParamsPagination } from '../../components/Table/useSearchParamsPagination' import { NUMBER_OF_ITEMS_ON_SEPARATE_PAGE } from '../../config' import { useComprehensiveSearchParamsPagination } from '../../components/Table/useComprehensiveSearchParamsPagination' -import { getOasisAddressOrNull } from '../../utils/helpers' export const useTokenInfo = (scope: SearchScope, address: string, enabled = true) => { const { network, layer } = scope @@ -24,7 +23,7 @@ export const useTokenInfo = (scope: SearchScope, address: string, enabled = true // There can be no ERC-20 or ERC-721 tokens on consensus throw AppErrors.UnsupportedLayer } - const query = useGetRuntimeEvmTokensAddress(network, layer, address!, { + const query = useGetRuntimeEvmTokensAddress(network, layer, address, { query: { enabled }, }) const token = query.data?.data @@ -38,19 +37,14 @@ export const useTokenInfo = (scope: SearchScope, address: string, enabled = true } export const useTokenTransfers = (scope: SearchScope, params: { address: string }) => { - const oasisAddress = getOasisAddressOrNull(params.address) - return _useTokenTransfers(scope, oasisAddress ? { rel: oasisAddress } : undefined) + return _useTokenTransfers(scope, { rel: params.address }) } export const useNFTInstanceTransfers = ( scope: SearchScope, params: { nft_id: string; contract_address: string }, ) => { - const oasisAddress = getOasisAddressOrNull(params.contract_address) - return _useTokenTransfers( - scope, - oasisAddress ? { nft_id: params.nft_id, contract_address: oasisAddress } : undefined, - ) + return _useTokenTransfers(scope, { nft_id: params.nft_id, contract_address: params.contract_address }) } export const _useTokenTransfers = (scope: SearchScope, params: undefined | GetRuntimeEventsParams) => { @@ -109,21 +103,10 @@ export const useTokenHolders = (scope: SearchScope, address: string) => { // There are no token holders on the consensus layer. } - const oasisAddress = getOasisAddressOrNull(address) - const query = useGetRuntimeEvmTokensAddressHolders( - network, - layer, - oasisAddress!, - { - limit: NUMBER_OF_ITEMS_ON_SEPARATE_PAGE, - offset: offset, - }, - { - query: { - enabled: !!oasisAddress, - }, - }, - ) + const query = useGetRuntimeEvmTokensAddressHolders(network, layer, address, { + limit: NUMBER_OF_ITEMS_ON_SEPARATE_PAGE, + offset: offset, + }) const { isFetched, isLoading, data } = query @@ -157,21 +140,10 @@ export const useTokenInventory = (scope: SearchScope, address: string) => { throw AppErrors.UnsupportedLayer // There are no tokens on the consensus layer. } - const oasisAddress = getOasisAddressOrNull(address) - const query = useGetRuntimeEvmTokensAddressNfts( - network, - layer, - oasisAddress!, - { - limit: NUMBER_OF_INVENTORY_ITEMS, - offset: offset, - }, - { - query: { - enabled: !!oasisAddress, - }, - }, - ) + const query = useGetRuntimeEvmTokensAddressNfts(network, layer, address, { + limit: NUMBER_OF_INVENTORY_ITEMS, + offset: offset, + }) const { isFetched, isLoading, data } = query const inventory = data?.data.evm_nfts @@ -204,23 +176,11 @@ export const useAccountTokenInventory = (scope: SearchScope, address: string, to // There are no tokens on the consensus layer. } - const oasisAddress = getOasisAddressOrNull(address) - const oasisTokenAddress = getOasisAddressOrNull(tokenAddress) - const query = useGetRuntimeAccountsAddressNfts( - network, - layer, - oasisAddress!, - { - limit: NUMBER_OF_INVENTORY_ITEMS, - offset: offset, - token_address: oasisTokenAddress!, - }, - { - query: { - enabled: !!oasisAddress && !!oasisTokenAddress, - }, - }, - ) + const query = useGetRuntimeAccountsAddressNfts(network, layer, address, { + limit: NUMBER_OF_INVENTORY_ITEMS, + offset: offset, + token_address: tokenAddress, + }) const { isFetched, isLoading, data } = query const inventory = data?.data.evm_nfts @@ -250,12 +210,7 @@ export const useNFTInstance = (scope: SearchScope, address: string, id: string) throw AppErrors.UnsupportedLayer // There are no tokens on the consensus layer. } - const oasisAddress = getOasisAddressOrNull(address) - const query = useGetRuntimeEvmTokensAddressNftsId(network, layer, oasisAddress!, id, { - query: { - enabled: !!oasisAddress, - }, - }) + const query = useGetRuntimeEvmTokensAddressNftsId(network, layer, address, id) const { data, isError, isFetched, isLoading } = query if (isError) { diff --git a/src/oasis-nexus/api.ts b/src/oasis-nexus/api.ts index 534692e04..fa33b5846 100644 --- a/src/oasis-nexus/api.ts +++ b/src/oasis-nexus/api.ts @@ -807,13 +807,11 @@ export const useGetRuntimeEvmTokensAddress: typeof generated.useGetRuntimeEvmTok address, options, ) => { - const oasisAddress = getOasisAddressOrNull(address) - - return generated.useGetRuntimeEvmTokensAddress(network, runtime, oasisAddress!, { + return generated.useGetRuntimeEvmTokensAddress(network, runtime, address, { ...options, query: { ...(options?.query ?? {}), - enabled: !!oasisAddress && (options?.query?.enabled ?? true), + enabled: options?.query?.enabled ?? true, }, request: { ...options?.request, From d572c4f88a78072f05d64de7994c19301b79b898 Mon Sep 17 00:00:00 2001 From: Michal Zielenkiewicz Date: Fri, 11 Oct 2024 10:27:35 +0200 Subject: [PATCH 05/11] Update tests --- playwright/tests/accounts.spec.ts | 6 +++--- playwright/tests/getPreciseNumberFormat.spec.ts | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/playwright/tests/accounts.spec.ts b/playwright/tests/accounts.spec.ts index d92ccf873..caec824b2 100644 --- a/playwright/tests/accounts.spec.ts +++ b/playwright/tests/accounts.spec.ts @@ -15,7 +15,7 @@ async function setup(page: Page) { }, ) await page.route( - '**/v1/sapphire/transactions?limit=10&offset=0&rel=oasis1qq2v39p9fqk997vk6742axrzqyu9v2ncyuqt8uek', + '**/v1/sapphire/transactions?limit=10&offset=0&rel=0x0000000000000000000000000000000000000000', route => { route.fulfill({ body: JSON.stringify({ @@ -26,7 +26,7 @@ async function setup(page: Page) { }) }, ) - await page.route('**/v1/sapphire/accounts/oasis1qq2v39p9fqk997vk6742axrzqyu9v2ncyuqt8uek', route => { + await page.route('**/v1/sapphire/accounts/0x0000000000000000000000000000000000000000', route => { route.fulfill({ body: JSON.stringify({ address: 'oasis1qq2v39p9fqk997vk6742axrzqyu9v2ncyuqt8uek', @@ -47,7 +47,7 @@ async function setup(page: Page) { }) }) await page.route( - '**/v1/sapphire/events?limit=10&offset=0&rel=oasis1qq2v39p9fqk997vk6742axrzqyu9v2ncyuqt8uek', + '**/v1/sapphire/events?limit=10&offset=0&rel=0x0000000000000000000000000000000000000000', route => { route.fulfill({ body: JSON.stringify({ diff --git a/playwright/tests/getPreciseNumberFormat.spec.ts b/playwright/tests/getPreciseNumberFormat.spec.ts index d051ee601..c358a6f98 100644 --- a/playwright/tests/getPreciseNumberFormat.spec.ts +++ b/playwright/tests/getPreciseNumberFormat.spec.ts @@ -15,7 +15,7 @@ async function setup(page: Page, balance: string, decimals: number) { }, ) - await page.route('**/v1/sapphire/accounts/oasis1qq2v39p9fqk997vk6742axrzqyu9v2ncyuqt8uek', route => { + await page.route('**/v1/sapphire/accounts/0x0000000000000000000000000000000000000000', route => { route.fulfill({ body: JSON.stringify({ address: 'oasis1qq2v39p9fqk997vk6742axrzqyu9v2ncyuqt8uek', From 2cc950ab26cf94aabcdea2fce03a7ee5ed69faab Mon Sep 17 00:00:00 2001 From: Michal Zielenkiewicz Date: Wed, 16 Oct 2024 12:05:14 +0200 Subject: [PATCH 06/11] Add getEthAddressMap helper function --- src/oasis-nexus/api.ts | 17 ++++++++++++----- 1 file changed, 12 insertions(+), 5 deletions(-) diff --git a/src/oasis-nexus/api.ts b/src/oasis-nexus/api.ts index fa33b5846..15501e0a5 100644 --- a/src/oasis-nexus/api.ts +++ b/src/oasis-nexus/api.ts @@ -231,6 +231,16 @@ function normalizeSymbol(rawSymbol: string | '' | undefined, scope: SearchScope) return whitelistedTickers.includes(symbol as Ticker) ? symbol : 'n/a' } +function getEthAddressMap(rel: string | undefined): { [oasis1Address: string]: `0x${string}` } { + if (rel && isValidEthAddress(rel)) { + const oasisAddr = getOasisAddressOrNull(rel) + if (oasisAddr) { + return { [oasisAddr]: toChecksumAddress(rel) } + } + } + return {} +} + export const useGetRuntimeTransactions: typeof generated.useGetRuntimeTransactions = ( network, runtime, @@ -248,14 +258,11 @@ export const useGetRuntimeTransactions: typeof generated.useGetRuntimeTransactio return { ...data, transactions: data.transactions.map(tx => { - const oasisAddress = - params?.rel && isValidEthAddress(params?.rel) - ? getOasisAddressOrNull(params.rel) - : params?.rel + const ethAddressMap = getEthAddressMap(params?.rel) return { ...tx, - to_eth: !tx.to_eth && tx.to === oasisAddress ? params?.rel : tx.to_eth, + to_eth: tx.to_eth || (tx.to ? ethAddressMap[tx.to] : undefined), eth_hash: tx.eth_hash ? `0x${tx.eth_hash}` : undefined, // TODO: Decimals may not be correct, should not depend on ParaTime decimals, but fee_symbol fee: fromBaseUnits(tx.fee, paraTimesConfig[runtime].decimals), From 6e752896ec176fd90d171d21e84706873e6106e5 Mon Sep 17 00:00:00 2001 From: lukaw3d Date: Thu, 17 Oct 2024 06:47:02 +0200 Subject: [PATCH 07/11] Refactor runtime event normalization into two iterations --- src/oasis-nexus/api.ts | 81 +++++++++++++++++++++--------------------- 1 file changed, 41 insertions(+), 40 deletions(-) diff --git a/src/oasis-nexus/api.ts b/src/oasis-nexus/api.ts index 15501e0a5..9402061d0 100644 --- a/src/oasis-nexus/api.ts +++ b/src/oasis-nexus/api.ts @@ -865,53 +865,54 @@ export const useGetRuntimeEvents: typeof generated.useGetRuntimeEvents = ( if (status !== 200) return data return { ...data, - events: data.events.map(event => { - const oasisAddress = - params?.rel && isValidEthAddress(params?.rel) - ? getOasisAddressOrNull(params.rel) - : params?.rel - const adjustedHash = event.eth_tx_hash ? `0x${event.eth_tx_hash}` : undefined - if ( - event.type === RuntimeEventType.accountstransfer || - event.type === RuntimeEventType.accountsmint || - event.type === RuntimeEventType.accountsburn || - event.type === RuntimeEventType.consensus_accountsdeposit || - event.type === RuntimeEventType.consensus_accountswithdraw || - event.type === RuntimeEventType.consensus_accountsdelegate || - event.type === RuntimeEventType.consensus_accountsundelegate_done - ) { + events: data.events + .map((event): generated.RuntimeEvent => { + const adjustedHash = event.eth_tx_hash ? `0x${event.eth_tx_hash}` : undefined return { ...event, evm_log_params: event.evm_log_params?.map(fixChecksumAddressInEvmEventParam), eth_tx_hash: adjustedHash, - body: { - ...event.body, - amount: { - // If denomination="" or missing then use runtime's native. Otherwise unknown (would have to get by token name?). - ...event.body.amount, - Amount: fromBaseUnits(event.body.amount.Amount, paraTimesConfig[runtime].decimals), - Denomination: - event.body.amount.Denomination || - getTokensForScope({ network, layer: runtime })[0].ticker, - }, - owner: - event.body?.owner && event.body.owner === oasisAddress ? params?.rel : event.body.owner, - from: - event.body?.from && event.body.from === oasisAddress ? params?.rel : event.body.from, - to: event.body?.to && event.body.to === oasisAddress ? params?.rel : event.body.to, - }, layer: runtime, network, } - } - return { - ...event, - evm_log_params: event.evm_log_params?.map(fixChecksumAddressInEvmEventParam), - eth_tx_hash: adjustedHash, - layer: runtime, - network, - } - }), + }) + .map((event): generated.RuntimeEvent => { + const oasisAddress = + params?.rel && isValidEthAddress(params?.rel) + ? getOasisAddressOrNull(params.rel) + : params?.rel + if ( + event.type === RuntimeEventType.accountstransfer || + event.type === RuntimeEventType.accountsmint || + event.type === RuntimeEventType.accountsburn || + event.type === RuntimeEventType.consensus_accountsdeposit || + event.type === RuntimeEventType.consensus_accountswithdraw || + event.type === RuntimeEventType.consensus_accountsdelegate || + event.type === RuntimeEventType.consensus_accountsundelegate_done + // consensus_accountsundelegate_start doesn't contain amount + ) { + return { + ...event, + body: { + ...event.body, + amount: { + // If denomination="" or missing then use runtime's native. Otherwise unknown (would have to get by token name?). + ...event.body.amount, + Amount: fromBaseUnits(event.body.amount.Amount, paraTimesConfig[runtime].decimals), + Denomination: event.body.amount.Denomination || getTokensForScope(event)[0].ticker, + }, + owner: + event.body?.owner && event.body.owner === oasisAddress + ? params?.rel + : event.body.owner, + from: + event.body?.from && event.body.from === oasisAddress ? params?.rel : event.body.from, + to: event.body?.to && event.body.to === oasisAddress ? params?.rel : event.body.to, + }, + } + } + return event + }), } }, ...arrayify(options?.request?.transformResponse), From f6322e86709341eea140ca08e3672802bf795811 Mon Sep 17 00:00:00 2001 From: lukaw3d Date: Thu, 17 Oct 2024 06:47:02 +0200 Subject: [PATCH 08/11] Run the fixing of eth address on undelegate_start events too --- src/oasis-nexus/api.ts | 24 +++++++++++++----------- 1 file changed, 13 insertions(+), 11 deletions(-) diff --git a/src/oasis-nexus/api.ts b/src/oasis-nexus/api.ts index 9402061d0..9cd2687cc 100644 --- a/src/oasis-nexus/api.ts +++ b/src/oasis-nexus/api.ts @@ -868,8 +868,21 @@ export const useGetRuntimeEvents: typeof generated.useGetRuntimeEvents = ( events: data.events .map((event): generated.RuntimeEvent => { const adjustedHash = event.eth_tx_hash ? `0x${event.eth_tx_hash}` : undefined + const oasisAddress = + params?.rel && isValidEthAddress(params?.rel) + ? getOasisAddressOrNull(params.rel) + : params?.rel + return { ...event, + body: { + ...event.body, + owner: + event.body?.owner && event.body.owner === oasisAddress ? params?.rel : event.body.owner, + from: + event.body?.from && event.body.from === oasisAddress ? params?.rel : event.body.from, + to: event.body?.to && event.body.to === oasisAddress ? params?.rel : event.body.to, + }, evm_log_params: event.evm_log_params?.map(fixChecksumAddressInEvmEventParam), eth_tx_hash: adjustedHash, layer: runtime, @@ -877,10 +890,6 @@ export const useGetRuntimeEvents: typeof generated.useGetRuntimeEvents = ( } }) .map((event): generated.RuntimeEvent => { - const oasisAddress = - params?.rel && isValidEthAddress(params?.rel) - ? getOasisAddressOrNull(params.rel) - : params?.rel if ( event.type === RuntimeEventType.accountstransfer || event.type === RuntimeEventType.accountsmint || @@ -901,13 +910,6 @@ export const useGetRuntimeEvents: typeof generated.useGetRuntimeEvents = ( Amount: fromBaseUnits(event.body.amount.Amount, paraTimesConfig[runtime].decimals), Denomination: event.body.amount.Denomination || getTokensForScope(event)[0].ticker, }, - owner: - event.body?.owner && event.body.owner === oasisAddress - ? params?.rel - : event.body.owner, - from: - event.body?.from && event.body.from === oasisAddress ? params?.rel : event.body.from, - to: event.body?.to && event.body.to === oasisAddress ? params?.rel : event.body.to, }, } } From 1b67fc15e72a1be679b44ecb7fb82780bee4dcc8 Mon Sep 17 00:00:00 2001 From: lukaw3d Date: Thu, 17 Oct 2024 06:47:03 +0200 Subject: [PATCH 09/11] Fix which field fallback eth address is saved to in event normalization --- src/oasis-nexus/api.ts | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/src/oasis-nexus/api.ts b/src/oasis-nexus/api.ts index 9cd2687cc..4c1620fa9 100644 --- a/src/oasis-nexus/api.ts +++ b/src/oasis-nexus/api.ts @@ -877,11 +877,15 @@ export const useGetRuntimeEvents: typeof generated.useGetRuntimeEvents = ( ...event, body: { ...event.body, - owner: - event.body?.owner && event.body.owner === oasisAddress ? params?.rel : event.body.owner, - from: - event.body?.from && event.body.from === oasisAddress ? params?.rel : event.body.from, - to: event.body?.to && event.body.to === oasisAddress ? params?.rel : event.body.to, + owner_eth: + event.body?.owner_eth || + (event.body?.owner && event.body.owner === oasisAddress ? params?.rel : undefined), + from_eth: + event.body?.from_eth || + (event.body?.from && event.body.from === oasisAddress ? params?.rel : undefined), + to_eth: + event.body?.to_eth || + (event.body?.to && event.body.to === oasisAddress ? params?.rel : undefined), }, evm_log_params: event.evm_log_params?.map(fixChecksumAddressInEvmEventParam), eth_tx_hash: adjustedHash, From 9ad19b1f7d8b4e341dcc4d85d950f48a04638437 Mon Sep 17 00:00:00 2001 From: lukaw3d Date: Thu, 17 Oct 2024 06:47:03 +0200 Subject: [PATCH 10/11] Replace getEthAddressMap with fallbackEthAddress helper function --- src/oasis-nexus/api.ts | 43 +++++++++++++++++------------------------- 1 file changed, 17 insertions(+), 26 deletions(-) diff --git a/src/oasis-nexus/api.ts b/src/oasis-nexus/api.ts index 4c1620fa9..a67d1eba7 100644 --- a/src/oasis-nexus/api.ts +++ b/src/oasis-nexus/api.ts @@ -231,14 +231,19 @@ function normalizeSymbol(rawSymbol: string | '' | undefined, scope: SearchScope) return whitelistedTickers.includes(symbol as Ticker) ? symbol : 'n/a' } -function getEthAddressMap(rel: string | undefined): { [oasis1Address: string]: `0x${string}` } { - if (rel && isValidEthAddress(rel)) { - const oasisAddr = getOasisAddressOrNull(rel) - if (oasisAddr) { - return { [oasisAddr]: toChecksumAddress(rel) } - } +/** Returns checksummed maybeMatchingEthAddr if it matches oasisAddress when converted */ +function fallbackEthAddress( + oasisAddress: generated.Address | undefined, + maybeMatchingEthAddr: generated.EthOrOasisAddress | undefined, +): `0x${string}` | undefined { + if ( + oasisAddress && + maybeMatchingEthAddr && + isValidEthAddress(maybeMatchingEthAddr) && + getOasisAddressOrNull(maybeMatchingEthAddr) === oasisAddress + ) { + return toChecksumAddress(maybeMatchingEthAddr) } - return {} } export const useGetRuntimeTransactions: typeof generated.useGetRuntimeTransactions = ( @@ -258,11 +263,9 @@ export const useGetRuntimeTransactions: typeof generated.useGetRuntimeTransactio return { ...data, transactions: data.transactions.map(tx => { - const ethAddressMap = getEthAddressMap(params?.rel) - return { ...tx, - to_eth: tx.to_eth || (tx.to ? ethAddressMap[tx.to] : undefined), + to_eth: tx.to_eth || fallbackEthAddress(tx.to, params?.rel), eth_hash: tx.eth_hash ? `0x${tx.eth_hash}` : undefined, // TODO: Decimals may not be correct, should not depend on ParaTime decimals, but fee_symbol fee: fromBaseUnits(tx.fee, paraTimesConfig[runtime].decimals), @@ -867,28 +870,16 @@ export const useGetRuntimeEvents: typeof generated.useGetRuntimeEvents = ( ...data, events: data.events .map((event): generated.RuntimeEvent => { - const adjustedHash = event.eth_tx_hash ? `0x${event.eth_tx_hash}` : undefined - const oasisAddress = - params?.rel && isValidEthAddress(params?.rel) - ? getOasisAddressOrNull(params.rel) - : params?.rel - return { ...event, body: { ...event.body, - owner_eth: - event.body?.owner_eth || - (event.body?.owner && event.body.owner === oasisAddress ? params?.rel : undefined), - from_eth: - event.body?.from_eth || - (event.body?.from && event.body.from === oasisAddress ? params?.rel : undefined), - to_eth: - event.body?.to_eth || - (event.body?.to && event.body.to === oasisAddress ? params?.rel : undefined), + owner_eth: event.body?.owner_eth || fallbackEthAddress(event.body.owner, params?.rel), + from_eth: event.body?.from_eth || fallbackEthAddress(event.body.from, params?.rel), + to_eth: event.body?.to_eth || fallbackEthAddress(event.body.to, params?.rel), }, evm_log_params: event.evm_log_params?.map(fixChecksumAddressInEvmEventParam), - eth_tx_hash: adjustedHash, + eth_tx_hash: event.eth_tx_hash ? `0x${event.eth_tx_hash}` : undefined, layer: runtime, network, } From 5a3a25d17026b5458f7ef6653c015336d7a20585 Mon Sep 17 00:00:00 2001 From: lukaw3d Date: Thu, 17 Oct 2024 06:47:04 +0200 Subject: [PATCH 11/11] Replace getEthAddressForAccount with fallbackEthAddress helper function --- .../__tests__/getEthAccountAddress.test.ts | 33 ------------------- .../getEthAccountAddressFromPreimage.test.ts | 14 ++++++++ src/app/utils/helpers.ts | 12 +------ src/oasis-nexus/api.ts | 6 ++-- 4 files changed, 19 insertions(+), 46 deletions(-) delete mode 100644 src/app/utils/__tests__/getEthAccountAddress.test.ts create mode 100644 src/app/utils/__tests__/getEthAccountAddressFromPreimage.test.ts diff --git a/src/app/utils/__tests__/getEthAccountAddress.test.ts b/src/app/utils/__tests__/getEthAccountAddress.test.ts deleted file mode 100644 index 6bd36401a..000000000 --- a/src/app/utils/__tests__/getEthAccountAddress.test.ts +++ /dev/null @@ -1,33 +0,0 @@ -import { getEthAccountAddressFromPreimage, getEthAddressForAccount } from '../helpers' -import { suggestedEmptyAccount, suggestedParsedAccount } from '../test-fixtures' - -describe('getEthAccountAddress', () => { - // TODO: enable when jest fixes "TypeError: Expected Uint8Array" - // https://github.com/facebook/jest/issues/4422 - it.skip('should convert preimage to evm addresses', () => { - expect(getEthAccountAddressFromPreimage(suggestedParsedAccount.address_preimage)).toEqual( - suggestedParsedAccount.address_eth, - ) - }) - - it('should return input address on empty account', () => { - const validEthAddress = suggestedParsedAccount.address_eth - expect(getEthAddressForAccount(suggestedEmptyAccount, validEthAddress)).toBe( - '0xBA504818FdD8D3dBA2Ef8fD9B4F4D5c71aD1d1D3', - ) - }) - - it('should return undefined on empty account with invalid ETH address', () => { - expect(getEthAddressForAccount(suggestedEmptyAccount, '0x0')).toBeUndefined() - }) - - it('should return undefined on empty account with undefined supplied as ETH address', () => { - expect(getEthAddressForAccount(suggestedEmptyAccount, undefined)).toBeUndefined() - }) - - it('should valid address from preimage', () => { - expect(getEthAddressForAccount(suggestedParsedAccount, undefined)).toBe( - suggestedParsedAccount.address_eth, - ) - }) -}) diff --git a/src/app/utils/__tests__/getEthAccountAddressFromPreimage.test.ts b/src/app/utils/__tests__/getEthAccountAddressFromPreimage.test.ts new file mode 100644 index 000000000..d7da68f2e --- /dev/null +++ b/src/app/utils/__tests__/getEthAccountAddressFromPreimage.test.ts @@ -0,0 +1,14 @@ +import { getEthAccountAddressFromPreimage } from '../helpers' +import { suggestedEmptyAccount, suggestedParsedAccount } from '../test-fixtures' + +describe('getEthAccountAddressFromPreimage', () => { + it('should convert preimage to evm addresses', () => { + expect(getEthAccountAddressFromPreimage(suggestedParsedAccount.address_preimage)).toEqual( + suggestedParsedAccount.address_eth, + ) + }) + + it('should return undefined on empty account', () => { + expect(getEthAccountAddressFromPreimage(suggestedEmptyAccount.address_preimage)).toBeUndefined() + }) +}) diff --git a/src/app/utils/helpers.ts b/src/app/utils/helpers.ts index 663df7683..c68ef31b9 100644 --- a/src/app/utils/helpers.ts +++ b/src/app/utils/helpers.ts @@ -3,7 +3,7 @@ import { Buffer } from 'buffer' import * as oasis from '@oasisprotocol/client' import * as oasisRT from '@oasisprotocol/client-rt' // eslint-disable-next-line no-restricted-imports -import { AddressPreimage, RuntimeAccount } from '../../oasis-nexus/generated/api' +import { AddressPreimage } from '../../oasis-nexus/generated/api' import { validateMnemonic } from 'bip39' import { sha512_256 } from 'js-sha512' @@ -94,16 +94,6 @@ export function getEthAccountAddressFromPreimage(preimage: AddressPreimage | und return getEthAccountAddressFromBase64(preimage.address_data) } -export function getEthAddressForAccount( - account: RuntimeAccount, - possibleEthAddress?: string, -): string | undefined { - // In case of an empty account - if (possibleEthAddress && isValidEthAddress(possibleEthAddress)) return possibleEthAddress - - return getEthAccountAddressFromPreimage(account.address_preimage) -} - export function uniq(input: T[] | undefined): T[] { return input === undefined ? [] : [...new Set(input)] } diff --git a/src/oasis-nexus/api.ts b/src/oasis-nexus/api.ts index a67d1eba7..c6d0746aa 100644 --- a/src/oasis-nexus/api.ts +++ b/src/oasis-nexus/api.ts @@ -18,7 +18,7 @@ import { } from './generated/api' import { getAccountSize, - getEthAddressForAccount, + getEthAccountAddressFromPreimage, getOasisAddressOrNull, isValidEthAddress, } from '../app/utils/helpers' @@ -448,7 +448,9 @@ export const useGetRuntimeAccountsAddress: typeof generated.useGetRuntimeAccount if (status !== 200) return data return groupAccountTokenBalances({ ...data, - address_eth: getEthAddressForAccount(data, address), + address_eth: + getEthAccountAddressFromPreimage(data.address_preimage) || + fallbackEthAddress(data.address, address), evm_contract: data.evm_contract && { ...data.evm_contract, eth_creation_tx: data.evm_contract.eth_creation_tx