@@ -11,193 +11,197 @@ import { getClientThirdwebClient } from "@/constants/thirdweb-client.client";
1111import { serverThirdwebClient } from "@/constants/thirdweb-client.server" ;
1212import type { Transaction } from "../../analytics/tx-table/types" ;
1313import {
14- getSingleTransaction ,
15- getTransactionActivityLogs ,
14+ getSingleTransaction ,
15+ getTransactionActivityLogs ,
1616} from "../../lib/analytics" ;
1717import { TransactionDetailsUI } from "./transaction-details-ui" ;
1818
1919type AbiItem =
20- | AbiFunction
21- | {
22- type : string ;
23- name ?: string ;
24- } ;
20+ | AbiFunction
21+ | {
22+ type : string ;
23+ name ?: string ;
24+ } ;
2525
2626export type DecodedTransactionData = {
27- contractName : string ;
28- functionName : string ;
29- functionArgs : Record < string , unknown > ;
27+ contractName : string ;
28+ functionName : string ;
29+ functionArgs : Record < string , unknown > ;
3030} | null ;
3131
3232export type DecodedTransactionResult = DecodedTransactionData [ ] ;
3333
3434async function decodeSingleTransactionParam (
35- txParam : any ,
36- chainId : number ,
35+ txParam : {
36+ to : string ;
37+ data : `0x${string } `;
38+ } ,
39+ chainId : number ,
3740) : Promise < DecodedTransactionData > {
38- try {
39- if ( ! txParam || ! txParam . to || ! txParam . data ) {
40- return null ;
41- }
42-
43- // Create contract instance
44- const contract = getContract ( {
45- client : serverThirdwebClient ,
46- address : txParam . to ,
47- chain : defineChain ( chainId ) ,
48- } ) ;
49-
50- // Fetch compiler metadata
51- const compilerMetadata = await getCompilerMetadata ( contract ) ;
52-
53- if ( ! compilerMetadata || ! compilerMetadata . abi ) {
54- return null ;
55- }
56-
57- const contractName = compilerMetadata . name || "Unknown Contract" ;
58- const abi = compilerMetadata . abi ;
59-
60- // Extract function selector from transaction data (first 4 bytes)
61- const functionSelector = txParam . data . slice ( 0 , 10 ) as `0x${string } `;
62-
63- // Find matching function in ABI
64- const functions = ( abi as readonly AbiItem [ ] ) . filter (
65- ( item ) : item is AbiFunction => item . type === "function" ,
66- ) ;
67- let matchingFunction : AbiFunction | null = null ;
68-
69- for ( const func of functions ) {
70- const selector = toFunctionSelector ( func ) ;
71- if ( selector === functionSelector ) {
72- matchingFunction = func ;
73- break ;
74- }
75- }
76-
77- if ( ! matchingFunction ) {
78- return null ;
79- }
80-
81- const functionName = matchingFunction . name ;
82-
83- // Decode function data
84- const decodedArgs = ( await decodeFunctionData ( {
85- contract : getContract ( {
86- ...contract ,
87- abi : [ matchingFunction ] ,
88- } ) ,
89- data : txParam . data ,
90- } ) ) as readonly unknown [ ] ;
91-
92- // Create a clean object for display
93- const functionArgs : Record < string , unknown > = { } ;
94- if ( matchingFunction . inputs && decodedArgs ) {
95- for ( let index = 0 ; index < matchingFunction . inputs . length ; index ++ ) {
96- const input = matchingFunction . inputs [ index ] ;
97- if ( input ) {
98- functionArgs [ input . name || `arg${ index } ` ] = decodedArgs [ index ] ;
99- }
100- }
101- }
102-
103- return {
104- contractName,
105- functionName,
106- functionArgs,
107- } ;
108- } catch ( error ) {
109- console . error ( "Error decoding transaction param:" , error ) ;
110- return null ;
111- }
41+ try {
42+ if ( ! txParam || ! txParam . to || ! txParam . data ) {
43+ return null ;
44+ }
45+
46+ // Create contract instance
47+ const contract = getContract ( {
48+ address : txParam . to ,
49+ // eslint-disable-next-line no-restricted-syntax
50+ chain : defineChain ( chainId ) ,
51+ client : serverThirdwebClient ,
52+ } ) ;
53+
54+ // Fetch compiler metadata
55+ const compilerMetadata = await getCompilerMetadata ( contract ) ;
56+
57+ if ( ! compilerMetadata || ! compilerMetadata . abi ) {
58+ return null ;
59+ }
60+
61+ const contractName = compilerMetadata . name || "Unknown Contract" ;
62+ const abi = compilerMetadata . abi ;
63+
64+ // Extract function selector from transaction data (first 4 bytes)
65+ const functionSelector = txParam . data . slice ( 0 , 10 ) as `0x${string } `;
66+
67+ // Find matching function in ABI
68+ const functions = ( abi as readonly AbiItem [ ] ) . filter (
69+ ( item ) : item is AbiFunction => item . type === "function" ,
70+ ) ;
71+ let matchingFunction : AbiFunction | null = null ;
72+
73+ for ( const func of functions ) {
74+ const selector = toFunctionSelector ( func ) ;
75+ if ( selector === functionSelector ) {
76+ matchingFunction = func ;
77+ break ;
78+ }
79+ }
80+
81+ if ( ! matchingFunction ) {
82+ return null ;
83+ }
84+
85+ const functionName = matchingFunction . name ;
86+
87+ // Decode function data
88+ const decodedArgs = ( await decodeFunctionData ( {
89+ contract : getContract ( {
90+ ...contract ,
91+ abi : [ matchingFunction ] ,
92+ } ) ,
93+ data : txParam . data ,
94+ } ) ) as readonly unknown [ ] ;
95+
96+ // Create a clean object for display
97+ const functionArgs : Record < string , unknown > = { } ;
98+ if ( matchingFunction . inputs && decodedArgs ) {
99+ for ( let index = 0 ; index < matchingFunction . inputs . length ; index ++ ) {
100+ const input = matchingFunction . inputs [ index ] ;
101+ if ( input ) {
102+ functionArgs [ input . name || `arg${ index } ` ] = decodedArgs [ index ] ;
103+ }
104+ }
105+ }
106+
107+ return {
108+ contractName,
109+ functionArgs,
110+ functionName,
111+ } ;
112+ } catch ( error ) {
113+ console . error ( "Error decoding transaction param:" , error ) ;
114+ return null ;
115+ }
112116}
113117
114118async function decodeTransactionData (
115- transaction : Transaction ,
119+ transaction : Transaction ,
116120) : Promise < DecodedTransactionResult > {
117- try {
118- // Check if we have transaction parameters
119- if (
120- ! transaction . transactionParams ||
121- transaction . transactionParams . length === 0
122- ) {
123- return [ ] ;
124- }
125-
126- // Ensure we have a chainId
127- if ( ! transaction . chainId ) {
128- return [ ] ;
129- }
130-
131- const chainId = parseInt ( transaction . chainId ) ;
132-
133- // Decode all transaction parameters in parallel
134- const decodingPromises = transaction . transactionParams . map ( ( txParam ) =>
135- decodeSingleTransactionParam ( txParam , chainId ) ,
136- ) ;
137-
138- const results = await Promise . all ( decodingPromises ) ;
139- return results ;
140- } catch ( error ) {
141- console . error ( "Error decoding transaction:" , error ) ;
142- return [ ] ;
143- }
121+ try {
122+ // Check if we have transaction parameters
123+ if (
124+ ! transaction . transactionParams ||
125+ transaction . transactionParams . length === 0
126+ ) {
127+ return [ ] ;
128+ }
129+
130+ // Ensure we have a chainId
131+ if ( ! transaction . chainId ) {
132+ return [ ] ;
133+ }
134+
135+ const chainId = parseInt ( transaction . chainId ) ;
136+
137+ // Decode all transaction parameters in parallel
138+ const decodingPromises = transaction . transactionParams . map ( ( txParam ) =>
139+ decodeSingleTransactionParam ( txParam , chainId ) ,
140+ ) ;
141+
142+ const results = await Promise . all ( decodingPromises ) ;
143+ return results ;
144+ } catch ( error ) {
145+ console . error ( "Error decoding transaction:" , error ) ;
146+ return [ ] ;
147+ }
144148}
145149
146150export default async function TransactionPage ( {
147- params,
151+ params,
148152} : {
149- params : Promise < { team_slug : string ; project_slug : string ; id : string } > ;
153+ params : Promise < { team_slug : string ; project_slug : string ; id : string } > ;
150154} ) {
151- const { team_slug, project_slug, id } = await params ;
152-
153- const [ authToken , project ] = await Promise . all ( [
154- getAuthToken ( ) ,
155- getProject ( team_slug , project_slug ) ,
156- ] ) ;
157-
158- if ( ! authToken ) {
159- loginRedirect ( `/team/${ team_slug } /${ project_slug } /transactions/tx/${ id } ` ) ;
160- }
161-
162- if ( ! project ) {
163- redirect ( `/team/${ team_slug } ` ) ;
164- }
165-
166- const [ transactionData , activityLogs ] = await Promise . all ( [
167- getSingleTransaction ( {
168- clientId : project . publishableKey ,
169- teamId : project . teamId ,
170- transactionId : id ,
171- } ) ,
172- getTransactionActivityLogs ( {
173- clientId : project . publishableKey ,
174- teamId : project . teamId ,
175- transactionId : id ,
176- } ) ,
177- ] ) ;
178-
179- const client = getClientThirdwebClient ( {
180- jwt : authToken ,
181- teamId : project . teamId ,
182- } ) ;
183-
184- if ( ! transactionData ) {
185- notFound ( ) ;
186- }
187-
188- // Decode transaction data on the server
189- const decodedTransactionData = await decodeTransactionData ( transactionData ) ;
190-
191- return (
192- < div className = "space-y-6 p-2" >
193- < TransactionDetailsUI
194- activityLogs = { activityLogs }
195- client = { client }
196- project = { project }
197- teamSlug = { team_slug }
198- transaction = { transactionData }
199- decodedTransactionData = { decodedTransactionData }
200- />
201- </ div >
202- ) ;
155+ const { team_slug, project_slug, id } = await params ;
156+
157+ const [ authToken , project ] = await Promise . all ( [
158+ getAuthToken ( ) ,
159+ getProject ( team_slug , project_slug ) ,
160+ ] ) ;
161+
162+ if ( ! authToken ) {
163+ loginRedirect ( `/team/${ team_slug } /${ project_slug } /transactions/tx/${ id } ` ) ;
164+ }
165+
166+ if ( ! project ) {
167+ redirect ( `/team/${ team_slug } ` ) ;
168+ }
169+
170+ const [ transactionData , activityLogs ] = await Promise . all ( [
171+ getSingleTransaction ( {
172+ clientId : project . publishableKey ,
173+ teamId : project . teamId ,
174+ transactionId : id ,
175+ } ) ,
176+ getTransactionActivityLogs ( {
177+ clientId : project . publishableKey ,
178+ teamId : project . teamId ,
179+ transactionId : id ,
180+ } ) ,
181+ ] ) ;
182+
183+ const client = getClientThirdwebClient ( {
184+ jwt : authToken ,
185+ teamId : project . teamId ,
186+ } ) ;
187+
188+ if ( ! transactionData ) {
189+ notFound ( ) ;
190+ }
191+
192+ // Decode transaction data on the server
193+ const decodedTransactionData = await decodeTransactionData ( transactionData ) ;
194+
195+ return (
196+ < div className = "space-y-6 p-2" >
197+ < TransactionDetailsUI
198+ activityLogs = { activityLogs }
199+ client = { client }
200+ decodedTransactionData = { decodedTransactionData }
201+ project = { project }
202+ teamSlug = { team_slug }
203+ transaction = { transactionData }
204+ />
205+ </ div >
206+ ) ;
203207}
0 commit comments