From 9fc9ca715dd931975d56e9f5b8cbf984f929bc1a Mon Sep 17 00:00:00 2001 From: Pranav Date: Mon, 17 Nov 2025 15:48:35 +0530 Subject: [PATCH 1/3] View vote extension and includebundle transaction --- src/components/dynamic/TxsElement.vue | 7 +- src/libs/client.ts | 13 ++- src/modules/[chain]/tx/[hash].vue | 112 +++++++++++++++++++++++++- 3 files changed, 127 insertions(+), 5 deletions(-) diff --git a/src/components/dynamic/TxsElement.vue b/src/components/dynamic/TxsElement.vue index fa38232e..ede1f1aa 100644 --- a/src/components/dynamic/TxsElement.vue +++ b/src/components/dynamic/TxsElement.vue @@ -46,7 +46,12 @@ const chain = useBlockchain(); {{ item.injected }} - {{ item.hash }} + + {{ item.hash }} + {{ item.hash }} diff --git a/src/libs/client.ts b/src/libs/client.ts index e9ae6249..b4eb5391 100644 --- a/src/libs/client.ts +++ b/src/libs/client.ts @@ -17,9 +17,11 @@ import semver from 'semver'; export class BaseRestClient { version: string; endpoint: string; + rpcEndpoint: string; registry: R; - constructor(endpoint: string, registry: R, version?: string) { + constructor(endpoint: string, registry: R, version?: string, rpcEndpoint?: string) { this.endpoint = endpoint; + this.rpcEndpoint = rpcEndpoint || ''; this.registry = registry; this.version = version || 'v0.40'; } @@ -70,7 +72,8 @@ export class CosmosRestClient extends BaseRestClient { profile = findApiProfileBySDKVersion(ver); } } - return new CosmosRestClient(endpoint, profile || DEFAULT, ver); + const rpcEndpoint = chain?.endpoints?.rpc?.[0]?.address; + return new CosmosRestClient(endpoint, profile || DEFAULT, ver, rpcEndpoint); } // Auth Module @@ -312,6 +315,12 @@ export class CosmosRestClient extends BaseRestClient { async getTx(hash: string) { return this.request(this.registry.tx_hash, { hash }); } + async getTxFromRPC(hash: string) { + return this.request( + { url: `${this.rpcEndpoint}/tx?hash=0x{hash}`, adapter: async (source: any) => source }, + { hash } + ); + } // mint async getMintParam() { diff --git a/src/modules/[chain]/tx/[hash].vue b/src/modules/[chain]/tx/[hash].vue index 89d89e94..07553e56 100644 --- a/src/modules/[chain]/tx/[hash].vue +++ b/src/modules/[chain]/tx/[hash].vue @@ -3,12 +3,14 @@ import { useBaseStore, useBlockchain, useFormatter } from '@/stores'; import DynamicComponent from '@/components/dynamic/DynamicComponent.vue'; import { computed, ref } from '@vue/reactivity'; import type { Tx, TxResponse } from '@/types'; +import { useRoute } from 'vue-router'; import { JsonViewer } from 'vue3-json-viewer'; // if you used v1.0.5 or latster ,you should add import "vue3-json-viewer/dist/index.css" import 'vue3-json-viewer/dist/index.css'; const props = defineProps(['hash', 'chain']); +const route = useRoute(); const blockchain = useBlockchain(); const baseStore = useBaseStore(); @@ -19,9 +21,35 @@ const tx = ref( tx_response: TxResponse; } ); +const voteExtension = ref(null as any); +const bundleTx = ref(null as any); +const isVoteExtension = computed(() => route.query.type === 'injected'); + if (props.hash) { - blockchain.rpc.getTx(props.hash).then((x) => (tx.value = x)); + if (isVoteExtension.value) { + blockchain.rpc.getTxFromRPC(props.hash).then((data) => { + if (data.result) { + const txBytes = new Uint8Array(atob(data.result.tx).split('').map((c: string) => c.charCodeAt(0))); + const decoded = new TextDecoder().decode(txBytes); + + // Check if it's a bundle tx + if (decoded.startsWith('INCLUDE_BUNDLE_TX:')) { + const bundleJson = decoded.replace('INCLUDE_BUNDLE_TX:', ''); + bundleTx.value = { + ...data.result, + decoded: JSON.parse(bundleJson) + }; + } else { + // It's a vote extension + voteExtension.value = data.result; + } + } + }); + } else { + blockchain.rpc.getTx(props.hash).then((x) => (tx.value = x)); + } } + const messages = computed(() => { return ( tx.value.tx?.body?.messages.map((x) => { @@ -33,6 +61,16 @@ const messages = computed(() => { }) || [] ); }); + +const voteExtDecoded = computed(() => { + if (!voteExtension.value?.tx) return null; + try { + const txBytes = new Uint8Array(atob(voteExtension.value.tx).split('').map((c: string) => c.charCodeAt(0))); + return JSON.parse(new TextDecoder().decode(txBytes)); + } catch (e) { + return null; + } +});