diff --git a/src/libs/client.ts b/src/libs/client.ts index 2e39c185..7625546d 100644 --- a/src/libs/client.ts +++ b/src/libs/client.ts @@ -39,6 +39,7 @@ import { type AbciQueryParams, type CometClient, type QueryTag, + type TxData, type TxResponse, } from '@cosmjs/tendermint-rpc'; import { buildQuery } from '@cosmjs/tendermint-rpc/build/tendermint37/requests'; @@ -104,13 +105,17 @@ import { type RequestRegistry, } from './registry'; import { decodeProto } from '@/components/dynamic'; +import { convertStr } from './utils'; export const DEFAULT_SDK_VERSION = '0.45.16'; export const LCD_FALLBACK_CHAINS = ['OraiBtcMainnet']; -export type ExtraTxResponse = TxResponse & { +export type ExtraTxResponse = Omit & { txRaw: DecodedTxRaw; timestamp?: string; + result: Omit & { + readonly events: readonly Event[]; + }; }; export interface ExtraTxSearchResponse { readonly txs: ExtraTxResponse[]; @@ -965,6 +970,13 @@ export class CosmosRestClient extends BaseRestClient { if (decodeRaw) { res.txs.forEach((tx, i) => { const txRaw = decodeTxRaw(tx.tx); + tx.result.events.forEach((event) => { + event.attributes.forEach((attr) => { + const key = convertStr(attr.key); + const value = convertStr(attr.value); + Object.assign(attr, { key, value }); + }); + }); // txRaw.body.messages = txRaw.body.messages.map(decodeProto); // @ts-ignore @@ -1062,7 +1074,6 @@ export class CosmosRestClient extends BaseRestClient { // hash, // }); const res = await this.queryClient.ibc.transfer.denomTrace(hash); - console.log(res); return res; } async getIBCConnections(page?: PageRequest) { diff --git a/src/libs/utils.ts b/src/libs/utils.ts index 179c96a9..d00d301c 100644 --- a/src/libs/utils.ts +++ b/src/libs/utils.ts @@ -1,5 +1,5 @@ import { fromBinary, type JsonObject } from '@cosmjs/cosmwasm-stargate'; -import { fromAscii, toBase64 } from '@cosmjs/encoding'; +import { fromAscii, fromBase64, toBase64 } from '@cosmjs/encoding'; import type { Timestamp } from 'cosmjs-types/google/protobuf/timestamp'; export const formatTitle = (title: string) => { @@ -26,6 +26,26 @@ export const percent = (num: number) => { return parseFloat((num * 100).toFixed(2)); }; +export const convertStr = (value: Uint8Array | string) => { + let valueArr; + if (typeof value === 'string') { + try { + valueArr = fromBase64(value); + } catch { + // pure string + return value; + } + } else { + valueArr = value; + } + + try { + return fromAscii(valueArr); + } catch { + return typeof value === 'string' ? value : toBase64(valueArr); + } +}; + export const decodeBuffer = (value: Uint8Array) => { try { const str = fromAscii(value); @@ -56,14 +76,6 @@ export const parseJSONRecursive = (value: JsonObject) => { return value; }; -export function stringToUint8Array(str: string) { - const arr = []; - for (let i = 0, j = str.length; i < j; ++i) { - arr.push(str.charCodeAt(i)); - } - return new Uint8Array(arr); -} - export function uint8ArrayToString(arr: Uint8Array) { let str = ''; for (let i = 0, j = arr.length; i < j; ++i) { diff --git a/src/modules/[chain]/account/[address].vue b/src/modules/[chain]/account/[address].vue index 4717859d..ccc5a2d5 100644 --- a/src/modules/[chain]/account/[address].vue +++ b/src/modules/[chain]/account/[address].vue @@ -19,7 +19,7 @@ import { type UnbondingResponses, PageRequest, } from '@/types'; -import type { Event } from '@cosmjs/tendermint-rpc'; + import type { Coin } from '@cosmjs/amino'; import Countdown from '@/components/Countdown.vue'; import { fromAscii, fromBase64, toBase64, toHex } from '@cosmjs/encoding'; @@ -31,6 +31,7 @@ import { BaseAccount } from 'cosmjs-types/cosmos/auth/v1beta1/auth'; import type { ExtraTxResponse } from '@/libs/client'; import type { QueryDelegationTotalRewardsResponse } from 'cosmjs-types/cosmos/distribution/v1beta1/query'; import { fromTimestamp } from 'cosmjs-types/helpers'; +import type { Event } from 'cosmjs-types/tendermint/abci/types'; const props = defineProps(['address', 'chain']); @@ -151,9 +152,7 @@ function mapAmount(events: readonly Event[]) { if (!events) return []; return events .find((x) => x.type === 'coin_received') - ?.attributes.filter( - (x) => (typeof x.key === 'string' ? x.key : fromAscii(x.key)) === 'amount' - ) + ?.attributes.filter((x) => x.key === 'amount') .map((x) => x.value); } diff --git a/src/modules/[chain]/staking/[validator].vue b/src/modules/[chain]/staking/[validator].vue index 32c5d1cc..ce80a8fe 100644 --- a/src/modules/[chain]/staking/[validator].vue +++ b/src/modules/[chain]/staking/[validator].vue @@ -10,7 +10,6 @@ import { decodeKey, } from '@/libs'; import type { ExtraTxResponse, ExtraTxSearchResponse } from '@/libs/client'; -import { stringToUint8Array } from '@/libs/utils'; import { useBlockchain, useFormatter, @@ -21,7 +20,7 @@ import { import { PageRequest } from '@/types'; import { fromAscii, fromBech32, toBase64, toHex } from '@cosmjs/encoding'; import type { Coin } from '@cosmjs/stargate'; -import type { Event } from '@cosmjs/tendermint-rpc'; +import type { Event, EventAttribute } from 'cosmjs-types/tendermint/abci/types'; import { Icon } from '@iconify/vue'; import type { QueryValidatorDelegationsResponse } from 'cosmjs-types/cosmos/staking/v1beta1/query'; import { @@ -286,45 +285,39 @@ function mapAmounts(tx: ExtraTxResponse) { }) .filter(Boolean); if (amounts.length) return amounts; - return mapEvents(tx.result.events); + // fallback from event + return filterEvents(tx.result.events).map((x) => + x.attributes.find((a) => a.key === 'amount')?.value.replace(/[^\d]+/g, '') + ); } -function mapEvents(events: readonly Event[]) { +function filterEvents(events: readonly Event[]) { const attributes = events .filter((x) => x.type === selectedEventType.value) - .filter( - (x) => - x.attributes.findIndex((attr) => { - const base64Validator = toBase64(stringToUint8Array(validator)); - return ( - toBase64(attr.value) === base64Validator || - toBase64(attr.value) === base64Validator - ); - }) > -1 - ) - .map((x) => { - // check if attributes need to decode - const output = {} as { [key: string]: string }; - - if (x.attributes.findIndex((a) => fromAscii(a.key) === `amount`) > -1) { - x.attributes.forEach((attr) => { - output[fromAscii(attr.key)] = fromAscii(attr.value); - }); - } else - x.attributes.forEach((attr) => { - output[fromAscii(attr.key)] = fromAscii(attr.value); - }); - return output; - }); + .filter((x) => + x.attributes.some((attr) => { + return attr.value === validator; + }) + ); return attributes; } -function mapDelegators(messages: any[]) { +function mapDelegators(tx: ExtraTxResponse) { + const { messages } = tx.txRaw.body; if (!messages) return []; - return Array.from( - new Set(messages.map((x) => x.delegatorAddress || x.grantee || x.contract)) - ); + + const delegators = Array.from( + new Set( + messages.map((x: any) => x.delegatorAddress || x.grantee || x.contract) + ) + ).filter(Boolean); + + if (delegators.length) return delegators; + // fallback from event + return filterEvents(tx.result.events) + .map((x) => x.attributes.find((a) => a.key === 'validator')?.value) + .filter(Boolean); }